找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開始

搜索
查看: 3784|回復(fù): 1
打印 上一主題 下一主題
收起左側(cè)

Stm32串口的無(wú)阻塞收發(fā)調(diào)試

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:344395 發(fā)表于 2019-9-6 18:09 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
Stm32 芯片目前具有一定的用戶,例程也很多。大多數(shù)僅僅是“ demo”的作用,往往只注重于功能的實(shí)現(xiàn),意思就是說(shuō),看,這個(gè)usart 功能這樣就可以實(shí)現(xiàn)了。但應(yīng)用于一個(gè)運(yùn)行穩(wěn)定的產(chǎn)品還需要很多技巧。

串口應(yīng)用大概可以說(shuō)在mcu 應(yīng)用來(lái)說(shuō)是最廣的,其主要作用就是收發(fā),它在應(yīng)用中又表現(xiàn)出兩種類型,客戶端( client )和服務(wù)端( server)。通信就是人與人聊天,人與人聊天的表現(xiàn)把人分為為問(wèn)者和答者,或者說(shuō)采訪者和受訪者。也可以說(shuō)主從結(jié)構(gòu)。先說(shuō)客戶端,通常有兩者做法,一是無(wú)阻塞式,一種是有阻塞式。前者是發(fā)出命令后,做一全局變量標(biāo)識(shí),記下我前一次發(fā)的什么命令,然后等待收數(shù)據(jù)的消息,在消息處理過(guò)程中根據(jù)命令標(biāo)識(shí)來(lái)對(duì)收到的數(shù)據(jù)進(jìn)行相應(yīng)處理。后者有阻塞式做法就是一個(gè)函數(shù),發(fā)出命令后等待收到正確數(shù)據(jù)并解析。在等待的過(guò)程中可以用TickCount 做timeout,超過(guò)一定時(shí)間還沒(méi)收到數(shù)據(jù)可以退出返回false,解決死等的問(wèn)題。因?yàn)椴僮飨到y(tǒng)有多線程,前者可以在主線程中寫出穩(wěn)定的通信,后者在線程中應(yīng)用比較多。
服務(wù)端的做法思想和客戶端不一樣,是受訪者,要做到有求必答。服務(wù)端重中之重的還是穩(wěn)定第一。當(dāng)然,收到正確的請(qǐng)求后到應(yīng)答可以允許有一定的“思考”時(shí)間, 。我通常是這樣做,做一全局變量用于放接收到的數(shù)據(jù),接收的地方只管往接收容器里裝,發(fā)現(xiàn)滿了就全部倒掉再裝,其它不用管。在主循環(huán)中解析收到的幀數(shù)據(jù)做出應(yīng)答,解析過(guò)程中對(duì)不符合協(xié)議的數(shù)據(jù)可以根據(jù)協(xié)議做出錯(cuò)誤回應(yīng)或不做任何反應(yīng)。完成后把收容器里的數(shù)據(jù)清空。下面的代碼實(shí)現(xiàn)了在stm32 的dma 的usart 的收發(fā),體現(xiàn)了上述的一些思想。我用了systick 實(shí)現(xiàn)了超時(shí)的概念,有說(shuō)uart 的idle (總線空閑)本身就是一個(gè)超時(shí),我又想例如和一個(gè)口吃者“聊天“用它做超時(shí)是不是不太友善? 這一點(diǎn)我沒(méi)做深究。
代碼中含有485 方向的控制,一開始是在dma 的中斷控制的,但總是少兩個(gè)字節(jié),我就在發(fā)送的時(shí)候多發(fā)2 個(gè)字節(jié),也可以用。請(qǐng)讀者選擇吧。

