在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

STM32如何高效接收串口數據?

GReq_mcu168 ? 來源:玩轉單片機 ? 作者:玩轉單片機 ? 2020-12-09 16:48 ? 次閱讀

硬件:stm32f103cbt6
軟件:STM32F10x_StdPeriph_Lib_V3.5.0

DMA,直接內存存取,可以用它的雙手釋放CPU的靈魂,所以,本文通過USART3進行串口收發,接受使用DMA的方式,無需CPU進行干預,當接受完成之后,數據可以直接從內存的緩沖區讀取,從而減少了CPU的壓力。

具體的代碼實現如下:

usart_driver.h 封裝了接口,數據接收回調函數類型,基本數據結構等;

usart_driver.c 函數原型實現,中斷服務函數實現等;

拷貝這兩個文件即可,可以根據目錄下的參考用例,進行初始化。

頭文件usart_driver.h已經聲明了外部函數可能用到的接口;

USART3_DR的地址

因為USART3接收到數據會存在DR寄存器中,而DMA控制器則負責將該寄存器中的內容一一搬運到內存的緩沖區中(比如你定義的某個數組中),所以這里需要告訴DMA控制去哪里搬運,因此需要設置USART3_DR的總線地址。

USART3的基址如下圖所示;

9037015a-2e2b-11eb-a64d-12bb97331649.png

USART3的基址

DR寄存器的偏移地址如下圖所示;

9087af60-2e2b-11eb-a64d-12bb97331649.png

DR偏移地址

所以最終地址為:0x40004800 + 0x004#define USART_DR_Base 0x40004804

DMA的通道

因為有很多外設都可以使用DMA,比如ADCI2C,SPI等等,所以,不同的外設就要選擇屬于自己的DMA通道,查找參考手冊;

90a75b8a-2e2b-11eb-a64d-12bb97331649.png

DMA通道

因此USART3_RX在這里會使用DMA1的通道3,這都是硬件上已經預先分配好的,我們需要遵循這個規則。所以在代碼中我們做出相應的定義;如下所示;

#defineUSART_Rx_DMA_ChannelDMA1_Channel3

DMA的中斷

DMA支持三種中斷:傳輸過半,傳輸完成,傳輸出錯;

90e44446-2e2b-11eb-a64d-12bb97331649.png

DMA中斷

因此在使用是相當安全也相當靈活,而本文只是用了傳輸完成中斷;如下定義了,傳輸完成中斷的標志位,DMA1_FLAG_TC3也就對應了圖中的TCIF;

#defineUSART_Rx_DMA_FLAGDMA1_FLAG_TC3

USART接收回調函數

在STM32的HAL中封裝了大量外設的回調函數,使用起來十分方便,但是標準庫中則沒有這樣的做法,但是這里我們可以自己實現,rx_cbk就是回調,即串口數據接收完成就會執行已經注冊的回調函數;

typedefvoid(*rx_cbk)(void*args);

通過使用接口usart_set_rx_cbk進行回調函數的注冊,pargs為將傳遞的參數指針;

voidusart_set_rx_cbk(uart_mod_t*pmod,rx_cbkpfunc,void*pargs);

頭文件源碼

