随记:STM32L053LL库LPUART串口DMA接收数据
老俗話說(shuō):不想寫好代碼的廚子都不是好業(yè)務(wù),老俗是誰(shuí)?老俗是我的又一個(gè)外號(hào)。本人技校畢業(yè),水平有限,發(fā)現(xiàn)問(wèn)題還請(qǐng)批評(píng)指正,還有就是輕點(diǎn)噴。
????????言歸正傳,這次寫個(gè)代碼,沒辦法客戶指定了STM32L053的MCU,之前確實(shí)沒有用過(guò),不過(guò)我一直堅(jiān)信,所有的芯片制造出來(lái),都是給大家使用的,而不是炫耀我們公司生產(chǎn)的多高科技,需要費(fèi)死洋勁才能使用,這樣的廠家肯定也快倒閉了。所以,放平心態(tài),不急不躁,沒有啥問(wèn)題是解決不了的。
???????? 說(shuō)著說(shuō)著又扯遠(yuǎn)了,這次的項(xiàng)目使用LPUART連接傳感器進(jìn)行通訊,好在傳感器的通訊也是自己寫的,通訊協(xié)議采用定長(zhǎng)數(shù)據(jù),10個(gè)byte,本想著直接用DMA的定長(zhǎng)接收,無(wú)奈現(xiàn)場(chǎng)總線上有N多傳感器,這要是串了數(shù)據(jù),是一個(gè)錯(cuò),個(gè)個(gè)錯(cuò),就永遠(yuǎn)回不了頭了。然后就想著用DMA做個(gè)不固定長(zhǎng)度的接收。多年前用STM32F1系列做過(guò),想著也不算太難,無(wú)奈找了找資料,STM32L053已經(jīng)不支持標(biāo)準(zhǔn)庫(kù)了。(此時(shí)內(nèi)心略有波動(dòng),我不想告訴你們其實(shí)是萬(wàn)馬奔騰),然后看了看資料,下載cubeMX,網(wǎng)上看了看,大部分是HAL庫(kù),然后用HAL庫(kù)試了試串口中斷,效果不是很理想。看了看代碼,還是決定轉(zhuǎn)戰(zhàn)LL庫(kù)。
? ? ? ? 首先要感謝songrsp先生的《基于STM32CubeMX的LL庫(kù)學(xué)習(xí)記錄(五)USART_DMA_IDLE 串口接收空閑中斷接收數(shù)據(jù)》
文章鏈接:https://www.pianshen.com/source/36308857364027/
? ? ? ? 其實(shí)DMA接受不定長(zhǎng)數(shù)據(jù)大部分思路就是使用DMA+串口空閑中斷,因?yàn)槲掖_實(shí)水平有限,也想不出什么更好的辦法。至于DMA是個(gè)啥?手冊(cè)里是這樣說(shuō)的:
? ? ? ? 這就好比你買了個(gè)房子要裝修,DMA就像是那個(gè)上料的師傅(不知道DMA有思維的話這么說(shuō)他會(huì)不會(huì)痛扁我),你去買了水泥,沙子,油漆啥的,我只告訴師傅從哪里搬到哪里就可以了,至于師傅是扛著,推著,還是背著,就和你沒太大關(guān)系了,你只是在需要的時(shí)候去看下東西的狀況就可以了,看看到?jīng)]到,到了多少。當(dāng)然,如果你買了很多種類,有些是急需的,你就得告訴師傅先上哪部分料,那么怎么告訴我們的“師傅”呢?
寫的有點(diǎn)啰嗦了,開整:
首先是打開cubeMX配置
個(gè)人習(xí)慣,先把調(diào)試勾選上,不然忘了這一步下次程序就下不進(jìn)去了,需要重新搞BOOT腳。
?
?配置時(shí)鐘,使用外部時(shí)鐘
?配置時(shí)鐘樹,因?yàn)楸敬问堑凸?#xff0c;所以直接用了外部8M晶振做主時(shí)鐘,PLL是個(gè)用電大戶。
?配置串口:異步模式,波特率9600,數(shù)據(jù)位8位,無(wú)校驗(yàn),1位停止位,由于默認(rèn)GPIO接到了別的功能上,這里復(fù)用了PC10和PC11。
?配置DMA,點(diǎn)Add添加,LPUART1_RX的接收在DMA的通道3上,這個(gè)查手冊(cè)可以知道,Direction是設(shè)置傳輸方向的,本次是從外設(shè)搬運(yùn)到內(nèi)存,Prionty是設(shè)置LPUART1_RX在DMA中的優(yōu)先級(jí)。Mode 是指單次傳輸還是循環(huán)傳輸。
?
?使能串口中斷,因?yàn)橐玫酱诘目臻e中斷,DMA的中斷是自動(dòng)生成的。
?管理下中斷優(yōu)先級(jí),畢竟是測(cè)試,我這也懶得動(dòng)了
?選擇下LL庫(kù),之后生產(chǎn)代碼。
?系統(tǒng)生成的代碼如下,在dma.文件中,初始化代碼MX_DMA_Init干了三件事,使能DMA時(shí)鐘,設(shè)置DMA中斷優(yōu)先級(jí),使能DMA中斷。
void MX_DMA_Init(void) {/* Init with LL driver *//* DMA controller clock enable */LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);/* DMA interrupt init *//* DMA1_Channel2_3_IRQn interrupt configuration */NVIC_SetPriority(DMA1_Channel2_3_IRQn, 0);NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);}大部分的代碼在串口初始化中,在usart.c文件中
void MX_LPUART1_UART_Init(void) {LL_LPUART_InitTypeDef LPUART_InitStruct = {0};//LL_GPIO_InitTypeDef GPIO_InitStruct = {0};///* 使能外設(shè)時(shí)鐘 */LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_LPUART1);//LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOC);///**LPUART1 GPIO ConfigurationPC10 ------> LPUART1_TXPC11 ------> LPUART1_RX*///配置管腳模式GPIO_InitStruct.Pin = LL_GPIO_PIN_10;GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;GPIO_InitStruct.Alternate = LL_GPIO_AF_0;LL_GPIO_Init(GPIOC, &GPIO_InitStruct);GPIO_InitStruct.Pin = LL_GPIO_PIN_11;GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;GPIO_InitStruct.Alternate = LL_GPIO_AF_0;LL_GPIO_Init(GPIOC, &GPIO_InitStruct);/* LPUART1 DMA Init *//* LPUART1_RX Init */LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_3, LL_DMA_REQUEST_5);//使用參數(shù)為通道x上的DMA實(shí)例配置DMA請(qǐng)求LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_3, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);//傳輸方向設(shè)置數(shù)據(jù)傳輸方向(從外設(shè)或內(nèi)存讀取)。LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PRIORITY_MEDIUM);//設(shè)置DMA的“通道優(yōu)先級(jí)”LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MODE_NORMAL);//設(shè)置工作模式,單次//LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MODE_CIRCULAR);// 循環(huán) LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PERIPH_NOINCREMENT);//設(shè)置外設(shè)增量模式外設(shè)非自增模式LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MEMORY_INCREMENT);//設(shè)置內(nèi)存自增模式LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PDATAALIGN_BYTE);//設(shè)置外設(shè)尺寸LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MDATAALIGN_BYTE);//設(shè)置內(nèi)存尺寸/* LPUART1 interrupt Init */NVIC_SetPriority(RNG_LPUART1_IRQn, 0);//設(shè)置中斷優(yōu)先級(jí)NVIC_EnableIRQ(RNG_LPUART1_IRQn);//使能中斷/* USER CODE BEGIN LPUART1_Init 1 *//* USER CODE END LPUART1_Init 1 */LPUART_InitStruct.BaudRate = 9600;LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B;LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1;LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE;LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX;LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE;LL_LPUART_Init(LPUART1, &LPUART_InitStruct); }至于為什么是LL_DMA_CHANNEL_3和?LL_DMA_REQUEST_5,看這里
?
這個(gè)自生成的代碼有個(gè)坑,不知道小伙伴們發(fā)現(xiàn)了沒?在初始化了串口之后,并沒有使能串口,對(duì)于LPUART而言,這里需要增加2行代碼:
LL_LPUART_Enable(LPUART1);//使能LPUART1LL_LPUART_EnableIT_IDLE(LPUART1);//使能串口空閑中斷同樣的,如果想使用接收中斷或者其他中斷,只需要使能相應(yīng)中斷即可。
接下來(lái)就是增加DMA的代碼,在生成的代碼中,并未詳細(xì)配置DMA,官方給的步驟是這樣:
在添加代碼前,需要在內(nèi)存中給DMA的搬運(yùn)開辟一塊內(nèi)存空間,創(chuàng)建個(gè)數(shù)組uint8_t Lpuart_buf[10];
在頭文件里定義下DMA接收的數(shù)據(jù)長(zhǎng)度: #define LPRXLEN?? ?10;
接下來(lái)添加DMA代碼:
LL_DMA_SetMemoryAddress(DMA1,LL_DMA_CHANNEL_3,(uint32_t)Lpuart_buf);//接收目標(biāo)地址LL_DMA_SetDataLength(DMA1,LL_DMA_CHANNEL_3,LPRXLEN);//設(shè)置DMA接受數(shù)據(jù)長(zhǎng)度LL_DMA_SetPeriphAddress(DMA1,LL_DMA_CHANNEL_3,(uint32_t)&(LPUART1->RDR));//設(shè)置外設(shè)地址,從哪里搬運(yùn)數(shù)據(jù)LL_DMA_EnableIT_TC(DMA1,LL_DMA_CHANNEL_3);//使能DMA完成中斷LL_DMA_EnableChannel(DMA1,LL_DMA_CHANNEL_3);//使能通道LL_LPUART_EnableDMAReq_RX(LPUART1);//使能串口DMA接收?到這里,基本的配置就完成了,然后我們打開stm32l0xx_it.c文件,在這里面找到中斷函數(shù)
void DMA1_Channel2_3_IRQHandler(void)//DMA中斷函數(shù)
void RNG_LPUART1_IRQHandler(void)//LPUART中斷函數(shù)
我再DMA完成中斷里至做了1件事,就是串口發(fā)出0x99,標(biāo)識(shí)已經(jīng)進(jìn)入了DMA完成中斷,這里留作以后備用,把DMA的搬運(yùn)數(shù)量加長(zhǎng),一旦進(jìn)入此中斷,就標(biāo)識(shí)協(xié)議錯(cuò)誤了。
void DMA1_Channel2_3_IRQHandler(void) {/* USER CODE BEGIN DMA1_Channel2_3_IRQn 0 *///判斷是否是通道3的中斷并且使能了該中斷if(LL_DMA_IsActiveFlag_TC3(DMA1)&&LL_DMA_IsEnabledChannel(DMA1,LL_DMA_CHANNEL_3)){LL_LPUART_TransmitData8(LPUART1,0x99);}LL_DMA_ClearFlag_TC3(DMA1);//清除中斷標(biāo)準(zhǔn)/* USER CODE END DMA1_Channel2_3_IRQn 0 *//* USER CODE BEGIN DMA1_Channel2_3_IRQn 1 *//* USER CODE END DMA1_Channel2_3_IRQn 1 */ }?在串口空閑中斷中,首先要關(guān)閉DMA,這樣防止在處理的時(shí)候有新的數(shù)據(jù)傳進(jìn)來(lái),還有就是為了重新配置DMA。
void RNG_LPUART1_IRQHandler(void) {/* USER CODE BEGIN RNG_LPUART1_IRQn 0 */uint8_t i,count;if(LL_LPUART_IsActiveFlag_IDLE(LPUART1))//如果是串口空閑中斷{LL_DMA_DisableChannel(DMA1,LL_DMA_CHANNEL_3);//關(guān)閉DMA//接收到的數(shù)據(jù)數(shù)量count = LPRXLEN-(LL_DMA_GetDataLength(DMA1,LL_DMA_CHANNEL_3));LL_LPUART_TransmitData8(LPUART1,count);//把數(shù)組傳輸出來(lái)for(i=0;i<10;i++){LL_LPUART_TransmitData8(LPUART1,Lpuart_buf[i]);LL_mDelay(10);}//重新配置DMA LL_DMA_SetDataLength(DMA1,LL_DMA_CHANNEL_3,LPRXLEN);LL_DMA_EnableChannel(DMA1,LL_DMA_CHANNEL_3);}LL_LPUART_ClearFlag_IDLE(LPUART1);//清除中斷標(biāo)志位/* USER CODE END RNG_LPUART1_IRQn 0 *//* USER CODE BEGIN RNG_LPUART1_IRQn 1 *//* USER CODE END RNG_LPUART1_IRQn 1 */ }驗(yàn)證一下,這里為了方便,我直接用ASCII字符發(fā)送了10個(gè)字符串,選擇了ASCII發(fā)送,HEX接收。這里輸入的0-9相當(dāng)于HEX的0x30-0x39
0x99說(shuō)明進(jìn)入了 DMA完成中斷,0A標(biāo)識(shí)接收了10個(gè)字節(jié),30-30就是數(shù)組的內(nèi)容了。
這次我們換幾個(gè)數(shù)字發(fā)一下,發(fā)送了3個(gè)0x36
?03說(shuō)明接收到了3個(gè)byte,因?yàn)闆]有對(duì)數(shù)組進(jìn)行清除,所以數(shù)組后邊的數(shù)據(jù)沒變化,所以從數(shù)組第四個(gè)數(shù)到最后一個(gè)沒有變化。所以測(cè)試數(shù)據(jù)小于數(shù)組時(shí),記得用不同的數(shù)據(jù),不然會(huì)因?yàn)榘l(fā)送前幾位數(shù)據(jù)一致,導(dǎo)致誤判斷。這里stm32沒有返回0x99,說(shuō)明沒有進(jìn)入到DMA完成中斷。如果發(fā)出的數(shù)據(jù)超過(guò)10個(gè)byte呢?我們來(lái)看下,這個(gè)就比較有趣了,既然說(shuō)有趣,就是根據(jù)配置的不一樣,會(huì)產(chǎn)生不一樣的結(jié)果。
現(xiàn)在的配置是DMA單次模式
0x99說(shuō)明進(jìn)入了DMA完成中斷,0A表示接收到了10個(gè)byte,后面就是數(shù)組內(nèi)的數(shù)據(jù)了,我們把DMA配置為循環(huán)模式再試一次:
LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MODE_CIRCULAR);?
?可以看到,數(shù)組前面的數(shù)據(jù)已經(jīng)被覆蓋掉了。
以上就是我個(gè)人調(diào)試LPUART的過(guò)程,寫的不對(duì)的地方,還請(qǐng)批評(píng)指正。
?
總結(jié)
以上是生活随笔為你收集整理的随记:STM32L053LL库LPUART串口DMA接收数据的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 欧美简约商务PPT模板
- 下一篇: Geant4学习之能谱输入