USART作為一種標(biāo)準(zhǔn)接口在應(yīng)用中十分常見。
1、STM32固件庫使用外圍設(shè)備的主要思路
在STM32中,外圍設(shè)備的配置思路比較固定。
首先是使能相關(guān)的時(shí)鐘,一方面是設(shè)備本身的時(shí)鐘,如果設(shè)備是通過IO口輸入輸出則還需要使能對應(yīng)IO口的時(shí)鐘。
其次是配置對應(yīng)外設(shè)的各項(xiàng)相關(guān)參數(shù),如果設(shè)備是通過IO口輸入輸出則還需要配置相關(guān)的GPIO。
最后是使能對應(yīng)外設(shè)。
如果相關(guān)設(shè)備需要使用中斷功能,則還需要配置對應(yīng)外設(shè)中斷通道的中斷優(yōu)先級,然后使能相應(yīng)設(shè)備的中斷,最后還要填寫相應(yīng)的中斷服務(wù)程序,在服務(wù)程序中進(jìn)行相應(yīng)的操作。
2、UART的配置步驟
2.1、打開時(shí)鐘
由于UART1的TX和RX和AFIO都掛在APB2橋上,因此采用固件庫函數(shù)RCC_APB2PeriphClockCmd()進(jìn)行初始化。UARTx需要分情況討論,如果是UART1,則掛在APB2橋上,因此采用RCC_APB2PeriphClockCmd()進(jìn)行初始化,其余的UART2~5均掛在APB1上。
2.2、GPIO初始化
GPIO的屬性包含在結(jié)構(gòu)體GPIO_InitTypeDef,其中對于TX引腳,GPIO_Mode字段設(shè)置為GPIO_Mode_AF_PP(復(fù)用推挽輸出),GPIO_Speed切換速率設(shè)置為GPIO_Speed_50MHz;對于RX引腳,GPIO_Mode字段設(shè)置為GPIO_Mode_IN_FLOATING(浮空輸入),不需要設(shè)置切換速率。最后通過GPIO_Init()使能IO口。
以下是TX引腳設(shè)置的實(shí)例代碼:
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = UART_TX_PIN[COM];
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(UART_TX_PORT[COM], &GPIO_InitStructure);
2.3、中斷優(yōu)先級的配置
這是STM32比較奇怪的地方,在只有一個(gè)中斷的情況下,仍然需要配置優(yōu)先級,其作用是使能某條中斷的觸發(fā)通道。STM32的中斷有至多兩個(gè)層次,分別是先占優(yōu)先級和從優(yōu)先級,而整個(gè)優(yōu)先級設(shè)置參數(shù)的長度為4位,因此需要首先劃分先占優(yōu)先級位數(shù)和從優(yōu)先級位數(shù),通過NVIC_PriorityGroupConfig()實(shí)現(xiàn);
特定設(shè)備的中斷優(yōu)先級NVIC的屬性包含在結(jié)構(gòu)體NVIC_InitTypeDef中,其中字段NVIC_IRQChannel包含了設(shè)備的中斷向量,保存在啟動(dòng)代碼中;字段NVIC_IRQChannelPreemptionPriority為主優(yōu)先級,NVIC_IRQChannelSubPriority為從優(yōu)先級,取值的范圍應(yīng)根據(jù)位數(shù)劃分的情況而定;最后NVIC_IRQChannelCmd字段是是否使能,一般定位ENABLE。最后通過NVIC_Init()來使能這一中斷向量。實(shí)例代碼如下:
/* Configure theNVIC Preemption Priority Bits */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
/* Enable the USARTy Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = UART4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
2.4、配置USART相關(guān)屬性
通過結(jié)構(gòu)體USART_InitTypeDef來確定。UART模式下的字段如下
USART_BaudRate:波特率,視具體設(shè)備而定
USART_WordLength:字長
USART_StopBits:停止位
USART_Parity:校驗(yàn)方式
USART_HardwareFlowControl:硬件流控制
USART_Mode:單/雙工
最后通過USART_Init()來設(shè)置。實(shí)例代碼為:
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()來啟動(dòng)設(shè)備UART。
2.5、中斷的服務(wù)程序的設(shè)計(jì)
目前使用了UART的兩個(gè)中斷USART_IT_RXNE(接收緩存非空中斷)和USART_IT_TXE(發(fā)送緩存空中斷),前一個(gè)中斷保證了一旦有數(shù)據(jù)接收到就進(jìn)入中斷以接收特定長度的數(shù)據(jù),后一個(gè)中斷表示一旦發(fā)完一個(gè)數(shù)據(jù)就進(jìn)入中斷函數(shù),保證連續(xù)發(fā)送一段數(shù)據(jù)。一個(gè)設(shè)備的所有中斷都包含在一個(gè)中斷服務(wù)程序中,因此必須首先分清楚這次響應(yīng)的是哪一個(gè)中斷,使用USART_GetITStatus()函數(shù)查看標(biāo)志位來確定進(jìn)入的是哪個(gè)中斷;采用USART_ReceiveData()函數(shù)接收一個(gè)字節(jié)數(shù)據(jù),采用USART_SendData()函數(shù)發(fā)送一個(gè)字節(jié)數(shù)據(jù),當(dāng)關(guān)閉串口的某一個(gè)中斷時(shí)采用USART_ITConfig()失能響應(yīng)的中斷。實(shí)例程序:
voidUART4_IRQHandler(void)
{
if(USART_GetITStatus(UART4,USART_IT_RXNE) != RESET)
{//當(dāng)檢測掉讀入中斷
RxBuffer[RxCounter++] = USART_ReceiveData(UART4);
if (RxCounter ==NbrOfDataToRead)
{
USART_ITConfig(UART4, USART_IT_RXNE, DISABLE); //禁止中斷
}
}
if(USART_GetITStatus(UART4, USART_IT_TXE) != RESET)
{
/* Write one byte to the transmit data register */
USART_SendData(UART4, TxBuffer[TxCounter++]);
if(TxCounter ==NbrOfDataToTransfer)
{
//TxCounter = 0;
/* Disable the USARTy Transmit interrupt */
USART_ITConfig(UART4, USART_IT_TXE, DISABLE);
}
}
}
其中主程序與中斷服務(wù)程序通過全局變量來通信,這也是一種多進(jìn)程共享存儲區(qū)的體現(xiàn)形式。
下面分析一下串口相關(guān)的寄存器。
STM32有數(shù)個(gè)串口,每個(gè)串口都有一個(gè)自己獨(dú)立的波特率寄存器USART_BRR,通過設(shè)置該寄存器就可以達(dá)到配置不同波特率的目的,由于STM32采用了分?jǐn)?shù)波特率,所以STM32的串口波特率設(shè)置范圍很寬,而且誤差很小。
由上表可知:USART_BRR的最低4位(位[3:0])用來存放小數(shù)部分DIV_Fraction,緊接著的12位(位[15:4])用來存放整數(shù)部分DIV_Mantissa,最高16位保留。
STM32的串口波特率計(jì)算公式如下:
上式中,F(xiàn)pclk是給串口的時(shí)鐘(PCLK1用于USART2、3、4、5,PCLK2用于USART1,以前說過,PCLK1是由系統(tǒng)時(shí)鐘分頻得來,最大36MHZ,PCLK2是直接由系統(tǒng)時(shí)鐘得來,最大72MHZ。)
USARTDIV是一個(gè)無符號定點(diǎn)數(shù),我們已知要設(shè)置的波特率及系統(tǒng)時(shí)鐘,這樣就可以算出USARTDIV的值,并把它的整數(shù)和小數(shù)分離開來,分別寫入U(xiǎn)SART_BRR寄存器里的對應(yīng)位就行了,假設(shè)我們的串口1要設(shè)置為115200的波特率,而PCLK2的時(shí)鐘為72M。這樣,我們根據(jù)上面的公式有:
USARTDIV=72000000/(115200*16)=39.0625
那么得到:
DIV_Fraction=16*0.0625=1=0X01;
DIV_Mantissa=468=0X27;
這樣,我們就得到了USART1->BRR的值為0X27。只要設(shè)置串口1的BRR寄存器值為
0X27就可以得到115200的波特率。
以上是串口最重要的比特率設(shè)置,下面是串口的一些其他寄存器。
1:首先是使能串口時(shí)鐘,串口作為STM32的一個(gè)外設(shè),其時(shí)鐘由外設(shè)時(shí)鐘使能寄存器控制,串口1是在APB2ENR寄存器的第14位,除了串口1的時(shí)鐘使能在APB2ENR寄存器,其他串口的時(shí)鐘使能位都在APB1ENR寄存器。
2:復(fù)位串口,一般在系統(tǒng)剛開始配置外設(shè)的時(shí)候,都會先執(zhí)行復(fù)位該外設(shè)的操作。串口1的復(fù)位是通過配置APB2RSTR寄存器的第14位來實(shí)現(xiàn)的,通過向該位寫1復(fù)位串口1,寫0結(jié)束復(fù)位。其他串口的復(fù)位位在APB1RSTR里面設(shè)置。
APB2RSTR寄存器描述:
3:串口功能控制,串口控制寄存器有三個(gè):USATR_CR1-3,串口的很多配置都是通過這3個(gè)寄存器來設(shè)置的,這里我們只要用到USART_CR1就可以實(shí)現(xiàn)我們的功能了,該寄存器的各位描述如下圖所示:
該寄存器的高18位未使用,低14位用于串口的功能設(shè)置,。
UE為串口使能位,通過該位置1,以使能串口。
M為字長選擇位,當(dāng)該位為0的時(shí)候設(shè)置串口為8個(gè)字長外加n個(gè)停止位,停止位的個(gè)數(shù)(n)是根據(jù)USART_CR2的[13:12]位設(shè)置來決定的,默認(rèn)為0。PCE為校驗(yàn)使能位,設(shè)置為0,則禁止校驗(yàn),否則使能校驗(yàn)。
PS為校驗(yàn)位選擇,設(shè)置為0則為偶校驗(yàn),否則為奇校驗(yàn)。
TXIE為發(fā)送緩沖區(qū)空中斷使能位,設(shè)置該位為1,當(dāng)USART_SR中的TXE位為1時(shí),將產(chǎn)生串口中斷。
TCIE為發(fā)送完成中斷使能位,設(shè)置該位為1,當(dāng)USART_SR中的TC位為1時(shí),將產(chǎn)生串口中斷。
RXNEIE為接收緩沖區(qū)非空中斷使能,設(shè)置該位為1,當(dāng)USART_SR中的ORE或者RXNE位為1時(shí),將產(chǎn)生串口中斷。
TE為發(fā)送使能位,設(shè)置為1,將開啟串口的發(fā)送功能。
RE為接收使能位,用法同TE。
RWU為接收喚醒,該位用來決定是否把USART置于靜默模式。軟件對該位置位或者清零。當(dāng)喚醒序列到來時(shí),硬件也會將其清零。
4:數(shù)據(jù)發(fā)送與接收。STM32的發(fā)送與接收是通過數(shù)據(jù)寄存器USART_DR來實(shí)現(xiàn)的,這是一個(gè)雙寄存器,包含了TDR和RDR。當(dāng)向該寄存器寫數(shù)據(jù)的時(shí)候,串口就會自動(dòng)發(fā)送,當(dāng)收到收據(jù)的時(shí)候,也是存在該寄存器內(nèi)。該寄存器的各位描述如下圖所示:
雖然是一個(gè)32位寄存器,但是只用了低9位(DR[8:0]),其他都是保留。
DR[8:0]為串口數(shù)據(jù),包含了發(fā)送或接收的數(shù)據(jù)。由于它是由兩個(gè)寄存器組成的,一個(gè)給發(fā)送用(TDR),一個(gè)給接收用(RDR),該寄存器兼具讀和寫的功能。
5:串口狀態(tài)。串口的狀態(tài)可以通過狀態(tài)寄存器USART_SR讀取。
這里有兩個(gè)位,比較常用第5、6位RXNE和TC。
RXNE(讀數(shù)據(jù)寄存器非空),當(dāng)該位被置1的時(shí)候,就是提示已經(jīng)有數(shù)據(jù)被接收到了,如果設(shè)置了這個(gè)位的中斷,則會產(chǎn)生中斷。。這時(shí)候我們要做的就是盡快去讀取USART_DR,通過讀USART_DR可以將該位清零,也可以向該位寫0,直接清除。
TC(發(fā)送完成),當(dāng)該位被置位的時(shí)候,表示USART_DR內(nèi)的數(shù)據(jù)已經(jīng)被發(fā)送完成了。如果設(shè)置了這個(gè)位的中斷,則會產(chǎn)生中斷。該位也有兩種清零方式:1)讀USART_SR,寫USART_DR。2)直接向該位寫0。
串口初始化函數(shù):
voiduart_init(u32pclk2,u32bound)
{
floattemp;
u16mantissa;
u16fraction;
temp=(float)(pclk2*1000000)/(bound*16);//得到USARTDIV
mantissa=temp; //得