#ifndefUSART_DRIVER_H #defineUSART_DRIVER_H #include #include /*Privatefunctionprototypes-----------------------------------------------*/ #defineUSE_MICROLIB_USART1 #ifUSE_MICROLIB_USART #ifdef__GNUC__ /*WithGCC/RAISONANCE,smallprintf(optionLDLinker->Libraries->Smallprintf setto'Yes')calls__io_putchar()*/ #definePUTCHAR_PROTOTYPEint__io_putchar(intch) #else #definePUTCHAR_PROTOTYPEintfputc(intch,FILE*f) //#defineGETCHAR_PROTOTYPEintfgetc(FILE*f) #endif/*__GNUC__*/ externPUTCHAR_PROTOTYPE; #else #endif //default8N1 #defineCOM_PORTUSART3 #defineTX_PINGPIO_Pin_10 #defineRX_PINGPIO_Pin_11 #defineBAUDRATE115200 #defineIRQ_UART_PRE3 #defineIRQ_UART_SUB3 #defineUSART_Rx_DMA_ChannelDMA1_Channel3 #defineUSART_Rx_DMA_FLAGDMA1_FLAG_TC3 #defineUSART_DR_Base0x40004804 #defineUSART_BUF_SIZE((uint16_t)16) typedefvoid(*rx_cbk)(void*args); structuart_mod{ uint8_trx_buf[USART_BUF_SIZE]; uint8_trx_dat_len; uint8_thead; uint8_ttail; void(*init)(void); void*pargs; rx_cbkpfunc_rx_cbk; }; typedefstructuart_moduart_mod_t; externuart_mod_tuser_uart_mod; voidusart_init(void); voidusart_set_rx_cbk(uart_mod_t*pmod,rx_cbkpfunc,void*pargs); voidusart_send_char(charch); voidusart_test_echo(void); uint8_tusart_recv_char(void); intusart_printf(constchar*fmt,...); //externGETCHAR_PROTOTYPE; #endif

DMA的基本配置

串口接收DMA的配置在函數dma_init中;

staticvoiddma_init(void)

已經定義了數據緩沖區,如下:

uint8_tRxBuffer[USART_BUF_SIZE]={0};

因此需要在DMA的配置中設置USART_DR的地址,和數據緩沖區的地址,以及兩者的大小;還有就是數據流向;

寄存器流向內存;

內存流向寄存器;這個需要搞清楚;相關配置如下所示;

DMA_InitStructure.DMA_PeripheralBaseAddr=USART_DR_Base; DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)RxBuffer; DMA_InitStructure.DMA_BufferSize=USART_BUF_SIZE; DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC;

注意:DMA_DIR_PeripheralSRC表示,外設作為源地址,數據是從外設寄存器流向內存,即DMA會把數據從地址USART_DR_Base搬運到RxBuffer去。如果這個地方搞錯,會導致RxBuffer始終沒有你想要的數據。

環形隊列接收數據

線性緩沖區會因為緩沖器接收數據已滿導致無法繼續接收的問題;而環形隊列進行接收的話,會自動進行覆蓋,這樣一來,在讀取數據的時候,也要配置一個環形隊列進行數據處理,下面的配置是把DMA配置為循環模式;

DMA_InitStructure.DMA_Mode=DMA_Mode_Circular;

在結構體user_uart_mod中,則用兩個變量分別指向隊首head和隊尾tail;具體數據的讀取在函數USART3_IRQHandler中,會把數據從內存的RxBuffer讀取到結構體user_uart_mod的成員變量rx_buf中;最終調用回調函數。

函數原型

usart_driver.c

