【蓝桥杯】【嵌入式组别】第十二节:USART串口通讯

USART串口通讯

  • USART:通用同步异步收发器
  • 串口发送程序设计:
    • 如何连续打印helloworld
    • 能不能发送中文?
    • 串口发送printf重定向
  • 串口接收程序设计:
    • 串口接收固定长度数据:
    • 串口接收带帧尾的不定长数据

USART:通用同步异步收发器

所谓异步,意思就是收发设备的时钟不同,是各自用各自的时钟。因此也就决定了它可能发的数据过多的话会导致混乱。所以uart一次最多发8个bit的数据,也就是一个字节。
串行意思就是数据一个一个的发送出去或者接受回来。
发送数据和接收数据时:高电平代表1,低电平代表0。但是我们怎么知道:如果一直是高电平,那么到底代表多少个1呢?其实这个是由波特率控制的。波特率是xxxbit per second,就告诉了机器每过多长时间就代表一个bit的信号了。

而iic和spi都是同步的通信方式。也就是收发设备处于同一个时钟之下工作,所以单词可传送的数据没有特殊限制,不会因为传送的数据多了就混淆掉。可以知道iic和spi再连线时都会有一根时钟线,用这根时钟线就可以保证收发设备处于同一个时钟之下。

USART可以同步也可以异步。区别是同步的时候需要三根线:TXD,RXD和CLK三根线,CLK用来同步收发两端的波特率和时间。
而异步只需要两根线:TXD,RXD就可以了。竞赛开发板上就只有两根线,所以我们只用异步就可以了,
【蓝桥杯】【嵌入式组别】第十二节:USART串口通讯_第1张图片

开发板上的USART管脚是PA9和PA10。

串口发送程序设计:

步骤:

  1. 【模板】作为STM32CUBEMX生成代码的工程;
  2. 配置USART1的PA9、PA10为串口收发管脚
  3. 根据需求,配置USART的波特率、数据位长度、奇偶校验位、停止位和时钟;
  4. 将usart.c 和usart.h移植到【编程工程】
    4.1main.c包含#include “usart.h”
    4.2添加usart.c和stm32g4的HAL库函数到工程中;
    4.3stm32g4xx_hal_confh中启动UART模块;
    4.4时钟初始化,一定要初始化USART1的时钟!
    4.5在主函数调用MX USART1_UART_Init()串口初始化函数;
    4.6调用HAL_UART_Transmit串口发送函数!

在cube里面配置PA9和PA10管脚为异步通信模式:(其他参数可以使用默认给出的,不用更改)
【蓝桥杯】【嵌入式组别】第十二节:USART串口通讯_第2张图片

可以看到时钟树里面又被点亮了一部分,这部分就是控制USART收发频率和波特率的时钟,我们可以选择这四个选项,但此处保持默认选项也是一个不错的选择。然后在移植过程中记得移植时钟部分的代码即可。

【蓝桥杯】【嵌入式组别】第十二节:USART串口通讯_第3张图片
移植操作全部完成之后就可在main函数中编写利用usart发送字符的代码了:

HAL_UART_Transmit(&huart1,(unsigned char*)"helloworld!\r\n",sizeof("helloworld!\r\n"),50);

这一句就是发送helloworld的代码。
在串口监视器里面显示为16进制观察为:
在这里插入图片描述

如何连续打印helloworld

最后的00是停止位,有了00之后,后面再发送什么东西这里都不会有显示了。
再往前面的0D和0A是换行符号。
所以我们如果想要一上电发送两次helloworld,就不能让00出现,不然的话就会出现这样的情况:
代码是这样的:

	HAL_UART_Transmit(&huart1,(unsigned char*)"helloworld!\r\n",sizeof("helloworld!\r\n"),50);
	HAL_UART_Transmit(&huart1,(unsigned char*)"helloworld!\r\n",sizeof("helloworld!\r\n"),50);

