stm32学习笔记1——stm32f103电机闭环控制(标准库)

一.电机驱动及电机简介

1.TB6612电机驱动基础引脚简介

AIN1和AIN2功能:电机输出端 控制电机转动方向

PWM功能:控制信号输入  控制电机转速 

STBY功能:正常工作\待机状态控制端

一个使能信号端,当 STBY=1时,正常工作,输入PWM信号,电机可正常运行;当 STBY=0 时,电机驱动处于待机状态,输入信号,电机不会运行。(一般接3.3V即可)

E1A E1B:编码器A B相         编码器原理运用 见下方链接

编码器分类及原理和测速应用

ADC:模数转换器  用于获取电池电压

2.MG513电机 霍尔编码器

具体见下方链接

电机学习:直流电机原理、结构、特点

二.STM32f103VET6电机驱动代码简介

1.电机初始化(驱动两个电机)

/*
PE4  --->AIN1  PE2 --->AIN2
PE5  --->BIN1  PE6 --->BIN2
*/

void Motor_Init(void)   //电机初始化
{
  GPIO_InitTypeDef GPIO_InitStructure;            //定义结构体GPIO_InitStructure
  RCC_APB2PeriphClockCmd(AIN1_GPIO_CLK, ENABLE);  // 使能PE端口时钟  
  //选择io口	
  GPIO_InitStructure.GPIO_Pin =  AIN1_GPIO_PIN| AIN2_GPIO_PIN|BIN1_GPIO_PIN|BIN2_GPIO_PIN;	 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   //推挽输出,增大电流输出能力  
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  //IO口速度
	GPIO_Init(GPIOE, &GPIO_InitStructure);           //GBIOE初始化  
  
}

电机转动方向控制代码   对应上述tb6612真值表

void Motor_start(int8_t mode)   //电机模式选择  正转 反转
{
	if(mode==1)       //正转
	{
	 GPIO_SetBits(AIN1_GPIO_PORT, AIN1_GPIO_PIN);	 // 高电平   PE4 --- AIN1      1   
	 GPIO_ResetBits(AIN2_GPIO_PORT, AIN2_GPIO_PIN);	 // 低电平   PE2 --- AIN2      0
	
	 GPIO_SetBits(BIN1_GPIO_PORT, BIN1_GPIO_PIN);    // 高电平   PE5 --- BIN1      1
	 GPIO_ResetBits(BIN2_GPIO_PORT, BIN2_GPIO_PIN);  // 低电平   PE6 --- BIN2      0
	
	}
	else if(mode==0)   //反转
	{
	 GPIO_ResetBits(AIN1_GPIO_PORT, AIN1_GPIO_PIN);	 // 低电平   PE4 --- AIN1      0   
	 GPIO_SetBits(AIN2_GPIO_PORT, AIN2_GPIO_PIN);	 // 高电平   PE2 --- AIN2      1
	
	 GPIO_ResetBits(BIN1_GPIO_PORT, BIN1_GPIO_PIN);  // 低电平   PE5 --- BIN1      0
	 GPIO_SetBits(BIN2_GPIO_PORT, BIN2_GPIO_PIN);    // 高电平   PE6 --- BIN2      1
	}
}


void Motor_stop(void)   //电机模式 停止
{
	 GPIO_ResetBits(AIN1_GPIO_PORT, AIN1_GPIO_PIN);	 // 低电平   PE4 --- AIN1      0   
	 GPIO_ResetBits(AIN2_GPIO_PORT, AIN2_GPIO_PIN);	 // 低电平   PE2 --- AIN2      0
	
	 GPIO_ResetBits(BIN1_GPIO_PORT, BIN1_GPIO_PIN);  // 低电平   PE5 --- BIN1      0
	 GPIO_ResetBits(BIN2_GPIO_PORT, BIN2_GPIO_PIN);  // 低电平   PE6 --- BIN2      0
}

motor.h 宏定义

#define AIN1_GPIO_PIN    GPIO_Pin_4
#define AIN1_GPIO_PORT   GPIOE
#define AIN1_GPIO_CLK    RCC_APB2Periph_GPIOE   

#define AIN2_GPIO_PIN    GPIO_Pin_2  
#define AIN2_GPIO_PORT   GPIOE
#define AIN2_GPIO_CLK    RCC_APB2Periph_GPIOE   