#include #include #include"stm32f10x_usart.h" #include"usart_driver.h" uint8_tRxBuffer[USART_BUF_SIZE]={0}; uart_mod_tuser_uart_mod={ .rx_dat_len=0, .head=0, .tail=0, .pfunc_rx_cbk=NULL, .pargs=NULL }; staticUSART_InitTypeDefUSART_InitStructure; staticvoidrcc_init(void){ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); /*EnableGPIOclock*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |RCC_APB2Periph_AFIO,ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE); } staticvoidgpio_init(void){ GPIO_InitTypeDefGPIO_InitStructure; /*ConfigureUSARTTxasalternatefunctionpush-pull*/ GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin=TX_PIN; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO_InitStructure); /*ConfigureUSARTRxasinputfloating*/ GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin=RX_PIN; GPIO_Init(GPIOB,&GPIO_InitStructure); } staticvoiddma_init(void){ DMA_InitTypeDefDMA_InitStructure; /*USARTy_Tx_DMA_Channel(triggeredbyUSARTyTxevent)Config*/ DMA_DeInit(USART_Rx_DMA_Channel); DMA_InitStructure.DMA_PeripheralBaseAddr=USART_DR_Base; DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)RxBuffer; //DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize=USART_BUF_SIZE; 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_VeryHigh; DMA_InitStructure.DMA_M2M=DMA_M2M_Disable; DMA_Init(USART_Rx_DMA_Channel,&DMA_InitStructure); } staticvoidirq_init(void){ NVIC_InitTypeDefNVIC_InitStructure; /*EnabletheUSART3_IRQnInterrupt*/ NVIC_InitStructure.NVIC_IRQChannel=USART3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=IRQ_UART_PRE; NVIC_InitStructure.NVIC_IRQChannelSubPriority=IRQ_UART_SUB; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); } voidusart_send_char(charch){ /*Loopuntiltheendoftransmission*/ //while(USART_GetFlagStatus(COM_PORT,USART_FLAG_TC)==RESET){} while((COM_PORT->SR&USART_FLAG_TC)!=USART_FLAG_TC){ } USART_SendData(COM_PORT,(uint8_t)ch); } uint8_tusart_recv_char(){ /*WaitthebyteisentirelyreceivedbyUSARTy*/ //while(USART_GetFlagStatus(COM_PORT,USART_FLAG_RXNE)==RESET){} while((COM_PORT->SR&USART_FLAG_RXNE)!=USART_FLAG_RXNE){ } /*StorethereceivedbyteintheRxBuffer1*/ return(uint8_t)USART_ReceiveData(COM_PORT); } intusart_printf(constchar*fmt,...) { uint8_ti=0; uint8_tusart_tx_buf[128]={0}; va_listap; va_start(ap,fmt); vsprintf((char*)usart_tx_buf,fmt,ap); va_end(ap); while(usart_tx_buf[i]&&ipargs=pargs; pmod->pfunc_rx_cbk=pfunc; } voidDMA1_Channel3_IRQHandler(void){ if(DMA_GetITStatus(USART_Rx_DMA_FLAG)==SET){ DMA_ClearITPendingBit(USART_Rx_DMA_FLAG); } } /** *@briefThisfunctionhandlesUSART3globalinterruptrequest. *@paramNone *@retvalNone */ voidUSART3_IRQHandler(void) { uint8_tbuf[USART_BUF_SIZE]; uint16_trect_len=0; if(USART_GetITStatus(COM_PORT,USART_IT_IDLE)!=RESET) { uint8_ti=0; USART_ReceiveData(COM_PORT); user_uart_mod.head=USART_BUF_SIZE-DMA_GetCurrDataCounter(USART_Rx_DMA_Channel); //fifoisnotfull while(user_uart_mod.head%USART_BUF_SIZE!=user_uart_mod.tail%USART_BUF_SIZE){ user_uart_mod.rx_buf[i++]=RxBuffer[user_uart_mod.tail++%USART_BUF_SIZE]; } user_uart_mod.rx_dat_len=i; //DMA_Cmd(USART_Rx_DMA_Channel,ENABLE); if(user_uart_mod.pfunc_rx_cbk!=NULL){ user_uart_mod.pfunc_rx_cbk(user_uart_mod.pargs); } } USART_ClearITPendingBit(COM_PORT,USART_IT_IDLE); //USART_ClearITPendingBit(COM_PORT,USART_IT_RXNE); } #ifUSE_MICROLIB_USART /** *@briefRetargetstheClibraryprintffunctiontotheUSART. *@paramNone *@retvalNone */ PUTCHAR_PROTOTYPE { /*Placeyourimplementationoffputchere*/ /*e.g.writeacharactertotheUSART*/ USART_SendData(COM_PORT,(uint8_t)ch); /*Loopuntiltheendoftransmission*/ while(USART_GetFlagStatus(COM_PORT,USART_FLAG_TC)==RESET) {} returnch; } #else #pragmaimport(__use_no_semihosting) struct__FILE { inthandle; }; FILE__stdout; int_sys_exit(intx) { x=x; return0; } intfputc(intch,FILE*f) { /*Placeyourimplementationoffputchere*/ /*e.g.writeacharactertotheUSART*/ USART_SendData(COM_PORT,(uint8_t)ch); /*Loopuntiltheendoftransmission*/ while(USART_GetFlagStatus(COM_PORT,USART_FLAG_TC)==RESET) {} returnch; } #endif

