stm32 串口通信
扫描二维码
随时随地手机看文章
这次讲讲利用串口收发中断来进行串口通讯。STM32 上为每个串口分配了一个中断。也就是说无论是发送完成还是收到数据或是数据溢出都产生同一个中断。程序需在中断处理函数中读取状态寄存器(USART_SR)来判断当前的是什么中断。下面的中断映像图给出了这些中断源是如何汇合成最终的中断信号的。图中也给出了如何控制每一个单独的中断源是否起作用。
另外,Cortex-M3内核中还有个NVIC,可以控制这里的中断信号是否触发中断处理函数的执行,还有这些外部中断的级别。关于NVIC可以参考《ARM CortexM3权威指南》,里面讲解的非常详细。
简单的说,为了开启中断,我们需要如下的代码:
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启接收中断
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);// 开启发送中断
这里多说一句,串口的发送中断有两个,分别是:
l发送数据寄存器空中断(TXE)
l发送完成中断(TC)
一般来说我们会使用发送数据寄存器空中断,用这个中断发送的效率会高一些。
中断处理函数的框架如下,如果检测到错误就清除错误,收到数了就处理。发完当前数据了就发下一个。
voidUSART1_IRQHandler(void)
{
unsignedintdata;
if(USART1->SR & 0x0F)
{
// See if we have some kind of error, Clear interrupt
data = USART1->DR;
}
elseif(USART1->SR & USART_FLAG_RXNE)//Receive Data Reg Full Flag
{
data = USART1->DR;
// 对收到的数据进行处理,或者干些其他的事
}
elseif(USART1->SR & USART_FLAG_TXE)
{
{// 可以发送数据了,如果没有数据需要发送,就在这里关闭发送中断
USART1->DR = something;// Yes, Send character
}
}
}
下面给一个利用环形缓冲区的串口驱动程序。
#ifndef _COM_BUFFERED_H_
#define _COM_BUFFERED_H_
#define COM1 0
#define COM2 1
#define COM_RX_BUF_SIZE 64
#define COM_TX_BUF_SIZE 64
#define COM_NO_ERR 0
#define COM_BAD_CH 1
#define COM_RX_EMPTY 2
#define COM_TX_FULL 3
#define COM_TX_EMPTY 4
unsignedcharCOMGetCharB (unsignedcharch, unsignedchar*err);
unsignedcharCOMPutCharB (unsignedcharport, unsignedcharc);
voidCOMBufferInit (void);
unsignedcharCOMBufferIsEmpty (unsignedcharport);
unsignedcharCOMBufferIsFull (unsignedcharport);
#endif
#include "stm32f10x_usart.h"
#include "com_buffered.h"
#define OS_ENTER_CRITICAL() __set_PRIMASK(1)
#define OS_EXIT_CRITICAL() __set_PRIMASK(0)
staticvoidCOMEnableTxInt(unsignedcharport)
{
staticUSART_TypeDef* map[2] = {USART1, USART2};
USART_ITConfig(map[port], USART_IT_TXE, ENABLE);
}
typedefstruct{
shortRingBufRxCtr;
unsignedchar*RingBufRxInPtr;
unsignedchar*RingBufRxOutPtr;
unsignedcharRingBufRx[COM_RX_BUF_SIZE];
shortRingBufTxCtr;
unsignedchar*RingBufTxInPtr;
unsignedchar*RingBufTxOutPtr;
unsignedcharRingBufTx[COM_TX_BUF_SIZE];
} COM_RING_BUF;
COM_RING_BUF COM1Buf;
COM_RING_BUF COM2Buf;
unsignedcharCOMGetCharB (unsignedcharport, unsignedchar*err)
{
// unsigned char cpu_sr;
unsignedcharc;
COM_RING_BUF *pbuf;
switch(port)
{
caseCOM1:
pbuf = &COM1Buf;
break;
caseCOM2:
pbuf = &COM2Buf;
break;
default:
*err = COM_BAD_CH;
return(0);
}
OS_ENTER_CRITICAL();
if(pbuf->RingBufRxCtr > 0)
{
pbuf->RingBufRxCtr--;
c = *pbuf->RingBufRxOutPtr++;
if(pbuf->RingBufRxOutPtr == &pbuf->RingBufRx[COM_RX_BUF_SIZE])
{
pbuf->RingBufRxOutPtr = &pbuf->RingBufRx[0];
}
OS_EXIT_CRITICAL();
*err = COM_NO_ERR;
return(c);
}else{
OS_EXIT_CRITICAL();
*err = COM_RX_EMPTY;
c = 0;
return(c);
}
}
unsignedcharCOMPutCharB (unsignedcharport, unsignedcharc)
{
// unsigned char cpu_sr;
COM_RING_BUF *pbuf;
switch(port)
{
caseCOM1:
pbuf = &COM1Buf;
break;
caseCOM2:
pbuf = &COM2Buf;
break;
default:
return(COM_BAD_CH);
}
OS_ENTER_CRITICAL();
if(pbuf->RingBufTxCtr < COM_TX_BUF_SIZE) {
pbuf->RingBufTxCtr++;
*pbuf->RingBufTxInPtr++ = c;
if(pbuf->RingBufTxInPtr == &pbuf->RingBufTx[COM_TX_BUF_SIZE]) {
pbuf->RingBufTxInPtr = &pbuf->RingBufTx[0];
}
if(pbuf->RingBufTxCtr == 1) {
COMEnableTxInt(port);
OS_EXIT_CRITICAL();
}else{
OS_EXIT_CRITICAL();
}
return(COM_NO_ERR);
}else{
OS_EXIT_CRITICAL();
return(COM_TX_FULL);
}
}
voidCOMBufferInit (void)
{
COM_RING_BUF *pbuf;
pbuf = &COM1Buf;
pbuf->RingBufRxCtr = 0;
pbuf->RingBufRxInPtr = &pbuf->RingBufRx[0];
pbuf->RingBufRxOutPtr = &pbuf->RingBufRx[0];
pbuf->RingBufTxCtr = 0;
pbuf->RingBufTxInPtr = &pbuf->RingBufTx[0];
pbuf->RingBufTxOutPtr = &pbuf->RingBufTx[0];
pbuf = &COM2Buf;
pbuf->RingBufRxCtr = 0;
pbuf->RingBufRxInPtr = &pbuf->RingBufRx[0];
pbuf->RingBufRxOutPtr = &pbuf->RingBufRx[0];
pbuf->RingBufTxCtr = 0;
pbuf->RingBufTxInPtr = &pbuf->RingBufTx[0];
pbuf->RingBufTxOutPtr = &pbuf->RingBufTx[0];
}
unsignedcharCOMBufferIsEmpty (unsignedcharport)
{
// unsigned char cpu_sr;
unsignedcharempty;
COM_RING_BUF *pbuf;
switch(port)
{
caseCOM1:
pbuf = &COM1Buf;
break;
caseCOM2:
pbuf = &COM2Buf;
break;
default:
return(1);
}
OS_ENTER_CRITICAL();
if(pbuf->RingBufRxCtr > 0)
{
empty = 0;
}
else
{
empty = 1;
}
OS_EXIT_CRITICAL();
return(empty);
}
unsignedcharCOMBufferIsFull (unsignedcharport)
{
// unsigned char cpu_sr;
charfull;
COM_RING_BUF *pbuf;
switch(port)
{
caseCOM1:
pbuf = &COM1Buf;
break;
caseCOM2:
pbuf = &COM2Buf;
break;
default:
return(1);
}
OS_ENTER_CRITICAL();
if(pbuf->RingBufTxCtr < COM_TX_BUF_SIZE) {
full = 0;
}else{
full = 1;
}
OS_EXIT_CRITICAL();
return(full);
}
// This function is called by the Rx ISR to insert a character into the receive ring buffer.
staticvoidCOMPutRxChar (unsignedcharport, unsignedcharc)
{
COM_RING_BUF *pbuf;
switch(port)
{
caseCOM1:
pbuf = &COM1Buf;
break;
caseCOM2:
pbuf = &COM2Buf;
break;
default:
return;
}
if(pbuf->RingBufRxCtr < COM_RX_BUF_SIZE) {
pbuf->RingBufRxCtr++;
*pbuf->RingBufRxInPtr++ = c;
if(pbuf->RingBufRxInPtr == &pbuf->RingBufRx[COM_RX_BUF_SIZE]) {
pbuf->RingBufRxInPtr = &pbuf->RingBufRx[0];
}
}
}
// This function is called by the Tx ISR to extract the next character from the Tx buffer.
// The function returns FALSE if the buffer is empty after the character is extracted from
// the buffer. This is done to signal the Tx ISR to disable interrupts because this is the
// last character to send.
staticunsignedcharCOMGetTxChar (unsignedcharport, unsignedchar*err)
{
unsignedcharc;
COM_RING_BUF *pbuf;
switch(port)
{
caseCOM1:
pbuf = &COM1Buf;
break;
caseCOM2:
pbuf = &COM2Buf;
break;
default:
*err = COM_BAD_CH;
return(0);
}
if(pbuf->RingBufTxCtr > 0) {
pbuf->RingBufTxCtr--;
c = *pbuf->RingBufTxOutPtr++;
if(pbuf->RingBufTxOutPtr == &pbuf->RingBufTx[COM_TX_BUF_SIZE])
{
pbuf->RingBufTxOutPtr = &pbuf->RingBufTx[0];
}
*err = COM_NO_ERR;
return(c);
}else{
*err = COM_TX_EMPTY;
return(0);
}
}
voidUSART1_IRQHandler(void)
{
unsignedintdata;
unsignedcharerr;
if(USART1->SR & 0x0F)
{
// See if we have some kind of error
// Clear interrupt (do nothing about it!)
data = USART1->DR;
}
elseif(USART1->SR & USART_FLAG_RXNE)//Receive Data Reg Full Flag
{
data = USART1->DR;
COMPutRxChar(COM1, data);// Insert received character into buffer
}
elseif(USART1->SR & USART_FLAG_TXE)
{
data = COMGetTxChar(COM1, &err);// Get next character to send.
if(err == COM_TX_EMPTY)
{// Do we have anymore characters to send ?
// No, Disable Tx interrupts
//USART_ITConfig(USART1, USART_IT_TXE| USART_IT_TC, ENABLE);
USART1->CR1 &= ~USART_FLAG_TXE | USART_FLAG_TC;
}
else
{
USART1->DR = data;// Yes, Send character
}
}
}
voidUSART2_IRQHandler(void)
{
unsignedintdata;
unsignedcharerr;
if(USART2->SR & 0x0F)
{
// See if we have some kind of error
// Clear interrupt (do nothing about it!)
data = USART2->DR;
}
elseif(USART2->SR & USART_FLAG_RXNE)//Receive Data Reg Full Flag
{
data = USART2->DR;
COMPutRxChar(COM2, data);// Insert received character into buffer
}
elseif(USART2->SR & USART_FLAG_TXE)
{
data = COMGetTxChar(COM2, &err);// Get next character to send.
if(err == COM_TX_EMPTY)
{// Do we have anymore characters to send ?
// No, Disable Tx interrupts
//USART_ITConfig(USART2, USART_IT_TXE| USART_IT_TC, ENABLE);
USART2->CR1 &= ~USART_FLAG_TXE | USART_FLAG_TC;
}
else
{
USART2->DR = data;// Yes, Send character
}
}
}
下面给个例子主程序,来演示如何使用上面的串口驱动代码。
#include "misc.h"
#include "stm32f10x.h"
#include "com_buffered.h"
voidUART_PutStrB (unsignedcharport, uint8_t *str)
{
while(0 != *str)
{
COMPutCharB(port, *str);
str++;
}
}
voidUSART1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GP