一个小阶段下来收获颇丰,经过复盘,整理相关笔记如下。(本早该发布一直拖到了现在)
1. 关于延时效果除了HAL_Delay外常用的方法
在这个阶段中做了一个小项目——楼道灯。
因为还未接触到继电器,所以这个小项目是以代码实现的,配置好相应硬件后开始编写代码,其中要实现一个让灯亮的中断条件,这个问题定义几个变量并判断大于或小于某个值、或是处于某个状态就可解决。
此外,要实现楼道灯亮一定时间后自动熄灭的效果,在此考虑的问题较多。在这里的代码实现里,我们在点灯的时候记录当前时间,然后循环获取当前时间看有没有超时,如果超时则控制关灯。
Part1.
实现这一功能作为初学者的自己开始想到的是直接调用HAL_Delay函数进行直接延时,后边在老师指导下才知道,HAL_Delay函数的确可以在这个项目中达到效果,但其实它是低效的,因为用HAL_Delay函数直接延时的话,程序就不能再做其它的控制,其他操作无法进行了。
Part2.
在判断时间是否到没有,我们不能直接用类似 if( (HAL_GetTick() - start) > 3000 )这样的条件判断( 3000是3000ms的意思 ),因为变量的最大记值是有限制的( uint32_t 最大计数到2^32-1 ),而时间是无限的,这样会存在时间回绕的问题。
【PS: 但在如果给出的问题类似 “ 在 xx ms内实现XX操作” 时就可用 if((HAL_GetTick() - start) > 3000 )这样的条件判断,这时这样的判断语句是最常用的。】
Part3.
这里我们参考Linux内核里的jiffies , 回绕解决方案,使用了time_after()这个宏来判断是否超时。这个宏我们需要添加到main.h头文件中。
楼道灯部分代码:
int main(void)
{
uint32_t lux, noisy;
uint32_t start = 0;
uint8_t light_status = 0;
while (1)
{
if( OFF == light_status)
{
adc_sample_lux_noisy(&lux, &noisy);
printf("Lux[%lu] Noisy[%lu]\r\n", lux, noisy);
if( lux<30 && noisy>800)
{
turn_led(BlueLed, ON);
//turn_relay(Relay2, ON); //继电器
light_status = ON;
/* record current time and will turn off in 3 seconds */
start = HAL_GetTick();
}
}
else
{
if( time_after(HAL_GetTick(), start+3000) )
{
turn_led(BlueLed, OFF);
//turn_relay(Relay2, OFF); //继电器
/* must give delay for turn relay off got noisy , it will affect next
sample */
HAL_Delay(200);
/* set light status as OFF */
light_status = OFF;
}
}
HAL_Delay( 10 ); //在这里可以进行别的希望进行的操作
}
}
2.杂
(1)切换1、0对应状态可相应左右移动,后再进行位运算调整。
(2)datasheet对应用到的需更深研究地进行学习效果更好。
(3)不知道的函数之类的不必死扣,百度关键字搜索。
(4)中断向量表里每一个中断服务处理程序都是4个字节,因为32位cpu字长是4个字节(.word),且要按4个字节来补齐,一个中断4个地址存放,中断号不存在或者无用不能删掉,要补0。
(5)函数返回值类型为_weak 说明该函数是可以被重写的,但_weak一定不能不写。
(6)只需1个位控制的只需低16个管脚,而2个位的需32个管脚。
(7)GPIOx_IDR知道管脚当前是高/低电平(先读这个寄存器再获取这个管脚位的值,获取位值:右移当前要看的管脚位,然后做相应取反,看最后输出是1是0),GPIOx_ODR设置一个管脚输出高/低电平(如果设置高电平就用1 或上 1的左移当前管脚序号;低的话就先重复设置高电平的操作,然后再),GPIOx_BSRR控制某一个管脚。(其他寄存器去看datasheet)