參考用例

這里需要調用usart_init,并設置回調函數,如果不設置,則不會執行回調。

voidmotor_get_cmd_from_uart(void*pargs){ if(pargs==NULL){ return; } uart_mod_t*p=pargs; if(p->rx_dat_len>0&&p->rx_dat_len==PACKAGE_SIZE){ if(p->rx_buf[0]==PACKAGE_HEAD &&p->rx_buf[PACKAGE_SIZE-1]==PACKAGE_TAIL){ user_cmd_mod.head=p->rx_buf[0]; user_cmd_mod.cmd.value_n[0]=p->rx_buf[1]; user_cmd_mod.cmd.value_n[1]=p->rx_buf[2]; user_cmd_mod.option=p->rx_buf[3]; user_cmd_mod.data.value_n[0]=p->rx_buf[4]; user_cmd_mod.data.value_n[1]=p->rx_buf[5]; user_cmd_mod.data.value_n[2]=p->rx_buf[6]; user_cmd_mod.data.value_n[3]=p->rx_buf[7]; user_cmd_mod.tail=p->rx_buf[PACKAGE_SIZE-1]; user_cmd_mod.process_flag=1; } } p->rx_dat_len=0; } intmain(void){ usart_init(); usart_set_rx_cbk(&user_uart_mod,motor_get_cmd_from_uart,&user_uart_mod); }

總結

本文簡單介紹了基于STM32基于DMA,利用串口空閑中斷進行串口數據接收的具體配置和實現方法,代碼基于標準庫3.5版本;
因為標準庫ST目前已經不再更新,并且ST提供了cubemx工具可以進行基于HAL庫和LL庫的外設快速配置,從而簡化大量工作;當然為了不掉頭發感覺擼寄存器也不錯,最終適合自己的才是最好的。


責任編輯:lq

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • cpu
    cpu
    +關注

    關注

    68

    文章

    10889

    瀏覽量

    212388
  • STM32
    +關注

    關注

    2270

    文章

    10915

    瀏覽量

    356754
  • 串口
    +關注

    關注

    14

    文章

    1557

    瀏覽量

    76735

原文標題:STM32如何高效接收串口數據?

