蓝桥杯嵌入式_14th_STM32
# LanQiaoBei_14th_STM32## 介绍题目要求本题为第十四届蓝桥杯省赛嵌入式组真题复刻原题要求使用STM32G431实现此处使用STM32F103C8T6开发- 按键输入- ADC采集- 原题使用LCD显示此处使用OLED显示- I2C通信- PWM输出由于LED4-LED8在整个题中并未发生变化所以在此并没有配置全只保留了必要的LED指示灯。【查看完整题目】file:///C:/Users/%E5%B0%B9/Desktop/%E8%93%9D%E6%A1%A5%E6%9D%AF%E7%AC%AC14%E5%B1%8A%E7%9C%81%E8%B5%9BSTM32.pdf## 我的实现### 整体方案本项目基于STM32F103C8T6开发板使用HAL库实现蓝桥杯省赛题要求的功能。整体才用模块化设计将各功能拆分为独立的.c/.h文件### 硬件连接- 按键按键1-PA11; 按键2-PA10按键3-PA9按键4-PA8.- ADCADC1-PC0.- I2C驱动OLED显示I2C1_SCL-PB6; I2C1_SDA-PB7. OLED_W_SCL(x) HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, (GPIO_PinState)(x))OLED_W_SDA(x) HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, (GPIO_PinState)(x))- TIM: TIM2_CH2-PA1; TIM2_CH3-PA2; TIM3_CH1-PA6; TIM3_CH2-PA7.- LEDLED1-PA12; LED2-PA15; LED3-PB3; LED4-PB4; LED5-PB5### 软件架构main.c - 主程序初始化各模块adc.c/h - ADC采集相关gpio.c/h - 按键输入相关oled.c/h - OLED显示相关tim.c/h - 定时器/PWM相关i2c.c/h - I2C通信### 实现流程1.系统时钟初始化2.各外设初始化GPIO,ADC,UART,TIM,OLED3.主循环中轮询按键状态4.按键触发ADC采集5.采集结果通过I2C打印并显示在OLED上6.PWM输出随ADC值变化## 改动与优化### 1.代码结构优化原题示例代码通常把所有功能写在一个文件里我将各功能模块化拆分为独立的.c/h文件提高了代码的可读性和可维护性### 2.添加注释对关键函数和寄存器操作添加了注释方便自己和他人理解### 3.变量命名解释a-界面a1——界面1数据界面 a2——界面2参数界面 a3——界面3记录界面.c代表实际电压freq代表频率D2用来切换D20时指示参数RD21时指示参数K。D3用来表示输出信号占空比是否受 R37 电位器输出电压控制D30时 输出信号占空比受 R37 电位器输出电压控制D31时 输出信号占空比不受 R37 电位器输出电压控制即处于锁定状态R,K是题目提到的参数初始化R1,K1.且在代码中按照题目要求完成参数值不超过数值10.N是题目中提到的PWM输出模式切换次数初始状态N0. 参数N代表高频和低频的切换次数。MH高频模式最大速度ML低频模式最大速度显示保留小数点后1位有效数字。MH1和ML1各表示MH和ML改变后的值。M代表PWM 输出模式p代表实时占空比v代表实时速度v1表示v改变后的值输出模式以“H”表示高频模式、“L” 表示低频模式HZ代表显示的是低频模式还是高频模式HZ0显示低频模式HZ1显示高频模式。## 关键代码### 1.按键输入与消抖uint8_t KEY_Scan(void){static uint8_t key_up 1;// 1按键松开0按键按下uint8_t key_val KEY_NO_PRESS;// 检测按键1 (Input_1) if (key_up (HAL_GPIO_ReadPin(GPIOA, Input_1_Pin) GPIO_PIN_RESET)) // 按下{HAL_Delay(20); // 延时20ms消抖if (HAL_GPIO_ReadPin(GPIOA, Input_1_Pin) GPIO_PIN_RESET) // 再次确认{key_val KEY1_PRESS;key_up 0; // 标记已按下}}// 检测按键2 (Input_2) else if (key_up (HAL_GPIO_ReadPin(GPIOA, Input_2_Pin) GPIO_PIN_RESET)){HAL_Delay(20);if (HAL_GPIO_ReadPin(GPIOA, Input_2_Pin) GPIO_PIN_RESET){key_val KEY2_PRESS;key_up 0;}}// 检测按键3 (Input_3) else if (key_up (HAL_GPIO_ReadPin(GPIOA, Input_3_Pin) GPIO_PIN_RESET)){HAL_Delay(20);if (HAL_GPIO_ReadPin(GPIOA, Input_3_Pin) GPIO_PIN_RESET){key_val KEY3_PRESS;key_up 0;}}// 检测按键4 (Input_4) else if (key_up (HAL_GPIO_ReadPin(GPIOA, Input_4_Pin) GPIO_PIN_RESET)){HAL_Delay(20);if (HAL_GPIO_ReadPin(GPIOA, Input_4_Pin) GPIO_PIN_RESET){key_val KEY4_PRESS;key_up 0;}}// 检查是否所有按键都已松开 if ((HAL_GPIO_ReadPin(GPIOA, Input_1_Pin) GPIO_PIN_SET) (HAL_GPIO_ReadPin(GPIOA, Input_2_Pin) GPIO_PIN_SET) (HAL_GPIO_ReadPin(GPIOA, Input_3_Pin) GPIO_PIN_SET) (HAL_GPIO_ReadPin(GPIOA, Input_4_Pin) GPIO_PIN_SET)){key_up 1; // 恢复松开标志}return key_val;}### 2.ADC采集AD_ReadHAL_ADC_GetValue(hadc1);//获取ADC原始数字量 // D31 时锁定电压采集c 值不再更新if(D30) c(AD_Read*3.05)/4095.0;//实际电压 原始值 × 参考电压 / 4095### 3.PWM配置PWM初始化配置//TIM2配置为PWM 输出模式用于产生 PWM 信号通道 2、3//TIM3:配置为输入捕获模式用于检测外部 PWM 信号通道 2void MX_TIM2_Init(void){TIM_ClockConfigTypeDef sClockSourceConfig {0};TIM_MasterConfigTypeDef sMasterConfig {0};TIM_OC_InitTypeDef sConfigOC {0};htim2.Instance TIM2;htim2.Init.Prescaler 71;htim2.Init.CounterMode TIM_COUNTERMODE_UP;htim2.Init.Period 4000-1;htim2.Init.ClockDivision TIM_CLOCKDIVISION_DIV1;htim2.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_ENABLE;if (HAL_TIM_Base_Init(htim2) ! HAL_OK){Error_Handler();}sClockSourceConfig.ClockSource TIM_CLOCKSOURCE_INTERNAL;if (HAL_TIM_ConfigClockSource(htim2, sClockSourceConfig) ! HAL_OK){Error_Handler();}if (HAL_TIM_PWM_Init(htim2) ! HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(htim2, sMasterConfig) ! HAL_OK){Error_Handler();}sConfigOC.OCMode TIM_OCMODE_PWM1;sConfigOC.Pulse 2000;sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH;sConfigOC.OCFastMode TIM_OCFAST_DISABLE;if (HAL_TIM_PWM_ConfigChannel(htim2, sConfigOC, TIM_CHANNEL_2) ! HAL_OK){Error_Handler();}sConfigOC.Pulse 0;if (HAL_TIM_PWM_ConfigChannel(htim2, sConfigOC, TIM_CHANNEL_3) ! HAL_OK){Error_Handler();}/* USER CODE BEGIN TIM2_Init 2 *//* USER CODE END TIM2_Init 2 */HAL_TIM_MspPostInit(htim2);}void MX_TIM3_Init(void){TIM_ClockConfigTypeDef sClockSourceConfig {0};TIM_SlaveConfigTypeDef sSlaveConfig {0};TIM_MasterConfigTypeDef sMasterConfig {0};TIM_IC_InitTypeDef sConfigIC {0};htim3.Instance TIM3;htim3.Init.Prescaler 72-1;htim3.Init.CounterMode TIM_COUNTERMODE_UP;htim3.Init.Period 65535;htim3.Init.ClockDivision TIM_CLOCKDIVISION_DIV1;htim3.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_ENABLE;if (HAL_TIM_Base_Init(htim3) ! HAL_OK){Error_Handler();}sClockSourceConfig.ClockSource TIM_CLOCKSOURCE_INTERNAL;if (HAL_TIM_ConfigClockSource(htim3, sClockSourceConfig) ! HAL_OK){Error_Handler();}if (HAL_TIM_IC_Init(htim3) ! HAL_OK){Error_Handler();}sSlaveConfig.SlaveMode TIM_SLAVEMODE_RESET;sSlaveConfig.InputTrigger TIM_TS_TI1FP1;sSlaveConfig.TriggerPolarity TIM_TRIGGERPOLARITY_RISING;sSlaveConfig.TriggerFilter 0;if (HAL_TIM_SlaveConfigSynchro(htim3, sSlaveConfig) ! HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(htim3, sMasterConfig) ! HAL_OK){Error_Handler();}sConfigIC.ICPolarity TIM_INPUTCHANNELPOLARITY_RISING;sConfigIC.ICSelection TIM_ICSELECTION_DIRECTTI;sConfigIC.ICPrescaler TIM_ICPSC_DIV1;sConfigIC.ICFilter 0;if (HAL_TIM_IC_ConfigChannel(htim3, sConfigIC, TIM_CHANNEL_2) ! HAL_OK){Error_Handler();}PWM动态调整if(HZ0D30){__HAL_TIM_SET_AUTORELOAD(htim2,4000);__HAL_TIM_SET_AUTORELOAD(htim3,4000);if(c1.0){__HAL_TIM_SET_COMPARE(htim2,TIM_CHANNEL_2,4000*0.1);}else if(c3.0){__HAL_TIM_SET_COMPARE(htim2,TIM_CHANNEL_2,4000*0.85);}else __HAL_TIM_SET_COMPARE(htim2,TIM_CHANNEL_2,4000*(0.10.75*(c-1.0)/2));}if(HZ1D30){__HAL_TIM_SET_AUTORELOAD(htim2,8000);__HAL_TIM_SET_AUTORELOAD(htim3,8000);if(c1.0){__HAL_TIM_SET_COMPARE(htim2,TIM_CHANNEL_2,8000*0.1);}else if(c3.0){__HAL_TIM_SET_COMPARE(htim2,TIM_CHANNEL_2,8000*0.85);}else __HAL_TIM_SET_COMPARE(htim2,TIM_CHANNEL_2,8000*(0.10.75*(c-1.0)/2));}//P(c-1.0)*42.5;if(c1.0){P 10.0;}else if(c3.0){P 85.0;}else{P 37.5 * (c-1.0) 10.0;}V(PWM_A*2*pai*R)/(100*K);V110*(V-(int)V);if(HZ0){if(VML) MLV;}ML110*(ML-(int)ML);if(HZ1){if(VMH) MHV;}MH110*(MH-(int)MH);### 4.OLED显示if(a1){OLED_Init();OLED_ShowString(1, 7, DATA);if(HZ0) OLED_ShowString(2, 4, ML);else OLED_ShowString(2, 4, MH);OLED_ShowString(3, 4, P);OLED_ShowNum(3, 6, P, 2 );OLED_ShowString(3, 8, %);OLED_ShowString(4, 4, V);OLED_ShowNum(4, 6, V, 2 );OLED_ShowString(4, 8, .);OLED_ShowNum(4, 9, V1, 1 );}if (a2){OLED_Init();OLED_ShowString(1, 7, PARA);OLED_ShowString(2, 4, R);OLED_ShowNum(2, 6, R, 2 );OLED_ShowString(3, 4, K);OLED_ShowNum(3, 6, K, 2 );}if(a3){OLED_Init();OLED_ShowString(1, 7, RECD);OLED_ShowString(2, 5, N);OLED_ShowNum(2, 7, N, 2 );OLED_ShowString(3, 5, MH);OLED_ShowNum(3, 8, MH, 2 );OLED_ShowString(3, 10, .);OLED_ShowNum(3, 11, MH1, 1 );OLED_ShowString(4, 5, ML);OLED_ShowNum(4, 8, ML, 2 );OLED_ShowString(4, 10, .);OLED_ShowNum(4, 11, ML1, 1 );}}}## 效果展示功能展示视频视频中用螺丝拧的器件是R37电位器用来控制信号占空比。## 文件结构--L14-STM32MDK-ARM * Keil工程文件LED3_(1).ioc * CubeMx工程配置Core和User * 用户代码Drivers * HAL库驱动### 以下代码根据回忆整理如有错误欢迎指正使用HAL库