The first time, I feel unpressed

作者分享了第一次轻松加班的经历,反思从学生到职场人的转变,并表达了对上海生活的某些不解,如公交车不让座的现象。此外,还提到了对过去学习JAVA时光的怀念及对朋友的思念。
   周六,来加班,但好像没多少人来,只有四个人在。老大没来,顾问没来,呵呵。
   这是第一次,加班还睡到自然醒,这是第一次,加班还觉得没有压力,好多的第一次,这样的感觉挺好的,可以研究一些自己感兴趣的东西。
   这次的实习,似乎已经把我的学生生涯彻底结束了,现在每天都觉得自己是在上班而不是在实习。只是还没有会被淘汰的压力罢了。这样的心态也许更好吧,假如有一天,自己真的被“DONE”掉了,也不至于那么在意。

   上海的生活,让人觉得很多时候都不理解,今天的公车,有一个老太太站了好久,竟然没有人让座,有了空位,大家还是抢着坐,太不能理解了,在这里,似乎还真的没怎么见过让座的事发生,站一会能怎么着了,怎么让座在这里竟成了怪现象。讨厌这样的情况,I hate that.

    FORM,报表,似乎JAVA已经离我有一段距离了,以至于昨天看到JAVA代码都有想哭的冲动。还记得当初是怎么喜欢上这个语言的,它承载着我们四个人的友情,曾经的生活。那么熟悉,也许回不去了,可是记忆永远都是美好的。好怀念当时努力学习的时候,现在,不知道到什么时候才能再一次开始写JAVA代码,呵呵。

    有一年没看到妹妹了,好想她,想她傻乎乎的样子,哈哈。大家说她比我更像是一个姐姐,GOD,从小就是一个被照顾的人,现在一个人在外面,有的时候还真的不习惯。I miss her so much.

    eleven , vanessa, 88250. 好难得,大家竟都在优快云上有共同的爱好与默契,大三的生活把我们的友情连在了一起,以前是,现在是,以后也是。开心的,不开心的,都过去了,现在想见一面都不容易了,唉。。。。。。。

    At last, bless everybody happy.
   
   