下面是.h 文件輸出函數(shù)
void USART1_GPIOConfig(void);
void USART1_Config(void);
void USART1_NVICConfig(void);
void USART1_SendString( char* data); // 無(wú)阻塞發(fā)送字符串函數(shù)
void USART1_SendData(u8* data,u8 len);// 無(wú)阻塞發(fā)送函數(shù)
下面是.c 文件
#define DMA_USART1_BUFLEN 32 // 保存DMA 每次數(shù)據(jù)傳送的長(zhǎng)度
u8 USART1_SendBuff[DMA_USART1_BUFLEN];
u8 USART1_TX_Finish=Co_TRUE; // USART 發(fā)送完成標(biāo)志量
u8 USART1_ReceivBuff[DMA_USART1_BUFLEN];
void USART1_GPIOConfig(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; /* 復(fù)用推挽輸出模式*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; /* 輸出最大頻率為50MHz */
GPIO_Init(GPIOA, &GPIO_InitStruct);
/* 配置USART1 Rx (PA10) */
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; /* 浮空輸入模式*/

GPIO_Init(GPIOA, &GPIO_InitStruct);
/// 485 dir
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init( GPIOA, &GPIO_InitStruct);
GPIO_ResetBits( GPIOA, GPIO_Pin_8);// dir
}
void USART1_NVICConfig(void)
{
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = DMA1_Channel4_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
NVIC_InitStruct.NVIC_IRQChannelCmd =ENABLE;
NVIC_Init(&NVIC_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel=DMA1_Channel5_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStruct.NVIC_IRQChannelSubPriority=2;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
void USART1_Config(void)
{
USART_InitTypeDef USART_InitStruct;
DMA_InitTypeDef DMA_InitStructure;
/* USART and USART2 configured as follow:
- BaudRate = 9600 baud
- Word Length = 8 Bits
- One Stop Bit
- No parity
- Hardware flow control disabled (RTS and CTS signals)
- Receive and transmit enabled */
USART_InitStruct.USART_BaudRate = 9600;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStruct);
USART_ITConfig(USART1,USART_IT_IDLE, ENABLE);// 空閑中斷
USART_ITConfig(USART1, USART_IT_TC, ENABLE);
USART_Cmd(USART1, ENABLE);
USART_ClearFlag(USART1,USART_FLAG_TC);///*CPU 的小缺陷:串口配置好,如果直接Send,則
第1 個(gè)字節(jié)發(fā)送不出去?如下語(yǔ)句解決第1 個(gè)字節(jié)無(wú)法正確發(fā)送出去的問(wèn)題*/
////////////////////////////////// 發(fā)送dma////////////////////////////////////////////////
DMA_DeInit(DMA1_Channel4); //將DMA 的通道1 寄存器重設(shè)為缺省值
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&((USART_TypeDef*)USART1)->DR);
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART1_SendBuff; //DMA 內(nèi)存基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //數(shù)據(jù)傳輸方向,
DMA_InitStructure.DMA_BufferSize = DMA_USART1_BUFLEN; //DMA 通道的DMA 緩存的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外設(shè)地址寄存器不變
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //內(nèi)存地址寄存器遞增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //數(shù)據(jù)寬度為8 位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 數(shù)據(jù)寬度為8 位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常緩存模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA 通道x 擁有中優(yōu)先級(jí)
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA 通道x 沒(méi)有設(shè)置為內(nèi)存到內(nèi)存?zhèn)鬏?br /> DMA_Init(DMA1_Channel4, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel4,DISABLE);
DMA_ITConfig(DMA1_Channel4,DMA_IT_TE,ENABLE);// 錯(cuò)誤中斷
DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);// 完成中斷
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
//接收dma///////////////////////////////////////////////
DMA_DeInit(DMA1_Channel5);
DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)(&((USART_TypeDef*)USART1)->DR);
DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)USART1_ReceivBuff;
DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize=DMA_USART1_BUFLEN;
DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode=DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority=DMA_Priority_High;
DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;
DMA_Init(DMA1_Channel5,&DMA_InitStructure);
DMA_ITConfig(DMA1_Channel5,DMA_IT_TC,ENABLE);

