最近想做一个控制电机的项目,其中会用到Pytho与单片机STM32之间的互同,最近也在看一些关于数据通信和拆包的相关知识,所以记录一下这段时间里对两者之间的互通所做的事情和发现的问题,以供自己和大家参考。
单片机的串口是我们常用的与电脑通信的外设,本次与Python互通就采用的串口实现上位机与下位机的通讯。
本章先讲解串口外设的使用,下一章讲解在Python中接收单片机发送的数据。
我采用的单片机型号是STM32F103ZET6,使用usart1进行数据的收发,所使用的引脚是PA9、PA10。使用STM32Cube打开串口进行初始化。
第一步,设置时钟源,在未设置的情况下,我们的单片机默认的系统时钟是8MHz,如下图所示。
所以,要想系统时钟达到最大就要使用外部晶振,不过,值得注意的是,F1系列的板子使用外部晶振作为时钟源时,系统时钟可超过72MHz,但是为了单片机的稳定性,我们对系统时钟的设置不能超过官方的限制。
下面,将打开外部时钟源
选择高速时钟源,然后选择72MHz,让系统自己设置。
第二步,在SYS里面的Debug选择 Serial write,非常重要的一步,否则会造成第一次烧录程序后续无法识别调试器
第三步打开串口USART1,在USART1
中的
Mode
选择 Asynchronous
异步通信
波特率为 115200 Bits/s
。传输数据长度为 8 Bit
。奇偶检验 None
,停止位 1
,接收和发送都使能
。
开启中断
并且采用DMA进行数据传输,采用DMA 好处是不需要占用CPU的资源即可完成数据的接收和发送,极大的节约了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 */
中间写函数,例如
/* USER CODE BEGIN 1 */
interesting();
/* USER CODE END 1 */
这样,如果向开其他外设的时候直接在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 */
串口使能中断函数
void usart_dma_int() { __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断 HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);//开启DMA接收 }
DMA发送函数
HAL_UART_Transmit_DMA(&huart1, buf,len)
重定向printf函数
int fputc(int ch,FILE *stream) { HAL_UART_Transmit(&huart1,( uint8_t *)&ch,1,0xFFFF); return ch; }
我们的接收数据采用DMA接收方式,使用DMA+IDLE空闲中断,这样的好处是,当接收到一串数据时,串口不会发生中断,而是将接收到的数据通过DMA存到缓存区中,当数据传输完成后,IDLE产生中断标志位,进而产生一次中断,我们就可以在这次中断中做一些我们想要实现的功能,这种DMA+IDLE接收的方式,很符合数据传输通讯的形式,代码如下
void USART1_IRQHandler(void) { uint32_t flag = 0; uint32_t num;//DMA没有传输的个数 flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位 if((tmp_flag != RESET))//IDE产生中断 { __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位 HAL_UART_DMAStop(&huart1); //停止DMA传输 num = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数 rx_num = BUFFER_SIZE - temp; //BUFFER_SIZE(接收数据缓存的最大个数)-num(剩余的个数)=当前接收的数据个数 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 */ }
主函数
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 */ }
实验结果
本次先讲解串口收发的使用,下一章介绍在Python中接收单片机发送的数据并解析。
跳转连接:https://blog.csdn.net/m0_73816319/article/details/135667240?spm=1001.2014.3001.5502