基于STM32与FreeRTOS的四轴机械臂项目-3

简介: 基于STM32与FreeRTOS的四轴机械臂项目

基于STM32与FreeRTOS的四轴机械臂项目-2

https://developer.aliyun.com/article/1507997


四、裸机三种控制测试

首先我在Core文件夹里面的Src和Inc里分别创建pwm和oled的.c和.h文件

1.摇杆控制

摇杆控制在这里我用上面讲到的两个按键摇杆传感器模块实现,只需要开启四路ADC采集,两个按键摇杆传感器分别接收 IN0~3 ADC的模拟量,其中需要结合实际操作控制舵机初始化角度和运动过程中的角度变化。

代码示例:

pwm.c

extern uint16_t adc_dma[4];//DMA搬运的ADC采集值
 
extern int8_t angle[4] = {45,45,180,135};//舵机角度
 
//根据输入的0~180角度获取对应pwm占空比参数
unsigned char Angle(unsigned char pwm_pulse)
{
  return pwm_pulse + 44;
}
 
//舵机A,夹爪  CH4_B11  45-135 张开闭合 初始化135
void sg90_A()
{
  if(adc_dma[3] > 4000 && angle[3] < 135)
  {
    angle[3]++;
  }
  else if(adc_dma[3] <1000 && angle[3] > 45)
  {
    angle[3]--;
  }
}
 
//舵机B,上下  CH3_B10  45-180    初始化180
void sg90_B()
{
  if(adc_dma[2] <1000 && angle[2] < 180)
  {
    angle[2]++;
  }
  else if(adc_dma[2] > 4000 && angle[2] > 45)
  {
    angle[2]--;
  }
}
 
//舵机C,前后  CH2_B3  45-180  前后   初始化45
void sg90_C()
{
  if(adc_dma[1] <1000 && angle[1] < 180)
  {
    angle[1]++;
  }
  else if(adc_dma[1] > 4000 && angle[1] > 45)
  {
    angle[1]--;
  }
}
 
//舵机D,底座  CH1_A15 45-135 左到右   初始化45
void sg90_D()
{
  if(adc_dma[0] <1000 && angle[0] < 135)
  {
    angle[0]++;
  }
  else if(adc_dma[0] > 4000 && angle[0] > 45)
  {
    angle[0]--;
  }
}

pwm.h

#ifndef __PWM_H__
#define __PWM_H__
 
unsigned char Angle(unsigned char pwm_pulse);
 
void sg90_A(void);
 
void sg90_B(void);
 
void sg90_C(void);
 
void sg90_D(void);
 
#endif

oled.c

#include "main.h"
#include "i2c.h"
#include "oled.h"
 
