这次讲讲利用串口收发中断来进行串口通讯。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)
一般来说我们会使用发送数据寄存器空中断,用这个中断发送的效率会高一些。
中断处理函数的框架如下,如果检测到错误就清除错误,收到数了就处理。发完当前数据了就发下一个。
void USART1_IRQHandler(void)
{
unsigned int data;
if(USART1->SR & 0x0F)
{
// See if we have some kind of error, Clear interrupt
data = USART1->DR;
}
else if(USART1->SR & USART_FLAG_RXNE) //Receive Data Reg Full Flag
{
data = USART1->DR;
// 对收到的数据进行处理,或者干些其他的事
}
else if(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
unsigned char COMGetCharB (unsigned char ch, unsigned char *err);
unsigned char COMPutCharB (unsigned char port, unsigned char c);
void COMBufferInit (void);
unsigned char COMBufferIsEmpty (unsigned char port);
unsigned char COMBufferIsFull (unsigned char port);
#endif
#include "stm32f10x_usart.h"
#include "com_buffered.h"
#define OS_ENTER_CRITICAL() __set_PRIMASK(1)
#define OS_EXIT_CRITICAL() __set_PRIMASK(0)
static void COMEnableTxInt(unsigned char port)
{
static USART_TypeDef* map[2] = {USART1, USART2};
USART_ITConfig(map[port], USART_IT_TXE, ENABLE);
}
typedef struct {
short RingBufRxCtr;
unsigned char *RingBufRxInPtr;
unsigned char *RingBufRxOutPtr;
unsigned char RingBufRx[COM_RX_BUF_SIZE];
short RingBufTxCtr;
unsigned char *RingBufTxInPtr;
unsigned char *RingBufTxOutPtr;
unsigned char RingBufTx[COM_TX_BUF_SIZE];
} COM_RING_BUF;
COM_RING_BUF COM1Buf;
COM_RING_BUF COM2Buf;
unsigned char COMGetCharB (unsigned char port, unsigned char *err)
{
// unsigned char cpu_sr;
unsigned char c;
COM_RING_BUF *pbuf;
switch (port)
{
case COM1:
pbuf = &COM1Buf;
break;
case COM2:
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);
}
}
unsigned char COMPutCharB (unsigned char port, unsigned char c)
{
// unsigned char cpu_sr;
COM_RING_BUF *pbuf;
switch (port)
{
case COM1:
pbuf = &COM1Buf;
break;
case COM2:
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);
}
}
void COMBufferInit (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];
}
unsigned char COMBufferIsEmpty (unsigned char port)
{
// unsigned char cpu_sr;
unsigned char empty;
COM_RING_BUF *pbuf;
switch (port)
{
case COM1:
pbuf = &COM1Buf;
break;
case COM2:
pbuf = &COM2Buf;
break;
default:
return (1);
}
OS_ENTER_CRITICAL();
if (pbuf->RingBufRxCtr > 0)
{
empty = 0;
}
else
{
empty = 1;
}
OS_EXIT_CRITICAL();
return (empty);
}
unsigned char COMBufferIsFull (unsigned char port)
{
// unsigned char cpu_sr;
char full;
COM_RING_BUF *pbuf;
switch (port)
{
case COM1:
pbuf = &COM1Buf;
break;
case COM2:
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.
static void COMPutRxChar (unsigned char port, unsigned char c)
{
COM_RING_BUF *pbuf;
switch (port)
{
case COM1:
pbuf = &COM1Buf;
break;
case COM2:
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.
static unsigned char COMGetTxChar (unsigned char port, unsigned char *err)
{
unsigned char c;
COM_RING_BUF *pbuf;
switch (port)
{
case COM1:
pbuf = &COM1Buf;
break;
case COM2:
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);
}
}
void USART1_IRQHandler(void)
{
unsigned int data;
unsigned char err;
if(USART1->SR & 0x0F)
{
// See if we have some kind of error
// Clear interrupt (do nothing about it!)
data = USART1->DR;
}
else if(USART1->SR & USART_FLAG_RXNE) //Receive Data Reg Full Flag
{
data = USART1->DR;
COMPutRxChar(COM1, data); // Insert received character into buffer
}
else if(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
}
}
}
void USART2_IRQHandler(void)
{
unsigned int data;
unsigned char err;
if(USART2->SR & 0x0F)
{
// See if we have some kind of error
// Clear interrupt (do nothing about it!)
data = USART2->DR;
}
else if(USART2->SR & USART_FLAG_RXNE) //Receive Data Reg Full Flag
{
data = USART2->DR;
COMPutRxChar(COM2, data); // Insert received character into buffer
}
else if(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"
void UART_PutStrB (unsigned char port, uint8_t *str)
{
while (0 != *str)
{
COMPutCharB(port, *str);
str++;
}
}
void USART1_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;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure );
USART_Cmd(USART1, ENABLE);
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);
}
void USART2_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART2, &USART_InitStructure );
USART_Cmd(USART2, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
int main(void)
{
unsigned char c;
unsigned char err;
USART1_Init();
USART2_Init();
COMBufferInit();
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
UART_PutStrB(COM1, "Hello World!\n");
for(;;)
{
c = COMGetCharB(COM1, &err);
if(err == COM_NO_ERR)
{
COMPutCharB(COM1, c);
}
}
}
上一篇:STM32的GPIO中断例子
下一篇:STM32启动文件简单分析
推荐阅读最新更新时间:2024-03-16 15:41