文章出處:【微信號:mcu168,微信公眾號:硬件攻城獅】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    【代碼分享】基于樂鑫ESP32的串口不定長數據接收方法

    【代碼分享】基于樂鑫ESP32的串口不定長數據接收方法
    的頭像 發表于 11-15 01:02 ?686次閱讀
    【代碼分享】基于樂鑫ESP32的<b class='flag-5'>串口</b>不定長<b class='flag-5'>數據</b><b class='flag-5'>接收</b>方法

    STM32G030F6用串口中斷函數接收數據,發送數據就死機怎么解決?

    平臺介紹: 芯片是使用的STM32G030F6,系統是rt-thread nano-v3.1.5, 使用rtthread studio + cubemx生成工程項目 問題描述:想使用串口中斷方式去
    發表于 07-11 06:44

    求助,使用STM32G030C8T6的串口接收問題求解

    目前使用STM32G030的芯片做了一個485接收從機,由主機STM32F407的芯片發送數據去詢問從機當前數據,但是在使用過程中遇見了一個
    發表于 05-28 06:11

    STM32F0xx_HAL_Driver庫的串口接收數據個數,是不是只能寫成1,一個一個數據接收

    在使用STM32F0xx_HAL_Driver庫進行串口接收數據 HAL_StatusTypeDef HAL_UART_Receive( UART_HandleTypeDef *hu
    發表于 05-14 06:39

    stm32 usb轉串口,android接收有數據丟失要如何處理?

    我用pc上的串門專家可以準確的收取到stm32通過usb轉串口發送的數據,但是在android上用apk接收時會有數據的丟失。這是什么問題啊
    發表于 05-13 08:45

    單片機在串口發送數據時可以接收串口助手發的數據,為什么只能接收兩字節?

    最近做串口通信,單片機在串口發送數據時可以接收串口助手發的數據,不過只能
    發表于 05-08 07:52

    STM32 HAL printf所占用串口不能做中斷接收如何解決?

    目前在使用STM32L053系列MCU,基于HAL庫的程序,硬件串口資源有3個,都已經被使用;其中USART2通過Switch芯片來實現printf打印和讀取傳感器數據;因為USART2串口
    發表于 04-29 09:01

    STM32 hal庫無法接收串口數據是怎么回事?

    型號:STM32L432KC官方開發板 串口使用的是板載的USB下載口以及串口 用法: 在串口初始化后,while(1)之前調用 接收中斷
    發表于 04-29 06:11

    Python怎么讀取STM32串口數據

    =ser.readlines()print(s) 可是什么都讀取不了。如果用買的STM32開發板送的串口助手,能接收數據。板子上燒錄的printf輸出程序。 請問各位大神,Pytho
    發表于 04-24 07:30

    Freertos利用STM32 HAL做串口接收,中斷突然失效的原因?

    Freertos 利用STM32 HAL做串口接收,發送數據正常,沒有通過中斷處理,接收通過中斷接收
    發表于 04-07 07:21

    STM32F030F4串口空閑中斷接收不定長數據,發生中斷后不知道如何計算接收到的字節數?

    我用STM32F030F4串口空閑中斷接收不定長數據,發生中斷后不知道怎樣計算接收到的字節數。
    發表于 04-03 07:12

    STM32F429IGT6 ADC和串口接收中斷同時使用,ADC值出錯的原因?

    STM32F429IGT6,ADC1四個通道通過PollForConversion方式采集,同時串口3通過接收中斷接收傳感器發的數據,此時A
    發表于 03-29 08:03

    為什么stm32h750VBT6串口通信接收不到自己發送過去的數據數據溢出會有影響嗎?

    請問有沒有知道stm32h750VBT6為什么串口通信接收不到自己發送過去的數據數據溢出會有影響嗎?
    發表于 03-15 07:09

    STM32f072在串口接收數據時總發生ORE溢出錯誤,導致主程序被卡死,怎么解決?

    STM32f072在串口接收數據時總發生ORE溢出錯誤,導致主程序被卡死,怎么解決?求大神指導
    發表于 03-08 07:07

    stm32串口接收中斷觸發原理

    配置串口接收中斷使能:在初始化串口時,需要設置相應的控制寄存器來使能串口接收中斷。這通常可以通過設置相應的標志位或使用特定的寄存器位來完成。
    發表于 01-17 15:42 ?8007次閱讀
    <b class='flag-5'>stm32</b><b class='flag-5'>串口</b><b class='flag-5'>接收</b>中斷觸發原理
    主站蜘蛛池模板: 热re99久久精品国产99热| 国产色网址| 欧美69xxx| 色天天躁夜夜躁天干天干| 天天狠天天透天干天天怕处| 天天插天天狠| 久久午夜影院| 91精品啪国产在线观看免费牛牛 | 成人国产三级精品| h视频在线免费观看| 黄黄网| 天天操夜夜爱| 久久国产乱子伦精品免费一| 手机看片神马午夜| 久久久精品免费| 欧美男人的天堂| 欧美深深色噜噜狠狠yyy| 一个综合色| 天天视频色版| 欧美成人 一区二区三区| 国产一级特黄毛片| 天天做天天爱天天爽综合网| 午夜在线影视| 国产永久免费爽视频在线| 狠狠躁夜夜躁人人爽天天miya| 在线观看h视频| 在线免费看影视网站| 999国产精品| 双性受粗大撑开白浊| 亚洲一本视频| 亚洲视频一区网站| 亚洲第一视频区| 亚洲欧美视频在线| 免费的黄色毛片| 夜夜福利| 超级香蕉97视频在线观看一区| 日本不卡视频免费| 在线小视频你懂的| a黄色网| 亚洲一级毛片免费在线观看| 日本特黄特色大片免费播放视频 |