一、制作目标
以STM32F103C8T6单片机作为主控,使用PID控制算法,控制TB6612FNG电机驱动板模块驱动直流减速电机(带AB相编码器),实现任意设定转速的电机转速动态控制,类似于汽车的定速巡航功能,可将速度设定在固定转速,同时,设置5个按键,分别实现电机的正转、反转、停止、加速和减速控制。
二、做好的实物结果
三、模块清单
1、STM32F103C8T6单片机
2、18650电池2节3.7V串联
3、OLED屏
4、TB6612FNG电机驱动板
商品详情
5、AB相编码器直流减速电机
https://item.taobao.com/item.htm?_u=p8bd2jv1548&id=637752100260&pisk=g2HY6st-IUYcO6HYqmRlIv5hbp-oBQmq4qoCIP4c143-WDImi54g1R3EJ56Mg-VTB4aonrm0hlNsS2G0lj4GBA3rktXghVvT5DlybrqmnGEsNqBiim4mwGeqEjXgoEytf229-evHKmo4LRTH-iyhVywQAi11Srs7VRqtZm1cAmo4QPsl5BcS0NQOK5__fPt8FlEO5RNb1z97zl411VabNaZLYRws5rw5NlqAhoaj5_a7Ak5bfSwsV7ZLfRas5Rt-VzrTCPG4AIUMryWtOd_5oitIEO6seoFWZmaWEcHuD8UKcY1PahrYMyibyexp8APSb5HNbOFncXuaAq_XWPk-VxGIRUf0lXEtxf3pdOrrGqH76YxwK0yYXWw_wG6s2-a_9Y2BdaErNmcs3qIOCokmK5UUwh6as8M3OXgAbHo7hlgaTAYF3risYvl3pKQUDbiLegRmKvEXPwDSvstJ215aGu7-QabxYr_uYuUHq3fN_73Y2yxJ215aGur8-3uF_1P-k&spm=a1z09.2.0.0.3edc2e8dsKV9iY&sku_properties=-3%3A-3
四、电路原理图
五、程序组成
1、main.c中
#include "bsp_sys.h"
#include "stdio.h"
#include "key.h"extern int setpoint;
extern u8 OLED_ADC_Flag;
extern u8 OLED_Speed_Flag;extern u16 ADC_BAT_Val,ADC_BAT_temp;
extern u16 ADC_SYS_Val,ADC_SYS_temp;
extern int Encoder_B,Encoder_A;float adc11;
float adc22;
char adc1[5];
char adc2[4];uint8_t key;
uint8_t count;int Speed_B,Speed_A;
char Speed_B1[5];
char Speed_A1[5];char state=0;/*** @brief 主函数* @param 无 * @retval 无*/
int main(void)
{
//***************************设置局部变量***************************////*****************************************************************////*****************************系统初始*****************************//STM32_System_Init();//所有系统配置在这个函数里完成
// KEY_Init();//*****************************************************************//
//Car_Back();Car_Stop();OLED_ShowString(32,4,"stop ",12);while (1){key=KEY_Scan(0);if(key==1){setpoint=setpoint+20;//设定速度加20}if(key==2){setpoint=setpoint-20;//设定速度减20}
// OLED_ShowNum(16,3,setpoint,3,12); OLED_ShowNum(32,2,setpoint,3,12); //显示设置的速度if(key==3){state=1;OLED_ShowString(32,4,"forword ",12);//显示屏显示正转
// Car_Go();}if(key==4){state=2;OLED_ShowString(32,4,"reversal",12);//显示屏显示反转
// Car_Back();}if(state==1){Car_Go();}//控制电机正转if(state==2){Car_Back();}//控制电机反转if(OLED_ADC_Flag==1)//电压1S显示一次{OLED_ADC_Flag=0;adc11=ADC_BAT_Val*3.3/4096*11;sprintf(adc1,"%2.1f",adc11);
// OLED_ShowString(16,0, adc1,12); //OLED显示电池电压if(adc11<7.4){OLED_ShowString(0,0,"Low Power",12);//如果电池低于7.4V,提示低电压,需要充电}adc22=ADC_SYS_Val*3.3/4096*2;sprintf(adc2,"%2.1f",adc22);
// OLED_ShowString(96,0, adc2,12); //OLED显示电池电压// OLED_ShowNum(16,2,setpoint,3,12); OLED_ShowNum(64,1,Speed_A,3,12); //显示电机当前的实际速度}}}
2、TB6612.C中
#include "tb6612.h"//***************************TB6612配置***************************//
void TB6612_Init(void)
{TB6612_PWM_Init(3600, 1);//PWM频率初始化20KHzTB6612_GPIO_Config(); //电机驱动IO配置
}//***************************PWM频率及占空比初始化***************************//
//=====初始化PWM 20KHZ 高频可以防止电机低频时的尖叫声
// ARR= 3599 时频率为20Khz
//PB0控制PWMA--left motor,PB1控制PWMB--right motor。STBY直接拉高
//arr:自动重装寄存器,psc分频系数
//PWM的频率 = 72MHz/ARR/PCS 例如 20K = 72M/3600/1 = 20K
void TB6612_PWM_Init(u32 arr, int psc)
{TIM_OCInitTypeDef TIM_OCInitSructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;GPIO_InitTypeDef GPIO_InitStructure;//配置pwm输出端口RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8| GPIO_Pin_9;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化定时器RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);TIM_TimeBaseStructure.TIM_Period = arr-1; //自动重新装载寄存器周期的值澹ㄥ计数值澹)TIM_TimeBaseStructure.TIM_Prescaler = psc-1; //时钟分频系数TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //对外部时钟进行采样的时钟分频TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数TIM_TimeBaseInit(TIM4,&TIM_TimeBaseStructure); //参数初始化TIM_ClearFlag(TIM4, TIM_FLAG_Update);TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);//设置通道3 pwm参数TIM_OCInitSructure.TIM_OCMode = TIM_OCMode_PWM1;TIM_OCInitSructure.TIM_OutputState= TIM_OutputState_Enable;TIM_OCInitSructure.TIM_Pulse = 0; //占空比=TIM_OCInitSructure.TIM_OCPolarity = TIM_OCPolarity_High; //当定时器计数值小于CCR1_Val时为高电平TIM_OC3Init(TIM4, &TIM_OCInitSructure); //参数初始化TIM_OC3PolarityConfig(TIM4, TIM_OCPreload_Enable); //开始输出pwm//设置通道4 pwm参数TIM_OCInitSructure.TIM_OCMode = TIM_OCMode_PWM1;TIM_OCInitSructure.TIM_OutputState= TIM_OutputState_Enable;TIM_OCInitSructure.TIM_Pulse = 0; //占空比= TIM_OCInitSructure.TIM_OCPolarity = TIM_OCPolarity_High; //当定时器计数值小于CCR1_Val时为高电平TIM_OC4Init(TIM4, &TIM_OCInitSructure); //参数初始化TIM_OC4PolarityConfig(TIM4, TIM_OCPreload_Enable); //开始输出pwmTIM_ARRPreloadConfig(TIM4, ENABLE); //启动自动重装TIM_Cmd(TIM4, ENABLE); //启动定时
}//***************************占空比调节***************************//
//占空比 = TIMx_CCRx / TIMx_ARR
//moto_r:右轮电机,moto_l:左轮电机. 数值 0-3600
void TB6612_PWM_Out(u16 moto_l, u16 moto_r)
{TIM_OCInitTypeDef TIM_OCInitSructure;TIM_OCInitSructure.TIM_OCMode = TIM_OCMode_PWM1;TIM_OCInitSructure.TIM_OutputState= TIM_OutputState_Enable;//CH3 左电机TIM_OCInitSructure.TIM_Pulse = moto_l; //占空比= ccr/3600TIM_OC3Init(TIM4, &TIM_OCInitSructure); //参数初始化TIM_OC3PolarityConfig(TIM4, TIM_OCPreload_Enable); //开始输出pwm//CH4 右电机TIM_OCInitSructure.TIM_Pulse = moto_r; //占空比= ccr /3600TIM_OC4Init(TIM4, &TIM_OCInitSructure); //参数初始化TIM_OC4PolarityConfig(TIM4, TIM_OCPreload_Enable); //开始输出pwmTIM_ARRPreloadConfig(TIM4, ENABLE); //启动自动重装} void TB6612_GPIO_Config(void)
{ /*定义一个GPIO_InitTypeDef类型的结构体*/GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE); /*开启GPIO的外设时钟*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; /*选择要控制的GPIO引脚*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /*设置引脚模式为通用推挽输出*/ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /*设置引脚速率为50MHz */ GPIO_Init(GPIOB, &GPIO_InitStructure); /*调用库函数,初始化GPIO*/
}//以下单独定义每个轮子运动状态,如果不对可以修改里面的参数
//比如左轮前进,AIN1(0),AIN2(1).如果往后转了,就改成AIN1(1),AIN2(0)void Motor_Left(u8 state)
{if(state == GO) //左电机前进{AIN1(0);AIN2(1);}if(state == BACK) //左电机后退{AIN1(1);AIN2(0);}if(state == STOP) //停转{AIN1(1);AIN2(1);}
}void Motor_Right(u8 state)
{if(state == GO) //右电机前进{BIN1(0);BIN2(1);}if(state == BACK) //右电机后退{BIN1(1);BIN2(0);}if(state == STOP) //停转{BIN1(1);BIN2(1);}
}//以下定义了小车整体的运动方向。转弯靠左右电机差速实现。void Car_Go(void)
{//小车前进//左电机前进 右电机前进Motor_Left(GO); Motor_Right(GO);
}void Car_Back(void)
{//小车后退//左电机后退 右电机后退Motor_Left(BACK); Motor_Right(BACK);}void Car_Right(void)
{//小车右转圈//左电机前进 右电机后退Motor_Left(GO); Motor_Right(BACK);
}void Car_Left(void)
{//小车左转圈//左电机后退 右电机前进Motor_Left(BACK); Motor_Right(GO);
}void Car_Stop(void)
{//小车停车//左电机停止 右电机停止Motor_Left(STOP); Motor_Right(STOP);
}
3、car.c中
#include "car.h"
#include "outputdata.h"
#include "PID.h"u8 temp1=0;
u8 OLED_Timer1=0;//时间片,更新ADC
u8 OLED_Timer2=0;//时间片,更新速度
u8 Encoder_Timer = 0;u8 OLED_ADC_Flag=0;
u8 OLED_Speed_Flag=0;u16 ADC_BAT_Val,ADC_BAT_temp;
u16 ADC_SYS_Val,ADC_SYS_temp;int Encoder_B,Encoder_A;
extern float OutData[4];
extern int Speed_A,Speed_B;uint16_t jsb = 1320;//11x30x4,电机的输出轴转一圈产生的脉冲数量。11是电机霍尔转动一圈的脉冲数量,30是减速比,4是四倍频
//设置目标转速
int setpoint = 60;//RPMint Moto_A,Moto_B;
//目标转速转换成脉冲数量,因为最后PID控制的是脉冲数量
#define Set_Moto_A setpoint*jsb/1200
#define Set_Moto_B setpoint*jsb/1200
//Time1定时器1中断服务函数
//10ms定时
void TIM1_UP_IRQHandler(void)
{if(TIM_GetFlagStatus(TIM1, TIM_IT_Update) != RESET) //时间到了{TIM_ClearITPendingBit(TIM1, TIM_FLAG_Update);//清中断LED_Flash(50);//500ms闪烁一次temp1++;OLED_Timer1++;OLED_Timer2++;Encoder_Timer++;if(Encoder_Timer>=5)//电机转速50ms采样一次{Encoder_Timer = 0;Encoder_A=Read_Encoder(2)-30000; //读取编码器,计算出变化量Encoder_B=-((Read_Encoder(3)-30000)); //读取编码器,计算出变化量,负号是因为两个电机 相对位置180°if(Encoder_A < 0){Encoder_A = -Encoder_A;}if(Encoder_B < 0){Encoder_B = -Encoder_B;}//PID计算Moto_B = PID_VelocityPidB(Set_Moto_B, Encoder_B);Moto_A = PID_VelocityPidA(Set_Moto_A, Encoder_A);//PWM占空比给定时器,控制电机TIM4->CCR3=Moto_B; //更新pwmTIM4->CCR4=Moto_A; //更新pwm//编码器脉冲数转换成圈数 单位RPMSpeed_A=Encoder_A*1200/1320;Speed_B=Encoder_B*1200/1320;//虚拟示波器显示OutData[0] = Speed_A;OutData[1] = Speed_B;OutPut_Data();//打印数据,用虚拟示波器查看}//更新电压if(temp1>=10)//100ms{temp1=0;ADC_BAT_temp += Get_BAT_ADC();//ADC 采样 电池电压ADC_SYS_temp += Get_SYS_ADC();//aDC 采样 系统电压ADC_BAT_Val = ADC_BAT_temp;ADC_SYS_Val = ADC_SYS_temp;ADC_BAT_temp = 0;ADC_SYS_temp = 0;}//OLED显示更新if(OLED_Timer1 >= 100)//1S 更新一次ADC{OLED_Timer1=0;OLED_ADC_Flag = 1;}}}
4、PID.C中
#include "PID.h"//PID参数
float MotorA_kp=200;
float MotorA_ki=2;
float MotorA_kd=0; float MotorB_kp=200;
float MotorB_ki=2;
float MotorB_kd=0; #define PID_SCALE 0.01f //PID缩放系数int16_t PID_VelocityPidA(float Spd_Target, int16_t Spd_Now)
{static float Motor_Pwm_Out;static float bias,bias_last,bias_integral = 0;float para;bias = Spd_Target - Spd_Now;bias_integral += bias;para = MotorA_kp*bias*PID_SCALE + MotorA_kd*(bias-bias_last)*PID_SCALE + MotorA_ki*bias_integral*PID_SCALE;if(para<-1 || para>1){Motor_Pwm_Out +=para; }if(Motor_Pwm_Out > 3500) Motor_Pwm_Out = 3500;if(Motor_Pwm_Out < 0)Motor_Pwm_Out = 0;bias_last = bias; return Motor_Pwm_Out;
} int16_t PID_VelocityPidB(float Spd_Target, int16_t Spd_Now)
{static float Motor_Pwm_Out;static float bias,bias_last,bias_integral = 0;float para;bias = Spd_Target - Spd_Now;bias_integral += bias;para = MotorB_kp*bias*PID_SCALE + MotorB_kd*(bias-bias_last)*PID_SCALE + MotorB_ki*bias_integral*PID_SCALE;if(para<-1 || para>1){Motor_Pwm_Out +=para; }if(Motor_Pwm_Out > 3500) Motor_Pwm_Out = 3500;if(Motor_Pwm_Out < 0)Motor_Pwm_Out = 0;bias_last = bias; return Motor_Pwm_Out;
}
5、key.c中
#include "key.h"
#include "delay.h"//按键初始化函数
//PA15和PC5 设置成输入
void KEY_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能PORTA,PORTC时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;//PC15// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;; //设置成上拉输入GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;; //设置成上拉输入GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOC15GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;//PC14GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;; //设置成上拉输入GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOC14GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PC13GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;; //设置成上拉输入 GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOC13}
//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//返回值:
//0,没有任何按键按下
//KEY0_PRES,KEY0按下
//KEY1_PRES,KEY1按下
//WKUP_PRES,WK_UP按下
//注意此函数有响应优先级,KEY0>KEY1>WK_UP!!
u8 KEY_Scan(u8 mode)
{ static u8 key_up=1;//按键按松开标志if(mode)key_up=1; //支持连按 if(key_up&&(KEY0==0||KEY1==0||KEY2==0||KEY3==0)){Delay_ms(10);//去抖动 key_up=0;if(KEY0==0)return KEY0_PRES;else if(KEY1==0)return KEY1_PRES;else if(KEY2==0)return KEY2_PRES; else if(KEY3==0)return KEY3_PRES; }else if(KEY0==1&&KEY1==1&&KEY2==1&&KEY3==1)key_up=1; return 0;// 无按键按下
}
6、Encoder.c中
#include "Encoder.h"//***************************定时器2初始化 ,使用编码器功能***************************//
//左电机编码器计数
//PA15----接 编码器A相 或者电机驱动的B1A标识
//PB3 ----接 编码器B相 或者电机驱动的B1B标识
void Encoder_Init_TIM2(void)
{GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_ICInitTypeDef TIM_ICInitStructure; //GPIO功能时钟使能RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //禁用JTAGGPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE);//配置IO口为复用功能-定时器通道GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //复用功能GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度100MHzGPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;GPIO_Init(GPIOA, &GPIO_InitStructure);//TIM时钟使能RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//Timer configuration in Encoder mode TIM_DeInit(TIM2);TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);TIM_TimeBaseStructure.TIM_Prescaler = 0x0; // No prescaling TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);TIM_ICStructInit(&TIM_ICInitStructure);TIM_ICInitStructure.TIM_ICFilter = 0;TIM_ICInit(TIM2, &TIM_ICInitStructure);//Reset counterTIM2->CNT = 0;TIM_Cmd(TIM2, ENABLE);
}//***************************定时器3初始化 ,使用编码器功能***************************//
//左电机编码器计数
//PB4----接 编码器A相 或者电机驱动的B2A标识
//PB5----接 编码器B相 或者电机驱动的B2B标识
void Encoder_Init_TIM3(void)
{GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_ICInitTypeDef TIM_ICInitStructure; //GPIO功能时钟使能RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);//配置IO口为复用功能-定时器通道GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //复用功能GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度100MHzGPIO_Init(GPIOB, &GPIO_InitStructure);//TIM时钟使能RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3 , ENABLE); //这个就是重映射功能函数//Timer configuration in Encoder mode TIM_DeInit(TIM3);TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);TIM_TimeBaseStructure.TIM_Prescaler = 0x0; // No prescaling TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);TIM_ICStructInit(&TIM_ICInitStructure);TIM_ICInitStructure.TIM_ICFilter = 0;TIM_ICInit(TIM3, &TIM_ICInitStructure);//Reset counterTIM_SetCounter(TIM3,0);TIM_Cmd(TIM3, ENABLE); }/**************************************************************************
函数功能:单位时间读取编码器计数
入口参数:定时器
返回 值:速度值
**************************************************************************/
int Read_Encoder(u8 TIMX)
{int Encoder_TIM; switch(TIMX){case 2: Encoder_TIM= (short)TIM2 -> CNT; TIM2 -> CNT=30000;break;case 3: Encoder_TIM= (short)TIM3 -> CNT; TIM3 -> CNT=30000;break; //case 4: Encoder_TIM= (short)TIM4 -> CNT; TIM4 -> CNT=30000;break; // case 5: Encoder_TIM= (short)TIM5 -> CNT; TIM5 -> CNT=30000;break;default: Encoder_TIM=0;}return Encoder_TIM;
}
7、OLED.C中
#include "oled.h"
#include "stdlib.h"
#include "oledfont.h"
#include "delay.h"
#include "bsp_iic.h"
//OLED的显存
//存放格式如下.
//[0]0 1 2 3 ... 127
//[1]0 1 2 3 ... 127
//[2]0 1 2 3 ... 127
//[3]0 1 2 3 ... 127
//[4]0 1 2 3 ... 127
//[5]0 1 2 3 ... 127
//[6]0 1 2 3 ... 127
//[7]0 1 2 3 ... 127 void Write_IIC_Command(unsigned char IIC_Command)
{IIC_Start();IIC_Send_Byte(0x78); //Slave address,SA0=0IIC_Wait_Ack(); IIC_Send_Byte(0x00); //write commandIIC_Wait_Ack(); IIC_Send_Byte(IIC_Command); IIC_Wait_Ack(); IIC_Stop();
}
/**********************************************
// IIC Write Data
**********************************************/
void Write_IIC_Data(unsigned char IIC_Data)
{IIC_Start();IIC_Send_Byte(0x78); //D/C#=0; R/W#=0IIC_Wait_Ack(); IIC_Send_Byte(0x40); //write dataIIC_Wait_Ack(); IIC_Send_Byte(IIC_Data);IIC_Wait_Ack(); IIC_Stop();
}
void OLED_WR_Byte(unsigned dat,unsigned cmd)
{if(cmd){Write_IIC_Data(dat);}else {Write_IIC_Command(dat);}}/********************************************
// fill_Picture
********************************************/
void fill_picture(unsigned char fill_Data)
{unsigned char m,n;for(m=0;m<8;m++){OLED_WR_Byte(0xb0+m,0); //page0-page1OLED_WR_Byte(0x00,0); //low column start addressOLED_WR_Byte(0x10,0); //high column start addressfor(n=0;n<128;n++){OLED_WR_Byte(fill_Data,1);}}
}/***********************Delay****************************************/
void Delay_50ms(unsigned int Del_50ms)
{unsigned int m;for(;Del_50ms>0;Del_50ms--)for(m=6245;m>0;m--);
}void Delay_1ms(unsigned int Del_1ms)
{unsigned char j;while(Del_1ms--){ for(j=0;j<123;j++);}
}//坐标设置void OLED_Set_Pos(unsigned char x, unsigned char y)
{ OLED_WR_Byte(0xb0+y,OLED_CMD);OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);OLED_WR_Byte((x&0x0f),OLED_CMD);
}
//开启OLED显示
void OLED_Display_On(void)
{OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令OLED_WR_Byte(0X14,OLED_CMD); //DCDC ONOLED_WR_Byte(0XAF,OLED_CMD); //DISPLAY ON
}
//关闭OLED显示
void OLED_Display_Off(void)
{OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令OLED_WR_Byte(0X10,OLED_CMD); //DCDC OFFOLED_WR_Byte(0XAE,OLED_CMD); //DISPLAY OFF
}
//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!
void OLED_Clear(void)
{ u8 i,n; for(i=0;i<8;i++) { OLED_WR_Byte (0xb0+i,OLED_CMD); //设置页地址(0~7)OLED_WR_Byte (0x00,OLED_CMD); //设置显示位置—列低地址OLED_WR_Byte (0x10,OLED_CMD); //设置显示位置—列高地址 for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA); } //更新显示
}
void OLED_On(void)
{ u8 i,n; for(i=0;i<8;i++) { OLED_WR_Byte (0xb0+i,OLED_CMD); //设置页地址(0~7)OLED_WR_Byte (0x00,OLED_CMD); //设置显示位置—列低地址OLED_WR_Byte (0x10,OLED_CMD); //设置显示位置—列高地址 for(n=0;n<128;n++)OLED_WR_Byte(1,OLED_DATA); } //更新显示
}
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示
//size:选择字体 16/12
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size)
{ unsigned char c=0,i=0; c=chr-' ';//得到偏移后的值 if(x>Max_Column-1){x=0;y=y+2;}if(Char_Size ==16){OLED_Set_Pos(x,y); for(i=0;i<8;i++)OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);OLED_Set_Pos(x,y+1);for(i=0;i<8;i++)OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);}else { OLED_Set_Pos(x,y);for(i=0;i<6;i++)OLED_WR_Byte(F6x8[c][i],OLED_DATA);}
}
//m^n函数
u32 oled_pow(u8 m,u8 n)
{u32 result=1; while(n--)result*=m; return result;
}
//显示2个数字
//x,y :起点坐标
//len :数字的位数
//size:字体大小
//mode:模式 0,填充模式;1,叠加模式
//num:数值(0~4294967295);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size2)
{ u8 t,temp;u8 enshow=0; for(t=0;t<len;t++){temp=(num/oled_pow(10,len-t-1))%10;if(enshow==0&&t<(len-1)){if(temp==0){OLED_ShowChar(x+(size2/2)*t,y,' ',size2);continue;}else enshow=1; }OLED_ShowChar(x+(size2/2)*t,y,temp+'0',size2); }
}
//显示一个字符号串
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 Char_Size)
{unsigned char j=0;while (chr[j]!='\0'){ OLED_ShowChar(x,y,chr[j],Char_Size);x+=8;if(x>120){x=0;y+=2;}j++;}
}
//显示汉字
void OLED_ShowCHinese(u8 x,u8 y,u8 no)
{ u8 t,adder=0;OLED_Set_Pos(x,y); for(t=0;t<16;t++){OLED_WR_Byte(Hzk[2*no][t],OLED_DATA);adder+=1;} OLED_Set_Pos(x,y+1); for(t=0;t<16;t++){ OLED_WR_Byte(Hzk[2*no+1][t],OLED_DATA);adder+=1;}
}
/***********功能描述:显示显示BMP图片128×64起始点坐标(x,y),x的范围0~127,y为页的范围0~7*****************/
void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[])
{ unsigned int j=0;unsigned char x,y;if(y1%8==0) y=y1/8; else y=y1/8+1;for(y=y0;y<y1;y++){OLED_Set_Pos(x0,y);for(x=x0;x<x1;x++){ OLED_WR_Byte(BMP[j++],OLED_DATA); }}
} //初始化SSD1306
void OLED_Init(void)
{ IIC_Init();Delay_ms(800);OLED_WR_Byte(0xAE,OLED_CMD);//--display offOLED_WR_Byte(0x00,OLED_CMD);//---set low column addressOLED_WR_Byte(0x10,OLED_CMD);//---set high column addressOLED_WR_Byte(0x40,OLED_CMD);//--set start line address OLED_WR_Byte(0xB0,OLED_CMD);//--set page addressOLED_WR_Byte(0x81,OLED_CMD); // contract controlOLED_WR_Byte(0xFF,OLED_CMD);//--128 OLED_WR_Byte(0xA1,OLED_CMD);//set segment remap OLED_WR_Byte(0xA6,OLED_CMD);//--normal / reverseOLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)OLED_WR_Byte(0x3F,OLED_CMD);//--1/32 dutyOLED_WR_Byte(0xC8,OLED_CMD);//Com scan directionOLED_WR_Byte(0xD3,OLED_CMD);//-set display offsetOLED_WR_Byte(0x00,OLED_CMD);//OLED_WR_Byte(0xD5,OLED_CMD);//set osc divisionOLED_WR_Byte(0x80,OLED_CMD);//OLED_WR_Byte(0xD8,OLED_CMD);//set area color mode offOLED_WR_Byte(0x05,OLED_CMD);//OLED_WR_Byte(0xD9,OLED_CMD);//Set Pre-Charge PeriodOLED_WR_Byte(0xF1,OLED_CMD);//OLED_WR_Byte(0xDA,OLED_CMD);//set com pin configuartionOLED_WR_Byte(0x12,OLED_CMD);//OLED_WR_Byte(0xDB,OLED_CMD);//set VcomhOLED_WR_Byte(0x30,OLED_CMD);//OLED_WR_Byte(0x8D,OLED_CMD);//set charge pump enableOLED_WR_Byte(0x14,OLED_CMD);//OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel
}
8、bsp_sys.c中
#include "bsp_sys.h"
#include "key.h"void STM32_System_Init(void)
{ //STM32内部初始化Delay_Init(72); //滴答时钟初始化NVIC_Config(); //中断配置配置初始化LED_GPIO_Init(); //LED初始化USART1_Init(115200); //串口初始化IIC_Init(); //IIC模拟初始化ADC_Power_Init();KEY_Init();OLED_Init();OLED_Clear();OLED_ShowString(16,0,"PID Speed Ctr",12);
// OLED_ShowString(0,0,"B: V , ",12);
// OLED_ShowString(80,0,"S: V",12);OLED_ShowString(0,1,"Current: RPM",12);OLED_ShowString(0,2,"Set: RPM",12);OLED_ShowString(0,3,"Direction: ",12);TB6612_Init();//电机驱动Encoder_Init_TIM2();TIM2 -> CNT=30000;//编码器Encoder_Init_TIM3();TIM3 -> CNT=30000;Timer1_Init();//最后启动定时器1Delay_ms(100); //等待初始化完成
}
六、程序源码及原理图下载链接
https://download.csdn.net/download/jacklood/90532730