十一届蓝桥杯物联网程序题


题目分析

比赛题目

原题参考蓝桥杯大赛—十一届蓝桥杯物联网

硬件分析

蓝桥杯物联网基于STM32L071KBU微控制器和LoRa收发器。LoRa终端集成了CMSIS DPA Link 编程调试工具,与目标微控制器STM32L071KBU连接。调试器内部实现了USB转串口功能,与目标控制器的USART2连接。使用虚拟串口时,需要短接P5的1,2脚和3,4脚。蓝桥杯物联网的套装产品,其外设有以下几个:

  1. OLED显示屏
引脚 功能
PA8 I2C3_SCL
PB4 I2C3_SDA
PB5 电源控制引脚
  1. 继电器
引脚 功能
PA0 RELAY1
PA1 RELAY2
  1. LoRa模块
引脚 功能
PA10 LoRa数据收,发中断输出
PA9 R复位,低电平有效
PA4 SPI片选信号
PA5 SPI时钟信号
PA6 SPI主器件输入从器件输出信号
PA7 SPI主器件输出从器件输入信号
  1. 通用接口 :矩阵键盘,模拟电压输出模块,温度传感器模块
引脚 功能
PB0 GPIO/EXTI/ADC/TIM3_CH3
PB1 GPIO/EXTI/ADC/TIM3_CH4
PB4 GPIO/EXTI/TIM2_CH2/I2C3_SDA
PB6 GPIO/EXTI/I2C1_SCL/USART1_TX
PB7 GPIO/EXTI/I2C1_SDA/USART1_RX
PB8 GPIO/EXTI

硬件配置

  1. 在LoRa终端A上配置温度传感器
  2. 在LoRa终端B上配置键盘模块

软件配置

LoRa终端A

STM32CubeMx 配置
  1. Clock选择PLLCLK,HCLK为24MHz;
  2. Connectivity配置I2C1,I2C3,SPI1,USART2,引脚配置如上硬件分析;
  3. 继电器PA0,PA1选择GPIO_Output;
  4. 配置SPI的片选信号PA4为GPIO_Output,OLED电源控制引脚PB5配置为GPIO_Output;
  5. PC14配置为GPIO_Input,模式为上拉。
按键检测函数

读取按键状态,将当前按键状态与上一次按键状态进行异或,如果为1,按键状态发生改变,再与上上一次按键状态,如果为1,表示按键松开,上一次按键状态为按下。

uint8_t UserKey(void)
{
  static uint8_t key_o = 0x00, key_n = 0x00;
  uint8_t x = 0;
  if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_14) == 0)
   key_n = 0x01;
  else 
   key_n = 0x00;
  x = (key_n ^ key_o) & key_o;
  key_o = key_n;
  return x;
}

当串口接收到数据时产生中断,将接收的数据存入RxBuffer数组中。

串口中断函数
void USART2_IRQHandler(void)
{
  static uint16_t ch = 0;
  if((__HAL_UART_GET_FLAG(&huart2,UART_FLAG_RXNE)!=RESET))
  {
     ch=( uint16_t)READ_REG(huart2.Instance->RDR);//接收字符
     RxBuffer[CountRx++] = ch;
  }
}
串口数据检测函数

当串口接收五个字节后,进行指令判断。

void Check(uint8_t * cmd , uint8_t *dataMax , uint8_t *dataMin)
{
   if(cmd[0] == 'M' && cmd[1] == 'A' && cmd[2] == 'X')
   {
     HAL_UART_Transmit(&huart2,(uint8_t *)"ok",2,100);
     *dataMax = ((cmd[3]-0x30)*10)+(cmd[4]-0x30);
     sprintf(g_lcdLine_2st_line, "Tmax:%.0F      ",(float) *dataMax);
   }
   else if(cmd[0] == 'M' && cmd[1] == 'I' && cmd[2] == 'N')
   {
     HAL_UART_Transmit(&huart2,(uint8_t *)"ok",2,100);
     *dataMin = ((cmd[3]-0x30)*10)+(cmd[4]-0x30);
     sprintf(g_lcdLine_3st_line, "Tmin:%.0F      ",(float) *dataMin);
   }
   else
    HAL_UART_Transmit(&huart2,(uint8_t *)"error",5,100);
}
滴答定时器

