根据自身目标板的芯片开始创建工程
设置高速外部时钟,选择外部时钟源
设置串口
设置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 */
编译,下载到板卡,测试成功!!!