DMA_ITConfig(DMA1_Channel5,DMA_IT_TE,ENABLE);
USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
DMA_Cmd(DMA1_Channel5,ENABLE);
}
void DMA1_Channel4_IRQHandler(void)
{
DMA_ClearITPendingBit(DMA1_IT_TC4);
DMA_ClearITPendingBit(DMA1_IT_TE4);
DMA_Cmd(DMA1_Channel4,DISABLE);
USART1_TX_Finish=Co_TRUE;
}
void DMA1_Channel5_IRQHandler(void){
DMA_ClearITPendingBit(DMA1_IT_TC5);
DMA_ClearITPendingBit(DMA1_IT_TE5);
DMA_Cmd(DMA1_Channel5,DISABLE);
pc_AppendData(USART1_ReceivBuff,DMA_USART1_BUFLEN);
DMA1_Channel5->CNDTR=DMA_USART1_BUFLEN;// 重裝填
DMA_Cmd(DMA1_Channel5,ENABLE);
}
void USART1_IRQHandler(void)
{
u16 alen;
if(USART_GetITStatus(USART1,USART_IT_IDLE)!=RESET){// 如果為空閑總線中斷?
USART_ClearITPendingBit(USART1,USART_IT_IDLE);
DMA_Cmd(DMA1_Channel5,DISABLE);// 關(guān)閉DMA, 防止處理其間有數(shù)據(jù)
alen=DMA_USART1_BUFLEN-DMA_GetCurrDataCounter(DMA1_Channel5);
if(alen>0){
pc_AppendData(USART1_ReceivBuff,alen);
}
DMA_ClearFlag(DMA1_FLAG_GL5|DMA1_FLAG_TC5|DMA1_FLAG_TE5|DMA1_FLAG_HT5);// 清標(biāo)
志?
DMA1_Channel5->CNDTR=DMA_USART1_BUFLEN;// 重裝填?
DMA_Cmd(DMA1_Channel5,ENABLE);// 處理完,重開DMA
//讀SR 后讀DR 清除Idle
alen=USART1->SR;
alen=USART1->DR;
}
if(USART_GetITStatus(USART1,USART_IT_PE|USART_IT_FE|USART_IT_NE)!=RESET){// 出錯(cuò)
USART_ClearITPendingBit(USART1,USART_IT_PE|USART_IT_FE|USART_IT_NE);
}

//發(fā)送中斷處理
if(USART_GetITStatus(USART1, USART_IT_TC) != RESET){
USART_ClearITPendingBit(USART1, USART_IT_TC); //清除中斷標(biāo)志
GPIO_ResetBits( GPIOA, GPIO_Pin_8);
}
}
void USART1_SendString(char* data)
{
USART1_SendData((u8*)data,strlen(data));
}
void USART1_SendData(u8* data,u8 len)
{
memcpy(USART1_SendBuff,data,len);
while(USART1_TX_Finish==0);
DMA_Cmd(DMA1_Channel4,DISABLE);
GPIO_SetBits( GPIOA, GPIO_Pin_8);
DMA1_Channel4->CNDTR=len;
USART1_TX_Finish=0;
DMA_Cmd(DMA1_Channel4,ENABLE);
}
上述代碼實(shí)現(xiàn)了stm32 的串口1 的收發(fā)功能。應(yīng)用說(shuō)明如下:
實(shí)現(xiàn)windows 操作中的TickCount 方法:
U32 sysTickCount;
void SysTick_Handler(void)
{
sysTickCount++;
}
實(shí)現(xiàn)接收容器:
#define PC_LEN 48
u8 pc_receivdata[IDCPC_LEN]; // 用來(lái)乘放接收數(shù)據(jù)
u8 pc_count=0; // 接收字節(jié)數(shù)
U32 pc_lastrec; // 最后一次接收的tickcount 值
void pc_AppendData(u8* value,u8 alen){
if((pc_count+alen)>PC_LEN)
pc_count=0;
if(alen<=PC_LEN){
memcpy(&pc_receivdata[pc_count],value,alen);
pc_count+=alen;
pc_lastrec = sysTickCount;
}
}
然后在主循環(huán)中檢測(cè)接收數(shù)據(jù):

u32 tvalue;
if(pc_count>0){
tvalue = sysTickCount - pc_lastrec;
if(tvalue>3){ // 這里3 可以根據(jù)systickcount 的頻率修改適當(dāng),可以取20ms
⋯⋯⋯ ..//分析幀數(shù)據(jù)
USART4_SendData(..); 調(diào)用無(wú)阻塞發(fā)送應(yīng)答
idcpc_count=0; // 清空容器
}
}

評(píng)分

參與人數(shù) 1黑幣 +50 收起 理由
admin + 50 共享資料的黑幣獎(jiǎng)勵(lì)!

查看全部評(píng)分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏2 分享淘帖 頂 踩

相關(guān)帖子

回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:731155 發(fā)表于 2020-5-12 23:28 | 只看該作者
感謝分享,但好像沒(méi)有看到應(yīng)答,超時(shí)請(qǐng)求重發(fā)
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表