#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
按照以上要求在不改变代码框架上整合并修改完善代码,使代码更加合理并给出说明,按键只保留单击模式