久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

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

QQ登錄

只需一步,快速開(kāi)始

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

STM32單片機(jī)串口DMA數(shù)據(jù)讀取(詳細(xì)代碼)

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:955050 發(fā)表于 2023-4-8 16:12 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
最近重新開(kāi)始學(xué)STM32單片機(jī),搞到串口 DMA 的時(shí)候, 數(shù)據(jù)讀取卡了很長(zhǎng)一段時(shí)間,最終,功夫不負(fù)有心人終于搞出來(lái)了。在此以記錄一下,方便以后查詢(xún)使用。
在調(diào)試的過(guò)程中也遇到了很多bug,有些簡(jiǎn)直就是低級(jí)問(wèn)題,但是還是卡了很久,在此寫(xiě)出來(lái)給自己加深印象,同時(shí)已給后來(lái)者一個(gè)前車(chē)之鑒
1、在32的程序編寫(xiě)中,若使用到了中斷部分,特別是中斷函數(shù)一定要注意,查詢(xún)清除中斷標(biāo)志位到底是什么。
獲取中斷標(biāo)志位 是 USART_GetITStatus 函數(shù),筆者就是沒(méi)弄清 用成了 USART_ GetFlagStatus 函數(shù),
USART_GetITStatus   檢查指定的 USART 中斷發(fā)生與否
USART_ GetFlagStatus 檢查指定的 USART 標(biāo)志位設(shè)置與否
就是這個(gè)問(wèn)題卡了筆者很久,甚至筆者檢查了很多次都沒(méi)注意到

2、中斷函數(shù)的使用, 這個(gè)在使用時(shí)若不注意就會(huì)用錯(cuò),比如 中斷串口2 ,卻使用成了 中斷串口1
關(guān)于中斷函數(shù)的查詢(xún)可以通過(guò)kile5 Startup文件夾 下的 startup_stm32f10x_md.s 進(jìn)行查詢(xún)

3、代碼功能獨(dú)立
這部分主要就是要將各函數(shù)功能進(jìn)行,獨(dú)立。方便理清邏輯,可能很多人覺(jué)得很麻煩,且很多人沒(méi)有此習(xí)慣,但是在學(xué)習(xí)和工作中稍微花點(diǎn)時(shí)間,將其進(jìn)行整理還是很有必要的。
筆者在調(diào)試這部分時(shí),就出現(xiàn)了邏輯混亂的情況,將 GPIO, 串口,DMA 全部寫(xiě)在一起,導(dǎo)致最后混亂。最終每次只會(huì)搬運(yùn)最后一個(gè)字符。具體原因筆者也沒(méi)搞清楚。若有大佬能夠了解,望指點(diǎn)一二。

—————————————————————分割線———————————————

以上廢話,下為正文
DMA 串口數(shù)據(jù)讀取主要就是 將串口的數(shù)據(jù)搬移到內(nèi)存中
在實(shí)際代碼中,筆者定義了一個(gè)char 數(shù)組進(jìn)行存儲(chǔ)。并通過(guò)重定義printf 的方式將 char 數(shù)組內(nèi)的內(nèi)容輸出出來(lái)(后面看大家需不需要,也可以將DMA 數(shù)據(jù)發(fā)送的代碼 分享出來(lái) 嘿嘿嘿)。
主要步驟如下
1、選擇使用的串口(串口1)
2、查詢(xún)?cè)摯趯?duì)應(yīng)的引腳及DMA通道(A9,A10, DMA1chanel4, DMA1chanel5)Tip:可以通過(guò)STM32參考手冊(cè)查詢(xún),后面會(huì)放的(感謝正點(diǎn)原子)
3、配置串口1 GPIO
4、配置串口1 USART 及 中斷
5、配置串口1 對(duì)應(yīng) DMA

