這次講講利用串口收發(fā)中斷來進(jìn)行串口通訊。STM32 上為每個(gè)串口分配了一個(gè)中斷。也就是說無論是發(fā)送完成還是收到數(shù)據(jù)或是數(shù)據(jù)溢出都產(chǎn)生同一個(gè)中斷。程序需在中斷處理函數(shù)中讀取狀態(tài)寄存器(USART_SR)來判斷當(dāng)前的是什么中斷。下面的中斷映像圖給出了這些中斷源是如何匯合成最終的中斷信號(hào)的。圖中也給出了如何控制每一個(gè)單獨(dú)的中斷源是否起作用。
另外,Cortex-M3內(nèi)核中還有個(gè)NVIC,可以控制這里的中斷信號(hào)是否觸發(fā)中斷處理函數(shù)的執(zhí)行,還有這些外部中斷的級(jí)別。關(guān)于NVIC可以參考《ARM CortexM3權(quán)威指南》,里面講解的非常詳細(xì)。
簡單的說,為了開啟中斷,我們需要如下的代碼:
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);// 開啟發(fā)送中斷
這里多說一句,串口的發(fā)送中斷有兩個(gè),分別是:
l發(fā)送數(shù)據(jù)寄存器空中斷(TXE)
l發(fā)送完成中斷(TC)
一般來說我們會(huì)使用發(fā)送數(shù)據(jù)寄存器空中斷,用這個(gè)中斷發(fā)送的效率會(huì)高一些。
中斷處理函數(shù)的框架如下,如果檢測(cè)到錯(cuò)誤就清除錯(cuò)誤,收到數(shù)了就處理。發(fā)完當(dāng)前數(shù)據(jù)了就發(fā)下一個(gè)。
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;
// 對(duì)收到的數(shù)據(jù)進(jìn)行處理,或者干些其他的事
}
elseif(USART1->SR & USART_FLAG_TXE)
{
{// 可以發(fā)送數(shù)據(jù)了,如果沒有數(shù)據(jù)需要發(fā)送,就在這里關(guān)閉發(fā)送中斷
USART1->DR = something;// Yes, Send character
}
}
}
下面給一個(gè)利用環(huán)形緩沖區(qū)的串口驅(qū)動(dòng)程序。
#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
}
}
}
下面給個(gè)例子主程序,來演示如何使用上面的串口驅(qū)動(dòng)代碼。
#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