#define BIN1_GPIO_PIN    GPIO_Pin_5
#define BIN1_GPIO_PORT   GPIOE
#define BIN1_GPIO_CLK    RCC_APB2Periph_GPIOE   

#define BIN2_GPIO_PIN    GPIO_Pin_6
#define BIN2_GPIO_PORT   GPIOE
#define BIN2_GPIO_CLK    RCC_APB2Periph_GPIOE


void Motor_Init(void);   //电机初始化
void Motor_start(int8_t mode);   //电机模式选择  正转 反转
void Motor_stop(void);   //电机模式 停止

2.配置PWM

默认配置系统时钟为72M

//pwm初始化     参数: arr:设为一个时钟频率的最大值   psc: 预分频值
//以PWM_Int(7199,0)为例;    
//初始化pwm输出  72000 000/psc+1=72000 000  记一个数时间为1/7200 0000   
//每(7199+1)个计数中断一次    7200 0000/7199+1=10000 
//从0开始的  所以要+1                   
void PWM_Int(uint16_t arr,uint16_t psc)
{
	GPIO_InitTypeDef GPIO_InitStructure;                //定义结构体GPIO_InitStructure
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;      //定义结构体TIM_TimeBaseStructure   
	TIM_OCInitTypeDef TIM_OCInitStructure;              //定义结构体TIM_OCInitStructure
	
	RCC_APB2PeriphClockCmd(PWMA_GPIO_CLK,ENABLE);        //使能PA端口时钟
	RCC_APB2PeriphClockCmd(PWMA_TIM_CLK,ENABLE);         //使能定时器1
	RCC_APB1PeriphClockCmd(PWMB_TIM_CLK,ENABLE);         //使能定时器2
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;         //复用模式输出
	GPIO_InitStructure.GPIO_Pin  = PWMA_GPIO_PIN;           //PA8
	GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;        //IO口速度
	GPIO_Init(PWMA_GPIO_PORT,&GPIO_InitStructure);          //GPIO初始化
 
//设置自动重装载寄存器的值   决定每多少个数 记一次中断 即决定占空比的周期 CRR计数器决定占空比
    TIM_TimeBaseStructure.TIM_Period = arr;
	
    TIM_TimeBaseStructure.TIM_Prescaler = psc;      //预分频值    决定计一个数的时间
	TIM_TimeBaseStructure.TIM_ClockDivision = 0;                    //时钟分割
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;     //向上计数
	TIM_TimeBaseInit(PWMA_TIM,&TIM_TimeBaseStructure);              //配置定时器1
	
	TIM_OCInitStructure.TIM_OCMode= TIM_OCMode_PWM1;                //PWM脉冲宽度调制1
	TIM_OCInitStructure.TIM_Pulse = 0;                   //设置待装入捕获比较寄存器的脉冲值
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;       //设置TIM输出极性为高
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;   //比较输出使能
	TIM_OC1Init(PWMA_TIM,&TIM_OCInitStructure);                     //定时器1通道1初始化

	TIM_CtrlPWMOutputs(PWMA_TIM,ENABLE);                      //主输出使能
	TIM_OC1PreloadConfig(PWMA_TIM,TIM_OCPreload_Enable);      //启用TIM1外设的预装载寄存器 
	TIM_ARRPreloadConfig(PWMA_TIM,ENABLE);                    //使能自动装载允许位
	TIM_Cmd(PWMA_TIM,ENABLE);                                 //启动定时器1
	
	//TIM2
	GPIO_InitStructure.GPIO_Pin  = PWMB_GPIO_PIN;           
	GPIO_Init(PWMB_GPIO_PORT,&GPIO_InitStructure);            //GPIO初始化
	
	TIM_TimeBaseInit(PWMB_TIM,&TIM_TimeBaseStructure);        //配置定时器2
	TIM_OC2Init(PWMB_TIM,&TIM_OCInitStructure);               //定时器2通道2初始化
	TIM_CtrlPWMOutputs(PWMB_TIM,ENABLE);                      //主输出使能
    TIM_OC2PreloadConfig(PWMB_TIM,TIM_OCPreload_Enable);      //使能预装载寄存器
	TIM_ARRPreloadConfig(PWMB_TIM,ENABLE);                    //使能自动装载允许位
    TIM_Cmd(PWMB_TIM,ENABLE);                                 //启动定时器1
}

