蓝桥杯——入门

1 LED

点亮LED的步骤

1 PD2 输出高电平

2 然后再将PC8-13置为低电平(低电平有效)

3 PD2 输出低电平

如果不想让LED上电就亮,在初始化之前给PD2低电平就可以了

所有的代码都要写在BEGIN END之间,要不然修改CubMX的时候,代码都会消失

通过一个headfile.h文件所有的头文件包含都放在这个里面管理

由地址编码可以知道GPIO_PIN_8左移一位的话,就得到了GPIO_PIN_9

2 按键

按键扫描放到while循环中就可以了

同时,按键要支持长摁和段摁(需要一个定时器去配合)

思路:储存两个状态当摁下时候,state 变为0 如果last_state为1就说明刚开始按下,那么就要把定时器清零,如果state ==0 last_state ==0 说明是长摁(长摁需要设定时间,一般一秒或者两秒就可以)

当state == 1 last_state ==0 的时候,说明是按键松开了,这时候就要去判断定时器,如果小于1s(设定的值)那么就去执行相应的操作

组合按键

3 lcd

解决lcd与按键引脚冲突的问题

吧所有用到lcd的函数都这样写一下

Init 和clear 就要写一下,下面两个就不用了

lcd的高亮显示(能够去设置界面,有选中标志)

cs 复制代码
char text[20];
void lcd_display()
{
	sprintf(text,"       Test        ");
	LCD_DisplayStringLine(Line0,(uint8_t *)text);
	
	sprintf(text,"count:%d        ",count);
	LCD_DisplayStringLine(Line1,(uint8_t *)text);
	
	if(hight_light==0)
	{
		LCD_SetBackColor(Yellow);
		sprintf(text,"      parm1        ");
		LCD_DisplayStringLine(Line2,(uint8_t *)text);
		LCD_SetBackColor(Black);
		
		sprintf(text,"      parm2        ");
		LCD_DisplayStringLine(Line3,(uint8_t *)text);
	
		sprintf(text,"      parm3        ");
		LCD_DisplayStringLine(Line4,(uint8_t *)text);
	}
	else if(hight_light==1)
	{
		
		sprintf(text,"      parm1        ");
		LCD_DisplayStringLine(Line2,(uint8_t *)text);
		
		LCD_SetBackColor(Yellow);
		sprintf(text,"      parm2        ");
		LCD_DisplayStringLine(Line3,(uint8_t *)text);
		LCD_SetBackColor(Black);
		
		sprintf(text,"      parm3        ");
		LCD_DisplayStringLine(Line4,(uint8_t *)text);
	}
	else if(hight_light==2)
	{
		
		sprintf(text,"      parm1        ");
		LCD_DisplayStringLine(Line2,(uint8_t *)text);
		
		sprintf(text,"      parm2        ");
		LCD_DisplayStringLine(Line3,(uint8_t *)text);
		
		LCD_SetBackColor(Yellow);
		sprintf(text,"      parm3        ");
		LCD_DisplayStringLine(Line4,(uint8_t *)text);
		LCD_SetBackColor(Black);
	}

	led_show(1,led_mode);
}

4 定时器

使用定时器的时候,一定要去调用函数使能定时器,定时中断和PWM输出也是需要去使能的

pwm输出

输入捕获获得PWM周期T

这个capture_value 是通过捕获引脚检测到PWM的一个上升沿的时候发生中断开始计时,下一个中断到来的时候,两次中断的时间差就是capture_value

系统的t0=1/(系统频率/(PSC+1))

输入捕获是定时器的一种模式

我们捕获的时候,一般是要得到CNT 的值,在函数里面可知,将CCR的值返回了,这是因为在捕获到上升沿的时候,会将CNT赋值给CCR

所以可以不用去调用函数,直接获取CCR的值就可以了

cpp 复制代码
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM17)
	{
		capture_val = TIM17->CCR1;
		TIM17->CNT = 0;
		freq1 = 80000000/(80*(capture_val+1));
	}
	if(htim->Instance == TIM3)
	{
		capture_val = TIM3->CCR1;
		TIM3->CNT = 0;
		freq2 = 80000000/(80*(capture_val+1));
	}
	if(htim->Instance == TIM8)
	{
		capture_val = TIM8->CCR1;
		TIM8->CNT = 0;
		freq3 = 80000000/(80*(capture_val+1));
	}
}

