STM32CubeMX系列 | 串口通讯

发布时间:2023-03-20  

1. 串口简介

在串行通信中,一个字符一个字符地传输,每个字符一位一位地传输,并且传输一个字符时,总是以“起始位”开始,以“停止位”结束。在进行传输之前,双方一定要使用相同的波特率设置。波特率就是每秒钟传输的数据位数。常用的两种基本串行通信方式包括同步通信和异步通信。我们通常使用的是异步通信,异步通信规定传输的数据格式由起始位(start bit)、数据位(data bit)、奇偶校验位(parity bit)和停止位(stop bit)组成。串口通讯有HAL 库轮询,中断,DMA 三种通信模式:

  • 轮询方式:CPU不断查询IO设备,如设备有请求则加以处理。例如CPU不断查询串口是否传输完成,如传输超过则返回超时错误。轮询方式会占用CPU处理时间,效率较低。

  • 中断控制方式:当I/O操作完成时,输入输出设备控制器通过中断请求线向处理器发出中断信号,处理器收到中断信号之后,转到中断处理程序,对数据传送工作进行相应的处理。

  • 直接内存存取技术(DMA)方式:所谓直接传送,即在内存与IO设备间传送一个数据块的过程中,不需要CPU的任何中间干涉,只需要CPU在过程开始时向设备发出“传送块数据”的命令,然后通过中断来得知过程是否结束和下次操作是否准备就绪。

USART框图以及串口通讯过程如下图示

2. 硬件设计

本实验通过CH340芯片把STM32F1的串口1与PC的USB口进行连接,实现串口连接。串口通讯需要将数据收发管脚交叉连接,电路中的其他部分是自动下载电路部分,目的是控制BOOT的启动模式与复位

3. 软件设计

3.1 STM32CubeMX设置

  • RCC设置外接HSE,时钟设置为72M

  • PC0设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平

  • USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位

  • 若使用中断通讯方式,还需要开启串口中断

  • 若使用直接内存存取(DMA)方式,除以上步骤外(串口中断要开启,否则程序只能发送一次数据,且不能判断DMA传输是否完成,USART一直处于busy状态)还需要设置DMA传输方向、通道、优先级、数据长度以及指针递增与否

  • 输入工程名,选择工程路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码

3.2 MDK-ARM软件编程

轮询方式

/*****usart.c文件中的UART初始化函数以及IO口配置函数*****/

void MX_USART1_UART_Init(void){

  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();

  }

}


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);

  /* USER CODE BEGIN USART1_MspInit 1 */

  /* USER CODE END USART1_MspInit 1 */

  }

}

C语言中的标准库中所用的标准输出函数,默认的输出设备是显示屏,要实现串口或LCD的输出,必须重新定义标准库函数里与输出函数相关的函数,例如printf输出到串口,需要将fputc函数里面的输出指向串口(重定向)

/*****在usart.c中添加如下函数*****/

int fputc(int ch, FILE *f){

    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);

    return ch;

}

/*****main.c文件中编写相关代码*****/

while (1){

    HAL_UART_Transmit(&huart1,"HAL_UART_Transmit Test...",25,0xffff);

    printf("rn printf test...rn");

    HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);

    HAL_Delay(1000);

    /* USER CODE END WHILE */

}

中断方式

/*****usart.c文件中的UART初始化函数以及IO口配置函数*****/

void MX_USART1_UART_Init(void){

    //....该函数与轮询方式的UART初始化函数相同....

}


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 interrupt Init */

    HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);

    HAL_NVIC_EnableIRQ(USART1_IRQn);

  /* USER CODE BEGIN USART1_MspInit 1 */

  /* USER CODE END USART1_MspInit 1 */

  }

}

找到弱符号中断接收完成回调函数原型,并在usart.c中自定义该回调函数 __weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){

  if(huart->Instance == USART1){

    HAL_UART_Transmit(&huart1,RxMsg,10,0xffff); //将接收的数据通过串口1发送回去

    HAL_UART_Receive_IT(&huart1,RxMsg,10);      //再次开启接收中断

  }

}

/*****main.c文件中编写相关代码*****/

/* USER CODE BEGIN PV */

uint8_t TxMsg[] = "rn*****USART communication based on IT*****rn";

uint8_t RxMsg[20];

/* USER CODE END PV */

/* USER CODE BEGIN 2 */

HAL_UART_Transmit_IT(&huart1,TxMsg,sizeof(TxMsg));

HAL_UART_Receive_IT(&huart1,RxMsg,10);

/* USER CODE END 2 */

while (1){

  HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);

  HAL_Delay(1000);

  /* USER CODE END WHILE */

}

DMA方式

/*****dma.c文件中的DMA初始化函数*****/

void MX_DMA_Init(void) {

  /* DMA controller clock enable */

  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */

  /* DMA1_Channel4_IRQn interrupt configuration */

  HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 0, 0);

  HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);

  /* DMA1_Channel5_IRQn interrupt configuration */

  HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0);

  HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);

}

/*****usart.c文件中的UART初始化函数以及IO口和DMA配置函数*****/

void MX_USART1_UART_Init(void){

    //....该函数与轮询方式的UART初始化函数相同....

}


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);

    /* USART1 interrupt Init */

    HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);

    HAL_NVIC_EnableIRQ(USART1_IRQn);

  /* USER CODE BEGIN USART1_MspInit 1 */

  /* USER CODE END USART1_MspInit 1 */

  }

}

/*****main.c文件中编写相关代码*****/

/* USER CODE BEGIN PV */

uint8_t TxMsg[] = "rn*****USART communication based on DMA*****rn";

/* USER CODE END PV */

while (1){

    HAL_UART_Transmit_DMA(&huart1,TxMsg,sizeof(TxMsg));

    HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);

    HAL_Delay(1000);

    /* USER CODE END WHILE */

}


4. 下载验证

  • 轮询方式

  • 串口中断:使用串口助手发送10个字符,串口助手回显发送的数据;串口要发够10个字符才会触发中断;超过10个字符,串口只会发送10个字符(注意不要勾选‘发送新行’)

  • DMA方式


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>