就是上电之后会发送两个helloworld,但是运行结果是这样的:
在这里插入图片描述
很明显只发送了一次,因为后面的一次被覆盖掉了。观察其16进制发现:
在这里插入图片描述

确实发了两次,但是由于第一次的最后一位发送了停止位00,所以后面的那一次是无效的,因为默认接收到00之后不论后面有什么都不接受了。

所以如果想要实现连续两次接收helloworld,就要把00删除掉,方法就是让发送的数据的size减一,就把末位删除掉了。
代码如下:

	HAL_UART_Transmit(&huart1,(unsigned char*)"helloworld!\r\n",sizeof("helloworld!\r\n")-1,50);
	HAL_UART_Transmit(&huart1,(unsigned char*)"helloworld!\r\n",sizeof("helloworld!\r\n")-1,50);

在这里插入图片描述
可以看到很完美的打印了两次。

能不能发送中文?

答案是可以发送中文。我们可以编写如下代码:
在main函数以外编写一个全局变量:

u8 tx_buf[]={"您好\r\n"};

在main函数中:

	HAL_UART_Transmit(&huart1,(unsigned char*)tx_buf,sizeof(tx_buf)-1,50);

最后实现的结果如下;
在这里插入图片描述
如果我们开启16进制观察,可以发现是这样的:
在这里插入图片描述
其实是keil自动把中文翻译为了16进制字符,每一个中文对应两个16进制字符:
“您”对应“C4 FA”
“好”对应“BA C3”
后面的“0D 0A“表示的是换行符

串口发送printf重定向

我们可以使用重定向之后的printf函数作为串口输出的函数。关于如何将printf重定向:只需要在usart.c文件中添加这样的代码即可:

int fputc(int ch, FILE *f) 
{
  HAL_UART_Transmit(&huart1,(unsigned char*)&ch,1,50);
  return ch;
}

这个函数我们可以在keil的help中查询到,步骤如下:
点击help的第一项,进入help页面
【蓝桥杯】【嵌入式组别】第十二节:USART串口通讯_第4张图片

然后点击搜索:搜索”retarget“,第三项就是我们要找的printf的重定向函数
【蓝桥杯】【嵌入式组别】第十二节:USART串口通讯_第5张图片
依照他的要求,我们把#include (标准的输入输出库)加到usart.c文件中,然后复制下面的函数到usart.c文件中:

int fputc(int ch, FILE *f) 
{
  /* Your implementation of fputc(). */
  return ch;
}

/* Your implementation of fputc(). */这里换成hal库的usart发送函数即可。完整的函数如下所示:

int fputc(int ch, FILE *f) 
{
  HAL_UART_Transmit(&huart1,(unsigned char*)&ch,1,50);
  return ch;
}

添加好之后我们就可以在main.c文件中直接写printf来进行usart的发送了,如:

 printf("%s \n","你好!");
 printf("您输入的数字是%d",2);

效果如下:
在这里插入图片描述
记得要勾选上微库才能正常使用printf!【蓝桥杯】【嵌入式组别】第十二节:USART串口通讯_第6张图片

串口接收程序设计:

步骤:

  1. 【模板】作为STM32CUBEMX生成代码的工程
  2. 配置USART1的PA9、PA10为串口收发管脚;
  3. 根据需求,配置USART的波特率、数据位长度、奇偶校验位、停止位和时钟;
  4. 勾选NVIC Settings中的使能USART1的中断;
  5. 将usart.c 和usart,h移植到【编程工程】
    5.1 USART初始化部分,需要移植NVIC初始化内容
    5.2 stm32g4xx it.c中,处理USART1 IRQHandler中断服务函数;
    5.3在初始化部分,启动HAL UART_ReceiveIT(&huart1,rx_buf,1);
    5.4在回调函数HAL_UART_RxCpltCallback实现串口接收缓存处理,并再次使能HAL_UART_Receive_IT;

