MCU:STM32F103ZET6
IDE: MDK-ARM V5 +STM32CubeMX5.2.1
原子哥f429教程学习
一、打开Cube,建立工程
图片:
点击ACCESS project from MCU
然后选择芯片类型
图片:
二、系统配置
在 Pinout&Configuration—System Core中:
设置时钟RCC的HSE(外部高速时钟)为晶振模式:
Crystal/ceramic Resonator:
设置系统SYS的Debug为Serial Wire(SWD调试):
三、配置串口
在 Pinout&Configuration—Connectivity中:
打开UART1,配置工作模式,在NVIC选项中勾选使能
四、在 Clock Configuration中:
配置时钟为72 Mhz。
五、工程输出配置
Tips:最好把Linker Settings中的Minimum Heap Size设置为0x600。
最后点击GENERATE CODE代码就生成了:
至此,一个工程就创建完了。
六、代码部分处理
printf重定义
在usart.c文件中添加如下代码
/* USER CODE BEGIN 1 */
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
* @brief? Retargets the C library printf function to the USART.
* @param? None
* @retval None
*/
PUTCHAR_PROTOTYPE
{
/* Place your implementation of fputc here */
/* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
/* USER CODE END 1 */
以上步骤参考帖https://blog.youkuaiyun.com/weixin_42108484/article/details/84578414.
相关函数
串口使能和关闭方法
__HAL_UART_ENABLE(handler); //使能句柄 handler 指定的串口
__HAL_UART_DISABLE(handler); //关闭句柄 handler 指定的串口
串口中断开启和关闭函数
* This parameter can be one of the following values:
* @arg UART_IT_CTS: CTS change interrupt
* @arg UART_IT_LBD: LIN Break detection interrupt
* @arg UART_IT_TXE: Transmit Data Register empty interrupt
* @arg UART_IT_TC: Transmission complete interrupt
* @arg UART_IT_RXNE: Receive Data register not empty interrupt
* @arg UART_IT_IDLE: Idle line detection interrupt
* @arg UART_IT_PE: Parity Error interrupt
* @arg UART_IT_ERR: Error interrupt(Frame error, noise error, overrun error)
__HAL_UART_ENABLE_IT(huart,UART_IT_RXNE); //开启接收完成中断
__HAL_UART_DISABLE_IT(huart,UART_IT_RXNE); //关闭接收完成中断
串口发送函数
HAL_UART_Transmit();
七、代码分析
打开usart.c文件
发现有三个函数
void MX_USART1_UART_Init(void)
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
- 函数 HAL_UART_Init 主要用来初始化与串口相关的参数(这些参数与 MCU 无关),包括波特率,停止位等;
- 而串口 MSP 函数 HAL_UART_MspInit 用来设置 GPIO 初始化,NVIC 配置等于 MCU 相关的配置;
- 串口 MSP 函数 HAL_UART_MspDeInit 则与HAL_UART_MspInit 功能相反,失能初始化。
HAL 库定义了一个串口中断处理通用函数 HAL_UART_IRQHandler,该函数声明如下:
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);
该函数一般在中断服务函数中调用,作为串口中断处理的通用入口。一般调用方法为:
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&UART1_Handler); //调用 HAL 库中断处理公用函数
…//中断处理完成后的结束工作
}
也就是说,真正的串口中断处理逻辑我们会最终在函数HAL_UART_IRQHandler内部执行。而该函数是 HAL 库已经定义好,而且用户一般不能随意修改。那么我们的中断控制逻辑编写在哪里呢?
我们就以接收中断为例
我们打开函数 HAL_UART_IRQHandler 源码
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
uint32_t tmp1 = 0, tmp2 = 0;
…//此处省略部分代码
tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_RXNE);
tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_RXNE);
if((tmp1 != RESET) && (tmp2 != RESET))
{
UART_Receive_IT(huart);
}
…//此处省略部分代码
}
从代码逻辑可以看出,在函数 HAL_UART_IRQHandler 内部通过判断中断类型是否为接收完成中断,确定是否调用 HAL 另外一个函数UART_Receive_IT()。函数 UART_Receive_IT()的作用是把每次中断接收到的字符保存在串口句柄的缓存指针 pRxBuffPtr 中,同时每次接收一个字符,其计数器 RxXferCount 减 1,直到接收完成 RxXferSize 个字符之后 RxXferCount 设置为0,同时调用接收完成回调函数 HAL_UART_RxCpltCallback 进行处理。
UART_Receive_IT()源码如下
static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
{
...//此处省略部分代码
if(--huart->RxXferCount == 0)
{
HAL_UART_RxCpltCallback(huart);
}
...//此处省略部分代码
}
我们可以发现UART_Receive_IT()函数也是通过判断选需要的回调函数,而这些回调函数都是weak定义的,也就是说我们需要用哪个就定义哪个
HAL 库一共提供了 5 个中断处理回调函数:
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);//发送完成回调函数
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart);//发送完成过半
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);//接收完成回调函数
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);//接收完成过半
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart);//错误处理回调函数
对于串口接收中断,一般流程为
具体操作
- 在MX_USART1_UART_Init()中调用完 HAL_UART_Init后还调用HAL_UART_Receive_IT开启接收中断,并且初始化串口句柄的缓存相关参数。 代码如下:
aRxBuffer 是我们定义的一个全局数组变量,RXBUFFERSIZE 是我们定义的一个标识符:(定义在usart.h中)HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE);
#define RXBUFFERSIZE 1
u8 aRxBuffer[RXBUFFERSIZE];
调用 HAL_UART_Receive_IT 函数后,除了开启接收中断外还确定了每次接收RXBUFFERSIZE 个字符后标示接收结束从而进入回调函数HAL_UART_RxCpltCallback 进行相应处理。
- 编写HAL_UART_RxCpltCallback和USART1_IRQHandler :
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance==USART1)//如果是串口1
{
if((USART_RX_STA&0x8000)==0)//接收未完成
{
if(USART_RX_STA&0x4000)//接收到了0x0d
{
if(aRxBuffer[0]!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D
{
if(aRxBuffer[0]==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=aRxBuffer[0] ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
}
//串口1中断服务程序
void USART1_IRQHandler(void)
{
u32 timeout=0;
u32 maxDelay=0x1FFFF;
HAL_UART_IRQHandler(&UART1_Handler); //调用HAL库中断处理公用函数
timeout=0;
while (HAL_UART_GetState(&UART1_Handler) != HAL_UART_STATE_READY)//等待就绪
{
timeout++;超时处理
if(timeout>maxDelay) break;
}
timeout=0;
while(HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)//一次处理完成之后,重新开启中断并设置RxXferCount为1
{
timeout++; //超时处理
if(timeout>maxDelay) break;
}
}
因为我们设置了串口句柄成员变量 RxXferSize 为 1,也就是每当串口 1 发生了接收完成中断后(接收到一个字符),就会跳到该函数执行。当串口接受到一个字符后,它会保存在缓存aRxBuffer 中,由于我们设置了缓存大小为 1,而且 RxXferSize=1,所以每次接受一个字符,回直接保存到RxXferSize[0]中,我们直接通过读取 RxXferSize[0]的值就是本次接收到的字符。
总的来说,就是需要你先在串口初始化中开启你需要的中断例如接收中断HAL_UART_Receive_IT(),然后再编写中断回调函数和中断函数。
如果我们不用中断处理回调函数,那么就不用初始化串口句柄的中断接收缓存,所以我们HAL_UART_Receive_IT 函数就不用出现在串口初始化函数中而是直接在要开启中断的地方通过调用__HAL_UART_ENABLE_IT 单独开启中断即可。如果不用中断回调函数处理,中断服务函数内容为:
/*下面代码我们直接把中断控制逻辑写在中断服务函数内部。
说明:采用HAL库处理逻辑,效率不高。*/
//串口1中断服务程序
void USART1_IRQHandler(void)
{
u8 Res;
if((__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_RXNE)!=RESET)) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
HAL_UART_Receive(&UART1_Handler,&Res,1,1000);
if((USART_RX_STA&0x8000)==0)//接收未完成
{
if(USART_RX_STA&0x4000)//接收到了0x0d
{
if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D
{
if(Res==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
HAL_UART_IRQHandler(&UART1_Handler);
}