• 二轮平衡小车1:舵机与电机的基本控制


    今日开始使用STM32F103 C8T6尝试做一个二轮平衡小车,从电机舵机控制开始,小车也是在三个小时的自主设计下框架结构与引脚安排都做好了。

    本文主要贴代码,之前的文章都有原理,代码中相应初始化驱动部分也有注释~~

    文章提供源码,解释以及工程下载,测试效果视频。

    目录

    电机与舵机控制基础原理:

    初始化TIM1为通用定时器:

    初始化定时器TIM2为PWM输出控制电机:

    TB6612电机正反转控制引脚的初始化:

    编写函数封装电机控制:

    初始化定时器TIM4为舵机控制:

    如此以后我们将这些动作加入主函数

    测试效果:

    测试工程下载:


    电机与舵机控制基础原理:

    原理方面其余文章都有讲到:

    使用模块分别为:TB6612带稳压模块、MG996R舵机、JGB-520电机 。

    STM32 F103C8T6学习笔记5:定时器输出不同占空比PWM驱动舵机旋转角度_NULL指向我的博客-CSDN博客

    【MSP432电机驱动学习—上篇】TB6612带稳压电机驱动模块、MG310电机、霍尔编码器_tb6612fng电机驱动模块_NULL指向我的博客-CSDN博客

    初始化TIM1为通用定时器:

    这里我选择了TIM1为通用定时器,因为它与定时器TIM2、3、4不同,

    1、它是高级定时器,拥有带死区控制,但我们不需要,

    2、它的部分初始化内容也与TIM2、3、4不同,因此这里初始化一下TIM1,巩固一下,

    3、它占用的引脚有俩个是串口1的PA9\PA10,为了使得串口1能正常使用,因此不使用TIM1的通道,来控制舵机电机了

    直接贴代码,这里初始化TIM1为通用定时器,都是周期1ms:

    1. void TIM1_init(void)
    2. {
    3. // 定时器初始化结构体定义
    4. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    5. // 定时器中断向量配置
    6. NVIC_InitTypeDef NVIC_InitStructure;
    7. // 使能定时器1时钟
    8. RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
    9. // 定时器时钟分频系数设置为72-1
    10. TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1;
    11. // 定时器重载值设置为1000-1,即定时器溢出时间为1ms
    12. TIM_TimeBaseStructure.TIM_Period = 1000 - 1;
    13. // 定时器计数模式设置为向上计数
    14. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    15. // 定时器时钟分频因子设置为1
    16. TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1;
    17. //高级计数器需要,不需要用到的直接给0就好
    18. TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
    19. // 应用定时器初始化配置
    20. TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
    21. // 清除定时器中断标志位
    22. TIM_ClearFlag(TIM1, TIM_FLAG_Update);
    23. // 使能定时器中断
    24. TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);
    25. // 中断优先级设置为最低
    26. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
    27. // 中断子优先级设置为最低
    28. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
    29. // 使能中断
    30. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    31. // 应用中断配置
    32. NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;
    33. NVIC_Init(&NVIC_InitStructure);
    34. // 启动定时器1
    35. TIM_Cmd(TIM1, ENABLE);
    36. }

     中断服务函数:

    1. //通用定时器 定时器1 中断服务函数
    2. void TIM1_UP_IRQHandler(void)
    3. {
    4. if (TIM_GetITStatus(TIM1, TIM_IT_Update) == SET)
    5. {
    6. // if(++t==1000)
    7. // {
    8. // T++;t=0;
    9. // printf("%d\r\n",T);
    10. // }
    11. TIM_ClearITPendingBit(TIM1, TIM_IT_Update);//清出中断寄存器标志位,用于退出中断
    12. }
    13. }

    初始化定时器TIM2为PWM输出控制电机:

    1. TIM2_init(); //定时器2初始化为电机PWM (频率 18K HZ,重载值 1000)
    2. void TIM2_init(void)
    3. {
    4. TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//定义初始化参数结构体
    5. TIM_OCInitTypeDef TIM_OCInitStructure;//结构体变量需要赋值
    6. GPIO_InitTypeDef GPIO_Initstructure; //引脚结构体
    7. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//开启TIM2的时钟
    8. TIM_InternalClockConfig(TIM2);//开启定时器2的时钟源作为内部时钟源
    9. TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim,这里是不分频
    10. TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//TIM向上计数模式
    11. TIM_TimeBaseInitStructure.TIM_Period=1000- 1; //ARR
    12. TIM_TimeBaseInitStructure.TIM_Prescaler=4 - 1; //PSC
    13. TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;;//定时器的倍率,如果定时1秒,参数是1,那就是1+1=2倍
    14. TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
    15. // TIM_ClearFlag(TIM2,TIM_FLAG_Update);//中断清除
    16. TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//开启中断
    17. TIM_OCStructInit(&TIM_OCInitStructure);//结构体赋初始值的函数
    18. TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//输出比较模式
    19. TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//输出比较的极性
    20. TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//输出比较的使能
    21. TIM_OCInitStructure.TIM_Pulse =0;//设置CCR的
    22. TIM_OC1Init(TIM2,&TIM_OCInitStructure);//初始化TIM2_CH1
    23. TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);//使能预装载寄存器
    24. TIM_OC2Init(TIM2,&TIM_OCInitStructure);//初始化TIM2_CH2
    25. TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);//使能预装载寄存器
    26. // 用结构体初始化输出比较单元,不同函数不同的GPIO(A0)
    27. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    28. //这里的注释部分是展示开启定时器相应通道管脚的重映射
    29. //RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//开启AFIO的时钟
    30. //GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);//部分重映射
    31. //GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//关闭调试端口的复用
    32. GPIO_Initstructure.GPIO_Mode= GPIO_Mode_AF_PP;//复用推挽输出
    33. GPIO_Initstructure.GPIO_Pin= GPIO_Pin_0;
    34. GPIO_Initstructure.GPIO_Speed= GPIO_Speed_50MHz;
    35. GPIO_Init(GPIOA,&GPIO_Initstructure);
    36. GPIO_Initstructure.GPIO_Mode= GPIO_Mode_AF_PP;//复用推挽输出
    37. GPIO_Initstructure.GPIO_Pin= GPIO_Pin_1;
    38. GPIO_Initstructure.GPIO_Speed= GPIO_Speed_50MHz;
    39. GPIO_Init(GPIOA,&GPIO_Initstructure);
    40. // GPIO_SetBits(GPIOA,GPIO_Pin_0);
    41. TIM_Cmd(TIM2,ENABLE);
    42. }
    43. //void PWM_SetCompare1(uint16_t Compare1)
    44. //{
    45. // TIM_SetCompare1(TIM2,Compare1);//TIM2通道1,改变比较值CCRx,达到不同的占空比效果
    46. //}

    TB6612电机正反转控制引脚的初始化:

    控制速度的PWM在上面已经初始化了,现在还需要初始化启用几个类似于点灯的,可以输出1和0逻辑的几个引脚,用于控制电机正反转:

    1. //电机正反转控制引脚初始化:
    2. void TB6612_init(void)
    3. {
    4. //初始化BIN2 (PB9) 和 BIN1 (PB10)
    5. /*定义一个GPIO_InitTypeDef类型的结构体*/
    6. GPIO_InitTypeDef GPIO_InitStructure0;
    7. /*开启LED相关的GPIO外设时钟*/
    8. RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB,ENABLE);
    9. /*选择要控制的GPIO引脚*/
    10. GPIO_InitStructure0.GPIO_Pin = GPIO_Pin_9;
    11. /*设置引脚模式为通用推挽输出*/
    12. GPIO_InitStructure0.GPIO_Mode = GPIO_Mode_Out_PP;
    13. /*设置引脚速率为50MHz */
    14. GPIO_InitStructure0.GPIO_Speed = GPIO_Speed_50MHz;
    15. /*调用库函数,初始化GPIO*/
    16. GPIO_Init(GPIOB, &GPIO_InitStructure0);
    17. /*开启LED相关的GPIO外设时钟*/
    18. RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB,ENABLE);
    19. /*选择要控制的GPIO引脚*/
    20. GPIO_InitStructure0.GPIO_Pin = GPIO_Pin_10;
    21. /*设置引脚模式为通用推挽输出*/
    22. GPIO_InitStructure0.GPIO_Mode = GPIO_Mode_Out_PP;
    23. /*设置引脚速率为50MHz */
    24. GPIO_InitStructure0.GPIO_Speed = GPIO_Speed_50MHz;
    25. /*调用库函数,初始化GPIO*/
    26. GPIO_Init(GPIOB, &GPIO_InitStructure0);
    27. //初始化AIN2 (PA8) 和 AIN1 (PA11)
    28. /*开启LED相关的GPIO外设时钟*/
    29. RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA,ENABLE);
    30. /*选择要控制的GPIO引脚*/
    31. GPIO_InitStructure0.GPIO_Pin = GPIO_Pin_8;
    32. /*设置引脚模式为通用推挽输出*/
    33. GPIO_InitStructure0.GPIO_Mode = GPIO_Mode_Out_PP;
    34. /*设置引脚速率为50MHz */
    35. GPIO_InitStructure0.GPIO_Speed = GPIO_Speed_50MHz;
    36. /*调用库函数,初始化GPIO*/
    37. GPIO_Init(GPIOA, &GPIO_InitStructure0);
    38. /*开启LED相关的GPIO外设时钟*/
    39. RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA,ENABLE);
    40. /*选择要控制的GPIO引脚*/
    41. GPIO_InitStructure0.GPIO_Pin = GPIO_Pin_4;
    42. /*设置引脚模式为通用推挽输出*/
    43. GPIO_InitStructure0.GPIO_Mode = GPIO_Mode_Out_PP;
    44. /*设置引脚速率为50MHz */
    45. GPIO_InitStructure0.GPIO_Speed = GPIO_Speed_50MHz;
    46. /*调用库函数,初始化GPIO*/
    47. GPIO_Init(GPIOA, &GPIO_InitStructure0);
    48. //初始化完先都置0,不转
    49. GPIO_ResetBits(GPIOA,GPIO_Pin_8); //AIN1
    50. GPIO_ResetBits(GPIOA,GPIO_Pin_4); //AIN2
    51. GPIO_ResetBits(GPIOB,GPIO_Pin_10); //BIN1
    52. GPIO_ResetBits(GPIOB,GPIO_Pin_9); //BIN2
    53. }

    编写函数封装电机控制:

    1. //设置轮子速度,方向
    2. //PWMA PWMB分别设置 左PWMA 右PWMB 电机
    3. void set_wheels(uint16_t PWMA,uint16_t PWMB,uint16_t DIRA,uint16_t DIRB)
    4. {
    5. TIM_SetCompare1(TIM2,PWMB);//TIM2通道1,改变比较值CCRx,达到不同的占空比效果
    6. TIM_SetCompare2(TIM2,PWMA);//TIM2通道1,改变比较值CCRx,达到不同的占空比效果
    7. if(DIRA==0) //反转
    8. {
    9. GPIO_SetBits(GPIOA,GPIO_Pin_8); //AIN1
    10. GPIO_ResetBits(GPIOA,GPIO_Pin_4);//AIN2
    11. }
    12. else if(DIRA==1) //正转
    13. {
    14. GPIO_SetBits(GPIOA,GPIO_Pin_4); //AIN1
    15. GPIO_ResetBits(GPIOA,GPIO_Pin_8);//AIN2
    16. }
    17. if(DIRB==0) //反转
    18. {
    19. GPIO_SetBits(GPIOB,GPIO_Pin_10); //BIN1
    20. GPIO_ResetBits(GPIOB,GPIO_Pin_9);//BIN2
    21. }
    22. else if(DIRB==1) //正转
    23. {
    24. GPIO_SetBits(GPIOB,GPIO_Pin_9); //BIN1
    25. GPIO_ResetBits(GPIOB,GPIO_Pin_10);//BIN2
    26. }
    27. }

    初始化定时器TIM4为舵机控制:

    1. void TIM4_init(void)
    2. {
    3. GPIO_InitTypeDef GPIO_InitStructure;
    4. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    5. TIM_OCInitTypeDef TIM_OCInitStructure;
    6. // //开时钟
    7. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
    8. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    9. // //PWM输出管脚配置
    10. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    11. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    12. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    13. GPIO_Init(GPIOB, &GPIO_InitStructure);
    14. TIM_TimeBaseStructure.TIM_Period = 3000;
    15. TIM_TimeBaseStructure.TIM_Prescaler =71;
    16. TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    17. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    18. TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
    19. // //PWM模式配置
    20. TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
    21. TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;
    22. TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
    23. TIM_OC3Init(TIM4,&TIM_OCInitStructure);
    24. TIM_OC3PreloadConfig(TIM4,TIM_OCPreload_Enable);
    25. TIM_Cmd(TIM4,ENABLE);
    26. //TIM_SetCompare3(TIM4,psc); //设置舵机占空比,控制舵机转动
    27. }
    1. // TIM_SetCompare3(TIM4,350); // 0度
    2. // TIM_SetCompare3(TIM4,2600); //180度
    3. // TIM_SetCompare3(TIM4,1475); //90度

    如此以后我们将这些动作加入主函数

    1. uint16_t T,t;
    2. int main(void)
    3. {
    4. init_ALL(); //初始化所有函数
    5. TIM_SetCompare3(TIM4,350); // 0度
    6. delay_ms(5000);
    7. set_wheels(555,555,1,1);
    8. delay_ms(1000);
    9. set_wheels(0,0,0,0);
    10. delay_ms(1000);
    11. set_wheels(555,555,0,0);
    12. delay_ms(1000);
    13. set_wheels(0,0,0,0);
    14. while(1)
    15. {
    16. }
    17. }
    18. //初始化所有函数:
    19. void init_ALL(void)
    20. {
    21. SysTick_Init(72); //初始化滴答计时器
    22. Usart1_Init(115200); //串口1初始化
    23. // i2c_GPIO_Config(); //IIC初始化
    24. // Usart2_Init(115200); //串口2初始化
    25. // Usart3_Init(115200); //串口3初始化
    26. // OLED_Init(); //初始化OLED屏幕
    27. // OLED_Clear(); //清空屏幕数据
    28. // RTC_init(); //初始化RTC实时时钟
    29. TIM1_init(); //定时器1初始化为通用定时器 (周期1ms)
    30. TIM2_init(); //定时器2初始化为电机PWM (频率 18K HZ,重载值 1000)
    31. TIM4_init(); //定时器4初始化为舵机PWM (频率333 hz ,重载值3000)
    32. TB6612_init(); //电机正反转控制引脚初始化:
    33. printf("HELLO"); //开机测试 串口1
    34. // TIM_SetCompare3(TIM4,350); // 0度
    35. // TIM_SetCompare3(TIM4,2600); //180度
    36. // TIM_SetCompare3(TIM4,1475); //90度
    37. }

    测试效果:

    今日就做到这吧,明日在接着做下面的东西......

    二轮平衡小车:舵机与电机的基本控制与编码器信号捕获

    测试工程下载:

    https://download.csdn.net/download/qq_64257614/88286409?spm=1001.2014.3001.5503

  • 相关阅读:
    [附源码]计算机毕业设计JAVA高校知识产权管理系统论文2022
    flutter Failed to download https://flutter-io.cn/flutter_infra_release/
    写给技术人的管理入门知识1:什么是管理
    在mysql存储过程中动态拼接sql并执行
    msvcp120.dll丢失的解决方法,有效的两种msvcp120.dll修复方法分享
    prometheus helm install 如何配置告警模版
    autollm 指令设计
    关于Spring的两三事:万物之始—BeanDefinition
    路由器基础(八):策略路由配置
    Qt QPen
  • 原文地址:https://blog.csdn.net/qq_64257614/article/details/132625740