当前位置:   article > 正文

基于STM32与FreeRTOS的四轴机械臂项目_基于stm32的机械臂项项目

基于stm32的机械臂项项目

目录

一、项目介绍

二、前期准备

1.硬件准备

2.开发环境

3.CubeMX配置

三、裸机各种模块测试

1.舵机模块

2.蓝牙模块

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

4.OLED模块

5.W25Q128模块

四、裸机三种控制测试

1.摇杆控制

2.示教器控制

3.蓝牙控制

五、裸机与FreeRTOS

1.CubeMX配置

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

六、项目演示视频


一、项目介绍

        该项目是基于FreeRTOS实时操作系统,主控为 STM32F103C8T6 开发板 ,机械臂为四轴分别被四个舵机控制。本项目实现了 3 种控制方法,分别为摇杆控制示教器控制串口蓝牙控制,采用8路ADC采集按键摇杆传感器和旋钮电位器的模拟量并由DMA搬运数据,可自制手机蓝牙APP或者直接使用官方手机蓝牙助手作为上位机,USART串口蓝牙实时收发信息,IIC驱动OLED屏幕实时显示机械臂移动张爪夹爪信息,人为控制抓取目标物。

        扩展:后续可以通过二维数组或者链表实现存储动作,通过SPI驱动W25Q128模块进行动作记忆扩容,即可以录制上百组动作,还可以附加树莓派等开发板进行视觉抓取开发,等以后有时间我再把扩展功能一起实现呈现给大家。

二、前期准备

1.硬件准备

本项目可用步进电机和驱动器作为支撑,以便提高项目扩展性,但在这里我直接用四个舵机实现。

首先你可以自己建模3D打印四轴机械臂模型,也可以直接去网上购买一套成品套件。然后需要四个舵机控制机械臂,型号无所谓,控制起来是一样的,注意需要是180度的角度型舵机,而不是360度的速度型舵机。

然后需要购买两个按键摇杆传感器实现摇杆控制,购买蓝牙模块实现串口蓝牙控制,购买四个旋钮电位器实现示教器控制。

硬件清单:

  • 四轴机械臂模型
  • 四个舵机
  • 两个按键摇杆传感器
  • HC系列蓝牙串口模块
  • 四个旋钮电位器
  • IIC协议OLED屏幕
  • SPI协议W25Q128模块

2.开发环境

单片机型号为STM32F103C8T6,开发环境为STM32CubeMX和Keil5,蓝牙控制需要手机下载蓝牙助手,在这里我下载的是官方给的HC蓝牙助手,(需要蓝牙调试助手的可以私信作者提供

STM32F103C8T6原理图:

3.CubeMX配置

我用STM32CubeMX配置如下,仅供参考:

RCC:配置外部高速晶振        

SYS:Debug设置成Serial Wire

      ADC:打开8个通道

   

DMA:搬运ADC数据

TIM2:PWM输出:选用799*1799,这样可以把舵机有效的 0.5~2.5ms / 20ms 这个区间分成180段,对应0~180度。

usart:设置波特率为9600,因为蓝牙模块默认波特率为9600,开启NVIC中断接收信息

I2C:用来显示OLED模块

时钟树配置:

最后点击 generate code 生成代码

三、裸机各种模块测试

硬件模块接线:

  • 四个舵机分别接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舵机模块测试

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

  1. //舵机A,夹爪 CH4_B11 45-135 张开闭合 初始化135
  2. void sg90_A()
  3. {
  4. if(adc_dma[3] > 4000 && angle[3] < 135)
  5. {
  6. angle[3]++;
  7. }
  8. else if(adc_dma[3] <1000 && angle[3] > 45)
  9. {
  10. angle[3]--;
  11. }
  12. }
  13. //舵机B,上下 CH3_B10 45-180 初始化180
  14. void sg90_B()
  15. {
  16. if(adc_dma[2] <1000 && angle[2] < 180)
  17. {
  18. angle[2]++;
  19. }
  20. else if(adc_dma[2] > 4000 && angle[2] > 45)
  21. {
  22. angle[2]--;
  23. }
  24. }
  25. //舵机C,前后 CH2_B3 45-180 前后 初始化45
  26. void sg90_C()
  27. {
  28. if(adc_dma[1] <1000 && angle[1] < 180)
  29. {
  30. angle[1]++;
  31. }
  32. else if(adc_dma[1] > 4000 && angle[1] > 45)
  33. {
  34. angle[1]--;
  35. }
  36. }
  37. //舵机D,底座 CH1_A15 45-135 左到右 初始化45
  38. void sg90_D()
  39. {
  40. if(adc_dma[0] <1000 && angle[0] < 135)
  41. {
  42. angle[0]++;
  43. }
  44. else if(adc_dma[0] > 4000 && angle[0] > 45)
  45. {
  46. angle[0]--;
  47. }
  48. }
  49. //开启4路PWM
  50. HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
  51. HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
  52. HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3);
  53. HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4);

2.蓝牙模块

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

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

HC-05介绍 和 HC-08介绍

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

重映射代码:

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