/*--  文字:  向  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
char x1[16] = {0x00,0xF8,0x08,0x08,0x0C,0xCA,0x49,0x48,0x48,0xC8,0x08,0x08,0x08,0xF8,0x00,0x00};
char x2[16] = {0x00,0xFF,0x00,0x00,0x00,0x1F,0x08,0x08,0x08,0x1F,0x00,0x40,0x80,0x7F,0x00,0x00};
 
/*--  文字:  前  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
char f1[16] = {0x08,0x08,0xE8,0x29,0x2E,0x28,0xE8,0x08,0x08,0xC8,0x0C,0x0B,0xE8,0x08,0x08,0x00};
char f2[16] = {0x00,0x00,0xFF,0x09,0x49,0x89,0x7F,0x00,0x00,0x0F,0x40,0x80,0x7F,0x00,0x00,0x00};
 
/*--  文字:  后  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
char b1[16] = {0x00,0x00,0x00,0xFC,0x24,0x24,0x24,0x24,0x22,0x22,0x22,0x23,0x22,0x20,0x20,0x00};
char b2[16] = {0x40,0x20,0x18,0x07,0x00,0xFE,0x42,0x42,0x42,0x42,0x42,0x42,0xFE,0x00,0x00,0x00};
 
/*--  文字:  上  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
char u1[16] = {0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00};
char u2[16] = {0x40,0x40,0x40,0x40,0x40,0x40,0x7F,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00};
 
/*--  文字:  下  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
char d1[16] = {0x02,0x02,0x02,0x02,0x02,0x02,0xFE,0x02,0x02,0x42,0x82,0x02,0x02,0x02,0x02,0x00};
char d2[16] = {0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x01,0x06,0x00,0x00,0x00};
 
/*--  文字:  左  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
char l1[16] = {0x08,0x08,0x08,0x08,0x88,0x78,0x0F,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00};
char l2[16] = {0x20,0x10,0x48,0x46,0x41,0x41,0x41,0x41,0x7F,0x41,0x41,0x41,0x41,0x40,0x40,0x00};
 
/*--  文字:  右  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
char r1[16] = {0x08,0x08,0x08,0x08,0xC8,0x38,0x0F,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00};
char r2[16] = {0x08,0x04,0x02,0x01,0xFF,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0xFF,0x00,0x00,0x00};
 
/*--  文字:  张  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
char o1[16] = {0x02,0xE2,0x22,0x22,0x3E,0x80,0x80,0xFF,0x80,0xA0,0x90,0x88,0x86,0x80,0x80,0x00};
char o2[16] = {0x00,0x43,0x82,0x42,0x3E,0x00,0x00,0xFF,0x40,0x21,0x06,0x08,0x10,0x20,0x40,0x00};
 
/*--  文字:  夹  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
char c1[16] = {0x00,0x08,0x08,0x28,0x48,0x08,0x08,0xFF,0x08,0x08,0x48,0x28,0x08,0x08,0x00,0x00};
char c2[16] = {0x81,0x81,0x41,0x41,0x21,0x11,0x0D,0x03,0x0D,0x11,0x21,0x41,0x41,0x81,0x81,0x00};
 
/*--  文字:  爪  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
char z1[16] = {0x00,0x00,0x00,0xFC,0x04,0x04,0xFC,0x04,0x02,0x02,0xFE,0x03,0x02,0x00,0x00,0x00};
char z2[16] = {0x80,0x60,0x18,0x07,0x00,0x00,0x7F,0x00,0x00,0x00,0x01,0x0E,0x30,0x40,0x80,0x00};
 
void Oled_Write_Cmd(uint8_t dataCmd)
{
  
  HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x00, I2C_MEMADD_SIZE_8BIT,
                    &dataCmd, 1, 0xff);
}
 
void Oled_Write_Data(uint8_t dataData)
{
  HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x40, I2C_MEMADD_SIZE_8BIT,
                    &dataData, 1, 0xff);
}
 
void Oled_Init(void){
  Oled_Write_Cmd(0xAE);//--display off
  Oled_Write_Cmd(0x00);//---set low column address
  Oled_Write_Cmd(0x10);//---set high column address
  Oled_Write_Cmd(0x40);//--set start line address  
  Oled_Write_Cmd(0xB0);//--set page address
  Oled_Write_Cmd(0x81); // contract control
  Oled_Write_Cmd(0xFF);//--128   
  Oled_Write_Cmd(0xA1);//set segment remap 
  Oled_Write_Cmd(0xA6);//--normal / reverse
  Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
  Oled_Write_Cmd(0x3F);//--1/32 duty
  Oled_Write_Cmd(0xC8);//Com scan direction
  Oled_Write_Cmd(0xD3);//-set display offset
  Oled_Write_Cmd(0x00);//
  
  Oled_Write_Cmd(0xD5);//set osc division
  Oled_Write_Cmd(0x80);//
  
  Oled_Write_Cmd(0xD8);//set area color mode off
  Oled_Write_Cmd(0x05);//
  
  Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
  Oled_Write_Cmd(0xF1);//
  
  Oled_Write_Cmd(0xDA);//set com pin configuartion
  Oled_Write_Cmd(0x12);//
  
  Oled_Write_Cmd(0xDB);//set Vcomh
  Oled_Write_Cmd(0x30);//
  
  Oled_Write_Cmd(0x8D);//set charge pump enable
  Oled_Write_Cmd(0x14);//
  
  Oled_Write_Cmd(0xAF);//--turn on oled panel   
}
 
void Oled_Screen_Clear(void){
  unsigned char i,n;
  Oled_Write_Cmd (0x20);                    //set memory addressing mode
  Oled_Write_Cmd (0x02);                    //page addressing mode
 
  for(i=0;i<8;i++){
    Oled_Write_Cmd(0xb0+i);               //
    Oled_Write_Cmd(0x00);                 //
    Oled_Write_Cmd(0x10);                 //
    for(n=0;n<128;n++)Oled_Write_Data(0x00);      
  } 
}
 
void Oled_Show_open()
{
    unsigned char  i;
 
    Oled_Init();
    // 选择一个位置确认页寻址模式
    Oled_Write_Cmd(0x20);
    Oled_Write_Cmd(0x02);
    Oled_Screen_Clear();
    // 选择PAGE0   1011 0000 0xB0     
    Oled_Write_Cmd(0xB0);
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0x10);
 
    for(i=0;i<16;i++){
      Oled_Write_Data(o1[i]);
    }
    for(i=0;i<16;i++){
      Oled_Write_Data(z1[i]);
    }
 
    Oled_Write_Cmd(0xB1);
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0x10);
 
    for(i=0;i<16;i++){
      Oled_Write_Data(o2[i]);
    }
    for(i=0;i<16;i++){
      Oled_Write_Data(z2[i]);
    }
 
}
 
void Oled_Show_close()
{
    unsigned char  i;
 
    Oled_Init();
 
    Oled_Write_Cmd(0x20);
    Oled_Write_Cmd(0x02);
    Oled_Screen_Clear();
 
    Oled_Write_Cmd(0xB0);
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0x10);
 
    for(i=0;i<16;i++){
      Oled_Write_Data(c1[i]);
    }
    for(i=0;i<16;i++){
      Oled_Write_Data(z1[i]);
    }
 
    Oled_Write_Cmd(0xB1);
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0x10);
 
    for(i=0;i<16;i++){
      Oled_Write_Data(c2[i]);
    }
    for(i=0;i<16;i++){
      Oled_Write_Data(z2[i]);
    }
 
}
 
void Oled_Show_up()
{
    unsigned char  i;
 
    Oled_Init();
 
    Oled_Write_Cmd(0x20);
    Oled_Write_Cmd(0x02);
    Oled_Screen_Clear();
 
    Oled_Write_Cmd(0xB0);
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0x10);
 
    for(i=0;i<16;i++){
      Oled_Write_Data(x1[i]);
    }
    for(i=0;i<16;i++){
      Oled_Write_Data(u1[i]);
    }
 
    Oled_Write_Cmd(0xB1);
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0x10);
 
    for(i=0;i<16;i++){
      Oled_Write_Data(x2[i]);
    }
    for(i=0;i<16;i++){
      Oled_Write_Data(u2[i]);
    }
 
}
 
void Oled_Show_down()
{
    unsigned char  i;
  
    Oled_Init();
 
    Oled_Write_Cmd(0x20);
    Oled_Write_Cmd(0x02);
    Oled_Screen_Clear();
 
    Oled_Write_Cmd(0xB0);
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0x10);
 
    for(i=0;i<16;i++){
      Oled_Write_Data(x1[i]);
    }
    for(i=0;i<16;i++){
      Oled_Write_Data(d1[i]);
    }
 
    Oled_Write_Cmd(0xB1);
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0x10);
 
    for(i=0;i<16;i++){
      Oled_Write_Data(x2[i]);
    }
    for(i=0;i<16;i++){
      Oled_Write_Data(d2[i]);
    }
 
}
 
void Oled_Show_left()
{
    unsigned char  i;
 
    Oled_Init();
 
    Oled_Write_Cmd(0x20);
    Oled_Write_Cmd(0x02);
    Oled_Screen_Clear();
 
    Oled_Write_Cmd(0xB0);
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0x10);
 
    for(i=0;i<16;i++){
      Oled_Write_Data(x1[i]);
    }
    for(i=0;i<16;i++){
      Oled_Write_Data(l1[i]);
    }
 
    Oled_Write_Cmd(0xB1);
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0x10);
 
    for(i=0;i<16;i++){
      Oled_Write_Data(x2[i]);
    }
    for(i=0;i<16;i++){
      Oled_Write_Data(l2[i]);
    }
 
}
 
void Oled_Show_right()
{
    unsigned char  i;
 
    Oled_Init();
 
    Oled_Write_Cmd(0x20);
    Oled_Write_Cmd(0x02);
    Oled_Screen_Clear();
 
    Oled_Write_Cmd(0xB0);
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0x10);
 
    for(i=0;i<16;i++){
      Oled_Write_Data(x1[i]);
    }
    for(i=0;i<16;i++){
      Oled_Write_Data(r1[i]);
    }
 
    Oled_Write_Cmd(0xB1);
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0x10);
 
    for(i=0;i<16;i++){
      Oled_Write_Data(x2[i]);
    }
    for(i=0;i<16;i++){
      Oled_Write_Data(r2[i]);
    }
 
}
 
void Oled_Show_front()
{
    unsigned char  i;
 
    Oled_Init();
 
    Oled_Write_Cmd(0x20);
    Oled_Write_Cmd(0x02);
    Oled_Screen_Clear();
 
    Oled_Write_Cmd(0xB0);
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0x10);
 
    for(i=0;i<16;i++){
      Oled_Write_Data(x1[i]);
    }
    for(i=0;i<16;i++){
      Oled_Write_Data(f1[i]);
    }
 
    Oled_Write_Cmd(0xB1);
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0x10);
 
    for(i=0;i<16;i++){
      Oled_Write_Data(x2[i]);
    }
    for(i=0;i<16;i++){
      Oled_Write_Data(f2[i]);
    }
 
}
 
void Oled_Show_behind()
{
    unsigned char  i;
 
    Oled_Init();
 
    Oled_Write_Cmd(0x20);
    Oled_Write_Cmd(0x02);
    Oled_Screen_Clear();
 
    Oled_Write_Cmd(0xB0);
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0x10);
 
    for(i=0;i<16;i++){
      Oled_Write_Data(x1[i]);
    }
    for(i=0;i<16;i++){
      Oled_Write_Data(b1[i]);
    }
 
    Oled_Write_Cmd(0xB1);
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0x10);
 
    for(i=0;i<16;i++){
      Oled_Write_Data(x2[i]);
    }
    for(i=0;i<16;i++){
      Oled_Write_Data(b2[i]);
    }
 
}

oled.h

#ifndef __OLED_H__
#define __OLED_H__
 
void Oled_Write_Cmd(uint8_t dataCmd);
 
void Oled_Write_Data(uint8_t dataData);
 
void Oled_Init(void);
 
void Oled_Screen_Clear(void);
 
// oled显示封装
void Oled_Show_open();
void Oled_Show_close();
void Oled_Show_up();
void Oled_Show_down();
void Oled_Show_left();
void Oled_Show_right();
void Oled_Show_front();
void Oled_Show_behind();
 
#endif

main.c

uint16_t adc_dma[8];//DMA搬运的ADC采集值
 
uint8_t angle[4] = {45,45,180,135};//舵机角度
 
uint8_t cnt = 0;//计数用,定时串口打印信息
 
 
//覆写printf,用于串口打印数据
int fputc(int ch, FILE *f)
{      
    unsigned char temp[1]={ch};
    HAL_UART_Transmit(&huart1,temp,1,0xffff);  
    return ch;
}
 
//int main
 
    //开始ADC和DMA采集
  HAL_ADC_Start_DMA(&hadc1,(uint32_t *)adc_dma,4);
  
  //开启4路PWM
  HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
  HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
  HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3);
  HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4);
  
  //延时半秒,系统稳定一下
  HAL_Delay(500);
  printf("test\r\n");
  
  while (1)
  {
    
    sg90_A();
    sg90_B();
    sg90_C();
    sg90_D();
    
    //输出PWM波使舵机运动
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, Angle(angle[0]));
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, Angle(angle[1]));
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_3, Angle(angle[2]));
    __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_4, Angle(angle[3]));
    
    cnt++;//计数,每循环一次+1
    if(cnt>= 50)//循环50次,每次20ms,即一共1s。每一秒发送一次数据
    {
      printf("Angle = {%d, %d, %d, %d}\r\n",angle[0],angle[1],angle[2],angle[3]);
      cnt = 0;
    }
    HAL_Delay(20);//每20ms循环一次(改成15更流畅)
    
  }

2.示教器控制

示教器控制在这里我用上面讲到的四个旋钮电位器模块实现,跟上面摇杆控制一样只需要开启四路ADC采集,代码跟摇杆控制基本差不多,新增加一个函数封装把采集的模拟值转换为角度,即0~4095 变为 0~180,除以22.75即可


在pwm.c上新增加一个转换函数

 
void translate()//直接用8通道就是adc_dma[4~7]
{
  angle[3] = (uint8_t)((double)adc_dma[0] / 22.75)/2;
  angle[2] = (uint8_t)((double)adc_dma[1] / 22.75);
  angle[1] = (uint8_t)((double)adc_dma[2] / 22.75) - 10;
  angle[0] = 180 - (uint8_t)((double)adc_dma[3] / 22.75);//电位器装反,改为 180 - 即可
}

在main.c上增加打印调试信息在while(1)循环里面

printf("adc_dma = {%d, %d, %d, %d}\r\n",adc_dma[0],adc_dma[1],adc_dma[2],adc_dma[3]);

3.蓝牙控制

使用蓝牙模块,打开串口中断,用串口调试助手查看中断测试收发的数据,只需要新增加串口接收中断代码和在原先的pwm.c上面做一些修改

usart.c

#include "stdio.h"
#include "string.h"
#include "pwm.h"
 
#include "adc.h"
#include "dma.h"
 
/*蓝牙控制机械臂指令:
s   停
l/r 左右
u/d 上下
f/b 前后
o/c 开合*/
uint8_t cmd_BLE = 's';
 
