蓝桥杯嵌入式十三届国赛程序题刷题记录

先贴题目
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里分每个模块来记录刷题过程,有的地方很碎,因为毕竟是竞速比赛,写的时候一定要快,刷完也不想修结构了。

  • 先从LCD开始,CUBEMX设置就不讲了,这个没啥可说的
  • LCD有两部分,一部分是正常显示,一部分是倒转显示
  • 倒转显示详情看这个博客https://blog.csdn.net/qq_53960242/article/details/128868650
// 记得一定要在main函数里加上初始化
void main()
{
	'''
    LCD_Init();
    LCD_Clear(Black);
    LCD_SetBackColor(Black);
    LCD_SetTextColor(White);
    '''
}
//这里是要用到的变量,其实写成结构体好一些,但是写单个更方便随时更改
float data_pa4 = 0.0;
float data_pa5 = 0.0;
uint16_t data_pa1 = 0;
uint8_t para_x = 1;
uint8_t para_y = 2;
uint8_t rec_pa4_n = 0;
float rec_pa4_a = 0.0;
float rec_pa4_t = 0.0;
float rec_pa4_h = 0.0;
uint8_t rec_pa5_n = 0;
float rec_pa5_a = 0.0;
float rec_pa5_t = 0.0;
float rec_pa5_h = 0.0;
uint8_t rec_mode = 1;
void page(uint8_t page_select)
{
    char str2[20],str4[20],str5[20],str6[20],str7[20]; //每行最大长度20
    
    static uint8_t str2_len= 0; //这几个都是我用来做长度判定的
    static uint8_t str4_len = 0;//行的长度从长变短的时候触发一次clear
    static uint8_t str5_len = 0;
    static uint8_t str6_len = 0;
    static uint8_t str7_len = 0;
    
    if(page_select == 1)
    {
        sprintf(str2,"        DATA");
        sprintf(str4,"     PA4=%0.2f",data_pa4);
        sprintf(str5,"     PA5=%0.2f",data_pa5);
        sprintf(str6,"     PA1=%d",data_pa1);
        sprintf(str7,"");
    }else if(page_select == 2)
    {
        sprintf(str2,"        PARA");
        sprintf(str4,"     X=%d",para_x);
        sprintf(str5,"     Y=%d",para_y);
        sprintf(str6,"");
        sprintf(str7,"");
    }else if(page_select == 3)
    {
        if(rec_mode)
        {
            sprintf(str2,"        REC-PA4");
            sprintf(str4,"     N=%d",rec_pa4_n);
            sprintf(str5,"     A=%0.2f",rec_pa4_a);
            sprintf(str6,"     T=%0.2f",rec_pa4_t);
            sprintf(str7,"     H=%0.2f",rec_pa4_h);
        }
        else
        {
            sprintf(str2,"        REC-PA5");
            sprintf(str4,"     N=%d",rec_pa5_n);
            sprintf(str5,"     A=%0.2f",rec_pa5_a);
            sprintf(str6,"     T=%0.2f",rec_pa5_t);
            sprintf(str7,"     H=%0.2f",rec_pa5_h);
        }
    }
    if(str2_len>strlen(str2) || str4_len>strlen(str4) ||str5_len>strlen(str5) ||str6_len>strlen(str6) ||str7_len>strlen(str7))
        LCD_Clear(Black);
    LCD_DisplayStringLine(Line1,(uint8_t*)str2);
    LCD_DisplayStringLine(Line3,(uint8_t*)str4);
    LCD_DisplayStringLine(Line4,(uint8_t*)str5);
    LCD_DisplayStringLine(Line5,(uint8_t*)str6);
    LCD_DisplayStringLine(Line6,(uint8_t*)str7);
    str2_len = strlen(str2);
    str4_len = strlen(str4);
    str5_len = strlen(str5);
    str6_len = strlen(str6);
    str7_len = strlen(str7);
}
uint8_t display_mode = 0;
void setDisplayMode(uint8_t mode)
{
    if(mode) //倒转显示
    {
        LCD_WriteReg(R1,0x0100);//从下往上显示
        LCD_WriteReg(R96,0xA700);//从右往左显示
        LCD_Clear(Black);
    }else //正常显示
    {
        LCD_WriteReg(R1,0x0000);//从上往下显示
        LCD_WriteReg(R96,0x2700);//从左往右显示
        LCD_Clear(Black);
    }
}
  • 按键KEY,CUBEMX设置也没啥好说的
  • 这个函数还是很简单的,不过因为要区分长短按键还要互不影响,所以写了松手检测的死循环while,当然这样写有一个问题就是按键不松手的时候程序无法往后执行,按键之后的执行代码写在主函数里了
