1.实验要求:
(1)在STM32平台下,设计一个丁字路口交通控制系统;
(2)利用10个LED发光二极管来表示交通灯的状态,用74HC595驱动芯片驱动两位7段数码管来显示倒计时;
(3)丁字路口分成主干道和支干道,主干道放行时间35s,支干道放行时间25s;
(4)通过按键实现整体清零、修改放行时长以及控制特殊情况(主干道和支干道均显示红灯或黄灯)等功能;
2.系统总体设计
(1)系统框图
(2)软件流程图
3.参考代码
(涉及各个模块的驱动文件详见文章末的链接。)
/**
********************************************************************************
* 这是一个模拟交通信号灯的实验程序
* (1)利用9个LED发光二极管来表示交通灯的状态,用两位7段数码管来显示倒计时;
* (2)丁字路口分成主干道和支干道,主干道放行时间35s,支干道放行时间25s;
* (3)通过按键实现整体清零、修改放行时长以及控制特殊情况(主干道和支干道均显示红灯或黄灯)等功能;
* (4)加分项:自行增加其他功能。
*
*
*
********************************************************************************
* 2020/7/1第次修改:加入数码管的驱动
* 使用IO直接驱动
* PB8--PB15用于数码管段码输出
* PB0,PB1用于数码管位码输出
*
* 2020/7/2第2次修改:加入LED的驱动
* PA0--PA8,PA11用于交通灯显示
* PA15状态显示,亮:可修改当前计数值
*
* 2020/7/2第3次修改:加入HC595驱动,使用HC595驱动数码管显示------没实现,有BUG
* HC595的连接 DIO--PA12,SCLK---PA14,RCLK---PA13
*
* 2020/7/2第3次修改:加入外部中断按键
* PE2---KEY_INT PE3----KEYCOM
* 2020/7/2第4次修改,中断中实现计数的加减
* 2020/7/2第4次修改,所有初始化函数的相关引脚进行了宏定义,完善相关注释
* 2020/7/3第5次修改,增加了TM1638驱动动程序
* STB--PB12,CLK--PB11,DIO--PB14
*
* 2020/7/3第6次修改,增加了LED交通灯驱动程序
* 2020/7/3第7次,最后一次修改
* 2020/7/6第8次,把显示程序调到了定时器3中断中,实现通过变量a来延长支干放行时间
* 2020/7/6第9次,使用从洋桃电子学到的方法----MENU菜单切换的方法,来优化
* 2020/7/6第10次 实现通过变量a,b来延长主干,支干放行时间
* 2020/7/7第11次 实现通过按键0---3来实现主干,支干放行时长和紧急情况显示
* 按键0---3分别对应PB0---PB3
* 交换了LEDB_G0,LEDB_R0的引脚
* 2020/7/8第12次 修复了595驱动程序,实现595驱动数码管显示
********************************************************************************
**********************************引脚连接图*************************************
基于普中HC6800开发板的交通模块:
* * * * * * * *
LEDQ_R0 LEDQ_G0 LEDB_R1 LEDB_Y1 LEDB_G1 LEDQ_R1 LEDQ_Y1 LEDQ_G1
PA0 PA1 PA7 PA8 PA11 PA2 PA3 PA4
*
LEDB_G0
PA5
*
LEDB_R0
PA6
********************************************************************************
*/
#include "..\User\tm1638\tm1638.h"
#include "stm32f10x.h"
#include "usart.h"
#include "bsp_led.h"
#include "delay.h"
#include "bsp_BasicTim.h"
#include "bsp_key.h"
#include "hc595.h"
void In_Urgency(void);
void Out_Urgency(void);
void dispaly_xx(u8 num);
u8 count=0; //初始化过程中会产生一次中断,所以上电瞬间显示34
u8 a=0; //主干放行时间延长,主干绿灯时间延长
u8 b=0; //支干放行时间延长,支干绿灯时间延长
u8 MENU=0;
int main(void)
{
USART1_Init(9600);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
LED_GPIO_Config();
KEY_GPIO_Config();
HC595_Config();
TIM3_Int_Init(9999,7199);
MENU=0;
while(1)
{
if(MENU==0) //----------------------------------------------------------------------------MENU=0时,主干到通行
{
In_Urgency(); //黄灯等待切换状态,5秒---------------------------[0,5]
if(count>=(30+a)){
LEDALLCLOSE();
//--黄灯阶段--//
LEDQ_R0(1);//前进路人行道红灯亮
LEDQ_Y1(1);//前进路黄灯亮
LEDQ_G0(0);
LEDQ_R1(0);
LEDQ_G1(0);
LEDB_R0(1);//宝华路人行道红灯亮
LEDB_R1(1);//宝华路红灯亮
LEDB_G0(0);
LEDB_G1(0);
LEDB_Y1(0);
}else{
LEDALLCLOSE(); //前进路通行----------------------[6,35]
LEDQ_G1(1);//前进路绿灯亮
LEDQ_R0(1);//前进路人行道红灯亮
LEDQ_G0(0);
LEDQ_R1(0);
LEDQ_Y1(0);
LEDB_R1(1);//宝华路红灯亮
LEDB_G0(1);//宝华路人行道绿灯亮
LEDB_R0(0);
LEDB_G1(0);
LEDB_Y1(0);
}
dispaly_xx(35+a);
if(count==(35+a)){ MENU=1;count=0;}
if(READKEY3==0){
delay_ms(10); //按键2,切换到MENU=2,设置主干放行时长
if(READKEY3==0)
{ MENU=2;}
}
}
if(MENU==1) //----------------------------------------------------------------------------MENU=1时,支干到通行
{
In_Urgency(); //黄灯等待切换状态,5秒-------------------------------[0,5]
if(count>=(20+b)) {
LEDALLCLOSE();
//--宝华路黄灯阶段--//
LEDQ_R0(1);//前进路人行道红灯亮
LEDQ_R1(1);//前进路红灯亮
LEDQ_G0(0);
LEDQ_Y1(0);
LEDQ_G1(0);
LEDB_Y1(1);//宝华路黄灯亮
LEDB_R0(1);//宝华路人行道红灯亮
LEDB_G0(0);
LEDB_G1(0);
LEDB_R1(0);
}else{ //---------------------------------[6,25]
LEDALLCLOSE();
//宝田路通行
LEDQ_R1(1);//前进路红灯亮
LEDQ_G0(1);//前进路人行道绿灯亮
LEDQ_R0(0);
LEDQ_Y1(0);
LEDQ_G1(0);
LEDB_G1(1);//宝华路绿灯亮
LEDB_R0(1);//宝华路人行道红灯亮
LEDB_G0(0);
LEDB_R1(0);
LEDB_Y1(0);
}
dispaly_xx(25+b);
if(count==25+b){ MENU=0;count=0;}
if(READKEY3==0){
delay_ms(10); //按键2,切换到MENU=2,设置主干放行时长
if(READKEY3==0)
{MENU=3;}
}
} //MENU==1
if(MENU==2) //--------------------------------------------------------------------------------MENU=2时,延长主干道放行时长
{
printf("1\r\n");
if( READKEY1==0&&a!=0) //按键1,a--
{
while( READKEY1==0&&a!=0);
a--;
}
//实现闪烁的效果
In_Urgency();
HC595_display(0,20);
delay_ms(200);
HC595_display(1,20);
delay_ms(200);
HC595_display(0,(35+a)/10);
delay_ms(200);
HC595_display(1,(35+a)%10);
delay_ms(200);
if(READKEY2==0)
{ //按键2,a++
while(READKEY2==0);
a++;
}
delay_us(100);
if(READKEY3==0)
{ //按键0,切换到MENU=0
while(READKEY3==0);
MENU=0;
count=0;
}
}
if(MENU==3) //--------------------------------------------------------------------------------MENU=3时,延长支干道放行时长
{
printf("1\r\n");
if( READKEY1==0&&b!=0) //按键1,b--
{
while( READKEY1==0){}
b--;
}
In_Urgency(); //实现闪烁的效果
HC595_display(0,(25+b)/10);
delay_ms(200);
HC595_display(1,(25+b)%10);
delay_ms(200);
HC595_display(0,20);
delay_ms(200);
HC595_display(1,20);
delay_ms(200);
if(READKEY2==0){ //按键2,b++
while( READKEY2==0){}
b++;
}
if(READKEY3==0){ //按键0,切换到MENU=1
while(READKEY3==0);
MENU=1;
count=0;
}
}
//*********************************************紧急情况程序
if(MENU==4)
{
Out_Urgency();
HC595_display(0,0);
delay_ms(500);
HC595_display(1,0);
delay_ms(500);
HC595_display(0,20);
delay_ms(500);
HC595_display(1,20);
delay_ms(500);
LEDALLOPEN();
}
} //while
}
void In_Urgency(void)
{
if( READKEY0==0)
{
while( READKEY0==0){}
MENU=4;
}
}
void Out_Urgency(void)
{
if( READKEY0==0)
{
delay_ms(10);
while( READKEY0==0){}
MENU=0;
}
}
void dispaly_xx(u8 num)
{
HC595_display(0,((num)-count)/10);
HC595_display(1,((num)-count)%10);
}
//定时器3中断服务程序
void TIMx_IRQnHandler(void) //TIM3中断
{
if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查TIM3更新中断发生与否
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx更新中断标志
count++;
}
}
4.实验总结
(1)主函数设计的思路:把各个功能划分为各个模块单独实现,再设置一个菜单状态变量MENU,用于把各个功能模块串联起来。
当MENU=0时先执行主干道的交通显示,执行完后使MENU=1,跳出该段菜单。
当MENU=1时再执行支干道的交通显示,执行后使MENU=0,跳出该段菜单。
在MENU=0的功能模块中加入按键检测程序,当检测到KEY2时MENU=3,当检测到KEY0时MENU=5。
在MENU=1的功能模块中加入按键检测程序,当检测到KEY2时MENU=4,当检测到KEY0时MENU=5。
当MENU=3时执行主干道放行时间延时程序,用KEY3,KEY4设置加减。
当MENU=4时执行支干道放行时间延时程序,用KEY3,KEY4设置加减。
当MENU=5时执行紧急情况程序。
(2)74HC595驱动芯片总结:
HC595是串口转并口的芯片,串口是8位的,也是就HC595一次能同时输出8位二进制,设计程序时一次只能传输8位的变量,若要传输16位或32位变量则要分为2次或4次传输,由芯片手册得到在SCK引脚上升沿时SDI可传输一位数据,程序设计时可先将SCK引脚拉低,用if语句判断8位变量的最高位,若为1则拉高SDI引脚,若为0则拉低SDI引脚,判断后把SCK引脚拉高,形成上升沿,则传输了一位数据,然后8位变量的次高位左移一位在进行新轮的传输,用for语句循环8次,便可传输8位变量,同时HC595芯片内部已经存储满了,若再传输一位就会从Q7’引脚溢出。若是把LCK引脚从低拉高,则8位变量就会从HC595芯片的Q0~Q7引脚输出。
若是要同时输出16位的数据则需要HC595级联如图所示:
级联后当一片HC595存储满后,再传输一位,前一片的最高位就会传输到下一片HC595芯片,当两片HC595都存储满时,LCK引脚从低电平拉成高电平,就可以同时输出16位的二进制数了。 数码管的位选码先传输到U2中,数码管的段选码再传输到U2,此时数码管的位选码会被溢出到U3,此时LCK引脚从低电平拉成高电平,就可点亮数码管。
资料链接:
链接:https://pan.baidu.com/s/1k8gQtofUaEwcVto0nmmIRQ
提取码:sxmh