extern uint16_t adc_dma[4];//DMA搬运的ADC采集值
 
//覆写printf
int fputc(int ch, FILE *f)
{      
    unsigned char temp[1]={ch};
    HAL_UART_Transmit(&huart1,temp,1,0xffff);  
    return ch;
}
 
//=====串口(中断)=======
//串口接收缓存(1字节)
uint8_t buf=0;
//定义最大接收字节数 200,可根据需求调整
#define UART1_REC_LEN 200
// 接收缓冲, 串口接收到的数据放在这个数组里,最大UART1_REC_LEN个字节
uint8_t UART1_RX_Buffer[UART1_REC_LEN];
//  接收状态
//  bit15,      接收完成标志
//  bit14,      接收到0x0d
//  bit13~0,    接收到的有效字节数目
uint16_t UART1_RX_STA=0;
 
// 串口中断:接收完成回调函数,收到一个数据后,在这里处理
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  // 判断中断是由哪个串口触发的
  if(huart->Instance == USART1)
  {
    // 判断接收是否完成(UART1_RX_STA bit15 位是否为1)
    if((UART1_RX_STA & 0x8000) == 0)
    {
      // 如果已经收到了 0x0d (回车),
      if(UART1_RX_STA & 0x4000)
      {
        // 则接着判断是否收到 0x0a (换行)
        if(buf == 0x0a)
        {
          // 如果 0x0a 和 0x0d 都收到,则将 bit15 位置为1
          UART1_RX_STA |= 0x8000;
          
          //=======中断信息处理=======
 
          //获取蓝牙控制指令,A打头,后面一个字母就是指令内容
            if(UART1_RX_Buffer[0] == 'A')
          {
            HAL_ADC_Stop_DMA(&hadc1);//停止ADC DMA
            MX_ADC1_Init();//初始化ADC1
            HAL_ADC_Start_DMA(&hadc1,(uint32_t *)adc_dma,4); //开启ADC DMA
            cmd_BLE = UART1_RX_Buffer[1];
          }
          
          else {
            if(UART1_RX_Buffer[0] != '\0')
              printf("指令发送错误:%s\r\n", UART1_RX_Buffer);
          }
          
          //==========================
          memset(UART1_RX_Buffer, 0, strlen((const char *)UART1_RX_Buffer));
       
            // 重新开始下一次接收
          UART1_RX_STA = 0;
          //==========================
        }
        else
          // 否则认为接收错误,重新开始
          UART1_RX_STA = 0;
      }
      else  // 如果没有收到了 0x0d (回车)
      {
        //则先判断收到的这个字符是否是 0x0d (回车)
        if(buf == 0x0d)
        {
          // 是的话则将 bit14 位置为1
          UART1_RX_STA |= 0x4000;
        }
        else
        {
          // 否则将接收到的数据保存在缓存数组里
          UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf;
          UART1_RX_STA++;
          
          // 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
          if(UART1_RX_STA > UART1_REC_LEN - 1)
            UART1_RX_STA = 0;
        }
      }
    }
    // 重新开启中断
    HAL_UART_Receive_IT(&huart1, &buf, 1);
  }
}
 
 
  // 在串口初始化中开启接收中断
  HAL_UART_Receive_IT(&huart1, &buf, 1);