著重需要注意的是:這里使用的是 串口1 的 空閑中斷。空閑中斷在設(shè)備接收到數(shù)據(jù)后 一個(gè)字節(jié)內(nèi)的時(shí)間還是為接收到數(shù)據(jù)就會(huì)產(chǎn)生,在清除之后,當(dāng)下一次接收到數(shù)據(jù)之后的1個(gè)字節(jié)時(shí)間內(nèi)才會(huì)再次產(chǎn)生。   說(shuō)人話就是,正常情況下不會(huì)產(chǎn)生,只有在接收數(shù)據(jù)后,沒(méi)有數(shù)據(jù)了,才會(huì)再次產(chǎn)生。
此處使用串口 的空閑中斷可以實(shí)現(xiàn) 接收不定長(zhǎng)數(shù)據(jù),若使用的是 定長(zhǎng)數(shù)據(jù) 就可以使用DMA 的接收完成中斷(接收完成是當(dāng)前數(shù)據(jù)接收完成,還需要判斷接收次數(shù)是否為 0)

筆者設(shè)備 STM32F103RCT6 + window10,若其他設(shè)備可根據(jù)提供的資料進(jìn)行查詢(xún)對(duì)應(yīng)的引腳和通道


—————————————分割線———————————————
開(kāi)始放代碼
配置引腳
  1. void USART1_GPIO_Init(void){//初始化 串口 1 GPIO
  2.                 //GPIO引腳功能初始化結(jié)構(gòu)體
  3.                 GPIO_InitTypeDef GPIO_InitStruct;
  4.                 //1、使能串口引腳,復(fù)用功能
  5.                 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA, ENABLE);
  6.                
  7.                 //2、初始化引腳功能 輸入 懸空(輸入高為高, 輸入低為低), 輸出 復(fù)用推挽
  8.                 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;//初始化引腳為 A9
  9.                 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;//初始化引腳功能為 復(fù)用推挽模式
  10.                 GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;//設(shè)置引腳傳輸速率為 10MHz
  11.                 GPIO_Init(GPIOA, & GPIO_InitStruct);//初始化 A9引腳 (發(fā)送)
  12.                
  13.                 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;//初始化引腳為 A10
  14.                 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;//初始化引腳功能為 懸空輸入
  15.                 GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;//設(shè)置引腳傳輸速率為 10MHz
  16.                 GPIO_Init(GPIOA, & GPIO_InitStruct);//初始化 A1引腳 (接收)
  17. }
復(fù)制代碼
配置串口
  1. void USART1_Init(uint32_t BaudRate){               
  2.                 //串口初始化結(jié)構(gòu)體
  3.                 USART_InitTypeDef USART_InitStruct;
  4.                 //串口優(yōu)先級(jí)結(jié)構(gòu)體
  5.                 NVIC_InitTypeDef NVIC_InitStruct;

  6.                 //1、使能串口引腳,復(fù)用功能
  7.                 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

  8.                 //2、串口初始化
  9.                 //2.1 初始化波特率
  10.                 USART_InitStruct.USART_BaudRate = BaudRate;//初始化串口波特率(使用形參確定)
  11.                 //2.2 初始化數(shù)據(jù)位
  12.                 USART_InitStruct.USART_WordLength = USART_WordLength_8b;//初始化串口數(shù)據(jù)位為 8 位
  13.                         //2.3 初始化停止位
  14.                 USART_InitStruct.USART_StopBits = USART_StopBits_1;//初始化串口數(shù)據(jù)位為 0 位
  15.                 //2.4 初始化奇偶校驗(yàn)位
  16.                 USART_InitStruct.USART_Parity = USART_Parity_No;//初始化串口奇偶校驗(yàn)位
  17.                 //2.5 初始化流控
  18.                 USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//初始化串口控制為無(wú)流控
  19.                 //2.6 初始化串口工作模式
  20.                 USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;

  21.                 USART_Init(USART1, & USART_InitStruct);               

  22.                 //3、設(shè)置串口 接收中斷 (空閑中斷-搬運(yùn)數(shù)據(jù)完畢 或 未搬運(yùn)數(shù)據(jù) 當(dāng)開(kāi)始搬運(yùn)數(shù)據(jù)后 一段時(shí)間未搬運(yùn)下一個(gè)數(shù)據(jù),發(fā)出此中斷)
  23.                 //3.1 配置串口中斷優(yōu)先級(jí)通道
  24.                 NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
  25.                 //3.2 使能命令
  26.                 NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
  27.                 //3.3 設(shè)置搶占優(yōu)先級(jí)
  28.                 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
  29.                 //3.4 設(shè)置優(yōu)先級(jí)
  30.                 NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
  31.                 NVIC_Init( & NVIC_InitStruct);
  32.                
  33.                 //5、配置串口接收中斷
  34.                 USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//串口1 空閑中斷               
  35.                 //5、中斷處理
  36.                 USART_DMACmd(USART1,USART_DMAReq_Rx, ENABLE);//開(kāi)啟串口1 DMA 接收請(qǐng)求
  37.                 //6、使能串口
  38.                 USART_Cmd(USART1,ENABLE);//若重載 printf ,就無(wú)需使用 DMA 進(jìn)行數(shù)據(jù)搬移。(故此處應(yīng)該無(wú)需使用重載)
  39. }
