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

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

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

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


三、裸机各种模块测试

硬件模块接线:


  • 四个舵机分别接CH1_A15,CH2_B3,CH3_B10, CH4_B11
  • 蓝牙模块TX接RX,RX接TX
  • 两个按键摇杆传感器分别接 PA0 到 PA3 对应 IN0 到 IN3
  • 四个旋钮电位器分别接 PA4 到 PA7 对应 IN4 到 IN7
  • OLED模块 SCL 和 SDA 分别接 PB6 和 PB7
  • W25Q128模块自行扩展

1.舵机模块

舵机模块测试可以看之前我写过的文章,链接如下:

SG90舵机模块测试

注意舵机角度不能从0转到180度的话,很有可能是因为供电不够,SG90舵机额定电压为4.8~6V。

然后对四个舵机进行函数封装,舵机初始化角度跟运动角度自行配置调试:

//舵机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]--;
  }
}
 
  //开启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);
 

2.蓝牙模块

可以使用HC-05(下图蓝色)或者HC-08(下图绿色),这两种我都测试通过。

有关蓝牙模块具体使用跟AT指令可以看以下两篇博客,链接如下:

HC-05介绍HC-08介绍

使用串口中断测试收发的数据,串口重映射设置:

重映射代码:

int fputc(int ch, FILE *f)
{      
  unsigned char temp[1]={ch};
  HAL_UART_Transmit(&huart1,temp,1,0xffff);  
  return ch;
}

中断接收信息代码:

// 接收完成回调函数,收到一个数据后,在这里处理
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;
        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);
  }
}
 

在main.c函数中开启中断测试,然后打开蓝牙助手:

  // 开启接收中断
  HAL_UART_Receive_IT(&huart1, &buf, 1);
 
  while (1)
  {
    //判断判断串口是否接收完成
    if(UART1_RX_STA & 0x8000)
    {
      if(!strcmp((const char *)UART1_RX_Buffer, "open"))
      {
                printf("张爪\r\n");
      }
      else if (!strcmp((const char *)UART1_RX_Buffer, "close"))
      {
                printf("夹爪\r\n");
      }
      else
      {
        if(UART1_RX_Buffer[0] != '\0')
          printf("指令发送错误:%s\r\n", UART1_RX_Buffer);
      }
      printf("\r\n");
      // 重新开始下一次接收
      UART1_RX_STA = 0;
    }
    HAL_Delay(40);
  }

手机打开蓝牙助手,记得把发送新行勾上,不然中断接收不到数据

3.按键摇杆传感器模块和旋钮电位器模块

这里用两个按键摇杆传感器分别接收 IN0~3 ADC的模拟量,用四个旋钮电位器分别接收 IN4~7 ADC的模拟量

利用DMA传输接收到的ADC的值通过串口打印进行调试,通过按钮和电位器控制角度

代码示例:

uint16_t adc_dma[8];//DMA搬运的ADC采集值
 
uint8_t angle[4] = {45,45,180,135};//舵机角度
 
uint8_t cnt = 0;//计数用,定时串口打印信息
 
//根据输入的0~180角度获取对应pwm占空比参数
uint8_t Angle(uint8_t pwm_pulse)
{
  return pwm_pulse + 44;
}
 
 
  //开始ADC和DMA采集
  HAL_ADC_Start_DMA(&hadc1,(uint32_t *)adc_dma,8);
 
  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]);
      printf("adc_dma = {%d, %d, %d, %d, %d, %d, %d, %d}\r\n",adc_dma[0],adc_dma[1],adc_dma[2],adc_dma[3],adc_dma[4],adc_dma[5],adc_dma[6],adc_dma[7]);
      cnt = 0;
    }
    HAL_Delay(20);//每20ms循环一次
  }

4.OLED模块

在这里我们可以用取模软件显示机械臂动作,张爪夹爪,向左向右,向前向后,向上向下,也可以后续自行扩展其他内容,例如角度和控制模式等。

oled介绍可看我之前写过的:OLED取模生成文字图片

代码实现如下:

/*--  文字:  向  --*/
/*--  宋体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]);
    }
 
}
 
  while (1)
  {
      Oled_Show_open();
      HAL_Delay(1000);
      Oled_Show_close();
      HAL_Delay(1000);
      Oled_Show_up();
      HAL_Delay(1000);
      Oled_Show_down();
      HAL_Delay(1000);
      Oled_Show_left();
      HAL_Delay(1000);
      Oled_Show_right();
      HAL_Delay(1000);
      Oled_Show_front();
      HAL_Delay(1000);  
      Oled_Show_behind();
      HAL_Delay(1000);
  }

5.W25Q128模块

W25Q128模块测试可看我之前写过的文章然后自行扩展:W25Q128模块测试


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

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

相关文章
|
4月前
STM32CubeMX FreeRTOS 互斥锁
STM32CubeMX FreeRTOS 互斥锁
162 12
|
4月前
STM32CubeMX FreeRTOS 任务的挂起和恢复
STM32CubeMX FreeRTOS 任务的挂起和恢复
102 12
|
4月前
STM32Cubemx FreeRTOS Event
STM32Cubemx FreeRTOS Event
38 11
|
4月前
|
消息中间件
STM32CubeMX FreeRTOS 消息队列
STM32CubeMX FreeRTOS 消息队列
139 11
|
4月前
STM32Cubmx FreeRTOS Timer
STM32Cubmx FreeRTOS Timer
47 10
|
4月前
STM32CubeMX FreeRTOS点亮LED
STM32CubeMX FreeRTOS点亮LED
78 10
|
4月前
|
编解码
STM32CubeMX FreeRTOS u8g2图片菜单制作
STM32CubeMX FreeRTOS u8g2图片菜单制作
130 8
|
6月前
|
传感器
基于STM32与FreeRTOS的四轴机械臂项目-3
基于STM32与FreeRTOS的四轴机械臂项目
基于STM32与FreeRTOS的四轴机械臂项目-3
|
5月前
|
传感器 数据采集 人工智能
【STM32+k210项目】基于AI技术智能语音台灯的设计(完整工程资料源码)
【STM32+k210项目】基于AI技术智能语音台灯的设计(完整工程资料源码)
233 1
|
6月前
|
传感器 存储
基于STM32与FreeRTOS的四轴机械臂项目-1
基于STM32与FreeRTOS的四轴机械臂项目
基于STM32与FreeRTOS的四轴机械臂项目-1