pwm.c

#include "pwm.h"
#include "main.h"
#include "oled.h"
 
extern uint16_t adc_dma[8];//DMA搬运的ADC采集值
 
extern uint8_t angle[4];//舵机角度
 
extern uint8_t Mode;
extern uint8_t cmd_BLE;
 
//根据输入的0~180角度获取对应pwm占空比参数
 
unsigned char Angle(unsigned char pwm_pulse)
{
  return pwm_pulse + 44;
}
 
//舵机A,夹爪  CH4_B11  45-135 张开闭合 初始化135
void sg90_A()
{
  if((cmd_BLE == 'c' || adc_dma[3] > 4000) && angle[3] < 135)//合
  {
    angle[3]++;
    Oled_Show_close();
    
  }
  else if((cmd_BLE == 'o' || adc_dma[3] <1000) && angle[3] > 45)//开
  {
    angle[3]--;
    Oled_Show_open();
  }
 
}
 
//舵机B,上下  CH3_B10  45-180    初始化180
void sg90_B()
{
  if((cmd_BLE == 'u' || adc_dma[2] <1000) && angle[2] < 180)//上
  {
    angle[2]++;
    Oled_Show_up();
  }
  else if((cmd_BLE == 'd' || adc_dma[2] > 4000) && angle[2] > 45)//下
  {
    angle[2]--;
    Oled_Show_down();
  }
}
 