#define KEY1    HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)
#define KEY2    HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)
#define KEY3    HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)
#define KEY4    HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)
#define KEY1_PRE    1
#define KEY2_PRE    2
#define KEY3_PRE    3
#define KEY4_PRE    4
uint8_t key4_long_flag = 0;
uint8_t key_scan()
{
    uint8_t key_long = 0;
    if((!KEY1 || !KEY2 || !KEY3 || !KEY4 ))
    {
        HAL_Delay(10);
        if(!KEY1)
        {
            HAL_Delay(10);
            while(!KEY1);
            key4_long_flag = 0;
            return KEY1_PRE;
        }
        if(!KEY2)
        {
            HAL_Delay(10);
            while(!KEY2);
            key4_long_flag = 0;
            return KEY2_PRE;
        }
        if(!KEY3)
        {
            HAL_Delay(10);
            while(!KEY3);
            key4_long_flag = 0;
            return KEY3_PRE;
        }
        if(!KEY4)
        {
            HAL_Delay(10);
            key4_long_flag = 0;
            while(!KEY4)
            {
                HAL_Delay(10);
                key_long++;
                if(key_long >= 100)
                {
                    key4_long_flag = 1;
                }
            }
            return KEY4_PRE;
        }
    }
    return 0;
}
  • PA4和PA5的数据存储和最大最小值平均值
  • 这部分写的不忍直视,没写结构体太细碎了,这部分代码就是纯体力活
float rec_pa4_note[100];
uint8_t pa4_note_index = 0;
float rec_pa5_note[100];
uint8_t pa5_note_index = 0;
float rec_pa4_h_all = 0.0;
float rec_pa5_h_all = 0.0;
void rec_note_update()
{

    rec_pa4_note[pa4_note_index++] = data_pa4;
    rec_pa5_note[pa5_note_index++] = data_pa5;
    if(rec_pa4_a < data_pa4)
        rec_pa4_a = data_pa4;
    if(pa4_note_index > 1)
    {
        if(rec_pa4_t > data_pa4)
            rec_pa4_t = data_pa4;
    }else if(pa4_note_index == 1)
        rec_pa4_t = data_pa4;
    
    if(rec_pa5_a < data_pa5)
        rec_pa5_a = data_pa5;
    if(pa5_note_index > 1)
    {
        if(rec_pa5_t > data_pa5)
            rec_pa5_t = data_pa5;
    }else if(pa5_note_index == 1)
        rec_pa5_t = data_pa5;
    rec_pa4_h_all += data_pa4;
    rec_pa5_h_all += data_pa5;
    rec_pa4_h = rec_pa4_h_all / pa4_note_index;
    rec_pa5_h = rec_pa5_h_all / pa5_note_index;
}
  • 接下来是输入捕获的部分,TIM2 CHANNLE2
  • 这个贴一下CUBEMX,调下时钟源,定时器模式,这里PSC 80-1是因为我用的80Mhz时钟源,这样好算,ARR 10000-1看个人设置,实际上调多少都没啥影响,默认使用上升沿

在这里插入图片描述

//主函数里加上初始化
    HAL_TIM_Base_Start_IT(&htim2);
    HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_2);
uint8_t timer_up = 0; //检测上升沿
uint16_t timer_count = 0; //用于计算两个上升沿之间的计数值
uint8_t timer_over = 0; //计数期间计数器是否溢出
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2)
    {
        timer_up++;//检测到上升沿
        if(timer_up == 1)//第一个上升沿
        {
            __HAL_TIM_DISABLE(htim);
            __HAL_TIM_SetCounter(htim,0);
            timer_count = 0;
            __HAL_TIM_DISABLE(htim);
        }else if(timer_up == 2)//第二个上升沿,要计算值
        {
            __HAL_TIM_DISABLE(htim);
            /* 计算两个上升沿之间的计数 */
            timer_count = 10000*timer_over + HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_2);
            /* 因为PSC 80-1,TIM时钟80Mhz;所以一秒钟1000000的计数*/
            /* 所以除一下就是频率 */
            data_pa1 = 1000000 / timer_count;
            if(data_pa1 < 100) //题目要求测量范围100hz-10khz
                data_pa1 = 100;
            else if(data_pa1 > 10000)
                data_pa1 = 10000;
            timer_up = 0;
            timer_over = 0;
            __HAL_TIM_DISABLE(htim);
        }
    }
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2)
    {
        timer_over++;
    }
}
  • PWM输出 TIM3 CHANNEL2
  • CUBEMX就改个PSC,ARR和CPR在程序里面赋值
    在这里插入图片描述
