141 lines
6.0 KiB
Markdown
141 lines
6.0 KiB
Markdown
|
---
|
|||
|
title: SMT32串口接收、空闲中断
|
|||
|
tags:
|
|||
|
- 串口(UART)
|
|||
|
categories:
|
|||
|
- STM32
|
|||
|
comments: true
|
|||
|
abbrlink: 977ab4d9
|
|||
|
date: 2018-03-01 15:20:12
|
|||
|
images:
|
|||
|
---
|
|||
|
## 识别尾进行接收结束的验证
|
|||
|
1. 串口接收完毕标志可以利用识别特定字符(字符串)来检测,比如 "\r\n"、'\*#' 之类的。所以每次发数据都需要加上这些字符才能被识别为接收完毕,这样好处就是比较通用。无论是什么硬件平台都能用。代码也不复杂,在STM32平台上简单的例子如下代码段:当接收到 '\*' 时候就会置位接收完成标志位,就可以进行处理了。
|
|||
|
``` C
|
|||
|
#define MAXUSART1BUFSIZE 200
|
|||
|
uint8_t Uart1_BUF[MAXUSART1BUFSIZE];
|
|||
|
void USART1_IRQHandler(void) //串口1中断服务程序
|
|||
|
{
|
|||
|
uint8_t RecByte;
|
|||
|
static uint32_t pos = 0;
|
|||
|
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
|
|||
|
{
|
|||
|
RecByte = USART1->DR;//读取接收到的数据
|
|||
|
if(pos < MAXUSART1BUFSIZE)//防止内存溢出
|
|||
|
Uart1_BUF[pos++] = RecByte;
|
|||
|
if(RecByte == '*')
|
|||
|
{
|
|||
|
//TODO: 接收完毕相关处理
|
|||
|
pos = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
<!---more--->
|
|||
|
## 识别头尾进行数据段的保护
|
|||
|
2. 上面的方法就是发送的时候麻烦一点,每次都需要在后面加 '\*', 并且如果发送错一次(忘记加 '*')就会把缓冲区的内容累计到下一次(可以添加头识别进行解决)。这样就只会识别指定的数据段。一般这样适用于发送带有意义的控制数据保证数据不多收也不少收。验证的字符要保证不会出现在内容里,或者用多个字符进行验证。
|
|||
|
```c
|
|||
|
#define MAXUSART1BUFSIZE 200
|
|||
|
uint8_t Uart1_BUF[MAXUSART1BUFSIZE];
|
|||
|
void USART1_IRQHandler(void) //串口1中断服务程序
|
|||
|
{
|
|||
|
uint8_t RecByte;
|
|||
|
static uint32_t pos = 0;
|
|||
|
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
|
|||
|
{
|
|||
|
RecByte = USART1->DR;//读取接收到的数据
|
|||
|
if(RecByte == '#')
|
|||
|
pos = 1; //开始接收数据
|
|||
|
if(pos)
|
|||
|
{
|
|||
|
if(pos < MAXUSART1BUFSIZE)//防止内存溢出
|
|||
|
Uart1_BUF[pos - 1] = RecByte;
|
|||
|
pos ++;
|
|||
|
if(RecByte == '*')
|
|||
|
{
|
|||
|
//TODO: 接收完毕相关处理
|
|||
|
pos = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
## STM32空闲中断的配置
|
|||
|
1. 前几天做项目也要用到串口传输控制信息在STM32上的话可以利用串口空闲中断(接收完字符以后在下一个传输字符的时间内没有字符传来)来接收数据,这样就不用像上面那样做特定的识别,也比较方便。在这里备注一下,因为当时一开始配置完了并不起作用,后来上网查证了最后才找到解决办法。初始化的代码就不贴了,主要是最后配置完毕,开中断的代码贴一下。
|
|||
|
```c
|
|||
|
...上续初始化的相关配置
|
|||
|
USART_Init(USART1, &USART_InitStructure); //初始化串口
|
|||
|
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断
|
|||
|
USART_ITConfig(USART1, USART_IT_IDLE,ENABLE);//开启中断
|
|||
|
//USART_ClearITPendingBit(USART1, USART_IT_IDLE |USART_IT_RXNE);//清除中断
|
|||
|
USART_Cmd(USART1, ENABLE); //使能串口
|
|||
|
```
|
|||
|
2. 一开始我使用的是下面这条语句进行中断配置的,发现并不能触发空闲中断,后来才知道<font color=#ff0000 size=5>必须要分两次</font>才能正确配置:
|
|||
|
```c
|
|||
|
USART_ITConfig(USART1, USART_IT_RXNE | USART_IT_IDLE, ENABLE);//开启中断
|
|||
|
```
|
|||
|
3. 查看了一下<font color=#00cccc size=5>USART_ITConfig()</font>函数的源码才看出来,他这个函数为了USART_IT_IDLE等中断宏定义的通用性,必须每次只能初始化一个中断标志。
|
|||
|
```c
|
|||
|
void USART_ITConfig(USART_TypeDef *USARTx, uint16_t USART_IT, FunctionalState NewState)
|
|||
|
{
|
|||
|
....
|
|||
|
/* Get the interrupt position */
|
|||
|
itpos = USART_IT & IT_Mask;
|
|||
|
itmask = (((uint32_t)0x01) << itpos);
|
|||
|
....
|
|||
|
}
|
|||
|
/*相关计算
|
|||
|
#define USART_IT_IDLE ((uint16_t)0x0424)
|
|||
|
#define IT_Mask ((uint16_t)0x001F) //!< USART Interrupt Mask
|
|||
|
itpos = 0x0424 & 0x001F = 0x04;
|
|||
|
itmask = (((uint32_t)0x01) << itpos) = 0x10;
|
|||
|
0x10 = 0b00010000//bit[4];
|
|||
|
查阅官方的手册可以看到空闲中断使能也恰好就是bit[4];
|
|||
|
*/
|
|||
|
```
|
|||
|
4. 究其原因是因为itmask这个变量其实就是中断位于寄存器的位置,他是一个0bxx1xx的变量。只能表示一个中断标志位所以每次只能初始化一个标志位。
|
|||
|
|
|||
|
## STM32空闲中断服务函数
|
|||
|
1. 接下来就是中断服务函数了:
|
|||
|
```c
|
|||
|
uint8_t USART1RecBuf[USART1RecBufMaxSize];//接收缓冲区
|
|||
|
uint8_t sdfsdf = 0;
|
|||
|
void USART1_IRQHandler(void) //串口1中断服务程序
|
|||
|
{
|
|||
|
static uint16_t pos = 0;
|
|||
|
if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) //接收完毕触发空闲中断
|
|||
|
{
|
|||
|
pos = 0;
|
|||
|
USART1->DR;//清除空闲中断标志位
|
|||
|
//TODO: 中断接收完成标志
|
|||
|
|
|||
|
}
|
|||
|
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
|
|||
|
{
|
|||
|
uint8_t RecByte;
|
|||
|
Res = USART1->DR; //读取接收到的数据
|
|||
|
if(pos >= USART1RecBufMaxSize) //超过范围返回
|
|||
|
return ;
|
|||
|
USART1RecBuf[pos++] = RecByte;
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
2. 这里需要注意的就是空闲中断的清除并不是调用USART_ClearITPendingBit()函数来清除,查看库的函数源码实现也可以看到注释里并没有说明 USART_IT_IDLE 该参数是可以被传入的,并且我们使用 <font color=#00cccc size=3>USART1->DR</font> 就能清除串口空闲中断。
|
|||
|
```c
|
|||
|
/**
|
|||
|
...
|
|||
|
* @param USART_IT: specifies the interrupt pending bit to clear.
|
|||
|
* This parameter can be one of the following values:
|
|||
|
* @arg USART_IT_CTS: CTS change interrupt (not available for UART4 and UART5)
|
|||
|
* @arg USART_IT_LBD: LIN Break detection interrupt
|
|||
|
* @arg USART_IT_TC: Transmission complete interrupt.
|
|||
|
* @arg USART_IT_RXNE: Receive Data register not empty interrupt.
|
|||
|
...
|
|||
|
*/
|
|||
|
void USART_ClearITPendingBit(USART_TypeDef *USARTx, uint16_t USART_IT)
|
|||
|
{
|
|||
|
····
|
|||
|
|
|||
|
```
|
|||
|
3. 到此完成了串口空闲中断的配置,之后只要两次接收数据之间的时间大于一个字符的时间(跟波特率有关),就能触发这个中断。
|