蓝桥杯嵌入式STM32G431RBT6模块配置详解及代码实践指南
1. 时钟配置
80MHZ(按题目要求)
HSE
SYS debug->Serial Wire
2. LED灯
PD2所存设置low, 其余IO口上拉或高电平
相关函数参考:
uint8_t led_sta; // 显示当前lcd_sta状态 static void led_disp() { // 全灭 HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOC,led_sta<<8,GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET); } // 点亮 void led_on(uint8_t sta) { led_sta |= sta; led_disp(); } // 熄灭 void led_off(uint8_t sta) { led_sta &= ~sta; led_disp(); }
3. LCD
GPIO设置output
赛题包有lcd的底层驱动
相关函数参考:
初始化
LCD_Init(); LCD_Clear(Black); LCD_SetBackColor(Black); LCD_SetTextColor(White);
显示
char text_lcd[20]; sprintf(text_lcd," "); LCD_DisplayStringLine(Line1, (uint8_t*)text_lcd);
4. 定时器
公式:
频率:f = 周期:T = =
占空比 = duty = Pulse / (ARR+1) Pulse = duty * (ARR+1)
(数据手册P76)
4.1 按键
GPIO上拉、定时器以10ms中断为例
相关代码参考:
按键定时器中断使能
HAL_TIM_Base_Start_IT(&htim3);
按键处理
interrupt.h #include "main.h" typedef struct{ uint8_t shortprs; // 短按 uint8_t longprs; // 长按 uint8_t ispressed; // 是否按下 uint8_t procedures; // 流程 uint8_t key_times; // 按下时长 uint8_t prs_keep; // 保持按下状态 }keys;
interrupt.c: #include "interrupt.h" keys key[4]={0,0,0,0}; void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance==TIM2) // 10ms { key[0].ispressed = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0); key[1].ispressed = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1); key[2].ispressed = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2); key[3].ispressed = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0); for(int i = 0; i < 4; i++) { switch(key[i].procedures) { case 0: { if(key[i].ispressed == 0) key[i].procedures = 1; }break; case 1: { if(key[i].ispressed == 0){ key[i].procedures = 2; key[i].prs_keep = 1; } else key[i].procedures = 0; }break; case 2: { key[i].key_times++; if(key[i].ispressed == 1) { if(key[i].key_times >= 70) key[i].longprs = 1; else key[i].shortprs = 1; key[i].procedures = 0; key[i].key_times = 0; key[i].prs_keep = 0; } }break; } } } }
4.2 PWM输出
例:PA6引脚输出100Hz,占空比可调节的脉冲信号
公式:占空比=
Pulse=duty*(ARR+1)
选择定时器(后面带CH的)
PWM占空比cubeMX设置
相关函数参考:
pwm定时器使能
HAL_TIM_PWM_Start(&htim16, TIM_CHANNEL_1);
设置占空比
__HAL_TIM_SetCompare(&htim16, TIM_CHANNEL_1, pa6_duty); // TIM16->CCR1 = pa6_duty; // 或者按照公式,直接改变CCR的值来改变占空比
4.3 输入捕获测量PWM频率
例:PA7引脚完成脉冲捕获功能,测量输入PA6的信号的频率
公式:输入捕获频率 f =
选择定时器
1.使能 2.选择通道模式 3.设置PSC
相关代码参考:
输入捕获定时器中断使能
HAL_TIM_IC_Start_IT(&htim17, TIM_CHANNEL_1);
interrupt.c:
uint32_t frq, capture_val; void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == TIM17) { // 捕获到上升沿,将CNT赋值给CCR // capture_val = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); capture_val = TIM17->CCR1; TIM17->CNT = 0; // 计数器清零 frq = 80000000/(80*capture_val); } }
4.4 输入捕获测555信号发生器频率
配置PB4、PA15,选择定时器
相关代码参考:
uint32_t frq_PB4, frq_PA15, capt_PB4, capt_PA15; void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { // 捕获555信号发生器频率 if(htim->Instance == TIM3) // R39 { capt_PB4 = TIM3->CCR1; TIM3->CNT = 0; frq_PB4 = 80000000/(80*capt_PB4); } if(htim->Instance == TIM8) // R40 { capt_PA15 = TIM8->CCR1; TIM8->CNT = 0; frq_PA15 = 80000000/(80*capt_PA15); } }
5. ADC
PB15、PB12设置ADCx_INxx
相关代码参考:
double getADC(ADC_HandleTypeDef *pin) { unsigned int adc; HAL_ADC_Start(pin); // 打开ADC adc = HAL_ADC_GetValue(pin); // 读取 return adc*3.3/4096; }
6. EEPROM
PB6和PB7选择GPIO_Output模式
参赛数据包I2C驱动加读写函数
i2c_hal.c
void eeprom_write(uint8_t addr, uint8_t data)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CSendByte(data);
I2CWaitAck();
I2CStop();
HAL_Delay(10);
}
uint8_t eeprom_read(uint8_t addr)
{
uint8_t data;
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CStop();
I2CStart();
I2CSendByte(0xa1);
I2CWaitAck();
data = I2CReceiveByte();
I2CSendAck();
I2CStop();
return data;
}
7. USART
一般题目波特率:9600(按照题目设置)
相关代码参考:
usart.c:
// 发送 char trans_data[20]; void UART_trans() { HAL_UART_Transmit(&huart1, (uint8_t*)trans_data, sizeof(trans_data), 50); HAL_Delay(1000); } // 接收 uint8_t rec_data; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART1) { HAL_UART_Receive_IT(huart, &rec_data, 1); // 接受一次之后返回给电脑(测试)HAL_UART_Transmit(&huart1, &rec_data, 1, 50); } }
第12届真题为例
串口传输一个数据帧包含:起始位,数据位,结束位。(一共10bit)
题目串口波特率9600s/bit
传输一次需要 10*1/9600 = 0.00104s = 1.04ms
解决方法:利用定时器(PSC=8000-1)cnt,如果cnt>15,也就是超过1.5ms则说明接受完成。
代码:
定时器4使能:
HAL_TIM_Base_Start_IT(&htim4);
接收函数:
uint8_t rec_data; uint8_t rec_flg; char rec_buff[20], buff_cnt; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART1) { TIM4->CNT = 0; rec_flg = 1; rec_buff[buff_cnt++] = rec_data; HAL_UART_Receive_IT(huart, &rec_data, 1); } } void rec_string() { if(rec_flg) { if(TIM4->CNT > 15){ // 接收完成 sscanf(rec_buff, "%4s:%4s:%12s", car_type, car_data, car_time); } else{ char temp[20]; sprintf(temp, "Error"); HAL_UART_Transmit(&huart1, (uint8_t*)temp, strlen(temp), 50); } rec_flg = 0; memset(rec_buff,0,20); buff_cnt = 0; } }
8. RTC实时时钟
可以在cubeMX里设置年月日时分秒,以及闹钟时间和模式
设置和获取时间
Format: RTC_FORMAT_BIN
RTC_FORMAT_BCD
HAL_RTC_SetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format); HAL_RTC_GetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format); HAL_RTC_SetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format); HAL_RTC_GetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format);
闹钟中断处理函数
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc) { }
/* 其他 */

作者:WuOvo-_-