先打开模板工程,然后配置USART的中断向量控制:
【蓝桥杯】【嵌入式组别】第十二节:USART串口通讯_第7张图片
勾选启用NVIC。
后面的:Preemption Priority是抢占优先级的意思。比如程序目前在主函数中运行,遇到了Systick的中断触发,就会到Systick中断处理函数中执行程序,如果在这期间又遇到了更高级别的优先级的中断,就会再进入更高级别的中断处理函数中,也就是一个中断嵌套。这就是抢占优先级要处理的问题。
而Sub Priority是响应优先级,解决的是当程序运行主函数时,同时来了两个中断触发,那么选择哪一个先响应的问题。
我们需要点击到NVIC页面来配置相应的抢占优先级和响应优先级:

【蓝桥杯】【嵌入式组别】第十二节:USART串口通讯_第8张图片
一般配置成如图所示的4bit抢占优先级和0bit响应优先级。
4个bit 就是4位的2进制数,范围是从0000-1111,也就是从0到15一共有16种优先级。
一般来说本竞赛不涉及中断嵌套,所以下面配置各个外设的抢占优先级的时候可以都设置为0,也就是不施行抢断,就是哪个先来就响应哪个。如果实在希望设置为不一样的优先级的话,可以把USART设置为高优先级,也就是0,其他的可以稍微低优先级一点,也就是设置为大于0的某个数即可。我们此处保持默认即可。

然后生成代码,进行移植。
移植完成后我们就可以编写代码了:
在编写代码之前先了解一下这个中断是如何进行的,以及我们需要编写哪些函数:
在主函数的初始化代码部分我们需要先调用“开启串口中断”的函数:

  HAL_UART_Receive_IT(&huart1,uart_buf,1);//开启串口接收中断

第二个参数是一个接受字符的数组,我们发送给USART的数据都会存储在这个数组里面,第三个参数是意味着我们接收到几个数据就出发中断。我们事先可以定义:

u8 uart_buf[2]

我们设置第三个参数为1,意思是我们希望接收到一个数据就触发中断,而不是接收好几个数据才触发。
所以上面句代码意味着我们接收到一个字节的数据就应该触发中断,然后进入到USART的中断处理函数中。
那么USART的中断处理函数在哪里呢?在stm32g4xx_it.c文件中的下面这个函数部分:

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

其中HAL_UART_IRQHandler(&huart1);这一句函数就是中断处理函数。
进入HAL_UART_IRQHandler(&huart1);函数的定义中(节选了一部分):

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
  uint32_t isrflags   = READ_REG(huart->Instance->ISR);
  uint32_t cr1its     = READ_REG(huart->Instance->CR1);
  uint32_t cr3its     = READ_REG(huart->Instance->CR3);

  uint32_t errorflags;
  uint32_t errorcode;

  /* If no error occurs */
  errorflags = (isrflags & (uint32_t)(USART_ISR_PE | USART_ISR_FE | USART_ISR_ORE | USART_ISR_NE));
  if (errorflags == 0U)
  {
    /* UART in mode Receiver ---------------------------------------------------*/
    if (((isrflags & USART_ISR_RXNE_RXFNE) != 0U)
        && (((cr1its & USART_CR1_RXNEIE_RXFNEIE) != 0U)
            || ((cr3its & USART_CR3_RXFTIE) != 0U)))
    {
      if (huart->RxISR != NULL)
      {
        huart->RxISR(huart);
      }
      return;
    }
  }

这部分的意思就是如果没有任何错误发生的话,就会进行一系列接收标志位的查验,然后调用RxISR(huart);函数。
点击进入RxISR(huart);函数的定义可以看到:

void (*RxISR)(struct __UART_HandleTypeDef *huart); /*!< Function pointer on Rx IRQ handler   */

