|
一.STM32串口介紹
a.串口的數(shù)據(jù)包格式為 起始位+數(shù)據(jù)位+校驗(yàn)位+停止位,所以一般需要設(shè)置數(shù)據(jù)位為8,校驗(yàn)位為1,停止位為1。我們?cè)侔l(fā)送過程中只發(fā)送數(shù)據(jù),其他的都由硬件來完成了,所以通信的雙方在數(shù)據(jù)包格式配置相同時(shí)才能正確通信。
b.除去數(shù)據(jù)包格式設(shè)置一樣外,因?yàn)榇诖蠖鄶?shù)都是用異步通信,由于沒有時(shí)鐘信號(hào),所以2個(gè)通信設(shè)備需約定好波特率,常見的有4800、9600、115200等,波特率一致時(shí)才能正確通信。
c.stm32的庫(kù)文件中將這些需要配置的參數(shù)都寫在了USART_InitTypeDef 結(jié)構(gòu)體中,我們只要對(duì)其進(jìn)行賦值,再調(diào)用函數(shù)USART_Init(),USART_Init函數(shù)會(huì)將USART_InitTypeDef 結(jié)構(gòu)體中的數(shù)據(jù)寫入相應(yīng)的寄存器,這樣就完成了對(duì)32串口的配置。
二.串口初始化(統(tǒng)一初始化)
a.串口配置時(shí),只有少數(shù)值需要時(shí)常更改,大部分都是重復(fù)內(nèi)容,因此將常用的這些值做為參數(shù)傳入。這樣調(diào)用一個(gè)函數(shù)可以對(duì)所有的串口進(jìn)行賦值。(串口使用的GPIO在后續(xù)的文章中統(tǒng)一配置)借鑒前輩的代碼。
b.串口初始化流程 開外設(shè)時(shí)鐘->配置引腳(統(tǒng)一配置)->配置參數(shù) ->使能中斷 ->使能串口

void User_Usart_Init(USART_TypeDef* USARTx, u32 BaudRate, u16 WordLength, u16 StopBits, u16 Parity)
{
USART_InitTypeDef USART_InitStructure;
USART_ClockInitTypeDef USART_ClockInitStructure ;
if(USARTx == USART1)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);// Enable USART1使能或者失能APB2外設(shè)時(shí)鐘 高速72MHz
}
else if(USARTx == USART2)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);// Enable USART2使能或者失能APB1外設(shè)時(shí)鐘 低速36MHz
}
else if(USARTx == USART3)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);// Enable USART3使能或者失能APB1外設(shè)時(shí)鐘 低速36MHz
}
else if(USARTx == UART4)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4,ENABLE);// Enable USART4使能或者失能APB1外設(shè)時(shí)鐘 低速36MHz
}
else if(USARTx == UART5)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5,ENABLE);// Enable USART5使能或者失能APB1外設(shè)時(shí)鐘 低速36MH
}
USART_DeInit(USARTx);
USART_InitStructure.USART_BaudRate = BaudRate; //波特率
USART_InitStructure.USART_WordLength = WordLength; //數(shù)據(jù)長(zhǎng)度
USART_InitStructure.USART_StopBits = StopBits; //一個(gè)停止位
USART_InitStructure.USART_Parity = Parity; //無校驗(yàn)
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //禁止硬件流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //Receive and transmit enabled
USART_Init(USARTx, &USART_InitStructure); // Configure the USART1
USART_ClockInitStructure.USART_Clock = USART_Clock_Disable; //USART Clock disabled
USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low; //USART CPOL: Clock is active low
USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge; //USART CPHA: Data is captured on the second edge
USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable; //USART LastBit: The clock pulse of the last data bit is not output to the SCLK pin
USART_ClockInit(USARTx, &USART_ClockInitStructure);
USART_ITConfig(USARTx, USART_IT_RXNE, ENABLE); //允許接收寄存器非空中斷
USART_Cmd(USARTx, ENABLE); // Enable USARTx
}
三.串口結(jié)構(gòu)體
a.使能串口中斷后,串口在接收到數(shù)據(jù)后會(huì)進(jìn)入中斷函數(shù),中斷函數(shù)就是我們要對(duì)數(shù)據(jù)進(jìn)行整理的地方。(中斷函數(shù)中不能寫大量代碼,有可能導(dǎo)下次中斷來之前,數(shù)據(jù)還未處理完成,所以數(shù)據(jù)分析在后文)。
b.stm32的串口數(shù)量很多,因此將每個(gè)串口在運(yùn)行中所需要的變量整合寫進(jìn)一個(gè)結(jié)構(gòu)體中,相對(duì)更加方面快捷。按照本人經(jīng)常使用的數(shù)據(jù),在串口對(duì)應(yīng)的.H文件中寫出的結(jié)構(gòu)體如下,之后在.C文件中對(duì)使用的結(jié)構(gòu)體進(jìn)行初始化就可以了。
#define SBUF_SIZE 255 //數(shù)據(jù)緩沖區(qū)大小
#define RBUF_SIZE 255
typedef struct
{
u8 sbuf[SBUF_SIZE]; //發(fā)送數(shù)組
u8 rbuf[RBUF_SIZE]; //接收數(shù)組
u8 temporary_buf[RBUF_SIZE]; //接收臨時(shí)存儲(chǔ)buf
u16 sbuf_head; //需要發(fā)送數(shù)據(jù)的位置
u16 sbuf_tail; //需要發(fā)送數(shù)據(jù)的結(jié)束位置
u16 rbuf_head; //需要發(fā)送數(shù)據(jù)的位置
u16 rbuf_tail; //需要發(fā)送數(shù)據(jù)的結(jié)束位置
u8 com_already; //接收到數(shù)據(jù)
u32 com_timeout; //接收到數(shù)據(jù)到處理數(shù)據(jù)間延時(shí)
uint32_t rc; //計(jì)數(shù)
}UART_InformationType;
//使用幾個(gè)串口就可以創(chuàng)建幾個(gè)結(jié)構(gòu)體
extern UART_InformationType UART1_Information; //創(chuàng)建串口1的結(jié)構(gòu)體
extern UART_InformationType UART2_Information;
extern UART_InformationType UART3_Information;

