1.题目要求(注意这里代码配合湖科大开发板使用)
基础:
- 用左右两个8位数码管显示路口交通灯的倒数计数数字(如01号显示南北秒数、67号管显示东西秒数);
- K1键按下一次表示进入红绿黄灯秒数设置状态,此时计数倒计数停止使用K2,K3调整秒数;再按一次K1进入绿设置,再按一次K1进入黄灯秒数设置....,第四次按K1退出设置状态;
- 可使用串口通信程序来进行交通灯的秒数设置,参考:-ns45, -we60表示将交通灯的南北和东西秒数分别设置为45S和60S;
加分项:
- 显示和按键处理使用中断或定时器的方式。
- 考虑秒数最后5S的闪烁效果。
- 采用ucOS实时操作系统实现。
2.效果展示(中断实现)
图2.1显示红绿灯倒计时与灯的类型
最左最右的数码管表示两个方向的倒计时,中间为灯的类型
LD绿灯‘L’字符, FD黄灯‘F’字符 ,HD红灯‘H’字符
图2.2设置模式,设置红灯时间
图2.3串口设置
3.参考代码(中断实现)
1. main.c
/******************************交通灯显示********************************
*Id: 2*********
*Coder:芒果酱
*Time: 2024.4.23
***********************************************************************/
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "stdlib.h"
#define uchar unsigned char
//-----------------------函数定义----------------------------
void ClearShow(int start, int end);
void InitData(void);
void ModeNowShow(void);
void InitBand(void);
void SettingMode(void);
void ShowNode(void);
void TrafficLightControl(void);
//-----------------------物理量---------------------------
// 数码管当前存储(10为不显示)
int NodeShow[8] = {10, 10, 10, 10, 10, 10, 10, 10};
// 按键当前值(keyup赋值1,keyudowm赋值-1)
int keyup = 0, keydown = 0;
//-----------------------配置表----------------------------
// 红绿灯文字串口输出
char *ledstr[] = {
"Green",
"Yellow",
"Red",
};
// 0表示绿灯 1示黄灯 2表示红灯
int A_lendChangeconfig[] = {0, 1, 2}, // A灯显示顺序表
B_lendChangeconfig[] = {2, 0, 1}, // B灯显示顺序表
// 两个数码管显示的什么时间(0绿灯 1黄灯 2红灯),这里表示lendChangeconfig数组的位置
A_LedNowLocal = 0, // 存储位置(这里0不代表绿灯,只是数组位置,范围0-2)
B_LedNowLocal = 0, // 存储位置(这里0不代表绿灯,只是数组位置,范围0-2)
// 注意:绿灯 + 黄灯= 红灯 15s+5s=20s
LedTime[] = {15, 5, 20},
// 0绿灯‘L’字符, 1黄灯‘F’字符 ,2红灯‘H’字符
ShowType[] = {20, 17, 18},
// 两个数码管当前显示的时间s
A_LedNowTime = 0,
B_LedNowTime = 0,
// 两个数码管当前显示的时间是否闪
A_Ledflashing = 0,
B_Ledflashing = 0,
// 闪烁开始时间s
FlashingTime = 5,
// 设置模式判断0为显示模式,1为设置绿灯,2为设置黄灯,3为设置红灯(这里自动计算,无法直接设置)
ModeNow = 0;
//-------------------------中间变量--------------------------------------
u8 t, len;
long times = 0;
//-----------------------定时器相关函数----------------------------
// 数码管
void Timer2Init(u16 arr, u16 psc)
{
RCC->APB1ENR |= 1 << 0; // TIM2时钟使能
TIM2->ARR = arr; // 设定计数器自动重装值,10为1ms
TIM2->PSC = psc; // 预分频器7200,得到10KHZ的计数时钟
TIM2->DIER |= 1 << 0; // 允许更新中断
TIM2->CR1 |= 0x01; // 使能定时器3
MY_NVIC_Init(1, 2, TIM2_IRQChannel, 2); // 抢占1,子优先级2,组2
}
// 秒时间
void Timer3Init(u16 arr, u16 psc)
{
RCC->APB1ENR |= 1 << 1; // TIM3时钟使能
TIM3->ARR = arr; // 设定计数器自动重装值,10为1ms
TIM3->PSC = psc; // 预分频器7200,得到10KHZ的计数时钟
TIM3->DIER |= 1 << 0; // 允许更新中断
TIM3->CR1 |= 0x01; // 使能定时器3
MY_NVIC_Init(3, 3, TIM3_IRQChannel, 3); // 抢占3,子优先级3,组3
}
// 按键
void Timer4Init(u16 arr, u16 psc)
{
RCC->APB1ENR |= 1 << 2; // TIM2时钟使能
TIM4->ARR = arr; // 设定计数器自动重装值,10为1ms
TIM4->PSC = psc; // 预分频器7200,得到10KHZ的计数时钟
TIM4->DIER |= 1 << 0; // 允许更新中断
TIM4->CR1 |= 0x01; // 使能定时器3
MY_NVIC_Init(4, 4, TIM4_IRQChannel, 3); // 抢占4,子优先级4,组3
}
// 显示定时器
void TIM2_IRQHandler(void)
{
if (TIM2->SR & 0x0001) // 溢出中断
{
ShowNode();
}
TIM2->SR &= ~(1 << 0); // 清除中断标志位
}
// 时钟定时器
void TIM3_IRQHandler(void)
{
if (TIM3->SR & 0x0001) // 溢出中断
{
// AB倒计时
if (A_LedNowTime != 0)
A_LedNowTime--;
if (B_LedNowTime != 0)
B_LedNowTime--;
}
TIM3->SR &= ~(1 << 0); // 清除中断标志位
}
// 按键定时器
void TIM4_IRQHandler(void)
{
if (TIM4->SR & 0x0001) // 溢出中断
{
if (KEY1 == 0)
{
keyup = 1; // 设置keyup标志
}
if (KEY2 == 0)
{
keydown = -1; // 设置keydown标志
}
if (KEY3 == 0)
{
// 进入设置
ModeNow = (++ModeNow) % 4;
// 清除缓存区显示(1-8数码管内容)
ClearShow(1, 8);
}
}
TIM4->SR &= ~(1 << 0); // 清除中断标志位
}
//-----------------------初始化相关函数----------------------------
// 板子初始化函数
void InitBand()
{
// 系统频率
Stm32_Clock_Init(6);
// 延时启动
delay_init(72);
// 数码管显示定时器
Timer2Init(9, 7199);
// 倒计时定时器 10Khz的计数频率,计数到10000表示1s
Timer3Init(9999, 7199);
// 按键读取定时器(设置999用来消抖)
Timer4Init(999, 7199);
// 串口初始化为9600
uart_init(72, 9600);
// led初始化
LED_Init();
// 按键初始化
KEY_Init();
// 屏蔽LED
LED_SEL = 0;
}
// 数据初始化函数
void InitData()
{
// 显示归位
A_LedNowLocal = 0;
B_LedNowLocal = 0;
// 计算初始的时间
A_LedNowTime = LedTime[A_lendChangeconfig[A_LedNowLocal]];
B_LedNowTime = LedTime[B_lendChangeconfig[B_LedNowLocal]];
}
//-----------------------运行模式相关函数----------------------------
// 模式选择函数
void ModeNowShow()
{
if (ModeNow == 0)
{
// 红绿灯模式
TrafficLightControl();
}
else
{
// 设置模式
SettingMode();
}
}
// 红绿灯控制与显示函数
void TrafficLightControl()
{
int temptime = times % 10000;
// 闪烁显示,这里可以控制闪烁速度
if (A_Ledflashing == 1 && (temptime > 0 && temptime < 5000))
{
//不显示
NodeShow[1] = 10;
NodeShow[0] = 10;
}
else
{
// 第一个灯显示
NodeShow[0] = A_LedNowTime / 10;
if (NodeShow[0] == 0)
{
NodeShow[0] = 10; // 0限制不显示
}
NodeShow[1] = A_LedNowTime % 10;
}
// 闪烁显示,这里可以控制闪烁速度
if (B_Ledflashing == 1 && (temptime > 0 && temptime < 5000))
{
//不显示
NodeShow[6] = 10;
NodeShow[7] = 10;
}
else
{
// 第二个灯显示
NodeShow[6] = B_LedNowTime / 10;
if (NodeShow[6] == 0)
{
NodeShow[6] = 10; // 0限制不显示
}
NodeShow[7] = B_LedNowTime % 10;
}
// 如果两个灯到了0,进入下一个灯的显示(如:绿灯->黄灯)
if (A_LedNowTime == 0)
{
// 下一个灯
A_LedNowLocal = (++A_LedNowLocal) % 3;
// 下一个灯的时间
A_LedNowTime = LedTime[A_lendChangeconfig[A_LedNowLocal]];
}
if (B_LedNowTime == 0)
{
// 下一个灯
B_LedNowLocal = (++B_LedNowLocal) % 3;
// 下一个灯的时间
B_LedNowTime = LedTime[B_lendChangeconfig[B_LedNowLocal]];
}
// AB闪烁控制判断
if (A_LedNowTime <= FlashingTime)
A_Ledflashing = 1;
else
A_Ledflashing = 0;
if (B_LedNowTime <= FlashingTime)
B_Ledflashing = 1;
else
B_Ledflashing = 0;
// 显示灯的类别
NodeShow[3] = ShowType[A_lendChangeconfig[A_LedNowLocal]];
NodeShow[4] = ShowType[B_lendChangeconfig[B_LedNowLocal]];
// 不显示2、5数码管
NodeShow[2] = 10;
NodeShow[5] = 10;
}
// 设置模式函数
void SettingMode()
{
// '设置数字'存储变量
int SetTime01 = 0,
SetTime02 = 0;
// 自动计算红灯时间
LedTime[2] = LedTime[0] + LedTime[1];
// 计算每个位的数
SetTime01 = LedTime[ModeNow - 1] / 10;
SetTime02 = LedTime[ModeNow - 1] % 10;
// 1为设置绿灯
if (ModeNow == 1)
{
// 显示字母Ld
NodeShow[0] = 20;
NodeShow[1] = 15;
}
// 2为设置黄灯
if (ModeNow == 2)
{
// 显示字母Fd
NodeShow[0] = 17;
NodeShow[1] = 15;
}
// 3为设置红灯
if (ModeNow == 3)
{
// 显示字母Hd
NodeShow[0] = 18;
NodeShow[1] = 15;
}
// 显示‘-’
NodeShow[2] = 28;
NodeShow[3] = ModeNow;
// 显示‘-’
NodeShow[4] = 28;
// 显示设置的数字
NodeShow[5] = SetTime01;
NodeShow[6] = SetTime02;
// 按键控制,<3限制按键控制范围
if (ModeNow < 3)
{
// 设置数据,>0
if (LedTime[ModeNow - 1] > 1)
{
//+keydowm(keydowm==-1)
LedTime[ModeNow - 1] = LedTime[ModeNow - 1] + keydown;
// 清除按键标志
keydown = 0;
}
// 设置数据,<99
if (LedTime[ModeNow - 1] < 99)
{
//+keyup(keyup==1)
LedTime[ModeNow - 1] = LedTime[ModeNow - 1] + keyup;
// 清除按键标志
keyup = 0;
}
}
NodeShow[7] = 10;
// 更新显示数据
InitData();
}
//-----------------------显示相关函数----------------------------
// 数码管显示函数
void ShowNode()
{ // 变量定义
uchar i, icode;
// 屏蔽LED
LED_SEL = 0;
// 8位显示
for (i = 0; i < 8; i++)
{
// 第i位数码管显示数字
icode = NodeShow[i];
// 显示数字
SetLed(i, icode);
// 频率控制(显示亮度控制,数值越大显示越亮)
delay_us(100);
}
}
// 清除指定区域显示 1-8数码管内容函数
void ClearShow(int start, int end)
{
for (int i = start - 1; i < (end); i++)
{
// 用10去除原来的数据
NodeShow[i] = 10;
}
}
//-----------------------串口相关-----------------------------
// 串口字符串中的具体信息并且设置
void ConfigLEDShow(int i, int SetTime)
{
switch (i)
{
case 1:
LedTime[2] = SetTime;
break;
case 2:
LedTime[0] = SetTime;
break;
case 3:
LedTime[1] = SetTime;
break;
case 4:
A_LedNowTime = SetTime;
break;
case 5:
B_LedNowTime = SetTime;
break;
default:
break;
}
}
// 获取串口字符串中的具体信息
char *GetStrInfo()
{
// 长度不够判断
if (len <= 3)
return "\nERROR:len too low";
//变量定义
int num = 0, NumLength = len - 3;
char str[NumLength], temp;
//读取数据大小
for (int i = 0; i < NumLength; i++)
{
temp = USART_RX_BUF[3 + i];
// 存在其他字符判断
if (temp >= '0' && temp <= '9')
str[i] = temp;
else
return "\nERROR:String have 'abc...'";
}
// char数组转int
const char *ptr = str;
while (*ptr)
{
num *= 10;
num += *ptr - '0';
ptr++;
}
// 配置程序
ConfigLEDShow((USART_RX_BUF[1] - '0'), num);
// 返回信息
return "\nSucess";
}
// 输出串口信息
void PrintMsg()
{
printf("\n\n----Now LED Config,your can input:\"(2)12\"----");
printf("\n(1)Red Time(Can't Change):%d", LedTime[2]);
printf("\n(2)Green Time:%d", LedTime[0]);
printf("\n(3)Yellow Time:%d", LedTime[1]);
printf("\n(4)NS Now Time:%d (Min:1,Max:%d,Type:%s)", A_LedNowTime, LedTime[A_lendChangeconfig[A_LedNowLocal]], ledstr[A_lendChangeconfig[A_LedNowLocal]]);
printf("\n(5)WE Now Time:%d (Min:1,Max:%d,Type:%s)", B_LedNowTime, LedTime[B_lendChangeconfig[B_LedNowLocal]], ledstr[B_lendChangeconfig[B_LedNowLocal]]);
printf("\n------------------------------\n");
}
// 串口函数
void Serial()
{
if (USART_RX_STA & 0x80) // 7位STA0-6数据,7-8标志与长度
{
len = USART_RX_STA & 0x3f; // 得到此次接收到的数据长度,6位
// 输出结果
printf(GetStrInfo());
printf(" : ");
// 返回结果
for (t = 0; t < len; t++)
{
USART1->DR = USART_RX_BUF[t];
while ((USART1->SR & 0X40) == 0)
; // 等待发送结束
}
printf("\n"); // 插入换行
len = 0;
PrintMsg();
USART_RX_STA = 0;
}
else
{
times++;
if (times % 200000 == 0)
{
times = 0;
PrintMsg();
}
}
}
//-----------------------主函数----------------------------
int main()
{
// 板子初始化
InitBand();
// 数据初始化
InitData();
// 主循环
while (TRUE)
{
// 当前模式控制显示
ModeNowShow();
// 串口配置函数
Serial();
}
}
//--------------------使用教程---------------------------------
// 最左最右的数码管表示两个方向的倒计时,中间为灯的类型
// LD绿灯‘L’字符, FD黄灯‘F’字符 ,HD红灯‘H’字符
// 按键1进入设置,按键2设置减小,按键3设置增加,跳出即可设置完成
//---------------------附件-----------------------------
/*
导入下面这里的库文件,学校提供
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "usart.h"
#include "key.h"
(重要)注意这里要修改led.c变量segTable为
u8 segTable[] =
0x3f,//0
0x06,//1
0x5b,//2
0x4f,
0x66,//数字4
0x6d,
0x7d,
0x07,
0x7f,
0x6f,
0x00,
0x77,
0x7C,
0x39,
0x58,
0x5E,
0x79,
0x71,
0x76,
0x74,
0x38,
0x54,
0x37,
0x5C,
0x73,
0x50,
0x78,
0x3E,
0x40
*/
2.项目地址
gitee地址:MGJ520/HNUST_Traffic_lights (gitee.com)
4.附件及其重要事项
1. 注意这里要修改led.c变量segTable