设置占空比函数

void Set_PWMA(int PWM)          //设置占空比函数 PWM最大不能超过7199
{
   TIM_SetCompare1(PWMA_TIM,PWM);//设置TIM1通道1的占空比 = pwm/7200 
}
void Set_PWMB(int PWM)
{
   TIM_SetCompare2(PWMB_TIM,PWM);   //设置占空比函数
}

pwm.h 宏定义

#define PWMA_GPIO_PIN    GPIO_Pin_8
#define PWMA_GPIO_PORT   GPIOA
#define PWMA_GPIO_CLK    RCC_APB2Periph_GPIOA  

#define PWMA_TIM         TIM1   
#define PWMA_TIM_CLK     RCC_APB2Periph_TIM1


#define PWMB_GPIO_PIN    GPIO_Pin_1
#define PWMB_GPIO_PORT   GPIOA
#define PWMB_GPIO_CLK    RCC_APB2Periph_GPIOA 

#define PWMB_TIM         TIM2
#define PWMB_TIM_CLK     RCC_APB1Periph_TIM2

void PWM_Int(uint16_t arr,uint16_t psc);         //PWM 初始化函数
void Set_PWMA(int PWM);            //设置占空比函数
void Set_PWMB(int PWM);            //设置占空比函数

3.主函数(开环控制)

#include "stm32f10x.h"   
#include "stm32f10x_it.h"
#include "systick.h"

#include "motor.h"
#include "pwm.h"
int main(void)
{ 
	SystemInit();             //配置系统时钟为72M

	Motor_Init();             //电机gpio初始化
	PWM_Int(7199,0);          //电机pwm初始化
	Motor_start(0);           //控制电机反转
	
	SysTick_Delay_Ms(100);   //延时100ms 等待初始化
	
	while(1) 
	{ 		
    Set_PWMA(3600);          //设置占空比函数 3600/7200=1/2
    Set_PWMB(3600);
	}
}

三.STM32f103VET6 霍尔编码器PID闭环控制

1.配置编码器

采用定时器的输入捕获功能进行编码器脉冲的采集。(也可以采用外部中断 见下方链接)

stm32读取编码器的两种方式

配置编码器GPIO函数

void Encoder_GPIO_Config(void)  //编码器GPIO初始化   重定义TIM3和TIM4引脚
{
  GPIO_InitTypeDef GPIO_InitStructure; //定义一个引脚初始化的结构体
  
  RCC_APB2PeriphClockCmd(E1A_GPIO_CLK, ENABLE); //使能GPIOD时钟
  RCC_APB1PeriphClockCmd(E1A_TIM_CLK, ENABLE);	//使能TIM4时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);   //使能AFIO时钟
  GPIO_PinRemapConfig(GPIO_Remap_TIM4, ENABLE);    //重映射TIM4
	
  GPIO_InitStructure.GPIO_Pin = E1A_GPIO_PIN|E1B_GPIO_PIN;	//PD13、PD12
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;   //上拉输入
  GPIO_Init(E1A_GPIO_PORT, &GPIO_InitStructure);  //根据GPIO_InitStructure的参数初始化
  
  RCC_APB2PeriphClockCmd(E2A_GPIO_CLK, ENABLE); //使能GPIOC时钟
  RCC_APB1PeriphClockCmd(E2A_TIM_CLK, ENABLE);	//使能TIM3时钟 
  GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE);    //重映射TIM4
	
  GPIO_InitStructure.GPIO_Pin = E2A_GPIO_PIN|E2B_GPIO_PIN;	//PC6、PC7
  GPIO_Init(E2A_GPIO_PORT, &GPIO_InitStructure);	  //根据GPIO_InitStructure的参数初始化
}

将定时器配置为编码器接口模式   配置定时器中断优先级NVIC

