LED 开发板上的电路图 1
RCC和SYS的作用
RCC:模块负责管理单片机的时钟系统,控制外部和内部时钟源的选择,频率调节,时钟使能 SYS:SYS模块主要负责管理单片机的系统时钟,包括电源管理和复位控制等功能。
用到的HAL库函数
我在点亮一个流水灯的时候出现了一个问题,我给GPIOC8写低电平的时候,LED亮了四个灯
1 2 3 4 5 6 7 8 9 10 11 void HAL_GPIO_WritePin (GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState) ;void HAL_GPIO_TogglePin (GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) ;void HAL_Delay (uint32_t Delay) ;
位运算 &:两个都为1的时候,结果为1 |:有一个为1,结果为1 ^:两个二进制位不同时,结果位为 1,否则为 0。 ~:所有二进制位取反 <<:所有二进制位左移 n 位,低位补 0。 1>>:所有二进制位右移 n 位,高位补符号位(算术右移)或 0(逻辑右移)。
1 2 3 4 uint_32t ucled=0x02 ; ucled<<8 ;
关于为什么进行位运算向左移动八位就能实现点亮对应LED如图
位运算向左实现流水灯:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void Led_Disp (uint8_t ucLed) { HAL_GPIO_WritePin (GPIOC,0XFF <<8 ,GPIO_PIN_SET) HAL_GPIO_WritePin (GPIOC,ucLed<<8 ,GPIO_PIN_RESET) } uint8_t ucLed=0x01 ;LED_Disp (ucLed);HAL_Delay (100 );ucLed<<=1 ; IF (ucLed==0 ){ucLed=0x01 }
关于这个代码中if判断为什么当ucled=0x00重置我出现了一个问题 我在定义ucled的类型的时候,定义- uint32所以就会导致0x80向右移过程不会变成0x00
常用函数解释 led_flag ^= 1;实现反转
1 2 3 4 5 6 7 8 9 10 11 12 13 uint8_t led_flag;uint8_t B1_data,B2_data,B3_data,B4_data,B1_last,B2_last,B3_last,B4_last;void key_Proc (void ) { B1_data=HAL_GPIO_ReadPin (GPIOB,GPIO_PIN_0);B2_data=HAL_GPIO_ReadPin (GPIOB,GPIO_PIN_1); B3_data=HAL_GPIO_ReadPin (GPIOB,GPIO_PIN_2);B4_data=HAL_GPIO_ReadPin (GPIOA,GPIO_PIN_0); if (!B1_data&B1_last) { LED_show (1 ,1 ); } B1_last=B1_data;B2_last=B2_data;B3_last=B3_data;B4_last=B4_data; }
当按键一直处于高电平的时候,B1_data和B1_last就一直是1,去反后出现一个0所以在进行&运算后,仍然是0,所以就不会进入if判断 而当高电平转换成低电平的时候,B1_data变成了0,就会出现&运算后总体为1,然后进入if判断
系统滴答定时器(SysTick) 1 2 3 4 5 6 7 8 9 10 void SysTick_Handler (void ) {HAL_IncTick ();usled++; }
这是一个嵌入式中断服务函数,在主函数调用HAl_Init()时,会自动配置SysTick定时器
关于如何解决LED引脚占用问题 观察LCD引脚图可以发现,LED和LCD共用一组引脚,所以我们需要将LED的引脚配置为输出模式,而LCD的引脚配置为输入模式。
所以就有了用到LCD时,我们需要把PD2初始状态修改为低电平,使锁存器两端不导通。需要配置LED时,将锁存器 设置为高电平,将PC8PC15端口状态传送到左侧1Q8Q,再配置为低电平
模块代码:
详细见真题总结
1 2 3 4 5 6 7 8 9 10 11 void display (uint8_t ucLed) { HAL_GPIO_WritePin (GPIOC,0xFF <<8 ,GPIO_PIN_SET); HAL_GPIO_WritePin (GPIOD,GPIO_PIN_2,GPIO_PIN_SET); HAL_GPIO_WritePin (GPIOD,GPIO_PIN_2,GPIO_PIN_RESET); HAL_GPIO_WritePin (GPIOC,ucled<<8 ,GPIO_PIN_SET); HAL_GPIO_WritePin (GPIOD,GPIO_PIN_2,GPIO_PIN_RESET); HAL_GPIO_WritePin (GPIOD,GPIO_PIN_2,GPIO_PIN_SET); }
这代码不能解决LED,LCD引脚占用的问题
按键 KEY按键电路图
1 2 3 4 5 6 7 8 GPIO_PinState HAL_GPIO_ReadPin (GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) ;
当按键按下的时候返回的是低电平,松开的时候返回的是高电平
按键模块代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 uint8_t Key_Scan (void ) {uint8_t key_val=0 ;if (HAL_GPIO_ReadPin (GPIOB,GPIO_PIN_0)==GPIO_PIN_RESET){ key_val=1 ; } f (HAL_GPIO_ReadPin (GPIOB,GPIO_PIN_1)==GPIO_PIN_RESET){ key_val=2 ; } f (HAL_GPIO_ReadPin (GPIOB,GPIO_PIN_2)==GPIO_PIN_RESET){ key_val=3 ; } f (HAL_GPIO_ReadPin (GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET){ key_val=4 ; } eturn key_val; }
按键状态判断:
1 2 3 4 5 !B4_data & B4_last !B4_data & !B4_last B4_data & !B4_last
关于输入输出上拉和下拉
上拉:默认电平为高电平 下拉:默认电平低电平 浮空:不连接,悬空状态
按键三行代码 按键的三种状态
1 2 3 4 5 6 7 8 9 10 11 12 13 void Key_Proc (void ) {key_val = Key_Scan (); key_down = key_val & (key_val ^ key_old); key_up = ~key_val & (key_val ^ key_old); key_old = key_val; if (key_down==1 ){ } }
key_val ^ key_old 会计算当前按键状态与上一状态的异或结果
按键的长按和短按
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void key_longandshort (void ) { uint8_t key_val=key_ReadPin (); uint8_t key_down=key_val&(key_val^ key_old); uint8_t key_up=~key_val&(key_val^key_old); key_old=key_val; if (key_down) { usled=0 ; } if (key_up) { if (usled<1000 ) { LEDdisplay (0x01 ); HAL_Delay (2000 ); HAL_GPIO_WritePin (GPIOC,0XFF <<8 ,GPIO_PIN_SET); } else { LEDdisplay (0x02 ); HAL_Delay (2000 ); HAL_GPIO_WritePin (GPIOC,0XFF <<8 ,GPIO_PIN_SET); } } }
关于位运算^: 如果两个对应的位相同,则该位的结果为0;如果不同,结果为1。
LCD屏幕 sprintf函数可以将其他字符传化为字符串
LCD函数大全
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void LCD_Init (void ) ;void LCD_SetTextColor (vu16 Color) ;void LCD_SetBackColor (vu16 Color) ;void LCD_DisplayStringLine (uint16_t Line, uint8_t *ptr) ;
关于按键的双击
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 void key_doubleandonce (void ) { uint8_t key_val=key_ReadPin (); uint8_t key_down=key_val&(key_val^key_old); uint8_t key_up=~key_val&(key_val^key_old); uint8_t key_temp; uint8_t key_flag=0 ; if (key_up) { key_temp=key_up; if (key_flag==0 ) { usled2=0 ; key_flag=1 ; } else { key_flag=0 ; } } if (key_flag==1 ) { if (usled2<300 ) { if (key_down==1 &&key_flag==1 ) { LEDdisplay (0x02 ); } if (key_down==2 &&key_flag==2 ) { LEDdisplay (0x02 ); } if (key_down==3 &&key_flag==3 ) { LEDdisplay (0x02 ); } if (key_down==4 &&key_flag==4 ) { LEDdisplay (0x02 ); } } else { if (key_temp==1 ) { usled2=0x01 ; } if (key_temp==2 ) { } if (key_temp==2 ) { } if (key_temp==2 ) { } } } }
LCD
1 2 3 4 5 6 7 8 9 HAL_StatusTypeDef HAL_UART_Transmit (UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout) 功能:串口发送数据 参数: UART_HandleTypeDef *huart UART句柄 huart1 uint8_t *pData 需要发送的数据uint16_t Size 发送的字节数uint32_t Timeout 最大发送时间,发送数据超时退出发送
串口接受函数:
1 2 3 4 5 6 7 HAL_StatusTypeDef HAL_UART_Receive_IT (UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
meset函数: 用于初始化一块内存,将数组全部清零,或者设置为某个特定值 例如:
1 2 3 memset (arr, 'A' , sizeof (arr));
sscanf函数:
例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <stdio.h> int main () { char str[] = "123 456.78 hello" ; int i; float f; char s[20 ]; int result = sscanf (str, "%d %f %s" , &i, &f, s); if (result == 3 ) { printf ("整数: %d\n" , i); printf ("浮点数: %.2f\n" , f); printf ("字符串: %s\n" , s); } else { printf ("解析失败\n" ); } return 0 ; }
LCD2 LCD函数基本功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void LCD_Clear (u16 Color) void LCD_SetTextColor (vu16 Color) void LCD_SetBackColor (vu16 Color) void LCD_DisplayStringLine (u8 Line,u8 *ptr)
关于sprintf函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include "stdio.h" uint8_t B1_count,B2_count,B3_count,B4_count;char buf[21 ];void Lcd_Proc (void ) {sprintf (buf,"B1_Count:%d" ,B1_count);LCD_DisplayStringLine (Line1,(uint8_t *)buf);sprintf (buf,"B2_Count:%d" ,B2_count);LCD_DisplayStringLine (Line2,(uint8_t *)buf);sprintf (buf,"B3_Count:%d" ,B3_count);LCD_DisplayStringLine (Line3,(uint8_t *)buf);sprintf (buf,"B4_Count:%d" ,B4_count);LCD_DisplayStringLine (Line4,(uint8_t *)buf);}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 void LCD_Proc () { key_val=key_ReadPin () key_down=key_val (key_val^key_old); key_up=~key_val (key_val^key_old); key_val=key_old; if (key_down=1 ) { LCD_Clear (Black); lcd_pag++; if (lcd_pag==3 )lcd_pag==0 ; } } uint8_t buf[21 ];uint8_t lcd_pag;uint8_t count;if (lcd_pag==1 ){ sprintf (buf,"count:%d" ,count); LCD_DisplayStringLine (Line1,(uint8_t *)buf); }
串口通信 USART1串口接受函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 HAL_StatusTypeDef HAL_UART_Transmit (UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout) HAL_UART_Transmit (&huart1,(uint8_t *)tx_buf,strlen(tx_buf),50 ) ; while (1 ) { if (rate=='1' ) { LEDdispla (0x01 ); sprintf (tx_buf,"LED1,OPEN" ); HAL_UART_Transmit (&huart1,(uint8_t *)tx_buf,strlen (tx_buf),50 ); } else if (rate=='2' ) { LEDdispla (0x02 ); sprintf (tx_buf,"LED2 OPEN" ); HAL_UART_Transmit (&huart1,(uint8_t *)tx_buf,strlen (tx_buf),50 ); } else { sprintf (tx_buf,"Erron" ); HAL_UART_Transmit (&huart1,(uint8_t *)tx_buf,strlen (tx_buf),50 ); } HAL_UART_Receive_IT (&huart1,&rate,1 ); }
3.5 关于锁存器占用问题:GPIOD2初始电平要为低电平才不会发生引脚占用
字符串函数strcmp
比较字符串:如果字符串1大于字符串2则返回复数 反之返回正数,否则则返回0
串口解释字符串 定时器 关于定时器ADCARR,CRRX,CNT的解释:
关于ADC ADC测电压函数
1 2 3 4 5 6 7 8 uint16_t git_ADC2 (void ) { uint8_t ADC2=0 ; HAL_ADC_Start (&hadc2); ADC2=HAL_ADC_GetValue (&hadc2); return ADC2; }
关于PWM如何捕获占空比
**关于CNT,CCR,ARR三个值的解释 CNT类似秒表,CCR某个阈值,低于这个阈值输出高电平,高于这个阈值输出低电平,ARR表示整个路程,即周期
通过寄存器去输入捕获测量频率
利用中断回调函数测量中断频率
1 2 3 4 5 6 7 8 9 void HAL_TIM_IC_CaptureCallback (TIM_HandleTypeDef *htim) { if (htim->Instance=TIM17) { capture_vaL=HAL_TIM_ReadCapturedValue (&htim17,TIM_CHANNEL_1); TIM17->CNT=0 ; PA17_frg=80000000 /(800 *capture_vaL); } }
测量占空比函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void HAL_TIM_IC_CaptureCallback (TIM_HandleTypeDef *htim) { if (htim->Instance==TIM2) { if (htim->Channel==HAL_TIM_ACTIVE_CHANNEL_1) { capture_val1=HAL_TIM_ReadCapturedValue (&htim2,TIM_CHANNEL_1); capture_val2=HAL_TIM_ReadCapturedValue (&htim2,TIM_CHANNEL_2); TIM2->CNT=0 ; 40 _frq=80000000 /(80 *capture_val1); duty1 = ((1.0 *capture_val1_2)/capture_val1)*100 ; HAL_TIM_IC_Start (&htim2,TIM_CHANNEL_1); HAL_TIM_IC_Start (&htim2,TIM_CHANNEL_2); } } }
EEPROM模块 EEPROM模块可以用于长期保存,但偶尔会改动的数据
代码:
1 2 3 4 5 6 7 8 9 10 11 12 I2CStart (); I2CSendByte (0xA0 ); I2CWaitAck (); I2CSendByte (ucAddr); I2CWaitAck (); I2CStart (); I2CSendByte (0xA1 ); I2CWaitAck ();
中断时钟回调函数
1 2 3 void HAL_TIM_PeriodElapsedCallback (TIM_HandleTypeDef *htim) ;
时间中断回调函数在tim库5933行
真题总结 十四届真题总结 考点 :LED,LCD,KEY(长按短按),PWM输出,测试频率,ADC
新学到了:可以两个LED模块换着用,然后呢按键的实现