//舵机C,前后  CH2_B3  45-180  前后   初始化45
void sg90_C()
{
  if((cmd_BLE == 'f' || adc_dma[1] <1000) && angle[1] < 180)//前
  {
    angle[1]++;
    Oled_Show_front();
  }
  else if((cmd_BLE == 'b' || adc_dma[1] > 4000) && angle[1] > 45)//后
  {
    angle[1]--;
    Oled_Show_behind();
  }
}
 
//舵机D,底座  CH1_A15 45-135 左到右   初始化45
void sg90_D()
{
  if((cmd_BLE == 'l' || adc_dma[0] <1000) && angle[0] < 135)//左
  {
    angle[0]++;
    Oled_Show_left();
  }
  else if((cmd_BLE == 'r' || adc_dma[0] > 4000) && angle[0] > 45)//右
  {
    angle[0]--;
    Oled_Show_right();
  }
}
 

五、裸机与FreeRTOS

移植 FreeRTOS 到 STM32F103C8T6上我们可以手动移植或者使用CubeMX快速移植,在这里我用CubeMX快速移植,具体介绍可看我之前写过的文章,链接如下:CubeMx快速移植FreeRTOS

接着我们要创建三个任务,一个任务负责角度信息处理,一个任务负责串口收发数据,一个任务负责显示OLED屏幕,具体创建删除任务介绍可看我之前写过的文章,链接如下:任务的创建和删除


