• 蓝桥杯嵌入式STM32G431RBT6知识点(主观题部分)


    目录

    1  前置准备

    1.1 Keil

    1.1.1  编译器版本及微库

    1.1.2  添加官方提供的LCD及I2C文件

    1.2  CubeMX

    1.2.1   时钟树

    1.2.2   其他

    1.2.3  明确CubeMX路径,放置芯片包

    2  GPIO

    2.1  实验1:LED1-LED8循环亮灭

    ​编辑

    2.2  实验2:按键控制LED亮灭(检测电平法)

    2.3  实验3:按键控制LED亮灭(外部中断法)

     2.4  实验4:蜂鸣器

    2.5  实验5:按键消抖

    2.6  实验6:长按短按

    2.7  实验7:双击

    2.8  实验8: 长按双击综合

    3  ADC/DAC

    3.1  实验1:获取电位器引脚的电压

     3.2  实验2:设定双引脚电压并读取

    4  I2C EEPROM

    4.1  实验1:EEPROM的读写+浮点数的处理

    4.2  实验2:大位数读取

    4.3  实验3:EEPROM掉电不丢失

    4.4  实验4:第一次上电问题

     4.5  实验5:MCP4017可编程电阻

    5  UART/USART

    5.1  实验1:轮询收发

     5.2  实验2:中断收发

    5.3  实验3:中断回调函数

    5.4  字符串问题注意

     5.5  实验4:发送指定格式的字符串并从字符串中提取指定信息

    5.6  实验5:DMA及几种收发方式的分析

    5.7  实验6:串口的不定长收发(DMA+空闲中断)

    6  TIM

    6.1  实验1:延时

    6.2  实验2:PWM输出(控制蜂鸣器)

    6.3  实验3:检测555信号发生器信号频率和占空比

     6.4  实验4:检测自己输出的PWM频率和占空比(上升沿中断)

    6.5  实验5:检测自己输出的PWM频率和占空比(PWM中断)

    7  RTC

    7.1  实验1:显示年月日时分秒

    7.2  实验2:秒中断

    7.3  实验3:闹钟中断


    1  前置准备

    1.1 Keil

    1.1.1  编译器版本及微库

    编译器版本调整至version 5,勾选Micro LIB

    1.1.2  添加官方提供的LCD及I2C文件

    这五个文件是需要添加进自己的工程中的

    这个是官方比赛提供的数据包,有关I2C的文件从2中提取,有关LCD的文件从5中提取(.c文件在Src中,.h文件在Inc中)

    1.2  CubeMX

    1.2.1   时钟树

    1.2.2   其他

     

    1.2.3  明确CubeMX路径,放置芯片包

    2  GPIO

    2.1  实验1:LED1-LED8循环亮灭

     在最小系统原理图中找到LED1-LED8对应的引脚是PC8-PC15,那么我们在CubeMX中将这几个引脚配置成GPIO_Output即可

    while内的代码:

    1. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_SET);//常用函数1
    2. HAL_Delay(100);//常用函数2
    3. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_RESET);
    4. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_9,GPIO_PIN_SET);
    5. HAL_Delay(100);
    6. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_9,GPIO_PIN_RESET);
    7. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_10,GPIO_PIN_SET);
    8. HAL_Delay(100);
    9. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_10,GPIO_PIN_RESET);
    10. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_11,GPIO_PIN_SET);
    11. HAL_Delay(100);
    12. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_11,GPIO_PIN_RESET);
    13. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_12,GPIO_PIN_SET);
    14. HAL_Delay(100);
    15. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_12,GPIO_PIN_RESET);
    16. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);
    17. HAL_Delay(100);
    18. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);
    19. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_SET);
    20. HAL_Delay(100);
    21. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_RESET);
    22. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_SET);
    23. HAL_Delay(100);
    24. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_RESET);

    2.2  实验2:按键控制LED亮灭(检测电平法)

     参考按键的引脚,同时别忘了把按键对应的引脚调整为上拉输入(起始高电平)

    while内代码:

    1. if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==GPIO_PIN_RESET)
    2. {
    3. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_SET);
    4. HAL_Delay(500);
    5. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_RESET);
    6. }

    2.3  实验3:按键控制LED亮灭(外部中断法)

    找到按键对应引脚:

     打开外部中断:

    1. /* USER CODE BEGIN PFP */
    2. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_PIN)
    3. {
    4. if(GPIO_PIN==GPIO_PIN_0)
    5. {
    6. HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_8);
    7. }
    8. }
    9. /* USER CODE END PFP */

     2.4  实验4:蜂鸣器

    当PB3为高电平时,二极管断开,所以蜂鸣器路; 当PB3为低电平时,二极管导通,所以蜂鸣器路短路

    只要配置好PB3的GPIO,就能轻松使用,这里不再用代码解释

    2.5  实验5:按键消抖

    按键按下和放下的过程中会出现抖动,进而出现高低电平的交替,我们通过扫描两次按键的情况来具体判断情况

    1.如果第一次扫描为高电平,那么按键没有被按下

    2.如果第一次扫描为低电平,第二次扫描为高电平,那么认为是抖动,不计入成功按键

    3.如果两次扫描均为低电平,成功按键

    两次扫描的间隔用定时器中断来做

    芯片信号频率为80MHz,分频系数设置为8000-1,计数器溢出值设置为100-1,那么定时器溢出时间为10ms

    1. /* USER CODE BEGIN PTD */
    2. char buf[20];
    3. struct keys{
    4. int step;
    5. int state;
    6. }key[4];
    7. /* USER CODE END PTD */
    8. /* USER CODE BEGIN PFP */
    9. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    10. {
    11. if(htim->Instance==TIM1)
    12. {
    13. key[0].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
    14. key[1].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
    15. key[2].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
    16. key[3].state=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
    17. for(int i=0;i<4;i++)
    18. {
    19. switch(key[i].step)
    20. {
    21. case 0:
    22. {
    23. if(key[i].state==GPIO_PIN_RESET)
    24. {
    25. key[i].step=1;
    26. }
    27. }
    28. break;
    29. case 1:
    30. {
    31. if(key[i].state==GPIO_PIN_RESET)
    32. {
    33. key[i].step=2;
    34. sprintf(buf,"%d",i);
    35. LCD_DisplayStringLine(Line4,(uint8_t*)buf);
    36. }
    37. else
    38. {
    39. key[i].step=0;
    40. }
    41. }
    42. break;
    43. case 2:
    44. {
    45. if(key[i].state==GPIO_PIN_SET)
    46. {
    47. key[i].step=0;
    48. }
    49. }
    50. break;
    51. }
    52. }
    53. }
    54. }
    55. /* USER CODE END PFP */
    56. /* USER CODE BEGIN 2 */
    57. LCD_Init();
    58. LCD_Clear(Blue);
    59. LCD_SetBackColor(Blue);
    60. LCD_SetTextColor(White);
    61. I2CInit();
    62. HAL_TIM_Base_Start_IT(&htim1);
    63. /* USER CODE END 2 */

    2.6  实验6:长按短按

    1.如果第一次扫描为高电平,则没有按键

    2.如果第一次扫描为低电平,第二次扫描为高电平,那么认为是抖动,不计入成功按键

    3.如果两次扫描均为低电平,成功按键,如果按键时间大于700ms,则视为长按,若小于700ms,则视为短按

    1. /* USER CODE BEGIN PTD */
    2. char buf[20];
    3. struct keys{
    4. int step;
    5. int state;
    6. }key[4];
    7. int a=-1;
    8. int cnt;
    9. /* USER CODE END PTD */
    10. /* USER CODE BEGIN PFP */
    11. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    12. {
    13. if(htim->Instance==TIM1)
    14. {
    15. key[0].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
    16. key[1].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
    17. key[2].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
    18. key[3].state=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
    19. for(int i=0;i<4;i++)
    20. {
    21. switch(key[i].step)
    22. {
    23. case 0:
    24. {
    25. if(key[i].state==GPIO_PIN_RESET)
    26. {
    27. key[i].step=1;
    28. cnt=0;
    29. }
    30. }
    31. break;
    32. case 1:
    33. {
    34. if(key[i].state==GPIO_PIN_RESET)
    35. {
    36. key[i].step=2;
    37. sprintf(buf,"SINGLE");
    38. LCD_ClearLine(Line4);
    39. LCD_DisplayStringLine(Line4,(uint8_t*)buf);
    40. }
    41. else
    42. {
    43. key[i].step=0;
    44. }
    45. }
    46. break;
    47. case 2:
    48. {
    49. if(key[i].state==GPIO_PIN_RESET)
    50. {
    51. if(i==a&&cnt>70)
    52. {
    53. sprintf(buf,"%d %d",i,cnt);
    54. LCD_ClearLine(Line4);
    55. LCD_DisplayStringLine(Line4,(uint8_t*)buf);
    56. }
    57. else
    58. {
    59. a=i;
    60. }
    61. cnt++;
    62. }
    63. else
    64. {
    65. key[i].step=0;
    66. }
    67. }
    68. break;
    69. }
    70. }
    71. }
    72. }
    73. /* USER CODE END PFP */

    2.7  实验7:双击

    1.如果第一次能够被视为成功按键,那么计时开始

    2.如果两次成功按键的间隔小于700ms,视为双击成功

    1. /* USER CODE BEGIN PTD */
    2. char buf[20];
    3. struct keys{
    4. int step;
    5. int state;
    6. int cnt;
    7. }key[4];
    8. int a=-1;
    9. int cnt;
    10. /* USER CODE END PTD */
    11. /* USER CODE BEGIN PFP */
    12. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    13. {
    14. if(htim->Instance==TIM1)
    15. {
    16. key[0].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
    17. key[1].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
    18. key[2].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
    19. key[3].state=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
    20. for(int i=0;i<4;i++)
    21. {
    22. switch(key[i].step)
    23. {
    24. case 0:
    25. {
    26. if(key[i].state==GPIO_PIN_RESET)
    27. {
    28. key[i].step=1;
    29. }
    30. }
    31. break;
    32. case 1:
    33. {
    34. if(key[i].state==GPIO_PIN_RESET)
    35. {
    36. sprintf(buf,"SINGLE");
    37. LCD_ClearLine(Line4);
    38. LCD_DisplayStringLine(Line4,(uint8_t*)buf);
    39. if(i==a)
    40. {
    41. if(key[i].cnt<70)
    42. {
    43. sprintf(buf,"%d %d",i,key[i].cnt);
    44. LCD_ClearLine(Line4);
    45. LCD_DisplayStringLine(Line4,(uint8_t*)buf);
    46. key[i].step=0;
    47. a=-1;
    48. }
    49. key[0].cnt=0;
    50. key[1].cnt=0;
    51. key[2].cnt=0;
    52. key[3].cnt=0;
    53. }
    54. else
    55. {
    56. a=i;
    57. }
    58. key[i].step=2;
    59. }
    60. else
    61. {
    62. key[i].step=0;
    63. }
    64. }
    65. break;
    66. case 2:
    67. {
    68. if(key[i].state==GPIO_PIN_SET)
    69. {
    70. key[i].step=0;
    71. }
    72. }
    73. break;
    74. }
    75. }
    76. key[a].cnt++;
    77. }
    78. }
    79. /* USER CODE END PFP */

    2.8  实验8: 长按双击综合

    其实不用写在一个定时器内,那样逻辑会比较复杂。我们可以开两个定时器,一个定时器检测长按,一个定时器检测双击

    1. /* USER CODE BEGIN PTD */
    2. char buf[20];
    3. struct keys{
    4. int state;
    5. int step1;
    6. int step2;
    7. int double_time;
    8. }key[4];
    9. int a=-1,b=-1;
    10. int cnt;
    11. /* USER CODE END PTD */
    12. /* USER CODE BEGIN PFP */
    13. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    14. {
    15. if(htim->Instance==TIM3)
    16. {
    17. key[0].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
    18. key[1].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
    19. key[2].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
    20. key[3].state=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
    21. for(int i=0;i<4;i++)
    22. {
    23. switch(key[i].step2)
    24. {
    25. case 0:
    26. {
    27. if(key[i].state==GPIO_PIN_RESET)
    28. {
    29. key[i].step2=1;
    30. cnt=0;
    31. }
    32. }
    33. break;
    34. case 1:
    35. {
    36. if(key[i].state==GPIO_PIN_RESET)
    37. {
    38. key[i].step2=2;
    39. /*sprintf(buf,"SINGLE");
    40. LCD_ClearLine(Line4);
    41. LCD_DisplayStringLine(Line4,(uint8_t*)buf);*/
    42. }
    43. else
    44. {
    45. key[i].step2=0;
    46. }
    47. }
    48. break;
    49. case 2:
    50. {
    51. if(key[i].state==GPIO_PIN_RESET)
    52. {
    53. if(i==b&&cnt>70)
    54. {
    55. sprintf(buf,"LONG:%d %d",i,cnt);
    56. LCD_ClearLine(Line4);
    57. LCD_DisplayStringLine(Line4,(uint8_t*)buf);
    58. }
    59. else
    60. {
    61. b=i;
    62. }
    63. cnt++;
    64. }
    65. else
    66. {
    67. key[i].step2=0;
    68. }
    69. }
    70. break;
    71. }
    72. }
    73. }
    74. if(htim->Instance==TIM1)
    75. {
    76. key[0].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
    77. key[1].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
    78. key[2].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
    79. key[3].state=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
    80. for(int i=0;i<4;i++)
    81. {
    82. switch(key[i].step1)
    83. {
    84. case 0:
    85. {
    86. if(key[i].state==GPIO_PIN_RESET)
    87. {
    88. key[i].step1=1;
    89. }
    90. }
    91. break;
    92. case 1:
    93. {
    94. if(key[i].state==GPIO_PIN_RESET)
    95. {
    96. sprintf(buf,"SINGLE");
    97. LCD_ClearLine(Line4);
    98. LCD_DisplayStringLine(Line4,(uint8_t*)buf);
    99. if(i==a)
    100. {
    101. if(key[i].double_time<70)
    102. {
    103. sprintf(buf,"DOUBLE:%d %d",i,key[i].double_time);
    104. LCD_ClearLine(Line4);
    105. LCD_DisplayStringLine(Line4,(uint8_t*)buf);
    106. key[i].step1=0;
    107. a=-1;
    108. }
    109. key[0].double_time=0;
    110. key[1].double_time=0;
    111. key[2].double_time=0;
    112. key[3].double_time=0;
    113. }
    114. else
    115. {
    116. a=i;
    117. }
    118. key[i].step1=2;
    119. }
    120. else
    121. {
    122. key[i].step1=0;
    123. }
    124. }
    125. break;
    126. case 2:
    127. {
    128. if(key[i].state==GPIO_PIN_SET)
    129. {
    130. key[i].step1=0;
    131. }
    132. }
    133. break;
    134. }
    135. }
    136. key[a].double_time++;
    137. }
    138. }
    139. /* USER CODE END PFP */
    140. /* USER CODE BEGIN 2 */
    141. LCD_Init();
    142. LCD_Clear(Blue);
    143. HAL_TIM_Base_Start_IT(&htim1);
    144. HAL_TIM_Base_Start_IT(&htim3);
    145. /* USER CODE END 2 */

    2.9  锁存器

    由于LCD和LED共用一些引脚,导致在使用LCD显示时会出现LED全亮的情况,干扰LED正常的工作,所以我们需要对锁存器进行控制

    当PD2高电平时,锁存器使能,此时LED受引脚高低电平控制; 当PD2低电平时,锁存器失能,此时LED不受引脚高低电平控制

    所以我们需要在控制LED亮灭的语句前后分别对锁存器进行使能和失能操作,并且在程序一开始就对所有灯进行灭处理

    1. HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
    2. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15,GPIO_PIN_SET);
    3. HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);

    3  ADC/DAC

    3.1  实验1:获取电位器引脚的电压

    我们想获取两个电位器的电压:

    这里以PB12为例: 

    1. /* USER CODE BEGIN PTD */
    2. char buf[20];
    3. /* USER CODE END PTD */
    4. /* USER CODE BEGIN PFP */
    5. double getADC()
    6. {
    7. HAL_ADC_Start(&hadc1);
    8. return HAL_ADC_GetValue(&hadc1)*3.3/4096;
    9. }
    10. /* USER CODE END PFP */
    11. /* USER CODE BEGIN 2 */
    12. LCD_Init();
    13. LCD_Clear(Blue);
    14. LCD_SetBackColor(Blue);
    15. LCD_SetTextColor(White);
    16. /* USER CODE END 2 */
    17. /* USER CODE BEGIN 3 */
    18. sprintf(buf,"%.3lf",getADC());
    19. LCD_ClearLine(Line4);
    20. LCD_DisplayStringLine(Line4,(uint8_t*)buf);
    21. HAL_Delay(1000);
    22. }
    23. /* USER CODE END 3 */

    转动电位器R38可观察到电压发生明显变化 

     3.2  实验2:设定双引脚电压并读取

     

    这里我们发现测量ADC的两个引脚均在ADC1上,所以获取电压会有先后之分

     采样时间调长,采样准确些,这里我们看到优先采集PA4,再采集PA3

    1. /* USER CODE BEGIN PFP */
    2. void setDAC()
    3. {
    4. HAL_DAC_Start(&hdac1,DAC_CHANNEL_1);
    5. HAL_DAC_Start(&hdac1,DAC_CHANNEL_2);
    6. HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_1,0,1.1*4096/3.3);
    7. HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_2,0,2.2*4096/3.3);
    8. }
    9. double getADC()
    10. {
    11. HAL_ADC_Start(&hadc2);
    12. return HAL_ADC_GetValue(&hadc2)*3.3/4096;
    13. }
    14. /* USER CODE END PFP */
    15. /* USER CODE BEGIN 2 */
    16. LCD_Init();
    17. LCD_Clear(Blue);
    18. LCD_SetBackColor(Blue);
    19. LCD_SetTextColor(White);
    20. setDAC();
    21. /* USER CODE END 2 */
    22. /* USER CODE BEGIN 3 */
    23. sprintf(buf,"%.3lf",getADC());
    24. LCD_ClearLine(Line4);
    25. LCD_DisplayStringLine(Line4,(uint8_t*)buf);
    26. HAL_Delay(1);//需要延时
    27. sprintf(buf,"%.3lf",getADC());
    28. LCD_ClearLine(Line5);
    29. LCD_DisplayStringLine(Line5,(uint8_t*)buf);
    30. HAL_Delay(1000);
    31. }
    32. /* USER CODE END 3 */

    3.3  实验3:对于AD采集的数据进行滤波处理

    假如有20个数据,我们可以对这些数据排序,然后选中间的十个数据取平均值

    排序算法可以用qsort函数,头文件是algorithm

    1. int cmp_int(const void* e1, const void* e2)
    2. {
    3. //排序为升序
    4. return (*(int*)e1 - *(int*)e2); //*(int*)e1表示将e1强制类型转化为int*,return在这里返回大于0小于0或等于0的数
    5. }
    6. qsort(arr, sz,sizeof(arr[0]),cmp_int); //用qsort排整型

    4  I2C EEPROM

    4.1  实验1:EEPROM的读写+浮点数的处理

    对于EEPROM读写的函数我们有固定模板:

    1. uint8_t EEPROM_Read(uint8_t addr)
    2. {
    3. I2CStart();
    4. I2CSendByte(0xa0);
    5. I2CWaitAck();
    6. I2CSendByte(addr);
    7. I2CWaitAck();
    8. I2CStart();
    9. I2CSendByte(0xa1);
    10. I2CWaitAck();
    11. uint8_t temp=I2CReceiveByte();
    12. I2CWaitAck();
    13. I2CStop();
    14. return temp;
    15. }
    16. void EEPROM_Write(uint8_t addr,uint8_t info)
    17. {
    18. I2CStart();
    19. I2CSendByte(0xa0);
    20. I2CWaitAck();
    21. I2CSendByte(addr);
    22. I2CWaitAck();
    23. I2CSendByte(info);
    24. I2CWaitAck();
    25. I2CStop();
    26. }

    借用3.2的实验数据进行读写,建议EEPROM不要写在while内,EEPROM的读写是有寿命的,每次读写都要延时一下:

    1. /* USER CODE BEGIN PTD */
    2. char buf[20];
    3. double V1[5],V2[5];
    4. uint8_t addr,res1,res2,res3,res4;
    5. /* USER CODE END PTD */
    6. /* USER CODE BEGIN PFP */
    7. void setDAC()
    8. {
    9. HAL_DAC_Start(&hdac1,DAC_CHANNEL_1);
    10. HAL_DAC_Start(&hdac1,DAC_CHANNEL_2);
    11. HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_1,0,1.1*4096/3.3);
    12. HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_2,0,2.2*4096/3.3);
    13. }
    14. double getADC()
    15. {
    16. HAL_ADC_Start(&hadc2);
    17. return HAL_ADC_GetValue(&hadc2)*3.3/4096;
    18. }
    19. uint8_t EEPROM_Read(uint8_t addr)
    20. {
    21. I2CStart();
    22. I2CSendByte(0xa0);
    23. I2CWaitAck();
    24. I2CSendByte(addr);
    25. I2CWaitAck();
    26. I2CStart();
    27. I2CSendByte(0xa1);
    28. I2CWaitAck();
    29. uint8_t temp=I2CReceiveByte();
    30. I2CWaitAck();
    31. I2CStop();
    32. return temp;
    33. }
    34. void EEPROM_Write(uint8_t addr,uint8_t info)
    35. {
    36. I2CStart();
    37. I2CSendByte(0xa0);
    38. I2CWaitAck();
    39. I2CSendByte(addr);
    40. I2CWaitAck();
    41. I2CSendByte(info);
    42. I2CWaitAck();
    43. I2CStop();
    44. }
    45. /* USER CODE END PFP *//* USER CODE BEGIN 2 */
    46. LCD_Init();
    47. LCD_Clear(Blue);
    48. LCD_SetBackColor(Blue);
    49. LCD_SetTextColor(White);
    50. I2CInit();
    51. setDAC();
    52. HAL_Delay(1);
    53. for(int i=0;i<5;i++)
    54. {
    55. V1[i]=getADC();
    56. HAL_Delay(1);
    57. V2[i]=getADC();
    58. HAL_Delay(1);
    59. }
    60. for(int i=0;i<5;i++)
    61. {
    62. EEPROM_Write(addr++,(uint8_t)V1[i]);
    63. HAL_Delay(50);
    64. EEPROM_Write(addr++,(V1[i]-(uint8_t)V1[i])*100);
    65. HAL_Delay(50);
    66. EEPROM_Write(addr++,(uint8_t)V2[i]);
    67. HAL_Delay(50);
    68. EEPROM_Write(addr++,(V2[i]-(uint8_t)V2[i])*100);
    69. HAL_Delay(50);
    70. }
    71. addr=0;
    72. for(int i=0;i<5;i++)
    73. {
    74. res1=EEPROM_Read(addr++);
    75. HAL_Delay(50);
    76. res2=EEPROM_Read(addr++);
    77. HAL_Delay(50);
    78. res3=EEPROM_Read(addr++);
    79. HAL_Delay(50);
    80. res4=EEPROM_Read(addr++);
    81. HAL_Delay(50);
    82. sprintf(buf,"%.2lf %.2lf",res1+(double)res2/100,res3+(double)res4/100);
    83. LCD_DisplayStringLine(Line4,(uint8_t*)buf);
    84. }
    85. /* USER CODE END 2 */

    4.2  实验2:大位数读取

    8位范围:0-255

    16位范围:0-65535

    24位范围:0-16777215

    32位范围:0-4294967296

    这里以到24位为例

    1. /* USER CODE BEGIN PTD */
    2. char buf[20];
    3. int num[10]={105798,367842,56674,4,256,8917,56565,34343,1025,788};
    4. uint8_t temp1,temp2,temp3,res1,res2,res3;
    5. uint8_t addr;
    6. /* USER CODE END PTD */
    7. /* USER CODE BEGIN PFP */
    8. uint8_t EEPROM_Read(uint8_t addr)
    9. {
    10. I2CStart();
    11. I2CSendByte(0xa0);
    12. I2CWaitAck();
    13. I2CSendByte(addr);
    14. I2CWaitAck();
    15. I2CStart();
    16. I2CSendByte(0xa1);
    17. I2CWaitAck();
    18. uint8_t temp=I2CReceiveByte();
    19. I2CWaitAck();
    20. I2CStop();
    21. return temp;
    22. }
    23. void EEPROM_Write(uint8_t addr,uint8_t info)
    24. {
    25. I2CStart();
    26. I2CSendByte(0xa0);
    27. I2CWaitAck();
    28. I2CSendByte(addr);
    29. I2CWaitAck();
    30. I2CSendByte(info);
    31. I2CWaitAck();
    32. I2CStop();
    33. }
    34. /* USER CODE END PFP */
    35. /* USER CODE BEGIN 2 */
    36. LCD_Init();
    37. LCD_Clear(Blue);
    38. LCD_SetBackColor(Blue);
    39. LCD_SetTextColor(White);
    40. I2CInit();
    41. for(int i=0;i<10;i++)
    42. {
    43. temp1=num[i]&0xFF;
    44. temp2=((num[i]-temp1)>>8)&0xFF;
    45. temp3=((num[i]-temp1-(temp2<<8))>>16)&0xFF;
    46. EEPROM_Write(addr++,temp1);
    47. HAL_Delay(50);
    48. EEPROM_Write(addr++,temp2);
    49. HAL_Delay(50);
    50. EEPROM_Write(addr++,temp3);
    51. HAL_Delay(50);
    52. }
    53. addr=0;
    54. for(int i=0;i<10;i++)
    55. {
    56. res1=EEPROM_Read(addr++);
    57. HAL_Delay(50);
    58. res2=EEPROM_Read(addr++);
    59. HAL_Delay(50);
    60. res3=EEPROM_Read(addr++);
    61. HAL_Delay(50);
    62. sprintf(buf,"%d",res1+(res2<<8)+(res3<<16));
    63. LCD_ClearLine(Line4);
    64. LCD_DisplayStringLine(Line4,(uint8_t*)buf);
    65. HAL_Delay(1000);
    66. }
    67. /* USER CODE END 2 */

    4.3  实验3:EEPROM掉电不丢失

    去掉上面程序的写的部分,重新烧录即可验证

    4.4  实验4:第一次上电问题

    我们以这道题为例:如果我们将程序烧录到新板时,EEPROM的值是不确定的,而题目要求初次上电就要能读取相关值。所以我们需要判断板子是否是第一次上电,然后做出相关步骤

    1. if(EEPROM_Read(0xaa)!=1&&EEPROM_Read(0xab)!=1)
    2. {
    3. EEPROM_Write(0xaa,1);//我们设定0xaa为是否是第一次上电(新板)的标志位1
    4. HAL_Delay(20);
    5. EEPROM_Write(0xab,1);//我们设定0xab为是否是第一次上电(新板)的标志位2
    6. HAL_Delay(20);
    7. EEPROM_Write(0xa0,30);
    8. HAL_Delay(20);
    9. EEPROM_Write(0xa1,50);
    10. HAL_Delay(20);
    11. EEPROM_Write(0xa2,70);
    12. HAL_Delay(20);
    13. }

     4.5  实验5:MCP4017可编程电阻

    原理:

    Rs为单个电阻阻值;RWS为总阻值,与R17串联,两者对VDD电压进行分压,可以通过测量PB14的电压判断可编程电阻的阻值。

    MCP4017的默认总阻值为100kΩ ,对应0-127个档位,当寄存器为0时,阻值为0;当寄存器为0x7F时阻值为100kΩ

    R = 787.4 * readresistor


    V=3.3*\tfrac{R}{R+10}

    模板:

    1. void RWrite(uint8_t value)
    2. {
    3. I2CStart();
    4. I2CSendByte(0x5e);
    5. I2CWaitAck();
    6. I2CSendByte(value);
    7. I2CWaitAck();
    8. I2CStop();
    9. }
    10. uint8_t RRead(void)
    11. {
    12. uint8_t value;
    13. I2CStart();
    14. I2CSendByte(0x5F);
    15. I2CWaitAck();
    16. value = I2CReceiveByte();
    17. I2CSendNotAck();
    18. I2CStop();
    19. return value;
    20. }

    检测从0-127,PB14的电压

    1. /* USER CODE BEGIN PFP */
    2. void RWrite(uint8_t value)
    3. {
    4. I2CStart();
    5. I2CSendByte(0x5e);
    6. I2CWaitAck();
    7. I2CSendByte(value);
    8. I2CWaitAck();
    9. I2CStop();
    10. }
    11. uint8_t RRead(void)
    12. {
    13. uint8_t value;
    14. I2CStart();
    15. I2CSendByte(0x5F);
    16. I2CWaitAck();
    17. value = I2CReceiveByte();
    18. I2CSendNotAck();
    19. I2CStop();
    20. return value;
    21. }
    22. double getADC()
    23. {
    24. HAL_ADC_Start(&hadc1);
    25. return HAL_ADC_GetValue(&hadc1)*3.3/4096;
    26. }
    27. /* USER CODE END PFP */
    28. /* USER CODE BEGIN WHILE */
    29. for(int i=0;i<128;i++)
    30. {
    31. RWrite(i);
    32. HAL_Delay(20);
    33. sprintf(buf,"%d %lf",RRead(),getADC());
    34. LCD_ClearLine(Line4);
    35. LCD_DisplayStringLine(Line4,(uint8_t*)buf);
    36. HAL_Delay(1000);
    37. }
    38. while (1)
    39. {
    40. /* USER CODE END WHILE */

    5  UART/USART

    CubeMX默认的引脚是不对的,所以别忘了更改引脚

    5.1  实验1:轮询收发

    HAL_UART_Receive:接收不完指定数量的字符不会进行下一步,设置的等待时间一般为无限大

    1. /* USER CODE BEGIN WHILE */
    2. HAL_UART_Receive(&huart1,(uint8_t*)buf,5,0xFFFF);//接收不完指定数量的字符不会进行下一步
    3. HAL_UART_Transmit(&huart1,(uint8_t*)buf,5,0xFFFF);
    4. while (1)
    5. {
    6. /* USER CODE END WHILE */

     5.2  实验2:中断收发

    CubeMX别忘打开串口中断

    HAL_UART_Receive_IT:不会等待,最多收指定个数,收几个无所谓,但是只有收满了才会进入中断回调函数

    下面这个程序如果不写在while里是不会收到除了空白以外的任何字符的,因为只执行一次的话Receive函数已经过了,不会再执行

    1. while (1)
    2. {
    3. /* USER CODE END WHILE */
    4. /* USER CODE BEGIN 3 */
    5. HAL_UART_Receive_IT(&huart1,(uint8_t*)buf,5);//不会等待,最多收5个,收几个无所谓
    6. HAL_UART_Transmit_IT(&huart1,(uint8_t*)buf,5);
    7. }
    8. /* USER CODE END 3 */

    5.3  实验3:中断回调函数

    1. /* USER CODE BEGIN PFP */
    2. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    3. {
    4. if(huart->Instance==USART1)
    5. {
    6. HAL_UART_Transmit_IT(&huart1,(uint8_t*)buf,5);
    7. HAL_UART_Receive_IT(&huart1,(uint8_t*)buf,5);
    8. }
    9. }
    10. /* USER CODE END PFP */
    11. /* USER CODE BEGIN WHILE */
    12. HAL_UART_Receive_IT(&huart1,(uint8_t*)buf,5);//不会等待,最多收5个,收几个无所谓
    13. while (1)
    14. {
    15. /* USER CODE END WHILE */

    5.4  字符串问题注意

    每个字符串结尾都有\r\n占两个位置,而且如果我们在串口助手里勾选发送新行后,每次发送的字符串后都带\r\n

     5.5  实验4:发送指定格式的字符串并从字符串中提取指定信息

    比如我们想发送时间,指定格式为时:分:秒,我们想从串口收到的字符串中提取到时,分,秒三个信息

    这里用到sscanf函数来提取信息

    当然也可以根据实际情况单个单个提取,不过会略显复杂

    1. /* USER CODE BEGIN PTD */
    2. char buf[20];
    3. int hour,min,second;
    4. /* USER CODE END PTD */
    5. /* USER CODE BEGIN PFP */
    6. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    7. {
    8. if(huart->Instance==USART1)
    9. {
    10. sscanf(buf,"%d:%d:%d",&hour,&min,&second);
    11. sprintf(buf,"hour:%d",hour);
    12. LCD_ClearLine(Line2);
    13. LCD_ClearLine(Line4);
    14. LCD_ClearLine(Line6);
    15. LCD_DisplayStringLine(Line2,(uint8_t*)buf);
    16. sprintf(buf,"min:%d",min);
    17. LCD_DisplayStringLine(Line4,(uint8_t*)buf);
    18. sprintf(buf,"second:%d",second);
    19. LCD_DisplayStringLine(Line6,(uint8_t*)buf);
    20. memset(buf,0,sizeof(buf));
    21. HAL_UART_Receive_IT(&huart1,(uint8_t*)buf,8);
    22. }
    23. }
    24. /* USER CODE END PFP */
    25. /* USER CODE BEGIN 2 */
    26. LCD_Init();
    27. LCD_Clear(Blue);
    28. LCD_SetBackColor(Blue);
    29. LCD_SetTextColor(White);
    30. I2CInit();
    31. /* USER CODE END 2 */
    32. /* Infinite loop */
    33. /* USER CODE BEGIN WHILE */
    34. HAL_UART_Receive_IT(&huart1,(uint8_t*)buf,8);
    35. while (1)
    36. {
    37. /* USER CODE END WHILE */

    5.6  实验5:DMA及几种收发方式的分析

    打开DMA: 

     打开中断:

    DMA是默认打开中断的

     因为DMA是不占用CPU的,我们可以尽量使用DMA来提高效率

    这里我们发现收到的数据不完整,然后我们延时一下,这次数据完整

    1. /* USER CODE BEGIN PTD */
    2. char buf[20];
    3. int hour,min,second;
    4. /* USER CODE END PTD */
    5. /* USER CODE BEGIN PFP */
    6. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    7. {
    8. if(huart->Instance==USART1)
    9. {
    10. sscanf(buf,"%d:%d:%d",&hour,&min,&second);
    11. sprintf(buf,"hour:%d",hour);
    12. LCD_ClearLine(Line2);
    13. LCD_ClearLine(Line4);
    14. LCD_ClearLine(Line6);
    15. LCD_DisplayStringLine(Line2,(uint8_t*)buf);
    16. sprintf(buf,"min:%d",min);
    17. LCD_DisplayStringLine(Line4,(uint8_t*)buf);
    18. sprintf(buf,"second:%d",second);
    19. LCD_DisplayStringLine(Line6,(uint8_t*)buf);
    20. sprintf(buf,"%02d:%02d:%02d",hour,min,second);
    21. HAL_UART_Transmit_DMA(&huart1,(uint8_t*)buf,8);
    22. //int t=100000; while(t--);
    23. LCD_DisplayStringLine(Line8,(uint8_t*)buf);
    24. memset(buf,0,sizeof(buf));
    25. HAL_UART_Receive_DMA(&huart1,(uint8_t*)buf,8);
    26. }
    27. }
    28. /* USER CODE END PFP */
    29. /* USER CODE BEGIN WHILE */
    30. HAL_UART_Receive_DMA(&huart1,(uint8_t*)buf,8);
    31. while (1)
    32. {
    33. /* USER CODE END WHILE */

    然后我们试图将Transmit一句改成IT,然后将USART1的中断优先级调整比DMA高,然后发现仍然可以接收到

    但是如果我们全部改成IT,就不可取了,初步分析是中断回调函数的事件过多

    所以我们尽量采取DMA的方式收发数据

    5.7  实验6:串口的不定长收发(DMA+空闲中断)

    其中中断服务函数需要到

     中找

    1. /* USER CODE BEGIN WHILE */
    2. HAL_UART_Receive_DMA(&huart1,(uint8_t*)buf,20);
    3. __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
    4. while (1)
    5. {
    6. /* USER CODE END WHILE */
    7. void USART1_IRQHandler(void)
    8. {
    9. /* USER CODE BEGIN USART1_IRQn 0 */
    10. if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)==SET)
    11. {
    12. __HAL_UART_CLEAR_IDLEFLAG(&huart1);
    13. HAL_UART_DMAStop(&huart1);
    14. len=20-__HAL_DMA_GET_COUNTER(huart1.hdmarx);
    15. HAL_UART_Transmit_DMA(&huart1,(uint8_t*)buf,len);
    16. HAL_UART_Receive_DMA(&huart1,(uint8_t*)buf,20);
    17. }
    18. /* USER CODE END USART1_IRQn 0 */
    19. HAL_UART_IRQHandler(&huart1);
    20. /* USER CODE BEGIN USART1_IRQn 1 */
    21. /* USER CODE END USART1_IRQn 1 */
    22. }

    6  TIM

    6.1  实验1:延时

    1. while (1)
    2. {
    3. /* USER CODE END WHILE */
    4. /* USER CODE BEGIN 3 */
    5. if(__HAL_TIM_GetCounter(&htim1)==10000)
    6. {
    7. HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_8);
    8. __HAL_TIM_SetCounter(&htim1,0);
    9. }
    10. }
    11. /* USER CODE END 3 */

    6.2  实验2:PWM输出(控制蜂鸣器)

    PWM原理如图所示: 

    1. /* USER CODE BEGIN 2 */
    2. LCD_Init();
    3. LCD_Clear(Blue);
    4. LCD_SetBackColor(Blue);
    5. LCD_SetTextColor(White);
    6. I2CInit();
    7. LED_Close();
    8. HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
    9. __HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,5000);
    10. /* USER CODE END 2 */

    6.3  实验3:检测555信号发生器信号频率和占空比

     分析:每次捕捉到上升沿我们就进入一次中断,这时我们就得到了一个信号周期的大小

    现在我们已知定时器计一个数的时间,只要我们读取定时器计了多少数,就能通过公式:

    信号周期=定时器计一个数的时间*定时器计数值

    算出信号周期,进而算出信号频率

    占空比的计算可以另外设置一个通道,根据占空比的定义:

    占空比=一个周期内高电平的时间/一个周期

    测量PA15引脚对应的555信号发生器:

    注意:分频系数设置为80比较好,这样记一次数的时间比较短,测量比较精确,而且计数器不容易溢出

    注意TIM2CHANNEL1对应的引脚设置成PA15

    这里注意打印%的方法

    1. /* USER CODE BEGIN PFP */
    2. void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
    3. {
    4. if(htim->Instance==TIM2&&htim->Channel==HAL_TIM_ACTIVE_CHANNEL_1)
    5. {
    6. cnt=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_1);
    7. cnt_down=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_2);
    8. __HAL_TIM_SetCounter(&htim2,0);
    9. f=10000000/cnt;
    10. duty=1-(double)cnt_down/(double)cnt;
    11. }
    12. }
    13. /* USER CODE END PFP */
    14. /* USER CODE BEGIN 2 */
    15. LCD_Init();
    16. LCD_Clear(Blue);
    17. LCD_SetBackColor(Blue);
    18. LCD_SetTextColor(White);
    19. I2CInit();
    20. HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1);
    21. HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_2);
    22. /* USER CODE END 2 */
    23. while (1)
    24. {
    25. /* USER CODE END WHILE */
    26. /* USER CODE BEGIN 3 */
    27. sprintf(buf,"%dHz %.2lf%%",f,duty*100);
    28. LCD_DisplayStringLine(Line4,(uint8_t*)buf);
    29. HAL_Delay(1000);
    30. LCD_ClearLine(Line4);
    31. }
    32. /* USER CODE END 3 */

     6.4  实验4:检测自己输出的PWM频率和占空比(上升沿中断)

    找到板子上能插杜邦线的两个引脚,一个引脚输出PWM,另一个引脚测量输入的PWM有关性质

    我选择了PB15和PB11两个引脚,PB15用来产生PWM波,PB11用来测量PWM的有关性质

     PB15:

    设置分频系数为8,计数器最大值为1000-1,所以PWM的频率为:

    80MHz/8/1000=10000Hz

    PB11:

    分频系数为80-1,所以计数频率(1/计一个数的时间)为:

    80MHz/80=1MHz

     set可以设置为在0-1000内的值,假如我们设置为300,那么占空比为30%

    1. /* USER CODE BEGIN PTD */
    2. int cnt,cnt_down,f;
    3. double duty;
    4. char buf[20];
    5. int set;
    6. /* USER CODE END PTD */
    7. /* USER CODE BEGIN PFP */
    8. void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
    9. {
    10. if(htim->Instance==TIM2&&htim->Channel==HAL_TIM_ACTIVE_CHANNEL_4)
    11. {
    12. cnt=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_4);
    13. cnt_down=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_3);
    14. __HAL_TIM_SetCounter(&htim2,0);
    15. f=1000000/cnt;
    16. duty=(double)cnt_down/(double)cnt;
    17. }
    18. }
    19. /* USER CODE END PFP */
    20. /* USER CODE BEGIN 2 */
    21. HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_4);
    22. HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_3);
    23. HAL_TIM_PWM_Start(&htim15,TIM_CHANNEL_2);
    24. __HAL_TIM_SetCompare(&htim15,TIM_CHANNEL_2,set);
    25. LCD_Init();
    26. LCD_Clear(Blue);
    27. /* USER CODE END 2 */
    28. /* USER CODE BEGIN WHILE */
    29. while (1)
    30. {
    31. /* USER CODE END WHILE */
    32. /* USER CODE BEGIN 3 */
    33. sprintf(buf,"%dHz %.4lf",f,duty);
    34. LCD_DisplayStringLine(Line2,(uint8_t*)buf);
    35. HAL_Delay(200);
    36. }
    37. /* USER CODE END 3 */

     最后测量得到频率为10204Hz,占空比为0.5102,和预估结果大致相同

    6.5  实验5:检测自己输出的PWM频率和占空比(PWM中断)

     捕捉到上升沿中断其实跟PWM中断一样,都是上升沿开始时触发中断

    我们打开PWM中断,使用PWM中断回调函数

    1. /* USER CODE BEGIN PTD */
    2. int cnt,cnt_down,f;
    3. double duty;
    4. char buf[20];
    5. int set;
    6. /* USER CODE END PTD */
    7. /* USER CODE BEGIN PFP */
    8. void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
    9. {
    10. if(htim->Instance==TIM15&&htim->Channel==HAL_TIM_ACTIVE_CHANNEL_2)
    11. {
    12. cnt=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_4);
    13. cnt_down=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_3);
    14. __HAL_TIM_SetCounter(&htim2,0);
    15. f=1000000/cnt;
    16. duty=1-(double)cnt_down/(double)cnt;
    17. }
    18. }
    19. /* USER CODE END PFP */
    20. /* USER CODE BEGIN 2 */
    21. HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_4);
    22. HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_3);
    23. HAL_TIM_PWM_Start_IT(&htim15,TIM_CHANNEL_2);
    24. __HAL_TIM_SetCompare(&htim15,TIM_CHANNEL_2,set);
    25. LCD_Init();
    26. LCD_Clear(Blue);
    27. /* USER CODE END 2 */
    28. /* USER CODE BEGIN WHILE */
    29. while (1)
    30. {
    31. /* USER CODE END WHILE */
    32. /* USER CODE BEGIN 3 */
    33. sprintf(buf,"%dHz %.4lf",f,duty);
    34. LCD_DisplayStringLine(Line2,(uint8_t*)buf);
    35. HAL_Delay(200);
    36. }
    37. /* USER CODE END 3 */

    6.6  实验6:呼吸灯

    这里着重强调写呼吸灯的一点小问题:由于LED和LCD有互相干扰的情况,我们写LCD的初始化语句之后发现呼吸灯不好使了,代码如下:

    1. /* Initialize all configured peripherals */
    2. MX_GPIO_Init();
    3. MX_TIM3_Init();
    4. /* USER CODE BEGIN 2 */
    5. LCD_Init();
    6. LCD_Clear(Yellow);
    7. HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);
    8. HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
    9. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15,GPIO_PIN_SET);
    10. HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
    11. /* USER CODE END 2 */
    12. /* Infinite loop */
    13. /* USER CODE BEGIN WHILE */
    14. HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
    15. while (1)
    16. {
    17. /* USER CODE END WHILE */
    18. /* USER CODE BEGIN 3 */
    19. for(int i=0;i<1000;i++)
    20. {
    21. __HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_3,i);
    22. HAL_Delay(1);
    23. }
    24. for(int i=1000;i>=0;i--)
    25. {
    26. __HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_3,i);
    27. HAL_Delay(1);
    28. }
    29. }
    30. /* USER CODE END 3 */

    但是如果我们把定时器3的初始化语句加在循环内,就好用了

    初步分析是呼吸灯与定时器有关,但LCD与GPIO有关,需要在使用不同功能时切换

    修改后如下,这样就好用了:

    1. /* Initialize all configured peripherals */
    2. MX_GPIO_Init();
    3. MX_TIM3_Init();
    4. /* USER CODE BEGIN 2 */
    5. LCD_Init();
    6. LCD_Clear(Yellow);
    7. HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);
    8. HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
    9. HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15,GPIO_PIN_SET);
    10. HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
    11. /* USER CODE END 2 */
    12. /* Infinite loop */
    13. /* USER CODE BEGIN WHILE */
    14. HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
    15. while (1)
    16. {
    17. /* USER CODE END WHILE */
    18. /* USER CODE BEGIN 3 */
    19. for(int i=0;i<1000;i++)
    20. {
    21. MX_TIM3_Init();
    22. __HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_3,i);
    23. HAL_Delay(1);
    24. }
    25. for(int i=1000;i>=0;i--)
    26. {
    27. MX_TIM3_Init();
    28. __HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_3,i);
    29. HAL_Delay(1);
    30. }
    31. }
    32. /* USER CODE END 3 */

    7  RTC

    7.1  实验1:显示年月日时分秒

    1. /* USER CODE BEGIN PTD */
    2. char buf[20];
    3. /* USER CODE END PTD */
    4. /* USER CODE BEGIN PD */
    5. RTC_TimeTypeDef TIME;
    6. RTC_DateTypeDef DATE;
    7. /* USER CODE END PD */
    8. /* USER CODE BEGIN WHILE */
    9. while (1)
    10. {
    11. /* USER CODE END WHILE */
    12. /* USER CODE BEGIN 3 */
    13. HAL_RTC_GetTime(&hrtc,&TIME,RTC_FORMAT_BIN);
    14. HAL_RTC_GetDate(&hrtc,&DATE,RTC_FORMAT_BIN);
    15. LCD_ClearLine(Line2);
    16. LCD_ClearLine(Line4);
    17. sprintf(buf,"%d-%d-%d",DATE.Year,DATE.Month,DATE.Date);
    18. LCD_DisplayStringLine(Line2,(uint8_t*)buf);
    19. sprintf(buf,"%d:%d:%d",TIME.Hours,TIME.Minutes,TIME.Seconds);
    20. LCD_DisplayStringLine(Line4,(uint8_t*)buf);
    21. }
    22. /* USER CODE END 3 */

    7.2  实验2:秒中断

    上面我们已经设置好RTC的频率为750MHz,按照如上系数配置,750K/125/6000=1Hz

    1秒发生一次中断,而跟我们设定的闹钟时间无关

    1. /* USER CODE BEGIN PTD */
    2. char buf[20];
    3. /* USER CODE END PTD */
    4. /* USER CODE BEGIN PD */
    5. RTC_TimeTypeDef TIME;
    6. RTC_DateTypeDef DATE;
    7. /* USER CODE END PD */
    8. /* USER CODE BEGIN PFP */
    9. void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
    10. {
    11. HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_3);
    12. }
    13. /* USER CODE END PFP */
    14. /* USER CODE BEGIN WHILE */
    15. while (1)
    16. {
    17. /* USER CODE END WHILE */
    18. /* USER CODE BEGIN 3 */
    19. HAL_RTC_GetTime(&hrtc,&TIME,RTC_FORMAT_BIN);
    20. HAL_RTC_GetDate(&hrtc,&DATE,RTC_FORMAT_BIN);
    21. LCD_ClearLine(Line2);
    22. LCD_ClearLine(Line4);
    23. sprintf(buf,"%d-%d-%d",DATE.Year,DATE.Month,DATE.Date);
    24. LCD_DisplayStringLine(Line2,(uint8_t*)buf);
    25. sprintf(buf,"%d:%d:%d",TIME.Hours,TIME.Minutes,TIME.Seconds);
    26. LCD_DisplayStringLine(Line4,(uint8_t*)buf);
    27. }
    28. /* USER CODE END 3 */

    7.3  实验3:闹钟中断

    日期时分都不看,只看秒,如果闹钟设定的秒跟当前时间一样,则进入中断,自己需要编写的其它代码跟实验2一致

  • 相关阅读:
    【原理篇】四、自定义starter
    魔百盒CM201-2-CH-Hi3798MV300-300H-EMMC和NAND_红外蓝牙语音_通刷固件包
    单目标追踪——【孪生网络】SiamMask论文阅读笔记
    Redis为何快_
    Minecraft 1.19.2 Forge模组开发 05.矿石生成
    C/C++家谱管理系统
    5年磨一剑|优酷Android包瘦身治理思路全解
    Jumpserver堡垒机使用与二次开发详解
    中断处理
    C goto 语句
  • 原文地址:https://blog.csdn.net/m0_71934846/article/details/136112090