单片机
基于51单片机开发计时器,使其实现以下功能:
(1)开发板上四个独立按键分别控制计时器的启停、计时器的复位(按下计时值归零)、计时位切换、计时时间的递增,最小间隔为1。
(2)数码管上显示当前计时时间(用小时-分钟-秒的格式显示),启动计时后数码管上实时显示计时剩余时长。(中间用-隔开)
(3)计时时间到了后启动蜂鸣器(按下计时器复位按键后蜂鸣器停止)
要求:能在设备中运行即可,软件用到keil和普中
附图:
#include <reg51.h>
// 数码管位选控制端口
sbit digit1 = P2^0;
sbit digit2 = P2^1;
sbit digit3 = P2^2;
// 数码管段选控制端口
sbit segmentA = P1^0;
sbit segmentB = P1^1;
sbit segmentC = P1^2;
sbit segmentD = P1^3;
sbit segmentE = P1^4;
sbit segmentF = P1^5;
sbit segmentG = P1^6;
sbit segmentDP = P1^7;
// 按键控制端口
sbit startStopButton = P3^0; // 启停按键
sbit resetButton = P3^1; // 复位按键
sbit toggleButton = P3^2; // 切换按键
sbit incrementButton = P3^3; // 递增按键
// 蜂鸣器控制端口
sbit buzzer = P3^4;
unsigned char displayData[3]; // 数码管显示数据(小时、分钟、秒)
unsigned char currentTime[3]; // 当前计时时间(小时、分钟、秒)
unsigned char targetTime[3]; // 目标计时时间(小时、分钟、秒)
unsigned char displayIndex = 0; // 数码管显示索引
bit timerRunning = 0; // 计时器运行状态
// 数码管段选码表
unsigned char code digitSegments[10] = {
0x3F, // 0
0x06, // 1
0x5B, // 2
0x4F, // 3
0x66, // 4
0x6D, // 5
0x7D, // 6
0x07, // 7
0x7F, // 8
0x6F // 9
};
// 延时函数
void delay(unsigned int ms) {
unsigned int i, j;
for(i = 0; i < ms; i++) {
for(j = 0; j < 120; j++);
}
}
// 数码管位选函数
void selectDigit(unsigned char digit) {
digit1 = 1;
digit2 = 1;
digit3 = 1;
switch(digit) {
case 0: digit1 = 0; break;
case 1: digit2 = 0; break;
case 2: digit3 = 0; break;
}
}
// 数码管段选函数
void displayDigit(unsigned char digit, unsigned char data) {
segmentA = (data & 0x01) ? 1 : 0;
segmentB = (data & 0x02) ? 1 : 0;
segmentC = (data & 0x04) ? 1 : 0;
segmentD = (data & 0x08) ? 1 : 0;
segmentE = (data & 0x10) ? 1 : 0;
segmentF = (data & 0x20) ? 1 : 0;
segmentG = (data & 0x40) ? 1 : 0;
segmentDP = (data & 0x80) ? 1 : 0;
}
// 数码管显示函数
void updateDisplay() {
selectDigit(displayIndex);
displayDigit(displayIndex, displayData[displayIndex]);
}
// 数码管刷新中断服务函数
void timer0Interrupt() interrupt 1 {
TH0 = 0x3C;
TL0 = 0xB0;
if (timerRunning) {
if (currentTime[0] == 0 && currentTime[1] == 0 && currentTime[2] == 0) {
buzzer = 1; // 计时时间到了,启动蜂鸣器
timerRunning = 0; // 停止计时器
} else {
if (currentTime[2] > 0) {
currentTime[2]--;
} else {
if (currentTime[1] > 0) {
currentTime[1]--;
currentTime[2] = 59;
} else {
if (currentTime[0] > 0) {
currentTime[0]--;
currentTime[1] = 59;
currentTime[2] = 59;
}
}
}
}
if (displayIndex == 0) {
displayData[0] = currentTime[0] / 10;
displayData[1] = currentTime[0] % 10;
} else if (displayIndex == 1) {
displayData[0] = currentTime[1] / 10;
displayData[1] = currentTime[1] % 10;
} else if (displayIndex == 2) {
displayData[0] = currentTime[2] / 10;
displayData[1] = currentTime[2] % 10;
}
updateDisplay();
}
}
// 按键中断服务函数
void buttonInterrupt() interrupt 0 {
if (!startStopButton) { // 启停按键
if (!timerRunning) {
timerRunning = 1;
} else {
timerRunning = 0;
}
}
if (!resetButton) { // 复位按键
currentTime[0] = 0;
currentTime[1] = 0;
currentTime[2] = 0;
buzzer = 0; // 停止蜂鸣器
}
if (!toggleButton) { // 切换按键
displayIndex++;
if (displayIndex > 2) {
displayIndex = 0;
}
}
if (!incrementButton) { // 递增按键
if (timerRunning) {
buzzer = 0; // 停止蜂鸣器
if (displayIndex == 0) {
if (currentTime[0] < 23) {
currentTime[0]++;
} else {
currentTime[0] = 0;
}
} else if (displayIndex == 1) {
if (currentTime[1] < 59) {
currentTime[1]++;
} else {
currentTime[1] = 0;
}
} else if (displayIndex == 2) {
if (currentTime[2] < 59) {
currentTime[2]++;
} else {
currentTime[2] = 0;
}
}
if (currentTime[0] == targetTime[0] && currentTime[1] == targetTime[1] && currentTime[2] == targetTime[2]) {
buzzer = 1; // 计时时间到了,启动蜂鸣器
timerRunning = 0; // 停止计时器
}
}
}
delay(50); // 延时去抖动
}
// 主函数
void main() {
TMOD = 0x01; // 设置定时器0为模式1
TH0 = 0x3C; // 设置定时器0初值,用于定时1ms
TL0 = 0xB0;
ET0 = 1; // 允许定时器0中断
EA = 1; // 允许总中断
TR0 = 1; // 启动定时器0
EX0 = 1; // 允许外部中断0(按键中断)
IT0 = 1; // 设置外部中断0为下降沿触发
displayIndex = 0;
displayData[0] = 0;
displayData[1] = 0;
displayData[2] = 0;
currentTime[0] = 0;
currentTime[1] = 0;
currentTime[2] = 0;
targetTime[0] = 0;
targetTime[1] = 0;
targetTime[2] = 10; // 设置目标计时时间为10秒
while (1) {
if (timerRunning) {
targetTime[0] = currentTime[0];
targetTime[1] = currentTime[1];
targetTime[2] = currentTime[2];
}
updateDisplay();
}
}
参考代码:
#include <reg52.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#define uint unsigned int
#define uchar unsigned char
sbit led = P1^0; // 数码管位选
sbit led1 = P1^1;
sbit led2 = P1^2;
sbit led3 = P1^3;
sbit led4 = P1^4;
sbit buzzer = P1^5; // 蜂鸣器
sbit key1 = P3^2; // 按键1
sbit key2 = P3^3; // 按键2
sbit key3 = P3^4; // 按键3
sbit key4 = P3^5; // 按键4
uchar code数码管[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90}; // 数码管段码
uchar time[3] = {0, 0, 0}; // 计时时间,单位为秒
uchar flag = 0; // 计时标志位,1表示正在计时,0表示未计时
uchar key_state = 0; // 按键状态,记录最近一次按键状态,用于消除抖动
void delay_ms(uint x) { // 延时函数,单位为毫秒
uint i, j;
for (i = x; i > 0; i--) {
for (j = 114; j > 0; j--) {
_nop_();
}
}
}
void display() { // 显示函数,显示当前计时时间
P0 =数码管[time[2]]; // 显示秒位
led = 1; // 选择数码管第一位
delay_ms(1); // 延时一段时间,让数码管稳定显示
led = 0; // 选择下一数码管位
P0 =数码管[time[1]]; // 显示分钟位
led1 = 1; // 选择数码管第二位
delay_ms(1); // 延时一段时间,让数码管稳定显示
led1 = 0; // 选择下一数码管位
P0 =数码管[time[0]]; // 显示小时位
led2 = 1; // 选择数码管第三位
delay_ms(1); // 延时一段时间,让数码管稳定显示
led2 = 0; // 选择下一数码管位
}
void timer0() interrupt 1 { // Timer0中断函数,用于计时,每1ms中断一次
TH0 = 0xFC; // 重置Timer0初值高位
TL0 = 0x67; // 重置Timer0初值低位
if (flag) { // 如果正在计时
time[0]++; // 分钟加1秒
if (time[0] == 60) { // 如果分钟加到60,则进位到小时,并重置分钟为0
time[0] = 0;
time[1] += 1; // 小时加1分钟
if (time[1] == 60) { // 如果小时加到60,则进位到小时,并重置小时为0,分钟为0
time[1] = 0;
time[2] += 1; // 总时间加1秒
if (time[2] == 60) { // 如果总时间加到60秒,则进位到分钟,并重置总时间为0秒
#未完待续
简单点说,当传感器检测到障碍物时,对应的TTL输出低电平,比如第一路传感器信号线连接在单片机的P10口,当第一路传感器检测到障碍物时,单片机P10口就为低电平,也就是说通过读取传感器信号线连接的单片机I/O口的高低电平,就可以知道传感器前方有没有障碍物。
开发板基本都带有例程,把其中的定时器控制和按键控制例程结合一下,这点功能很容易实现了
#include <reg51.h>
// 定义 IO 引脚连接
sbit keyStartStop = P1^0; // 启停按键
sbit keyReset = P1^1; // 复位按键
sbit keySwitch = P1^2; // 切换按键
sbit keyIncrement = P1^3; // 递增按键
sbit buzzer = P2^0; // 蜂鸣器
sbit segA = P2^4; // 数码管段 A
sbit segB = P2^5; // 数码管段 B
sbit segC = P2^6; // 数码管段 C
sbit segD = P2^7; // 数码管段 D
sbit digit1 = P0^0; // 数码管位1
sbit digit2 = P0^1; // 数码管位2
sbit digit3 = P0^2; // 数码管位3
// 全局变量
unsigned int hours = 0; // 小时
unsigned int minutes = 0; // 分钟
unsigned int seconds = 0; // 秒
bit timerRunning = 0; // 计时器是否正在运行
// 数码管显示函数
void displayDigits(unsigned int num) {
// 数码管位选逻辑,根据具体硬件连接和驱动方式编写
digit1 = 1;
digit2 = 0;
digit3 = 0;
// 数码管显示逻辑,根据具体硬件连接和驱动方式编写
// 例如,使用共阳数码管和段选锁存器的驱动方式
switch(num / 100) {
case 0:
segA = segB = segC = segD = 0; // 显示 0
break;
case 1:
segA = 1; segB = segC = segD = 0; // 显示 1
break;
// 其他数字的显示逻辑...
}
// 控制延时等待,以确保数码管刷新稳定
// 具体延时时间根据硬件和显示器件的响应时间来确定
// 可以通过调试和测试来优化延时时间
delay();
// 重复上述逻辑,显示分钟和秒的数码管位
}
// 延时函数
void delay() {
// 根据需要的延时时间和单片机的主频进行计算
// 可以通过调试和测试来优化延时时间
unsigned int i, j;
for (i = 0; i < 1000; i++) {
for (j = 0; j < 100; j++) {
// 空循环来消耗一定的时间
}
}
}
// 计时器递增函数
void incrementTimer() {
seconds++;
if (seconds >= 60) {
seconds = 0;
minutes++;
if (minutes >= 60) {
minutes = 0;
hours++;
if (hours >= 24) {
hours = 0;
}
}
}
}
// 主函数
void main() {
// 初始化 IO 引脚状态
keyStartStop = 1;
keyReset = 1;
keySwitch = 1;
keyIncrement = 1;
buzzer = 0; // 关闭蜂鸣器
// 主循环
while(1) {
// 检测启停按键
if (keyStartStop == 0) {
timerRunning = !timerRunning; // 切换计时器运行状态
while (keyStartStop == 0); // 等待按键释放
}
// 检测复位按键
if (keyReset == 0) {
hours = minutes = seconds = 0; // 将计时值归零
buzzer = 0; // 关闭蜂鸣器
while (keyReset == 0); // 等待按键释放
}
// 检测切换按键
if (keySwitch == 0) {
// 切换计时位的显示方式
// 可以使用一个全局变量来控制显示方式,例如使用 24 小时制或 12 小时制
while (keySwitch == 0); // 等待按键释放
}
// 检测递增按键
if (keyIncrement == 0) {
incrementTimer(); // 递增计时器的时间
while (keyIncrement == 0); // 等待按键释放
}
// 显示当前计时时间
displayDigits(hours * 100 + minutes); // 显示小时和分钟
// 如果计时器正在运行,则显示剩余时间
if (timerRunning) {
unsigned int remainingSeconds = (hours * 3600 + minutes * 60 + seconds);
displayDigits(remainingSeconds); // 显示剩余秒数
// 检测计时时间是否到达
if (remainingSeconds == 0) {
buzzer = 1; // 启动蜂鸣器
}
}
// 控制延时等待,以确保计时器刷新频率
delay();
}
}
请注意,以上代码示例是一个基本的框架,你需要根据具体的硬件电路和显示器件的驱动方式进行适当的修改。此外,延时函数的实现需要根据单片机的主频和需要的延时时间进行调整。在具体的硬件和电路连接中,你还需要确认按键的接线和设置外部中断来检测按键的状态变化。
请确保你在编写和调试代码时参考相关的单片机手册和文档,并根据实际情况进行适当的修改和调整。