//把TIM3,TIM4初始化为编码器接口模式
void Encoder_TIM_Config(void)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;//定义一个定时器初始化的结构体
    TIM_ICInitTypeDef TIM_ICInitStructure; //定义一个定时器编码器模式初始化的结构体
	
	RCC_APB1PeriphClockCmd(E1A_TIM_CLK,ENABLE); //使能E1的TIM4时钟
	RCC_APB2PeriphClockCmd(E2A_TIM_CLK,ENABLE);  //使能E2的TIM3时钟
  
	//时基配置
	TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); //清除之前配置
	TIM_TimeBaseStructure.TIM_Period = 65535; //设定计数器自动重装值
	TIM_TimeBaseStructure.TIM_Prescaler = 0; // 预分频器 
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //选择时钟分频:不分频
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
	//根据TIM_TimeBaseInitStruct的参数 初始化定时器TIM4
    TIM_TimeBaseInit(E1A_TIM, &TIM_TimeBaseStructure); 
    
	//输入捕获结构体初始化
	TIM_ICStructInit(&TIM_ICInitStructure); //把TIM_ICInitStruct 中的每一个参数按缺省值填入
	TIM_EncoderInterfaceConfig(E1A_TIM, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, 
    TIM_ICPolarity_Rising);//使用编码器模式3:CH1、CH2同时计数,四分频
	TIM_ICInitStructure.TIM_ICFilter = 10;  //设置滤波器长度
    //将TIM_ICInitStructure参数初始化定时器TIM4编码器模式	
    TIM_ICInit(E1A_TIM, &TIM_ICInitStructure); 

	TIM_ClearFlag(E1A_TIM, TIM_FLAG_Update);   //清除TIM的更新标志位
	TIM_ITConfig(E1A_TIM, TIM_IT_Update, ENABLE);  //启用定时器中断
	
	TIM_SetCounter(E1A_TIM,0);   //定时器计数值清0
	TIM_Cmd(E1A_TIM, ENABLE); //使能定时器4
  
    //TIM3
    //根据TIM_TimeBaseInitStruct的参数初始化定时器TIM3
	TIM_TimeBaseInit(E2A_TIM, &TIM_TimeBaseStructure); 
  
	TIM_EncoderInterfaceConfig(E2A_TIM, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, 
    TIM_ICPolarity_Rising);//使用编码器模式3:CH1、CH2同时计数,四分频
	TIM_ICInitStructure.TIM_ICFilter = 10;  //设置滤波器长度
    //TIM_ICInitStructure参数初始化定时器TIM4编码器模式
    TIM_ICInit(E2A_TIM, &TIM_ICInitStructure); 
  
	TIM_ITConfig(E2A_TIM, TIM_IT_Update, ENABLE);  //启用定时器中断
	TIM_ClearFlag(E2A_TIM, TIM_FLAG_Update);   //清除TIM的更新标志位
	
	TIM_SetCounter(E2A_TIM,0);   //定时器计数值清0
	TIM_Cmd(E2A_TIM, ENABLE); //使能定时器3

}



static void TIM3_NVIC_Config(void)   //TIM3中断设置
{
    NVIC_InitTypeDef NVIC_InitStructure; 
    // 设置中断组为0
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);		
		// 设置中断来源
    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn ;	
		// 设置主优先级为 0
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	 
	  // 设置抢占优先级为3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;	
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

static void TIM4_NVIC_Config(void)   //TIM4中断设置
{
    NVIC_InitTypeDef NVIC_InitStructure; 
    // 设置中断组为0
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);		
		// 设置中断来源
    NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn ;	
		// 设置主优先级为 0
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	 
	  // 设置抢占优先级为3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;	
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

编码器脉冲数读取函数

void Read_Encoder_L(void)
{
	 Encoder_L= TIM_GetCounter(TIM4);   //获取外部计数  四倍频要除以4
	 if(Encoder_L>0x8000)Encoder_L=Encoder_L-0xffff;    //判断方向  
     //大于0xffff的一半 即为负数  因为CNT计数器值为int16 最大为0x8000
     TIM_SetCounter(TIM4,0);         //清除外部计数 
}


void Read_Encoder_R(void)
{
	 Encoder_R= TIM_GetCounter(TIM3);    //获取外部计数  四倍频要除以4
	 if(Encoder_R>0x8000)Encoder_R=Encoder_R-0xffff;
     //大于0xffff的一半 即为负数  因为CNT计数器值为int16 最大为0x8000	 
     TIM_SetCounter(TIM3,0);          //清除外部计数           

}

Encoder.h宏定义