每隔20ms检测一次按键并判断OLED显示状态;每隔100msSensor_Flag置1,200msg_Radio_Recv_Flag置1。

  static uint16_t temp_num2 = 0;
  static uint16_t temp_num3 = 0;
  static uint16_t temp_num4 = 0;
  temp_num2++;
  temp_num3++;
  temp_num4++;
  if(temp_num2 >= 20)
  {
   temp_num2 = 0;
   if(UserKey())      //判断User是否按下
   switch(key)        //判断OLED显示状态
   {
     case WenDu: key = Shangxian; break;
     case Shangxian: key = WenDu; break;
    }
  }
  if(temp_num3 >= 100) 
  {
    temp_num3 = 0;
    Sensor_Flag = 1; //温度读取以及更新标志
  }
  if(temp_num4 >= 200)
  {
    temp_num4 = 0;
    g_Radio_Recv_Flag = 1;  //LoRa接收终端B数据标志
  }
继电器控制函数

根据温度是上下限,控制两个继电器不同状态。

void tempControl(char temp ,uint8_t dataMax , uint8_t dataMin)
{
   if(temp>dataMax)
   {
      HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);
      HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); 
   }
   else if(temp<dataMin)
   {
     HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
     HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); 
   }
   else 
  {
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); 
  }
}
main.c文件while(1)程序

Task_Recv函数是对终端B接收的数据并执行相关的数据发送。

 if(CountRx>=5)
   {
     CountRx = 0;
     Check(RxBuffer,&max,&min);
     RxBuffer[0] = RxBuffer[1] = RxBuffer[2] = RxBuffer[3] = RxBuffer[4] = 0;
   }
   tempControl(temper,max,min);
   switch(key)
   {
    case Shangxian: 
     OLED_ShowString(0, 0, (uint8_t *)g_lcdLine_2st_line, 16);
     OLED_ShowString(0, 2, (uint8_t *)g_lcdLine_3st_line, 16);
    break;
    case WenDu: 
     OLED_ShowString(0, 0, (unsigned char *)"Temperature", 16);
     OLED_ShowString(0, 2, (uint8_t *)g_lcdLine_1st_line, 16);
    break; 
     }
   if(Sensor_Flag)
   {
     Sensor_Flag = 0;
     temper = Get_Temperature();
     sprintf(g_lcdLine_1st_line, "   %.1FC  ", temper);
   }
   if (g_Radio_Recv_Flag == 1)
    {
        g_Radio_Recv_Flag = 0;
        Task_Recv(handle);
    }
  }

LoRa终端B

通过赛点提供的包对终端B的程旭进行修改。

按键任务

判断哪个按键按下执行相关程序

#include "key_task.h"

extern radio_handle_t  Radio_handle;
extern char g_lcdLine_1st_line[16];
extern char g_lcdLine_2st_line[16];
extern uint8_t g_Key_Value;

uint8_t Cmd_B1[5] = {0x0A, 0x0B, 0xB1, 0xEE, 0xEF};
uint8_t Cmd_B2[5] = {0x0A, 0x0B, 0xB2, 0xEE, 0xEF};
uint8_t Cmd_B3[5] = {0x0A, 0x0B, 0xB3, 0xEE, 0xEF};
uint8_t Cmd_B4[5] = {0x0A, 0x0B, 0xB4, 0xEE, 0xEF};

uint8_t Radio_State = 0;

void TaskKey_init(void)
{
    Keyboard_Init();
}

