stm32CubeMX,配合Keil,使用HAL库进行串口通信

根据自身目标板的芯片开始创建工程

 设置高速外部时钟,选择外部时钟源

设置串口

 

 

设置LED灯

 

设置外部时钟,我的是25MHz

 

设置项目文件

用keil5打开工程文件,点击编译一遍,确认没有报错 。

首先我们需要重定向printf函数。

单片机为什么要重定向printf???

        在c语言中我们只要包含<stdio.h>的库就可以直接调用printf打印函数,(因为printf输出底层用到fputc,而fputc会输出到标准输出流,再由操作系统拿到这个标准输出流指针去自动匹配显示到你的电脑屏幕,而对于不带操作系统的单片机,输出到fputc就打住了,因为没有人继续帮你操作这个标准输出流指针了,不重定向到串口之类的显示窗口就看不到打印信息了)但是对于单片机而言,即使包含了这个库,我们也不知道打印的输出方是谁,这时候我们就需要告诉单片机向电脑PC端打印数据,因此需要重写printf函数,将fputc和fgetc函数重写。

方法一:勾选Use MicroLib,使用半主机模式

MicroLib是对标准C库进行了高度优化之后的库,供MDK默认使用,相比之下,MicroLIB的代码更少,资源占用更少,在MDK编译环境下可以使用其MicroLib,快速实现printf重定向;

MicroLib很好,但在其他编译环境下不可用,有的MCU也不支持;

微库MicroLib:是缺省的C库。如果不勾选“Use MicroLIB”,keil会连接标准C库。所以勾选“Use MicroLIB”会减小code大小。它不完全兼容ANSI,但已能满足大多数的应用。

勾选这个会导致很多库函数用不了,但换来的是节省了大量的空间。

在usart.c文件中加入下列程序,记得在usart.h文件添加头文件#include<stdio.h>

/**
  * 函数功能: 重定向c库函数printf到DEBUG_USARTx
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
int fputc(int ch, FILE *f)
{
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
  return ch;
}
 
/**
  * 函数功能: 重定向c库函数getchar,scanf到DEBUG_USARTx
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
int fgetc(FILE *f)
{
  uint8_t ch = 0;
  HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
  return ch;
}

现在可以在main.c里面使用printf

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		printf ("test_running!!!\r\n");
		HAL_GPIO_WritePin(GPIOI,LED_G_Pin,GPIO_PIN_RESET);//低电平点亮绿灯
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

点击魔术棒,设置好如下选项,编译,下载程序到板卡 

 运行结果,正常打印到串口!

测试printf和scanf功能,重定向这两个函数后,在main.c中输入以下测试代码

  /* USER CODE BEGIN 2 */
	uint8_t name[10];
	int32_t age;
	float score;
	int8_t level;

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	printf("Enter your  name: \r\n");
	scanf ("%10s",name);  
	printf("Enter your age: \r\n");
	scanf ("%d",&age);
	printf("Enter your score: \r\n");
	scanf ("%f",&score);
	printf("Enter your level: \r\n");
	scanf ("%c",&level);
	printf("\r\nHi, %s, %d years old. \r\n", name, age);
	printf("Your score is %2.2f. Your level is %c.\r\n\r\n", score, level);	

	printf ("test_running!!!\r\n");
	HAL_GPIO_WritePin(GPIOI,LED_G_Pin,GPIO_PIN_RESET);//低电平点亮绿灯
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

还是按以上设置并编译下载,运行结果如下:

方法二 :此方法不需要勾选MicroLib,而且可以很方便的自定义多个printf函数。

在usart.c文件中添加以下函数 

//重定向printf1至uart4的demo
void printf1(const char *fmt,...)
{
  char buf[100] = {0};
  va_list arg;
  
  va_start(arg,fmt);
  vsnprintf(buf,sizeof(buf),fmt,arg);
  va_end(arg);
  
  HAL_UART_Transmit(&huart4, (uint8_t*)buf, strlen(buf), HAL_MAX_DELAY);
}

在usart.h文件中添加以下两个头文件和prinf1函数的声明

