蓝桥杯物联网竞赛_STM32L071KBU6_第十五届蓝桥杯物联网竞赛国赛代码解析

作者 : admin 本文共16908个字,预计阅读时间需要43分钟 发布时间: 2024-06-10 共3人阅读

目录

  • 前言:
  • 1、配置及代码:
    • (1) A板:
      • [1] CUBMX配置:
      • [2] keil5代码:
    • (2) B板:
      • [1] CUBMX配置:
      • [2] keil5代码:
  • 2、代码解析:
  • 3、总结:

前言:

这届国赛是目前为止最难一届,我赛后完完整整又做了一遍,花了我两天大概12小时,这个难度完全可以当一个stm32的期末设计,下面将对本届的题结合对应代码做重点解析

对应试题在这里: 第十五届蓝桥杯物联网试题(国赛)

1、配置及代码:

(1) A板:

[1] CUBMX配置:

1、DMA串口配置:
蓝桥杯物联网竞赛_STM32L071KBU6_第十五届蓝桥杯物联网竞赛国赛代码解析插图
蓝桥杯物联网竞赛_STM32L071KBU6_第十五届蓝桥杯物联网竞赛国赛代码解析插图(1)
2、TIM3配置:
蓝桥杯物联网竞赛_STM32L071KBU6_第十五届蓝桥杯物联网竞赛国赛代码解析插图(2)
3、TIM7定时器配置:
蓝桥杯物联网竞赛_STM32L071KBU6_第十五届蓝桥杯物联网竞赛国赛代码解析插图(3)
蓝桥杯物联网竞赛_STM32L071KBU6_第十五届蓝桥杯物联网竞赛国赛代码解析插图(4)


[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 
#include 
#include "usart.h"
#include "tim.h"
#include 
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'){  // 切换和同步命令需要返还OK
Function_UartTxOk();  // 返还OK
Function_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){  // 10ms
if(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配置:

1、PWM方波配置:
蓝桥杯物联网竞赛_STM32L071KBU6_第十五届蓝桥杯物联网竞赛国赛代码解析插图(5)
蓝桥杯物联网竞赛_STM32L071KBU6_第十五届蓝桥杯物联网竞赛国赛代码解析插图(6)
2、TIM7定时器配置:
蓝桥杯物联网竞赛_STM32L071KBU6_第十五届蓝桥杯物联网竞赛国赛代码解析插图(7)
蓝桥杯物联网竞赛_STM32L071KBU6_第十五届蓝桥杯物联网竞赛国赛代码解析插图(8)


[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);  // 开启方波 1kHZ
HAL_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 
#include "tim.h"
#include "adc.h"
#include 
#include "spi.h"
#include "lora.h"
#include 
#include 
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.2s
ADCRUNNUM ++;
// 更新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不一定能整除,选择最接近的PWM
NEWPWM = (int)strtof((char* )LORXMSG, NULL);  // 获取要配置的新的PWM
MAINDRT = 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

2、代码解析:

下面将对比赛中的难点进行着重分析:

蓝桥杯物联网竞赛_STM32L071KBU6_第十五届蓝桥杯物联网竞赛国赛代码解析插图(9)
由题意可以看出来,设备采集器应是独立于OLED显示的,也就是说OLED显示的界面不影响设备采集器的状态,同样设备采集器只会在OLED出现对应显示的时候才会对其做特定影响

怎样分开两者呢?

这里可以将设备采集器放在定时器里,而oled显示放在主函数里,首先题意明确了设备采集器的采集间隔为0.2s,获胜条件为AA在左右两端保持5s,有获胜者还要LD5闪烁5s,所以说毫无疑问可以将这些过程全部放在定时器做处理,刚好定时器是中断处理,与主函数分开

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){  // 10ms中断一次
/* 为run状态服务 */
if(STATEADC == 2){   
if(ADCRUNNUM % 20 == 0) Function_GetAdc();  // 按题意理解采集器在run状态才会采集且采集间隔为0.2s
ADCRUNNUM ++;
// 更新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灯关掉
}
}
}

定时器一直保持着开启,通过接收A板的信息来改变STATEADC的值,进入定时器后就能做相应操作

蓝桥杯物联网竞赛_STM32L071KBU6_第十五届蓝桥杯物联网竞赛国赛代码解析插图(10)
动态页面的实现逻辑其实就是填充往########填充AA,所以只需记录A的下标即可,显示的时候直接根据下标填充显示

// 更新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;
}

更新部分是在定时器里更新的,填充两个A只需要记录左边一个A的下标即可右边一个A的下标就是index + 1

蓝桥杯物联网竞赛_STM32L071KBU6_第十五届蓝桥杯物联网竞赛国赛代码解析插图(11)

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      ");
}
}

显示部分由两个参数控制一个是OLEDSHOW这个参数是由按键控制其值,STATADC由B端发送的数据控制

蓝桥杯物联网竞赛_STM32L071KBU6_第十五届蓝桥杯物联网竞赛国赛代码解析插图(12)
串口发送的数据给A板,让A板对数据做检验或初步检验,减轻B板数据处理的负担,A板检验后的数据B板能直接用数据,B板处理后又能回复给A板

蓝桥杯物联网竞赛_STM32L071KBU6_第十五届蓝桥杯物联网竞赛国赛代码解析插图(13)
脉冲同步,比较麻烦因为B端要同步A端脉冲不一定能完美的同步因为32M / PSC / ARR不一定等于PWM

那就只能设计一个算法让他近似等于了

void Function_PwmChange(){  // 传过来的PWM不一定能整除,选择最接近的PWM
NEWPWM = (int)strtof((char* )LORXMSG, NULL);  // 获取要配置的新的PWM
MAINDRT = 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);
}

设计算法找到让同步的结果和预期最接近的PSC和ARR

3、总结:

总结来说这次比赛难点在于功能复杂,导致难设计,以及代码量的增加

本站无任何商业行为
个人在线分享 » 蓝桥杯物联网竞赛_STM32L071KBU6_第十五届蓝桥杯物联网竞赛国赛代码解析
E-->