蓝桥杯-嵌入式3-USART

STM32外设之USART

对于STM32G4系列微控制器来说,共有三个USART(USART1…3)和一个UART(UART4)。在我们板子上用到的是USART1。其中PA9引脚对应USART_TXPA10引脚对应USART_RX
当串口接收数据时,数据通过RX引脚,进入到(RX Shift Reg)移位寄存器中,然后通过移位将数据传入到RxFIFO中,然后再传到USART_RDR中。
当串口发送数据时,数据从USART_TDR中传到TxFIFO中,然后再传到(TX Shift Reg)移位寄存器中,然后再一位一位的将数据从TX引脚发送出去。
其中USART_RDRUSART_TDR都是常用的8位寄存器,通过CR1控制寄存器的M1和M0位控制7/8/9位。
如下图所示为发送步骤时序图
在这里插入图片描述步骤分别为:
在这里插入图片描述如下图所示为接收的步骤和时序图:
在这里插入图片描述从上述发送与接收的步骤和时序中可以看出,发送时不需要用到中断,而接收时需要用到中断。如下图所示:
在这里插入图片描述在这里插入图片描述

收发程序编写

程序编写一共包括两个部分,一部分是STM32CUBEMX的配置,另外一部分是Keil5中的程序编写。

STM32CUBEMX配置

由于USART_TX和USART_RX分别对应PA9和PA10引脚,所以如下图所示,选中这两个引脚。
在这里插入图片描述一个为开漏输入一个为开漏输出,两个引脚都为无上下拉。
在这里插入图片描述配置此中断优先级为1,和抢占优先级为0。
在这里插入图片描述配置的波特率为9600Bit/s,数据长度为8,其他都为默认。
在这里插入图片描述时钟树配置80Mhz。
在这里插入图片描述生成代码即可,接下来为Keil5编写代码。

Keil5程序编写

STM32CUBEMX生成如下初始化函数:

#include "usart\bsp_usart.h"

UART_HandleTypeDef huart1;

void USART1_Init(void)
{
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 9600;
  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;
  huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart1.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetTxFifoThreshold(&huart1, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetRxFifoThreshold(&huart1, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_DisableFifoMode(&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)
  {
    __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_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    HAL_NVIC_SetPriority(USART1_IRQn, 1, 0);
    HAL_NVIC_EnableIRQ(USART1_IRQn);
  }
}

在串口的头文件中定义;

#include "main.h"
extern UART_HandleTypeDef huart1;
void USART1_Init(void);

在stm32g4xx_it.c中,多了一个串口的中断

void USART1_IRQHandler(void)
{
  HAL_UART_IRQHandler(&huart1);
}

由于此函数中的huart1在其他c文件中有定义所以直接把其他头文件引过来即可

#include "usart\bsp_usart.h"

然后进入bsp_usart.h中调用的main.h,然后在进入main.h中的stm32g4xx_hal.h,然后再进入stm32g4xx_hal_conf.h,在这里面,由于串口没有使用,要把串口使能,如下代码所示:

#define HAL_UART_MODULE_ENABLED   

接下来是在主函数中写程序:
首先要控制串口的执行速度,此处用到了滴答定时器,如下所示;

__IO uint32_t uwTick_Usart_Set_Point =0;//控制Usart_Proc的执行速度

//*串口专用变量
int counter = 0;
char str[40];
unsigned char rx_buffer;

if ((uwTick - uwTick_Usart_Set_Point) <= 500) 
	{
		return;
	}
	uwTick_Usart_Set_Point = uwTick;

发数据

首先是初始化,然后进行向外打印数据

  USART1_Init();
void Usart_Proc(void)
{
	if ((uwTick - uwTick_Usart_Set_Point) <= 500) 
	{
		return;
	}
	uwTick_Usart_Set_Point = uwTick;
	
	sprintf(str,"%04d:Hello,world.\r\n",counter);
	HAL_UART_Transmit(&huart1,(unsigned char *)str,strlen(str),50);//发送的消息的地址,内容,长度,时间
	HAL_Delay(500);
	
	if(++counter == 10000)
	{
		counter = 0;
	}
}

接数据

同样是先初始化,然后一个中断接收函数,当程序运行时,运行到中断接收函数是会一直在while中等待,知道单片机接收到数据,会跳入到中断处理函数USART1_IRQHandler,跳入到中断处理函数后,会直接调用一个回调函数,所以写一个中断回调函数,此回调函数中用led演示为例子。代码如下所示;

unsigned char rx_buffer;//定义一个变量用来存放数据
HAL_UART_Receive_IT(&huart1,&rx_buffer,1);//打开中断,当有数据时,一个字节存入到rx_buffer这个地址上,再来一个再存入这覆盖掉,一直接收
void USART1_IRQHandler(void)
{
  HAL_UART_IRQHandler(&huart1);//当串口数据接收时,直接进入此处理函数
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//然后跳入此中断回调函数,执行此中程序。
{
	  LED_Disp(0xff);
	  HAL_Delay(300);
	  LED_Disp(0x00);
		HAL_UART_Receive_IT(&huart1,&rx_buffer,1);
	
}//当执行完后再次进入主函数中

滴答定时器补充内容

由于在此接收程序中使用到了滴答定时器延时,即在接收中断中嵌套了滴答定时器,如果想要滴答定时器打断接收中断让回调函数可以运行,那么滴答定时器的优先级一定要高于串口中断优先级,否则程序将无法运行。滴答定时器的优先级可在如下函数内修改:

HAL_Init();

HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);//此设置优先级组,此设置为4个抢占优先级,无子优先级

HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK

#define  TICK_INT_PRIORITY           (0UL)//此处将滴答定时器的优先级改为0,即抢占优先级最高
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喜欢小施恶霸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值