STM32CubeMX-HAL库开发笔记(常用语句)-基于Proteus仿真
Posted Preference for stars
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32CubeMX-HAL库开发笔记(常用语句)-基于Proteus仿真相关的知识,希望对你有一定的参考价值。
STM32CubeMX-HAL库开发笔记
前言
我自己刚刚开始学习STM32时,跟随正点原子课程,一节节课慢慢学,裸机开发可以深入了解和学习到寄存器内部,但是也偏无聊一点。后来,在做项目时,发现很难选择芯片型号,一直使用F103C8T6这款芯片。随着2021年芯片价格翻了接近30倍,自己觉得选型芯片的无比重要,遂学习STM32CubeMX。起初HAL库令自己很难适应,毕竟写个一个延时还得找资料,但是随着学习,发现CubeMX对项目的帮助是巨大的,它可以帮助你不受芯片型号的限制,快速开发项目,且内置FreeRTOS,随手就能跑个操作系统,简直不要太香。
不过界面至今没有汉化版,也是唯一的遗憾。如果只学习HAL库的话,可能会有不知底层是何为的困惑,但是也无妨。就像我作为物理学的学生,老师常说只要会用数学公式一样,我们会用即可。
这是自己编写的一个打地鼠的小游戏,串口显示程序运行位置,右方小灯显示分数,达到17分进入彩蛋程序。
Proteus 8 配置工程
使用Proteus 8可以仿真STM32F103系列T4、T6、C4、C6、R4、R6单片机,可以帮助项目前期少走一点弯路,学习者前期可以少花点钱去学习STM32。
使用STM32CubeMX配置基础工程的部分不做讲解,因为图形化真的很简单。本文章主要记录在配置工程后,HAL库函数的使用。
1、GPIO
读取IO:
HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
HAL_GPIO_ReadPin(GPIOA,BUTTONO_Pin);
写入IO:
HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);
HAL_GPIO_WritePin(GPIOA,LED0_Pin,GPIO_PIN_RESET) //置0;
HAL_GPIO_WritePin(GPIOA,LED0_Pin,GPIO_PIN_SET) //置1;
翻转IO:
HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
HAL_GPIO_TogglePin(GPIOA, LED0_Pin);
按键例子:
if(HAL_GPIO_ReadPin(GPIOA,BUTTONO_Pin) == GPIO_Pin_SET);
while(HAL_GPIO_ReadPin(GPIOA,BUTTONO_Pin) == GPIO_Pin_SET);//等待按键抬起
HAL_GPIO_TogglePin(GPIOA, LED0_Pin); //翻转
HAL_Delay(200); //延时200ms
HAL_GPIO_TogglePin(GPIOA, LED0_Pin); //翻转
2、串口通信
串口通信
串口通信模式:
Asynchronous:异步通信
发送、接收数据
//发送数据
HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
uint8_t temp[] = "Hello World!\\n\\r";
HAL_UART_Transmit(&huart1,temp,12,50);
HAL_UART_Transmit((UART_HandleTypeDef *) &huart1, (uint8_t *) "Hello World!\\r\\n", (uint16_t) 14, (uint32_t) 30);
//接收数据
HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
printf 重定向
在Private includes中引入
#include <stdio.h>
在USER CODE BEGIN 0 添加
int fputc(int ch,FILE *f)
uint8_t temp[1]=ch;
HAL_UART_Transmit(&huart1,temp,1,2); //h-uart1需要根据自己的配置修改
return ch;
然后就可以在任意地方使用printf语句方便的输出你想要的内容。
printf("Hello World!\\n\\r");
HAL_Delay(200);
多个串口发送数据
#include <stdio.h>
#include "stdarg.h"
#include "string.h"
#define TXBUF_SIZE_MAX 100
void uart?_printf(const char *format, ...)
va_list args;
uint32_t length;
uint8_t txbuf[TXBUF_SIZE_MAX] = 0;
va_start(args, format);
length = vsnprintf((char *)txbuf, sizeof(txbuf), (char *)format, args);
va_end(args);
HAL_UART_Transmit(&huart?, (uint8_t *)txbuf, length, HAL_MAX_DELAY);
memset(txbuf, 0, TXBUF_SIZE_MAX);
scanf重定向
int fgetc(FILE *f)
uint8_t ch = 0;
HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
return ch;
scanf("%d %d",&a,&b);
多个串口接收信息-回调函数
- 开启中断,只起一次作用
HAL_UART_Receive_IT(&huart1,(uint8_t *)aRxBuffer1,1);
HAL_UART_Receive_IT(&huart2,(uint8_t *)aRxBuffer2,1);
- 回调函数
#define USART1_RXBUFF_SIZE 1024 //定义串口2 接收缓冲区大小 1024字节
char USART1_RXBUFF[USART1_RXBUFF_SIZE]; //定义接收数组
uint8_t data;
uint8_t t=0;
uint8_t aRxBuffer1[1]; //定义临时存放数组
uint8_t aRxBuffer2[1]; //定义临时存放数组
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
if(huart->Instance == USART1) // 判断是由哪个串口触发的中断
data = aRxBuffer1[0];
USART1_RXBUFF[t] = data;
t++;
HAL_UART_Receive_IT(&huart1,aRxBuffer1,1); // 重新使能串口1接收中断
if(huart->Instance == USART2)
HAL_UART_Transmit(&huart2,aRxBuffer2,1,100); // 接收到数据马上使用串口1发送出去
HAL_UART_Receive_IT(&huart2,aRxBuffer2,1); // 重新使能串口2接收中断
- while函数输出
HAL_Delay(2000);
int i;
for(i=0;i<USART1_RXBUFF_SIZE;i++)
printf("%c",USART1_RXBUFF[i]);
t=0;
例2
#define USART1_RXBUFF_SIZE 256 //最大接收字节数
#define USART2_RXBUFF_SIZE 256 //最大接收字节数
char Usart1_RxBuff[USART1_RXBUFF_SIZE]; //接收数据
char Usart2_RxBuff[USART2_RXBUFF_SIZE]; //接收数据
uint8_t aRxBuffer1; //接收中断缓冲
uint8_t aRxBuffer2; //接收中断缓冲
uint8_t Uart1_Rx_Cnt = 0; //接收缓冲计数
uint8_t Uart2_Rx_Cnt = 0; //接收缓冲计数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) //串口回调函数
if(huart->Instance == USART1) // 判断是由哪个串口触发的中断
// HAL_UART_Transmit(&huart1,(uint8_t *)&aRxBuffer1,1,100); // 接收到数据马上使用串口1发送出去
// HAL_UART_Transmit(&huart2,(uint8_t *)&aRxBuffer1,1,100); // 接收到数据马上使用串口2发送出去
// HAL_UART_Receive_IT(&huart1,(uint8_t *)&aRxBuffer1,1); // 重新使能串口1接收中断
if(Uart1_Rx_Cnt >= 255) //溢出判断
Uart1_Rx_Cnt = 0;
memset(Usart1_RxBuff,0x00,sizeof(Usart1_RxBuff));
HAL_UART_Transmit(&huart1, (uint8_t *)"数据溢出", 10,0xFFFF);
else
Usart1_RxBuff[Uart1_Rx_Cnt++] = aRxBuffer1; //接收数据转存
if((Usart1_RxBuff[Uart1_Rx_Cnt-1] == 0x0A)&&(Usart1_RxBuff[Uart1_Rx_Cnt-2] == 0x0D)) //判断结束位
HAL_UART_Transmit(&huart1, (uint8_t *)&Usart1_RxBuff, Uart1_Rx_Cnt,0xFFFF); //将收到的信息发送出去
while(HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX);//检测UART发送结束
HAL_UART_Transmit(&huart2, (uint8_t *)&Usart1_RxBuff, Uart1_Rx_Cnt,0xFFFF); //将收到的信息发送出去
while(HAL_UART_GetState(&huart2) == HAL_UART_STATE_BUSY_TX);//检测UART发送结束
Uart1_Rx_Cnt = 0;
memset(Usart1_RxBuff,0x00,sizeof(Usart1_RxBuff)); //清空数组
HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer1, 1); //再开启接收中断
if(huart->Instance == USART2)
// HAL_UART_Transmit(&huart1,(uint8_t *)&aRxBuffer2,1,100); // 接收到数据马上使用串口1发送出去
if(Uart2_Rx_Cnt >= 255) //溢出判断
Uart2_Rx_Cnt = 0;
memset(Usart2_RxBuff,0x00,sizeof(Usart2_RxBuff));
HAL_UART_Transmit(&huart1, (uint8_t *)"数据溢出", 10,0xFFFF);
else
Usart2_RxBuff[Uart2_Rx_Cnt++] = aRxBuffer2; //接收数据转存
if((Usart2_RxBuff[Uart2_Rx_Cnt-1] == 0x4B)&&(Usart2_RxBuff[Uart2_Rx_Cnt-2] == 0x4F)) //判断结束位“OK”
HAL_UART_Transmit(&huart1, (uint8_t *)&Usart2_RxBuff, Uart2_Rx_Cnt,0xFFFF); //将收到的信息发送出去
while(HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX);//检测UART发送结束
Uart2_Rx_Cnt = 0;
memset(Usart2_RxBuff,0x00,sizeof(Usart2_RxBuff)); //清空数组
HAL_UART_Receive_IT(&huart2, (uint8_t *)&aRxBuffer2, 1); //再开启接收中断
strstr()函数检索信息
- 函数的声明
char *strstr(const char *haystack, const char *needle)
参数 | 说明 |
---|---|
haystack | 要被检索的 C 字符串 |
needle | 在 haystack 字符串内要搜索的小字符串 |
该函数返回在 haystack 中第一次出现 needle 字符串的位置,如果未找到则返回 null。
- 实例
#include <stdio.h>
#include <string.h>
int main()
const char haystack[20] = "RUNOOB";
const char needle[10] = "NOOB";
char *ret;
ret = strstr(haystack, needle);
printf("子字符串是: %s\\n", ret);
return(0);
char *tim_strl = strstr((char *)MQTT_CMDOutPtr+2,"timing\\":"); //strstr返回字符首次出现的地址
if((int)*(tim_strl+9) == 125) //时间为0 - 9
timing = (int)(*(tim_strl+8)) - 48; //char转化为int
else if((int)*(tim_strl+10) == 125)
timing = ((int)(*(tim_strl+8))-48)*10 + ((int)(*(tim_strl+9)) - 48); //char转化为int
else if((int)*(tim_strl+11) == 125)
timing = ((int)(*(tim_strl+8))-48)*100 + ((int)(*(tim_strl+9))-48)*10 + ((int)(*(tim_strl+10))-48);
sprintf(temp,"\\"method\\":\\"thing.event.property.post\\",\\"id\\":\\"203302322\\",\\"params\\":\\"timing\\":%d,\\"currentstate\\":1,\\"version\\":\\"1.0.0\\"",timing); //需要回复状态给服务器
MQTT_PublishQs0(P_TOPIC_NAME,temp,strlen(temp)); //添加数据,发布给服务器
memset()清空接收缓存区
memset(USART1_RXBUFF,0,USART1_RXBUFF_SIZE); //清空WiFi接收缓冲区
每次发送数据前,先情况一下缓存区
LOG信息打印
主流嵌入式输出格式:[日志级别] 文件名: 日志信息
"[info] main.c : init ok!"
"[debug] adc.c : adc_getvalue -> 3.3V"
printf("[info] main.c : HAL_Init ok! \\r\\n");
条件编译
在单片机开发过程中,需要大量的LOG信息;但是开发结束后,不需要一直打印(拖慢单片机速度)。
所以在main.h头文件添加:
#define Log 1 //打印Log信息,不想打印时改为0即可
再把.c文件中所有printf语句包裹上#id Log 与 #endif:
#if Log
printf("[info] main.c : HAL_Init ok! \\r\\n");
# endif
个性化串口输出
字符转ASCII码网站: 个性化
#define Log 1 //打印Log信息,不想打印时改为0即可
#if Log
printf(" _____ ______ _______ _ _ _____ _ _ \\r\\n");
printf(" | __ )| ____|__ __| | | | __ )( ) | |\\r\\n");
printf(" | |__) | |__ | | | | | | |__) | )| |\\r\\n");
printf(" | _ /| __| | | | | | | _ /| . ` |\\r\\n");
printf(" | | ) )| |____ | | | |__| | | ) (| |( |\\r\\n");
printf(" |_| )_)______| |_| (_____/|_| )_(_| (_|\\r\\n");
# endif
可变参数宏
在Private includes中引入
#include <stdio.h>
在USER CODE BEGIN 0 添加
int fputc(int ch,FILE *f)
uint8_t temp[1]=ch;
HAL_UART_Transmit(&huart1,temp,1,2); //h-uart1需要根据自己的配置修改
return ch;
在USER CODE BEGIN 0 添加
#define USER_LOG //注释此行,不打印
#ifdef USER_LOG
#define user_main_printf(format,...) printf(format "\\r\\n",##__VA_ARGS__)
#define user_main_info(format,...) printf("[main]info:" format "\\r\\n",##__VA_ARGS__)
#define user_main_debug(format,...) printf("[main]debug:" format "\\r\\n",##__VA_ARGS__)
#define user_main_error(format,...) printf("[main]error:" format "\\r\\n",##__VA_ARGS__)
#else
#define user_main_printf(format,...)
#define user_main_info(format,...)
#define user_main_debug(format,...)
#define user_main_error(format,...)
#endif
在while()中添加
以上是关于STM32CubeMX-HAL库开发笔记(常用语句)-基于Proteus仿真的主要内容,如果未能解决你的问题,请参考以下文章
STM32CubeMX-HAL库开发笔记-基于Proteus仿真
stm32开发笔记:stm32系列使用V3.5固件库的帮助文件以及GPIO基本功能