它本身是一个函数指针。所以RxISR(huart);函数指向哪个函数,就意味着在USART中断处理中会调用哪个函数。
我们可以点击进入 HAL_UART_Receive_IT(&huart1,uart_buf,1);//开启串口接收中断这个函数的定义:

 if ((huart->FifoMode == UART_FIFOMODE_ENABLE) && (Size >= huart->NbRxDataToProcess))
    {
      /* Set the Rx ISR function pointer according to the data word length */
      if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
      {
        huart->RxISR = UART_RxISR_16BIT_FIFOEN;
      }
      else
      {
        huart->RxISR = UART_RxISR_8BIT_FIFOEN;
      }

可以看到上面这几行的代码,由于我们设置的是8bit数据位,所以执行的是UART_RxISR_8BIT_FIFOEN而不是UART_RxISR_16BIT_FIFOEN(关于这一点可以仿真然后在此处设置一个断点看是否会停留在这个函数这里,就可以知道)。
所以我们知道RxISR其实是指向了UART_RxISR_8BIT_FIFOEN这个函数。
我们在仿真时候打个断点可以最终得知,RxISR会通过UART_RxISR_8BIT_FIFOEN最终执行这个函数里面的HAL_UART_RxCpltCallback(huart);函数。这个函数上面的注释是:

/*Call legacy weak Rx complete callback*/

说明这是一个弱定义函数,就是只有一个函数体,没有里面的内容,他是长这个样子:

__weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(huart);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_UART_RxCpltCallback can be implemented in the user file.
   */
}

所以我们确定了我们要编写的就是void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)这个函数的函数体。
那么我们把他复制出来到main.c文件中编写他的函数本体:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	LED_Control(uart_buf[0]);
	HAL_UART_Receive_IT(&huart1,uart_buf,1);//开启串口接收中断
}

第一行是我们自己想实现的目标,第二行是必须要做的,要重新开启定时器中断,因为一方面他执行完一次中断后会清楚标志位,另一方面我们需要这句代码把buffer清除掉,方便下一次串口接收。
上面这个回调函数能实现的功能就是将我们发送给单片机的第一个数据作为一个指令指导led灯的亮灭。
此处还要明确的一点是:
我们设置的串口发送与接收的位数都是1个字节。1个字节就是8个bit,8个bit就是8位二进制数。而4位二进制数可以表示的最大值是15,也就是4位二进制数等效于1位十六进制数。而1个字节有8位二进制数,就等效于2位16进制数。
所以我们可以发送两位十六进制数给单片机,就相当于1个bit的数据。根据我们设置的HAL_UART_Receive_IT函数的参数,单片机接收到1个bit的数据之后就会进入中断,并且把这1个bit的数据存储到uart_buf里面。
所以我们如果发送十六进制55,就相当于是0x55,此时进入USART中断,然后执行这个回调函数,uart_buf[0]被赋值为0x55,然后就会间隔点亮led灯。

并且要注意的是:虽然我们这里定义的是uart_buf[2],也就是这个数组有三位可以存储数据,也就是能接受3个bit的数据,然是实际上只能用到1位,就是uart_buf[0]这一位。每次发送来的数据都会更新uart_buf[0]位,而不会对后面的位有任何影响和操作。

串口接收固定长度数据:

我们如果想接收多位数据,然后让不同位的数据执行不同的操作,应该怎么做呢?
uart_buf[]只能接收1位数据,后面来的数据会把前面的数据顶掉。所以我们应该重新定义一个数组,用来存储我们需要的位数的数据。
比如说我们需要接收3个数据,第一个数据写入到eeprom里面,第二个数据用来控制led灯的点亮,第三个数据用来设置dac的值。那么我们此处定义一个10位的数组(大于3即可)和一个计数单位:

u8 rx_buf[10];
u8 rx_cnt=0;

