【蓝桥嵌入式笔记】第一个工程 HAL_01_LED_TEST
一、ODR、BSRR、BRR寄存器
1.ODR
可读可写寄存器:既能控制管脚为高电平,也能控制管脚为低电平。
管脚对于位写1 GPIO管脚为高电平,写 0 为低电平。
2.BSRR
只写寄存器:既能控制管脚为高电平,也能控制管脚为低电平。
对寄存器高 16bit 写1 对应管脚为低电平,对寄存器低16bit写1对应管脚为高电平。写 0 无动作。
3.BRR
只写寄存器:只能改变管脚状态为低电平,对寄存器 管脚对于位写 1 相应管脚会为低电平。写 0 无动作。
二、HAL库函数
1.void HAL_GPIO_TogglePin (GPIO_TypeDef * GPIOx, uint16_t GPIO_Pin)
翻转所选引脚的状态
void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
/* Check the parameters */
assert_param(IS_GPIO_PIN(GPIO_Pin)); //检查参数是否有效
if ((GPIOx->ODR & GPIO_Pin) != 0x00u)//判断当前引脚状态与所选引脚位是否为0
{
GPIOx->BRR = (uint32_t)GPIO_Pin; //如果为1,则通过BRR置0
}
else
{
GPIOx->BSRR = (uint32_t)GPIO_Pin;//如果为0,则通过BSRR置1
}
}
ODR是引脚输出值,与所选引脚进行与操作,若当前状态为1,则通过BRR置0;若当前状态为0,则通过BSRR置1。
2.void HAL_GPIO_WritePin (GPIO_TypeDef * GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
将所选引脚置0或置1
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
{
/* Check the parameters */
assert_param(IS_GPIO_PIN(GPIO_Pin));
assert_param(IS_GPIO_PIN_ACTION(PinState));
if (PinState != GPIO_PIN_RESET)
{
GPIOx->BSRR = (uint32_t)GPIO_Pin;
}
else
{
GPIOx->BRR = (uint32_t)GPIO_Pin;
}
}
思考:
既然ODR 能控制管脚高低电平为什么还需要BSRR和BRR寄存器?
意法半导体给的答案是:
“This way, there is no risk that an IRQ occurs between the read and the modify access.”
什么意思呢?就就是你用BSRR和BRR去改变管脚状态的时候,没有被中断打断的风险。也就不需要关闭中断。
用ODR操作GPIO的伪代码如下:
disable_irq()
save_gpio_pin_state = read_gpio_pin_state();
save_gpio_pin_state = xxxx;
chang_gpio_pin_state(save_gpio_pin_sate);
enable_irq();
关闭中断明显会延迟或丢失一事件的捕获,所以控制GPIO的状态最好还是用BSRR和BRR
3.HAL_Delay(DELAY_TIME);
ms延时函数,弱函数,能重定义(比赛的时候直接用就可以,不必修改),参数的单位为毫秒,使用的时钟为SysTick。
__weak void HAL_Delay(uint32_t Delay)
{
uint32_t tickstart = HAL_GetTick();
uint32_t wait = Delay;
/* Add a freq to guarantee minimum wait */
if (wait < HAL_MAX_DELAY)
{
wait += (uint32_t)(uwTickFreq);
// 查看全局变量uwTickFreq的定义,其数值为systick时钟的默认频率(1khz)
}
while ((HAL_GetTick() - tickstart) < wait)
{
}
}
__weak uint32_t HAL_GetTick(void)
{
return uwTick;
}
三、void LED_TEST()函数
while(1){
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_8);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
HAL_Delay(DELAY_TIME);
…
}
问题一:为什么要配置PD2引脚?
如下图所示,板子引脚PD2已经与LE相连,所以需要通过对PD2的配置来对锁存器(74HC573)进行开关操控。
问题二:在翻转函数第一次作用之前,灯的状态是怎样的?
可以通过ODR得知灯的状态,查看ODR上电后的状态(Reset value):0x0000 0000
补充:灯对应引脚的默认值也可以在CubeMx里面修改
问题三:锁存器有什么作用?在这个函数中是如何体现的?
锁存器能够将相连引脚的状态锁住,一旦关闭锁存器,不论引脚的状态如何改变,仍然保留的是关闭锁存器前的状态。
- 当OE为H(高电平)时,无论Input(输入端D0~D7)输入何种电平状态,Output(输出端Q0~Q7))都为Z(高阻态此时芯片处于不可控状态。
- 当OE为L(低电平)时,若LE为H,则D(输入)与Q(输出)同时为L或H(Q端的电平状态紧随着D端变化);而当LE为L时,无论D为何种电平状态,Q都会保持上一次的电平(Q端电平状态将保持住LE端变化为低电平L之前端的电平状态)。
下面我们附上在开发板的74HC573原理图:
在LED_TEST函数中,需要实现灯依次亮并保持到所有的灯全亮后,再依次灭掉,其中保持灯亮或灭这个状态就是通过锁存器实现的。
补充:在CubeMx创建的工程中,代码要写在对应的区域,这样能够防止重新在CubeMx中修改时,已有的代码被刷新掉