目录
STM32F10x的USART有不少功能(例如LIN、irDA等),这里只用作普通串口。
STM32F10x最多有3组USART和2组UART。
1. USART和UART的区别
UART:Universal Asynchronous Receiver and Transmitter通用异步收发器。信号: RXD, TXD。
USART:Universal Synchronous Asynchronous Receiver and Transmitter通用同步异步收发器。信号:RXD,TXD,CK,其中CK为同步时钟信号。
这里只考虑UART的情况。
2. UART1/UART2/UART3的IO配置
IO采用默认的复用脚。这里只列举了3个USART的IO配置
RCC->APB2ENR |= RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB;
//A9: TX1, A10: RX1
GPIOA->CRH &= ~(0x000000FF << ((9 % 8) * 4));
GPIOA->CRH |= (0x0000008B << ((9 % 8) * 4));
GPIOA->BSRR = ((uint32_t)1 << 10);
//A2: TX2, A3: RX2
GPIOA->CRL &= ~(0x000000FF << ((2 % 8) * 4));
GPIOA->CRL |= (0x0000008B << ((2 % 8) * 4));
GPIOA->BSRR = ((uint32_t)1 << 3);
//B10: TX3, B11: RX3
GPIOB->CRH &= ~(0x000000FF << ((10 % 8) * 4));
GPIOB->CRH |= (0x0000008B << ((10 % 8) * 4));
GPIOB->BSRR = ((uint32_t)1 << 11);
3. 全局变量
首先定义需要用到的全局变量:
USART_TypeDef* const USARTxGroup[] = {USART1, USART2, USART3};
用于对应STM32的USART寄存器结构。
EXTERN volatile uint8_t gUart0RecvBuf[UART0_RECV_BUFSIZE];
EXTERN volatile uint8_t gUart1RecvBuf[UART1_RECV_BUFSIZE];
EXTERN volatile uint8_t gUart2RecvBuf[UART2_RECV_BUFSIZE];
各个USART接收缓冲,如果不使用则设置大小为0。
static volatile uint16_t gUartRecvIn[] = {0, 0, 0};
static volatile uint16_t gUartRecvRd[] = {0, 0, 0};
USART读写接收缓冲的位置,gUartRecvIn表示写入位置,gUartRecvRd表示读出位置。
4. UART初始化
程序采用数字来表示不同的Uart,port 0表示USART1,port 1表示USART2,以此类推。
UART初始化的API函数原型如下:
void uartInit(uint8_t port, uint32_t baudrate,
uartparity_e parity, uartStop_e stopBit, uint8_t datBit)
参数说明:
port - UART端口号,有效值0 – n,具体根据MCU和应用设计对应的端口定义。这里有效值是0到2.
baudrate - UART的波特率。
parity - UART的奇偶校验位设置。定义其枚举类型如下:
typedef enum eUartParity
{
UART_PARITY_NONE = 0,
UART_PARITY_EVEN,
UART_PARITY_ODD,
}uartparity_e;
stopBit - UART的停止位设置。定义其枚举类型如下:
typedef enum eUartStop
{
UART_STOP_1 = 0,
UART_STOP_0_5,
UART_STOP_1_5,
UART_STOP_2,
}uartStop_e;
4.1 使能USART的RCC并DeInit USART
switch(port)
{
case HW_UART0:
RCC->APB2ENR |= RCC_APB2Periph_USART1;
RCC->APB2RSTR |= RCC_APB2Periph_USART1; //DeInit
RCC->APB2RSTR &= ~RCC_APB2Periph_USART1;
break;
case HW_UART1:
RCC->APB1ENR |= RCC_APB1Periph_USART2;
RCC->APB1RSTR |= RCC_APB1Periph_USART2; //DeInit
RCC->APB1RSTR &= ~RCC_APB1Periph_USART2;
break;
case HW_UART2:
RCC->APB1ENR |= RCC_APB1Periph_USART3;
RCC->APB1RSTR |= RCC_APB1Periph_USART3; //DeInit
RCC->APB1RSTR &= ~RCC_APB1Periph_USART3;
break;
default:
return;
}
4.2 中断初始化
void uartNVICInit(USART_TypeDef *USARTx, uint8_t priority)
{
NVIC_InitTypeDef NVIC_InitStructure;
if(USARTx == USART1)
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
else if(USARTx == USART2)
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
else if(USARTx == USART3)
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = priority;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USARTx, USART_IT_RXNE, ENABLE);
}
使能RXNE中断。
优先级为0:
USART_TypeDef *USARTx;
USARTx = USARTxGroup[port];
uartNVICInit(USARTx, 0);
4.3 配置USART
USART_InitStructure.USART_BaudRate = baudrate;
switch(datBit)
{
case 8:
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
break;
case 9:
USART_InitStructure.USART_WordLength = USART_WordLength_9b;
break;
default:
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
break;
}
switch(stopBit)
{
case UART_STOP_1:
USART_InitStructure.USART_StopBits = USART_StopBits_1;
break;
case UART_STOP_0_5:
USART_InitStructure.USART_StopBits = USART_StopBits_0_5;
break;
case UART_STOP_1_5:
USART_InitStructure.USART_StopBits = USART_StopBits_1_5;
break;
case UART_STOP_2:
USART_InitStructure.USART_StopBits = USART_StopBits_2;
break;
default:
USART_InitStructure.USART_StopBits = USART_StopBits_1;
break;
}
switch(parity)
{
case UART_PARITY_NONE:
default:
USART_InitStructure.USART_Parity = USART_Parity_No;
break;
case UART_PARITY_EVEN:
USART_InitStructure.USART_Parity = USART_Parity_Even;
break;
case UART_PARITY_ODD:
USART_InitStructure.USART_Parity = USART_Parity_Odd;
break;
}
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
/* Configure USART1 */
USART_Init(USARTx, &USART_InitStructure);
5. 中断处理程序
void USART1_IRQHandler(void)
{
USART_IRQHandler(0);
}
void USART2_IRQHandler(void)
{
USART_IRQHandler(1);
}
void USART3_IRQHandler(void)
{
USART_IRQHandler(2);
}
void USART_IRQHandler(uint8_t port)
{
USART_TypeDef *USARTx = USARTxGroup[port];
volatile uint8_t *pUartRecvBuf[] = {gUart0RecvBuf, gUart1RecvBuf, gUart2RecvBuf};
uint16_t bufSize;
switch(port)
{
case HW_UART0:
bufSize = sizeof(gUart0RecvBuf);
break;
case HW_UART1:
bufSize = sizeof(gUart1RecvBuf);
break;
case HW_UART2:
bufSize = sizeof(gUart2RecvBuf);
break;
}
if (USART_GetFlagStatus(USARTx, USART_FLAG_ORE) != RESET)
{
pUartRecvBuf[port][gUartRecvIn[port]] = (uint8_t)(USART_ReceiveData(USARTx) & 0xff);
gUartRecvIn[port] %= bufSize;
USART_ClearITPendingBit(USARTx, USART_FLAG_ORE);
}
else if (USART_GetITStatus(USARTx, USART_IT_RXNE) != RESET)
{
pUartRecvBuf[port][gUartRecvIn[port]++] = (uint8_t)(USART_ReceiveData(USARTx) & 0xff);
gUartRecvIn[port] %= bufSize;
USART_ClearITPendingBit(USARTx, USART_IT_RXNE);
}
}
接收到的数据只保存在接收缓冲中,Circle的方式,数据如果没有被及时取走会被覆盖。
6. 写数据
void uartSendByte(USART_TypeDef *USARTx, uint8_t dat)
{
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC)==RESET)
{
}
USART_SendData(USARTx, dat);
}
bool_t uartWriteBytes(uint8_t port, uint8_t *buf, uint16_t len)
{
if(port >= HW_UART_MAX)
return FALSE;
while(len)
{
uartSendByte(USARTxGroup[port], *buf);
buf++;
len--;
}
return TRUE;
}
6. 读数据
6.1 获取接收缓冲有效数据个数
uint16_t uartGetQueueStatus(uint8_t port)
{
uint16_t datLen = 0;
uint16_t bufSize;
uint16_t recvIn = gUartRecvIn[port];
switch(port)
{
case HW_UART0:
bufSize = sizeof(gUart0RecvBuf);
break;
case HW_UART1:
bufSize = sizeof(gUart1RecvBuf);
break;
case HW_UART2:
bufSize = sizeof(gUart2RecvBuf);
break;
default:
return 0;
}
datLen = (bufSize + recvIn - gUartRecvRd[port]) % bufSize;
return datLen;
}
6.2 读取缓冲数据
bool_t uartReadBytes(uint8_t port, uint8_t *buf, uint16_t len)
{
volatile uint8_t *pUartRecvBuf[] = {gUart0RecvBuf, gUart1RecvBuf};
uint16_t bufSize;
switch(port)
{
case HW_UART0:
bufSize = sizeof(gUart0RecvBuf);
break;
case HW_UART1:
bufSize = sizeof(gUart1RecvBuf);
break;
case HW_UART2:
bufSize = sizeof(gUart2RecvBuf);
break;
default:
return FALSE;
}
if(len > uartGetQueueStatus(port))
return FALSE;
while(len)
{
*buf = pUartRecvBuf[port][gUartRecvRd[port]++];
len--;
buf++;
gUartRecvRd[port] %= bufSize;
}
return TRUE;
}
7. DMA方式
7.1 全局变量
DMA_Channel_TypeDef* const USARTxDmaCH[] = {DMA1_Channel4, DMA1_Channel7, DMA1_Channel2};
DMA_Channel_TypeDef* const USARRxDmaCH[] = {DMA1_Channel5, DMA1_Channel6, DMA1_Channel3};
定义DMA收发对应的Channel以方便port去对应的Channel。
注意:USART使用的DMA Channel可能会和其他外设的DMA Channel重合,比如SPI,这样就不能2个外设都用DMA的方式。
7.2 初始化
void uartDmaInit(USART_TypeDef *USARTx)
{
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
USARTx->CR3 |= USART_DMAReq_Tx | USART_DMAReq_Rx;
}
7.3 配置DMA
void uartDmaCfg(uint8_t port)
{
DMA_Channel_TypeDef *dmaCH;
//TX Channel
dmaCH = USARTxDmaCH[port];
dmaCH->CCR = ((uint32_t)0 << 14) | //Peripheral to Mem
((uint32_t)0x03 << 12) | //Priority Very High
((uint32_t)0x00 << 10) | //Mem Size is 8bit
((uint32_t)0x00 << 8) | //Periph Size is 8bit
((uint32_t)0x01 << 7) | //Mem Increase
((uint32_t)0x00 << 6) | //Periph Increase Disable
((uint32_t)0x00 << 5) | //Circle mode Disable
((uint32_t)0x01 << 4) | //Mem to Periph
((uint32_t)0x00 << 3) | //Error Transfer Int Disable
((uint32_t)0x00 << 2) | //Half Transfer Int Disable
((uint32_t)0x00 << 1) | //Transfer Finish Int Disable
((uint32_t)0x00 << 0); //Channel enable
dmaCH->CNDTR = 0;
dmaCH->CPAR = (uint32_t)(&USARTxGroup[port]->DR);
dmaCH->CMAR = 0;
//RX Channel
dmaCH = USARRxDmaCH[port];
dmaCH->CCR = ((uint32_t)0 << 14) | //Mem to Peripheral
((uint32_t)0x03 << 12) | //Priority Very High
((uint32_t)0x00 << 10) | //Mem Size is 8bit
((uint32_t)0x00 << 8) | //Periph Size is 8bit
((uint32_t)0x01 << 7) | //Mem Increase
((uint32_t)0x00 << 6) | //Periph Increase Disable
((uint32_t)0x01 << 5) | //Circle mode Enable
((uint32_t)0x00 << 4) | //Periph to Mem
((uint32_t)0x00 << 3) | //Error Transfer Int Disable
((uint32_t)0x00 << 2) | //Half Transfer Int Enable
((uint32_t)0x00 << 1) | //Transfer Finish Int Disable
((uint32_t)0x00 << 0); //Channel enable
dmaCH->CPAR = (uint32_t)(&USARTxGroup[port]->DR);
switch(port)
{
case HW_UART0:
dmaCH->CNDTR = sizeof(gUart0RecvBuf);
dmaCH->CMAR = (uint32_t)gUart0RecvBuf;
break;
case HW_UART1:
dmaCH->CNDTR = sizeof(gUart1RecvBuf);
dmaCH->CMAR = (uint32_t)gUart1RecvBuf;
break;
case HW_UART2:
dmaCH->CNDTR = sizeof(gUart2RecvBuf);
dmaCH->CMAR = (uint32_t)gUart2RecvBuf;
break;
default:
break;
}
dmaCH->CCR |= (uint32_t)((uint32_t)0x01 << 0); //Channel Enable
}
发送DMA在配置好参数后不会启动,等实际发送数据时再更新对应的寄存器设置。
而接受DMA配置好就启动,接受到的数据会不停的更新到接受缓冲中,满了自动覆盖前面的数据。这样程序并不需要有接收中断了,需要把中断初始化去掉。
#ifndef UART_DMA
uartNVICInit(USARTx, 0);
#endif
而DMA初始化和配置要放在USART使能之后
USART_Cmd(USARTx, ENABLE);
#ifdef UART_DMA
uartDmaInit(USARTx);
uartDmaCfg(port);
#endif
7.4 发送
bool_t uartWriteBytes(uint8_t port, uint8_t *buf, uint16_t len)
{
if(port >= HW_UART_MAX)
return FALSE;
#ifdef UART_DMA
{
DMA_Channel_TypeDef *dmaCHTx;
dmaCHTx = USARTxDmaCH[port];
dmaCHTx->CCR &= ~((uint32_t)0x01 << 0); //Channel Disable
dmaCHTx->CMAR = (uint32_t)buf;
dmaCHTx->CNDTR = len;
dmaCHTx->CCR |= (uint32_t)((uint32_t)0x01 << 0); //Channel Enable
while(dmaCHTx->CNDTR > 0)
{
delayus(2);
}
}
#else
while(len)
{
uartSendByte(USARTxGroup[port], *buf);
buf++;
len--;
}
#endif
return TRUE;
}
以CNDTR为0表示发送结束。
7.5 接收
只需要修改uartGetQueueStatus,通过CNDTR获取接收缓冲中的数据个数。
switch(port)
{
case HW_UART0:
bufSize = sizeof(gUart0RecvBuf);
#ifdef UART_DMA
gUartRecvIn[0] = (bufSize - DMA1_Channel5->CNDTR) % bufSize;
#endif
break;
case HW_UART1:
bufSize = sizeof(gUart1RecvBuf);
#ifdef UART_DMA
gUartRecvIn[1] = (bufSize - DMA1_Channel6->CNDTR) % bufSize;
#endif
break;
case HW_UART2:
bufSize = sizeof(gUart2RecvBuf);
#ifdef UART_DMA
gUartRecvIn[2] = (bufSize - DMA1_Channel3->CNDTR) % bufSize;
#endif
break;
default:
return 0;
}