#define E1A_GPIO_PIN    GPIO_Pin_13
#define E1A_GPIO_PORT   GPIOD
#define E1A_GPIO_CLK    RCC_APB2Periph_GPIOD 
#define E1A_TIM         TIM4
#define E1A_TIM_CLK     RCC_APB1Periph_TIM4

#define E1B_GPIO_PIN    GPIO_Pin_12
#define E1B_GPIO_PORT   GPIOD
#define E1B_GPIO_CLK    RCC_APB2Periph_GPIOD 
#define E1B_TIM         TIM4
#define E1B_TIM_CLK     RCC_APB1Periph_TIM4

#define E2A_GPIO_PIN    GPIO_Pin_6
#define E2A_GPIO_PORT   GPIOC
#define E2A_GPIO_CLK    RCC_APB2Periph_GPIOC 
#define E2A_TIM         TIM3
#define E2A_TIM_CLK     RCC_APB1Periph_TIM3

#define E2B_GPIO_PIN    GPIO_Pin_7
#define E2B_GPIO_PORT   GPIOC
#define E2B_GPIO_CLK    RCC_APB2Periph_GPIOC
#define E2B_TIM         TIM8
#define E2B_TIM_CLK     RCC_APB2Periph_TIM8

void Encoder_Init(void);   //初始化编码器
static void TIM3_NVIC_Config(void);
static void TIM4_NVIC_Config(void);

void Encoder_Init(void);

2.增量式PI闭环控制代码

PID位置式和增量式

霍尔增量式编码器左右车轮线速度的计算

可以先了解位置式和增量式PID的概念和线速度计算 上方连接

增量式PI速度环

/增量式PI控制速度环/
//编码器线数13ppr*30(减速比)=390   轮子转一圈有390个脉冲  四倍频要除以4  
//速度计算公式:=脉冲数/390/4*0.53407(轮子周长)*0.01(单位时间)    单位m/s

int16_t Left_Goal;
int16_t Left_Current_Error;          //当前误差
int16_t Left_Last_Error = 0;         //上次误差
int16_t Left_Previous_Error = 0;     //上上次误差
float   Left_P = 0.2;
float   Left_I = 0.80;
float   Left_Speed_PID_OUT;          //电机脉冲输出    OUT/7200 = 占空比
int16_t Left_Pro_Speed_PID_OUT;      //限幅输出
float   Left_Speed = 0.00;            //左轮转速 m/s

int16_t Left_Speed_Control(float Goal,int16_t max_left,int16_t min_left)   //左电机PID控制
{ 
	Left_Goal = (int16_t)(Goal * 29.21);    //速度转编码器 
    Left_Current_Error = Left_Goal - Encoder_L;     //计算当前误差
                   
	Left_Speed_PID_OUT += ((Left_P * (Left_Current_Error - Left_Last_Error)) +
	                       Left_I * Left_Current_Error);
	
	//更新上次误差和上上次误差
	Left_Previous_Error = Left_Last_Error;
	Left_Last_Error = Left_Current_Error;
  
	Left_Pro_Speed_PID_OUT = 
    (int16_t)Range_protect_float(Left_Speed_PID_OUT,max_left,min_left);   //限幅保护函数
	Set_PWMA(Left_Pro_Speed_PID_OUT);
    return Left_Pro_Speed_PID_OUT;
}

3.定时器配置

要让mcu每10ms读取一次编码器脉冲 则需要开启mcu的定时器进行精准定时

定时器配置代码 配置一个1ms的定时器

uint32_t time = 0;

// 中断优先级配置
static void BASIC_TIM_NVIC_Config(void)
{
    NVIC_InitTypeDef NVIC_InitStructure; 
    // 设置中断组为0
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);		
		// 设置中断来源
    NVIC_InitStructure.NVIC_IRQChannel = BASIC_TIM_IRQ ;	
		// 设置主优先级为 0
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	 
	  // 设置抢占优先级为3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;	
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