//主函数里先初始化,这里pwm_fre的定义在外面,输出的是方波,所以compare就设置了/2
    HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);
    __HAL_TIM_SetAutoreload(&htim3,pwm_fre);
    __HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_2,pwm_fre/2);
/*这里用6000只是倍频系数1-4,就选了个能被1-4整除的数,题目没有要求初始频率*/
uint16_t pwm_fre = 6000; 
uint8_t pwm_mode = 1;//1是倍频模式,0是分频模式
void pwm_mode_set()
{
    if(pwm_mode)
    {
        HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_2);//关闭输出
        __HAL_TIM_SetCounter(&htim3,0);//计数器清零,不清零如果溢出会出错
        pwm_fre = 6000 / para_x; //除倍频系数做倍频
        __HAL_TIM_SetAutoreload(&htim3,pwm_fre);
        __HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_2,pwm_fre/2);
        HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);//重新开启输出
    }else
    {
        HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_2);
        __HAL_TIM_SetCounter(&htim3,0);
        pwm_fre = 6000 * para_x;
        __HAL_TIM_SetAutoreload(&htim3,pwm_fre);
        __HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_2,pwm_fre/2);
        HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);
    }
}
  • ADC 双路 ADC_IN_13 ADC_IN_17
  • CUBEMX选一下输入模式,把转换通道数改成2就行
    在这里插入图片描述
// main函数里先启动ADC自校准
HAL_ADCEx_Calibration_Start(&hadc2,ADC_SINGLE_ENDED);
void adc_get()
{
    data_pa4 = 0;//转换之前先把之前的值置零
    data_pa5 = 0;
    for(uint8_t n = 0; n < 5; n++) //这里取5次adc值做平均值
    {
        HAL_ADC_Start(&hadc2);//启动adc转换
        HAL_ADC_PollForConversion (&hadc2,50);//等待转换完成,50ms是最大超时时间
        data_pa4 += 3.3 * ((float)HAL_ADC_GetValue(&hadc2) / 4096);//得到adc值并转换成电压
        /* 连续模式第二次读的就是第二个通道 */
        HAL_ADC_Start(&hadc2);
        HAL_ADC_PollForConversion (&hadc2,50);
        data_pa5 += 3.3 * ((float)HAL_ADC_GetValue(&hadc2) / 4096);
    }
    data_pa4 = data_pa4 / 5;
    data_pa5 = data_pa5 / 5;
}
  • LED
  • 这个代码挺简单的,没啥可注意的,因为翻转led3不太好弄,毕竟和lcd共用IO,所以干脆加两个变量直接写了100ms切换亮灭的闪烁方法
uint8_t led_100ms = 0;
uint8_t led_100ms_ok = 0;
void led()
{
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
    if(pwm_mode)
    {
        HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_RESET);
        HAL_GPIO_WritePin(GPIOC,GPIO_PIN_9,GPIO_PIN_SET);
    }else
    {
        HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC,GPIO_PIN_9,GPIO_PIN_RESET);
    }
    if(data_pa4 > data_pa5)
    {
        if(led_100ms_ok)
            HAL_GPIO_WritePin(GPIOC,GPIO_PIN_10,GPIO_PIN_RESET);
        else
            HAL_GPIO_WritePin(GPIOC,GPIO_PIN_10,GPIO_PIN_SET);
    }else
        HAL_GPIO_WritePin(GPIOC,GPIO_PIN_10,GPIO_PIN_SET);
    if(!display_mode)
        HAL_GPIO_WritePin(GPIOC,GPIO_PIN_11,GPIO_PIN_RESET);
    else
        HAL_GPIO_WritePin(GPIOC,GPIO_PIN_11,GPIO_PIN_SET);
    
    HAL_GPIO_WritePin(GPIOC,GPIO_PIN_12,GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_SET);
    
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
void SysTick_Handler(void)//
{
  /* USER CODE BEGIN SysTick_IRQn 0 */
    extern uint8_t led_100ms;
    extern uint8_t led_100ms_ok;
  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  /* USER CODE BEGIN SysTick_IRQn 1 */
    led_100ms++;
    if(led_100ms > 100)
    {
        led_100ms = 0;
        led_100ms_ok = !led_100ms_ok;
    }
  /* USER CODE END SysTick_IRQn 1 */
}
  • 串口
  • CUBEMX跳过,因为接收的是不定长数据,所以用串口空闲中断,对于判定题目中发送指定字符输出内容,直接用strstr做字符搜索
//在main函数里使能串口空闲中断
 HAL_UARTEx_ReceiveToIdle_IT(&huart1,g_uart_rx,128);
