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

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外設時鐘 高速72MHz
}
else if(USARTx == USART2)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);// Enable USART2使能或者失能APB1外設時鐘 低速36MHz
}
else if(USARTx == USART3)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);// Enable USART3使能或者失能APB1外設時鐘 低速36MHz
}
else if(USARTx == UART4)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4,ENABLE);// Enable USART4使能或者失能APB1外設時鐘 低速36MHz
}
else if(USARTx == UART5)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5,ENABLE);// Enable USART5使能或者失能APB1外設時鐘 低速36MH
}
USART_DeInit(USARTx);
USART_InitStructure.USART_BaudRate = BaudRate; //波特率
USART_InitStructure.USART_WordLength = WordLength; //數據長度
USART_InitStructure.USART_StopBits = StopBits; //一個停止位
USART_InitStructure.USART_Parity = Parity; //無校驗
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
}
三.串口結構體
a.使能串口中斷后,串口在接收到數據后會進入中斷函數,中斷函數就是我們要對數據進行整理的地方。(中斷函數中不能寫大量代碼,有可能導下次中斷來之前,數據還未處理完成,所以數據分析在后文)。
b.stm32的串口數量很多,因此將每個串口在運行中所需要的變量整合寫進一個結構體中,相對更加方面快捷。按照本人經常使用的數據,在串口對應的.H文件中寫出的結構體如下,之后在.C文件中對使用的結構體進行初始化就可以了。
#define SBUF_SIZE 255 //數據緩沖區大小
#define RBUF_SIZE 255
typedef struct
{
u8 sbuf[SBUF_SIZE]; //發送數組
u8 rbuf[RBUF_SIZE]; //接收數組
u8 temporary_buf[RBUF_SIZE]; //接收臨時存儲buf
u16 sbuf_head; //需要發送數據的位置
u16 sbuf_tail; //需要發送數據的結束位置
u16 rbuf_head; //需要發送數據的位置
u16 rbuf_tail; //需要發送數據的結束位置
u8 com_already; //接收到數據
u32 com_timeout; //接收到數據到處理數據間延時
uint32_t rc; //計數
}UART_InformationType;
//使用幾個串口就可以創建幾個結構體
extern UART_InformationType UART1_Information; //創建串口1的結構體
extern UART_InformationType UART2_Information;
extern UART_InformationType UART3_Information;

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

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