stm32USART
1.stm32的USART分为UART(也就是通用异步收发器),和同步的USART,区别在于UART只需要两根线,即TX和RX即可,而USART配置为同步收发器时,需要额外的时钟线
2.USART收发数据有三种方式分别是
(1)轮询阻塞
(2)中断
(3)配合DMA
3.接收数据数量的判断
示例
HAL_UART_Receive(&huart1, buff, 200, 1000)
//RxXferSize这个变量表示需要接收的数据的总大小,单位是字节
//RxXferCount这个变量表示当前还剩下多少数据需要接收,单位是字节
number=RxXferSize -huart1.RxXferCount
二者相减,即可得到接收到的实际数量
4.
采用中断方式发送与接收数据时,
(采用一个字节有效数据,一位停止位时),每当发送/接收一个字节数据,就会进入一次USART1_IRQHandler(void),进而进入 HAL_UART_IRQHandler(&huart1)HAL中断公共处理函数,在这个函数中会根据发送/接收进入不同的更低一层的处理函数,
(1)只有在接收完最后一个数据之后(即接收完成后) ,会失能相应的RXNE的中断,然后将gstate置于ready状态,回调进入HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)在这个函数中处理数据。
(2)在发送完最后一个数据后,会失能相应的TXE的中断,而使能TC(transmit complete)中断,调用UART_EndTransmit_IT(huart)这个函数,在其中失能TC中断,然后将gstate置于ready状态,以等待下一批数据的发送,最后调用HAL_UART_TxCpltCallback(huart)。
(3)因此你可以发现,只有在这一批数据发送/接收完成后,才可以进入接收/发送完成回调函数
(4)你也可以发现每次一批数据接收/发送完成之后(在进入HAL_UART_TX/RxCpltCallback 前)都进行了对TXE/RXNE的失能,因此在处理HAL_UART_TX/RxCpltCallback回调函数时,不会因为有新一批数据而会产生中断,因此当你在此需要接收/发送数据时,都需要在重新调用
HAL_UART_Transmit_IT()或者HAL_UART_Receive_IT()来重新使能TXE/RXNE中断
void USART1_IRQHandler(void) //NVIC向量表中定义的USART1的中断函数
{
HAL_UART_IRQHandler(&huart1); //调用Hal库的USART中断公共处理函数
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
//接收完成回调函数
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
//发送完成回调函数
5.采用 空闲中断+循环缓冲区 实现不定长数据的收发
(1) 空闲中断:在接收到数据后,如果在一个字节的周期内没有新的数据接收,硬件会置位IDLE位
(2)循环缓冲区:
分别设置接收与发送缓冲区,接收缓冲区存放接收到的数据,而发送缓冲区存放需要发送的数据。
环形串口缓冲区建立的数组要定义一个头(记录新来的数据要存放的位置)和一个尾(记录读出数据的位置)。这时有三种情况:
1、当头等于尾时,我们便知道此时环形串口缓冲区无数据,此时不进行读操作。
2、有数据来时,数组存储数据,并且头按数据的长度向前移动,空闲时尾开始取数据,直到尾等于头为止。
3、当头存到数组长度的最后一位时,返回数组第一位开始存数据
(3)注意:我们需要定义接收数据的最大长度,每次接收的数据虽然是不定长数据,但是不能超过最大长度,否则就会回调HAL_UART_RxCpltCallback(huart); 这个接收完成回调函数,我们采用的时,监测到总线空闲,就产生空闲中断,然后人为中止此次接收过程,之后在空闲中断中,处理数据
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&UCB1.UART);
if(__HAL_UART_GET_FLAG(&UCB1.UART,UART_FLAG_IDLE)) //发生了空闲中断
{
__HAL_UART_CLEAR_IDLEFLAG(&UCB1.UART);
UCB1.RxCounter+=RX_MAX- UCB1.UART.RxXferCount;
//记录接收的总的数量
HAL_UART_AbortReceive_IT(&UCB1.UART);
//以中断的方式,中止接收传输过程,失能RXNE中断
}
}
(详细的实现调用过程,阅读源码可知)
6.利用串口实现多机通信,有两种区分通信从机的方式,一种时空闲空闲线路检测,另一种是地址标记检测。这两种方式,总线上都不会产生空闲中断。因此利用空闲中断判断接收一批数据结束的方式就失效了。
RWU: Receiver wakeup(USART_CR1)
0: Receiver in active mode 可以接收
1: Receiver in mute mode 静默模式
(1)地址标记检测,接收器的地址在 USART_CR2 寄存器的 ADD 位中进行设置,四位,
0000-1111,总共可以区分16个从机,在此模式下,发送的第一个字节为地址,该字节的最高位为 1,则将这些字节识别为地址,否则将其识别为数据。在地址字节中,目标接收器的地址位于 4 个 LSB 上,接收器会将此 4 位字与其地址进行比较,当接收到与编程地址匹配的地址字符时,它会退出静音模式。
(2)正如上述所说,不能通过总线空闲中断的方式,检测一批数据的接收完成,这边我们采用定时器超时判断的方式,在波特率为9600 bit/s时,1秒可以发送960个字节数据,一个字节数据需要1/960=1.04ms左右,因此可以用定时器定时20/30ms,
if(接收的是第一个字节)
{ 使能定时器计数,让其开始计时}
else// 接收的不是第一个字节,接收已经开始
{ 清除定时器计数器(只有最后一个数据才会 超时) }
只有接收到最后一个字节,后面没有给他清除计数器,会导致超时,表示这一批数据接收完成,超时就触发中断函数,在中断函数中,执行相应操作。
(2)空闲检测:
检测到总线空闲,那么从机就唤醒,RWU=0,发送的第一个数据为地址,地址不匹配时,手动进入静默模式,RWU=1,之后这一批数据发送完成,总线空闲时,从机的RWU=0,再次进入准备接收模式。
7.常见错误
作者:你心如何猜透