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.参考代码(实时操作系统实现)
/******************************交通灯显示********************************
*Id: 21********
*Coder:芒果酱
*Time: 2024.4.26
***********************************************************************/
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "stdlib.h"
#include "includes.h"
#define uchar unsigned char
// 设置任务堆栈大小
#define LED_STK_SIZE 64
#define TIME_STK_SIZE 64
#define KEY_STK_SIZE 64
#define SERIAL_STK_SIZE 64
#define START_STK_SIZE 256
// 设置任务优先级
#define LED_TASK_Prio 1
#define TIME_TASK_Prio 2
#define KEY_TASK_Prio 3
#define SERIAL_TASK_Prio 4
#define START_TASK_Prio 10
// 任务堆栈
OS_STK TASK_TIME_STK[TIME_STK_SIZE];
OS_STK TASK_LED_STK[LED_STK_SIZE];
OS_STK TASK_KEY_STK[KEY_STK_SIZE];
OS_STK TASK_SERIAL_STK[SERIAL_STK_SIZE];
OS_STK TASK_START_STK[START_STK_SIZE];
//-----------------------函数定义----------------------------
void ClearShow(int start, int end);
void InitData(void);
void ModeNowShow(void);
void InitBand(void);
void SettingMode(void);
void ShowNode(void);
char *GetStrInfo(void);
void TrafficLightControl(void);
void TaskStart(void *pdata);
// 任务申明
void TaskLed(void *pdata);
void TaskTime(void *pdata);
void TaskKey(void *pdata);
void TaskSerial(void *pdata);
// 系统时钟配置函数
void SysTick_Configuration(void);
//-----------------------物理量---------------------------
// 数码管当前存储(10为不显示)
int NodeShow[8] = {10, 1, 10, 10, 10, 1, 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 SysTick_Handler(void)
{
OS_ENTER_CRITICAL(); /* Tell uC/OS-II that we are starting an ISR */
OSIntNesting++;
OS_EXIT_CRITICAL();
OSTimeTick(); /* Call uC/OS-II's OSTimeTick() */
OSIntExit(); /* Tell uC/OS-II that we are leaving the ISR */
}
// 系统时钟配置,设计1ms产生一次中断
void SysTick_Configuration(void)
{
SysTick->CTRL &= ~(1 << 2); // SYSTICK使用外部时钟源
SysTick->CTRL |= 1 << 1; // 开启SYSTICK中断
SysTick->LOAD = 9000; // 产生1ms中断
// bit2清空,选择外部时钟 HCLK/8
// MY_NVIC_Init(3,3,SystemHandler_SysTick,2);//组2,最低优先级
SysTick->CTRL |= 1 << 0; // 开启SYSTICK
}
//-----------------------运行模式相关函数----------------------------
// 模式选择函数
void ModeNowShow()
{
if (ModeNow == 0)
{
// 红绿灯模式
TrafficLightControl();
}
else
{
// 设置模式
SettingMode();
}
}
// 红绿灯控制与显示函数
void TrafficLightControl()
{
int temptime = times % 100;
// 闪烁显示,这里可以控制闪烁速度
if (A_Ledflashing == 1 && (temptime > 0 && temptime < 50))
{
// 不显示
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 < 50))
{
// 不显示
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();
}
//-----------------------显示相关函数----------------------------
// 清除指定区域显示 1-8数码管内容函数
void ClearShow(int start, int end)
{
int i = start - 1;
for (; 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()
{
// 变量定义
int num = 0, NumLength = len - 3;
char str[NumLength], temp;
int i = 0;
// 长度不够判断
if (len <= 3)
return "\nERROR:len too low";
// 读取数据大小
for (; 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
{
PrintMsg();
}
}
//-----------------------初始化相关函数----------------------------
// 板子初始化函数
void InitBand()
{
// 系统频率
Stm32_Clock_Init(6);
// 延时启动
delay_init(72);
// 串口初始化为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]];
}
//-----------------------主函数----------------------------
int main()
{
// 板子初始化
InitBand();
// 数据初始化
InitData();
// 定时器
SysTick_Configuration();
//任务初始化
OSInit();
//初始化任务
OSTaskCreate(TaskStart, // task 函数
(void *)0, // parameter传递参数
(OS_STK *)&TASK_START_STK[START_STK_SIZE - 1], // task stack top pointer
START_TASK_Prio); // task 优先级
//开始任务
OSStart();
return 0;
}
//-----------------------任务相关函数----------------------------
// 开始任务
void TaskStart(void *pdata)
{
pdata = pdata;
OS_ENTER_CRITICAL();
OSTaskCreate(TaskLed, (void *)0, (OS_STK *)&TASK_LED_STK[LED_STK_SIZE - 1], LED_TASK_Prio);
OSTaskCreate(TaskTime, (void *)0, (OS_STK *)&TASK_TIME_STK[TIME_STK_SIZE - 1], TIME_TASK_Prio);
OSTaskCreate(TaskKey, (void *)0, (OS_STK *)&TASK_KEY_STK[KEY_STK_SIZE - 1], KEY_TASK_Prio);
OSTaskCreate(TaskSerial, (void *)0, (OS_STK *)&TASK_SERIAL_STK[SERIAL_STK_SIZE - 1], SERIAL_TASK_Prio);
OSTaskSuspend(START_TASK_Prio); // suspend but not delete
OS_EXIT_CRITICAL();
}
// 任务1
// 显示数码管
void TaskLed(void *pdata)
{
// 变量定义
uchar i, icode;
// 屏蔽LED
LED_SEL = 0;
while (TRUE)
{
//闪烁计数
if (times==1000)
{
times = 0;
}
times++;
// 当前模式控制显示
ModeNowShow();
// 8位显示
for (i = 0; i < 8; i++)
{
// 第i位数码管显示数字
icode = NodeShow[i];
// 显示数字
SetLed(i, icode);
//1ms 频率控制(显示亮度控制,数值越大显示越亮)
OSTimeDlyHMSM(0, 0, 0, 1);
}
}
}
// 任务2
// 倒计时.
void TaskTime(void *pdata)
{
while (TRUE)
{
OS_ENTER_CRITICAL();
// AB倒计时
if (A_LedNowTime != 0)
A_LedNowTime--;
if (B_LedNowTime != 0)
B_LedNowTime--;
OS_EXIT_CRITICAL();
//一秒钟倒计时
OSTimeDlyHMSM(0, 0, 1, 0);
}
}
// 任务3
// 按键控制
void TaskKey(void *pdata)
{
while (TRUE)
{
if (KEY1 == 0)
{
keyup = 1; // 设置keyup标志
}
if (KEY2 == 0)
{
keydown = -1; // 设置keydown标志
}
if (KEY3 == 0)
{
// 进入设置
ModeNow = (++ModeNow) % 4;
// 清除缓存区显示(1-8数码管内容)
ClearShow(1, 8);
}
//防抖延时
OSTimeDlyHMSM(0, 0, 0, 120);
}
}
// 任务4
// 倒计时.
void TaskSerial(void *pdata)
{
while (TRUE)
{
//倒计时
OSTimeDlyHMSM(0, 0, 2, 0);
// 串口配置函数
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
*/
4.项目地址
MGJ520/hnust_Traffic_Lights_Internet_of_Things_os (gitee.com)
我是芒果酱,给个关注吧 ( ͡° ͜ʖ ͡°)