復(fù)制代碼

配置 DMA
  1. void USART1_DMA_Init(void){
  2.         //DMA初始化結(jié)構(gòu)體
  3.         DMA_InitTypeDef DMA_InitStruct;

  4.         //1、使能DMA時(shí)鐘
  5.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
  6.         
  7.         //2、接收數(shù)據(jù) DMA通道5初始化
  8.         //2.1 初始化外設(shè)地址
  9.         DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)& USART1->DR;//設(shè)置串口發(fā)送數(shù)據(jù)寄存器
  10.         //2.2 初始化內(nèi)存地址
  11.         DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)Get_Date;//接收內(nèi)存地址
  12.         //2.3 初始化 DMA 方向
  13.         DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;//外設(shè)為源地址
  14.         //2.4 初始化 發(fā)送數(shù)據(jù)大小 ,6.2內(nèi)存大小
  15.         DMA_InitStruct.DMA_BufferSize = BUFFSIZE;//此處的接收數(shù)據(jù)大小設(shè)置一個(gè)固定的,較大的
  16.         //2.5 初始化外設(shè)地址增量(向發(fā)送端寫(xiě)入數(shù)據(jù),無(wú)需使用增量)
  17.         DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//無(wú)增量
  18.         //2.6 初始化內(nèi)存地址增量(讀取內(nèi)存地址,寫(xiě)入外設(shè),有增量)
  19.         DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;//有增量
  20.         //2.7 初始化外設(shè)數(shù)據(jù)寬度
  21.         DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//無(wú)增量的話應(yīng)該不用設(shè)置
  22.         //2.8 初始化內(nèi)存數(shù)據(jù)寬度
  23.         DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//增量為全字
  24.         //2.9 初始化DMA工作模式
  25.         DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;//正常循環(huán)模式
  26.         //2.10 初始化DMA通道優(yōu)先級(jí)
  27.         DMA_InitStruct.DMA_Priority = DMA_Priority_High;//通道優(yōu)先級(jí) 高
  28.         //2.11 初始化DMA存儲(chǔ)器to存儲(chǔ)器(內(nèi)存寫(xiě)入外設(shè),無(wú)需使用)
  29.         DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;//不使用 內(nèi)存到內(nèi)存               

  30.         DMA_Init(DMA1_Channel5, & DMA_InitStruct);//通道5 串口1 讀數(shù)據(jù)

  31.         DMA_Cmd(DMA1_Channel5, ENABLE);//開(kāi)啟通道5 使能位
  32. }
復(fù)制代碼