#include "stm32f10x.h" // Device header #include "Key.h" #define KEY_PRESSED 1 #define KEY_UNPRESSED 0 #define KEY_DEBOUNCE_TIME 20 uint8_t Key_Flag[KEY_COUNT]; void Key_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14 | GPIO_Pin_15; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); } uint8_t Key_GetState(uint8_t n) { if (n == KEY_1) { if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12) == 0) { return KEY_PRESSED; } } else if (n == KEY_2) { if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13) == 0) { return KEY_PRESSED; } } else if (n == KEY_3) { if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 1) { return KEY_PRESSED; } } else if (n == KEY_4) { if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_15) == 1) { return KEY_PRESSED; } } return KEY_UNPRESSED; } uint8_t Key_Check(uint8_t n, uint8_t Flag) { if (Key_Flag[n] & Flag) { if (Flag != KEY_HOLD) { Key_Flag[n] &= ~Flag; } return 1; } return 0; } // 按键扫描定时器中断处理(10ms调用一次) void Key_Tick(void) { static uint8_t Count; static uint8_t CurrState[KEY_COUNT], PrevState[KEY_COUNT]; static uint8_t State[KEY_COUNT]; // 0:等待按下 1:消抖 2:等待释放 static uint16_t DebounceTime[KEY_COUNT]; Count++; if (Count >= 2) // 20ms扫描一次 { Count = 0; for (uint8_t i = 0; i < KEY_COUNT; i++) { PrevState[i] = CurrState[i]; CurrState[i] = Key_GetState(i); switch (State[i]) { case 0: // 等待按下 if (CurrState[i] == KEY_PRESSED) { DebounceTime[i] = KEY_DEBOUNCE_TIME; State[i] = 1; } break; case 1: // 消抖检测 if (CurrState[i] != KEY_PRESSED) { State[i] = 0; } else if (DebounceTime[i] == 0) { State[i] = 2; } else { DebounceTime[i]--; } break; case 2: // 等待释放 if (CurrState[i] == KEY_UNPRESSED) { Key_Flag[i] |= KEY_SINGLE; State[i] = 0; } break; } } } }结合上个代码检查是否出错
09-25
#include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "Key.h" #include "Timer.h" #include "LED.h" #include "PWM.h" uint8_t LED1Mode=0; uint8_t LED2Mode=0; int main(void) { OLED_Init(); Key_Init(); Timer_Init(); LED_Init(); while(1) { if(Key_Check(0,KEY_SINGLE)||Key_Check(0,KEY_REPEAT)) { LED1Mode++; LED1Mode%=4; LED1_SetMode(LED1Mode); } if(Key_Check(1,KEY_SINGLE)) { LED2Mode++; LED2Mode%=2; LED2_SetMode(LED2Mode); } } } void TIM2_IRQHandler(void) { if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET) { Key_Tick(); LED_Tick(); TIM_ClearITPendingBit(TIM2,TIM_IT_Update); } } #include "stm32f10x.h" // Device header #include "Key.h" #define KEY_PRESSED 1 #define KEY_UNPRESSED 0 #define KEY_COUNT 4 uint8_t Key_Flag[KEY_COUNT]; void Key_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_11; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD; GPIO_InitStructure.GPIO_Pin=GPIO_Pin_13|GPIO_Pin_15; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO_InitStructure); } uint8_t Key_GetState(uint8_t n)//检测按了哪个按键 { if(n==0) { if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0) { return KEY_PRESSED; } } else if(n==1) { if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)==0) { return KEY_PRESSED; } } else if(n==2) { if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)==1) { return KEY_PRESSED; } } else if(n==3) { if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_15)==1) { return KEY_PRESSED; } } return KEY_UNPRESSED; } uint8_t Key_Check(uint8_t n,uint8_t Flag)//检查是哪种按键 { if(Key_Flag[n]&Flag) { if(Flag!=0x01) { Key_Flag[n]&=~Flag; } return 1; } return 0; } void Key_Tick(void)//对Key_Flag[i]进行赋值,将里面的不同数与按键对应功能进行匹配;对i一次次遍历,实现对按键绑定 { static uint8_t S[KEY_COUNT]; static uint8_t Count,i; static uint8_t CurrState[KEY_COUNT],PrevState[KEY_COUNT]; static uint16_t Time[KEY_COUNT]; for(i=0;i<KEY_COUNT;i++) { if(Time[i]>0) { Time[i]--; } } Count ++; if(Count>=20) { Count=0; for(i=0;i<KEY_COUNT;i++) { PrevState[i]=CurrState[i]; CurrState[i]=Key_GetState(i); if(CurrState[i]==KEY_PRESSED) { Key_Flag[i]|=0x01;//HOLD } else { Key_Flag[i]&=~0x01; } if(CurrState[i]==KEY_PRESSED&&PrevState[i]==KEY_UNPRESSED) { Key_Flag[i]|=0x02;//DOWN } if(CurrState[i]==KEY_UNPRESSED&&PrevState[i]==KEY_PRESSED) { Key_Flag[i]|=0x04;//UP } if(S[i]==0) { if(CurrState[i]==KEY_PRESSED) { Time[i]=2000;//长按检测 S[i]=1; } } else if(S[i]==1) { if(CurrState[i]==KEY_UNPRESSED) { Time[i]=200;//双击时间 S[i]=2; } else if(Time[i]==0) { Time[i]=100; Key_Flag[i]|=KEY_LONG; S[i]=4; } } else if(S[i]==2) { if(CurrState[i]==KEY_PRESSED) { Key_Flag[i]|=KEY_DOUBLE; S[i]=3; } else if(Time[i]==0) { Key_Flag[i]|=KEY_SINGLE; S[i]=0; } } else if(S[i]==3) { if(CurrState[i]==KEY_UNPRESSED) { S[i]=0; } } else if(S[i]==4) { if(CurrState[i]==KEY_UNPRESSED) { S[i]=0; } else if(Time[i]==0) { Time[i]=100; Key_Flag[i]|=KEY_REPEAT; S[i]=4; } } } } } #include "stm32f10x.h" // Device header void Timer_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); TIM_InternalClockConfig(TIM2); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period=1000-1; TIM_TimeBaseInitStructure.TIM_Prescaler=72-1; TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0; TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure); TIM_ClearFlag(TIM2,TIM_FLAG_Update); TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; NVIC_InitStructure.NVIC_IRQChannelSubPriority=1; NVIC_Init(&NVIC_InitStructure); TIM_Cmd(TIM2,ENABLE); } uint16_t Timer_GetCounter(void) { return TIM_GetCounter(TIM2); } #include "stm32f10x.h" // Device header #include "LED.h" #include "PWM.h" uint8_t LED1_Mode; uint16_t LED1_Count; uint8_t LED2_Mode; uint16_t LED2_Count; uint8_t i; void LED_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2); } void LED1_SetMode(uint8_t Mode) { LED1_Mode=Mode; } void LED2_SetMode(uint8_t Mode) { LED2_Mode=Mode; } void LED1_ON(void) { GPIO_ResetBits(GPIOA,GPIO_Pin_1); } void LED1_OFF(void) { GPIO_SetBits(GPIOA,GPIO_Pin_1); } void LED2_ON(void) { GPIO_ResetBits(GPIOA,GPIO_Pin_2); } void LED2_OFF(void) { GPIO_SetBits(GPIOA,GPIO_Pin_2); } void LED_Tick(void) { uint16_t pulse=0; if(LED1_Mode==0) { LED1_OFF(); } else if(LED1_Mode==1) { LED1_ON(); } else if(LED1_Mode==2) { pulse++; pulse%=1000; PWM_SetCompare1(pulse); } else if(LED1_Mode==3) { pulse--; pulse%=1000; PWM_SetCompare1(pulse); } else if(LED1_Mode==4) { LED1_Count++; LED1_Count%=1000; if(LED1_Count<100) { LED1_ON(); } else { LED1_OFF(); } } if(LED2_Mode==0) { LED2_OFF(); } else if(LED2_Mode==1) { LED2_ON(); } else if(LED2_Mode==2) { LED2_Count++; LED2_Count%=1000; if(LED2_Count<500) { LED2_ON(); } else { LED2_OFF(); } } else if(LED2_Mode==3) { LED2_Count++; LED2_Count%=100; if(LED2_Count<50) { LED2_ON(); } else { LED2_OFF(); } } else if(LED2_Mode==4) { LED2_Count++; LED2_Count%=1000; if(LED2_Count<100) { LED2_ON(); } else { LED2_OFF(); } } } void LED1_Turn(void) { if(GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_1)==0) { GPIO_SetBits(GPIOA,GPIO_Pin_1); } else { GPIO_ResetBits(GPIOA,GPIO_Pin_1); } } void LED2_Turn(void) { if(GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_11)==0) { GPIO_SetBits(GPIOA,GPIO_Pin_2); } else { GPIO_ResetBits(GPIOA,GPIO_Pin_2); } } #include "stm32f10x.h" // Device header void PWM_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); TIM_InternalClockConfig(TIM3); TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period=1000-1; TIM_TimeBaseInitStructure.TIM_Prescaler=72-1; TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0; TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure); TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCStructInit(&TIM_OCInitStructure); TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse=10; TIM_OC1Init(TIM3,&TIM_OCInitStructure); TIM_Cmd(TIM3,ENABLE); } void PWM_SetCompare1(uint16_t Compare) { TIM_SetCompare1(TIM3,Compare); } 在这段代码的基础上进行修改,实现 KEY1 单次按下打开或关闭 LED1、长按调节 LED1 亮度,KEY2 单次按下打开或关闭 LED2、长按调节 LED2 闪动频率的功能
11-17
#include “stm32f10x.h” // Device header int16_t Encoder_Count; //全局变量,用于计数旋转编码器的增量值 函 数:旋转编码器初始化 参 数:无 返 回 值:无 */ void Encoder_Init(void) { /开启时钟/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启AFIO的时钟,外部中断必须开启AFIO的时钟 /GPIO初始化/ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); //将PB0和PB1引脚初始化为上拉输入 /AFIO选择中断引脚/ GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);//将外部中断的0号线映射到GPIOB,即选择PB0为外部中断引脚 GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);//将外部中断的1号线映射到GPIOB,即选择PB1为外部中断引脚 /EXTI初始化/ EXTI_InitTypeDef EXTI_InitStructure; //定义结构体变量 EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1; //选择配置外部中断的0号线和1号线 EXTI_InitStructure.EXTI_LineCmd = ENABLE; //指定外部中断线使能 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //指定外部中断线为中断模式 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //指定外部中断线为下降沿触发 EXTI_Init(&EXTI_InitStructure); //将结构体变量交给EXTI_Init,配置EXTI外设 /NVIC中断分组/ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC为分组2 //即抢占优先级范围:0~3,响应优先级范围:0~3 //此分组配置在整个工程中仅需调用一次 //若有多个中断,可以把此代码放在main函数内,while循环之前 //若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置 /NVIC配置/ NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量 NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //选择配置NVIC的EXTI0线 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //指定NVIC线路的抢占优先级为1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC线路的响应优先级为1 NVIC_Init(&NVIC_InitStructure); //将结构体变量交给NVIC_Init,配置NVIC外设 NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn; //选择配置NVIC的EXTI1线 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //指定NVIC线路的抢占优先级为1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //指定NVIC线路的响应优先级为2 NVIC_Init(&NVIC_InitStructure); //将结构体变量交给NVIC_Init,配置NVIC外设 } 函 数:旋转编码器获取增量值 参 数:无 返 回 值:自上此调用此函数后,旋转编码器的增量值 */ int16_t Encoder_Get(void) { /使用Temp变量作为中继,目的是返回Encoder_Count后将其清零/ /在这里,也可以直接返回Encoder_Count 但这样就不是获取增量值的操作方法了 也可以实现功能,只是思路不一样/ int16_t Temp; Temp = Encoder_Count; Encoder_Count = 0; return Temp; } 函 数:EXTI0外部中断函数 参 数:无 返 回 值:无 注意事项:此函数为中断函数,无需调用,中断触发后自动执行 函数名为预留的指定名称,可以从启动文件复制 请确保函数名正确,不能有任何差异,否则中断函数将不能进入 */ void EXTI0_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line0) == SET) //判断是否是外部中断0号线触发的中断 { /如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动/ if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) { if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) //PB0的下降沿触发中断,此时检测另一相PB1的电平,目的是判断旋转方向 { Encoder_Count --; //此方向定义为反转,计数变量自减 } } EXTI_ClearITPendingBit(EXTI_Line0); //清除外部中断0号线的中断标志位 //中断标志位必须清除 //否则中断将连续不断地触发,导致主程序卡死 } } /** 函 数:EXTI1外部中断函数 参 数:无 返 回 值:无 注意事项:此函数为中断函数,无需调用,中断触发后自动执行 函数名为预留的指定名称,可以从启动文件复制 请确保函数名正确,不能有任何差异,否则中断函数将不能进入 */ void EXTI1_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line1) == SET) //判断是否是外部中断1号线触发的中断 { /如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动/ if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) { if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) //PB1的下降沿触发中断,此时检测另一相PB0的电平,目的是判断旋转方向 { Encoder_Count ++; //此方向定义为正转,计数变量自增 } } EXTI_ClearITPendingBit(EXTI_Line1); //清除外部中断1号线的中断标志位 //中断标志位必须清除 //否则中断将连续不断地触发,导致主程序卡死 } }#include “stm32f10x.h” // Device header /** 函 数:PWM初始化 参 数:无 返 回 值:无 */ void PWM_Init(void) { /开启时钟/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟 /GPIO初始化/ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA2引脚初始化为复用推挽输出 //受外设控制的引脚,均需要配置为复用模式 /配置时钟源/ TIM_InternalClockConfig(TIM2); //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟 /时基单元初始化/ TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量 TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能 TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数 TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //计数周期,即ARR的值 TIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1; //预分频器,即PSC的值 TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元 /输出比较初始化/ TIM_OCInitTypeDef TIM_OCInitStructure; //定义结构体变量 TIM_OCStructInit(&TIM_OCInitStructure); //结构体初始化,若结构体没有完整赋值 //则最好执行此函数,给结构体所有成员都赋一个默认值 //避免结构体初值不确定的问题 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //输出比较模式,选择PWM模式1 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性,选择为高,若选择极性为低,则输出高低电平取反 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能 TIM_OCInitStructure.TIM_Pulse = 0; //初始的CCR值 TIM_OC3Init(TIM2, &TIM_OCInitStructure); //将结构体变量交给TIM_OC3Init,配置TIM2的输出比较通道3 /TIM使能/ TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行 } /** 函 数:PWM设置CCR 参 数:Compare 要写入的CCR的值,范围:0~100 返 回 值:无 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比 占空比Duty = CCR / (ARR + 1) */ void PWM_SetCompare3(uint16_t Compare) { TIM_SetCompare3(TIM2, Compare); //设置CCR3的值 }#include “stm32f10x.h” // Device header #include “PWM.h” /** 函 数:直流电机初始化 参 数:无 返 回 值:无 */ void Motor_Init(void) { /开启时钟/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA4和PA5引脚初始化为推挽输出 PWM_Init(); //初始化直流电机的底层PWM } /** 函 数:直流电机设置速度 参 数:Speed 要设置的速度,范围:-100~100 返 回 值:无 */ void Motor_SetSpeed(int8_t Speed) { if (Speed >= 0) //如果设置正转的速度值 { GPIO_SetBits(GPIOA, GPIO_Pin_4); //PA4置高电平 GPIO_ResetBits(GPIOA, GPIO_Pin_5); //PA5置低电平,设置方向为正转 PWM_SetCompare3(Speed); //PWM设置为速度值 } else //否则,即设置反转的速度值 { GPIO_ResetBits(GPIOA, GPIO_Pin_4); //PA4置低电平 GPIO_SetBits(GPIOA, GPIO_Pin_5); //PA5置高电平,设置方向为反转 PWM_SetCompare3(-Speed); //PWM设置为负的速度值,因为此时速度值为负数,而PWM只能给正数 } }#include “stm32f10x.h” // Device header #include “Key.h” #define KEY_PRESSED 1 #define KEY_UNPRESSED 0 #define KEY_TIME_DOUBLE 200 #define KEY_TIME_LONG 2000 #define KEY_TIME_REPEAT 100 uint8_t Key_Flag[KEY_COUNT]; void Key_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); } uint8_t Key_GetState(uint8_t n) { if (n == KEY_1) { if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) { return KEY_PRESSED; } } else if (n == KEY_2) { if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0) { return KEY_PRESSED; } } else if (n == KEY_3) { if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13) == 1) { return KEY_PRESSED; } } else if (n == KEY_4) { if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_15) == 1) { return KEY_PRESSED; } } return KEY_UNPRESSED; } uint8_t Key_Check(uint8_t n, uint8_t Flag) { if (Key_Flag[n] & Flag) { if (Flag != KEY_HOLD) { Key_Flag[n] &= ~Flag; } return 1; } return 0; } void Key_Tick(void) { static uint8_t Count, i; static uint8_t CurrState[KEY_COUNT], PrevState[KEY_COUNT]; static uint8_t S[KEY_COUNT]; static uint16_t Time[KEY_COUNT]; for (i = 0; i < KEY_COUNT; i ++) { if (Time[i] > 0) { Time[i] --; } } Count ++; if (Count >= 20) { Count = 0; for (i = 0; i < KEY_COUNT; i ++) { PrevState[i] = CurrState[i]; CurrState[i] = Key_GetState(i); if (CurrState[i] == KEY_PRESSED) { Key_Flag[i] |= KEY_HOLD; } else { Key_Flag[i] &= ~KEY_HOLD; } if (CurrState[i] == KEY_PRESSED && PrevState[i] == KEY_UNPRESSED) { Key_Flag[i] |= KEY_DOWN; } if (CurrState[i] == KEY_UNPRESSED && PrevState[i] == KEY_PRESSED) { Key_Flag[i] |= KEY_UP; } if (S[i] == 0) { if (CurrState[i] == KEY_PRESSED) { Time[i] = KEY_TIME_LONG; S[i] = 1; } } else if (S[i] == 1) { if (CurrState[i] == KEY_UNPRESSED) { Time[i] = KEY_TIME_DOUBLE; S[i] = 2; } else if (Time[i] == 0) { Time[i] = KEY_TIME_REPEAT; Key_Flag[i] |= KEY_LONG; S[i] = 4; } } else if (S[i] == 2) { if (CurrState[i] == KEY_PRESSED) { Key_Flag[i] |= KEY_DOUBLE; S[i] = 3; } else if (Time[i] == 0) { Key_Flag[i] |= KEY_SINGLE; S[i] = 0; } } else if (S[i] == 3) { if (CurrState[i] == KEY_UNPRESSED) { S[i] = 0; } } else if (S[i] == 4) { if (CurrState[i] == KEY_UNPRESSED) { S[i] = 0; } else if (Time[i] == 0) { Time[i] = KEY_TIME_REPEAT; Key_Flag[i] |= KEY_REPEAT; S[i] = 4; } } } } }#include “stm32f10x.h” // Device header #include “MPU6050_Reg.h” #define MPU6050_ADDRESS 0xD0 //MPU6050的I2C从机地址 /** 函 数:MPU6050等待事件 参 数:同I2C_CheckEvent 返 回 值:无 / void MPU6050_WaitEvent(I2C_TypeDef I2Cx, uint32_t I2C_EVENT) { uint32_t Timeout; Timeout = 10000; //给定超时计数时间 while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS) //循环等待指定事件 { Timeout --; //等待时,计数值自减 if (Timeout == 0) //自减到0后,等待超时 { /超时的错误处理代码,可以添加到此处/ break; //跳出等待,不等了 } } } /** 函 数:MPU6050写寄存器 参 数:RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述 参 数:Data 要写入寄存器的数据,范围:0x00~0xFF 返 回 值:无 */ void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data) { I2C_GenerateSTART(I2C2, ENABLE); //硬件I2C生成起始条件 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //等待EV5 I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter); //硬件I2C发送从机地址,方向为发送 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //等待EV6 I2C_SendData(I2C2, RegAddress); //硬件I2C发送寄存器地址 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING); //等待EV8 I2C_SendData(I2C2, Data); //硬件I2C发送数据 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED); //等待EV8_2 I2C_GenerateSTOP(I2C2, ENABLE); //硬件I2C生成终止条件 } /** 函 数:MPU6050读寄存器 参 数:RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述 返 回 值:读取寄存器的数据,范围:0x00~0xFF */ uint8_t MPU6050_ReadReg(uint8_t RegAddress) { uint8_t Data; I2C_GenerateSTART(I2C2, ENABLE); //硬件I2C生成起始条件 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //等待EV5 I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter); //硬件I2C发送从机地址,方向为发送 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //等待EV6 I2C_SendData(I2C2, RegAddress); //硬件I2C发送寄存器地址 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED); //等待EV8_2 I2C_GenerateSTART(I2C2, ENABLE); //硬件I2C生成重复起始条件 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //等待EV5 I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver); //硬件I2C发送从机地址,方向为接收 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED); //等待EV6 I2C_AcknowledgeConfig(I2C2, DISABLE); //在接收最后一个字节之前提前将应答失能 I2C_GenerateSTOP(I2C2, ENABLE); //在接收最后一个字节之前提前申请停止条件 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED); //等待EV7 Data = I2C_ReceiveData(I2C2); //接收数据寄存器 I2C_AcknowledgeConfig(I2C2, ENABLE); //将应答恢复为使能,为了不影响后续可能产生的读取多字节操作 return Data; } /** 函 数:MPU6050初始化 参 数:无 返 回 值:无 */ void MPU6050_Init(void) { /开启时钟/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE); //开启I2C2的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟 /GPIO初始化/ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); //将PB10和PB11引脚初始化为复用开漏输出 /I2C初始化/ I2C_InitTypeDef I2C_InitStructure; //定义结构体变量 I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; //模式,选择为I2C模式 I2C_InitStructure.I2C_ClockSpeed = 50000; //时钟速度,选择为50KHz I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; //时钟占空比,选择Tlow/Thigh = 2 I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; //应答,选择使能 I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //应答地址,选择7位,从机模式下才有效 I2C_InitStructure.I2C_OwnAddress1 = 0x00; //自身地址,从机模式下才有效 I2C_Init(I2C2, &I2C_InitStructure); //将结构体变量交给I2C_Init,配置I2C2 /I2C使能/ I2C_Cmd(I2C2, ENABLE); //使能I2C2,开始运行 /MPU6050寄存器初始化,需要对照MPU6050手册的寄存器描述配置,此处仅配置了部分重要的寄存器/ MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01); //电源管理寄存器1,取消睡眠模式,选择时钟源为X轴陀螺仪 MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00); //电源管理寄存器2,保持默认值0,所有轴均不待机 MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09); //采样率分频寄存器,配置采样率 MPU6050_WriteReg(MPU6050_CONFIG, 0x06); //配置寄存器,配置DLPF MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18); //陀螺仪配置寄存器,选择满量程为±2000°/s MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18); //加速度计配置寄存器,选择满量程为±16g } /** 函 数:MPU6050获取ID号 参 数:无 返 回 值:MPU6050的ID号 */ uint8_t MPU6050_GetID(void) { return MPU6050_ReadReg(MPU6050_WHO_AM_I); //返回WHO_AM_I寄存器的值 } /** 函 数:MPU6050获取数据 参 数:AccX AccY AccZ 加速度计X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767 参 数:GyroX GyroY GyroZ 陀螺仪X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767 返 回 值:无 */ void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ) { uint8_t DataH, DataL; //定义数据高8位和低8位的变量 DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H); //读取加速度计X轴的高8位数据 DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L); //读取加速度计X轴的低8位数据 *AccX = (DataH << 8) | DataL; //数据拼接,通过输出参数返回 DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H); //读取加速度计Y轴的高8位数据 DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L); //读取加速度计Y轴的低8位数据 *AccY = (DataH << 8) | DataL; //数据拼接,通过输出参数返回 DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H); //读取加速度计Z轴的高8位数据 DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L); //读取加速度计Z轴的低8位数据 *AccZ = (DataH << 8) | DataL; //数据拼接,通过输出参数返回 DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H); //读取陀螺仪X轴的高8位数据 DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L); //读取陀螺仪X轴的低8位数据 *GyroX = (DataH << 8) | DataL; //数据拼接,通过输出参数返回 DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H); //读取陀螺仪Y轴的高8位数据 DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L); //读取陀螺仪Y轴的低8位数据 *GyroY = (DataH << 8) | DataL; //数据拼接,通过输出参数返回 DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H); //读取陀螺仪Z轴的高8位数据 DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L); //读取陀螺仪Z轴的低8位数据 *GyroZ = (DataH << 8) | DataL; //数据拼接,通过输出参数返回 }#include “stm32f10x.h” // Device header /** 函 数:输入捕获初始化 参 数:无 返 回 值:无 */ void IC_Init(void) { /开启时钟/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //开启TIM3的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟 /GPIO初始化/ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA6引脚初始化为上拉输入 /配置时钟源/ TIM_InternalClockConfig(TIM3); //选择TIM3为内部时钟,若不调用此函数,TIM默认也为内部时钟 /时基单元初始化/ TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量 TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能 TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数 TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; //计数周期,即ARR的值 TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //预分频器,即PSC的值 TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM3的时基单元 /PWMI模式初始化/ TIM_ICInitTypeDef TIM_ICInitStructure; //定义结构体变量 TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //选择配置定时器通道1 TIM_ICInitStructure.TIM_ICFilter = 0xF; //输入滤波器参数,可以过滤信号抖动 TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //极性,选择为上升沿触发捕获 TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //捕获预分频,选择不分频,每次信号都触发捕获 TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //输入信号交叉,选择直通,不交叉 TIM_PWMIConfig(TIM3, &TIM_ICInitStructure); //将结构体变量交给TIM_PWMIConfig,配置TIM3的输入捕获通道 //此函数同时会把另一个通道配置为相反的配置,实现PWMI模式 /选择触发源及从模式/ TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); //触发源选择TI1FP1 TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); //从模式选择复位 //即TI1产生上升沿时,会触发CNT归零 /TIM使能/ TIM_Cmd(TIM3, ENABLE); //使能TIM3,定时器开始运行 } /** 函 数:获取输入捕获的频率 参 数:无 返 回 值:捕获得到的频率 */ uint32_t IC_GetFreq(void) { return 1000000 / (TIM_GetCapture1(TIM3) + 1); //测周法得到频率fx = fc / N,这里不执行+1的操作也可 } /** 函 数:获取输入捕获的占空比 参 数:无 返 回 值:捕获得到的占空比 */ uint32_t IC_GetDuty(void) { return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1); //占空比Duty = CCR2 / CCR1 * 100,这里不执行+1的操作也可 }#include “stm32f10x.h” // Device header /** 函 数:AD初始化 参 数:无 返 回 值:无 */ void AD_Init(void) { /开启时钟/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //开启ADC1的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟 /设置ADC时钟/ RCC_ADCCLKConfig(RCC_PCLK2_Div6); //选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz /GPIO初始化/ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA0引脚初始化为模拟输入 /规则组通道配置/ ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); //规则组序列1的位置,配置为通道0 /ADC初始化/ ADC_InitTypeDef ADC_InitStructure; //定义结构体变量 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //模式,选择独立模式,即单独使用ADC1 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据对齐,选择右对齐 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发,使用软件触发,不需要外部触发 ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //连续转换,失能,每转换一次规则组序列后停止 ADC_InitStructure.ADC_ScanConvMode = DISABLE; //扫描模式,失能,只转换规则组的序列1这一个位置 ADC_InitStructure.ADC_NbrOfChannel = 1; //通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1 ADC_Init(ADC1, &ADC_InitStructure); //将结构体变量交给ADC_Init,配置ADC1 /ADC使能/ ADC_Cmd(ADC1, ENABLE); //使能ADC1,ADC开始运行 /ADC校准/ ADC_ResetCalibration(ADC1); //固定流程,内部有电路会自动执行校准 while (ADC_GetResetCalibrationStatus(ADC1) == SET); ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1) == SET); } /** 函 数:获取AD转换的值 参 数:无 返 回 值:AD转换的值,范围:0~4095 */ uint16_t AD_GetValue(void) { ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件触发AD转换一次 while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); //等待EOC标志位,即等待AD转换结束 return ADC_GetConversionValue(ADC1); //读数据寄存器,得到AD转换的结果 }#include “stm32f10x.h” // Device header /** 函 数:PWM初始化 参 数:无 返 回 值:无 */ void PWM_Init(void) { /开启时钟/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟 /GPIO初始化/ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIO_Pin_15; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA0引脚初始化为复用推挽输出 //受外设控制的引脚,均需要配置为复用模式 /配置时钟源/ TIM_InternalClockConfig(TIM2); //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟 /时基单元初始化/ TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量 TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能 TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数 TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //计数周期,即ARR的值 TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; //预分频器,即PSC的值 TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元 /输出比较初始化/ TIM_OCInitTypeDef TIM_OCInitStructure; //定义结构体变量 TIM_OCStructInit(&TIM_OCInitStructure); //结构体初始化,若结构体没有完整赋值 //则最好执行此函数,给结构体所有成员都赋一个默认值 //避免结构体初值不确定的问题 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //输出比较模式,选择PWM模式1 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性,选择为高,若选择极性为低,则输出高低电平取反 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能 TIM_OCInitStructure.TIM_Pulse = 0; //初始的CCR值 TIM_OC1Init(TIM2, &TIM_OCInitStructure); //将结构体变量交给TIM_OC1Init,配置TIM2的输出比较通道1 /TIM使能/ TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行 } /** 函 数:PWM设置CCR 参 数:Compare 要写入的CCR的值,范围:0~100 返 回 值:无 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比 占空比Duty = CCR / (ARR + 1) */ void PWM_SetCompare1(uint16_t Compare) { TIM_SetCompare1(TIM2, Compare); //设置CCR1的值 } /** 函 数:PWM设置PSC 参 数:Prescaler 要写入的PSC的值,范围:0~65535 返 回 值:无 注意事项:PSC和ARR共同决定频率,此函数仅设置PSC的值,并不直接是频率 频率Freq = CK_PSC / (PSC + 1) / (ARR + 1) */ void PWM_SetPrescaler(uint16_t Prescaler) { TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate); //设置PSC的值 }#include “stm32f10x.h” // Device header void Timer_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_InternalClockConfig(TIM2); TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period = 1000 - 1; TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); TIM_ClearFlag(TIM2, TIM_FLAG_Update); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_Init(&NVIC_InitStructure); TIM_Cmd(TIM2, ENABLE); } /* void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } } */#include “stm32f10x.h” // Device header #include “Delay.h” #include “OLED.h” #include “Encoder.h” int16_t Num; //定义待被旋转编码器调节的变量 int main(void) { /模块初始化/ OLED_Init(); //OLED初始化 Encoder_Init(); //旋转编码器初始化 /*显示静态字符串*/ OLED_ShowString(1, 1, "Num:"); //1行1列显示字符串Num: while (1) { Num += Encoder_Get(); //获取自上此调用此函数后,旋转编码器的增量值,并将增量值加到Num上 OLED_ShowSignedNum(1, 5, Num, 5); //显示Num } }#include “stm32f10x.h” // Device header #include “Delay.h” #include “OLED.h” #include “Motor.h” #include “Key.h” uint8_t KeyNum; //定义用于接收按键键码的变量 int8_t Speed; //定义速度变量 int main(void) { /模块初始化/ OLED_Init(); //OLED初始化 Motor_Init(); //直流电机初始化 Key_Init(); //按键初始化 /*显示静态字符串*/ OLED_ShowString(1, 1, "Speed:"); //1行1列显示字符串Speed: while (1) { KeyNum = Key_GetNum(); //获取按键键码 if (KeyNum == 1) //按键1按下 { Speed += 20; //速度变量自增20 if (Speed > 100) //速度变量超过100后 { Speed = -100; //速度变量变为-100 //此操作会让电机旋转方向突然改变,可能会因供电不足而导致单片机复位 //若出现了此现象,则应避免使用这样的操作 } } Motor_SetSpeed(Speed); //设置直流电机的速度为速度变量 OLED_ShowSignedNum(1, 7, Speed, 3); //OLED显示速度变量 } }#include “stm32f10x.h” // Device header #include “Delay.h” #include “OLED.h” #include “PWM.h” #include “IC.h” int main(void) { /模块初始化/ OLED_Init(); //OLED初始化 PWM_Init(); //PWM初始化 IC_Init(); //输入捕获初始化 /*显示静态字符串*/ OLED_ShowString(1, 1, "Freq:00000Hz"); //1行1列显示字符串Freq:00000Hz OLED_ShowString(2, 1, "Duty:00%"); //2行1列显示字符串Duty:00% /*使用PWM模块提供输入捕获的测试信号*/ PWM_SetPrescaler(720 - 1); //PWM频率Freq = 72M / (PSC + 1) / 100 PWM_SetCompare1(50); //PWM占空比Duty = CCR / 100 while (1) { OLED_ShowNum(1, 6, IC_GetFreq(), 5); //不断刷新显示输入捕获测得的频率 OLED_ShowNum(2, 6, IC_GetDuty(), 2); //不断刷新显示输入捕获测得的占空比 } }#include “stm32f10x.h” // Device header #include “Delay.h” #include “OLED.h” #include “AD.h” uint16_t ADValue; //定义AD值变量 float Voltage; //定义电压变量 int main(void) { /模块初始化/ OLED_Init(); //OLED初始化 AD_Init(); //AD初始化 /*显示静态字符串*/ OLED_ShowString(1, 1, "ADValue:"); OLED_ShowString(2, 1, "Voltage:0.00V"); while (1) { ADValue = AD_GetValue(); //获取AD转换的值 Voltage = (float)ADValue / 4095 * 3.3; //将AD值线性变换到0~3.3的范围,表示电压 OLED_ShowNum(1, 9, ADValue, 4); //显示AD值 OLED_ShowNum(2, 9, Voltage, 1); //显示电压值的整数部分 OLED_ShowNum(2, 11, (uint16_t)(Voltage * 100) % 100, 2); //显示电压值的小数部分 Delay_ms(100); //延时100ms,手动增加一些转换的间隔时间 } }#include “stm32f10x.h” // Device header #include “Delay.h” #include “OLED.h” #include “MPU6050.h” uint8_t ID; //定义用于存放ID号的变量 int16_t AX, AY, AZ, GX, GY, GZ; //定义用于存放各个数据的变量 int main(void) { /模块初始化/ OLED_Init(); //OLED初始化 MPU6050_Init(); //MPU6050初始化 /*显示ID号*/ OLED_ShowString(1, 1, "ID:"); //显示静态字符串 ID = MPU6050_GetID(); //获取MPU6050的ID号 OLED_ShowHexNum(1, 4, ID, 2); //OLED显示ID号 while (1) { MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ); //获取MPU6050的数据 OLED_ShowSignedNum(2, 1, AX, 5); //OLED显示数据 OLED_ShowSignedNum(3, 1, AY, 5); OLED_ShowSignedNum(4, 1, AZ, 5); OLED_ShowSignedNum(2, 8, GX, 5); OLED_ShowSignedNum(3, 8, GY, 5); OLED_ShowSignedNum(4, 8, GZ, 5); } }#include “stm32f10x.h” // Device header #include “Delay.h” #include “OLED.h” #include “Key.h” #include “Timer.h” uint16_t Num1; uint16_t Num2; int main(void) { OLED_Init(); Key_Init(); Timer_Init(); OLED_ShowString(1, 1, "Num1:"); OLED_ShowString(2, 1, "Num2:"); while (1) { /*示例1*/ /*示例5*/ // uint8_t K1_UP = Key_Check(KEY_1, KEY_UP); // uint8_t K2_UP = Key_Check(KEY_2, KEY_UP); // // if (K1_UP && Key_Check(KEY_3, KEY_HOLD)) // { // Num1 ++; // } // if (K2_UP && Key_Check(KEY_3, KEY_HOLD)) // { // Num1 --; // } // if (K1_UP && Key_Check(KEY_4, KEY_HOLD)) // { // Num2 ++; // } // if (K2_UP && Key_Check(KEY_4, KEY_HOLD)) // { // Num2 --; // } OLED_ShowNum(1, 6, Num1, 5); OLED_ShowNum(2, 6, Num2, 5); } } void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) { Key_Tick(); TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } }三.功能描述 3.1 功能概述 3.2 旋转编码器、按键、电位器 功能要求 旋转编码器对电机的速度控制,只要求电机能够正转。 按键对电机、oled 屏幕的控制 读取 mpu6050。 OLED 的显示功能 pwm 默认输出 1000Hz。 旋转编码器调整电机转速,调节范围为 0-100%,并且可用第四个按键改变旋 转编码器的调节步长。 (提示:每旋转一次旋转编码器,电机转速增加量,此为调节步长)。 可用按键控制电机转速,可在 0-100%范围内调节,调节步长可在第四个按键 切换 10%或 20%。 第一个按键实现 OLED 屏幕的翻页功能,在三个页面之间循环。 第二、第三按键实现对数据的增减控制。只有在相应页面执行对应数据的操 作。 3.3 mpu6050 要求当 mpu6050 陀螺仪翻转后立即停止电机工作。所有操作均为禁止。 (提示,翻转后无法对单片机进行除复位操作外的任何操作。) 3.4 OLED 显示 电机状态界面 电机界面显示当前系统的电机工作状态 页面参数设置如下 r: 电机转速% M_step: 旋转编码器的调节步长% r 值可通过旋转编码器和相应按键修改,范围为 0-100% M_step 通过相应按键修改,要求可修改为 10%、20% 陀螺仪状态 显示 mpu6050 陀螺仪中获取的 x,y,z 轴数据,原始读数即可,不用转换角度; 翻转时禁止所有按键操作。 X: Y: Z: pwm 输出状态界面 pwm 输出状态界面显示当前系统输出的 pwm 脉冲信号状态(频率 F、占空 比 CD) F:xxxHz CD:xx% 第二、第三按键可修改占空比 CD 的值,电机转速做出相应改变。 CD 为当前状态真实占空比。 四、自由发挥部分。 通过电位器控制 pwm 输出的频率,频率范围:1000-10000Hz 在 0-3V 线性变化,3-3.3V 无变化。 利用输入捕获功能获得任意一个 pwm 输出口的频率 CF。增加第四个页面,显示输入捕 获得到的频率,以及电位器的电压 Vol。 CF:xxx Hz Vol:xx V 按照以上要求在不改变代码框架上整合并修改完善代码,使代码更加合理并给出说明,按键只保留单击模式
09-26
#include "stm32f10x.h" #include "Delay.h" #include "OLED.h" #include "Encoder.h" #include "Motor.h" #include "Key.h" #include "PWM.h" #include "IC.h" #include "AD.h" #include "MPU6050.h" #include "Timer.h" // 全局变量 uint8_t Page = 1; // OLED当前页面(1-3) uint8_t M_Step = 10; // 旋转编码器调节步长(10%或20%) int8_t Speed = 0; // 电机速度(0-100) int16_t Duty = 0; // PWM占空比(0-100) uint16_t Freq = 1000; // PWM频率(Hz),默认1000Hz uint16_t CaptureFreq = 0; // 捕获频率(Hz) float Voltage = 0; // 电位器电压(V) int16_t CaptureCount; uint8_t flip_lock = 0; // 翻转锁定标志(1=锁定) uint8_t oled_update_required = 1; // 初始需要刷新 // MPU6050数据 int16_t AX, AY, AZ, GX, GY, GZ; uint8_t is_flipped = 0; // 翻转标志(1=翻转) // 更新OLED显示 void OLED_Update(void) { OLED_Clear(); switch(Page) { case 1: // 电机状态界面 OLED_ShowString(1, 1, "Motor:"); OLED_ShowString(1, 8, "r:"); OLED_ShowNum(1, 10, Speed, 3); OLED_ShowChar(1, 13, '%'); OLED_ShowString(2, 1, "M_step:"); OLED_ShowNum(2, 8, M_Step, 2); OLED_ShowChar(2, 11, '%'); break; case 2: // 陀螺仪状态界面 OLED_ShowString(1, 1, "GYRO:"); OLED_ShowString(2, 1, "X:"); OLED_ShowSignedNum(2, 3, GX, 6); OLED_ShowString(3, 1, "Y:"); OLED_ShowSignedNum(3, 3, GY, 6); OLED_ShowString(4, 1, "Z:"); OLED_ShowSignedNum(4, 3, GZ, 6); // 显示翻转状态 if(is_flipped) OLED_ShowString(4, 10, "LOCKED"); break; case 3: // PWM与捕获界面 OLED_ShowString(1, 1, "PWM:"); OLED_ShowString(1, 6, "F:"); OLED_ShowNum(1, 8, Freq, 5); OLED_ShowString(1, 13, "Hz"); OLED_ShowString(2, 1, "CD:"); OLED_ShowNum(2, 4, Duty, 3); OLED_ShowChar(2, 7, '%'); break; case 4: OLED_ShowString(1, 1, "CF:"); OLED_ShowNum(1, 4, CaptureFreq, 5); OLED_ShowString(1, 9, "Hz"); OLED_ShowString(2, 1, "Vol:"); OLED_ShowNum(2, 5, (uint16_t)Voltage, 1); OLED_ShowChar(2, 6, '.'); OLED_ShowNum(2, 7, (uint16_t)(Voltage*100)%100, 2); OLED_ShowChar(2, 9, 'V'); break; } } // 设置PWM频率(Hz) void PWM_SetFrequency(uint16_t NewFreq) { // 限制频率范围1000-10000Hz if(NewFreq < 1000) NewFreq = 1000; if(NewFreq > 10000) NewFreq = 10000; uint32_t TimerClock = 72000000; // APB1时钟 uint16_t ARR = 100 - 1; // 固定周期 uint32_t PSC = (TimerClock / ((ARR + 1) * NewFreq)) - 1; if (PSC > 65535) PSC = 65535; // 防止溢出 TIM_PrescalerConfig(TIM2, (uint16_t)PSC, TIM_PSCReloadMode_Immediate); Freq = NewFreq; } uint8_t MPU6050_CheckFlip(void) { static int16_t last_z ; // 上一次Z轴加速度值 static uint8_t stable_count ; // 稳定状态计数 // 第一次不判断,只记录初始值 if (last_z == 0) { last_z = AZ; return 0; } // 检测Z轴加速度是否过零(从正变负或从负变正) if((last_z > 0 && AZ < 0) || (last_z < 0 && AZ > 0)) { stable_count++; if(stable_count >= 3) { is_flipped = 1; stable_count = 0; return 1; } } else { stable_count = 0; } last_z = AZ; return 0; } int main(void) { // 第一步:设置中断优先级分组(只允许调用一次) NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 初始化顺序优化:先初始化定时器,再初始化依赖它的外设 Timer_Init(); // 首先初始化定时器,为按键扫描提供时基 OLED_Init(); // 初始化OLED Key_Init(); // 初始化按键 Encoder_Init(); // 初始化编码器 PWM_Init(); // 初始化PWM Motor_Init(); // 初始化电机 AD_Init(); // 初始化AD转换 IC_Init(); // 初始化输入捕获 MPU6050_Init(); // 初始化显示 oled_update_required = 1; // 初始设置电机速度为0,确保状态正确 Motor_SetSpeed(0); Duty = 50; PWM_SetCompare3(Duty); while (1) { MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ); // 检测翻转状态 if(( MPU6050_CheckFlip() == 1) || flip_lock == 1) { flip_lock = 1; // 锁定系统 Motor_SetSpeed(0); // 停止电机 oled_update_required = 1; // 更新显示锁定状态 continue; // 跳过其余操作,实现所有操作禁止 } else { // 旋转编码器调节速度 int16_t Enc = Encoder_Get(); if (Enc > 0) { Speed += M_Step; if (Speed > 100) Speed = 100; oled_update_required = 1; // 立即更新显示 } else if (Enc < 0) { Speed -= M_Step; if (Speed < 0) Speed = 0; oled_update_required = 1; // 立即更新显示 } // 更新电机速度 Motor_SetSpeed(Speed); // 同步Duty与Speed if (Speed != Duty) { Duty = Speed; PWM_SetCompare3(Duty); } if (Key_Check(KEY_1, KEY_SINGLE)) // 页面切换(1-3循环) { Page++; if (Page > 4) Page = 1; oled_update_required = 1; // 按键后短暂延时,防止误触发 Delay_ms(100); } if (Key_Check(KEY_4, KEY_SINGLE)) // 切换步长 { M_Step = (M_Step == 10) ? 20 : 10; if (Page == 1) { oled_update_required = 1; } Delay_ms(100); } if (Page == 3) // PWM页面 { if (Key_Check(KEY_2, KEY_SINGLE)) // 占空比增加 { Duty ++; if (Duty > 100) Duty = 100; Speed = Duty; Motor_SetSpeed(Speed); PWM_SetCompare3(Duty); oled_update_required = 1; Delay_ms(100); } if (Key_Check(KEY_3, KEY_SINGLE)) // 占空比减少 { Duty --; if (Duty < 0) Duty = 0; Speed = Duty; Motor_SetSpeed(Speed); PWM_SetCompare3(Duty); oled_update_required = 1; Delay_ms(100); } } } // 读取电位器控制频率(0-3V对应1000-10000Hz) uint16_t ADValue = AD_GetValue(); Voltage = (float)ADValue / 4095 * 3.3; if (Voltage <= 3.0) // 0-3V线性变化 { uint16_t NewFreq = 1000 + (uint16_t)(Voltage * 3000); if (NewFreq != Freq) { PWM_SetFrequency(NewFreq); oled_update_required = 1; } } // 获取捕获频率 uint16_t new_capture = IC_GetFreq(); if (new_capture != CaptureFreq) { CaptureFreq = new_capture; oled_update_required = 1; } // 统一刷新OLED if (oled_update_required) { OLED_Update(); oled_update_required = 0; } // 延时防止频繁刷新 Delay_ms(10); } } // 定时器3中断服务函数 - 专门用于按键扫描 void TIM3_IRQHandler(void) { if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET) { Key_Tick(); // 按键扫描定时调用 TIM_ClearITPendingBit(TIM3, TIM_IT_Update); } } #include "stm32f10x.h" // Device header #include "Key.h" #define KEY_PRESSED 1 #define KEY_UNPRESSED 0 #define KEY_DEBOUNCE_TIME 20 uint8_t Key_Flag[KEY_COUNT]; void Key_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13|GPIO_Pin_14 | GPIO_Pin_15; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); } uint8_t Key_GetState(uint8_t n) { if (n == KEY_1) { if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12) == 1) { return KEY_PRESSED; } } else if (n == KEY_2) { if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13) == 1) { return KEY_PRESSED; } } else if (n == KEY_3) { if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 1) { return KEY_PRESSED; } } else if (n == KEY_4) { if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_15) == 1) { return KEY_PRESSED; } } return KEY_UNPRESSED; } uint8_t Key_Check(uint8_t n, uint8_t Flag) { if (Flag == KEY_SINGLE && (Key_Flag[n] & KEY_SINGLE)) { Key_Flag[n] &= ~KEY_SINGLE; // 清除单击标志 return 1; } return 0; } // 按键扫描定时器中断处理(10ms调用一次) void Key_Tick(void) { static uint8_t CurrState[KEY_COUNT], PrevState[KEY_COUNT]; static uint8_t State[KEY_COUNT]; // 0:等待按下 1:消抖 2:等待释放 static uint16_t DebounceTime[KEY_COUNT]; for (uint8_t i = 0; i < KEY_COUNT; i++) { PrevState[i] = CurrState[i]; CurrState[i] = Key_GetState(i); switch (State[i]) { case 0: // 等待按下 if (CurrState[i] == KEY_PRESSED) { DebounceTime[i] = KEY_DEBOUNCE_TIME; State[i] = 1; } break; case 1: // 消抖检测 if (CurrState[i] != KEY_PRESSED) { State[i] = 0; } else if (DebounceTime[i] == 0) { State[i] = 2; } else { DebounceTime[i]--; } break; case 2: // 等待释放 if (CurrState[i] == KEY_UNPRESSED) { Key_Flag[i] |= KEY_SINGLE; State[i] = 0; } break; } } } #include "stm32f10x.h" // Device header #include "MPU6050_Reg.h" #define MPU6050_ADDRESS 0xD0 //MPU6050的I2C从机地址 /** * 函 数:MPU6050等待事件 * 参 数:同I2C_CheckEvent * 返 回 值:无 */ void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT) { uint32_t Timeout; Timeout = 10000; //给定超时计数时间 while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS) //循环等待指定事件 { Timeout --; //等待时,计数值自减 if (Timeout == 0) //自减到0后,等待超时 { /*超时的错误处理代码,可以添加到此处*/ break; //跳出等待,不等了 } } } /** * 函 数:MPU6050写寄存器 * 参 数:RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述 * 参 数:Data 要写入寄存器的数据,范围:0x00~0xFF * 返 回 值:无 */ void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data) { I2C_GenerateSTART(I2C2, ENABLE); //硬件I2C生成起始条件 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //等待EV5 I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter); //硬件I2C发送从机地址,方向为发送 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //等待EV6 I2C_SendData(I2C2, RegAddress); //硬件I2C发送寄存器地址 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING); //等待EV8 I2C_SendData(I2C2, Data); //硬件I2C发送数据 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED); //等待EV8_2 I2C_GenerateSTOP(I2C2, ENABLE); //硬件I2C生成终止条件 } /** * 函 数:MPU6050读寄存器 * 参 数:RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述 * 返 回 值:读取寄存器的数据,范围:0x00~0xFF */ uint8_t MPU6050_ReadReg(uint8_t RegAddress) { uint8_t Data; I2C_GenerateSTART(I2C2, ENABLE); //硬件I2C生成起始条件 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //等待EV5 I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter); //硬件I2C发送从机地址,方向为发送 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //等待EV6 I2C_SendData(I2C2, RegAddress); //硬件I2C发送寄存器地址 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED); //等待EV8_2 I2C_GenerateSTART(I2C2, ENABLE); //硬件I2C生成重复起始条件 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //等待EV5 I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver); //硬件I2C发送从机地址,方向为接收 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED); //等待EV6 I2C_AcknowledgeConfig(I2C2, DISABLE); //在接收最后一个字节之前提前将应答失能 I2C_GenerateSTOP(I2C2, ENABLE); //在接收最后一个字节之前提前申请停止条件 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED); //等待EV7 Data = I2C_ReceiveData(I2C2); //接收数据寄存器 I2C_AcknowledgeConfig(I2C2, ENABLE); //将应答恢复为使能,为了不影响后续可能产生的读取多字节操作 return Data; } /** * 函 数:MPU6050初始化 * 参 数:无 * 返 回 值:无 */ void MPU6050_Init(void) { /*开启时钟*/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE); //开启I2C2的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟 /*GPIO初始化*/ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); //将PB10和PB11引脚初始化为复用开漏输出 /*I2C初始化*/ I2C_InitTypeDef I2C_InitStructure; //定义结构体变量 I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; //模式,选择为I2C模式 I2C_InitStructure.I2C_ClockSpeed = 50000; //时钟速度,选择为50KHz I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; //时钟占空比,选择Tlow/Thigh = 2 I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; //应答,选择使能 I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //应答地址,选择7位,从机模式下才有效 I2C_InitStructure.I2C_OwnAddress1 = 0x00; //自身地址,从机模式下才有效 I2C_Init(I2C2, &I2C_InitStructure); //将结构体变量交给I2C_Init,配置I2C2 /*I2C使能*/ I2C_Cmd(I2C2, ENABLE); //使能I2C2,开始运行 /*MPU6050寄存器初始化,需要对照MPU6050手册的寄存器描述配置,此处仅配置了部分重要的寄存器*/ MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01); //电源管理寄存器1,取消睡眠模式,选择时钟源为X轴陀螺仪 MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00); //电源管理寄存器2,保持默认值0,所有轴均不待机 MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09); //采样率分频寄存器,配置采样率 MPU6050_WriteReg(MPU6050_CONFIG, 0x06); //配置寄存器,配置DLPF MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18); //陀螺仪配置寄存器,选择满量程为±2000°/s MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18); //加速度计配置寄存器,选择满量程为±16g } /** * 函 数:MPU6050获取ID号 * 参 数:无 * 返 回 值:MPU6050的ID号 */ uint8_t MPU6050_GetID(void) { return MPU6050_ReadReg(MPU6050_WHO_AM_I); //返回WHO_AM_I寄存器的值 } /** * 函 数:MPU6050获取数据 * 参 数:AccX AccY AccZ 加速度计X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767 * 参 数:GyroX GyroY GyroZ 陀螺仪X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767 * 返 回 值:无 */ void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ) { uint8_t DataH, DataL; //定义数据高8位和低8位的变量 DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H); //读取加速度计X轴的高8位数据 DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L); //读取加速度计X轴的低8位数据 *AccX = (DataH << 8) | DataL; //数据拼接,通过输出参数返回 DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H); //读取加速度计Y轴的高8位数据 DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L); //读取加速度计Y轴的低8位数据 *AccY = (DataH << 8) | DataL; //数据拼接,通过输出参数返回 DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H); //读取加速度计Z轴的高8位数据 DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L); //读取加速度计Z轴的低8位数据 *AccZ = (DataH << 8) | DataL; //数据拼接,通过输出参数返回 DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H); //读取陀螺仪X轴的高8位数据 DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L); //读取陀螺仪X轴的低8位数据 *GyroX = (DataH << 8) | DataL; //数据拼接,通过输出参数返回 DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H); //读取陀螺仪Y轴的高8位数据 DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L); //读取陀螺仪Y轴的低8位数据 *GyroY = (DataH << 8) | DataL; //数据拼接,通过输出参数返回 DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H); //读取陀螺仪Z轴的高8位数据 DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L); //读取陀螺仪Z轴的低8位数据 *GyroZ = (DataH << 8) | DataL; //数据拼接,通过输出参数返回 } #include "stm32f10x.h" // Device header /** * 函 数:输入捕获初始化 * 参 数:无 * 返 回 值:无 */ void IC_Init(void) { /*开启时钟*/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //开启TIM3的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟 /*GPIO初始化*/ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入,由外部信号决定 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA6引脚初始化为上拉输入 /*配置时钟源*/ TIM_InternalClockConfig(TIM3); //选择TIM3为内部时钟,若不调用此函数,TIM默认也为内部时钟 /*时基单元初始化*/ TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量 TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能 TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数 TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; //计数周期,即ARR的值 TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //预分频器,即PSC的值 TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM3的时基单元 /*PWMI模式初始化*/ TIM_ICInitTypeDef TIM_ICInitStructure; //定义结构体变量 TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //选择配置定时器通道1 TIM_ICInitStructure.TIM_ICFilter = 0xF; //输入滤波器参数,可以过滤信号抖动 TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //极性,选择为上升沿触发捕获 TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //捕获预分频,选择不分频,每次信号都触发捕获 TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //输入信号交叉,选择直通,不交叉 TIM_PWMIConfig(TIM3, &TIM_ICInitStructure); //将结构体变量交给TIM_PWMIConfig,配置TIM3的输入捕获通道 //此函数同时会把另一个通道配置为相反的配置,实现PWMI模式 /*选择触发源及从模式*/ TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); //触发源选择TI1FP1 TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); //从模式选择复位 //即TI1产生上升沿时,会触发CNT归零 /*TIM使能*/ TIM_Cmd(TIM3, ENABLE); //使能TIM3,定时器开始运行 } /** * 函 数:获取输入捕获的频率 * 参 数:无 * 返 回 值:捕获得到的频率 */ //uint32_t IC_GetFreq(void) //{ // return 1000000 / (TIM_GetCapture1(TIM4) + 1); //测周法得到频率fx = fc / N,这里不执行+1的操作也可 //} uint16_t IC_GetFreq(void) { static uint16_t LastCount = 0; static uint16_t CaptureFreq = 0; // 手动检测捕获事件(避免中断冲突) if(TIM_GetFlagStatus(TIM3, TIM_FLAG_CC1) == SET) { uint16_t CurrentCount = TIM_GetCapture1(TIM3); uint16_t Count = CurrentCount - LastCount; // 计算频率:1MHz / 计数差值 if(Count != 0) { CaptureFreq = 1000000 / Count; } LastCount = CurrentCount; TIM_ClearFlag(TIM3, TIM_FLAG_CC1); } return CaptureFreq; } ///** // * 函 数:获取输入捕获的占空比 // * 参 数:无 // * 返 回 值:捕获得到的占空比 // */ //uint32_t IC_GetDuty(void) //{ // return (TIM_GetCapture2(TIM4) + 1) * 100 / (TIM_GetCapture1(TIM4) + 1); //占空比Duty = CCR2 / CCR1 * 100,这里不执行+1的操作也可 //} #include "stm32f10x.h" // Device header void Timer_Init(void) { // 初始化定时器3用于按键扫描,避免与PWM定时器冲突 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); TIM_InternalClockConfig(TIM3); TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period = 1000 - 1; // 10ms中断 TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; // 72MHz / 72 = 1MHz TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 较高优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_Init(&NVIC_InitStructure); TIM_Cmd(TIM3, ENABLE); } /* void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } } */ #include "stm32f10x.h" // Device header /** * 函 数:PWM初始化 * 参 数:无 * 返 回 值:无 */ void PWM_Init(void) { /*开启时钟*/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟 /*GPIO初始化*/ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA2引脚初始化为复用推挽输出 //受外设控制的引脚,均需要配置为复用模式 /*配置时钟源*/ TIM_InternalClockConfig(TIM2); //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟 /*时基单元初始化*/ TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量 TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能 TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数 TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //计数周期,即ARR的值 TIM_TimeBaseInitStructure.TIM_Prescaler = 720- 1; //预分频器,即PSC的值 TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元 /*输出比较初始化*/ TIM_OCInitTypeDef TIM_OCInitStructure; //定义结构体变量 TIM_OCStructInit(&TIM_OCInitStructure); //结构体初始化,若结构体没有完整赋值 //则最好执行此函数,给结构体所有成员都赋一个默认值 //避免结构体初值不确定的问题 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //输出比较模式,选择PWM模式1 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性,选择为高,若选择极性为低,则输出高低电平取反 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能 TIM_OCInitStructure.TIM_Pulse = 0; //初始的CCR值 TIM_OC4Init(TIM2, &TIM_OCInitStructure); //将结构体变量交给TIM_OC3Init,配置TIM2的输出比较通道3 /*TIM使能*/ TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行 } /** * 函 数:PWM设置CCR * 参 数:Compare 要写入的CCR的值,范围:0~100 * 返 回 值:无 * 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比 * 占空比Duty = CCR / (ARR + 1) */ void PWM_SetCompare3(uint16_t Compare) { TIM_SetCompare4(TIM2, Compare); //设置CCR3的值 } #include "stm32f10x.h" // Device header #include "PWM.h" /** * 函 数:直流电机初始化 * 参 数:无 * 返 回 值:无 */ void Motor_Init(void) { /*开启时钟*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA4和PA5引脚初始化为推挽输出 PWM_Init(); //初始化直流电机的底层PWM } /** * 函 数:直流电机设置速度 * 参 数:Speed 要设置的速度,范围:-100~100 * 返 回 值:无 */ void Motor_SetSpeed(int8_t Speed) { // 强制限制为正转,忽略负速度 if (Speed < 0) Speed = 0; if (Speed > 100) Speed = 100; // 固定正转方向 GPIO_SetBits(GPIOA, GPIO_Pin_4); GPIO_ResetBits(GPIOA, GPIO_Pin_5); PWM_SetCompare3(Speed); // 设置PWM占空比 } #include "stm32f10x.h" // Device header /** * 函 数:AD初始化 * 参 数:无 * 返 回 值:无 */ void AD_Init(void) { /*开启时钟*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //开启ADC1的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟 /*设置ADC时钟*/ RCC_ADCCLKConfig(RCC_PCLK2_Div6); //选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz /*GPIO初始化*/ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA0引脚初始化为模拟输入 /*规则组通道配置*/ ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); //规则组序列1的位置,配置为通道0 /*ADC初始化*/ ADC_InitTypeDef ADC_InitStructure; //定义结构体变量 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //模式,选择独立模式,即单独使用ADC1 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据对齐,选择右对齐 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发,使用软件触发,不需要外部触发 ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //连续转换,失能,每转换一次规则组序列后停止 ADC_InitStructure.ADC_ScanConvMode = DISABLE; //扫描模式,失能,只转换规则组的序列1这一个位置 ADC_InitStructure.ADC_NbrOfChannel = 1; //通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1 ADC_Init(ADC1, &ADC_InitStructure); //将结构体变量交给ADC_Init,配置ADC1 /*ADC使能*/ ADC_Cmd(ADC1, ENABLE); //使能ADC1,ADC开始运行 /*ADC校准*/ ADC_ResetCalibration(ADC1); //固定流程,内部有电路会自动执行校准 while (ADC_GetResetCalibrationStatus(ADC1) == SET); ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1) == SET); } /** * 函 数:获取AD转换的值 * 参 数:无 * 返 回 值:AD转换的值,范围:0~4095 */ uint16_t AD_GetValue(void) { ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件触发AD转换一次 while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); //等待EOC标志位,即等待AD转换结束 // 清除转换完成标志 // ADC_ClearFlag(ADC1, ADC_FLAG_EOC); return ADC_GetConversionValue(ADC1); //读数据寄存器,得到AD转换的结果 } #include "stm32f10x.h" // Device header #include "Delay.h" int16_t Encoder_Count; //全局变量,用于计数旋转编码器的增量值 /** * 函 数:旋转编码器初始化 * 参 数:无 * 返 回 值:无 */ void Encoder_Init(void) { /*开启时钟*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启AFIO的时钟,外部中断必须开启AFIO的时钟 /*GPIO初始化*/ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); //将PB0和PB1引脚初始化为上拉输入 /*AFIO选择中断引脚*/ GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);//将外部中断的0号线映射到GPIOB,即选择PB0为外部中断引脚 GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);//将外部中断的1号线映射到GPIOB,即选择PB1为外部中断引脚 /*EXTI初始化*/ EXTI_InitTypeDef EXTI_InitStructure; //定义结构体变量 EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1; //选择配置外部中断的0号线和1号线 EXTI_InitStructure.EXTI_LineCmd = ENABLE; //指定外部中断线使能 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //指定外部中断线为中断模式 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //指定外部中断线为下降沿触发 EXTI_Init(&EXTI_InitStructure); //将结构体变量交给EXTI_Init,配置EXTI外设 /*NVIC中断分组*/ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC为分组2 //即抢占优先级范围:0~3,响应优先级范围:0~3 //此分组配置在整个工程中仅需调用一次 //若有多个中断,可以把此代码放在main函数内,while循环之前 //若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置 /*NVIC配置*/ NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量 NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //选择配置NVIC的EXTI0线 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //指定NVIC线路的抢占优先级为1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC线路的响应优先级为1 NVIC_Init(&NVIC_InitStructure); //将结构体变量交给NVIC_Init,配置NVIC外设 NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn; //选择配置NVIC的EXTI1线 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //指定NVIC线路的抢占优先级为1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //指定NVIC线路的响应优先级为2 NVIC_Init(&NVIC_InitStructure); //将结构体变量交给NVIC_Init,配置NVIC外设 } /** * 函 数:旋转编码器获取增量值 * 参 数:无 * 返 回 值:自上此调用此函数后,旋转编码器的增量值 */ int16_t Encoder_Get(void) { /*使用Temp变量作为中继,目的是返回Encoder_Count后将其清零*/ /*在这里,也可以直接返回Encoder_Count 但这样就不是获取增量值的操作方法了 也可以实现功能,只是思路不一样*/ int16_t Temp; Temp = Encoder_Count; Encoder_Count = 0; return Temp; } ///** // * 函 数:EXTI0外部中断函数 // * 参 数:无 // * 返 回 值:无 // * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行 // * 函数名为预留的指定名称,可以从启动文件复制 // * 请确保函数名正确,不能有任何差异,否则中断函数将不能进入 // */ //void EXTI0_IRQHandler(void) //{ // if (EXTI_GetITStatus(EXTI_Line0) == SET) //判断是否是外部中断0号线触发的中断 // { // /*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/ // if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) // { // if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) //PB0的下降沿触发中断,此时检测另一相PB1的电平,目的是判断旋转方向 // { // Encoder_Count --; //此方向定义为反转,计数变量自减 // } // } // EXTI_ClearITPendingBit(EXTI_Line0); //清除外部中断0号线的中断标志位 // //中断标志位必须清除 // //否则中断将连续不断地触发,导致主程序卡死 // } //} ///** // * 函 数:EXTI1外部中断函数 // * 参 数:无 // * 返 回 值:无 // * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行 // * 函数名为预留的指定名称,可以从启动文件复制 // * 请确保函数名正确,不能有任何差异,否则中断函数将不能进入 // */ //void EXTI1_IRQHandler(void) //{ // if (EXTI_GetITStatus(EXTI_Line1) == SET) //判断是否是外部中断1号线触发的中断 // { // /*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/ // if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) // { // if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) //PB1的下降沿触发中断,此时检测另一相PB0的电平,目的是判断旋转方向 // { // Encoder_Count ++; //此方向定义为正转,计数变量自增 // } // } // EXTI_ClearITPendingBit(EXTI_Line1); //清除外部中断1号线的中断标志位 // //中断标志位必须清除 // //否则中断将连续不断地触发,导致主程序卡死 // } //} void EXTI0_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line0) == SET) { if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) { if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) Encoder_Count--; else Encoder_Count++; } EXTI_ClearITPendingBit(EXTI_Line0); } } void EXTI1_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line1) == SET) { if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) { if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) Encoder_Count++; else Encoder_Count--; } EXTI_ClearITPendingBit(EXTI_Line1); } } 三.功能描述 3.1 功能概述 3.2 旋转编码器、按键、电位器 功能要求 1) 旋转编码器对电机的速度控制,只要求电机能够正转。 2) 按键对电机、oled 屏幕的控制 3) 读取 mpu6050。 4) OLED 的显示功能 5) pwm 默认输出 1000Hz。 1) 旋转编码器调整电机转速,调节范围为 0-100%,并且可用第四个按键改变旋 转编码器的调节步长。 (提示:每旋转一次旋转编码器,电机转速增加量,此为调节步长)。 2) 可用按键控制电机转速,可在 0-100%范围内调节,调节步长可在第四个按键 切换 10%或 20%。 3) 第一个按键实现 OLED 屏幕的翻页功能,在三个页面之间循环。 4) 第二、第三按键实现对数据的增减控制。只有在相应页面执行对应数据的操 作。 3.3 mpu6050 要求当 mpu6050 陀螺仪翻转后立即停止电机工作。所有操作均为禁止。 (提示,翻转后无法对单片机进行除复位操作外的任何操作。) 3.4 OLED 显示 1) 电机状态界面 电机界面显示当前系统的电机工作状态 页面参数设置如下 r: 电机转速% M_step: 旋转编码器的调节步长% r 值可通过旋转编码器和相应按键修改,范围为 0-100% M_step 通过相应按键修改,要求可修改为 10%、20% 2) 陀螺仪状态 显示 mpu6050 陀螺仪中获取的 x,y,z 轴数据,原始读数即可,不用转换角度; 翻转时禁止所有按键操作。 X: Y: Z: 3) pwm 输出状态界面 pwm 输出状态界面显示当前系统输出的 pwm 脉冲信号状态(频率 F、占空 比 CD) F:xxxHz CD:xx% 第二、第三按键可修改占空比 CD 的值,电机转速做出相应改变。 CD 为当前状态真实占空比。 四、自由发挥部分。 通过电位器控制 pwm 输出的频率,频率范围:1000-10000Hz 在 0-3V 线性变化,3-3.3V 无变化。 利用输入捕获功能获得任意一个 pwm 输出口的频率 CF。增加第四个页面,显示输入捕 获得到的频率,以及电位器的电压 Vol。 CF:xxx Hz Vol:xx V 按照以上要求修改完善代码,使代码更加合理并给出说明
09-25
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值