按照如下方法编写回调函数(即:每进入一次回调函数,把uart_buf[]里面的数据赋值给我们自定义的数据存储数组rx_buf,由于我们一共希望要三个数据,所以我们当rx_cnt==3的时候就可以退出,重新置位,这样就得到了三个数据)

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	rx_buf[rx_cnt++]=uart_buf[0];
	if(rx_cnt==3)
	{
		rx_cnt=0;
		//第一位数据写入eeprom。。。省略代码
		LED_Control(rx_buf[1]);
		//第三位数据用作dac、、、省略代码
	}
	HAL_UART_Receive_IT(&huart1,uart_buf,1);//开启串口接收中断
}

但是此时有一个问题是:
假设接收3个字节固定长度数据:

  1. 用户先发送了1个字节数据(错误的命令,正确的命令应该是3个字节);
  2. 用户发现发错数据了,又发送了3个字节的正确字节数据
  3. 结果,rx_buf会缓存1个字节的错误数据

比如:用户本来想发的是:11 55 22
但是一开始只发了一个数据:11
后来想起来应该发三个数据,所以又发了11 55 22
那么此时rx_buf的前三位是:11 11 55,也就是多存储了错误的那个命令。
这种问题应该如何避免呢?
由于我们的比特率通常都很高,所以如果连发三个数据,这三个数据之间的间隔应该是小于50ms的,但与前面的错误数据的时间间隔就会很大了,因为这是我们人为导致的。这就是我们区分错误数据和正确数据的依据。所以我们只需判断数据间的间隔时间就可以了:

//usart
u8 tx_buf[]={"您好\r\n"};
u8 uart_buf[2];
u8 rx_buf[10];
u8 rx_cnt=0;
__IO uint32_t usartTick =0;
void RxIdle_Process()
{
	if(uwTick-usartTick<50)return;
	usartTick=uwTick;
	//50ms执行一次清空rx_buf
	rx_cnt=0;
	memset(rx_buf,'\0',sizeof(rx_buf));
}
//串口接收回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	usartTick=uwTick;//重新开始计时50ms
	rx_buf[rx_cnt++]=uart_buf[0];
	if(rx_cnt==3)
	{
		rx_cnt=0;
		LED_Control(rx_buf[0]);
	}
	HAL_UART_Receive_IT(&huart1,uart_buf,1);//开启串口接收中断
}

就可以完美解决这样的问题了。

串口接收带帧尾的不定长数据

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	usartTick=uwTick;//重新开始计时50ms
	rx_buf[rx_cnt++]=uart_buf[0];
//	if(rx_cnt==3)
//	{
//		rx_cnt=0;
//		LED_Control(rx_buf[0]);
//	}
		if(uart_buf[0]!='\n')  //接收到换行符
	{
		rx_cnt=0;
		LED_Control(rx_buf[0]);
	}
	HAL_UART_Receive_IT(&huart1,uart_buf,1);//开启串口接收中断
}

最终的main.c如下:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * 

© Copyright (c) 2021 STMicroelectronics. * All rights reserved.

