led模块:
查看原理图可知,该led模块由PD2所控制,当PD2为高电平时,led模块才能够启用,在这个时候输入是有效的;当为低电平时,led模块会锁住当前PC口的值。所以在你要控制led灯时,就需要操控PD2。
点击pc8
选择输出模式
同样的操作配置好其他所有PC口和PD2口,如图:
这个user label 是自己定义的名字,STM32CubeMX会根据你定义的名字来宏定义这些端口。
void led_off(void)
{
HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(LED3_GPIO_Port,LED3_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(LED4_GPIO_Port,LED4_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(LED5_GPIO_Port,LED5_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(LED6_GPIO_Port,LED6_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(LED7_GPIO_Port,LED7_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(LED8_GPIO_Port,LED8_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
void led_control(void)
{
if(dis_flag == 0){
HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_RESET);
}
else{
HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_SET);
}
if(led_flag == 1 && cishu != 0){
HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,GPIO_PIN_RESET);
}
else{
HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,GPIO_PIN_SET);
}
if(lock == 1){
HAL_GPIO_WritePin(LED3_GPIO_Port,LED3_Pin,GPIO_PIN_RESET);
}
else{
HAL_GPIO_WritePin(LED3_GPIO_Port,LED3_Pin,GPIO_PIN_SET);
}
HAL_GPIO_WritePin(LED4_GPIO_Port,LED4_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(LED5_GPIO_Port,LED5_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(LED6_GPIO_Port,LED6_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(LED7_GPIO_Port,LED7_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(LED8_GPIO_Port,LED8_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
需要注意的是在操作led时要先写好端口的值,然后操控开关PD2,这一步骤必须连续,否则回合lcd显示屏模块冲突,因为他们都是用的pc口。
led每个都有每个的要求比如0.1毫秒的闪烁就用定时器来操控,十分的简单。
pwm模块:
选择定时器2通道口2
将通道二选择为pwm输出模式我的主频是80MHZ 就是 80 000 000HZ
题目说用PA1来实现pwm输出。重要的设置点是分频系数和重装载的值。主频除以(分频系数和重装载的值)后要等于
Pwm输出的频率,占空比是指对于高电平的时间和重装载值的比例。重装载值是200,占空比是50%的话就得填100,这里填了进入程序还是会改的,占空比随便是指就行
TIM_OC_InitTypeDef sConfigOC = {0};
//在主程序中初始化加入:
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);//启动pwm
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 100;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
//复制的tim.c文件中的
void pwm_deal(void)
{
if(reload_flag == 1){ //处于频率均匀变化期间设置重装载值
reload_flag = 0;
htim2.Init.Period = reload - 1; //重装载值设置
if (HAL_TIM_PWM_Init(&htim2) != HAL_OK) //设置
{
Error_Handler();
}
}
if((int)P!=(int)old_P && lock == 0){
old_P = P;
sConfigOC.Pulse = (int)((P/100.0)*reload - 1); //占空比设置
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2) != HAL_OK) //设置
{
Error_Handler();
}
}
}
还有重装载是指不要一直设置。那样会到时平时测量有误,没事的时候就不要一直设置了。占空比设置的值改成int。
输入捕获:
将PA7设置为TIM17的通道1。选择为输入捕获模式。主频设置为 1MHZ这里很重要影响到后续计算。不要忘了打开定时器中断。
// 定时器的回调函数
//主函数内
HAL_TIM_IC_Start_IT(&htim17,TIM_CHANNEL_1);
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
// 保存TIMx_CCR的值
uint32_t cclValue = 0;
// 定时器3时执行该段
if(htim->Instance == TIM17)
{
cclValue = __HAL_TIM_GET_COUNTER(&htim17);
__HAL_TIM_SetCounter(&htim17, 0);
f = 1000000 / cclValue;
HAL_TIM_IC_Start_IT(&htim17, TIM_CHANNEL_1);
}
}
定时器中断触发后会调用定时器中断函数。每次进入中断后会读取计数器的值,每记一次都代表了pwm的一个周期。在记完后,用主频频率除以计数值就可以算出被测量频率的值。当然每次进入中断后都要清空计数器的值和重新启动输入捕获装置。
测量出来的频率要进行数据处理,得到速度V.
if(dis_flag != 1){
V = f * 2 * 3.14 * R /(K * 100.0);
old_V = V;
if(reload == 100 ){
if(MH<V){
if(max_cishu >= 20){
MH = V;
max_cishu = 0;
}
}
}
else if(reload == 200){
if(ML>V){
if(max_cishu >= 20){
ML = V;
max_cishu = 0;
}
}
}
if((int)old_V != (int)V && (reload == 100 || reload == 200)){
max_cishu = 0;
}
}
adc模块:
这里要选择两个,分别就是这两个,点击PB15选两次就可以了。
记得来到adc2里面激活15输入口,其他的都不用改。
void adc(void)
{
P = getadc(&hadc2);
P *= 37.5;
if(P < 10.0){
P = 10.0;
}
else if(P > 85.0){
P = 85.0;
}
if(old_P_flag == 1){
old_P_flag = 0;
old_P = P;
}
}
double getadc(ADC_HandleTypeDef *pin)
{
unsigned int value = 0,i = 0;
HAL_ADC_Start(pin);
for(i=0;i<10;++i)
{
HAL_ADC_PollForConversion(pin,10);
value += HAL_ADC_GetValue(pin);
}
return value/10*3.3/4096;
}
连续取十次取平均值。因为adc的值是12位的,也就是2^12=4096,取出来的值和他计算出比例,最终算出之后的电压值。按照题目给出的比例计算出响应的占空比值。放到pwm里面设置。
按键模块
利用状态机来做判断并且消抖。题目还要求有长按设计,改一改结构体就好了,计时功能的话就用定时器,我是喜欢用0.1S的定时器每次,记数超过20就是按了2s。按键结构体最好是每个都搞一个长按标志和长按计时的数。这里我只改了一个。代码如下,第二个状态就是用来消抖的。第三个状态松开时就判断按下。
key.h
#ifndef __KEY_H
#define __KEY_H
struct keys
{
unsigned char judge_sta; //几次状态转换判断
char key_sta; //扫描端口时用来存放端口值
char single_flag; //单词按下
char long_single_flag; //多次按下
};
void key_scan(void);
#endif
key.c
#include "key.h"
#include "main.h"
unsigned char long_flag = 0;
int long_key_cishu = 0;
struct keys key[4] = {0,0,0,0};
//只含单击
void key_scan(void)
{
key[0].key_sta = HAL_GPIO_ReadPin(key1_GPIO_Port,key1_Pin);
key[1].key_sta = HAL_GPIO_ReadPin(key2_GPIO_Port,key2_Pin);
key[2].key_sta = HAL_GPIO_ReadPin(key3_GPIO_Port,key3_Pin);
key[3].key_sta = HAL_GPIO_ReadPin(key4_GPIO_Port,key4_Pin);
for(int i=0; i < 3; i++){
switch(key[i].judge_sta){
case 0:
if(key[i].key_sta == 0){
key[i].judge_sta = 1;
}
break;
case 1:
if(key[i].key_sta == 0){
key[i].judge_sta = 2;
}
else{
key[i].judge_sta = 0;
}
break;
case 2:
if(key[i].key_sta == 1){
key[i].judge_sta = 0;
key[i].single_flag = 1;
}
break;
}
}
switch(key[3].judge_sta){
case 0:
if(key[3].key_sta == 0){
key[3].judge_sta = 1;
}
break;
case 1:
if(key[3].key_sta == 0){
key[3].judge_sta = 2;
long_flag = 1;
}
else{
key[3].judge_sta = 0;
}
break;
case 2:
if(key[3].key_sta == 1 ){
key[3].judge_sta = 0;
if(long_key_cishu >= 20){
key[3].long_single_flag = 1;
}
else{
key[3].single_flag = 1;
}
long_flag = 0;
long_key_cishu = 0;
}
break;
}
}
main.h
void deal_key(void)
{
if(dis_flag == 1 && qh_flag == 1){
R_K = 1;
qh_flag = 0;
}
else if(dis_flag != 1 && qh_flag == 0){
qh_flag = 1;
}
if(key[0].single_flag == 1){
dis_flag = (dis_flag + 1) % 3;
key[0].single_flag = 0;
}
else if(key[1].single_flag == 1){
switch(dis_flag){
case 0:if(cishu==0){pwm_flag ^= 1;N+=1;cishu=50;}break;
case 1: R_K ^= 1;break;
default: break;
}
key[1].single_flag = 0;
}
else if(key[2].single_flag == 1){
if(dis_flag == 1){
switch(R_K){
case 0:if(K == 10){K=1;break;} K++;break;
case 1:if(R == 10){R=1;break;} R++;break;
}
}
key[2].single_flag = 0;
}
else if(key[3].single_flag == 1){
if(dis_flag == 1){
switch(R_K){
case 0:if(K == 1){K=10;break;} K--;break;
case 1:if(R == 1){R=10;break;} R--;break;
}
}
key[3].single_flag = 0;
}
else if(key[3].long_single_flag == 1){
if(dis_flag == 0){
lock^=1;
key[3].long_single_flag = 0;
}
}
}
//扫描就交给中断了
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM7){
key_scan();
}
}
lcd模块
很简单就是用了个0.1s的定时器通过标志位来在while里显示没什么可说的。
void display(void)
{
if(lcd_flag == 1){
if(dis_flag == 0){
sprintf((char *)lcd," DATA ");
LCD_DisplayStringLine(Line2,lcd);
if(reload == 100){
sprintf((char *)lcd2," M=H ");
}
else if(reload == 200){
sprintf((char *)lcd2," M=L ");
}
LCD_DisplayStringLine(Line4,lcd2);
sprintf((char *)lcd," P=%.0f%% ",P);
LCD_DisplayStringLine(Line5,lcd);
sprintf((char *)lcd," V=%.1f %d ",V,f);
LCD_DisplayStringLine(Line6,lcd);
}
else if(dis_flag == 1){
sprintf((char *)lcd," PARA ");
LCD_DisplayStringLine(Line2,lcd);
sprintf((char *)lcd," R=%d ",R);
LCD_DisplayStringLine(Line4,lcd);
sprintf((char *)lcd," K=%d ",K);
LCD_DisplayStringLine(Line5,lcd);
sprintf((char *)lcd," ");
LCD_DisplayStringLine(Line6,lcd);
}
else{
sprintf((char *)lcd," RECD ");
LCD_DisplayStringLine(Line2,lcd);
sprintf((char *)lcd," N=%d ",N);
LCD_DisplayStringLine(Line4,lcd);
sprintf((char *)lcd," MH=%.1f ",MH);
LCD_DisplayStringLine(Line5,lcd);
sprintf((char *)lcd," ML=%.1f ",ML);
LCD_DisplayStringLine(Line6,lcd);
}
lcd_flag = 0;
}
}