前言
本文章记录了博主嵌入式课的作业,基于上一个作业,这篇没提到的可以看上一篇的内容。
其也可以参考到其它项目中去,比如一些多个按键中断、定时器的使用中。
一.作业内容
设计游泳竞赛冲线检测系统,要求:设计8个泳道,编号为1——8。每个泳道设置一个冲线触摸传感器(可以用开关模拟),非触摸状态时输出低电平,触摸状态输出高电平。
- 设置2个OLED显示屏,用来显示排名:其第一行第一位显示排列顺序为1-8名冲刺泳道,第三位开始分别显示1位泳道号(1—8)、2位分钟(00—59)、2位秒(00—59)、3位毫秒(000—999。
- 同时设置一个LCD1602显示器,显示当前比赛时间:2位分钟(00—59)、2位秒(00—59)。
- 当比赛发令时启动定时器计时,运动员选手冲线时,读取定时器当前时间,按其冲线触摸传感器的顺序在对应的LED显示其泳道编号和冲线时间。
二.效果仿真
定时器应用
三.电路原理图设计及说明
按键
选用8个按键模拟冲线触摸传感器,8个LED指示灯对应8个泳道,当运动员选手冲线时(开关按下高电平)对应泳道的LED指示灯亮。
再加一个按键当作比赛发令,按下计时器开始计时比赛开始。
设置PA口为下拉输入模式,默认状态为低电平0,按下按键时PA0-PA7口与VCC相连,其状态由低电平0变为高电平1,触发中断,中断里先延时消抖再读取按键的IO(PA)电平状态判断按键是否按下,从而控制PA8-PA15口输出低电平实现控制LED灯亮和OLED显示排名、泳道号和时间。
LCD
LCD ( Liquid Crystal Display 的简称)液晶显示器。能够同时显示16x2,32个字符,是一种专门用来显示字母、数字、符号等的点阵型液晶模块。
LCD1602液晶显示器是广泛使用的一种字符型液晶显示模块。它是由字符型液晶显示屏(LCD)、控制驱动主电路HD44780及其扩展驱动电路HD44100,以及少量电阻、电容元件和结构件等装配在PCB板上而组成。该显示屏的优点是耗电量低、体积小、辐射低。
LCD1602主要用来显示数字、字母、图形以及少量自定义字符。可以显示2行16个字符,拥有16个引脚,其中8位数据总线D0-D7,和RS、R/W、EN三个控制端口,工作电压为5V,并且带有字符对比度调节V0和背光源AK。
接线方式用连线标号模式,可以让电路仿真图更加清晰明了。
- 其中8个按键连接PA0-PA7;
- LED连接PA8-PA15;
- 一个比赛发令按键连接PC8;
- LCD1602的RS、R/W、EN分别接PB0、PB1和PB2,数据端D0-D7连接PC0-PC7;
- OLED的SCL、SDA分别接PB10和PB11,OLED的SCL、SDA分别接PB6和PB7;
初始化
四.软件流程图
五.主要程序代码
中断初始化与上一篇相同,不再赘述
中断服务函数有所改变,在中断服务函数里显示OLED和定时器计时
uint16_t a=0;//变量a就是个排序的作用
void ShowInOled(int number){
a++;
OLED_ShowNum(a, 3, number, 1);
OLED_ShowNum(a, 5, min, 2);
OLED_ShowNum(a, 8, second, 2);
OLED_ShowNum(a,11,TIM_GetCounter(TIM2),3);
}
void ShowInOled_1(int number){
a++;
OLED1_ShowNum(a-4, 3, number, 1);
OLED1_ShowNum(a-4, 5, min, 2);
OLED1_ShowNum(a-4, 8, second, 2);
OLED1_ShowNum(a-4,11,TIM_GetCounter(TIM2),3);
}
//配置中断服务函数
void EXTI0_IRQHandler(void)
{
Delay_ms(20);
if (GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_8);
if(a<=3) ShowInOled(1);
else ShowInOled_1(1);
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
//中断1
void EXTI1_IRQHandler(void)
{
Delay_ms(20);
if (GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)==1)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_9);
if(a<=3) ShowInOled(2);
else ShowInOled_1(2);
}
EXTI_ClearITPendingBit(EXTI_Line1);
}
//中断2
void EXTI2_IRQHandler(void)
{
Delay_ms(20);
if (GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2)==1)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_10);
if(a<=3) ShowInOled(3);
else ShowInOled_1(3);
}
EXTI_ClearITPendingBit(EXTI_Line2);
}
//中断3
void EXTI3_IRQHandler(void)
{
Delay_ms(20);
if (GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3)==1)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_11);
if(a<=3) ShowInOled(4);
else ShowInOled_1(4);
}
EXTI_ClearITPendingBit(EXTI_Line3);
}
//中断4
void EXTI4_IRQHandler(void)
{
Delay_ms(20);
if (GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4)==1)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_12);
if(a<=3) ShowInOled(5);
else ShowInOled_1(5);
}
EXTI_ClearITPendingBit(EXTI_Line4);
}
//中断5-9
void EXTI9_5_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line5)==SET)
{
Delay_ms(20);
if (GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)==1)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_13);
if(a<=3) ShowInOled(6);
else ShowInOled_1(6);
EXTI_ClearITPendingBit(EXTI_Line5);
}
}
if(EXTI_GetITStatus(EXTI_Line6)==SET)
{
Delay_ms(20);
if (GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6)==1)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_14);
if(a<=3) ShowInOled(7);
else ShowInOled_1(7);
EXTI_ClearITPendingBit(EXTI_Line6);
}
}
if(EXTI_GetITStatus(EXTI_Line7)==SET)
{
Delay_ms(20);
if (GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_7)==1)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_15);
if(a<=3) ShowInOled(8);
else ShowInOled_1(8);
EXTI_ClearITPendingBit(EXTI_Line7);
}
}
//开始计时按键
if(EXTI_GetITStatus(EXTI_Line8)==SET)
{
Delay_ms(20);
if (GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_8)==1)
{
TIM_Cmd(TIM2, ENABLE);
EXTI_ClearITPendingBit(EXTI_Line7);
}
}
}
定时器的初始化和服务函数
在服务函数里开启LCD的计时
uint16_t second;
uint16_t min;
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟
TIM_InternalClockConfig(TIM2); //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
TIM_TimeBaseInitStructure.TIM_Period = 1000 - 1; //计数周期,即ARR的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //预分频器,即PSC的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除定时器更新标志位
//TIM_TimeBaseInit函数末尾,手动产生了更新事件
//若不清除此标志位,则开启中断后,会立刻进入一次中断
//如果不介意此问题,则不清除此标志位也可
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //开启TIM2的更新中断
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //选择配置NVIC的TIM2线
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStructure);
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
second ++;
if(second>=60)
{
second=0;
min ++;
}
LCD1602_Show_Num(5,2,min);
LCD1602_Show_Str(7,1,":");
LCD1602_Show_Num(8,2,second);
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
最后
以上就是作业的主要内容。
关于中断分组、定时器的配置可以参考其它博主的文章,他们讲的更加详细;
本人只是提供给已经有些基础的同学的思路。
希望能够帮到你们,如果有帮助就点个赞吧!
(如果还是不懂,可以私聊博主有偿提供源代码程序和仿真。)