中断接收信息代码:

  1. // 接收完成回调函数,收到一个数据后,在这里处理
  2. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
  3. {
  4. // 判断中断是由哪个串口触发的
  5. if(huart->Instance == USART1)
  6. {
  7. // 判断接收是否完成(UART1_RX_STA bit15 位是否为1)
  8. if((UART1_RX_STA & 0x8000) == 0)
  9. {
  10. // 如果已经收到了 0x0d (回车),
  11. if(UART1_RX_STA & 0x4000)
  12. {
  13. // 则接着判断是否收到 0x0a (换行)
  14. if(buf == 0x0a)
  15. // 如果 0x0a 和 0x0d 都收到,则将 bit15 位置为1
  16. UART1_RX_STA |= 0x8000;
  17. else
  18. // 否则认为接收错误,重新开始
  19. UART1_RX_STA = 0;
  20. }
  21. else // 如果没有收到了 0x0d (回车)
  22. {
  23. //则先判断收到的这个字符是否是 0x0d (回车)
  24. if(buf == 0x0d)
  25. {
  26. // 是的话则将 bit14 位置为1
  27. UART1_RX_STA |= 0x4000;
  28. }
  29. else
  30. {
  31. // 否则将接收到的数据保存在缓存数组里
  32. UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf;
  33. UART1_RX_STA++;
  34. // 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
  35. if(UART1_RX_STA > UART1_REC_LEN - 1)
  36. UART1_RX_STA = 0;
  37. }
  38. }
  39. }
  40. // 重新开启中断
  41. HAL_UART_Receive_IT(&huart1, &buf, 1);
  42. }
  43. }

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

  1. // 开启接收中断
  2. HAL_UART_Receive_IT(&huart1, &buf, 1);
  3. while (1)
  4. {
  5. //判断判断串口是否接收完成
  6. if(UART1_RX_STA & 0x8000)
  7. {
  8. if(!strcmp((const char *)UART1_RX_Buffer, "open"))
  9. {
  10. printf("张爪\r\n");
  11. }
  12. else if (!strcmp((const char *)UART1_RX_Buffer, "close"))
  13. {
  14. printf("夹爪\r\n");
  15. }
  16. else
  17. {
  18. if(UART1_RX_Buffer[0] != '\0')
  19. printf("指令发送错误:%s\r\n", UART1_RX_Buffer);
  20. }
  21. printf("\r\n");
  22. // 重新开始下一次接收
  23. UART1_RX_STA = 0;
  24. }
  25. HAL_Delay(40);
  26. }

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

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

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

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

代码示例:

  1. uint16_t adc_dma[8];//DMA搬运的ADC采集值
  2. uint8_t angle[4] = {45,45,180,135};//舵机角度
  3. uint8_t cnt = 0;//计数用,定时串口打印信息
  4. //根据输入的0~180角度获取对应pwm占空比参数
  5. uint8_t Angle(uint8_t pwm_pulse)
  6. {
  7. return pwm_pulse + 44;
  8. }
  9. //开始ADC和DMA采集
  10. HAL_ADC_Start_DMA(&hadc1,(uint32_t *)adc_dma,8);
  11. while (1)
  12. {
  13. sg90_A();
  14. sg90_B();
  15. sg90_C();
  16. sg90_D();
  17. //输出PWM波使舵机运动
  18. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, Angle(angle[0]));
  19. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, Angle(angle[1]));
  20. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_3, Angle(angle[2]));
  21. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_4, Angle(angle[3]));
  22. cnt++;//计数,每循环一次+1
  23. if(cnt>= 50)//循环50次,每次20ms,即一共1s。每一秒发送一次数据
  24. {
  25. printf("Angle = {%d, %d, %d, %d}\r\n",angle[0],angle[1],angle[2],angle[3]);
  26. 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]);
  27. cnt = 0;
  28. }
  29. HAL_Delay(20);//每20ms循环一次
  30. }

4.OLED模块

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

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