//定时器周期为7200 0000/(71+1)/(999+1)=1000  一秒1000个脉冲  1个脉冲1ms
void BASIC_TIM_Mode_Config(void)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
		
	// 开启定时器时钟,即内部时钟CK_INT=72M
    BASIC_TIM_APBxClock_FUN(BASIC_TIM_CLK, ENABLE);
	
	// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
    TIM_TimeBaseStructure.TIM_Period = BASIC_TIM_Period;	

	// 时钟预分频数为
    TIM_TimeBaseStructure.TIM_Prescaler= BASIC_TIM_Prescaler;
	
	// 初始化定时器
    TIM_TimeBaseInit(BASIC_TIM, &TIM_TimeBaseStructure); 
		
	// 清除计数器中断标志位
    TIM_ClearFlag(BASIC_TIM, TIM_FLAG_Update);
	  
	// 开启计数器中断
    TIM_ITConfig(BASIC_TIM,TIM_IT_Update,ENABLE);
		
	// 使能计数器
    TIM_Cmd(BASIC_TIM, ENABLE);	
}

void BASIC_TIM_Init(void)
{
	BASIC_TIM_NVIC_Config();   //中断优先级配置
	BASIC_TIM_Mode_Config();   //定时器配置
}

tim.h宏定义

#define       BASIC_TIM                   TIM6
#define       BASIC_TIM_APBxClock_FUN     RCC_APB1PeriphClockCmd   //定义开启外设时钟函数
#define       BASIC_TIM_CLK               RCC_APB1Periph_TIM6
#define       BASIC_TIM_Period            1000-1                //重装载的值
#define       BASIC_TIM_Prescaler         71                    //时钟预分频数
#define       BASIC_TIM_IRQ               TIM6_IRQn
#define       BASIC_TIM_IRQHandler        TIM6_IRQHandler


void BASIC_TIM_Init(void);

extern uint32_t time;

4.中断处理函数

主函数main配置好初始化代码即可  oled显示是另外增加的  下面函数放 it中断处理文件中

void  BASIC_TIM_IRQHandler (void)
{
	if ( TIM_GetITStatus( BASIC_TIM, TIM_IT_Update) != RESET )   //获取计数
	{	
		time++;
		Key_Proc();   //按键扫描
		TIM_ClearITPendingBit(BASIC_TIM , TIM_FLAG_Update);  		  //清除计数
	  if(time==10)
	 {
		 if(flag==1)  
		 {
		  Read_Encoder_L();  //读取编码器脉冲数
		  Left_Speed_Control(1.0,4000,-4000);
		  OLED_ShowNum(10,0,Encoder_L,4,12);//显示数字	
         }
      if(flag==0)
		 {
		  Read_Encoder_L();  //读取编码器脉冲数
		  Left_Speed_Control(0.5,4000,-4000);
		  OLED_ShowNum(10,0,Encoder_L,4,12);//显示数字	
		 }
	   time = 0;
	 }
	}		 	
}

实物图如上

  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在使用STM32F103C8T6蓝牙控制智能小车,我们可以使用Keil5作为开发工具。下面是关于如何进行学习并使用Keil5来开发的学习笔记。 1. 准备工作: - 下载并安装Keil5开发工具 - 配置好STM32F103C8T6开发板的硬件环境 2. 创建新的项目: - 在Keil5中选择“Project”->“New Project”菜单,选择合适的项目目录和名称 - 选择“Target”->“STM32F1 Series”->“STM32F103C8T6”作为目标设备 - 在“Manage”选项卡下,选择我们需要的文件,比如蓝牙文件 - 点击“Add”将文件添加到项目中,然后点击“OK”完成项目创建 3. 编写代码: - 在Keil5的项目窗口中,找到我们创建的主要源文件,例如“main.c” - 在这个文件中,我们可以使用C语言来编写控制智能小车的代码,利用蓝牙模块进行通信和控制 - 通过蓝牙模块接收控制指令,并根据指令进行相应的运动控制 4. 编译和烧录: - 在Keil5的工具栏中,点击“Build”按钮进行编译,确保代码没有错误 - 将开发板连接到计算机,并在Keil5的工具栏中点击“Flash”按钮进行烧录程序到开发板 5. 测试与调试: - 在Keil5的工具栏中选择“Debug”->“Start/Stop Debug Session”,进入调试模式 - 可以使用调试功能来观察程序的运行状态,查找和修复错误 - 使用蓝牙设备(例如手机或电脑)来连接蓝牙模块,并发送控制指令,观察智能小车的运动情况 以上是关于如何使用Keil5来开发STM32F103C8T6蓝牙控制智能小车的学习笔记。希望这些信息可以帮助你开始学习和开发该项目。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值