最近想做一個控制電機的項目,其中會用到Pytho與單片機STM32之間的互同,最近也在看一些關(guān)于數(shù)據(jù)通信和拆包的相關(guān)知識,所以記錄一下這段時間里對兩者之間的互通所做的事情和發(fā)現(xiàn)的問題,以供自己和大家參考。
單片機的串口是我們常用的與電腦通信的外設(shè),本次與Python互通就采用的串口實現(xiàn)上位機與下位機的通訊。
本章先講解串口外設(shè)的使用,下一章講解在Python中接收單片機發(fā)送的數(shù)據(jù)。
我采用的單片機型號是STM32F103ZET6,使用usart1進行數(shù)據(jù)的收發(fā),所使用的引腳是PA9、PA10。使用STM32Cube打開串口進行初始化。
第一步,設(shè)置時鐘源,在未設(shè)置的情況下,我們的單片機默認的系統(tǒng)時鐘是8MHz,如下圖所示。
所以,要想系統(tǒng)時鐘達到最大就要使用外部晶振,不過,值得注意的是,F(xiàn)1系列的板子使用外部晶振作為時鐘源時,系統(tǒng)時鐘可超過72MHz,但是為了單片機的穩(wěn)定性,我們對系統(tǒng)時鐘的設(shè)置不能超過官方的限制。
下面,將打開外部時鐘源
選擇高速時鐘源,然后選擇72MHz,讓系統(tǒng)自己設(shè)置。
第二步,在SYS里面的Debug選擇 Serial write,非常重要的一步,否則會造成第一次燒錄程序后續(xù)無法識別調(diào)試器
第三步打開串口USART1,在USART1
中的
Mode
選擇?Asynchronous
?異步通信
波特率為?115200 Bits/s
。傳輸數(shù)據(jù)長度為?8 Bit
。奇偶檢驗?None
,停止位?1
?,接收和發(fā)送都使能
。
?開啟中斷
并且采用DMA進行數(shù)據(jù)傳輸,采用DMA 好處是不需要占用CPU的資源即可完成數(shù)據(jù)的接收和發(fā)送,極大的節(jié)約了CPU 的占用。對于DMA 的原理這里不重點解釋,我們只需要知道他的功能和如何使用即可,下面將USART1的DMA開啟。
選擇DMA Settings點擊Add,添加USART1_RX和USART1_TX
選擇MDK-ARM和Code Generator 中的 Generate peripheral initialization as a pair of '.c/.h' files per peripheral
?最后生成文件
生成的代碼如下,這里說一個小技巧,我們可以在
/* USER CODE BEGIN 1 */
? /* USER CODE END 1 */
中間寫函數(shù),例如
/* USER CODE BEGIN 1 */
? ? ? interesting();
? /* USER CODE END 1 */
這樣,如果向開其他外設(shè)的時候直接在Cube里面打開,再生成即可,如果不卸載里面的話,我們自己寫的代碼將會在生成后被刪除,會很麻煩。?
main.c?
int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }
usart.c
UART_HandleTypeDef huart1; DMA_HandleTypeDef hdma_usart1_rx; DMA_HandleTypeDef hdma_usart1_tx; /* USART1 init function */ void MX_USART1_UART_Init(void) { /* USER CODE BEGIN USART1_Init 0 */ /* USER CODE END USART1_Init 0 */ /* USER CODE BEGIN USART1_Init 1 */ /* USER CODE END USART1_Init 1 */ huart1.Instance = USART1; huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN USART1_Init 2 */ /* USER CODE END USART1_Init 2 */ } void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(uartHandle->Instance==USART1) { /* USER CODE BEGIN USART1_MspInit 0 */ /* USER CODE END USART1_MspInit 0 */ /* USART1 clock enable */ __HAL_RCC_USART1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /**USART1 GPIO Configuration PA9 ------> USART1_TX PA10 ------> USART1_RX */ GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_10; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* USART1 DMA Init */ /* USART1_RX Init */ hdma_usart1_rx.Instance = DMA1_Channel5; hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_usart1_rx.Init.Mode = DMA_NORMAL; hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW; if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK) { Error_Handler(); } __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart1_rx); /* USART1_TX Init */ hdma_usart1_tx.Instance = DMA1_Channel4; hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE; hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_usart1_tx.Init.Mode = DMA_NORMAL; hdma_usart1_tx.Init.Priority = DMA_PRIORITY_LOW; if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK) { Error_Handler(); } __HAL_LINKDMA(uartHandle,hdmatx,hdma_usart1_tx); /* USER CODE BEGIN USART1_MspInit 1 */ /* USER CODE END USART1_MspInit 1 */ } } void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle) { if(uartHandle->Instance==USART1) { /* USER CODE BEGIN USART1_MspDeInit 0 */ /* USER CODE END USART1_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_USART1_CLK_DISABLE(); /**USART1 GPIO Configuration PA9 ------> USART1_TX PA10 ------> USART1_RX */ HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10); /* USART1 DMA DeInit */ HAL_DMA_DeInit(uartHandle->hdmarx); HAL_DMA_DeInit(uartHandle->hdmatx); /* USER CODE BEGIN USART1_MspDeInit 1 */ /* USER CODE END USART1_MspDeInit 1 */ } } /* USER CODE BEGIN 1 */ /* USER CODE END 1 */
串口使能中斷函數(shù)
void usart_dma_int() { __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中斷 HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);//開啟DMA接收 }
DMA發(fā)送函數(shù)
HAL_UART_Transmit_DMA(&huart1, buf,len)
重定向printf函數(shù)
int fputc(int ch,FILE *stream) { HAL_UART_Transmit(&huart1,( uint8_t *)&ch,1,0xFFFF); return ch; }
我們的接收數(shù)據(jù)采用DMA接收方式,使用DMA+IDLE空閑中斷,這樣的好處是,當接收到一串數(shù)據(jù)時,串口不會發(fā)生中斷,而是將接收到的數(shù)據(jù)通過DMA存到緩存區(qū)中,當數(shù)據(jù)傳輸完成后,IDLE產(chǎn)生中斷標志位,進而產(chǎn)生一次中斷,我們就可以在這次中斷中做一些我們想要實現(xiàn)的功能,這種DMA+IDLE接收的方式,很符合數(shù)據(jù)傳輸通訊的形式,代碼如下
void USART1_IRQHandler(void) { uint32_t flag = 0; uint32_t num;//DMA沒有傳輸?shù)膫€數(shù) flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //獲取IDLE標志位 if((tmp_flag != RESET))//IDE產(chǎn)生中斷 { __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除標志位 HAL_UART_DMAStop(&huart1); //停止DMA傳輸 num = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 獲取DMA中未傳輸?shù)臄?shù)據(jù)個數(shù) rx_num = BUFFER_SIZE - temp; //BUFFER_SIZE(接收數(shù)據(jù)緩存的最大個數(shù))-num(剩余的個數(shù))=當前接收的數(shù)據(jù)個數(shù) flag= 1; // 接受完成標志位置1 } /* USER CODE END USART1_IRQn 0 */ HAL_UART_IRQHandler(&huart1); /* USER CODE BEGIN USART1_IRQn 1 */ /* USER CODE END USART1_IRQn 1 */ }
主函數(shù)
int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ usart_dma_int(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ printf("實驗\r\n"); HAL_Delay(300); /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }
實驗結(jié)果
?本次先講解串口收發(fā)的使用,下一章介紹在Python中接收單片機發(fā)送的數(shù)據(jù)并解析。文章來源:http://www.zghlxwxcb.cn/news/detail-814070.html
跳轉(zhuǎn)連接:https://blog.csdn.net/m0_73816319/article/details/135667240?spm=1001.2014.3001.5502文章來源地址http://www.zghlxwxcb.cn/news/detail-814070.html
到了這里,關(guān)于HAL庫 STM32運用DMA與IDLE中斷實現(xiàn)高效串口通信 (附代碼)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!