代码实现如下:

  1. /*-- 文字: 向 --*/
  2. /*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
  3. char x1[16] = {0x00,0xF8,0x08,0x08,0x0C,0xCA,0x49,0x48,0x48,0xC8,0x08,0x08,0x08,0xF8,0x00,0x00};
  4. char x2[16] = {0x00,0xFF,0x00,0x00,0x00,0x1F,0x08,0x08,0x08,0x1F,0x00,0x40,0x80,0x7F,0x00,0x00};
  5. /*-- 文字: 前 --*/
  6. /*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
  7. char f1[16] = {0x08,0x08,0xE8,0x29,0x2E,0x28,0xE8,0x08,0x08,0xC8,0x0C,0x0B,0xE8,0x08,0x08,0x00};
  8. char f2[16] = {0x00,0x00,0xFF,0x09,0x49,0x89,0x7F,0x00,0x00,0x0F,0x40,0x80,0x7F,0x00,0x00,0x00};
  9. /*-- 文字: 后 --*/
  10. /*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
  11. char b1[16] = {0x00,0x00,0x00,0xFC,0x24,0x24,0x24,0x24,0x22,0x22,0x22,0x23,0x22,0x20,0x20,0x00};
  12. char b2[16] = {0x40,0x20,0x18,0x07,0x00,0xFE,0x42,0x42,0x42,0x42,0x42,0x42,0xFE,0x00,0x00,0x00};
  13. /*-- 文字: 上 --*/
  14. /*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
  15. char u1[16] = {0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00};
  16. char u2[16] = {0x40,0x40,0x40,0x40,0x40,0x40,0x7F,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00};
  17. /*-- 文字: 下 --*/
  18. /*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
  19. char d1[16] = {0x02,0x02,0x02,0x02,0x02,0x02,0xFE,0x02,0x02,0x42,0x82,0x02,0x02,0x02,0x02,0x00};
  20. char d2[16] = {0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x01,0x06,0x00,0x00,0x00};
  21. /*-- 文字: 左 --*/
  22. /*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
  23. char l1[16] = {0x08,0x08,0x08,0x08,0x88,0x78,0x0F,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00};
  24. char l2[16] = {0x20,0x10,0x48,0x46,0x41,0x41,0x41,0x41,0x7F,0x41,0x41,0x41,0x41,0x40,0x40,0x00};
  25. /*-- 文字: 右 --*/
  26. /*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
  27. char r1[16] = {0x08,0x08,0x08,0x08,0xC8,0x38,0x0F,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00};
  28. char r2[16] = {0x08,0x04,0x02,0x01,0xFF,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0xFF,0x00,0x00,0x00};
  29. /*-- 文字: 张 --*/
  30. /*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
  31. char o1[16] = {0x02,0xE2,0x22,0x22,0x3E,0x80,0x80,0xFF,0x80,0xA0,0x90,0x88,0x86,0x80,0x80,0x00};
  32. char o2[16] = {0x00,0x43,0x82,0x42,0x3E,0x00,0x00,0xFF,0x40,0x21,0x06,0x08,0x10,0x20,0x40,0x00};
  33. /*-- 文字: 夹 --*/
  34. /*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
  35. char c1[16] = {0x00,0x08,0x08,0x28,0x48,0x08,0x08,0xFF,0x08,0x08,0x48,0x28,0x08,0x08,0x00,0x00};
  36. char c2[16] = {0x81,0x81,0x41,0x41,0x21,0x11,0x0D,0x03,0x0D,0x11,0x21,0x41,0x41,0x81,0x81,0x00};
  37. /*-- 文字: 爪 --*/
  38. /*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
  39. char z1[16] = {0x00,0x00,0x00,0xFC,0x04,0x04,0xFC,0x04,0x02,0x02,0xFE,0x03,0x02,0x00,0x00,0x00};
  40. char z2[16] = {0x80,0x60,0x18,0x07,0x00,0x00,0x7F,0x00,0x00,0x00,0x01,0x0E,0x30,0x40,0x80,0x00};
  41. void Oled_Write_Cmd(uint8_t dataCmd)
  42. {
  43. HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x00, I2C_MEMADD_SIZE_8BIT,
  44. &dataCmd, 1, 0xff);
  45. }
  46. void Oled_Write_Data(uint8_t dataData)
  47. {
  48. HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x40, I2C_MEMADD_SIZE_8BIT,
  49. &dataData, 1, 0xff);
  50. }
  51. void Oled_Init(void){
  52. Oled_Write_Cmd(0xAE);//--display off
  53. Oled_Write_Cmd(0x00);//---set low column address
  54. Oled_Write_Cmd(0x10);//---set high column address
  55. Oled_Write_Cmd(0x40);//--set start line address
  56. Oled_Write_Cmd(0xB0);//--set page address
  57. Oled_Write_Cmd(0x81); // contract control
  58. Oled_Write_Cmd(0xFF);//--128
  59. Oled_Write_Cmd(0xA1);//set segment remap
  60. Oled_Write_Cmd(0xA6);//--normal / reverse
  61. Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
  62. Oled_Write_Cmd(0x3F);//--1/32 duty
  63. Oled_Write_Cmd(0xC8);//Com scan direction
  64. Oled_Write_Cmd(0xD3);//-set display offset
  65. Oled_Write_Cmd(0x00);//
  66. Oled_Write_Cmd(0xD5);//set osc division
  67. Oled_Write_Cmd(0x80);//
  68. Oled_Write_Cmd(0xD8);//set area color mode off
  69. Oled_Write_Cmd(0x05);//
  70. Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
  71. Oled_Write_Cmd(0xF1);//
  72. Oled_Write_Cmd(0xDA);//set com pin configuartion
  73. Oled_Write_Cmd(0x12);//
  74. Oled_Write_Cmd(0xDB);//set Vcomh
  75. Oled_Write_Cmd(0x30);//
  76. Oled_Write_Cmd(0x8D);//set charge pump enable
  77. Oled_Write_Cmd(0x14);//
  78. Oled_Write_Cmd(0xAF);//--turn on oled panel
  79. }
  80. void Oled_Screen_Clear(void){
  81. unsigned char i,n;
  82. Oled_Write_Cmd (0x20); //set memory addressing mode
  83. Oled_Write_Cmd (0x02); //page addressing mode
  84. for(i=0;i<8;i++){
  85. Oled_Write_Cmd(0xb0+i); //
  86. Oled_Write_Cmd(0x00); //
  87. Oled_Write_Cmd(0x10); //
  88. for(n=0;n<128;n++)Oled_Write_Data(0x00);
  89. }
  90. }
  91. void Oled_Show_open()
  92. {
  93. unsigned char i;
  94. Oled_Init();
  95. // 选择一个位置确认页寻址模式
  96. Oled_Write_Cmd(0x20);
  97. Oled_Write_Cmd(0x02);
  98. Oled_Screen_Clear();
  99. // 选择PAGE0 1011 0000 0xB0
  100. Oled_Write_Cmd(0xB0);
  101. Oled_Write_Cmd(0x00);
  102. Oled_Write_Cmd(0x10);
  103. for(i=0;i<16;i++){
  104. Oled_Write_Data(o1[i]);
  105. }
  106. for(i=0;i<16;i++){
  107. Oled_Write_Data(z1[i]);
  108. }
  109. Oled_Write_Cmd(0xB1);
  110. Oled_Write_Cmd(0x00);
  111. Oled_Write_Cmd(0x10);
  112. for(i=0;i<16;i++){
  113. Oled_Write_Data(o2[i]);
  114. }
  115. for(i=0;i<16;i++){
  116. Oled_Write_Data(z2[i]);
  117. }
  118. }
  119. void Oled_Show_close()
  120. {
  121. unsigned char i;
  122. Oled_Init();
  123. Oled_Write_Cmd(0x20);
  124. Oled_Write_Cmd(0x02);
  125. Oled_Screen_Clear();
  126. Oled_Write_Cmd(0xB0);
  127. Oled_Write_Cmd(0x00);
  128. Oled_Write_Cmd(0x10);
  129. for(i=0;i<16;i++){
  130. Oled_Write_Data(c1[i]);
  131. }
  132. for(i=0;i<16;i++){
  133. Oled_Write_Data(z1[i]);
  134. }
  135. Oled_Write_Cmd(0xB1);
  136. Oled_Write_Cmd(0x00);
  137. Oled_Write_Cmd(0x10);
  138. for(i=0;i<16;i++){
  139. Oled_Write_Data(c2[i]);
  140. }
  141. for(i=0;i<16;i++){
  142. Oled_Write_Data(z2[i]);
  143. }
  144. }
  145. void Oled_Show_up()
  146. {
  147. unsigned char i;
  148. Oled_Init();
  149. Oled_Write_Cmd(0x20);
  150. Oled_Write_Cmd(0x02);
  151. Oled_Screen_Clear();
  152. Oled_Write_Cmd(0xB0);
  153. Oled_Write_Cmd(0x00);
  154. Oled_Write_Cmd(0x10);
  155. for(i=0;i<16;i++){
  156. Oled_Write_Data(x1[i]);
  157. }
  158. for(i=0;i<16;i++){
  159. Oled_Write_Data(u1[i]);
  160. }
  161. Oled_Write_Cmd(0xB1);
  162. Oled_Write_Cmd(0x00);
  163. Oled_Write_Cmd(0x10);
  164. for(i=0;i<16;i++){
  165. Oled_Write_Data(x2[i]);
  166. }
  167. for(i=0;i<16;i++){
  168. Oled_Write_Data(u2[i]);
  169. }
  170. }
  171. void Oled_Show_down()
  172. {
  173. unsigned char i;
  174. Oled_Init();
  175. Oled_Write_Cmd(0x20);
  176. Oled_Write_Cmd(0x02);
  177. Oled_Screen_Clear();
  178. Oled_Write_Cmd(0xB0);
  179. Oled_Write_Cmd(0x00);
  180. Oled_Write_Cmd(0x10);
  181. for(i=0;i<16;i++){
  182. Oled_Write_Data(x1[i]);
  183. }
  184. for(i=0;i<16;i++){
  185. Oled_Write_Data(d1[i]);
  186. }
  187. Oled_Write_Cmd(0xB1);
  188. Oled_Write_Cmd(0x00);
  189. Oled_Write_Cmd(0x10);
  190. for(i=0;i<16;i++){
  191. Oled_Write_Data(x2[i]);
  192. }
  193. for(i=0;i<16;i++){
  194. Oled_Write_Data(d2[i]);
  195. }
  196. }
  197. void Oled_Show_left()
  198. {
  199. unsigned char i;
  200. Oled_Init();
  201. Oled_Write_Cmd(0x20);
  202. Oled_Write_Cmd(0x02);
  203. Oled_Screen_Clear();
  204. Oled_Write_Cmd(0xB0);
  205. Oled_Write_Cmd(0x00);
  206. Oled_Write_Cmd(0x10);
  207. for(i=0;i<16;i++){
  208. Oled_Write_Data(x1[i]);
  209. }
  210. for(i=0;i<16;i++){
  211. Oled_Write_Data(l1[i]);
  212. }
  213. Oled_Write_Cmd(0xB1);
  214. Oled_Write_Cmd(0x00);
  215. Oled_Write_Cmd(0x10);
  216. for(i=0;i<16;i++){
  217. Oled_Write_Data(x2[i]);
  218. }
  219. for(i=0;i<16;i++){
  220. Oled_Write_Data(l2[i]);
  221. }
  222. }
  223. void Oled_Show_right()
  224. {
  225. unsigned char i;
  226. Oled_Init();
  227. Oled_Write_Cmd(0x20);
  228. Oled_Write_Cmd(0x02);
  229. Oled_Screen_Clear();
  230. Oled_Write_Cmd(0xB0);
  231. Oled_Write_Cmd(0x00);
  232. Oled_Write_Cmd(0x10);
  233. for(i=0;i<16;i++){
  234. Oled_Write_Data(x1[i]);
  235. }
  236. for(i=0;i<16;i++){
  237. Oled_Write_Data(r1[i]);
  238. }
  239. Oled_Write_Cmd(0xB1);
  240. Oled_Write_Cmd(0x00);
  241. Oled_Write_Cmd(0x10);
  242. for(i=0;i<16;i++){
  243. Oled_Write_Data(x2[i]);
  244. }
  245. for(i=0;i<16;i++){
  246. Oled_Write_Data(r2[i]);
  247. }
  248. }
  249. void Oled_Show_front()
  250. {
  251. unsigned char i;
  252. Oled_Init();
  253. Oled_Write_Cmd(0x20);
  254. Oled_Write_Cmd(0x02);
  255. Oled_Screen_Clear();
  256. Oled_Write_Cmd(0xB0);
  257. Oled_Write_Cmd(0x00);
  258. Oled_Write_Cmd(0x10);
  259. for(i=0;i<16;i++){
  260. Oled_Write_Data(x1[i]);
  261. }
  262. for(i=0;i<16;i++){
  263. Oled_Write_Data(f1[i]);
  264. }
  265. Oled_Write_Cmd(0xB1);
  266. Oled_Write_Cmd(0x00);
  267. Oled_Write_Cmd(0x10);
  268. for(i=0;i<16;i++){
  269. Oled_Write_Data(x2[i]);
  270. }
  271. for(i=0;i<16;i++){
  272. Oled_Write_Data(f2[i]);
  273. }
  274. }
  275. void Oled_Show_behind()
  276. {
  277. unsigned char i;
  278. Oled_Init();
  279. Oled_Write_Cmd(0x20);
  280. Oled_Write_Cmd(0x02);
  281. Oled_Screen_Clear();
  282. Oled_Write_Cmd(0xB0);
  283. Oled_Write_Cmd(0x00);
  284. Oled_Write_Cmd(0x10);
  285. for(i=0;i<16;i++){
  286. Oled_Write_Data(x1[i]);
  287. }
  288. for(i=0;i<16;i++){
  289. Oled_Write_Data(b1[i]);
  290. }
  291. Oled_Write_Cmd(0xB1);
  292. Oled_Write_Cmd(0x00);
  293. Oled_Write_Cmd(0x10);
  294. for(i=0;i<16;i++){
  295. Oled_Write_Data(x2[i]);
  296. }
  297. for(i=0;i<16;i++){
  298. Oled_Write_Data(b2[i]);
  299. }
  300. }
  301. while (1)
  302. {
  303. Oled_Show_open();
  304. HAL_Delay(1000);
  305. Oled_Show_close();
  306. HAL_Delay(1000);
  307. Oled_Show_up();
  308. HAL_Delay(1000);
  309. Oled_Show_down();
  310. HAL_Delay(1000);
  311. Oled_Show_left();
  312. HAL_Delay(1000);
  313. Oled_Show_right();
  314. HAL_Delay(1000);
  315. Oled_Show_front();
  316. HAL_Delay(1000);
  317. Oled_Show_behind();
  318. HAL_Delay(1000);
  319. }

5.W25Q128模块

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

四、裸机三种控制测试

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

1.摇杆控制

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

代码示例:

pwm.c

  1. extern uint16_t adc_dma[4];//DMA搬运的ADC采集值
  2. extern int8_t angle[4] = {45,45,180,135};//舵机角度
  3. //根据输入的0~180角度获取对应pwm占空比参数
  4. unsigned char Angle(unsigned char pwm_pulse)
  5. {
  6. return pwm_pulse + 44;
  7. }
  8. //舵机A,夹爪 CH4_B11 45-135 张开闭合 初始化135
  9. void sg90_A()
  10. {
  11. if(adc_dma[3] > 4000 && angle[3] < 135)
  12. {
  13. angle[3]++;
  14. }
  15. else if(adc_dma[3] <1000 && angle[3] > 45)
  16. {
  17. angle[3]--;
  18. }
  19. }
  20. //舵机B,上下 CH3_B10 45-180 初始化180
  21. void sg90_B()
  22. {
  23. if(adc_dma[2] <1000 && angle[2] < 180)
  24. {
  25. angle[2]++;
  26. }
  27. else if(adc_dma[2] > 4000 && angle[2] > 45)
  28. {
  29. angle[2]--;
  30. }
  31. }
  32. //舵机C,前后 CH2_B3 45-180 前后 初始化45
  33. void sg90_C()
  34. {
  35. if(adc_dma[1] <1000 && angle[1] < 180)
  36. {
  37. angle[1]++;
  38. }
  39. else if(adc_dma[1] > 4000 && angle[1] > 45)
  40. {
  41. angle[1]--;
  42. }
  43. }
  44. //舵机D,底座 CH1_A15 45-135 左到右 初始化45
  45. void sg90_D()
  46. {
  47. if(adc_dma[0] <1000 && angle[0] < 135)
  48. {
  49. angle[0]++;
  50. }
  51. else if(adc_dma[0] > 4000 && angle[0] > 45)
  52. {
  53. angle[0]--;
  54. }
  55. }

pwm.h

  1. #ifndef __PWM_H__
  2. #define __PWM_H__
  3. unsigned char Angle(unsigned char pwm_pulse);
  4. void sg90_A(void);
  5. void sg90_B(void);
  6. void sg90_C(void);
  7. void sg90_D(void);
  8. #endif

oled.c

  1. #include "main.h"
  2. #include "i2c.h"
  3. #include "oled.h"
  4. /*-- 文字: 向 --*/
  5. /*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
  6. char x1[16] = {0x00,0xF8,0x08,0x08,0x0C,0xCA,0x49,0x48,0x48,0xC8,0x08,0x08,0x08,0xF8,0x00,0x00};
  7. char x2[16] = {0x00,0xFF,0x00,0x00,0x00,0x1F,0x08,0x08,0x08,0x1F,0x00,0x40,0x80,0x7F,0x00,0x00};
  8. /*-- 文字: 前 --*/
  9. /*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
  10. char f1[16] = {0x08,0x08,0xE8,0x29,0x2E,0x28,0xE8,0x08,0x08,0xC8,0x0C,0x0B,0xE8,0x08,0x08,0x00};
  11. char f2[16] = {0x00,0x00,0xFF,0x09,0x49,0x89,0x7F,0x00,0x00,0x0F,0x40,0x80,0x7F,0x00,0x00,0x00};
  12. /*-- 文字: 后 --*/
  13. /*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
  14. char b1[16] = {0x00,0x00,0x00,0xFC,0x24,0x24,0x24,0x24,0x22,0x22,0x22,0x23,0x22,0x20,0x20,0x00};
  15. char b2[16] = {0x40,0x20,0x18,0x07,0x00,0xFE,0x42,0x42,0x42,0x42,0x42,0x42,0xFE,0x00,0x00,0x00};
  16. /*-- 文字: 上 --*/
  17. /*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
  18. char u1[16] = {0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00};
  19. char u2[16] = {0x40,0x40,0x40,0x40,0x40,0x40,0x7F,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00};
  20. /*-- 文字: 下 --*/
  21. /*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
  22. char d1[16] = {0x02,0x02,0x02,0x02,0x02,0x02,0xFE,0x02,0x02,0x42,0x82,0x02,0x02,0x02,0x02,0x00};
  23. char d2[16] = {0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x01,0x06,0x00,0x00,0x00};
  24. /*-- 文字: 左 --*/
  25. /*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
  26. char l1[16] = {0x08,0x08,0x08,0x08,0x88,0x78,0x0F,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00};
  27. char l2[16] = {0x20,0x10,0x48,0x46,0x41,0x41,0x41,0x41,0x7F,0x41,0x41,0x41,0x41,0x40,0x40,0x00};
  28. /*-- 文字: 右 --*/
  29. /*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
  30. char r1[16] = {0x08,0x08,0x08,0x08,0xC8,0x38,0x0F,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00};
  31. char r2[16] = {0x08,0x04,0x02,0x01,0xFF,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0xFF,0x00,0x00,0x00};
  32. /*-- 文字: 张 --*/
  33. /*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
  34. char o1[16] = {0x02,0xE2,0x22,0x22,0x3E,0x80,0x80,0xFF,0x80,0xA0,0x90,0x88,0x86,0x80,0x80,0x00};
  35. char o2[16] = {0x00,0x43,0x82,0x42,0x3E,0x00,0x00,0xFF,0x40,0x21,0x06,0x08,0x10,0x20,0x40,0x00};
  36. /*-- 文字: 夹 --*/
  37. /*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
  38. char c1[16] = {0x00,0x08,0x08,0x28,0x48,0x08,0x08,0xFF,0x08,0x08,0x48,0x28,0x08,0x08,0x00,0x00};
  39. char c2[16] = {0x81,0x81,0x41,0x41,0x21,0x11,0x0D,0x03,0x0D,0x11,0x21,0x41,0x41,0x81,0x81,0x00};
  40. /*-- 文字: 爪 --*/
  41. /*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
  42. char z1[16] = {0x00,0x00,0x00,0xFC,0x04,0x04,0xFC,0x04,0x02,0x02,0xFE,0x03,0x02,0x00,0x00,0x00};
  43. char z2[16] = {0x80,0x60,0x18,0x07,0x00,0x00,0x7F,0x00,0x00,0x00,0x01,0x0E,0x30,0x40,0x80,0x00};
  44. void Oled_Write_Cmd(uint8_t dataCmd)
  45. {
  46. HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x00, I2C_MEMADD_SIZE_8BIT,
  47. &dataCmd, 1, 0xff);
  48. }
  49. void Oled_Write_Data(uint8_t dataData)
  50. {
  51. HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x40, I2C_MEMADD_SIZE_8BIT,
  52. &dataData, 1, 0xff);
  53. }
  54. void Oled_Init(void){
  55. Oled_Write_Cmd(0xAE);//--display off
  56. Oled_Write_Cmd(0x00);//---set low column address
  57. Oled_Write_Cmd(0x10);//---set high column address
  58. Oled_Write_Cmd(0x40);//--set start line address
  59. Oled_Write_Cmd(0xB0);//--set page address
  60. Oled_Write_Cmd(0x81); // contract control
  61. Oled_Write_Cmd(0xFF);//--128
  62. Oled_Write_Cmd(0xA1);//set segment remap
  63. Oled_Write_Cmd(0xA6);//--normal / reverse
  64. Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
  65. Oled_Write_Cmd(0x3F);//--1/32 duty
  66. Oled_Write_Cmd(0xC8);//Com scan direction
  67. Oled_Write_Cmd(0xD3);//-set display offset
  68. Oled_Write_Cmd(0x00);//
  69. Oled_Write_Cmd(0xD5);//set osc division
  70. Oled_Write_Cmd(0x80);//
  71. Oled_Write_Cmd(0xD8);//set area color mode off
  72. Oled_Write_Cmd(0x05);//
  73. Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
  74. Oled_Write_Cmd(0xF1);//
  75. Oled_Write_Cmd(0xDA);//set com pin configuartion
  76. Oled_Write_Cmd(0x12);//
  77. Oled_Write_Cmd(0xDB);//set Vcomh
  78. Oled_Write_Cmd(0x30);//
  79. Oled_Write_Cmd(0x8D);//set charge pump enable
  80. Oled_Write_Cmd(0x14);//
  81. Oled_Write_Cmd(0xAF);//--turn on oled panel
  82. }
  83. void Oled_Screen_Clear(void){
  84. unsigned char i,n;
  85. Oled_Write_Cmd (0x20); //set memory addressing mode
  86. Oled_Write_Cmd (0x02); //page addressing mode
  87. for(i=0;i<8;i++){
  88. Oled_Write_Cmd(0xb0+i); //
  89. Oled_Write_Cmd(0x00); //
  90. Oled_Write_Cmd(0x10); //
  91. for(n=0;n<128;n++)Oled_Write_Data(0x00);
  92. }
  93. }
  94. void Oled_Show_open()
  95. {
  96. unsigned char i;
  97. Oled_Init();
  98. // 选择一个位置确认页寻址模式
  99. Oled_Write_Cmd(0x20);
  100. Oled_Write_Cmd(0x02);
  101. Oled_Screen_Clear();
  102. // 选择PAGE0 1011 0000 0xB0
  103. Oled_Write_Cmd(0xB0);
  104. Oled_Write_Cmd(0x00);
  105. Oled_Write_Cmd(0x10);
  106. for(i=0;i<16;i++){
  107. Oled_Write_Data(o1[i]);
  108. }
  109. for(i=0;i<16;i++){
  110. Oled_Write_Data(z1[i]);
  111. }
  112. Oled_Write_Cmd(0xB1);
  113. Oled_Write_Cmd(0x00);
  114. Oled_Write_Cmd(0x10);
  115. for(i=0;i<16;i++){
  116. Oled_Write_Data(o2[i]);
  117. }
  118. for(i=0;i<16;i++){
  119. Oled_Write_Data(z2[i]);
  120. }
  121. }
  122. void Oled_Show_close()
  123. {
  124. unsigned char i;
  125. Oled_Init();
  126. Oled_Write_Cmd(0x20);
  127. Oled_Write_Cmd(0x02);
  128. Oled_Screen_Clear();
  129. Oled_Write_Cmd(0xB0);
  130. Oled_Write_Cmd(0x00);
  131. Oled_Write_Cmd(0x10);
  132. for(i=0;i<16;i++){
  133. Oled_Write_Data(c1[i]);
  134. }
  135. for(i=0;i<16;i++){
  136. Oled_Write_Data(z1[i]);
  137. }
  138. Oled_Write_Cmd(0xB1);
  139. Oled_Write_Cmd(0x00);
  140. Oled_Write_Cmd(0x10);
  141. for(i=0;i<16;i++){
  142. Oled_Write_Data(c2[i]);
  143. }
  144. for(i=0;i<16;i++){
  145. Oled_Write_Data(z2[i]);
  146. }
  147. }
  148. void Oled_Show_up()
  149. {
  150. unsigned char i;
  151. Oled_Init();
  152. Oled_Write_Cmd(0x20);
  153. Oled_Write_Cmd(0x02);
  154. Oled_Screen_Clear();
  155. Oled_Write_Cmd(0xB0);
  156. Oled_Write_Cmd(0x00);
  157. Oled_Write_Cmd(0x10);
  158. for(i=0;i<16;i++){
  159. Oled_Write_Data(x1[i]);
  160. }
  161. for(i=0;i<16;i++){
  162. Oled_Write_Data(u1[i]);
  163. }
  164. Oled_Write_Cmd(0xB1);
  165. Oled_Write_Cmd(0x00);
  166. Oled_Write_Cmd(0x10);
  167. for(i=0;i<16;i++){
  168. Oled_Write_Data(x2[i]);
  169. }
  170. for(i=0;i<16;i++){
  171. Oled_Write_Data(u2[i]);
  172. }
  173. }
  174. void Oled_Show_down()
  175. {
  176. unsigned char i;
  177. Oled_Init();
  178. Oled_Write_Cmd(0x20);
  179. Oled_Write_Cmd(0x02);
  180. Oled_Screen_Clear();
  181. Oled_Write_Cmd(0xB0);
  182. Oled_Write_Cmd(0x00);
  183. Oled_Write_Cmd(0x10);
  184. for(i=0;i<16;i++){
  185. Oled_Write_Data(x1[i]);
  186. }
  187. for(i=0;i<16;i++){
  188. Oled_Write_Data(d1[i]);
  189. }
  190. Oled_Write_Cmd(0xB1);
  191. Oled_Write_Cmd(0x00);
  192. Oled_Write_Cmd(0x10);
  193. for(i=0;i<16;i++){
  194. Oled_Write_Data(x2[i]);
  195. }
  196. for(i=0;i<16;i++){
  197. Oled_Write_Data(d2[i]);
  198. }
  199. }
  200. void Oled_Show_left()
  201. {
  202. unsigned char i;
  203. Oled_Init();
  204. Oled_Write_Cmd(0x20);
  205. Oled_Write_Cmd(0x02);
  206. Oled_Screen_Clear();
  207. Oled_Write_Cmd(0xB0);
  208. Oled_Write_Cmd(0x00);
  209. Oled_Write_Cmd(0x10);
  210. for(i=0;i<16;i++){
  211. Oled_Write_Data(x1[i]);
  212. }
  213. for(i=0;i<16;i++){
  214. Oled_Write_Data(l1[i]);
  215. }
  216. Oled_Write_Cmd(0xB1);
  217. Oled_Write_Cmd(0x00);
  218. Oled_Write_Cmd(0x10);
  219. for(i=0;i<16;i++){
  220. Oled_Write_Data(x2[i]);
  221. }
  222. for(i=0;i<16;i++){
  223. Oled_Write_Data(l2[i]);
  224. }
  225. }
  226. void Oled_Show_right()
  227. {
  228. unsigned char i;
  229. Oled_Init();
  230. Oled_Write_Cmd(0x20);
  231. Oled_Write_Cmd(0x02);
  232. Oled_Screen_Clear();
  233. Oled_Write_Cmd(0xB0);
  234. Oled_Write_Cmd(0x00);
  235. Oled_Write_Cmd(0x10);
  236. for(i=0;i<16;i++){
  237. Oled_Write_Data(x1[i]);
  238. }
  239. for(i=0;i<16;i++){
  240. Oled_Write_Data(r1[i]);
  241. }
  242. Oled_Write_Cmd(0xB1);
  243. Oled_Write_Cmd(0x00);
  244. Oled_Write_Cmd(0x10);
  245. for(i=0;i<16;i++){
  246. Oled_Write_Data(x2[i]);
  247. }
  248. for(i=0;i<16;i++){
  249. Oled_Write_Data(r2[i]);
  250. }
  251. }
  252. void Oled_Show_front()
  253. {
  254. unsigned char i;
  255. Oled_Init();
  256. Oled_Write_Cmd(0x20);
  257. Oled_Write_Cmd(0x02);
  258. Oled_Screen_Clear();
  259. Oled_Write_Cmd(0xB0);
  260. Oled_Write_Cmd(0x00);
  261. Oled_Write_Cmd(0x10);
  262. for(i=0;i<16;i++){
  263. Oled_Write_Data(x1[i]);
  264. }
  265. for(i=0;i<16;i++){
  266. Oled_Write_Data(f1[i]);
  267. }
  268. Oled_Write_Cmd(0xB1);
  269. Oled_Write_Cmd(0x00);
  270. Oled_Write_Cmd(0x10);
  271. for(i=0;i<16;i++){
  272. Oled_Write_Data(x2[i]);
  273. }
  274. for(i=0;i<16;i++){
  275. Oled_Write_Data(f2[i]);
  276. }
  277. }
  278. void Oled_Show_behind()
  279. {
  280. unsigned char i;
  281. Oled_Init();
  282. Oled_Write_Cmd(0x20);
  283. Oled_Write_Cmd(0x02);
  284. Oled_Screen_Clear();
  285. Oled_Write_Cmd(0xB0);
  286. Oled_Write_Cmd(0x00);
  287. Oled_Write_Cmd(0x10);
  288. for(i=0;i<16;i++){
  289. Oled_Write_Data(x1[i]);
  290. }
  291. for(i=0;i<16;i++){
  292. Oled_Write_Data(b1[i]);
  293. }
  294. Oled_Write_Cmd(0xB1);
  295. Oled_Write_Cmd(0x00);
  296. Oled_Write_Cmd(0x10);
  297. for(i=0;i<16;i++){
  298. Oled_Write_Data(x2[i]);
  299. }
  300. for(i=0;i<16;i++){
  301. Oled_Write_Data(b2[i]);
  302. }
  303. }

oled.h

  1. #ifndef __OLED_H__
  2. #define __OLED_H__
  3. void Oled_Write_Cmd(uint8_t dataCmd);
  4. void Oled_Write_Data(uint8_t dataData);
  5. void Oled_Init(void);
  6. void Oled_Screen_Clear(void);
  7. // oled显示封装
  8. void Oled_Show_open();
  9. void Oled_Show_close();
  10. void Oled_Show_up();
  11. void Oled_Show_down();
  12. void Oled_Show_left();
  13. void Oled_Show_right();
  14. void Oled_Show_front();
  15. void Oled_Show_behind();
  16. #endif

main.c

  1. uint16_t adc_dma[8];//DMA搬运的ADC采集值
  2. uint8_t angle[4] = {45,45,180,135};//舵机角度
  3. uint8_t cnt = 0;//计数用,定时串口打印信息
  4. //覆写printf,用于串口打印数据
  5. int fputc(int ch, FILE *f)
  6. {
  7. unsigned char temp[1]={ch};
  8. HAL_UART_Transmit(&huart1,temp,1,0xffff);
  9. return ch;
  10. }
  11. //int main
  12. //开始ADC和DMA采集
  13. HAL_ADC_Start_DMA(&hadc1,(uint32_t *)adc_dma,4);
  14. //开启4路PWM
  15. HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
  16. HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
  17. HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3);
  18. HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4);
  19. //延时半秒,系统稳定一下
  20. HAL_Delay(500);
  21. printf("test\r\n");
  22. while (1)
  23. {
  24. sg90_A();
  25. sg90_B();
  26. sg90_C();
  27. sg90_D();
  28. //输出PWM波使舵机运动
  29. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, Angle(angle[0]));
  30. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, Angle(angle[1]));
  31. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_3, Angle(angle[2]));
  32. __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_4, Angle(angle[3]));
  33. cnt++;//计数,每循环一次+1
  34. if(cnt>= 50)//循环50次,每次20ms,即一共1s。每一秒发送一次数据
  35. {
  36. printf("Angle = {%d, %d, %d, %d}\r\n",angle[0],angle[1],angle[2],angle[3]);
  37. cnt = 0;
  38. }
  39. HAL_Delay(20);//每20ms循环一次(改成15更流畅)
  40. }

2.示教器控制

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

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

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

在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

  1. #include "stdio.h"
  2. #include "string.h"
  3. #include "pwm.h"
  4. #include "adc.h"
  5. #include "dma.h"
  6. /*蓝牙控制机械臂指令:
  7. s 停
  8. l/r 左右
  9. u/d 上下
  10. f/b 前后
  11. o/c 开合*/
  12. uint8_t cmd_BLE = 's';
  13. extern uint16_t adc_dma[4];//DMA搬运的ADC采集值
  14. //覆写printf
  15. int fputc(int ch, FILE *f)
  16. {
  17. unsigned char temp[1]={ch};
  18. HAL_UART_Transmit(&huart1,temp,1,0xffff);
  19. return ch;
  20. }
  21. //=====串口(中断)=======
  22. //串口接收缓存(1字节)
  23. uint8_t buf=0;
  24. //定义最大接收字节数 200,可根据需求调整
  25. #define UART1_REC_LEN 200
  26. // 接收缓冲, 串口接收到的数据放在这个数组里,最大UART1_REC_LEN个字节
  27. uint8_t UART1_RX_Buffer[UART1_REC_LEN];
  28. // 接收状态
  29. // bit15, 接收完成标志
  30. // bit14, 接收到0x0d
  31. // bit13~0, 接收到的有效字节数目
  32. uint16_t UART1_RX_STA=0;
  33. // 串口中断:接收完成回调函数,收到一个数据后,在这里处理
  34. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
  35. {
  36. // 判断中断是由哪个串口触发的
  37. if(huart->Instance == USART1)
  38. {
  39. // 判断接收是否完成(UART1_RX_STA bit15 位是否为1)
  40. if((UART1_RX_STA & 0x8000) == 0)
  41. {
  42. // 如果已经收到了 0x0d (回车),
  43. if(UART1_RX_STA & 0x4000)
  44. {
  45. // 则接着判断是否收到 0x0a (换行)
  46. if(buf == 0x0a)
  47. {
  48. // 如果 0x0a 和 0x0d 都收到,则将 bit15 位置为1
  49. UART1_RX_STA |= 0x8000;
  50. //=======中断信息处理=======
  51. //获取蓝牙控制指令,A打头,后面一个字母就是指令内容
  52. if(UART1_RX_Buffer[0] == 'A')
  53. {
  54. HAL_ADC_Stop_DMA(&hadc1);//停止ADC DMA
  55. MX_ADC1_Init();//初始化ADC1
  56. HAL_ADC_Start_DMA(&hadc1,(uint32_t *)adc_dma,4); //开启ADC DMA
  57. cmd_BLE = UART1_RX_Buffer[1];
  58. }
  59. else {
  60. if(UART1_RX_Buffer[0] != '\0')
  61. printf("指令发送错误:%s\r\n", UART1_RX_Buffer);
  62. }
  63. //==========================
  64. memset(UART1_RX_Buffer, 0, strlen((const char *)UART1_RX_Buffer));
  65. // 重新开始下一次接收
  66. UART1_RX_STA = 0;
  67. //==========================
  68. }
  69. else
  70. // 否则认为接收错误,重新开始
  71. UART1_RX_STA = 0;
  72. }
  73. else // 如果没有收到了 0x0d (回车)
  74. {
  75. //则先判断收到的这个字符是否是 0x0d (回车)
  76. if(buf == 0x0d)
  77. {
  78. // 是的话则将 bit14 位置为1
  79. UART1_RX_STA |= 0x4000;
  80. }
  81. else
  82. {
  83. // 否则将接收到的数据保存在缓存数组里
  84. UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf;
  85. UART1_RX_STA++;
  86. // 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
  87. if(UART1_RX_STA > UART1_REC_LEN - 1)
  88. UART1_RX_STA = 0;
  89. }
  90. }
  91. }
  92. // 重新开启中断
  93. HAL_UART_Receive_IT(&huart1, &buf, 1);
  94. }
  95. }
  96. // 在串口初始化中开启接收中断
  97. HAL_UART_Receive_IT(&huart1, &buf, 1);