* * This software component is licensed by ST under BSD 3-Clause license, * the "License"; You may not use this file except in compliance with the * License. You may obtain a copy of the License at: * opensource.org/licenses/BSD-3-Clause * ****************************************************************************** */
/* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "gpio.h" #include "led.h" #include "key.h" #include "i2c.h" #include "dac.h" #include "rtc.h" #include "usart.h" #include "string.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ //Led执行程序 __IO uint32_t ledTick =0,keyTick=0; u8 led_ctrl=0xff; void LED_Process(void) { if(uwTick-ledTick<500)return; ledTick=uwTick; LED_Control(led_ctrl); led_ctrl=~led_ctrl; } //KEY void KEY_Process(void) { if(uwTick-keyTick<10)return; keyTick=uwTick; Key_Read(); // if(Trg&0x01) // { // LED_Control(0x01); // } if(Trg) { LED_Control(Trg); } } //EEPROM u8 val_24c02=0; //DAC u16 dac_ch1_val,dac_ch2_val; void DAC_Process(void) { dac_ch1_val=(1.1f/3.3f*4095);//输出1.1V dac_ch2_val=(2.1f/3.3f*4095);//输出2.2V HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_1,DAC_ALIGN_12B_R,dac_ch1_val);//0->0V;4095->3.3V HAL_DAC_Start(&hdac1,DAC_CHANNEL_1); HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_2,DAC_ALIGN_12B_R,dac_ch2_val);//0->0V;4095->3.3V HAL_DAC_Start(&hdac1,DAC_CHANNEL_2); } //RTC RTC_TimeTypeDef rtc_time; RTC_DateTypeDef rtc_date; void RTC_Process(void) { HAL_RTC_GetTime(&hrtc,&rtc_time,RTC_FORMAT_BIN); HAL_RTC_GetDate(&hrtc,&rtc_date,RTC_FORMAT_BIN); } //LCD void LCD_Process(void) { u8 display_buf[20]; sprintf((char*)display_buf,"%02d-%02d-%02d",rtc_time.Hours,rtc_time.Minutes,rtc_time.Seconds); LCD_DisplayStringLine(Line0,display_buf); sprintf((char*)display_buf,"%04d-%02d-%02d",rtc_date.Year,rtc_date.Month,rtc_date.Date); LCD_DisplayStringLine(Line1,display_buf); sprintf((char*)display_buf,"EEPROM:%d",val_24c02); LCD_DisplayStringLine(Line2,display_buf);//输出百分号:% } //usart u8 tx_buf[]={"您好\r\n"}; u8 uart_buf[2]; u8 rx_buf[10]; u8 rx_cnt=0; __IO uint32_t usartTick =0; void RxIdle_Process() { if(uwTick-usartTick<50)return; usartTick=uwTick; //50ms执行一次清空rx_buf rx_cnt=0; memset(rx_buf,'\0',sizeof(rx_buf)); } /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ 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(); /* USER CODE BEGIN 2 */ LCD_Init(); LED_Control(0x00); MX_DAC1_Init(); MX_RTC_Init(); I2CInit(); MX_USART1_UART_Init(); HAL_UART_Receive_IT(&huart1,uart_buf,1);//开启串口接收中断 /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ LCD_Clear(Blue); LCD_SetBackColor(Blue); LCD_SetTextColor(White); EEPROM_Write(0x10,0x55); val_24c02=EEPROM_Read(0x10); // HAL_UART_Transmit(&huart1,(unsigned char*)"helloworld!\r\n",sizeof("helloworld!\r\n")-1,50); // HAL_UART_Transmit(&huart1,(unsigned char*)"helloworld!\r\n",sizeof("helloworld!\r\n")-1,50); // HAL_UART_Transmit(&huart1,(unsigned char*)tx_buf,sizeof(tx_buf)-1,50); printf("%s \n","你好!"); printf("您输入的数字是%d",2); while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ //LED_Process(); KEY_Process(); DAC_Process(); RTC_Process(); LCD_Process(); RxIdle_Process(); } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; /** Configure the main internal regulator output voltage */ HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1); /** Initializes the CPU, AHB and APB busses clocks */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV2; RCC_OscInitStruct.PLL.PLLN = 20; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB busses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK) { Error_Handler(); } /** Initializes the peripherals clocks */ PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_USART1 |RCC_PERIPHCLK_ADC12; PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2; PeriphClkInit.Adc12ClockSelection = RCC_ADC12CLKSOURCE_SYSCLK; PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSI; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ //串口接收回调函数 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { usartTick=uwTick;//重新开始计时50ms rx_buf[rx_cnt++]=uart_buf[0]; // if(rx_cnt==3) // { // rx_cnt=0; // LED_Control(rx_buf[0]); // } if(uart_buf[0]!='\n') //接收到换行符 { rx_cnt=0; LED_Control(rx_buf[0]); } HAL_UART_Receive_IT(&huart1,uart_buf,1);//开启串口接收中断 } /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

你可能感兴趣的:(蓝桥杯(嵌入式),蓝桥杯,单片机,stm32,嵌入式硬件,人工智能)