站长之家查询,山东省城乡住房和城乡建设厅网站,wordpress页面修改插件,美工设计需要学什么stm32学习总结#xff1a;5、Proteus8STM32CubeMXMDK仿真串口并使用串口打印日志#xff08;注意重定向printf到串口打印的问题#xff09; 文章目录 stm32学习总结#xff1a;5、Proteus8STM32CubeMXMDK仿真串口并使用串口打印日志#xff08;注意重定向printf到串口打印…stm32学习总结5、Proteus8STM32CubeMXMDK仿真串口并使用串口打印日志注意重定向printf到串口打印的问题 文章目录 stm32学习总结5、Proteus8STM32CubeMXMDK仿真串口并使用串口打印日志注意重定向printf到串口打印的问题一、前言二、资料收集三、注意事项四、STM32CubeMX配置五、MDK工程相关代码1、非中断方式的按键处理2、开关机业务3、printf重定向到串口打印4、日志打印封装 六、Proteus项目配置七、仿真测试结果八、最后 一、前言
上一节模拟实现了串口收发打印一般我们裸机打印日志通过串口或者JLINK工具等带的RTT打印对于仿真我们选择使用串口打印再合适不过了这里总结一下重定向printf到串口打印日志的过程期间尝试了CLionarm gcc的方式发现stm32f10x的flash还是支撑不起来未裁剪的标准库只要使用stdio相关标准库编译时就很容易flash超标。
二、资料收集
https://blog.csdn.net/m0_54490453/article/details/128921674https://www.cnblogs.com/pianist/p/3315801.htmlhttps://blog.51cto.com/u_13682052/5670642STM32串口使用printf打印日志https://community.st.com/t5/stm32-mcus-products/how-to-get-printf-example-working-in-another-project/m-p/392014https://community.st.com/t5/stm32-mcus-products/stm32g0-redirect-printf-to-write-and-use-uart-how-to/m-p/66318
三、注意事项
如果是使用arm gcc编译器的尽量不要使用printf这会引入标准库而对应库不像mdk的microlib做了裁剪它是比较占用flash的而stm3210x的flash最多只有32KB很容易在编译时出现section .rodata’ will not fit in region FLASH也就是超出flash范围的问题网上所说的修改xxx.ld配置文件这些方法很多时候是无效的不能盲目去修改flash配置。可以使用比如RTT打印等方式来打印日志也可以换一些资源比较丰富的板子也许官方可以出一些裁剪过的利用arm-none-eabi gcc编译的标准库后面有机会的话我会来尝试一下用stm32F10x的话arm gcc基本上没办法用printf引入标准库加上一两个简单的外设接口就肯定会flash超标用mdk原有的编译器就不会有这个问题。
四、STM32CubeMX配置
这次彻底精简一下相关配置
1、一个按钮BUTTONPA1配置GPIO OUTPUT用来接入按钮使用默认配置即可默认低电平未拉高拉低添加用户标签BUTTON 2、五个LEDPA4-PA8配置GPIO OUTPUT用来接入LED使用默认配置即可默认低电平未拉高拉低添加用户标签LED_1到LED_5 3、开启USART1PA9\PA10来作为打印的串口配置只发送波特率设置为9600不需要配置全局中断我们使用该串口作为打印串口只需要发送即可所以不需要配置中断方式来接收
然后生成代码即可。
五、MDK工程相关代码
1、非中断方式的按键处理
通过读取IO口的电平判断是否按下按钮之后通过全局变量确认按下松开以及长短按这种方式在理解上比较直观按键这里的处理逻辑是判断LED灯1的电平变化来确定是否开关机开关机的逻辑我们通过控制LED灯的亮灭来展示
#include gpio.h
#include key.h
#include pwr.h
//#include log.h// 按键的键值
#define KEY_Press 1// 读取IO口的电平
#define KEY_PWR HAL_GPIO_ReadPin(GPIOA, BUTTON_Pin)uint8_t key_old, count;uint8_t ScanKey(void)
{if (GPIO_PIN_RESET KEY_PWR) {HAL_Delay(40);//延时10-20ms防抖if (GPIO_PIN_SET KEY_PWR) {count;return KEY_Press;}} else {HAL_Delay(40);}return 0;
}void DealKey(void)
{uint8_t key_value 0;//获取键值key_value ScanKey();if (key_value ! key_old) {//与上一次的键值比较 如果不相等表明有键值的变化开始计时key_old key_value;count 0;} else {//如果没有键值的改变 说明没有新按键按下或松开key_value 0;}if (key_value)// 短按处理{switch(key_value) {case 1 : {//LOG(LOG_DEBUG, KEY1 switch);if (GPIO_PIN_SET HAL_GPIO_ReadPin(GPIOA, LED_1_Pin)) {//LOG(LOG_DEBUG, pwr on);PWROn();} else {//LOG(LOG_DEBUG, pwr off);PWROff();key_old key_value;}}break;case 2 : {
// LOG(DEBUG, KEY2 switch);}break;}key_value 0;}return;
}
#ifndef __KEY_H
#define __KEY_H#include main.hvoid DealKey(void);#endif
2、开关机业务
这里暂时通过LED灯亮灭来模拟后续可增加底板电路的控制、蜂鸣器的控制等基本都是通过控制IO口高低电平方式来控制的
#include pwr.h
#include log.h
#include gpio.hvoid TurnOnLED(int flag)
{switch(flag){case 1:HAL_GPIO_WritePin(GPIOA, LED_1_Pin, GPIO_PIN_RESET);break;case 2:HAL_GPIO_WritePin(GPIOA, LED_2_Pin, GPIO_PIN_RESET);break;case 3:HAL_GPIO_WritePin(GPIOA, LED_3_Pin, GPIO_PIN_RESET);break;case 4:HAL_GPIO_WritePin(GPIOA, LED_4_Pin, GPIO_PIN_RESET);break;case 5:HAL_GPIO_WritePin(GPIOA, LED_5_Pin, GPIO_PIN_RESET);break;}
}void TurnOffLED(int flag)
{switch(flag){case 1 :HAL_GPIO_WritePin(GPIOA, LED_1_Pin, GPIO_PIN_SET);break;case 2 :HAL_GPIO_WritePin(GPIOA, LED_2_Pin, GPIO_PIN_SET);break;case 3 :HAL_GPIO_WritePin(GPIOA, LED_3_Pin, GPIO_PIN_SET);break;case 4 :HAL_GPIO_WritePin(GPIOA, LED_4_Pin, GPIO_PIN_SET);break;case 5 :HAL_GPIO_WritePin(GPIOA, LED_5_Pin, GPIO_PIN_SET);break;}
}void PWROn(void)
{LOG(LOG_DEBUG, PWROn LED blink...);for (int i 1; i 6; i) {HAL_Delay(100);TurnOnLED(i);}
}void PWROff(void)
{LOG(LOG_DEBUG, PWROn LED off...);for (int i 1; i 6; i) {HAL_Delay(100);TurnOffLED(i);}
}#ifndef __PWR_H
#define __PWR_H#include main.hvoid PWROn(void);void PWROff(void);#endif
3、printf重定向到串口打印
通过stm32的官方论坛和一些参考文章发现是通过对printf进行重写来将发送到终端输出设备的内容通过串口发送出来不同的编译器链接的库的printf底层调用方式可能有差异需要注意一下。目前官方论坛上给到的方式是通过宏PUTCHAR_PROTOTYPE控制我尝试去寻找对应printf的源码mdk库的printf源码没有找到据网上说其裁剪的标准库实现的printf是用fputc来将字符发送出去的而gun c标准库的printf最终查看源码发现是通过调用write函数写入到显示设备的而该函数为系统调用系统通过驱动调用硬件IO口去控制显示设备将写入的内容显示对于没有系统的stm32裸机来说其封装了一个__io_putchar的类似系统调用其文件名字就叫syscalls.c接口来让我们重写输出方式这样我们重写PUTCHAR_PROTOTYPE即可目前在main.c中添加对应宏控制并重写将写入的ch字符通过串口写入
#ifdef __GNUC__/* With GCC, small printf (option LD Linker-Libraries-Small printfset to Yes) calls __io_putchar() */#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)#else#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)#endif /* __GNUC__ */PUTCHAR_PROTOTYPE{/* Place your implementation of fputc here *//* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */HAL_UART_Transmit(huart1, (uint8_t *)ch, 1, 0xFFFF);return ch;}整体main.c大循环中调用按钮监听处理
/* USER CODE BEGIN Header */
/********************************************************************************* file : main.c* brief : Main program body******************************************************************************* attention** Copyright (c) 2024 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include main.h
#include usart.h
#include gpio.h/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include key.h
#include stdio.h/* USER CODE BEGIN 0 *//* 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 *//* 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 *//* 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 *//* 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 *//* 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 *//* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#ifdef __GNUC__/* With GCC, small printf (option LD Linker-Libraries-Small printfset to Yes) calls __io_putchar() */#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)#else#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)#endif /* __GNUC__ */PUTCHAR_PROTOTYPE
{/* Place your implementation of fputc here *//* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */HAL_UART_Transmit(huart1, (uint8_t *)ch, 1, 0xFFFF);return ch;
}
/* 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 *//* 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 *//* 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();MX_USART1_UART_Init();/* USER CODE BEGIN 2 *//* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */DealKey();}/* USER CODE END 3 */
}/*** brief System Clock Configuration* retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct {0};RCC_ClkInitTypeDef RCC_ClkInitStruct {0};/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType RCC_OSCILLATORTYPE_HSI;RCC_OscInitStruct.HSIState RCC_HSI_ON;RCC_OscInitStruct.HSICalibrationValue RCC_HSICALIBRATION_DEFAULT;RCC_OscInitStruct.PLL.PLLState RCC_PLL_NONE;if (HAL_RCC_OscConfig(RCC_OscInitStruct) ! HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource RCC_SYSCLKSOURCE_HSI;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_0) ! HAL_OK){Error_Handler();}
}/* USER CODE BEGIN 4 *//* 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 */__disable_irq();while (1){}/* 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,ex: printf(Wrong parameters value: file %s on line %d\r\n, file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
4、日志打印封装
然后我们将printf函数稍微封装一下也可以使用easylogger日志库
#include log.h
#include usart.hchar* get_log_level_str(const int level)
{if (level LOG_DEBUG) {return DEBUG;}else if (level LOG_INFO) {return INFO;}else if (level LOG_WARN) {return WARN;}return UNLNOW;
}void my_log(const int level,const char* fun, const int line ,const char* fmt, ...)
{
#ifdef OPEN_LOGva_list arg;va_start(arg, fmt);char buf[50] { 0 };vsnprintf(buf, sizeof(buf), fmt, arg);va_end(arg);if (level LOG_LEVEL)printf([%-5s] [%-20s%4d] %s \r\n, get_log_level_str(level), fun, line, buf);
#endif
}
#ifndef __LOG_H_
#define __LOG_H_#include stdarg.h
#include stdio.h
#define OPEN_LOG 1
#define LOG_LEVEL LOG_DEBUGtypedef enum
{LOG_DEBUG 0,LOG_INFO,LOG_WARN
}E_LOGLEVEL;void my_log(const int level, const char* fun, const int line, const char* fmt, ...);
#define LOG(level,fmt,...) my_log(level,__FUNCTION__,__LINE__,fmt, ##__VA_ARGS__)#endif
这样我们调用LOG()进行日志打印就可以了和常规软件开发的日志打印接口基本就一致了。
六、Proteus项目配置
Proteus创建串口和虚拟终端、LED灯、电阻灯我们之前已经总结过了不再重复这里再添加一个按键即可按下P搜索BUTTON即可添加之后按如下方式接线串口TX、RX和之前接法一样还需要配置其波特率也和之前一样虚拟终端这里有一些区别上节我们是TX、RX是和串口对应一致的但是这里我们要显示串口发送的内容所以RX接入串口的TX不要搞错了而且由于是显示日志的打印串口我们配置的只发送所以理论上RX是可以不接的。至于其它的接线基本没有什么注意的和之前的大体一致主要注意控制开关的高低电平即可一端接电源另一端设置低电平即导通电流流过。
七、仿真测试结果
按下按键根据key.c中的处理逻辑就会来回判断进行开关机业务处理了这里模拟开关机通过LED灯的亮灭来展示串口的TX发送接入虚拟终端RX将发送的日志直接显示出来了串口工具在Windows上打开对端的串口也是可以看到对应的信息的快去试一下吧
八、最后
下一节我们来试下ADC和蜂鸣器的使用吧蜂鸣器这种简单的音频提示元器件也是用的比较广泛的比较常见的像小区的门禁基本都会加蜂鸣器提示刷卡是否成功等ADC接口则常常用来读取一些传感器的信息或者电压信息等也是比较常用的。最近的一些总结基本都是用为主对于这些IO口的更深层次的配置及控制原理我们暂时不做深入分析只需要了解怎么用即可这些东西感觉总结C51的时候来分析总结更合适一些C51的应用相对简单一些STM32的应用开发已经比较接近Linux应用开发这种层级很多时候不用太考虑底层的实现只需要会用接口开发比较复杂的应用即可所以这种应用开发实时操作系统的学习就比较重要了等到解决一些疑难杂症时再对其做深入了解和理解新手直接深入容易被劝退。