先贴题目
这里分每个模块来记录刷题过程,有的地方很碎,因为毕竟是竞速比赛,写的时候一定要快,刷完也不想修结构了。
- 先从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 */
}
这比赛实在是磨人,连续写几小时感觉人都要被掏空了 ,目标搞个国二就行,完整代码上传了,应该可以从我主页看到吧,写的不好轻喷