四.串口中斷
a.結(jié)構(gòu)體寫好后,接下來就是中斷函數(shù),串口中斷來對(duì)接受的數(shù)據(jù)進(jìn)行整理,如果串口處理數(shù)據(jù)的方法相差不是太大,都可以使用此中斷函數(shù)來整理接收的數(shù)據(jù)。
b.串口數(shù)據(jù)整理的思想,以數(shù)據(jù)接受為例:
1.開辟兩個(gè)256字節(jié)的數(shù)組,用來存放接受或者發(fā)送的數(shù)據(jù)。
2.數(shù)據(jù)接收:給256個(gè)字節(jié)設(shè)數(shù)據(jù)頭尾,每當(dāng)進(jìn)入一次中斷,有一個(gè)數(shù)據(jù)傳入就把數(shù)據(jù)寫到結(jié)構(gòu)體的rbuf數(shù)組中保存起來,同時(shí)把數(shù)據(jù)頭rbuf_head 值+1,當(dāng)數(shù)據(jù)頭超過數(shù)據(jù)緩沖區(qū)大小時(shí)清零。
3.數(shù)據(jù)處理:有數(shù)據(jù)傳入就把標(biāo)志位 com_already 置1,處理完數(shù)據(jù)后清0,同時(shí)更新數(shù)據(jù)尾部rbuf_tail的數(shù)值。
4.例如:剛上電時(shí)都為0,傳入8個(gè)字節(jié)正確的數(shù)據(jù),先將8個(gè)字節(jié)的數(shù)據(jù)保存在結(jié)構(gòu)體中,同時(shí)每傳入一個(gè)字節(jié)數(shù)據(jù)頭加1。置1標(biāo)志位等待數(shù)據(jù)處理函數(shù)。 數(shù)據(jù)處理函數(shù)處理完成數(shù)據(jù)后將數(shù)據(jù)尾加8等于數(shù)據(jù)頭。(此時(shí)假設(shè)數(shù)據(jù)都是正確的情況,這樣就可以造成循環(huán)可以保存接受的每一個(gè)數(shù)據(jù),詳情請(qǐng)看第5節(jié)代碼。)
c.中斷函數(shù)中只寫了數(shù)據(jù)的接受,對(duì)于stm32來說,數(shù)據(jù)發(fā)送直接封裝為函數(shù)更加簡(jiǎn)單方便。
/********************************************************************
*函數(shù)描述:usart1中斷
*入口說明:無
*返回說明:無
**********************************************************************/
void USART1_IRQHandler(void)
{
Dispose_USART_IRQHandler(USART1,&UART1_Information);
}
/*********************************************************************
*函數(shù)描述:usart2中斷
*入口說明:無
*返回說明:無
**********************************************************************/
void USART2_IRQHandler(void)
{
Dispose_USART_IRQHandler(USART2,&UART2_Information);
}
/*********************************************************************
*函數(shù)描述:usart3中斷
*入口說明:無
*返回說明:無
**********************************************************************/
void USART3_IRQHandler(void)
{
Dispose_USART_IRQHandler(USART3,&UART3_Information);
}
/*********************************************************************
*函數(shù)描述:usart中斷,處理接受的數(shù)據(jù)
*入口說明:USART_TypeDef* USARTx UART_InformationType* USARTx_Information
中斷的串口 對(duì)應(yīng)串口的結(jié)構(gòu)體
*返回說明:無
**********************************************************************/
void Dispose_USART_IRQHandler(USART_TypeDef* USARTx,UART_InformationType* USARTx_Information)
{
if(USART_GetITStatus(USARTx, USART_IT_RXNE) != RESET) //接收數(shù)據(jù)
{
USARTx_Information->rbuf[USARTx_Information->rbuf_head++] = (u8)USARTx->DR;
if(USARTx_Information->rbuf_head == SBUF_SIZE)
{
USARTx_Information->rbuf_head = 0;
}
USARTx_Information->com_already = USART_SBUF_NO_EMPTY;//USART_SBUF_NO_EMPTY自定義的數(shù)值為1
// USARTx_Information->com_timeout = Timer_1ms; //更新空閑計(jì)時(shí)
}
}
/*********************************************************************
*函數(shù)描述:usart發(fā)送數(shù)據(jù)
*入口說明:USARTx:選擇USART通道
data:發(fā)送的數(shù)據(jù)
data_long:數(shù)據(jù)長(zhǎng)度
*返回說明:無
**********************************************************************/
void Send_Usart_data(USART_TypeDef* USARTx,u8* data,u16 data_long)
{
u16 a;
for(a=0;a<data_long;a++) //發(fā)送數(shù)據(jù)
{
USART_SendData(USART1,*(data+a));
while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
}
}
五.數(shù)據(jù)處理
a.串口接收完數(shù)據(jù)后,在數(shù)據(jù)處理函數(shù)中,處理相應(yīng)的數(shù)據(jù)。
在實(shí)際使用中串口通信一般會(huì)規(guī)定相應(yīng)的協(xié)議舉例下面兩種,實(shí)際中協(xié)議復(fù)雜多樣,本例子以2為基礎(chǔ)進(jìn)行代碼編寫。
1. 01 03 00 00 00 02 crcl crch
//常用的MODBUS協(xié)議格式 01為讀取的設(shè)備地址,03為功能碼,00 00 為讀取的寄存器 00 02 為讀取的數(shù)據(jù) ,后兩位為數(shù)據(jù)校驗(yàn)
2. FA 04 00 02 xx xx FF
//FA為規(guī)定的協(xié)議頭部 04為功能碼 00 02 為數(shù)據(jù)長(zhǎng)度 xx xx 為數(shù)據(jù) FF為數(shù)據(jù)結(jié)尾
串口接收是,我們會(huì)收到一大串?dāng)?shù)據(jù),我們首先要判斷一串?dāng)?shù)據(jù)第一位,用IF來判斷第一位是不是我們想要的數(shù)據(jù),不是的話就判斷下一位,知道找到正確數(shù)據(jù),最后對(duì)接收到的數(shù)據(jù)進(jìn)行校驗(yàn),看收到的一大串?dāng)?shù)據(jù)是否正確,從而進(jìn)行下一步處理。