void Task_Key(void)
{
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_SET);
    switch (g_Key_Value)
    {
    case B1:
      radio_buf_send(Radio_handle, Cmd_B1,5);   //发送读取温度cmd
      radio_mode_set(Radio_handle, RX_MODE);//进入接收模式
      HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_RESET);
      g_Key_Value = 0;
    break;
    case B2:
      radio_buf_send(Radio_handle, Cmd_B2,5);  //发送读取温度上下限cmd
      radio_mode_set(Radio_handle, RX_MODE);//进入接收模式
      g_Key_Value = 0;
    break;
    case B4:
      radio_buf_send(Radio_handle, Cmd_B3,5);  //发送读取继电器K1状态cmd
      radio_mode_set(Radio_handle, RX_MODE);//进入接收模式
      g_Key_Value = 0;
    break;
    case B5:
      radio_buf_send(Radio_handle, Cmd_B4,5); //发送读取继电器K2状态cmd
      radio_mode_set(Radio_handle, RX_MODE);//进入接收模式
      g_Key_Value = 0;
     break;
    default :
        ;
        break;
    }

}
数据接收任务

终端A发送数据时,终端B产生中断并在中断回调函数中接收数据并判断终端A发送的数据。Compare_Cmd函数是将数据存入oled显示缓存数据。

uint8_t Compare_Cmd(uint8_t *recv_cmd_buf, uint8_t recv_buf_num)
{
    uint8_t cmd_Num = 0;
    float temper = 0;
       switch (recv_cmd_buf[2])
       {
          case 0xA1 : 
           temper = (float)(((float)(recv_cmd_buf[5]*10+recv_cmd_buf[6]))/(float)10);
           sprintf(g_lcdLine_1st_line, "Temperature ");
           sprintf(g_lcdLine_2st_line, "   %.1FC    ",(float)temper);
          break;
          case 0xA2 :  
            sprintf(g_lcdLine_1st_line, "Tmax:%.0f    ",(float)recv_cmd_buf[5]);
            sprintf(g_lcdLine_2st_line, "Tmin:%.0f  ",(float)recv_cmd_buf[6]);
          break;
          case 0xA3 : 
            sprintf(g_lcdLine_1st_line, "K1:Status    ");
            sprintf(g_lcdLine_2st_line, "    %.0f   ",(float)recv_cmd_buf[5]);
          break;
          case 0xA4 : 
            sprintf(g_lcdLine_1st_line, "K2:Status     ");
            sprintf(g_lcdLine_2st_line, "    %.0f    ",(float)recv_cmd_buf[5]);
          break;
          default : cmd_Num = 0; break;
    }

    return cmd_Num;
}
主函数

每隔一段时间对OLED进行更新。

Task_Key();

    if (g_OLED_Flag == 1)
    {
        g_OLED_Flag = 0;
        OLED_ShowString(0, 0, (uint8_t *)g_lcdLine_1st_line, 16);
        OLED_ShowString(0, 2, (uint8_t *)g_lcdLine_2st_line, 16);
    }

完整代码

十一届蓝桥杯物联网完整代码


文章作者: 潘苏皖
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 潘苏皖 !
评论
 本篇
十一届蓝桥杯物联网程序题 十一届蓝桥杯物联网程序题
题目分析比赛题目原题参考蓝桥杯大赛—十一届蓝桥杯物联网 硬件分析蓝桥杯物联网基于STM32L071KBU微控制器和LoRa收发器。LoRa终端集成了CMSIS DPA Link 编程调试工具,与目标微控制器STM32L071KBU连接。调试
2021-04-09
下一篇 
基于NUCLEO-WB55开发板对代码进行解析以及实现 基于NUCLEO-WB55开发板对代码进行解析以及实现
实验目的本文利用STM32CubeMx初始化配置STM32WB55,使用自己生成的顾客服务与特性实现蓝牙之间的读写通知,通过写控制LED2亮灭,读取LED1的状态,通知LED3(300ms闪烁)来实现本实验的目的。 实验环境 STM32Cu
2020-12-27
  目录