5 adc测量

cpp 复制代码
double get_adcvalue(ADC_HandleTypeDef * hadc)
{
	HAL_ADC_Start(hadc);
	uint32_t adc_value = HAL_ADC_GetValue(hadc);
	return 3.3*adc_value/4096;
}

adc使用的时候还需要校准!!!!

6 串口

发送的时候加上\r\n就是换行的作用

利用定时器进行串口不定长数据接收

就是用一个计数器,在每一次接收到数据的时候,将计数器的CNT清零,在另外一个函数中判断,如果计数器>15的时候,就说明接收一次完成(这个函数放到whie循环中即可)

这样的处理比较符合赛题,如果自己写的话,不定长数据接收是要用帧头和帧尾来判断的

cpp 复制代码
uint8_t rxdata;
uint16_t count;
uint8_t rx_flag;
uint8_t rx_buff[20];
char send_buff[20];
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART1)
	{
		//HAL_UART_Transmit(&huart1,&rxdata,1,50);
		TIM4->CNT = 0;
		rx_flag = 1;
		rx_buff[count] = rxdata;
		count++;
		HAL_UART_Receive_IT(&huart1,&rxdata,1);
		
	}
}

void uart_data_rec()
{
	if(rx_flag)
	{
		if(TIM4->CNT>15)
		{
			if(rx_buff[0]=='l' && rx_buff[1]=='a' && rx_buff[2]=='n')
			{
				sprintf(send_buff,"lan\r\n");
				HAL_UART_Transmit(&huart1,(uint8_t *)send_buff,sizeof(send_buff),50);
			}
			else if(rx_buff[0]=='q' && rx_buff[1]=='i' && rx_buff[2]=='a' && rx_buff[3]=='o')
			{
				sprintf(send_buff,"qiao\r\n");
				HAL_UART_Transmit(&huart1,(uint8_t *)send_buff,sizeof(send_buff),50);
			}
			else if(rx_buff[0]=='b' && rx_buff[1]=='e' && rx_buff[2]=='i')
			{
				sprintf(send_buff,"bei\r\n");
				HAL_UART_Transmit(&huart1,(uint8_t *)send_buff,sizeof(send_buff),50);
			}
			else 
			{
				sprintf(send_buff,"Error!\r\n");
				HAL_UART_Transmit(&huart1,(uint8_t *)send_buff,sizeof(send_buff),50);
			}
			rx_flag = 0;
			for(int i = 0;i<count;i++)
			{
				rx_buff[i]=0;
			}
			count = 0;			
		}
	
	}
}

以上的代码基本上比赛时不能用,因为这个稳定性不高,如果while循环中还有打印,按键检测的话,这个串口接收的数据就会出问题,建议使用DMA串口接收发送

其实就是用DMA串口接收+printf重映射发送,因为DMA能够自动数出不定长数据的长度

找一下sscanf() 函数的使用方法(可以解析固定格式的一个字符串数据)

sscanf函数

C 库函数 -- sscanf() | 菜鸟教程

十二届省赛真题就可以这样用

strtok函数

atoi函数

7eeprom:非易失性存储器

很简单,就是包含iic文件之后用人家的,写两个函数就可以了

cpp 复制代码
void eeprom_write(uint8_t addr,uint8_t data)
{
	I2CStart();
	I2CSendByte(0xa0);//AT24c02的地址
	I2CWaitAck();
	I2CSendByte(addr);
	I2CWaitAck();
	I2CSendByte(data);
	I2CWaitAck();
	I2CStop();
	HAL_Delay(20);
}
uint8_t eeprom_read(uint8_t addr)
{
	I2CStart();
	I2CSendByte(0xa0);//AT24c02的地址
	I2CWaitAck();
	I2CSendByte(addr);
	I2CWaitAck();
	I2CStop();
	
	I2CStart();
	I2CSendByte(0xa1);//读取
	I2CWaitAck();
	uint8_t data = I2CReceiveByte();
	I2CSendNotAck();
	I2CStop();
	return data;
}