1.CubeMX配置

b52b1d2c29708e06bf8d36a4c57bb0c4_39a50718b48a42b194bc2387b4136852.png


2.移植裸机三种控制代码

在这里我们只需要在CubeMX生成的freertos.c文件中移植我们裸机控制的代码放在对应的任务中,可自行进行代码扩展,例如:  

aba04a871b6bd1067e31afa1efbc68a2_edb2d94063134f71b9affb69e7162afd.png


六、项目演示视频

image.png

四轴机械臂演示视频

相关文章
|
8月前
|
存储 数据安全/隐私保护
STM32实战项目—密码锁
本文完整详细地介绍了一个密码锁项目的要求,设计思路,程序实现,问题总结和成果展示内容。
153 2
STM32实战项目—密码锁
|
23天前
|
传感器
基于STM32与FreeRTOS的四轴机械臂项目-2
基于STM32与FreeRTOS的四轴机械臂项目
基于STM32与FreeRTOS的四轴机械臂项目-2
|
23天前
|
传感器 存储
基于STM32与FreeRTOS的四轴机械臂项目-1
基于STM32与FreeRTOS的四轴机械臂项目
基于STM32与FreeRTOS的四轴机械臂项目-1
|
23天前
|
存储 编解码 算法
基于STM32的开源简易示波器项目
基于STM32的开源简易示波器项目
94 0
|
8月前
|
运维 小程序 API
STM32实战项目—WIFI远程开关灯
本文介绍了如何利用正点原子ESP8266和STM32F103C8T6连接腾讯云,利用舵机实现远程开关灯。给出了详细的腾讯云配置和调试步骤,给出了部分程序设计。
164 0
STM32实战项目—WIFI远程开关灯
|
8月前
|
存储 索引
STM32实战项目—停车计费系统
本文详细介绍了一个停车计费系统的任务要求,实现思路。最后,给出了详细的程序设计和测试结果。
160 2
STM32实战项目—停车计费系统
|
8月前
STM32实战项目—楼宇人员计数系统
本文介绍了一个基于红外对管的楼宇人员计数系统设计。简单介绍了一下红外对管的原理和使用方法。针对任务要求给出了详细的实现思路和程序设计。
47 3
STM32实战项目—楼宇人员计数系统
|
12月前
QT上位机串口+STM32单片机项目(二)
QT上位机串口+STM32单片机项目
202 0
|
12月前
QT上位机串口+STM32单片机项目(一)
QT上位机串口+STM32单片机项目
562 0
|
C语言 智能硬件
STM32cubeMX详细教学及多个小项目合集(包含RTOS)
STM32cubeMX详细教学及多个小项目合集(包含RTOS)
274 0