赞
踩
目录
5.5 实验4:发送指定格式的字符串并从字符串中提取指定信息
6.4 实验4:检测自己输出的PWM频率和占空比(上升沿中断)
6.5 实验5:检测自己输出的PWM频率和占空比(PWM中断)
编译器版本调整至version 5,勾选Micro LIB
这五个文件是需要添加进自己的工程中的
这个是官方比赛提供的数据包,有关I2C的文件从2中提取,有关LCD的文件从5中提取(.c文件在Src中,.h文件在Inc中)
在最小系统原理图中找到LED1-LED8对应的引脚是PC8-PC15,那么我们在CubeMX中将这几个引脚配置成GPIO_Output即可
while内的代码:
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_SET);//常用函数1
- HAL_Delay(100);//常用函数2
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_RESET);
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_9,GPIO_PIN_SET);
- HAL_Delay(100);
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_9,GPIO_PIN_RESET);
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_10,GPIO_PIN_SET);
- HAL_Delay(100);
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_10,GPIO_PIN_RESET);
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_11,GPIO_PIN_SET);
- HAL_Delay(100);
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_11,GPIO_PIN_RESET);
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_12,GPIO_PIN_SET);
- HAL_Delay(100);
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_12,GPIO_PIN_RESET);
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);
- HAL_Delay(100);
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_SET);
- HAL_Delay(100);
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_RESET);
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_SET);
- HAL_Delay(100);
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_RESET);
参考按键的引脚,同时别忘了把按键对应的引脚调整为上拉输入(起始高电平)
while内代码:
- if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==GPIO_PIN_RESET)
- {
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_SET);
- HAL_Delay(500);
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_RESET);
- }
找到按键对应引脚:
打开外部中断:
- /* USER CODE BEGIN PFP */
- void HAL_GPIO_EXTI_Callback(uint16_t GPIO_PIN)
- {
- if(GPIO_PIN==GPIO_PIN_0)
- {
- HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_8);
- }
- }
- /* USER CODE END PFP */
当PB3为高电平时,二极管断开,所以蜂鸣器路通; 当PB3为低电平时,二极管导通,所以蜂鸣器路短路
只要配置好PB3的GPIO,就能轻松使用,这里不再用代码解释
按键按下和放下的过程中会出现抖动,进而出现高低电平的交替,我们通过扫描两次按键的情况来具体判断情况
1.如果第一次扫描为高电平,那么按键没有被按下
2.如果第一次扫描为低电平,第二次扫描为高电平,那么认为是抖动,不计入成功按键
3.如果两次扫描均为低电平,成功按键
两次扫描的间隔用定时器中断来做
芯片信号频率为80MHz,分频系数设置为8000-1,计数器溢出值设置为100-1,那么定时器溢出时间为10ms
- /* USER CODE BEGIN PTD */
- char buf[20];
- struct keys{
- int step;
- int state;
- }key[4];
- /* USER CODE END PTD */
- /* USER CODE BEGIN PFP */
- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
- {
- if(htim->Instance==TIM1)
- {
- key[0].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
- key[1].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
- key[2].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
- key[3].state=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
- for(int i=0;i<4;i++)
- {
- switch(key[i].step)
- {
- case 0:
- {
- if(key[i].state==GPIO_PIN_RESET)
- {
- key[i].step=1;
- }
- }
- break;
- case 1:
- {
- if(key[i].state==GPIO_PIN_RESET)
- {
- key[i].step=2;
- sprintf(buf,"%d",i);
- LCD_DisplayStringLine(Line4,(uint8_t*)buf);
- }
- else
- {
- key[i].step=0;
- }
- }
- break;
- case 2:
- {
- if(key[i].state==GPIO_PIN_SET)
- {
- key[i].step=0;
- }
- }
- break;
- }
-
- }
- }
- }
- /* USER CODE END PFP */
- /* USER CODE BEGIN 2 */
- LCD_Init();
- LCD_Clear(Blue);
- LCD_SetBackColor(Blue);
- LCD_SetTextColor(White);
- I2CInit();
- HAL_TIM_Base_Start_IT(&htim1);
-
- /* USER CODE END 2 */
1.如果第一次扫描为高电平,则没有按键
2.如果第一次扫描为低电平,第二次扫描为高电平,那么认为是抖动,不计入成功按键
3.如果两次扫描均为低电平,成功按键,如果按键时间大于700ms,则视为长按,若小于700ms,则视为短按
- /* USER CODE BEGIN PTD */
- char buf[20];
- struct keys{
- int step;
- int state;
- }key[4];
- int a=-1;
- int cnt;
- /* USER CODE END PTD */
- /* USER CODE BEGIN PFP */
- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
- {
- if(htim->Instance==TIM1)
- {
- key[0].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
- key[1].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
- key[2].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
- key[3].state=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
- for(int i=0;i<4;i++)
- {
- switch(key[i].step)
- {
- case 0:
- {
- if(key[i].state==GPIO_PIN_RESET)
- {
- key[i].step=1;
- cnt=0;
- }
- }
- break;
- case 1:
- {
- if(key[i].state==GPIO_PIN_RESET)
- {
- key[i].step=2;
- sprintf(buf,"SINGLE");
- LCD_ClearLine(Line4);
- LCD_DisplayStringLine(Line4,(uint8_t*)buf);
- }
- else
- {
- key[i].step=0;
- }
- }
- break;
- case 2:
- {
- if(key[i].state==GPIO_PIN_RESET)
- {
- if(i==a&&cnt>70)
- {
- sprintf(buf,"%d %d",i,cnt);
- LCD_ClearLine(Line4);
- LCD_DisplayStringLine(Line4,(uint8_t*)buf);
- }
- else
- {
- a=i;
- }
- cnt++;
- }
- else
- {
- key[i].step=0;
- }
- }
- break;
- }
-
- }
- }
- }
- /* USER CODE END PFP */
1.如果第一次能够被视为成功按键,那么计时开始
2.如果两次成功按键的间隔小于700ms,视为双击成功
- /* USER CODE BEGIN PTD */
- char buf[20];
- struct keys{
- int step;
- int state;
- int cnt;
- }key[4];
- int a=-1;
- int cnt;
- /* USER CODE END PTD */
- /* USER CODE BEGIN PFP */
- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
- {
- if(htim->Instance==TIM1)
- {
- key[0].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
- key[1].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
- key[2].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
- key[3].state=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
- for(int i=0;i<4;i++)
- {
- switch(key[i].step)
- {
- case 0:
- {
- if(key[i].state==GPIO_PIN_RESET)
- {
- key[i].step=1;
- }
- }
- break;
- case 1:
- {
- if(key[i].state==GPIO_PIN_RESET)
- {
- sprintf(buf,"SINGLE");
- LCD_ClearLine(Line4);
- LCD_DisplayStringLine(Line4,(uint8_t*)buf);
- if(i==a)
- {
- if(key[i].cnt<70)
- {
- sprintf(buf,"%d %d",i,key[i].cnt);
- LCD_ClearLine(Line4);
- LCD_DisplayStringLine(Line4,(uint8_t*)buf);
- key[i].step=0;
- a=-1;
- }
- key[0].cnt=0;
- key[1].cnt=0;
- key[2].cnt=0;
- key[3].cnt=0;
- }
- else
- {
- a=i;
- }
- key[i].step=2;
- }
- else
- {
- key[i].step=0;
- }
- }
- break;
- case 2:
- {
- if(key[i].state==GPIO_PIN_SET)
- {
- key[i].step=0;
- }
- }
- break;
- }
-
- }
- key[a].cnt++;
- }
- }
- /* USER CODE END PFP */
其实不用写在一个定时器内,那样逻辑会比较复杂。我们可以开两个定时器,一个定时器检测长按,一个定时器检测双击
- /* USER CODE BEGIN PTD */
- char buf[20];
- struct keys{
- int state;
- int step1;
- int step2;
- int double_time;
- }key[4];
- int a=-1,b=-1;
- int cnt;
- /* USER CODE END PTD */
- /* USER CODE BEGIN PFP */
- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
- {
- if(htim->Instance==TIM3)
- {
- key[0].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
- key[1].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
- key[2].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
- key[3].state=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
- for(int i=0;i<4;i++)
- {
- switch(key[i].step2)
- {
- case 0:
- {
- if(key[i].state==GPIO_PIN_RESET)
- {
- key[i].step2=1;
- cnt=0;
- }
- }
- break;
- case 1:
- {
- if(key[i].state==GPIO_PIN_RESET)
- {
- key[i].step2=2;
- /*sprintf(buf,"SINGLE");
- LCD_ClearLine(Line4);
- LCD_DisplayStringLine(Line4,(uint8_t*)buf);*/
- }
- else
- {
- key[i].step2=0;
- }
- }
- break;
- case 2:
- {
- if(key[i].state==GPIO_PIN_RESET)
- {
- if(i==b&&cnt>70)
- {
- sprintf(buf,"LONG:%d %d",i,cnt);
- LCD_ClearLine(Line4);
- LCD_DisplayStringLine(Line4,(uint8_t*)buf);
- }
- else
- {
- b=i;
- }
- cnt++;
- }
- else
- {
- key[i].step2=0;
- }
- }
- break;
- }
- }
- }
- if(htim->Instance==TIM1)
- {
- key[0].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
- key[1].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
- key[2].state=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
- key[3].state=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
- for(int i=0;i<4;i++)
- {
- switch(key[i].step1)
- {
- case 0:
- {
- if(key[i].state==GPIO_PIN_RESET)
- {
- key[i].step1=1;
- }
- }
- break;
- case 1:
- {
- if(key[i].state==GPIO_PIN_RESET)
- {
- sprintf(buf,"SINGLE");
- LCD_ClearLine(Line4);
- LCD_DisplayStringLine(Line4,(uint8_t*)buf);
- if(i==a)
- {
- if(key[i].double_time<70)
- {
- sprintf(buf,"DOUBLE:%d %d",i,key[i].double_time);
- LCD_ClearLine(Line4);
- LCD_DisplayStringLine(Line4,(uint8_t*)buf);
- key[i].step1=0;
- a=-1;
- }
- key[0].double_time=0;
- key[1].double_time=0;
- key[2].double_time=0;
- key[3].double_time=0;
- }
- else
- {
- a=i;
- }
- key[i].step1=2;
- }
- else
- {
- key[i].step1=0;
- }
- }
- break;
- case 2:
- {
- if(key[i].state==GPIO_PIN_SET)
- {
- key[i].step1=0;
- }
- }
- break;
- }
- }
- key[a].double_time++;
- }
- }
- /* USER CODE END PFP */
- /* USER CODE BEGIN 2 */
- LCD_Init();
- LCD_Clear(Blue);
- HAL_TIM_Base_Start_IT(&htim1);
- HAL_TIM_Base_Start_IT(&htim3);
- /* USER CODE END 2 */
我们想获取两个电位器的电压:
这里以PB12为例:
- /* USER CODE BEGIN PTD */
- char buf[20];
- /* USER CODE END PTD */
- /* USER CODE BEGIN PFP */
- double getADC()
- {
- HAL_ADC_Start(&hadc1);
- return HAL_ADC_GetValue(&hadc1)*3.3/4096;
- }
- /* USER CODE END PFP */
- /* USER CODE BEGIN 2 */
- LCD_Init();
- LCD_Clear(Blue);
- LCD_SetBackColor(Blue);
- LCD_SetTextColor(White);
-
- /* USER CODE END 2 */
- /* USER CODE BEGIN 3 */
- sprintf(buf,"%.3lf",getADC());
- LCD_ClearLine(Line4);
- LCD_DisplayStringLine(Line4,(uint8_t*)buf);
- HAL_Delay(1000);
- }
- /* USER CODE END 3 */
转动电位器R38可观察到电压发生明显变化
这里我们发现测量ADC的两个引脚均在ADC1上,所以获取电压会有先后之分
采样时间调长,采样准确些,这里我们看到优先采集PA4,再采集PA3
- /* USER CODE BEGIN PFP */
- void setDAC()
- {
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1);
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_2);
- HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_1,0,1.1*4096/3.3);
- HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_2,0,2.2*4096/3.3);
- }
- double getADC()
- {
- HAL_ADC_Start(&hadc2);
- return HAL_ADC_GetValue(&hadc2)*3.3/4096;
- }
- /* USER CODE END PFP */
- /* USER CODE BEGIN 2 */
- LCD_Init();
- LCD_Clear(Blue);
- LCD_SetBackColor(Blue);
- LCD_SetTextColor(White);
- setDAC();
- /* USER CODE END 2 */
- /* USER CODE BEGIN 3 */
- sprintf(buf,"%.3lf",getADC());
- LCD_ClearLine(Line4);
- LCD_DisplayStringLine(Line4,(uint8_t*)buf);
- HAL_Delay(1);//需要延时
- sprintf(buf,"%.3lf",getADC());
- LCD_ClearLine(Line5);
- LCD_DisplayStringLine(Line5,(uint8_t*)buf);
- HAL_Delay(1000);
- }
- /* USER CODE END 3 */
对于EEPROM读写的函数我们有固定模板:
- uint8_t EEPROM_Read(uint8_t addr)
- {
- I2CStart();
- I2CSendByte(0xa0);
- I2CWaitAck();
- I2CSendByte(addr);
- I2CWaitAck();
-
- I2CStart();
- I2CSendByte(0xa1);
- I2CWaitAck();
- uint8_t temp=I2CReceiveByte();
- I2CWaitAck();
- I2CStop();
- return temp;
- }
- void EEPROM_Write(uint8_t addr,uint8_t info)
- {
- I2CStart();
- I2CSendByte(0xa0);
- I2CWaitAck();
- I2CSendByte(addr);
- I2CWaitAck();
- I2CSendByte(info);
- I2CWaitAck();
- I2CStop();
- }
借用3.2的实验数据进行读写,建议EEPROM不要写在while内,EEPROM的读写是有寿命的,每次读写都要延时一下:
- /* USER CODE BEGIN PTD */
- char buf[20];
- double V1[5],V2[5];
- uint8_t addr,res1,res2,res3,res4;
- /* USER CODE END PTD */
- /* USER CODE BEGIN PFP */
- void setDAC()
- {
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1);
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_2);
- HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_1,0,1.1*4096/3.3);
- HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_2,0,2.2*4096/3.3);
- }
- double getADC()
- {
- HAL_ADC_Start(&hadc2);
- return HAL_ADC_GetValue(&hadc2)*3.3/4096;
- }
- uint8_t EEPROM_Read(uint8_t addr)
- {
- I2CStart();
- I2CSendByte(0xa0);
- I2CWaitAck();
- I2CSendByte(addr);
- I2CWaitAck();
-
- I2CStart();
- I2CSendByte(0xa1);
- I2CWaitAck();
- uint8_t temp=I2CReceiveByte();
- I2CWaitAck();
- I2CStop();
- return temp;
- }
- void EEPROM_Write(uint8_t addr,uint8_t info)
- {
- I2CStart();
- I2CSendByte(0xa0);
- I2CWaitAck();
- I2CSendByte(addr);
- I2CWaitAck();
- I2CSendByte(info);
- I2CWaitAck();
- I2CStop();
- }
- /* USER CODE END PFP *//* USER CODE BEGIN 2 */
- LCD_Init();
- LCD_Clear(Blue);
- LCD_SetBackColor(Blue);
- LCD_SetTextColor(White);
- I2CInit();
- setDAC();
- HAL_Delay(1);
- for(int i=0;i<5;i++)
- {
- V1[i]=getADC();
- HAL_Delay(1);
- V2[i]=getADC();
- HAL_Delay(1);
- }
- for(int i=0;i<5;i++)
- {
- EEPROM_Write(addr++,(uint8_t)V1[i]);
- HAL_Delay(50);
- EEPROM_Write(addr++,(V1[i]-(uint8_t)V1[i])*100);
- HAL_Delay(50);
- EEPROM_Write(addr++,(uint8_t)V2[i]);
- HAL_Delay(50);
- EEPROM_Write(addr++,(V2[i]-(uint8_t)V2[i])*100);
- HAL_Delay(50);
- }
- addr=0;
- for(int i=0;i<5;i++)
- {
- res1=EEPROM_Read(addr++);
- HAL_Delay(50);
- res2=EEPROM_Read(addr++);
- HAL_Delay(50);
- res3=EEPROM_Read(addr++);
- HAL_Delay(50);
- res4=EEPROM_Read(addr++);
- HAL_Delay(50);
- sprintf(buf,"%.2lf %.2lf",res1+(double)res2/100,res3+(double)res4/100);
- LCD_DisplayStringLine(Line4,(uint8_t*)buf);
- }
- /* USER CODE END 2 */
8位范围:0-255
16位范围:0-65535
24位范围:0-16777215
32位范围:0-4294967296
这里以到24位为例
- /* USER CODE BEGIN PTD */
- char buf[20];
- int num[10]={105798,367842,56674,4,256,8917,56565,34343,1025,788};
- uint8_t temp1,temp2,temp3,res1,res2,res3;
- uint8_t addr;
- /* USER CODE END PTD */
- /* USER CODE BEGIN PFP */
- uint8_t EEPROM_Read(uint8_t addr)
- {
- I2CStart();
- I2CSendByte(0xa0);
- I2CWaitAck();
- I2CSendByte(addr);
- I2CWaitAck();
-
- I2CStart();
- I2CSendByte(0xa1);
- I2CWaitAck();
- uint8_t temp=I2CReceiveByte();
- I2CWaitAck();
- I2CStop();
- return temp;
- }
- void EEPROM_Write(uint8_t addr,uint8_t info)
- {
- I2CStart();
- I2CSendByte(0xa0);
- I2CWaitAck();
- I2CSendByte(addr);
- I2CWaitAck();
- I2CSendByte(info);
- I2CWaitAck();
- I2CStop();
- }
- /* USER CODE END PFP */
- /* USER CODE BEGIN 2 */
- LCD_Init();
- LCD_Clear(Blue);
- LCD_SetBackColor(Blue);
- LCD_SetTextColor(White);
- I2CInit();
- for(int i=0;i<10;i++)
- {
- temp1=num[i]&0xFF;
- temp2=((num[i]-temp1)>>8)&0xFF;
- temp3=((num[i]-temp1-(temp2<<8))>>16)&0xFF;
- EEPROM_Write(addr++,temp1);
- HAL_Delay(50);
- EEPROM_Write(addr++,temp2);
- HAL_Delay(50);
- EEPROM_Write(addr++,temp3);
- HAL_Delay(50);
- }
- addr=0;
- for(int i=0;i<10;i++)
- {
- res1=EEPROM_Read(addr++);
- HAL_Delay(50);
- res2=EEPROM_Read(addr++);
- HAL_Delay(50);
- res3=EEPROM_Read(addr++);
- HAL_Delay(50);
- sprintf(buf,"%d",res1+(res2<<8)+(res3<<16));
- LCD_ClearLine(Line4);
- LCD_DisplayStringLine(Line4,(uint8_t*)buf);
- HAL_Delay(1000);
- }
- /* USER CODE END 2 */
去掉上面程序的写的部分,重新烧录即可验证
我们以这道题为例:如果我们将程序烧录到新板时,EEPROM的值是不确定的,而题目要求初次上电就要能读取相关值。所以我们需要判断板子是否是第一次上电,然后做出相关步骤
- if(EEPROM_Read(0xaa)!=1&&EEPROM_Read(0xab)!=1)
- {
- EEPROM_Write(0xaa,1);//我们设定0xaa为是否是第一次上电(新板)的标志位1
- HAL_Delay(20);
- EEPROM_Write(0xab,1);//我们设定0xab为是否是第一次上电(新板)的标志位2
- HAL_Delay(20);
- EEPROM_Write(0xa0,30);
- HAL_Delay(20);
- EEPROM_Write(0xa1,50);
- HAL_Delay(20);
- EEPROM_Write(0xa2,70);
- HAL_Delay(20);
- }
原理:
Rs为单个电阻阻值;RWS为总阻值,与R17串联,两者对VDD电压进行分压,可以通过测量PB14的电压判断可编程电阻的阻值。
MCP4017的默认总阻值为100kΩ ,对应0-127个档位,当寄存器为0时,阻值为0;当寄存器为0x7F时阻值为100kΩ
- void RWrite(uint8_t value)
- {
- I2CStart();
- I2CSendByte(0x5e);
- I2CWaitAck();
- I2CSendByte(value);
- I2CWaitAck();
- I2CStop();
- }
- uint8_t RRead(void)
- {
- uint8_t value;
- I2CStart();
- I2CSendByte(0x5F);
- I2CWaitAck();
-
- value = I2CReceiveByte();
- I2CSendNotAck();
- I2CStop();
- return value;
- }
检测从0-127,PB14的电压
- /* USER CODE BEGIN PFP */
- void RWrite(uint8_t value)
- {
- I2CStart();
- I2CSendByte(0x5e);
- I2CWaitAck();
- I2CSendByte(value);
- I2CWaitAck();
- I2CStop();
- }
- uint8_t RRead(void)
- {
- uint8_t value;
- I2CStart();
- I2CSendByte(0x5F);
- I2CWaitAck();
-
- value = I2CReceiveByte();
- I2CSendNotAck();
- I2CStop();
- return value;
- }
- double getADC()
- {
- HAL_ADC_Start(&hadc1);
- return HAL_ADC_GetValue(&hadc1)*3.3/4096;
- }
- /* USER CODE END PFP */
- /* USER CODE BEGIN WHILE */
- for(int i=0;i<128;i++)
- {
- RWrite(i);
- HAL_Delay(20);
- sprintf(buf,"%d %lf",RRead(),getADC());
- LCD_ClearLine(Line4);
- LCD_DisplayStringLine(Line4,(uint8_t*)buf);
- HAL_Delay(1000);
- }
-
- while (1)
- {
- /* USER CODE END WHILE */
CubeMX默认的引脚是不对的,所以别忘了更改引脚
HAL_UART_Receive:接收不完指定数量的字符不会进行下一步,设置的等待时间一般为无限大
- /* USER CODE BEGIN WHILE */
- HAL_UART_Receive(&huart1,(uint8_t*)buf,5,0xFFFF);//接收不完指定数量的字符不会进行下一步
- HAL_UART_Transmit(&huart1,(uint8_t*)buf,5,0xFFFF);
-
- while (1)
- {
- /* USER CODE END WHILE */
CubeMX别忘打开串口中断
HAL_UART_Receive_IT:不会等待,最多收指定个数,收几个无所谓,但是只有收满了才会进入中断回调函数
下面这个程序如果不写在while里是不会收到除了空白以外的任何字符的,因为只执行一次的话Receive函数已经过了,不会再执行
- while (1)
- {
- /* USER CODE END WHILE */
-
- /* USER CODE BEGIN 3 */
- HAL_UART_Receive_IT(&huart1,(uint8_t*)buf,5);//不会等待,最多收5个,收几个无所谓
- HAL_UART_Transmit_IT(&huart1,(uint8_t*)buf,5);
-
- }
- /* USER CODE END 3 */
- /* USER CODE BEGIN PFP */
- void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
- {
- if(huart->Instance==USART1)
- {
- HAL_UART_Transmit_IT(&huart1,(uint8_t*)buf,5);
- HAL_UART_Receive_IT(&huart1,(uint8_t*)buf,5);
- }
- }
- /* USER CODE END PFP */
- /* USER CODE BEGIN WHILE */
- HAL_UART_Receive_IT(&huart1,(uint8_t*)buf,5);//不会等待,最多收5个,收几个无所谓
-
- while (1)
- {
- /* USER CODE END WHILE */
每个字符串结尾都有\r\n占两个位置,而且如果我们在串口助手里勾选发送新行后,每次发送的字符串后都带\r\n
比如我们想发送时间,指定格式为时:分:秒,我们想从串口收到的字符串中提取到时,分,秒三个信息
这里用到sscanf函数来提取信息
当然也可以根据实际情况单个单个提取,不过会略显复杂
- /* USER CODE BEGIN PTD */
- char buf[20];
- int hour,min,second;
- /* USER CODE END PTD */
- /* USER CODE BEGIN PFP */
- void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
- {
- if(huart->Instance==USART1)
- {
- sscanf(buf,"%d:%d:%d",&hour,&min,&second);
- sprintf(buf,"hour:%d",hour);
- LCD_ClearLine(Line2);
- LCD_ClearLine(Line4);
- LCD_ClearLine(Line6);
- LCD_DisplayStringLine(Line2,(uint8_t*)buf);
- sprintf(buf,"min:%d",min);
- LCD_DisplayStringLine(Line4,(uint8_t*)buf);
- sprintf(buf,"second:%d",second);
- LCD_DisplayStringLine(Line6,(uint8_t*)buf);
- memset(buf,0,sizeof(buf));
- HAL_UART_Receive_IT(&huart1,(uint8_t*)buf,8);
- }
- }
- /* USER CODE END PFP */
- /* USER CODE BEGIN 2 */
- LCD_Init();
- LCD_Clear(Blue);
- LCD_SetBackColor(Blue);
- LCD_SetTextColor(White);
- I2CInit();
-
-
- /* USER CODE END 2 */
-
- /* Infinite loop */
- /* USER CODE BEGIN WHILE */
- HAL_UART_Receive_IT(&huart1,(uint8_t*)buf,8);
- while (1)
- {
- /* USER CODE END WHILE */
打开DMA:
打开中断:
DMA是默认打开中断的
因为DMA是不占用CPU的,我们可以尽量使用DMA来提高效率
这里我们发现收到的数据不完整,然后我们延时一下,这次数据完整
- /* USER CODE BEGIN PTD */
- char buf[20];
- int hour,min,second;
- /* USER CODE END PTD */
- /* USER CODE BEGIN PFP */
- void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
- {
- if(huart->Instance==USART1)
- {
- sscanf(buf,"%d:%d:%d",&hour,&min,&second);
- sprintf(buf,"hour:%d",hour);
- LCD_ClearLine(Line2);
- LCD_ClearLine(Line4);
- LCD_ClearLine(Line6);
- LCD_DisplayStringLine(Line2,(uint8_t*)buf);
- sprintf(buf,"min:%d",min);
- LCD_DisplayStringLine(Line4,(uint8_t*)buf);
- sprintf(buf,"second:%d",second);
- LCD_DisplayStringLine(Line6,(uint8_t*)buf);
- sprintf(buf,"%02d:%02d:%02d",hour,min,second);
- HAL_UART_Transmit_DMA(&huart1,(uint8_t*)buf,8);
- //int t=100000; while(t--);
- LCD_DisplayStringLine(Line8,(uint8_t*)buf);
- memset(buf,0,sizeof(buf));
- HAL_UART_Receive_DMA(&huart1,(uint8_t*)buf,8);
- }
- }
- /* USER CODE END PFP */
- /* USER CODE BEGIN WHILE */
- HAL_UART_Receive_DMA(&huart1,(uint8_t*)buf,8);
- while (1)
- {
- /* USER CODE END WHILE */
然后我们试图将Transmit一句改成IT,然后将USART1的中断优先级调整比DMA高,然后发现仍然可以接收到
但是如果我们全部改成IT,就不可取了,初步分析是中断回调函数的事件过多
所以我们尽量采取DMA的方式收发数据
其中中断服务函数需要到
中找
- /* USER CODE BEGIN WHILE */
- HAL_UART_Receive_DMA(&huart1,(uint8_t*)buf,20);
- __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
- while (1)
- {
- /* USER CODE END WHILE */
- void USART1_IRQHandler(void)
- {
- /* USER CODE BEGIN USART1_IRQn 0 */
- if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)==SET)
- {
- __HAL_UART_CLEAR_IDLEFLAG(&huart1);
- HAL_UART_DMAStop(&huart1);
- len=20-__HAL_DMA_GET_COUNTER(huart1.hdmarx);
- HAL_UART_Transmit_DMA(&huart1,(uint8_t*)buf,len);
- HAL_UART_Receive_DMA(&huart1,(uint8_t*)buf,20);
- }
- /* USER CODE END USART1_IRQn 0 */
- HAL_UART_IRQHandler(&huart1);
- /* USER CODE BEGIN USART1_IRQn 1 */
-
- /* USER CODE END USART1_IRQn 1 */
- }
- while (1)
- {
- /* USER CODE END WHILE */
-
- /* USER CODE BEGIN 3 */
- if(__HAL_TIM_GetCounter(&htim1)==10000)
- {
- HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_8);
- __HAL_TIM_SetCounter(&htim1,0);
- }
-
- }
- /* USER CODE END 3 */
PWM原理如图所示:
- /* USER CODE BEGIN 2 */
- LCD_Init();
- LCD_Clear(Blue);
- LCD_SetBackColor(Blue);
- LCD_SetTextColor(White);
- I2CInit();
- LED_Close();
- HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
- __HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,5000);
- /* USER CODE END 2 */
分析:每次捕捉到上升沿我们就进入一次中断,这时我们就得到了一个信号周期的大小
现在我们已知定时器计一个数的时间,只要我们读取定时器计了多少数,就能通过公式:
信号周期=定时器计一个数的时间*定时器计数值
算出信号周期,进而算出信号频率
占空比的计算可以另外设置一个通道,根据占空比的定义:
占空比=一个周期内高电平的时间/一个周期
测量PA15引脚对应的555信号发生器:
注意:分频系数设置为80比较好,这样记一次数的时间比较短,测量比较精确,而且计数器不容易溢出
注意TIM2CHANNEL1对应的引脚设置成PA15
这里注意打印%的方法
- /* USER CODE BEGIN PFP */
- void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
- {
- if(htim->Instance==TIM2&&htim->Channel==HAL_TIM_ACTIVE_CHANNEL_1)
- {
- cnt=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_1);
- cnt_down=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_2);
- __HAL_TIM_SetCounter(&htim2,0);
- f=10000000/cnt;
- duty=1-(double)cnt_down/(double)cnt;
- }
- }
- /* USER CODE END PFP */
- /* USER CODE BEGIN 2 */
- LCD_Init();
- LCD_Clear(Blue);
- LCD_SetBackColor(Blue);
- LCD_SetTextColor(White);
- I2CInit();
- HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1);
- HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_2);
- /* USER CODE END 2 */
- while (1)
- {
- /* USER CODE END WHILE */
-
- /* USER CODE BEGIN 3 */
- sprintf(buf,"%dHz %.2lf%%",f,duty*100);
- LCD_DisplayStringLine(Line4,(uint8_t*)buf);
- HAL_Delay(1000);
- LCD_ClearLine(Line4);
- }
- /* USER CODE END 3 */
找到板子上能插杜邦线的两个引脚,一个引脚输出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%
- /* USER CODE BEGIN PTD */
- int cnt,cnt_down,f;
- double duty;
- char buf[20];
- int set;
- /* USER CODE END PTD */
- /* USER CODE BEGIN PFP */
- void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
- {
- if(htim->Instance==TIM2&&htim->Channel==HAL_TIM_ACTIVE_CHANNEL_4)
- {
- cnt=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_4);
- cnt_down=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_3);
- __HAL_TIM_SetCounter(&htim2,0);
- f=1000000/cnt;
- duty=1-(double)cnt_down/(double)cnt;
- }
- }
- /* USER CODE END PFP */
- /* USER CODE BEGIN 2 */
- HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_4);
- HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_3);
- HAL_TIM_PWM_Start(&htim15,TIM_CHANNEL_2);
- __HAL_TIM_SetCompare(&htim15,TIM_CHANNEL_2,set);
- LCD_Init();
- LCD_Clear(Blue);
- /* USER CODE END 2 */
- /* USER CODE BEGIN WHILE */
- while (1)
- {
- /* USER CODE END WHILE */
-
- /* USER CODE BEGIN 3 */
- sprintf(buf,"%dHz %.4lf",f,duty);
- LCD_DisplayStringLine(Line2,(uint8_t*)buf);
- HAL_Delay(200);
- }
- /* USER CODE END 3 */
最后测量得到频率为10204Hz,占空比为0.5102,和预估结果大致相同
捕捉到上升沿中断其实跟PWM中断一样,都是上升沿开始时触发中断
我们打开PWM中断,使用PWM中断回调函数
- /* USER CODE BEGIN PTD */
- int cnt,cnt_down,f;
- double duty;
- char buf[20];
- int set;
- /* USER CODE END PTD */
- /* USER CODE BEGIN PFP */
- void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
- {
- if(htim->Instance==TIM15&&htim->Channel==HAL_TIM_ACTIVE_CHANNEL_2)
- {
- cnt=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_4);
- cnt_down=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_3);
- __HAL_TIM_SetCounter(&htim2,0);
- f=1000000/cnt;
- duty=1-(double)cnt_down/(double)cnt;
- }
- }
- /* USER CODE END PFP */
- /* USER CODE BEGIN 2 */
- HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_4);
- HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_3);
- HAL_TIM_PWM_Start_IT(&htim15,TIM_CHANNEL_2);
- __HAL_TIM_SetCompare(&htim15,TIM_CHANNEL_2,set);
- LCD_Init();
- LCD_Clear(Blue);
- /* USER CODE END 2 */
- /* USER CODE BEGIN WHILE */
- while (1)
- {
- /* USER CODE END WHILE */
-
- /* USER CODE BEGIN 3 */
- sprintf(buf,"%dHz %.4lf",f,duty);
- LCD_DisplayStringLine(Line2,(uint8_t*)buf);
- HAL_Delay(200);
- }
- /* USER CODE END 3 */
- /* USER CODE BEGIN PTD */
- char buf[20];
- /* USER CODE END PTD */
- /* USER CODE BEGIN PD */
- RTC_TimeTypeDef TIME;
- RTC_DateTypeDef DATE;
- /* USER CODE END PD */
- /* USER CODE BEGIN WHILE */
- while (1)
- {
- /* USER CODE END WHILE */
-
- /* USER CODE BEGIN 3 */
- HAL_RTC_GetTime(&hrtc,&TIME,RTC_FORMAT_BIN);
- HAL_RTC_GetDate(&hrtc,&DATE,RTC_FORMAT_BIN);
- LCD_ClearLine(Line2);
- LCD_ClearLine(Line4);
- sprintf(buf,"%d-%d-%d",DATE.Year,DATE.Month,DATE.Date);
- LCD_DisplayStringLine(Line2,(uint8_t*)buf);
- sprintf(buf,"%d:%d:%d",TIME.Hours,TIME.Minutes,TIME.Seconds);
- LCD_DisplayStringLine(Line4,(uint8_t*)buf);
- }
- /* USER CODE END 3 */
上面我们已经设置好RTC的频率为750MHz,按照如上系数配置,750K/125/6000=1Hz
1秒发生一次中断,而跟我们设定的闹钟时间无关
- /* USER CODE BEGIN PTD */
- char buf[20];
- /* USER CODE END PTD */
- /* USER CODE BEGIN PD */
- RTC_TimeTypeDef TIME;
- RTC_DateTypeDef DATE;
- /* USER CODE END PD */
- /* USER CODE BEGIN PFP */
- void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
- {
- HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_3);
- }
- /* USER CODE END PFP */
- /* USER CODE BEGIN WHILE */
- while (1)
- {
- /* USER CODE END WHILE */
-
- /* USER CODE BEGIN 3 */
- HAL_RTC_GetTime(&hrtc,&TIME,RTC_FORMAT_BIN);
- HAL_RTC_GetDate(&hrtc,&DATE,RTC_FORMAT_BIN);
- LCD_ClearLine(Line2);
- LCD_ClearLine(Line4);
- sprintf(buf,"%d-%d-%d",DATE.Year,DATE.Month,DATE.Date);
- LCD_DisplayStringLine(Line2,(uint8_t*)buf);
- sprintf(buf,"%d:%d:%d",TIME.Hours,TIME.Minutes,TIME.Seconds);
- LCD_DisplayStringLine(Line4,(uint8_t*)buf);
- }
- /* USER CODE END 3 */
日期时分都不看,只看秒,如果闹钟设定的秒跟当前时间一样,则进入中断,自己需要编写的其它代码跟实验2一致
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。