输入0xa0是写入,输入0xa1是读取,注意这个读取的时候的延时20ms是必不可少的

8 rtc实时时钟

配置界面

起始就是在MX上配置,打开中断(作为闹钟)

cpp 复制代码
uint8_t rtc_flag;
char text_time[20];
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
void rtc_lcd_show()
{
	HAL_RTC_GetTime(&hrtc,&sTime,RTC_FORMAT_BIN);
	HAL_RTC_GetDate(&hrtc,&sDate,RTC_FORMAT_BIN);
	sprintf(text_time,"      text       ");
	LCD_DisplayStringLine(Line0,(uint8_t *)text_time);
	sprintf(text_time,"    %2d-%2d-%2d       ",sTime.Hours,sTime.Minutes,sTime.Seconds);
	LCD_DisplayStringLine(Line1,(uint8_t *)text_time);
	sprintf(text_time,"    %d-%d-%d-%d       ",sDate.Year,sDate.Month,sDate.Date,sDate.WeekDay);
	LCD_DisplayStringLine(Line2,(uint8_t *)text_time);
	
	led_show(1,rtc_flag);
}

void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
	rtc_flag = 1;
}

9滴答定时器

cpp 复制代码
uint32_t my_tick;
void main_proc()
{
	if(uwTick-my_tick>=100)
	{
		HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_11);
		my_tick  = uwTick;
	}
	
}

这个函数的功能就是每1s反转一次LED4, 采取了滴答定时器的方法(不能用中断的方法,因为程序中如果用了lcd的话,中断打断lcd再去操作led会起冲突,用这样的方法就不会起冲突)

还有一种法方

通过it.c文件中的一个函数(这个函数中断1ms进入一次,所以就可以把自己代码中的东西写上去也可以)

10 屏幕反转代码

就是在lcd.c里面写上这个就可以了

cpp 复制代码
// 全局标志位:0表示正常显示,1表示翻转显示
uint8_t display_flip_flag = 0;
/**
 * @brief  LCD屏幕显示模式切换
 * @param  flip: 0=正常, 1=翻转(180度)
 */
void LCD_DisplayMode_Switch(uint8_t flip)
{
    if(flip)
    {
        // 配置为翻转模式 (垂直+水平翻转)
        LCD_WriteReg(R1,  0x0100); // 设置SS位,垂直翻转(自下而上)
        LCD_WriteReg(R96, 0xA700); // 设置GS位,水平翻转(自右而左)
    }
    else
    {
        LCD_WriteReg(R1,  0x0000);  // 官方默认
        LCD_WriteReg(R3,  0x1018);  // 官方默认!必须写这个值
        LCD_WriteReg(R96, 0x2700);  // 官方默认!必须写这个值
    }
    display_flip_flag = flip;
    
    // 翻转后建议清屏重绘,防止残留杂点
    LCD_Clear(Black);
}

比赛要用模板更快

相关推荐
网域小星球2 小时前
C 语言从 0 入门(二十一)|typedef 类型重定义:简化复杂类型,代码更清爽
c语言·算法·类型重定义·结构体简化·函数指针简化
weixin_446023562 小时前
c语言第一个编译器是用什么语言写的?自举原理
c语言·编译器·迭代优化·无代码开发平台·自举原理
脑神3 小时前
C/C++语言编译器
c语言
意法半导体STM323 小时前
【官方原创】STM32 USBx Host HID standardalone移植示例 LAT1449
开发语言·前端·stm32·单片机·嵌入式硬件
辰哥单片机设计3 小时前
STM32项目分享:空气质量检测系统(机智云)
stm32·单片机·嵌入式硬件
♛识尔如昼♛4 小时前
C 基础(9) - 数组和指针
c语言·指针·数组·多维数组
云栖梦泽4 小时前
Linux内核与驱动:12.设备树实例分析
linux·c++·单片机
一月千帆4 小时前
基于STM32的智能小型洗碗机控制系统设计
stm32·单片机·嵌入式硬件
计算机安禾4 小时前
【数据结构与算法】第45篇:跳跃表(Skip List)
c语言·数据结构·算法·list·排序算法·图论·visual studio