/*********************************************************************
*函數(shù)名稱: Usart1_Dispos_Send_dat
*函數(shù)描述:usart1處發(fā)送的數(shù)據(jù)
*入口說明:無
*返回說明:無
**********************************************************************/
void Usart1_Dispos_Send_command(void)
{
u16 i,j = 0;
u16 m,length;
u16 crc16 = 0;
if(!UART1_Information.com_already) //串口標(biāo)志位未使能就返回
return;
UART1_Information.com_already = USART_SBUF_EMPTY; //更新串口標(biāo)志位
i = UART1_Information.rbuf_tail;
while(i != UART1_Information.rbuf_head) //如果此時(shí)的數(shù)據(jù)尾等于數(shù)據(jù)頭退出循環(huán)
{
if(UART1_Information.rbu== 0xfa) //判斷數(shù)據(jù)頭是不是想要的數(shù)據(jù)
{
m = i;
length = UART1_Information.rbuf[i+3]+5; //如果數(shù)據(jù)正確,判斷數(shù)據(jù)長(zhǎng)度,rbuf[i+3]為數(shù)據(jù)長(zhǎng)度,再加5為一包數(shù)據(jù)的長(zhǎng)度
for(j = 0;j < length ;j++) //提取每一幀數(shù)據(jù),把數(shù)據(jù)放進(jìn)臨時(shí)數(shù)組
{
if(m == UART1_Information.rbuf_head) //提取過程中數(shù)據(jù)尾等于數(shù)據(jù)頭說明長(zhǎng)度不夠不是正確的數(shù)據(jù),返回
return;
UART1_Information.temporary_buf[j] = UART1_Information.rbuf[m++];
if(m == RBUF_SIZE)
m = 0;
}
if(UART1_Information.temporary_buf[j-1] == 0xff) //有效數(shù)據(jù)
{
Dispose_SVR_Commd(UART1_Information.temporary_buf); //處理臨時(shí)數(shù)組數(shù)據(jù)
UART1_Information.rbuf_tail = m;
i=m;
}
else //無效數(shù)據(jù)i++進(jìn)行下一位的判斷
{
i++;
if(i == RBUF_SIZE) //如果i等于數(shù)組上限清零
i = 0;
}
} else //如果第一位不是想要的數(shù)據(jù),進(jìn)行下一位判斷
{
i++;
if(i == RBUF_SIZE)
i = 0;
}
}
}
/*********************************************************************
*函數(shù)名稱: Dispos_Commd
*函數(shù)描述:處理服務(wù)器發(fā)送的指令
*入口說明:P_tbuf:保存服務(wù)器指令數(shù)組的指針
*返回說明:無
**********************************************************************/
void Dispos_Commd(u8 * p)
{
u8 function,length;
u16 register_addr;
function = *(p+1);
register_addr = *(p+3);
length= *p;
if(function==0x04) //功能碼判,功能嗎為自定義的功能
Write_Data(UART1_Information.temporary_buf,length); //寫入數(shù)據(jù)
//if else() {}
else{} //
return;
}
/*********************************************************************
*函數(shù)名稱: Write_Data
*函數(shù)描述:寫入數(shù)據(jù)
*入口說明:buf 要寫入的數(shù)據(jù) ,length 要寫入的數(shù)據(jù)長(zhǎng)度
*返回說明:無
**********************************************************************/
void Write_Data(u8 *p,u8 length)
{
u8 length = 0;
u8 buf[10]={0};
//自己定義寫到flash中或者各種地方
//下列數(shù)據(jù)是需要的返回的數(shù)據(jù),可以寫數(shù)據(jù)返回成功,寫可以返回一些其他數(shù)據(jù),供發(fā)送者觀看,或者判段是否接收成功
buf[length++] = 0xfa;
buf[length++] = 0x04;
buf[length++] = 0x00;
buf[length++] = 0x02;
buf[length++] = 0x00;
buf[length++] = 0x00;
buf[length++] = 0xff;
Send_Usart_data(USART1,buf,length);
}
|
-
-
STM32F103X模板.7z
2021-9-18 00:25 上傳
點(diǎn)擊文件名下載附件
下載積分: 黑幣 -5
177.76 KB, 下載次數(shù): 18, 下載積分: 黑幣 -5
評(píng)分
-
查看全部評(píng)分
|