/* 串口重定向 */
int fputc(int ch,FILE *f)
{
    HAL_UART_Transmit(&huart1,(uint8_t*)&ch,1,HAL_MAX_DELAY);
    return ch;
}
uint8_t g_uart_rx[128];
/* 写空闲中断处理函数 */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
    if(huart->Instance == USART1)
    {
        if(strlen((const char*)g_uart_rx) == 1)
        {
            if(strstr((const char*)g_uart_rx,"X") != NULL)
            {
                printf("X:%d\r\n",para_x);
            }else if(strstr((const char*)g_uart_rx,"Y") != NULL)
            {
                printf("Y:%d\r\n",para_y);
            }else if(strstr((const char*)g_uart_rx,"#") != NULL)
            {
                display_mode = !display_mode;
                setDisplayMode(display_mode);
            }
        }else if(strlen((const char*)g_uart_rx) == 3)
        {
            if(strstr((const char*)g_uart_rx,"PA1") != NULL)
            {
                printf("PA1:%d\r\n",data_pa1);
            }else if(strstr((const char*)g_uart_rx,"PA4") != NULL)
            {
                printf("PA4:%0.1f\r\n",data_pa4);
            }else if(strstr((const char*)g_uart_rx,"PA5") != NULL)
            {
                printf("PA5:%0.1f\r\n",data_pa5);
            }
        }
        memset(g_uart_rx,0,128);
        HAL_UARTEx_ReceiveToIdle_IT(&huart1,g_uart_rx,128);
    }
}

最后贴一下主函数

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_ADC2_Init();
  MX_TIM2_Init();
  MX_TIM3_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
    LCD_Init();
    LCD_Clear(Black);
    LCD_SetBackColor(Black);
    LCD_SetTextColor(White);
    
    HAL_TIM_Base_Start_IT(&htim2);
    HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_2);
    
    HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);
    __HAL_TIM_SetAutoreload(&htim3,pwm_fre);
    __HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_2,pwm_fre/2);
    
    HAL_ADCEx_Calibration_Start(&hadc2,ADC_SINGLE_ENDED);
    
    HAL_UARTEx_ReceiveToIdle_IT(&huart1,g_uart_rx,128);

    uint8_t key = 0;
    uint8_t page_lect = 1;
    
    I2CInit();
     uint8_t i2c_mem_flag = eeprom_read(0x04);
    if(i2c_mem_flag != 0x09)
    {
        eeprom_write(0x00,para_y);
        HAL_Delay(10);
        eeprom_write(0x01,para_x);
        HAL_Delay(10);
        eeprom_write(0x04,0x09);
        HAL_Delay(10);
    }else
    {
        para_y = eeprom_read(0x00);
        HAL_Delay(10);
        para_x = eeprom_read(0x01);
        HAL_Delay(10);
    }

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    key = key_scan();
    switch(key)
    {
        case KEY1_PRE:page_lect++;if(page_lect == 4)page_lect=1;rec_mode = 1;break;
        case KEY2_PRE:
            if(page_lect == 2)
            {
                para_x++;
                if(para_x == 5)
                    para_x = 1;
                eeprom_write(0x01,para_x);
                HAL_Delay(10);
                eeprom_write(0x04,9);
                HAL_Delay(10);
            }
            pwm_mode_set();
            break;
        case KEY3_PRE:
            if(page_lect == 2)
            {
                para_y++;
                if(para_y == 5)
                    para_y = 1;
                eeprom_write(0x00,para_y);
                HAL_Delay(10);
                eeprom_write(0x04,9);
                HAL_Delay(10);
            }
            break;
        case KEY4_PRE:
            if(page_lect == 1)
            {
                adc_get();
                rec_pa4_n++;
                rec_pa5_n++;
                rec_note_update();
            }else if(page_lect == 2)
            {
                pwm_mode = !pwm_mode;
                pwm_mode_set();
            }else if(page_lect == 3)
            {
                if(key4_long_flag == 1)
                {
                    if(rec_mode)
                    {
                        rec_pa4_n = 0;
                        pa4_note_index = 0;
                        rec_pa4_a = 0.0;
                        rec_pa4_t = 0.0;
                        rec_pa4_h_all = 0.0;
                        rec_pa4_h = 0.0;
                    }else
                    {
                        rec_pa5_n = 0;
                        pa5_note_index = 0;
                        rec_pa5_a = 0.0;
                        rec_pa5_t = 0.0;
                        rec_pa5_h_all = 0.0;
                        rec_pa5_h = 0.0;
                    }
                }else
                {
                    rec_mode = !rec_mode;
                }
            }
            break;
    }
    
    led();
    page(page_lect);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

这比赛实在是磨人,连续写几小时感觉人都要被掏空了 ,目标搞个国二就行,完整代码上传了,应该可以从我主页看到吧,写的不好轻喷

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值