注册网站的免费网址com,宣城网站 建设,专业网站建设公司用织梦吗?,公司网站域名cn和com目录
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路径#xff0c;放置芯片包
2 GPIO
2.1 实验1#xff1a;LED1-LED8循环亮灭
编辑
2.2 实验2#xff1a…目录
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 实验1LED1-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 实验1EEPROM的读写浮点数的处理
4.2 实验2大位数读取
4.3 实验3EEPROM掉电不丢失
4.4 实验4第一次上电问题 4.5 实验5MCP4017可编程电阻
5 UART/USART
5.1 实验1轮询收发 5.2 实验2中断收发
5.3 实验3中断回调函数
5.4 字符串问题注意 5.5 实验4发送指定格式的字符串并从字符串中提取指定信息
5.6 实验5DMA及几种收发方式的分析
5.7 实验6串口的不定长收发DMA空闲中断
6 TIM
6.1 实验1延时
6.2 实验2PWM输出控制蜂鸣器
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 实验1LED1-LED8循环亮灭 在最小系统原理图中找到LED1-LED8对应的引脚是PC8-PC15那么我们在CubeMX中将这几个引脚配置成GPIO_Output即可
while内的代码 HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_SET);//常用函数1HAL_Delay(100);//常用函数2HAL_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);
2.2 实验2按键控制LED亮灭检测电平法 参考按键的引脚同时别忘了把按键对应的引脚调整为上拉输入起始高电平
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);}
2.3 实验3按键控制LED亮灭外部中断法
找到按键对应引脚 打开外部中断 /* USER CODE BEGIN PFP */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_PIN)
{if(GPIO_PINGPIO_PIN_0){HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_8);}
}
/* USER CODE END PFP */ 2.4 实验4蜂鸣器 当PB3为高电平时二极管断开所以蜂鸣器路通 当PB3为低电平时二极管导通所以蜂鸣器路短路
只要配置好PB3的GPIO就能轻松使用这里不再用代码解释
2.5 实验5按键消抖
按键按下和放下的过程中会出现抖动进而出现高低电平的交替我们通过扫描两次按键的情况来具体判断情况
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-InstanceTIM1){key[0].stateHAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);key[1].stateHAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);key[2].stateHAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);key[3].stateHAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);for(int i0;i4;i){switch(key[i].step){case 0:{if(key[i].stateGPIO_PIN_RESET){key[i].step1;}}break;case 1:{if(key[i].stateGPIO_PIN_RESET){key[i].step2;sprintf(buf,%d,i);LCD_DisplayStringLine(Line4,(uint8_t*)buf);}else{key[i].step0;}}break;case 2:{if(key[i].stateGPIO_PIN_SET){key[i].step0;}}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 */
2.6 实验6长按短按
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-InstanceTIM1){key[0].stateHAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);key[1].stateHAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);key[2].stateHAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);key[3].stateHAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);for(int i0;i4;i){switch(key[i].step){case 0:{if(key[i].stateGPIO_PIN_RESET){key[i].step1;cnt0;}}break;case 1:{if(key[i].stateGPIO_PIN_RESET){key[i].step2;sprintf(buf,SINGLE);LCD_ClearLine(Line4);LCD_DisplayStringLine(Line4,(uint8_t*)buf);}else{key[i].step0;}}break;case 2:{if(key[i].stateGPIO_PIN_RESET){if(iacnt70){sprintf(buf,%d %d,i,cnt);LCD_ClearLine(Line4);LCD_DisplayStringLine(Line4,(uint8_t*)buf);}else{ai;}cnt;}else{key[i].step0;}}break;}}}
}
/* USER CODE END PFP */
2.7 实验7双击
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-InstanceTIM1){key[0].stateHAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);key[1].stateHAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);key[2].stateHAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);key[3].stateHAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);for(int i0;i4;i){switch(key[i].step){case 0:{if(key[i].stateGPIO_PIN_RESET){key[i].step1;}}break;case 1:{if(key[i].stateGPIO_PIN_RESET){sprintf(buf,SINGLE);LCD_ClearLine(Line4);LCD_DisplayStringLine(Line4,(uint8_t*)buf); if(ia){if(key[i].cnt70){sprintf(buf,%d %d,i,key[i].cnt);LCD_ClearLine(Line4);LCD_DisplayStringLine(Line4,(uint8_t*)buf);key[i].step0;a-1;}key[0].cnt0;key[1].cnt0;key[2].cnt0;key[3].cnt0;}else{ai;}key[i].step2;}else{key[i].step0;}}break;case 2:{if(key[i].stateGPIO_PIN_SET){key[i].step0;}}break;}}key[a].cnt;}
}
/* USER CODE END PFP */2.8 实验8 长按双击综合
其实不用写在一个定时器内那样逻辑会比较复杂。我们可以开两个定时器一个定时器检测长按一个定时器检测双击
/* 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-InstanceTIM3){key[0].stateHAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);key[1].stateHAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);key[2].stateHAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);key[3].stateHAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);for(int i0;i4;i){switch(key[i].step2){case 0:{if(key[i].stateGPIO_PIN_RESET){key[i].step21;cnt0;}}break;case 1:{if(key[i].stateGPIO_PIN_RESET){key[i].step22;/*sprintf(buf,SINGLE);LCD_ClearLine(Line4);LCD_DisplayStringLine(Line4,(uint8_t*)buf);*/}else{key[i].step20;}}break;case 2:{if(key[i].stateGPIO_PIN_RESET){if(ibcnt70){sprintf(buf,LONG:%d %d,i,cnt);LCD_ClearLine(Line4);LCD_DisplayStringLine(Line4,(uint8_t*)buf);}else{bi;}cnt;}else{key[i].step20;}}break;}}}if(htim-InstanceTIM1){key[0].stateHAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);key[1].stateHAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);key[2].stateHAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);key[3].stateHAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);for(int i0;i4;i){switch(key[i].step1){case 0:{if(key[i].stateGPIO_PIN_RESET){key[i].step11;}}break;case 1:{if(key[i].stateGPIO_PIN_RESET){sprintf(buf,SINGLE);LCD_ClearLine(Line4);LCD_DisplayStringLine(Line4,(uint8_t*)buf);if(ia){if(key[i].double_time70){sprintf(buf,DOUBLE:%d %d,i,key[i].double_time);LCD_ClearLine(Line4);LCD_DisplayStringLine(Line4,(uint8_t*)buf);key[i].step10;a-1;}key[0].double_time0;key[1].double_time0;key[2].double_time0;key[3].double_time0;}else{ai;}key[i].step12;}else{key[i].step10;}}break;case 2:{if(key[i].stateGPIO_PIN_SET){key[i].step10;}}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 */3 ADC/DAC
3.1 实验1获取电位器引脚的电压
我们想获取两个电位器的电压 这里以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可观察到电压发生明显变化 3.2 实验2设定双引脚电压并读取 这里我们发现测量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 */
4 I2C EEPROM
4.1 实验1EEPROM的读写浮点数的处理
对于EEPROM读写的函数我们有固定模板
uint8_t EEPROM_Read(uint8_t addr)
{I2CStart();I2CSendByte(0xa0);I2CWaitAck();I2CSendByte(addr);I2CWaitAck();I2CStart();I2CSendByte(0xa1);I2CWaitAck();uint8_t tempI2CReceiveByte();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 tempI2CReceiveByte();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 i0;i5;i){V1[i]getADC();HAL_Delay(1);V2[i]getADC();HAL_Delay(1);}for(int i0;i5;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);}addr0;for(int i0;i5;i){res1EEPROM_Read(addr);HAL_Delay(50);res2EEPROM_Read(addr);HAL_Delay(50);res3EEPROM_Read(addr);HAL_Delay(50);res4EEPROM_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 */
4.2 实验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 tempI2CReceiveByte();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 i0;i10;i){temp1num[i]0xFF;temp2((num[i]-temp1)8)0xFF;temp3((num[i]-temp1-(temp28))16)0xFF;EEPROM_Write(addr,temp1);HAL_Delay(50);EEPROM_Write(addr,temp2);HAL_Delay(50);EEPROM_Write(addr,temp3);HAL_Delay(50);}addr0;for(int i0;i10;i){res1EEPROM_Read(addr);HAL_Delay(50);res2EEPROM_Read(addr);HAL_Delay(50);res3EEPROM_Read(addr);HAL_Delay(50);sprintf(buf,%d,res1(res28)(res316));LCD_ClearLine(Line4);LCD_DisplayStringLine(Line4,(uint8_t*)buf);HAL_Delay(1000);}/* USER CODE END 2 */4.3 实验3EEPROM掉电不丢失
去掉上面程序的写的部分重新烧录即可验证
4.4 实验4第一次上电问题
我们以这道题为例如果我们将程序烧录到新板时EEPROM的值是不确定的而题目要求初次上电就要能读取相关值。所以我们需要判断板子是否是第一次上电然后做出相关步骤 if(EEPROM_Read(0xaa)!1EEPROM_Read(0xab)!1)
{EEPROM_Write(0xaa,1);//我们设定0xaa为是否是第一次上电新板的标志位1HAL_Delay(20);EEPROM_Write(0xab,1);//我们设定0xab为是否是第一次上电新板的标志位2HAL_Delay(20);EEPROM_Write(0xa0,30);HAL_Delay(20);EEPROM_Write(0xa1,50);HAL_Delay(20);EEPROM_Write(0xa2,70);HAL_Delay(20);
} 4.5 实验5MCP4017可编程电阻
原理 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-127PB14的电压
/* 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 i0;i128;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 */
5 UART/USART CubeMX默认的引脚是不对的所以别忘了更改引脚
5.1 实验1轮询收发
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 */5.2 实验2中断收发
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 */
5.3 实验3中断回调函数
/* USER CODE BEGIN PFP */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if(huart-InstanceUSART1){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 */
5.4 字符串问题注意
每个字符串结尾都有\r\n占两个位置而且如果我们在串口助手里勾选发送新行后每次发送的字符串后都带\r\n 5.5 实验4发送指定格式的字符串并从字符串中提取指定信息
比如我们想发送时间指定格式为时分秒我们想从串口收到的字符串中提取到时分秒三个信息
这里用到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-InstanceUSART1){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 */
5.6 实验5DMA及几种收发方式的分析
打开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-InstanceUSART1){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 t100000; 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的方式收发数据
5.7 实验6串口的不定长收发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);len20-__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 */
}
6 TIM
6.1 实验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 */
6.2 实验2PWM输出控制蜂鸣器
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 */
6.3 实验3检测555信号发生器信号频率和占空比 分析每次捕捉到上升沿我们就进入一次中断这时我们就得到了一个信号周期的大小
现在我们已知定时器计一个数的时间只要我们读取定时器计了多少数就能通过公式
信号周期定时器计一个数的时间*定时器计数值
算出信号周期进而算出信号频率
占空比的计算可以另外设置一个通道根据占空比的定义
占空比一个周期内高电平的时间/一个周期
测量PA15引脚对应的555信号发生器
注意分频系数设置为80比较好这样记一次数的时间比较短测量比较精确而且计数器不容易溢出 注意TIM2CHANNEL1对应的引脚设置成PA15
这里注意打印%的方法
/* USER CODE BEGIN PFP */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{if(htim-InstanceTIM2htim-ChannelHAL_TIM_ACTIVE_CHANNEL_1){cntHAL_TIM_ReadCapturedValue(htim2,TIM_CHANNEL_1);cnt_downHAL_TIM_ReadCapturedValue(htim2,TIM_CHANNEL_2);__HAL_TIM_SetCounter(htim2,0);f10000000/cnt;duty1-(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 */ 6.4 实验4检测自己输出的PWM频率和占空比上升沿中断
找到板子上能插杜邦线的两个引脚一个引脚输出PWM另一个引脚测量输入的PWM有关性质
我选择了PB15和PB11两个引脚PB15用来产生PWM波PB11用来测量PWM的有关性质 PB15:
设置分频系数为8计数器最大值为1000-1所以PWM的频率为
80MHz/8/100010000Hz PB11:
分频系数为80-1所以计数频率1/计一个数的时间为
80MHz/801MHz 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-InstanceTIM2htim-ChannelHAL_TIM_ACTIVE_CHANNEL_4){cntHAL_TIM_ReadCapturedValue(htim2,TIM_CHANNEL_4);cnt_downHAL_TIM_ReadCapturedValue(htim2,TIM_CHANNEL_3);__HAL_TIM_SetCounter(htim2,0);f1000000/cnt;duty1-(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和预估结果大致相同
6.5 实验5检测自己输出的PWM频率和占空比PWM中断 捕捉到上升沿中断其实跟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-InstanceTIM15htim-ChannelHAL_TIM_ACTIVE_CHANNEL_2){cntHAL_TIM_ReadCapturedValue(htim2,TIM_CHANNEL_4);cnt_downHAL_TIM_ReadCapturedValue(htim2,TIM_CHANNEL_3);__HAL_TIM_SetCounter(htim2,0);f1000000/cnt;duty1-(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 */
7 RTC
7.1 实验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 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 */
7.2 实验2秒中断
上面我们已经设置好RTC的频率为750MHz按照如上系数配置750K/125/60001Hz
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 */7.3 实验3闹钟中断
日期时分都不看只看秒如果闹钟设定的秒跟当前时间一样则进入中断自己需要编写的其它代码跟实验2一致