#include <string.h>
#include <stdarg.h>
void printf1(const char *fmt,...);

 然后即可在main.c中调用printf1()函数。记得在main.h文件中加上#include "usart.h"的头文件。

 /* USER CODE BEGIN WHILE */
  while (1)
  {
		printf1("running!!!\r\n");
		HAL_GPIO_WritePin(GPIOI,LED_G_Pin,GPIO_PIN_RESET);//低电平点亮绿灯
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

编译,下载到板卡,运行成功!!!

 

 方法三:一样写在 usart.c文件中即可,使用了寄存器操作,运行效率更高;

#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE 
{ 
	int handle; 
}; 
 
FILE __stdout;  
FILE __stdin;   
//定义_sys_exit()以避免使用半主机模式    
void _sys_exit(int x) 
{ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{ 	
	while((UART4->SR&0X40)==0);//循环发送,直到发送完毕   
	UART4->DR = (uint8_t) ch;      
	return ch;
}
 
///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
    uint8_t ch = 0;
	while((UART4->SR&0X20)==0);//循环发送,直到发送完毕   
	ch = (uint8_t) UART4->DR;   
    return ch;
}

当然,不直接操作寄存器,还是可以使用和之前一样的重定义代码。 

int fputc(int ch, FILE *f)
{
  HAL_UART_Transmit(&huart4, (uint8_t *)&ch, 1, HAL_MAX_DELAY );//0xffff
  return ch;
}
 
/**
  * 函数功能: 重定向c库函数getchar,scanf到DEBUG_USARTx
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
int fgetc(FILE *f)
{
  uint8_t ch = 0;
  HAL_UART_Receive(&huart4, &ch, 1, HAL_MAX_DELAY);//0xffff
  return ch;
}

 在main.c中加入测试代码

  /* USER CODE BEGIN 2 */
	uint8_t name[10];
	int32_t age;
	float score;
	int8_t level;

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		
		printf("Enter your  name: \r\n");
		scanf ("%10s",name);  
		printf("Enter your age: \r\n");
		scanf ("%d",&age);
		printf("Enter your score: \r\n");
		scanf ("%f",&score);
		printf("Enter your level: \r\n");
		scanf ("%c",&level);
		printf("\r\nHi, %s, %d years old. \r\n", name, age);
		printf("Your score is %2.2f. Your level is %c.\r\n", score, level);	

//		printf1("running!!!\r\n");
		HAL_GPIO_WritePin(GPIOI,LED_G_Pin,GPIO_PIN_RESET);//低电平点亮绿灯
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

编译,下载到板卡,测试成功!!!

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用STM32CubeMX来实现两个单片机间的串口通信使用HAL库编程。下面是一个基本的步骤: 1. 打开STM32CubeMX,并创建一个新的工程。 2. 选择你所使用STM32微控制器型号,并配置系统时钟和其他必要的设置。 3. 在"Pinout & Configuration"选项卡中,为两个单片机的串口选择引脚。确保它们不会冲突,并且与硬件连接一致。 4. 在"Configuration"选项卡中,启用你所选择的串口,并设置波特率和其他参数。 5. 在"Project"选项卡中,选择你所使用的IDE(如Keil、IAR等),然后生成代码。 6. 打开生成的代码,并在主函数中添加相关的初始化代码。 7. 使用HAL库提供的函数来配置和控制串口。例如,使用`HAL_UART_Init()`函数初始化串口,使用`HAL_UART_Transmit()`函数发送数据,使用`HAL_UART_Receive()`函数接收数据等等。 8. 配置中断(可选):如果你希望使用中断来处理串口数据,你可以使用`HAL_UART_Receive_IT()`函数启用接收中断,并在相应的中断处理函数中处理接收到的数据。 9. 在两个单片机上重复以上步骤,确保它们的串口配置一致。 10. 编译并下载代码到两个单片机上,并确保它们正确连接。 这样,你就可以使用HAL库实现两个单片机之间的串口通信了。你可以根据具体的需求,发送和接收数据。记得在接收端处理接收到的数据,并根据需要进行相应的操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值