中斷和調(diào)用函數(shù)部分
  1. void Mov_DmaDate_To_Buffer(void){//搬移函數(shù),將DMA寄存器中的數(shù)據(jù)搬移到另一個(gè)寄存器
  2.         DMA_Cmd(DMA1_Channel5, DISABLE);//關(guān)閉 DMA 通道 5 (關(guān)閉串口1 讀)
  3.                         
  4.         DMA1_Channel5->CNDTR = BUFFSIZE;//重新設(shè)置 通道 待傳輸數(shù)據(jù)大小
  5.                
  6.         DMA_Cmd(DMA1_Channel5, ENABLE);//開(kāi)啟 DMA 通道 5 (開(kāi)啟串口1 讀)
  7.         
  8.         printf("%s\n",Get_Date);//輸出接收的數(shù)據(jù)
  9. }


  10. void USART1_IRQHandler(void){//串口1 中斷處理函數(shù)
  11.         if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET){//若中斷標(biāo)志位為 空閑中斷
  12.                 //調(diào)用搬移函數(shù)
  13.                 Mov_DmaDate_To_Buffer();//調(diào)用空閑中斷處理函數(shù)

  14.                 USART_ReceiveData(USART1);//通過(guò)讀取數(shù)據(jù)實(shí)現(xiàn)關(guān)閉 空閑中斷
  15.                
  16.                 //清除標(biāo)志位
  17.                 Recevie_State = true;//設(shè)置接收數(shù)據(jù)完畢標(biāo)志位
  18.         }
  19. }
復(fù)制代碼

Keil代碼下載: 代碼STM32_Study_Serial_DMA_Read.7z (193.55 KB, 下載次數(shù): 52)

評(píng)分

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

查看全部評(píng)分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏4 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:353115 發(fā)表于 2023-4-8 17:15 | 只看該作者
定長(zhǎng)和不定長(zhǎng)都可以使用DMA功能,限制最大接收數(shù)量就好了。
既然用了空閑中斷,定長(zhǎng)數(shù)據(jù)和不定長(zhǎng)數(shù)據(jù)就失去了意義,反正空閑之后標(biāo)志這一幀結(jié)束。在空閑中斷中要停用串口的DMA功能,處理完數(shù)據(jù)后再開(kāi)啟串口的DMA功能。
回復(fù)

使用道具 舉報(bào)

板凳
ID:955050 發(fā)表于 2023-4-8 20:01 | 只看該作者
qsssuv 發(fā)表于 2023-4-8 17:15
定長(zhǎng)和不定長(zhǎng)都可以使用DMA功能,限制最大接收數(shù)量就好了。
既然用了空閑中斷,定長(zhǎng)數(shù)據(jù)和不定長(zhǎng)數(shù)據(jù)就失 ...

多謝補(bǔ)充,
回復(fù)

使用道具 舉報(bào)

地板
ID:291668 發(fā)表于 2023-4-8 20:28 | 只看該作者
學(xué)以致用,這種方法很好! 學(xué)習(xí)了
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

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

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

快速回復(fù) 返回頂部 返回列表
主站蜘蛛池模板: 久久久免费在线观看 | 午夜视频一区二区 | 精品中文字幕一区 | 国产偷录视频叫床高潮对白 | 精品美女久久久久久免费 | 久久久久国产一区二区三区四区 | 网站黄色av | 国产午夜三级一区二区三 | 欧美 日韩 国产 在线 | 亚洲精品一二区 | www.天天操.com | 久久人人网| 中文字幕av一区 | 欧美精品久久久久久久久久 | 中文字幕国 | 日韩av免费在线电影 | 国产精品成人一区二区 | 日韩在线一区二区 | 波波电影院一区二区三区 | 亚洲有码转帖 | 久久久久网站 | 区一区二在线观看 | 中文字幕在线视频免费视频 | 色综合色综合网色综合 | 国产一级一级毛片 | 国产清纯白嫩初高生视频在线观看 | 天天插天天射天天干 | www.99re5.com | 玖玖爱365 | 日韩aⅴ视频 | 亚洲精品影院 | 精品自拍视频在线观看 | 一级片在线观看 | 亚洲综合电影 | 黄视频免费观看 | 亚洲精品www久久久久久广东 | 久久久久1 | 九九九视频在线观看 | 伊人精品一区二区三区 | 精品视频一区二区三区在线观看 | 成人中文网 |