本文是对UART外设的加深学习(参考正点原子的教学),文章架构如下:
阅读完本文,要能回答以下问题:
文章最后,列出了一些值得进一步研究的内容,包括对中断函数内部的优化、进程管理等,后续敬请期待。
1、简述串口发送数据的配置流程。
int main(void)
{
u8 buff[]="test";
HAL_Init(); //³õʼ»¯HAL¿â
Stm32_Clock_Init(360,25,2,8); //ÉèÖÃʱÖÓ,180Mhz
delay_init(180);
uart1_init();
while(1)
{
HAL_UART_Transmit(&usart1_handler,buff,sizeof(buff),1000);
delay_ms(3000);
}
}
2、前文已经讲解如何通过HAL_UART_Init()配置串口数据格式,但是却没有注意串口使能、串口发送使能是如何实现的,简要阐述一下。
#define __HAL_UART_ENABLE(__HANDLE__) ((__HANDLE__)->Instance->CR1 |= USART_CR1_UE)
/* Disable the peripheral */
__HAL_UART_DISABLE(huart);
/* Set the UART Communication parameters */
UART_SetConfig(huart);
//......
/* Enable the peripheral */
__HAL_UART_ENABLE(huart);
//通过这里的huart->Init.Mode确定是否使能串口读或写
tmpreg |= (uint32_t)huart->Init.WordLength | huart->Init.Parity | huart->Init.Mode | huart->Init.OverSampling;
//Mode的取值如下
#define UART_MODE_RX ((uint32_t)USART_CR1_RE)
#define UART_MODE_TX ((uint32_t)USART_CR1_TE)
#define UART_MODE_TX_RX ((uint32_t)(USART_CR1_TE |USART_CR1_RE))
3、通过HAL_UART_Transmit()函数进行USART异步发送数据,从该函数接收参数、返回参数的角度简述该函数是如何发送数据的。
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
4、从该函数具体实现何种功能的角度简述该函数是如何发送数据的。
huart->TxXferSize = Size;
huart->TxXferCount = Size;
while(huart->TxXferCount > 0)
{
huart->TxXferCount--;
//...
huart->Instance->DR = (*pData++ & (uint8_t)0xFF);//8位数据位
5、该函数实现发送数据功能,需要UART_HandleTypeDef的成员变量*pTxBuffPtr、TxXferSize、 TxXferCount支持,阐述下这三个变量的功能。
上文介绍串口发送数据时,没有涉及中断的概念。本节在开启接收中断的前提下,研究程序流程。
1、简述串口开启接收中断时接收数据的初始化配置与工作流程。
int main(void)
{
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(360,25,2,8); //设置时钟,180Mhz
delay_init(180);
uart1_init();//初始化串口参数
HAL_UART_Receive_IT(&usart1_handler, (u8 *)rdata, 1);//该函数会开启接收中断,并且设置接收缓冲以及接收最大数据量
while(1)
{
}
}
uart1_init()函数调用HAL_UART_Init(),设置好了串口特性、数据格式,并且对串口以及串口接收数据进行了使能。同时在GPIO复用配置时,使能串口x的中断通道,并且设置中断优先级。中断配置相关概念在中断一文进行说明。
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
GPIO_InitTypeDef GPIO_Initure;
if(huart->Instance==USART1)//配置对应串口
{
//......
HAL_NVIC_EnableIRQ(USART1_IRQn); //使能串口1中断通道
HAL_NVIC_SetPriority(USART1_IRQn,3,3); //设置串口1的中断优先级
}
}
2、配置好串口相关特性后,接下来就是要开启中断,简要阐述开启接收中断的过程。
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
/* Process Locked */
__HAL_LOCK(huart);
huart->pRxBuffPtr = pData;
huart->RxXferSize = Size;
huart->RxXferCount = Size;
/* Process Unlocked */
__HAL_UNLOCK(huart);
/* Enable the UART Data Register not empty Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);
3、这里涉及到锁的概念,对资源池进行保护,防止抢占,基于进程相关概念分析下锁的作用。
4、开启接收中断后,中断函数的内部需要进行哪些操作?
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&usart1_handler);
while (HAL_UART_GetState(&usart1_handler) != HAL_UART_STATE_READY);//µÈ´ý¾ÍÐ÷
while(HAL_UART_Receive_IT(&usart1_handler, (u8 *)rdata, 1) != HAL_OK);
}
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
//......
/* UART in mode Receiver --------------*/
if((tmp1 != RESET) && (tmp2 != RESET))
{
UART_Receive_IT(huart);
}
//......
}
static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
{
//...
*huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);
//......
}
5、为了测试串口是否正确读取到了数据,通常会将读到的数据回调到串口输出,阐述下这一过程。
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
u8 rec;
if(huart->Instance==USART1)//如果串口1
{
rec=*(--(huart->pRxBuffPtr));
HAL_UART_Transmit(&usart1_handler,&rec,1,1000);
}
}
static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
{
//...
*huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);
//......
HAL_UART_RxCpltCallback(huart);
//......
}
在中断函数中使用HAL库执行中断操作并不是一个好的选择,因为在工控领域,中断常常需要高频响应的,这要求中断程序必须足够简洁,如果不够简洁,会造成高优先级中断始终抢占、低优先级中断始终死等。
后续工作包括对中断函数内部的优化、进程管理,有机会再深入研究