前言
串口是经常要使用的模块,比如利用串口中断进行通讯,或者打印字符串,此处只说一些细节。
串口重定向
1.重定向是什么意思呢?我们知道printf函数打印信息默认输出设备是显示器,而不是串口,我们通过printf重定向就可以将printf打印的信息输出在串口,方便上位机调试。
2.要实现printf重定向,即重写printf内部调用的fputc这个库函数,下面是usart1重定向的源码
int fputc(int ch,FILE *f)
{
USART_SendData(USART1,(uint8_t) ch);
while (USART_GetFlagStatus(USART1,USART1_FLAG_TC) != SET);
return (ch);
}
3.如果不想使用printf,想自定义格式化输出函数,可以参考下面代码
//串口发送缓冲区
__align(8) u8 USART_BT_TX_BUF[USART_BT_MAX_SEND_LEN]; //发送缓冲,最大USART3_MAX_SEND_LEN字节
void u3_printf(char* fmt,...) //括号内表示可变参,多个可变参组成一个可变参数列表
{
u16 i,j;
va_list ap; //初始化指向可变参数列表的指针
va_start(ap,fmt); //将第一个可变参数的地址付给ap,即 ap指向可变参数列表的开始
vsprintf((char*)USART_BT_TX_BUF,fmt,ap); // 将参数fmt,ap指向的可变参数一起转换成格式化字符串,存入USART_BT_TX_BUF数组;作用同sprintf,只是参数类型不同
va_end(ap); //没有任何意义,只是为了程序健壮性
i=strlen((const char*)USART_BT_TX_BUF);
for(j=0;j
{
while(USART_GetFlagStatus(USART_BT,USART_FLAG_TC)==RESET);
USART_SendData(USART_BT,(uint8_t)USART_BT_TX_BUF[j]);
}
}
4.当链接器发现用户写的函数和库函数名字相同时,会优先执行用户自定义的函数。
清标志位
void USART1_IRQHandler(void) //stm32中断服务程序的名字一般都是固定的,在启动文件里有定义
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //查看相应的标志位有没有置位
{
USART_ClearITPendingBit(USART1,USART_IT_RXNE); //清中断标志位
/****************处理语句******************************/
}
}
说明:有些中断标志位只要读取数据或者访问某些寄存器就会自动清除,比如串口的发送接收完成,发送缓冲区空等。但是大部分的中断标记需要手动清除,比如定时器,所以保险起见的话都加上清标志位语句。(读不一定是软件读,打开外设寄存器窗口观察寄存器的值也算是读,至少串口是这样)
串口数据流向
1.数据接收流程
RXD–》移位寄存器–》RDG(接收数据缓冲器)–》内核
2.数据发送流程
内核–》TDG(输出数据缓冲器)–》移位寄存器–》TXD
HAL库串口中断使用
如果使用的是CUBE库来进行软件开发的话,CUBEMX配置好之后,使用串口中断只需要两步
1.串口初始化函数里添加如下代码,打开串口中断
void MX_USART1_UART_Init(void)
{
if(HAL_UART_Receive_IT(&UART1Handle, Rxbuf, 3) != HAL_OK)
{
Error_Handler();
}
}
2.重载串口接收中断的CallBack函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
HAL_UART_Transmit_IT(&UART1Handle,Rxbuf,3);
if(HAL_UART_Receive_IT(&UART1Handle, Rxbuf,3) != HAL_OK) //由于中断处理流程里面会进去一次就关掉,所以要重新打开
{
Error_Handler();
}
}
说明
(1)HAL_UART_Receive_IT
这个函数的作用主要是传参和打开相应的中断,并不是真正的发送接收函数
(2)中断处理流程相关函数
USART1_IRQHandler >> HAL_UART_IRQHandler >> UART_Receive_IT >>HAL_UART_RxCpltCallback
(3)发送中断的流程同上
前言
串口是经常要使用的模块,比如利用串口中断进行通讯,或者打印字符串,此处只说一些细节。
串口重定向
1.重定向是什么意思呢?我们知道printf函数打印信息默认输出设备是显示器,而不是串口,我们通过printf重定向就可以将printf打印的信息输出在串口,方便上位机调试。
2.要实现printf重定向,即重写printf内部调用的fputc这个库函数,下面是usart1重定向的源码
int fputc(int ch,FILE *f)
{
USART_SendData(USART1,(uint8_t) ch);
while (USART_GetFlagStatus(USART1,USART1_FLAG_TC) != SET);
return (ch);
}
3.如果不想使用printf,想自定义格式化输出函数,可以参考下面代码
//串口发送缓冲区
__align(8) u8 USART_BT_TX_BUF[USART_BT_MAX_SEND_LEN]; //发送缓冲,最大USART3_MAX_SEND_LEN字节
void u3_printf(char* fmt,...) //括号内表示可变参,多个可变参组成一个可变参数列表
{
u16 i,j;
va_list ap; //初始化指向可变参数列表的指针
va_start(ap,fmt); //将第一个可变参数的地址付给ap,即 ap指向可变参数列表的开始
vsprintf((char*)USART_BT_TX_BUF,fmt,ap); // 将参数fmt,ap指向的可变参数一起转换成格式化字符串,存入USART_BT_TX_BUF数组;作用同sprintf,只是参数类型不同
va_end(ap); //没有任何意义,只是为了程序健壮性
i=strlen((const char*)USART_BT_TX_BUF);
for(j=0;j
{
while(USART_GetFlagStatus(USART_BT,USART_FLAG_TC)==RESET);
USART_SendData(USART_BT,(uint8_t)USART_BT_TX_BUF[j]);
}
}
4.当链接器发现用户写的函数和库函数名字相同时,会优先执行用户自定义的函数。
清标志位
void USART1_IRQHandler(void) //stm32中断服务程序的名字一般都是固定的,在启动文件里有定义
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //查看相应的标志位有没有置位
{
USART_ClearITPendingBit(USART1,USART_IT_RXNE); //清中断标志位
/****************处理语句******************************/
}
}
说明:有些中断标志位只要读取数据或者访问某些寄存器就会自动清除,比如串口的发送接收完成,发送缓冲区空等。但是大部分的中断标记需要手动清除,比如定时器,所以保险起见的话都加上清标志位语句。(读不一定是软件读,打开外设寄存器窗口观察寄存器的值也算是读,至少串口是这样)
串口数据流向
1.数据接收流程
RXD–》移位寄存器–》RDG(接收数据缓冲器)–》内核
2.数据发送流程
内核–》TDG(输出数据缓冲器)–》移位寄存器–》TXD
HAL库串口中断使用
如果使用的是CUBE库来进行软件开发的话,CUBEMX配置好之后,使用串口中断只需要两步
1.串口初始化函数里添加如下代码,打开串口中断
void MX_USART1_UART_Init(void)
{
if(HAL_UART_Receive_IT(&UART1Handle, Rxbuf, 3) != HAL_OK)
{
Error_Handler();
}
}
2.重载串口接收中断的CallBack函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
HAL_UART_Transmit_IT(&UART1Handle,Rxbuf,3);
if(HAL_UART_Receive_IT(&UART1Handle, Rxbuf,3) != HAL_OK) //由于中断处理流程里面会进去一次就关掉,所以要重新打开
{
Error_Handler();
}
}
说明
(1)HAL_UART_Receive_IT
这个函数的作用主要是传参和打开相应的中断,并不是真正的发送接收函数
(2)中断处理流程相关函数
USART1_IRQHandler >> HAL_UART_IRQHandler >> UART_Receive_IT >>HAL_UART_RxCpltCallback
(3)发送中断的流程同上
举报