目录
- 前言:
- 1、配置及代码:
- (1) A板:
- [1] CUBMX配置:
- [2] keil5代码:
- (2) B板:
- [1] CUBMX配置:
- [2] keil5代码:
- 2、代码解析:
前言:
这届国赛是目前为止最难一届,我赛后完完整整又做了一遍,花了我两天大概12小时,这个难度完全可以当一个stm32的期末设计,下面将对本届的题结合对应代码做重点解析
对应试题在这里:
第十五届蓝桥杯物联网试题(国赛)
1、配置及代码:
(1) A板:
[1] CUBMX配置:
[2] keil5代码:
main.c:
#include "main.h"
#include "dma.h"
#include "i2c.h"
#include "spi.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes *//* 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 */
#include "Function.h"
#include "oled.h"
#include "lora.h"
/* 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_DMA_Init();MX_I2C3_Init();MX_TIM3_Init();MX_USART2_UART_Init();MX_SPI1_Init();MX_TIM7_Init();/* USER CODE BEGIN 2 */Function_OledInit(50);LORA_Init();HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_3);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */Function_MyMain();}/* USER CODE END 3 */
}
Function.c:
#include "Function.h"
#include "main.h"
#include "oled.h"
#include "i2c.h"
#include "spi.h"
#include "lora.h"
#include <stdarg.h>
#include <stdio.h>
#include "usart.h"
#include "tim.h"
#include <string.h>unsigned char URXMSG[20];
unsigned char UTXMSG[20];
unsigned char LTXMSG[20];
unsigned char LRXMSG[20];
int BEIGNNUMBER = 0;
int ENDNUMBER = 0;
unsigned char STATETIM = 0;
unsigned char ARRAY1[20];
int PLUSEVALUE = 0;
uint16_t COUNTNUMBER = 0;
unsigned char MEMORDER[20] = {'N', 'F', ' '};void OLED_Write(unsigned char type, unsigned char data){unsigned char WriteData[2];WriteData[0] = type;WriteData[1] = data;HAL_I2C_Master_Transmit(&hi2c3, 0x78, WriteData, 2, 0xff);
}void Function_OledInit(unsigned char ms){HAL_GPIO_WritePin(OLED_Power_GPIO_Port, OLED_Power_Pin, GPIO_PIN_RESET);HAL_Delay(ms);OLED_Init();
}unsigned char SPI_WriteRead(unsigned char address, unsigned char data){unsigned char TxData[2], RxData[2];TxData[0] = address;TxData[1] = data;HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port, SPI1_NSS_Pin, GPIO_PIN_RESET);HAL_SPI_TransmitReceive(&hspi1, TxData, RxData, sizeof(TxData), 0xff);HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port, SPI1_NSS_Pin, GPIO_PIN_SET);return RxData[1];
}void Function_ArrayClean(unsigned char* array, uint16_t len){for(unsigned char i = 0; i < len; i ++) array[i] = '\0';
}void myprintf(unsigned char address, const char* format, ...) {char ARRAY[40];// 使用va_list和va_start来处理可变参数va_list args;va_start(args, format);// 使用vsprintf将可变参数列表格式化的字符串存储到ARRAY中vsprintf(ARRAY, format, args);// 清理可变参数列表va_end(args);// 假设OLED_ShowString函数可以在OLED屏幕上显示字符串OLED_ShowString(address, (unsigned char*)ARRAY);
}void Function_UartTxOk(){HAL_UART_Transmit(&huart2, (unsigned char* )"OK ", 5, 0xff);
}void Function_UartTxError(){HAL_UART_Transmit(&huart2, (unsigned char* )"ERROR ", 8, 0xff);
}void Function_HandleURxMsg(){if(URXMSG[0] == '$' && URXMSG[1] == '\0'){LORA_Tx((unsigned char* )"$", 1);}if(URXMSG[0] == '?' && URXMSG[1] == '\0'){LORA_Tx((unsigned char* )"?", 1);}if(URXMSG[0] == '@' && URXMSG[1] == '\0'){sprintf((char* )LTXMSG, "%d", PLUSEVALUE);LORA_Tx(LTXMSG, 10); Function_ArrayClean(LTXMSG, sizeof(LTXMSG)); }if(URXMSG[0] == 'F'){if(strncmp((char* )URXMSG, "FS1:", 4) == 0 || strncmp((char* )URXMSG, "FS2:", 4) == 0) LORA_Tx(URXMSG, 20); // 判断是不是正确的else Function_UartTxError(); }unsigned char temp = URXMSG[0];if(temp == '$' && URXMSG[1] == '\0'){ // 切换和同步命令需要返还OKFunction_UartTxOk(); // 返还OKFunction_ArrayClean(URXMSG, sizeof(URXMSG)); // 清理} if(((temp == '?' || temp == '@') && URXMSG[1] == '\0') || temp == 'F'){ // 参数和查询指令交给B端处理后统一返还Function_ArrayClean(URXMSG, sizeof(URXMSG));} if(URXMSG[0] != '\0') { // 接收了其他垃圾消息Function_ArrayClean(URXMSG, sizeof(URXMSG));Function_UartTxError(); // 返还错误}
}void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){ // 10msif(COUNTNUMBER < 300) COUNTNUMBER ++;else{COUNTNUMBER = 0;HAL_GPIO_WritePin(LD5_GPIO_Port, LD5_Pin, GPIO_PIN_SET); // 随手关灯}
}void Function_LoRxMsg(){ // B端处理后的结果LORA_Rx(LRXMSG);if(LRXMSG[0] != '\0'){if(LRXMSG[0] == '0') Function_UartTxError(); // A端接收的数据不正确else if(LRXMSG[0] == 'R' || LRXMSG[0] == 'N'){ // 接收到LORB传递的数据HAL_UART_Transmit(&huart2, LRXMSG, strlen((char* )LRXMSG), 0xff);HAL_TIM_Base_Start_IT(&htim7); // 开启定时器HAL_GPIO_WritePin(LD5_GPIO_Port, LD5_Pin, GPIO_PIN_RESET); // 开灯}else if(LRXMSG[0] == '1') Function_UartTxOk(); // A端接收的数据正确Function_ArrayClean(LRXMSG, sizeof(LRXMSG)); // 清除接收的数据}}void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){//OLED_ShowString(0, "ok");if(STATETIM == 0){STATETIM ++;BEIGNNUMBER = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_3);__HAL_TIM_SET_CAPTUREPOLARITY(&htim3, TIM_CHANNEL_3, TIM_ICPOLARITY_FALLING);}else if(STATETIM == 1){STATETIM ++;__HAL_TIM_SET_CAPTUREPOLARITY(&htim3, TIM_CHANNEL_3, TIM_ICPOLARITY_RISING);}else if(STATETIM == 2){STATETIM ++;ENDNUMBER = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_3);}
}void Function_UartRxMsg(){HAL_UARTEx_ReceiveToIdle_IT(&huart2, URXMSG, 50);if(URXMSG[0] != '\0'){HAL_Delay(10); // 让cpu等待DMA把数据接收完memcpy(MEMORDER, URXMSG, 20);Function_HandleURxMsg();}
}void Function_PluseShow(){if(STATETIM == 3){PLUSEVALUE = (int)(1000000 * 1.0 / (ENDNUMBER - BEIGNNUMBER));myprintf(2, " %dHZ ", PLUSEVALUE);//myprintf(0, " %d ", ENDNUMBER - BEIGNNUMBER); //myprintf(2, " %.1f ", (ENDNUMBER - BEIGNNUMBER) / 10.0);STATETIM = 0;HAL_Delay(200);}
}void Function_MyMain(){Function_UartRxMsg();//myprintf(0, URXMSG);myprintf(0, " %s ", MEMORDER); // 显示最近一次接收到的数据Function_LoRxMsg();Function_PluseShow();//Function_HandleURxMsg();
}
Function.h:
#ifndef __FUNCTION_H__
#define __FUNCTION_H__
#include "main.h"void OLED_Write(unsigned char type, unsigned char data);void Function_OledInit(unsigned char ms);void Function_MyMain();void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);#endif
(2) B板:
[1] CUBMX配置:
[2] keil5代码:
main.c:
#include "main.h"
#include "adc.h"
#include "i2c.h"
#include "spi.h"
#include "tim.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes *//* 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 */
#include "Function.h"
#include "oled.h"
#include "lora.h"
/* 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_I2C3_Init();MX_SPI1_Init();MX_TIM2_Init();MX_TIM7_Init();MX_ADC_Init();/* USER CODE BEGIN 2 */Function_OledInit(50);HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // 开启方波 1kHZHAL_TIM_Base_Start_IT(&htim7);LORA_Init();/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */Function_MyMain();}/* USER CODE END 3 */
}
Function.c
#include "Function.h"
#include "main.h"
#include "i2c.h"
#include "oled.h"
#include <stdio.h>
#include "tim.h"
#include "adc.h"
#include <stdarg.h>
#include "spi.h"
#include "lora.h"
#include <string.h>
#include <stdlib.h>float FS1 = 6.0;
float FS2 = 6.0;
float F1VL = 0;
float F2VL = 0;
unsigned char STATEADC = 0; // ADC状态
unsigned char OLEDSHOW = 0; // OLED显示
uint16_t COUNTNUM = 0;
unsigned char LEDSTATE = 0;
float DERTF1F2 = 0; // 获取FS1与FS2差值 / 0.2
unsigned char INDEX = 7; // 算一下AA中第一个A的具体位置
unsigned char MEMWINNER[20] = {'N', 'F', ' '};
uint16_t ADCRUNNUM = 0;
unsigned char RUNSHOWARR[20];
unsigned char LORXMSG[20];
int NEWPWM = 0;
int CLK = 32000000;
int MAINDRT = 32000000;
int INDEXX = 0;
int INDEXY = 0; // 获取要调的参数void OLED_Write(unsigned char type, unsigned char data){unsigned char WriteData[2];WriteData[0] = type;WriteData[1] = data;HAL_I2C_Master_Transmit(&hi2c3, 0x78, WriteData, 2, 0xff);
}void Function_OledInit(unsigned char ms){HAL_GPIO_WritePin(OLED_Power_GPIO_Port, OLED_Power_Pin, GPIO_PIN_RESET);HAL_Delay(ms);OLED_Init();
}unsigned char SPI_WriteRead(unsigned char address, unsigned char data){unsigned char TxData[2], RxData[2];TxData[0] = address;TxData[1] = data;HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port, SPI1_NSS_Pin, GPIO_PIN_RESET);HAL_SPI_TransmitReceive(&hspi1, TxData, RxData, sizeof(TxData), 0xff);HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port, SPI1_NSS_Pin, GPIO_PIN_SET);return RxData[1];
}void myprintf(unsigned char address, const char* format, ...) {char ARRAY[40];// 使用va_list和va_start来处理可变参数va_list args;va_start(args, format);// 使用vsprintf将可变参数列表格式化的字符串存储到ARRAY中vsprintf(ARRAY, format, args);// 清理可变参数列表va_end(args);// 假设OLED_ShowString函数可以在OLED屏幕上显示字符串OLED_ShowString(address, (unsigned char*)ARRAY);
}void Function_ArrayClean(unsigned char* array, uint16_t len){for(unsigned char i = 0; i < len; i ++) array[i] = '\0';
}void Function_GetAdc(){uint16_t AdcData[2];float AdcValue[2];for(unsigned char i = 0; i < 2; i ++){HAL_ADC_Start(&hadc);HAL_ADC_PollForConversion(&hadc, 0xff);AdcData[i] = HAL_ADC_GetValue(&hadc);AdcValue[i] = AdcData[i] * 3.30f / 4095;}HAL_ADC_Stop(&hadc);F1VL = FS1 / 3.0 * AdcValue[1] - 0.1 * FS1;F2VL = FS2 / 3.0 * AdcValue[0] - 0.1 * FS1;
}void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){ // 按键//OLED_ShowString(0, "we");OLEDSHOW = (OLEDSHOW + 1) % 3;
}void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){ // 10ms中断一次/* 为run状态服务 */if(STATEADC == 2){ if(ADCRUNNUM % 20 == 0) Function_GetAdc(); // 按题意理解采集器在run状态才会采集且采集间隔为0.2sADCRUNNUM ++;// 更新A的坐标if(F1VL >= F2VL){DERTF1F2 = (int)((F1VL - F2VL) / 0.2); // 记得要强转int一下,注意(int)的优先级if(DERTF1F2 >= 7) INDEX = 0; // 更新A的坐标else INDEX = 7 - DERTF1F2;}else{DERTF1F2 = (int)((F2VL - F1VL) / 0.2);if(DERTF1F2 + 7 >= 14) INDEX = 14;else INDEX = 7 + DERTF1F2;}if(INDEX == 0 || INDEX == 14){ // 判断AA是否在左右两边if(COUNTNUM < 500) COUNTNUM ++;else{ // 坚持5s,记录获胜者COUNTNUM = 0;ADCRUNNUM = 0;STATEADC = 0;if(INDEX == 0) sprintf((char* )MEMWINNER, "RP1:%.1f ", F1VL); else sprintf((char* )MEMWINNER, "RP2:%.1f ", F2VL);LEDSTATE = 1; // 有人获胜开启LD5}}else COUNTNUM = 0; // AA没在边缘不记时}/* 为LED闪烁服务 */if(LEDSTATE == 1){ if(COUNTNUM < 500){if(COUNTNUM % 10 == 0) HAL_GPIO_TogglePin(LD5_GPIO_Port, LD5_Pin); // 0.1s也就是100ms,整10倍数闪烁一次即可COUNTNUM ++;}else{COUNTNUM = 0;LEDSTATE = 0;HAL_GPIO_WritePin(LD5_GPIO_Port, LD5_Pin, GPIO_PIN_SET); // 将LD5灯关掉}}}void Function_RunStateShow(){for(unsigned char i = 0; i < 16; i ++) RUNSHOWARR[i] = '#'; RUNSHOWARR[INDEX] = 'A';RUNSHOWARR[INDEX + 1] = 'A'; // 填充 myprintf(0, " RUN ");myprintf(2, (char* )RUNSHOWARR);
}void Function_OledShow(){if(OLEDSHOW == 0){if(STATEADC == 0){myprintf(0, " Idle ");myprintf(2, "#######AA#######");}else if(STATEADC == 1){myprintf(0, " Ready ");myprintf(2, "#######AA#######");}else if(STATEADC == 2){Function_RunStateShow();}}else if(OLEDSHOW == 1){myprintf(0, " F1:%.1fKN ", F1VL);myprintf(2, " F2:%.1fKN ", F2VL); }else if(OLEDSHOW == 2){myprintf(0, " FS1:%.1fKN ", FS1);myprintf(2, " FS2:%.1fKN ", FS2);//myprintf(2, " kkkk ");}
}void Function_PwmChange(){ // 传过来的PWM不一定能整除,选择最接近的PWMNEWPWM = (int)strtof((char* )LORXMSG, NULL); // 获取要配置的新的PWMMAINDRT = CLK;for(int i = 1; i <= 65535; i ++){for(int j = 1; j <= 65535; j ++){if(i * j * NEWPWM - CLK > MAINDRT) break; // 差值太大说明j太大int temp = abs(i * j * NEWPWM - CLK); if(temp <= MAINDRT){ // 如果差值很小MAINDRT = temp;INDEXX = i;INDEXY = j;}}if(MAINDRT == 0) break; // 能整除就没必要继续了}__HAL_TIM_SET_PRESCALER(&htim2, INDEXX - 1);__HAL_TIM_SET_AUTORELOAD(&htim2, INDEXY - 1); // 修改预分频与自动重装载//myprintf(0, "MIN: %d ", MAINDRT); //myprintf(2, "NPM: %d ",CLK / INDEXX / INDEXY);//myprintf(2, "%d %d", INDEXX, INDEXY);
}void Function_LoRxMsg(){LORA_Rx(LORXMSG);//myprintf(0, LORXMSG);if(LORXMSG[0] == '$'){ // 切换模式STATEADC = (STATEADC + 1) % 3;//myprintf(0, "%d", STATEADC);}if(LORXMSG[0] == '?'){ // 发送上一次的胜利者LORA_Tx(MEMWINNER, sizeof(MEMWINNER));}if(LORXMSG[0] >= '0' && LORXMSG[0] <= '9'){ // 传回了脉冲信号//myprintf(2, "%d", STATEADC);if(STATEADC == 0){Function_PwmChange();LORA_Tx((unsigned char* )"1", 1);}else LORA_Tx((unsigned char* )"0", 1); // 传回报错信息 }if(LORXMSG[0] == 'F'){ // 修改命令unsigned char memfs[20];memcpy(memfs, LORXMSG + 4, 10);char* q = NULL;float temp = strtof((char* )memfs, &q);if(*q != '\0') LORA_Tx((unsigned char* )"0", 1); // 错误数据else if(LORXMSG[2] == '1'){FS1 = temp;LORA_Tx((unsigned char* )"1", 1);}else if(LORXMSG[2] == '2'){FS2 = temp;LORA_Tx((unsigned char* )"1", 1); // 返回成功信息}//myprintf(0, "temp: %.1f", temp);//myprintf(2, "F1: %.1f F2: %.1f", FS1, FS2);}if(LORXMSG[0] != '\0') Function_ArrayClean(LORXMSG, sizeof(LORXMSG));
}void Function_K1K2Ctrl(){if(STATEADC == 0){ // 空闲HAL_GPIO_WritePin(K1_GPIO_Port, K1_Pin, GPIO_PIN_RESET);HAL_GPIO_WritePin(K2_GPIO_Port, K2_Pin, GPIO_PIN_RESET);}else if(STATEADC == 1){HAL_GPIO_WritePin(K1_GPIO_Port, K1_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(K2_GPIO_Port, K2_Pin, GPIO_PIN_RESET);}else if(STATEADC == 2){HAL_GPIO_WritePin(K1_GPIO_Port, K1_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(K2_GPIO_Port, K2_Pin, GPIO_PIN_SET);}
} void Function_MyMain(){Function_LoRxMsg();Function_K1K2Ctrl();Function_OledShow();//myprintf(0, "%d,%c,%s,%.1fH", 20, 'a', "hello", 11.1);}
Function.h:
#ifndef __FUNCTION_H__
#define __FUNCTION_H__
#include "main.h"void OLED_Write(unsigned char type, unsigned char data);void Function_OledInit(unsigned char ms);void Function_MyMain();void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);void myprintf(unsigned char address, const char* format, ...);unsigned char SPI_WriteRead(unsigned char address, unsigned char data);#endif