pwm.c

  1. #include "pwm.h"
  2. #include "main.h"
  3. #include "oled.h"
  4. extern uint16_t adc_dma[8];//DMA搬运的ADC采集值
  5. extern uint8_t angle[4];//舵机角度
  6. extern uint8_t Mode;
  7. extern uint8_t cmd_BLE;
  8. //根据输入的0~180角度获取对应pwm占空比参数
  9. unsigned char Angle(unsigned char pwm_pulse)
  10. {
  11. return pwm_pulse + 44;
  12. }
  13. //舵机A,夹爪 CH4_B11 45-135 张开闭合 初始化135
  14. void sg90_A()
  15. {
  16. if((cmd_BLE == 'c' || adc_dma[3] > 4000) && angle[3] < 135)//合
  17. {
  18. angle[3]++;
  19. Oled_Show_close();
  20. }
  21. else if((cmd_BLE == 'o' || adc_dma[3] <1000) && angle[3] > 45)//开
  22. {
  23. angle[3]--;
  24. Oled_Show_open();
  25. }
  26. }
  27. //舵机B,上下 CH3_B10 45-180 初始化180
  28. void sg90_B()
  29. {
  30. if((cmd_BLE == 'u' || adc_dma[2] <1000) && angle[2] < 180)//上
  31. {
  32. angle[2]++;
  33. Oled_Show_up();
  34. }
  35. else if((cmd_BLE == 'd' || adc_dma[2] > 4000) && angle[2] > 45)//下
  36. {
  37. angle[2]--;
  38. Oled_Show_down();
  39. }
  40. }
  41. //舵机C,前后 CH2_B3 45-180 前后 初始化45
  42. void sg90_C()
  43. {
  44. if((cmd_BLE == 'f' || adc_dma[1] <1000) && angle[1] < 180)//前
  45. {
  46. angle[1]++;
  47. Oled_Show_front();
  48. }
  49. else if((cmd_BLE == 'b' || adc_dma[1] > 4000) && angle[1] > 45)//后
  50. {
  51. angle[1]--;
  52. Oled_Show_behind();
  53. }
  54. }
  55. //舵机D,底座 CH1_A15 45-135 左到右 初始化45
  56. void sg90_D()
  57. {
  58. if((cmd_BLE == 'l' || adc_dma[0] <1000) && angle[0] < 135)//左
  59. {
  60. angle[0]++;
  61. Oled_Show_left();
  62. }
  63. else if((cmd_BLE == 'r' || adc_dma[0] > 4000) && angle[0] > 45)//右
  64. {
  65. angle[0]--;
  66. Oled_Show_right();
  67. }
  68. }

五、裸机与FreeRTOS

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

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

1.CubeMX配置

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

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

六、项目演示视频

四轴机械臂演示视频

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/405228
推荐阅读
相关标签
  

闽ICP备14008679号