[DSP编程] F28035两轮平衡车系统设计

[复制链接]
626|10
电笔小新 发表于 2025-10-13 16:32 | 显示全部楼层 |阅读模式
本帖最后由 电笔小新 于 2025-10-13 16:35 编辑

1.设计背景
      设计初心源于去幼儿园参加小孩的家长会,老师让每个家长介绍自己的职业,我介绍自己是一个研发工程师;回家后孩子问我研发工程师是做什么的?我竞不知道怎么跟小孩解释清楚;于是决定给他做个小玩具,小孩全程介入,既能展现一个研发工程师的工作内容,又能将自己这些年学习的零零散散的现成一个闭环,输一个完整作品。具体做什么玩具查找了很多资料,最终决定做一个能够集巡线、避障、遥控的自平衡小车。
2.硬件设计
1)主控模块:TI DSP28035
选用TI公司的DSP28035作为核心控制器,其关键特性包括:
  · 32位浮点运算单元(FPU),主频高达60MHz,满足实时控制计算需求
  · 集成12位ADC(采样速率最高2MSPS),支持多通道同步采样(用于IMU信号采集)
  · 6路增强型PWM(ePWM)模块,可生成高精度电机驱动信号(频率≥20kHz)
  · 丰富的外设接口(SPI/I2C/CAN/UART),兼容主流IMU传感器与无线通信模块
2)感知模块:惯性测量单元(IMU)
采用MPU6050六轴传感器(集成三轴陀螺仪与三轴加速度计),通过I2C接口与DSP28035通信。陀螺仪测量角速度(量程±2000°/s,分辨率0.07°/s/LSB),加速度计测量线性加速度(量程±16g,分辨率0.0039g/LSB)。通过互补滤波或卡尔曼滤波融合两类数据,解算车体相对于竖直方向的实时倾角(θ)与角速度(ω)。
3)避障模块:超声波测距HC-SR04
采用HC-SR04超声波测距模块实现前方障碍物检测,该模块通过发射40kHz超声波脉冲并接收反射回波来测量与障碍物间的距离。模块包含**的发射(Trig)与接收(Echo)引脚,通过GPIO口与DSP28035连接:Trig引脚接收至少10μs的高电平触发信号后发射8个40kHz方波脉冲,Echo引脚在检测到回波时输出高电平脉冲,其持续时间与超声波往返时间成正比。
4巡线模块:激光扫描
采用8路激光传感器实现地面黑线检测,该模块通过发射红色激光束照射地面,并接收反射光信号来感知黑线位置。对于多路激光传感器方案,传感器阵列沿平衡车前行方向居中布置于车体底部(距离地面约10-20mm),形成"一"字形排列,每路传感器间隔10-15mm。
5)执行模块:直流无刷电机及驱动电路
选用12V/10W直流无刷电机(带编码器反馈,分辨率≥13线),通过H桥驱动电路实现转速与方向控制。驱动电路采用TB6612FNG全桥驱动芯片,支持PWM调制方式(频率10kHz),输入信号为DSP28035输出的6路ePWM信号(3对互补PWM控制左右电机)。
6)电源模块
系统供电采用12V三元锂电池组(容量2Ah),通过LM2596稳压芯片生成5V电源(为DSP28035、传感器供电),通过AMS1117-3.3生成3.3V电源(为部分逻辑电路供电)。电源回路包含LC滤波电路(抑制高频噪声)及反接保护二极管(防止电池接反损坏电路)。
7)人机交互模块
包含OLED显示屏(显示实时倾角、电池电压、电机转速等信息)、独立按键(启动/停止/模式切换)。可选配蓝牙模块(JDY-33)实现与手机APP的无线通信(查看状态/发送控制指令)。
综合上述所有硬件功能,将激光巡线功能单独做成一个PCB板,其他所有电路都集成到F28035主控板;PCBA如下:
电路图如下:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| 电笔小新 发表于 2025-10-13 18:16 | 显示全部楼层
4. 模块组装测试


1检查PCB是否正常
(2)PCBA焊接
(3)整机组装
(4)底层驱动编写
(5)驱动结果显示
(6)驱动代码



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| 电笔小新 发表于 2025-10-13 22:36 | 显示全部楼层
5. 调试上位机搭建
在两轮平衡车调试过程中,为确保系统稳定与响应精准,需对姿态角度进行滤波处理以抑制噪声干扰,同时通过上位机实时监测与调整各环PID(比例、积分、微分)控制参数,观察其对应波形变化,从而优化控制性能,实现平衡车的精准控制与动态调参。这就必须要用到可以监控数据的上位机,通过实时显示波形数据,反馈调整控制参数。上位机平台选用C#的WindowsForms,之前看视频学习的时候,老师讲过WindowsForms程序是基于事件触发的,能理解这句话的意思,去做WindowsForms上位机就很简单。这里我主要分享基于CAN通信和Uart通信的两个画波形的上位机。
(1) CanScope上位机设计
CAN盒都会附带二次开发的源文件,直接在源文件上面打开即可进行二次开发。选择上位机开发环境平台C#,打开工程文件。
画波形,选用chart插件,点击工具箱,输入chart,即可找到该插件。
双击进入编程界面,对接收到的can口数据进行处理显示在chart
最终上位机界面如下:
DSP下位机初始化CAN口,输出一个正弦波和余弦波
  1. /******************************************************************************
  2. *    FILENAME : Bsp_Can.c
  3. *
  4. *    PURPOSE  : Can Initialization & Support Functions.
  5. *
  6. *       Author: 电笔小新                      Created on: 2025年9月12日
  7. ******************************************************************************/
  8. #include "User_Include.h"
  9. #include "Bsp_Can.h"

  10. //=============================================================================
  11. // *  Variables & Function_Defines
  12. //=============================================================================
  13. Uint16  CAN_command0 = 0;
  14. Uint16  CAN_command1 = 0;

  15. CANFRAME CAN_TxBuf;
  16. CANFRAME CAN_RxBuf;

  17. int16 SinTabCnt = 0;
  18. int16 CosTabCnt = 100;

  19. const int16 SinCosTab[400]={
  20.     0,   64,  129,  193,  257,  321,  385,  449,  513,  577,
  21.   641,  704,  768,  831,  894,  956, 1019, 1081, 1143, 1204,
  22. 1266, 1327, 1387, 1448, 1508, 1567, 1627, 1686, 1744, 1802,
  23. 1860, 1917, 1973, 2029, 2085, 2140, 2195, 2249, 2302, 2355,
  24. 2408, 2459, 2510, 2561, 2611, 2660, 2709, 2757, 2804, 2850,
  25. 2896, 2941, 2986, 3030, 3072, 3115, 3156, 3197, 3236, 3276,
  26. 3314, 3351, 3388, 3423, 3458, 3492, 3526, 3558, 3589, 3620,
  27. 3650, 3678, 3706, 3733, 3759, 3784, 3808, 3832, 3854, 3875,
  28. 3896, 3915, 3933, 3951, 3967, 3983, 3997, 4011, 4023, 4035,
  29. 4046, 4055, 4064, 4071, 4078, 4083, 4088, 4091, 4094, 4095,

  30. 4096, 4095, 4094, 4091, 4088, 4083, 4078, 4071, 4064, 4055,
  31. 4046, 4035, 4023, 4011, 3997, 3983, 3967, 3951, 3933, 3915,
  32. 3896, 3875, 3854, 3832, 3808, 3784, 3759, 3733, 3706, 3678,
  33. 3650, 3620, 3589, 3558, 3526, 3492, 3458, 3423, 3388, 3351,
  34. 3314, 3276, 3236, 3197, 3156, 3115, 3072, 3030, 2986, 2941,
  35. 2896, 2850, 2804, 2757, 2709, 2660, 2611, 2561, 2510, 2459,
  36. 2408, 2355, 2302, 2249, 2195, 2140, 2085, 2029, 1973, 1917,
  37. 1860, 1802, 1744, 1686, 1627, 1567, 1508, 1448, 1387, 1327,
  38. 1266, 1204, 1143, 1081, 1019,  956,  894,  831,  768,  704,
  39.   641,  577,  513,  449,  385,  321,  257,  193,  129,   64,

  40.     0,  -64, -129, -193, -257, -321, -385, -449, -513, -577,
  41. -641, -704, -768, -831, -894, -956,-1019,-1081,-1143,-1204,
  42. -1266,-1327,-1387,-1448,-1508,-1567,-1627,-1686,-1744,-1802,
  43. -1860,-1917,-1973,-2029,-2085,-2140,-2195,-2249,-2302,-2355,
  44. -2408,-2459,-2510,-2561,-2611,-2660,-2709,-2757,-2804,-2850,
  45. -2896,-2941,-2986,-3030,-3072,-3115,-3156,-3197,-3236,-3276,
  46. -3314,-3351,-3388,-3423,-3458,-3492,-3526,-3558,-3589,-3620,
  47. -3650,-3678,-3706,-3733,-3759,-3784,-3808,-3832,-3854,-3875,
  48. -3896,-3915,-3933,-3951,-3967,-3983,-3997,-4011,-4023,-4035,
  49. -4046,-4055,-4064,-4071,-4078,-4083,-4088,-4091,-4094,-4095,

  50. -4096,-4095,-4094,-4091,-4088,-4083,-4078,-4071,-4064,-4055,
  51. -4046,-4035,-4023,-4011,-3997,-3983,-3967,-3951,-3933,-3915,
  52. -3896,-3875,-3854,-3832,-3808,-3784,-3759,-3733,-3706,-3678,
  53. -3650,-3620,-3589,-3558,-3526,-3492,-3458,-3423,-3388,-3351,
  54. -3314,-3276,-3236,-3197,-3156,-3115,-3072,-3030,-2986,-2941,
  55. -2896,-2850,-2804,-2757,-2709,-2660,-2611,-2561,-2510,-2459,
  56. -2408,-2355,-2302,-2249,-2195,-2140,-2085,-2029,-1973,-1917,
  57. -1860,-1802,-1744,-1686,-1627,-1567,-1508,-1448,-1387,-1327,
  58. -1266,-1204,-1143,-1081,-1019, -956, -894, -831, -768, -704,
  59. -641, -577, -513, -449, -385, -321, -257, -193, -129,  -64,

  60. };

  61. //=============================================================================
  62. // *  FUNCTION:  SetGpio_Cana()
  63. // *
  64. // *  PURPOSE :  Initializes the GPIOs for CANa.
  65. //=============================================================================
  66. //CAN的GPIO初始化
  67. //这里采用GPIO30 GPIO31
  68. //--------------------------------------------------------------------
  69. void SetGpio_Cana(void)
  70. {
  71.    EALLOW;
  72.     GpioCtrlRegs.GPAPUD.bit.GPIO30 = 0;     // Enable pull-up for GPIO30 (CANRXA)
  73.     GpioCtrlRegs.GPAPUD.bit.GPIO31 = 0;     // Enable pull-up for GPIO31 (CANTXA)

  74.     GpioCtrlRegs.GPAQSEL2.bit.GPIO30 = 3;   // Asynch qual for GPIO30 (CANRXA)

  75.     //IO30 IO21配置为CAN外设引脚
  76.     GpioCtrlRegs.GPAMUX2.bit.GPIO30 = 1;    // Configure GPIO30 for CANRXA operation
  77.     GpioCtrlRegs.GPAMUX2.bit.GPIO31 = 1;    // Configure GPIO31 for CANTXA operation

  78.     EDIS;
  79. }

  80. //=============================================================================
  81. // *  FUNCTION:  SetERegs_Cana()
  82. // *
  83. // *  PURPOSE :  Initializes the Registers for CANa.
  84. //=============================================================================
  85. void SetERegs_Cana(void)        // Initialize eCAN-A module
  86. {

  87. /* Create a shadow register structure for the CAN control registers. This is
  88. needed, since only 32-bit access is allowed to these registers. 16-bit access
  89. to these registers could potentially corrupt the register contents or return
  90. false data. */

  91. struct ECAN_REGS ECanaShadow;

  92.     EALLOW;     // EALLOW enables access to protected bits

  93. /* Configure eCAN RX and TX pins for CAN operation using eCAN regs*/

  94.     ECanaShadow.CANTIOC.all = ECanaRegs.CANTIOC.all;
  95.     ECanaShadow.CANTIOC.bit.TXFUNC = 1;
  96.     ECanaRegs.CANTIOC.all = ECanaShadow.CANTIOC.all;

  97.     ECanaShadow.CANRIOC.all = ECanaRegs.CANRIOC.all;
  98.     ECanaShadow.CANRIOC.bit.RXFUNC = 1;
  99.     ECanaRegs.CANRIOC.all = ECanaShadow.CANRIOC.all;

  100. /* Configure eCAN for HECC mode - (reqd to access mailboxes 16 thru 31) */
  101.                                     // HECC mode also enables time-stamping feature

  102.     ECanaShadow.CANMC.all = ECanaRegs.CANMC.all;
  103.     ECanaShadow.CANMC.bit.SCB = 1;               //P35  Select eCAN mode.
  104.     ECanaRegs.CANMC.all = ECanaShadow.CANMC.all;

  105. /* Initialize all bits of 'Message Control Register' to zero */
  106. // Some bits of MSGCTRL register come up in an unknown state. For proper operation,
  107. // all bits (including reserved bits) of MSGCTRL must be initialized to zero

  108.     ECanaMboxes.MBOX0.MSGCTRL.all = 0x00000000;
  109.     ECanaMboxes.MBOX1.MSGCTRL.all = 0x00000000;
  110.     ECanaMboxes.MBOX2.MSGCTRL.all = 0x00000000;
  111.     ECanaMboxes.MBOX3.MSGCTRL.all = 0x00000000;
  112.     ECanaMboxes.MBOX4.MSGCTRL.all = 0x00000000;
  113.     ECanaMboxes.MBOX5.MSGCTRL.all = 0x00000000;
  114.     ECanaMboxes.MBOX6.MSGCTRL.all = 0x00000000;
  115.     ECanaMboxes.MBOX7.MSGCTRL.all = 0x00000000;
  116.     ECanaMboxes.MBOX8.MSGCTRL.all = 0x00000000;
  117.     ECanaMboxes.MBOX9.MSGCTRL.all = 0x00000000;
  118.     ECanaMboxes.MBOX10.MSGCTRL.all = 0x00000000;
  119.     ECanaMboxes.MBOX11.MSGCTRL.all = 0x00000000;
  120.     ECanaMboxes.MBOX12.MSGCTRL.all = 0x00000000;
  121.     ECanaMboxes.MBOX13.MSGCTRL.all = 0x00000000;
  122.     ECanaMboxes.MBOX14.MSGCTRL.all = 0x00000000;
  123.     ECanaMboxes.MBOX15.MSGCTRL.all = 0x00000000;
  124.     ECanaMboxes.MBOX16.MSGCTRL.all = 0x00000000;
  125.     ECanaMboxes.MBOX17.MSGCTRL.all = 0x00000000;
  126.     ECanaMboxes.MBOX18.MSGCTRL.all = 0x00000000;
  127.     ECanaMboxes.MBOX19.MSGCTRL.all = 0x00000000;
  128.     ECanaMboxes.MBOX20.MSGCTRL.all = 0x00000000;
  129.     ECanaMboxes.MBOX21.MSGCTRL.all = 0x00000000;
  130.     ECanaMboxes.MBOX22.MSGCTRL.all = 0x00000000;
  131.     ECanaMboxes.MBOX23.MSGCTRL.all = 0x00000000;
  132.     ECanaMboxes.MBOX24.MSGCTRL.all = 0x00000000;
  133.     ECanaMboxes.MBOX25.MSGCTRL.all = 0x00000000;
  134.     ECanaMboxes.MBOX26.MSGCTRL.all = 0x00000000;
  135.     ECanaMboxes.MBOX27.MSGCTRL.all = 0x00000000;
  136.     ECanaMboxes.MBOX28.MSGCTRL.all = 0x00000000;
  137.     ECanaMboxes.MBOX29.MSGCTRL.all = 0x00000000;
  138.     ECanaMboxes.MBOX30.MSGCTRL.all = 0x00000000;
  139.     ECanaMboxes.MBOX31.MSGCTRL.all = 0x00000000;

  140. // TAn, RMPn, GIFn bits are all zero upon reset and are cleared again
  141. //  as a matter of precaution.
  142.     ECanaRegs.CANTA.all = 0xFFFFFFFF;   /* Clear all TAn bits */
  143.     ECanaRegs.CANRMP.all = 0xFFFFFFFF;  /* Clear all RMPn bits */
  144.     ECanaRegs.CANGIF0.all = 0xFFFFFFFF; /* Clear all interrupt flag bits */
  145.     ECanaRegs.CANGIF1.all = 0xFFFFFFFF;

  146. /* Configure bit timing parameters for eCANA*/
  147.     ECanaShadow.CANMC.all = ECanaRegs.CANMC.all;
  148.     ECanaShadow.CANMC.bit.CCR = 1 ;            // Set CCR = 1
  149.     ECanaRegs.CANMC.all = ECanaShadow.CANMC.all;

  150.     // Wait until the CPU has been granted permission to change the configuration registers
  151.     do
  152.     {
  153.       ECanaShadow.CANES.all = ECanaRegs.CANES.all;
  154.     } while(ECanaShadow.CANES.bit.CCE != 1 );       // Wait for CCE bit to be set..

  155.     ECanaShadow.CANBTC.all = 0;

  156.     //-------------------------自定义的配置------------------------------------------
  157.     //P47
  158.     /* The following block is only for 60 MHz SYSCLKOUT. (30 MHz CAN module clock Bit rate = 1 Mbps
  159.        See Note at end of file. */
  160.     //rate Bit =(SYSCLKOUT/2)/(BRP * Bit-time )
  161.     //BRP = BRPreg+1
  162.     //Bit-time = (TSEG1reg + 1) + (TSEG2reg+ 1) + 1
  163.     //配置TQ 和BRP ,
  164.     //方案一 TQ为10, 则TSEG1REG = 6, BRP = 5 ,为500K
  165.     //方案二,TQ为12,则TSEG1REG = 8, BRP = 4 ,为500K
  166.     //500KHz
  167.     ECanaShadow.CANBTC.bit.BRPREG = 5;
  168.     ECanaShadow.CANBTC.bit.TSEG2REG = 1;
  169.     ECanaShadow.CANBTC.bit.TSEG1REG = 6;
  170.     ECanaShadow.CANBTC.bit.SAM = 1;
  171.     ECanaRegs.CANBTC.all = ECanaShadow.CANBTC.all;

  172.     ECanaShadow.CANMC.all = ECanaRegs.CANMC.all;
  173.     ECanaShadow.CANMC.bit.CCR = 0;    // Set CCR = 0
  174.     ECanaShadow.CANMC.bit.STM = 0;    // 0: normal mode/1: self-test mode
  175.     ECanaRegs.CANMC.all = ECanaShadow.CANMC.all;



  176.     // Wait until the CPU no longer has permission to change the configuration registers
  177.     do
  178.     {
  179.       ECanaShadow.CANES.all = ECanaRegs.CANES.all;
  180.     } while(ECanaShadow.CANES.bit.CCE != 0 );       // Wait for CCE bit to be  cleared..

  181. /* Disable all Mailboxes  */
  182.     ECanaRegs.CANME.all = 0;        // Required before writing the MSGIDs

  183.     EDIS;
  184. }

  185. /**********************************************************************
  186. * FUNCION :   SetMailbox_Cana()
  187. * PURPOSE :   Initializes the Mailboxes for Can.
  188. //-----------------------------------------------
  189. //配置发送、接收邮箱等操作
  190. //数据区8个长度字节
  191. //发送邮箱   1到6
  192. //接收邮箱   16, ID为0x0A
  193. //-----------------------------------------------
  194. **********************************************************************/
  195. void SetMailbox_Cana(void)
  196. {
  197.    struct ECAN_REGS ECanaShadow;

  198.    EALLOW;
  199.     ECanaShadow.CANGAM.all = ECanaRegs.CANGAM.all;
  200.     ECanaShadow.CANGAM.bit.AMI=1;            // Standard and extended frames can be received.
  201.     ECanaRegs.CANGAM.all = ECanaShadow.CANGAM.all;
  202.    EDIS;

  203. //P59
  204. //In standard identifier mode,if the IDE bit(MSGID.31) =0,the message identifier is storedin bits ID.28:18.
  205. //IDE =0:The RECEIVED message had a standard identifier
  206. //Auto answer mode bit. AAM =0 ;  Normal transmit mode
  207. //AME = 1 ;The corresponding acceptance mask is used

  208. //MSGID.31=IDE; MSGID.30=AME; MSGID.29=AAM
  209. // Mailboxs can be written to 16-bits or 32-bits at a time

  210.    // Mailboxes can be written to 16-bits or 32-bits at a time
  211.       // Write to the MSGID field of TRANSMIT mailboxes MBOX0 - 15
  212.       ECanaMboxes.MBOX0.MSGID.all = ( (TXCanId0_Std|0x10000000)<<18); // stand Identifier
  213.       ECanaMboxes.MBOX1.MSGID.all = ( (TXCanId1_Std|0x10000000)<<18); // stand Identifier

  214.       // Write to the MSGID field of RECEIVE mailboxes MBOX16 - 31
  215.       ECanaMboxes.MBOX16.MSGID.all = ((RXCanId0_Std|0x10000000)<<18); // stand Identifier
  216.       ECanaMboxes.MBOX17.MSGID.all = ((RXCanId1_Std|0x10000000)<<18); // stand Identifier

  217.       // Configure Mailboxes 0-15 as Tx, 16-31 as Rx
  218.       // Since this write is to the entire register (instead of a bit
  219.       // field) a shadow register is not required.
  220.       ECanaRegs.CANMD.all = 0xFFFF0000;

  221.     // Specify that 8 bits will be sent/received //8字节数据
  222.       ECanaMboxes.MBOX0.MSGCTRL.bit.DLC  = 8;
  223.       ECanaMboxes.MBOX1.MSGCTRL.bit.DLC  = 8;

  224. ////////////////////////////////////////////////////////////////////////////////////
  225.     EALLOW;
  226.     ECanaShadow.CANMC.all = ECanaRegs.CANMC.all;
  227.     ECanaShadow.CANMC.bit.DBO = 1;
  228.     ECanaShadow.CANMC.bit.SCB = 1;//Select eCAN mode
  229.     ECanaRegs.CANMC.all = ECanaShadow.CANMC.all;

  230.     // Configure Mailboxes 0-15 as Tx, 16-31 as Rx
  231.     ECanaRegs.CANMD.all = 0xFFFF0000;

  232.     // Since this write is to the entire register (instead of a bit
  233.     // field) a shadow register is not required.
  234.     ECanaRegs.CANME.all = 0x00030003;
  235.     EDIS;
  236. }




  237. //=============================================================================
  238. // *  FUNCTION:  SetInterrupt_Cana()
  239. // *
  240. // *  PURPOSE :  Initializes the ISR for CANa.
  241. //=============================================================================
  242. void SetInterrupt_Cana(void)
  243. {
  244. /* Create a shadow register structure for the CAN control registers. This is
  245. needed, since only 32-bit access is allowed to these registers. 16-bit access
  246. to these registers could potentially corrupt the register contents or return
  247. false data. */
  248.     struct ECAN_REGS ECanaShadow;


  249.     EALLOW;     // EALLOW enables access to protected bits
  250.     ECanaShadow.CANMIL.all = ECanaRegs.CANMIL.all;
  251.     ECanaShadow.CANMIL.all = 0xFFFFFFFF ;            //P76, mailbox interrupts to level 1
  252.     ECanaRegs.CANMIL.all = ECanaShadow.CANMIL.all;


  253.     ECanaShadow.CANMIM.all = ECanaRegs.CANMIM.all;
  254.     ECanaShadow.CANMIM.all =0x00010000 ;            //P48,相应的邮箱中断使能位 Mailbox interrupt is enabled.
  255.     ECanaRegs.CANMIM.all = ECanaShadow.CANMIM.all;

  256.     //1-32号邮箱中断在中断线0上产生
  257.     ECanaShadow.CANMIL.all = ECanaRegs.CANMIL.all;
  258.     ECanaShadow.CANMIL.all = 0;
  259.     ECanaRegs.CANMIL.all = ECanaShadow.CANMIL.all;


  260.     //------------中断配置步骤-----1
  261.     //中断线0使能
  262.     ECanaShadow.CANGIM.all = ECanaRegs.CANGIM.all;
  263.     ECanaShadow.CANGIM.bit.I0EN = 1;
  264. //    ECanaShadow.CANGIM.bit.I1EN = 1;
  265.     ECanaRegs.CANGIM.all = ECanaShadow.CANGIM.all;


  266. // TAn, RMPn, GIFn bits are all zero upon reset and are cleared again
  267. //  as a matter of precaution.

  268.     ECanaRegs.CANTA.all   = 0xFFFFFFFF;   /* Clear all TAn bits */
  269.     ECanaRegs.CANRMP.all  = 0xFFFFFFFF;  /* Clear all RMPn bits */
  270.     ECanaRegs.CANGIF0.all = 0xFFFFFFFF; /* Clear all interrupt flag bits */
  271.     ECanaRegs.CANGIF1.all = 0xFFFFFFFF;

  272. //------------中断配置步骤-----3
  273. // Enable CAN in PIE
  274.    PieCtrlRegs.PIEIER9.bit.INTx5 = 1;      // Enable INT 9.5 in the PIE
  275. //   PieCtrlRegs.PIEIER9.bit.INTx6 = 1;      // Enable INT 9.6 in the PIE

  276. //------------中断配置步骤-----4
  277.    IER |= M_INT9;                          // Enable CPU Interrupt 9

  278.    EINT;
  279.    ERTM;
  280.    EDIS;

  281. }

  282. //=============================================================================
  283. // *  FUNCTION:  InitCana()
  284. // *
  285. // *  PURPOSE :   Initializes the Enhanced Can modules.
  286. //=============================================================================
  287. void InitCana(void)
  288. {

  289.     SetGpio_Cana();
  290.     SetERegs_Cana();
  291.     SetMailbox_Cana();
  292.     SetInterrupt_Cana();
  293. }


  294. //=============================================================================
  295. // *  FUNCTION:  sCanHdRead()
  296. // *
  297. // *  PURPOSE :   Read Can Data From Registers And Save Into Buffer.
  298. //=============================================================================
  299. void sCanHdRead(const Uint8 ubMailBox,CANFRAME *pdata)
  300. {
  301.     volatile struct MBOX *pMailbox;

  302.     pMailbox = &ECanaMboxes.MBOX0 + ubMailBox;

  303.     pdata->CanId.all = pMailbox->MSGID.all;
  304.     pdata->CanData0 = pMailbox->MDL.word.LOW_WORD;
  305.     pdata->CanData1 = pMailbox->MDL.word.HI_WORD;
  306.     pdata->CanData2 = pMailbox->MDH.word.LOW_WORD;
  307.     pdata->CanData3 = pMailbox->MDH.word.HI_WORD;
  308. }

  309. //=============================================================================
  310. // *  FUNCTION:  sCanHdSend()
  311. // *
  312. // *  PURPOSE :  Write Can Data Into Registers And Send.
  313. //=============================================================================
  314. void sCanHdSend(const Uint8 ubMailBox,const CANFRAME *pdata)
  315. {
  316.     struct ECAN_REGS ECanShadow;
  317.     volatile struct MBOX *pMailbox;

  318.     pMailbox = &ECanaMboxes.MBOX0 + ubMailBox;

  319.     /*step3: Load the message to the MSGID Registers of the object*/
  320.     pMailbox->MSGID.all= pdata->CanId.all;

  321.     /*step4: configure the data length*/
  322.     pMailbox->MSGCTRL.bit.DLC = 8;


  323.     /*step5: Write the message data to the mail box data field*/
  324.     pMailbox->MDL.word.LOW_WORD = pdata->CanData0;
  325.     pMailbox->MDL.word.HI_WORD = pdata->CanData1;
  326.     pMailbox->MDH.word.LOW_WORD = pdata->CanData2;
  327.     pMailbox->MDH.word.HI_WORD = pdata->CanData3;

  328.     /* Enable transmit mailbox*/
  329.     ECanShadow.CANME.all = ECanaRegs.CANME.all;
  330.     ECanShadow.CANME.all |= ((Uint32)1 << ubMailBox);
  331.     ECanaRegs.CANME.all = ECanShadow.CANME.all;

  332.     ECanShadow.CANTRS.all=0;
  333.     ECanShadow.CANTRS.all|= ((Uint32)1 << ubMailBox);
  334.     ECanaRegs.CANTRS.all= ECanShadow.CANTRS.all;

  335.     ECanShadow.CANTA.all = 0;
  336.     ECanShadow.CANTA.all |= ((Uint32)1 << ubMailBox);  // Clear all TAn
  337.     ECanaRegs.CANTA.all=ECanShadow.CANTA.all;

  338. }

  339. //---------------------------------------------------------------------------
  340. // Can_Task:
  341. //---------------------------------------------------------------------------
  342. void Can_Task(void)
  343. {
  344.    SinTabCnt++;
  345.    if(SinTabCnt>399)
  346.     {
  347.        SinTabCnt=0;
  348.     }
  349.    CosTabCnt++;
  350.    if(CosTabCnt>399)
  351.     {
  352.        CosTabCnt=0;
  353.     }
  354.    CAN_TxBuf.CanId.all = ((TXCanId0_Std|0x10000000)<<18); // stand Identifier
  355.    CAN_TxBuf.CanData0 = SinCosTab[SinTabCnt];
  356.    CAN_TxBuf.CanData1 = SinCosTab[CosTabCnt];
  357.    CAN_TxBuf.CanData2 = 0x5432;
  358.    CAN_TxBuf.CanData3 = 0x9876;
  359.    sCanHdSend(0,&CAN_TxBuf);


  360. }


  361. //---------------------------------------------------------------------------
  362. // INT_ISR for Ecana_RX
  363. //---------------------------------------------------------------------------
  364. //// INT9.5
  365. interrupt void ECAN0INTA_ISR(void)  // eCAN-A
  366. {

  367.     struct ECAN_REGS ECanaShadow;
  368.     // Insert ISR Code here
  369.     ECanaShadow.CANRMP.all=ECanaRegs.CANRMP.all;
  370.     if(ECanaShadow.CANRMP.bit.RMP16==1)
  371.     {
  372.         ECanaShadow.CANRMP.bit.RMP16=1;
  373.         ECanaRegs.CANRMP.all=ECanaShadow.CANRMP.all;
  374.         sCanHdRead(16,&CAN_RxBuf);


  375.     }

  376.     PieCtrlRegs.PIEACK.all = PIEACK_GROUP9;     // Must acknowledge the PIE group
  377. }

  378. //=============================================================================
  379. // End of file.
  380. //=============================================================================
下位机跟上位机波形传输联调如下:

上位机安装包和CAN驱动代码:


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
中国英茂科工 发表于 2025-10-14 05:52 | 显示全部楼层
牛啊牛,我在做Arduino的
dirtwillfly 发表于 2025-10-14 08:17 | 显示全部楼层
CAN调试我习惯用TSMaster
asdsfgwsafd 发表于 2025-10-14 09:11 | 显示全部楼层
教应届生都不敢拿这个上手,老哥你直接给幼儿园的灌
 楼主| 电笔小新 发表于 2025-10-14 12:09 | 显示全部楼层
电笔小新 发表于 2025-10-13 22:36
5. 调试上位机搭建在两轮平衡车调试过程中,为确保系统稳定与响应精准,需对姿态角度进行滤波处理以抑制噪 ...

(2) UartScope上位机设计
串口上位机使用的是虚拟示波器,网上直接找到的,个人感觉还是非常的好用。
DSP下位机函数接口代码
  1. /******************************************************************************
  2. *    FILENAME : User_IPMOS.c
  3. *
  4. *    PURPOSE  : Interface of software oscilloscope
  5. *
  6. *       Author: 电笔小新                      Created on: 2025年9月12日
  7. ******************************************************************************/
  8. #include "User_Include.h"
  9. #include "User_IPMOS.h"

  10. //=============================================================================
  11. // *  Variables & Function_Defines
  12. //=============================================================================

  13. Uint8   wScopeSelect=10;
  14. int16   uwScope[30];

  15. typedef int (*pFunc1)(void);

  16. extern Uint16 LinaWrite( Uint8 *pBuf, Uint16 u16Length);
  17. extern Uint16 SciWrite(Uint16 SciId, Uint8 *pBuf, Uint16 u16Length);
  18. /******************************variable definition******************************/

  19. Uint16  u16IPOMS_GraphDataBuff[4][400];
  20. Uint16  u16IPOMS_TransmitDataBuff[500];

  21. Uint16  u16IPOMS_CommandLength  = 0;
  22. Uint16  u16IPOMS_Interval       = 0;
  23. Uint16  u16IPOMS_Interval1      = 0;
  24. Uint16  u16IPOMS_SnatchDataCnt  = 0;
  25. Uint16  u16IPOMS_SaveDataCnt    = 0;
  26. Uint16  u16IPOMS_TransmitCnt    = 0;
  27. Uint16  u16IPOMS_CompareVal     = 0;
  28. Uint8   *pIPOMS_CommandIn0;

  29. Uint8   u8IPOMS_UserDataBuf0[80];
  30. Uint8   u8IPOMS_CommandBuffer0[50];
  31. Uint8   u8IPOMS_RxBuffer0[50];

  32. Uint8   u8IPOMS_DataKind[4]     = {0,0,0,0};
  33. Uint8   u8IPOMS_SnatchGraphEnable= 1;
  34. Uint8   u8IPOMS_SendHighHalfByte = 1;
  35. Uint8   u8IPOMS_wTriggerSource  = 0;
  36. Uint8   u8IPOMS_wTrigger        = 0;
  37. Uint8   u8IPOMS_HighByte        = 0;
  38. Uint8   u8IPOMS_LowByte         = 0;
  39. Uint8   u8IPOMS_Sign            = 0;

  40. Uint16 wTriggerWaiting = 0;


  41. /******************************function list******************************/
  42. Uint8 IPOMS_SnatchGraph(void);
  43. void  IPOMS_SCI(void);
  44. Uint8  IPOMS_Write(Uint16 SciId, Uint8 *pBuf, Uint16 u16Length);

  45. void FlashProgramming(void);
  46. void  IPOMS_Parsing(void);


  47. void  IPOMS_Q1Command(void);
  48. void  IPOMS_Q2Command(void);
  49. void  IPOMS_Q3Command(void);
  50. void  IPOMS_QDCommand(void);

  51. int16 IPOMS_GraphicView_0(void);
  52. int16 IPOMS_GraphicView_1(void);
  53. int16 IPOMS_GraphicView_2(void);
  54. int16 IPOMS_GraphicView_3(void);
  55. int16 IPOMS_GraphicView_4(void);
  56. int16 IPOMS_GraphicView_5(void);
  57. int16 IPOMS_GraphicView_6(void);
  58. int16 IPOMS_GraphicView_7(void);
  59. int16 IPOMS_GraphicView_8(void);
  60. int16 IPOMS_GraphicView_9(void);
  61. int16 IPOMS_GraphicView_10(void);

  62. Uint8 sbNumToAscii(Uint16 u16Number, int8 i8Exponent, Uint8 *pbBuffer);
  63. void IPOMS_WriteBinary(Uint16 *pstart, Uint16 u16Length);


  64. /******************************function list******************************/
  65. pFunc1 GetDataSubArray[] =
  66. {

  67. IPOMS_GraphicView_0,  // Note: Don't set "Channel x ID" to 0 in "Graphic View"
  68.                        // in IPOMS. 0 is invalid.

  69. IPOMS_GraphicView_1,  // 1- is the number set to "Channelx ID"
  70. IPOMS_GraphicView_2,
  71. IPOMS_GraphicView_3,
  72. IPOMS_GraphicView_4,
  73. IPOMS_GraphicView_5,
  74. IPOMS_GraphicView_6,
  75. IPOMS_GraphicView_7,
  76. IPOMS_GraphicView_8,
  77. IPOMS_GraphicView_9,
  78. IPOMS_GraphicView_10,
  79. };


  80. //=============================================================================
  81. // *  FUNCTION:  IPOMS_Write()
  82. // *
  83. // *  PURPOSE :  Use IPOMS to Write data.
  84. //=============================================================================
  85. Uint8 IPOMS_Write(Uint16 SciId, Uint8 *pBuf, Uint16 u16Length)
  86. {
  87.     Uint8 u8IPOMS_temp;
  88.     switch(SciId)
  89.         {
  90.             case ID_SCIA:
  91.                  u8IPOMS_temp=SciWrite(ID_SCIA, pBuf,u16Length);
  92.             break;

  93.             case ID_LINA:
  94.                  u8IPOMS_temp=LinaWrite(pBuf,u16Length);
  95.             break;

  96.             default:
  97.                 u8IPOMS_temp=1;
  98.                break;
  99.         }

  100.         return u8IPOMS_temp;
  101. }


  102. //=============================================================================
  103. // *  FUNCTION:  SCI_IPOMS()
  104. // *
  105. // *  PURPOSE :  Software OSC for debug.
  106. //=============================================================================
  107. void IPOMS_SCI(void)
  108. {
  109.     Uint8 u8IPOMS_temp;
  110.     while(1)
  111.     {
  112.         u8IPOMS_temp = SciRead(ID_IPOMS, pIPOMS_CommandIn0);

  113.         if(SCI_RX_EMPTY == u8IPOMS_temp)
  114.         {
  115.             break;
  116.         }
  117.         if(u16IPOMS_CommandLength >= con_MAX_COMMAND_LENGTH)
  118.         {
  119.             pIPOMS_CommandIn0 = u8IPOMS_CommandBuffer0;
  120.             u16IPOMS_CommandLength = 0;
  121.         }
  122.         else if(0xDD == (*pIPOMS_CommandIn0))
  123.         {
  124.             FlashProgramming();
  125.             pIPOMS_CommandIn0 = u8IPOMS_CommandBuffer0;
  126.             u16IPOMS_CommandLength = 0;
  127.         }
  128.         else if((con_CHAR_ENTER == (*pIPOMS_CommandIn0)) || (0x0A == (*pIPOMS_CommandIn0)))  //Deal with command with 0x0a
  129.         {
  130.             IPOMS_Parsing();
  131.             pIPOMS_CommandIn0 = u8IPOMS_CommandBuffer0;
  132.             u16IPOMS_CommandLength = 0;
  133.         }
  134.         else
  135.         {
  136.             u16IPOMS_CommandLength++;
  137.             pIPOMS_CommandIn0++;
  138.         }
  139.     }
  140. }

  141. //=============================================================================
  142. // *  FUNCTION:  FlashProgramming()
  143. // *
  144. // *  PURPOSE :  Online update User Code.
  145. //=============================================================================
  146. void FlashProgramming(void)
  147. {
  148.     if((u8IPOMS_CommandBuffer0[1] == 0x1A)&(u8IPOMS_CommandBuffer0[2] == 0x2A))
  149.     {
  150. /*------------------------------------------------------------------
  151.    Disable CPU interrupts and clear all CPU interrupt flags.
  152. ------------------------------------------------------------------*/
  153.         DINT;
  154.         DRTM;
  155.         IER = 0x0000;
  156.         IFR = 0x0000;

  157. /*------------------------------------------------------------------
  158. Jump to BootLoader.
  159. ------------------------------------------------------------------*/
  160.         asm("  LB  0x3F7FF6");
  161.     }

  162. }

  163. /*=============================================================================*
  164. * FUNCTION: IPOMS_Parsing(void)
  165. * PURPOSE : IPOMS Command Parse
  166. * INPUT:
  167. *     void
  168. *
  169. * RETURN:
  170. *     void
  171. *
  172. * CALLS:
  173. *     IPOMS_Q1Command();
  174. *     IPOMS_Q3Command();
  175. *     IPOMS_QDCommand();
  176. *
  177. * CALLED BY:
  178. *     TSK_F_IPOMS()
  179. *
  180. *============================================================================*/
  181. void IPOMS_Parsing(void)
  182. {

  183.     switch(u8IPOMS_CommandBuffer0[0])
  184.     {
  185.     case 'Q':
  186.         {

  187.             if('1' == u8IPOMS_CommandBuffer0[1])
  188.             {
  189.                 IPOMS_Q1Command();
  190.             }
  191.              if('2' == u8IPOMS_CommandBuffer0[1])
  192.             {
  193.                 IPOMS_Q2Command();
  194.             }
  195.             if('3' == u8IPOMS_CommandBuffer0[1])
  196.             {
  197.                 IPOMS_Q3Command();
  198.             }
  199.             if('D' == u8IPOMS_CommandBuffer0[1])
  200.             {
  201.                 IPOMS_QDCommand();
  202.             }
  203.         }
  204.     break;
  205.     default:
  206.     break;
  207.     }
  208. }
  209. /*=============================================================================*
  210. * FUNCTION: IPOMS_Q1Command(void)
  211. * PURPOSE : Send Data to IPOMS For Q1 Command
  212. * INPUT:
  213. *     void  // TRUE:disable the dog锟斤拷FALSE:enable the dog
  214. *
  215. * RETURN:
  216. *     void
  217. *
  218. * CALLS:
  219. *     //sbNumToAscii();
  220. *     //IPOMS_Write();
  221. *
  222. *
  223. *
  224. * CALLED BY:
  225. *   //  INT32 IPOMS_Parsing()
  226. *
  227. *============================================================================*/
  228.     void IPOMS_Q1Command(void)
  229.     {
  230.         Uint8   bStrLen;
  231.         Uint8   bStrLen1;
  232.         Uint8   *pDataBuf;

  233.         bStrLen1 = 0;
  234.         bStrLen = 0;

  235.         bStrLen = sbNumToAscii(0, 0, u8IPOMS_UserDataBuf0);//0
  236.         u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  237.         pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  238.         bStrLen1 = sbNumToAscii(uwScope[1], 0, pDataBuf); //1//
  239.         bStrLen += bStrLen1;
  240.         u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  241.         pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  242.         bStrLen1 = sbNumToAscii(uwScope[2], 0, pDataBuf); //2//
  243.         bStrLen += bStrLen1;
  244.         u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  245.         pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  246.         bStrLen1 = sbNumToAscii(uwScope[3], 0, pDataBuf);  //3//
  247.         bStrLen += bStrLen1;
  248.         u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  249.         pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  250.         bStrLen1 = sbNumToAscii(uwScope[4], 0, pDataBuf);  //4//
  251.         bStrLen += bStrLen1;
  252.         u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  253.         pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  254.         bStrLen1 = sbNumToAscii(uwScope[5], 0, pDataBuf);  //5//
  255.         bStrLen += bStrLen1;
  256.         u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  257.         pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  258.         bStrLen1 = sbNumToAscii(uwScope[6], 0, pDataBuf);  //6//
  259.         bStrLen += bStrLen1;
  260.         u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  261.         pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  262.         bStrLen1 = sbNumToAscii(uwScope[7], 0, pDataBuf);  //7//
  263.         bStrLen += bStrLen1;
  264.         u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  265.         pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  266.         bStrLen1 = sbNumToAscii(uwScope[8], 0, pDataBuf);  //8//
  267.         bStrLen += bStrLen1;
  268.         u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  269.         pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  270.         bStrLen1 = sbNumToAscii(uwScope[9], 0, pDataBuf);  //9//
  271.         bStrLen += bStrLen1;
  272.         u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  273.         pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  274.         bStrLen1 = sbNumToAscii(10, 0, pDataBuf);    //10//
  275.         bStrLen += bStrLen1;
  276.         u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  277.         pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  278.         bStrLen1 = sbNumToAscii(11, 0, pDataBuf);    //11//
  279.         bStrLen += bStrLen1;
  280.         u8IPOMS_UserDataBuf0[bStrLen++] = 32;


  281.         pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  282.         bStrLen1 = sbNumToAscii(12, 0, pDataBuf);    //12//
  283.         bStrLen += bStrLen1;
  284.         u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  285.         pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  286.         bStrLen1 = sbNumToAscii(13, 0, pDataBuf);    //13//
  287.         bStrLen += bStrLen1;
  288.         u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  289.         pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  290.         bStrLen1 = sbNumToAscii(14, 0, pDataBuf);    //14//
  291.         bStrLen += bStrLen1;
  292.         u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  293.         pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  294.         bStrLen1 = sbNumToAscii(15, 0, pDataBuf);    //15//
  295.         bStrLen += bStrLen1;
  296.         u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  297.         pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  298.         bStrLen1 = sbNumToAscii(16, 0, pDataBuf);  //16//
  299.         bStrLen += bStrLen1;
  300.         u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  301.         pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  302.         bStrLen1 = sbNumToAscii(17, 0, pDataBuf);  //17//
  303.         bStrLen += bStrLen1;
  304.         u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  305.         pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  306.         bStrLen1 = sbNumToAscii(18, 0, pDataBuf);  //18//
  307.         bStrLen += bStrLen1;
  308.         u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  309.         pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  310.         bStrLen1 = sbNumToAscii(19, 0, pDataBuf);  //19//
  311.         bStrLen += bStrLen1;
  312.         u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  313.         IPOMS_Write(ID_IPOMS, u8IPOMS_UserDataBuf0, bStrLen);
  314.     }

  315. void IPOMS_Q2Command(void)
  316. {
  317.     Uint8   bStrLen;
  318.     Uint8   bStrLen1;
  319.     Uint8   *pDataBuf;

  320.     bStrLen1 = 0;
  321.     bStrLen = 0;

  322.     bStrLen = sbNumToAscii(0, 0, u8IPOMS_UserDataBuf0);//0
  323.     u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  324.     pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  325.     bStrLen1 = sbNumToAscii(uwScope[1], 0, pDataBuf); //1//
  326.     bStrLen += bStrLen1;
  327.     u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  328.     pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  329.     bStrLen1 = sbNumToAscii(uwScope[2] , 0, pDataBuf); //2//
  330.     bStrLen += bStrLen1;
  331.     u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  332.     pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  333.     bStrLen1 = sbNumToAscii(uwScope[3], 0, pDataBuf);  //3//
  334.     bStrLen += bStrLen1;
  335.     u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  336.     pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  337.     bStrLen1 = sbNumToAscii(uwScope[4], 0, pDataBuf);  //4//
  338.     bStrLen += bStrLen1;
  339.     u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  340.     pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  341.     bStrLen1 = sbNumToAscii(uwScope[5], 0, pDataBuf);  //5//
  342.     bStrLen += bStrLen1;
  343.     u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  344.     pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  345.     bStrLen1 = sbNumToAscii(uwScope[6], 0, pDataBuf);  //6//
  346.     bStrLen += bStrLen1;
  347.     u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  348.     pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  349.     bStrLen1 = sbNumToAscii(uwScope[7], 0, pDataBuf);  //7//
  350.     bStrLen += bStrLen1;
  351.     u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  352.     pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  353.     bStrLen1 = sbNumToAscii(uwScope[8], 0, pDataBuf);  //8//
  354.     bStrLen += bStrLen1;
  355.     u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  356.     pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  357.     bStrLen1 = sbNumToAscii(uwScope[9], 0, pDataBuf);  //9//
  358.     bStrLen += bStrLen1;
  359.     u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  360.     pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  361.     bStrLen1 = sbNumToAscii(uwScope[10], 0, pDataBuf);  //10//
  362.     bStrLen += bStrLen1;
  363.     u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  364.     pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  365.     bStrLen1 = sbNumToAscii(uwScope[11], 0, pDataBuf);  //11//
  366.     bStrLen += bStrLen1;
  367.     u8IPOMS_UserDataBuf0[bStrLen++] = 32;


  368.     pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  369.     bStrLen1 = sbNumToAscii(uwScope[12], 0, pDataBuf);  //12//
  370.     bStrLen += bStrLen1;
  371.     u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  372.     pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  373.     bStrLen1 = sbNumToAscii(uwScope[13], 0, pDataBuf);  //13//
  374.     bStrLen += bStrLen1;
  375.     u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  376.     pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  377.     bStrLen1 = sbNumToAscii(uwScope[14], 0, pDataBuf);  //14//
  378.     bStrLen += bStrLen1;
  379.     u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  380.     pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  381.     bStrLen1 = sbNumToAscii(uwScope[15], 0, pDataBuf);  //15//
  382.     bStrLen += bStrLen1;
  383.     u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  384.     pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  385.     bStrLen1 = sbNumToAscii(0, 0, pDataBuf);  //16//
  386.     bStrLen += bStrLen1;
  387.     u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  388.     pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  389.     bStrLen1 = sbNumToAscii(0, 0, pDataBuf);  //17//
  390.     bStrLen += bStrLen1;
  391.     u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  392.     pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  393.     bStrLen1 = sbNumToAscii(0, 0, pDataBuf);  //18//
  394.     bStrLen += bStrLen1;
  395.     u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  396.     pDataBuf = &u8IPOMS_UserDataBuf0[bStrLen];
  397.     bStrLen1 = sbNumToAscii(0, 0, pDataBuf);  //19//
  398.     bStrLen += bStrLen1;
  399.     u8IPOMS_UserDataBuf0[bStrLen++] = 32;

  400.     IPOMS_Write(ID_IPOMS, u8IPOMS_UserDataBuf0, bStrLen);

  401. }

  402. /*=============================================================================*
  403. * FUNCTION: IPOMS_Q3Command(void)
  404. * PURPOSE : Pick up parameter for Snatch Graph fuction
  405. * INPUT:
  406. *     void  // TRUE:disable the dog锟斤拷FALSE:enable the dog
  407. *
  408. * RETURN:
  409. *     void
  410. *
  411. * CALLS:
  412. *    //IPOMS_Write

  413. * CALLED BY:
  414. * //    INT32 IPOMS_Parsing()
  415. *
  416. *============================================================================*/
  417. void IPOMS_Q3Command(void)
  418. {
  419.     Uint16 u16SnatchDataCntTemp, u16IntervalTemp;
  420.     Uint8 u8DataKindTemp1, u8DataKindTemp2, u8DataKindTemp3, u8DataKindTemp4;
  421.     Uint8 u8TriggerSourceTemp, u8TriggerTemp, u8SignTemp;
  422.     Uint16 i, u16LengthTemp, u16CompareValTemp;

  423.     u16LengthTemp = u16IPOMS_CommandLength;

  424.     for(i = 0;i < u16LengthTemp;i++)
  425.     {
  426.         u8IPOMS_UserDataBuf0[i] = u8IPOMS_CommandBuffer0[i];
  427.     }

  428.       IPOMS_Write(ID_IPOMS, u8IPOMS_UserDataBuf0, u16LengthTemp);

  429.     //aaa
  430.     u16SnatchDataCntTemp = (u8IPOMS_CommandBuffer0[2] - 48) * 100 + (u8IPOMS_CommandBuffer0[3] - 48) * 10 + u8IPOMS_CommandBuffer0[4] - 48;
  431.     //bbb
  432.     u16IntervalTemp = (u8IPOMS_CommandBuffer0[6] - 48) * 100 + (u8IPOMS_CommandBuffer0[7] - 48) * 10 + u8IPOMS_CommandBuffer0[8] - 48;
  433.     //cc
  434.     u8DataKindTemp1 = (u8IPOMS_CommandBuffer0[10] - 48) * 10 + u8IPOMS_CommandBuffer0[11] - 48;
  435.     //dd
  436.     u8DataKindTemp2 = (u8IPOMS_CommandBuffer0[13] - 48) * 10 + u8IPOMS_CommandBuffer0[14] - 48;
  437.     //ee
  438.     u8DataKindTemp3 = (u8IPOMS_CommandBuffer0[16] - 48) * 10 + u8IPOMS_CommandBuffer0[17] - 48;
  439.     //ff
  440.     u8DataKindTemp4 = (u8IPOMS_CommandBuffer0[19] - 48) * 10 + u8IPOMS_CommandBuffer0[20] - 48;
  441.     //gg
  442.     u8TriggerSourceTemp = (u8IPOMS_CommandBuffer0[22] - 48) * 10 + u8IPOMS_CommandBuffer0[23] - 48;
  443.     //h
  444.     u8TriggerTemp = u8IPOMS_CommandBuffer0[25] - 48;
  445.     //+/-
  446.     u8SignTemp = u8IPOMS_CommandBuffer0[27] - 48;
  447.     //iiiii
  448.     u16CompareValTemp = (u8IPOMS_CommandBuffer0[29] - 48) * 10000 + (u8IPOMS_CommandBuffer0[30] - 48) * 1000 \
  449.      + (u8IPOMS_CommandBuffer0[31] - 48) * 100 + (u8IPOMS_CommandBuffer0[32] - 48) * 10 + u8IPOMS_CommandBuffer0[33] - 48;

  450.     if((u16SnatchDataCntTemp > 500) ||(u16IntervalTemp > 500) ||(u8TriggerTemp > 4))
  451.     {
  452.         return;
  453.     }

  454.     u16IPOMS_SaveDataCnt = 0;
  455.     u16IPOMS_SnatchDataCnt = u16SnatchDataCntTemp;
  456.     u16IPOMS_Interval = u16IntervalTemp;
  457.     u16IPOMS_Interval1 = u16IPOMS_Interval;

  458.     u8IPOMS_DataKind[0] = u8DataKindTemp1;
  459.     u8IPOMS_DataKind[1] = u8DataKindTemp2;
  460.     u8IPOMS_DataKind[2] = u8DataKindTemp3;
  461.     u8IPOMS_DataKind[3] = u8DataKindTemp4;
  462.     u8IPOMS_wTriggerSource = u8TriggerSourceTemp;
  463.     u8IPOMS_wTrigger = u8TriggerTemp;
  464.     u8IPOMS_Sign = u8SignTemp;

  465.     u16IPOMS_CompareVal = u16CompareValTemp;
  466.     u16IPOMS_TransmitCnt = 0;

  467.     if ((u8IPOMS_wTriggerSource != 0) && (u8IPOMS_wTrigger != 0))
  468.     {
  469.         wTriggerWaiting = 1;
  470.     }
  471.     else
  472.     {
  473.         wTriggerWaiting=0;
  474.     }
  475. }
  476. /*=============================================================================*
  477. * FUNCTION:  IPOMS_QDCommand(void)
  478. * PURPOSE :  Send Data to IPOMS For QD Command
  479. * INPUT:
  480. *     void  // TRUE:disable the dog 锟斤拷 FALSE:enable the dog
  481. *
  482. * RETURN:
  483. *     void
  484. *
  485. * CALLS:
  486. *      //IPOMS_WriteBinary()
  487. *
  488. *
  489. * CALLED BY:
  490. *     //INT32 IPOMS_Parsing()
  491. *
  492. *============================================================================*/
  493. void IPOMS_QDCommand(void)
  494. {
  495.     Uint8 u8Temp;
  496.     int16 i;
  497.     int32 i32CheckSum;

  498.     u8Temp = u8IPOMS_CommandBuffer0[2] - 48;

  499.     if((u8Temp >=4 ) || (0 == u16IPOMS_TransmitCnt))
  500.     {
  501.         return;
  502.     }
  503.     u16IPOMS_TransmitDataBuff[0] = 0x01;        //SOH
  504.     u16IPOMS_TransmitDataBuff[1] = u16IPOMS_TransmitCnt;    //length

  505.     i32CheckSum =(int32)(0x01 + u16IPOMS_TransmitCnt);
  506.     for(i = 0;i < u16IPOMS_TransmitCnt;i++)
  507.     {
  508.         u16IPOMS_TransmitDataBuff[2 + i] = u16IPOMS_GraphDataBuff[u8Temp][i];
  509.         i32CheckSum += u16IPOMS_GraphDataBuff[u8Temp][i];
  510.     }

  511.     u16IPOMS_TransmitDataBuff[2 + u16IPOMS_TransmitCnt] = (Uint16)(i32CheckSum & 0x0000FFFF);

  512.     IPOMS_WriteBinary(u16IPOMS_TransmitDataBuff, u16IPOMS_TransmitCnt + 3);

  513. }
  514. /*=============================================================================*
  515. * FUNCTION:  IPOMS_SnatchGraph(void)
  516. * PURPOSE :  SnatchGraph Data in the switching interrupt
  517. * INPUT:
  518. *     void  // TRUE:disable the dog 锟斤拷 FALSE:enable the dog
  519. *
  520. * RETURN:
  521. *     void
  522. *
  523. * CALLS:
  524. *
  525. * CALLED BY:
  526. *     //INT32 IPOMS_QDCommand()
  527. *
  528. *============================================================================*/
  529. Uint8 IPOMS_SnatchGraph(void)
  530. {
  531.     int16 i,j;

  532.     //trigger condition check
  533.     if(0 == u8IPOMS_SnatchGraphEnable)
  534.     {
  535.       return(false);
  536.     }

  537.     if(0 == u8IPOMS_wTrigger)
  538.     {
  539.       return(false);
  540.     }

  541.     if( wTriggerWaiting==1 )
  542.     {
  543.         if (u8IPOMS_wTrigger==2 )
  544.         {
  545.             if (GetDataSubArray[u8IPOMS_wTriggerSource]()<((int)u16IPOMS_CompareVal) )
  546.             {
  547.                 wTriggerWaiting=0;
  548.             }
  549.             /*if (u8IPOMS_Sign=='+')
  550.             {
  551.                 if (GetDataSubArray[u8IPOMS_wTriggerSource]()<((int)u16IPOMS_CompareVal) )
  552.                 {
  553.                     wTriggerWaiting=0;
  554.                 }
  555.             }
  556.             else if ( u8IPOMS_Sign=='-')
  557.             {
  558.                 if (GetDataSubArray[u8IPOMS_wTriggerSource]() < -((int)u16IPOMS_CompareVal) )
  559.                 {
  560.                     wTriggerWaiting=0;
  561.                 }
  562.             }*/
  563.         }
  564.         else if (u8IPOMS_wTrigger == 3)
  565.         {
  566.             if (GetDataSubArray[u8IPOMS_wTriggerSource]() > ((int)u16IPOMS_CompareVal))
  567.             {
  568.                 wTriggerWaiting = 0;
  569.             }
  570.         }
  571.         else if (u8IPOMS_wTrigger == 4)
  572.         {
  573.             if (GetDataSubArray[u8IPOMS_wTriggerSource]() == ((int)u16IPOMS_CompareVal))
  574.             {
  575.                 wTriggerWaiting=0;
  576.             }
  577.         }
  578.         return(false);
  579.     }

  580.     if(u16IPOMS_Interval1 > 0)
  581.     {
  582.         if(0 == (--u16IPOMS_Interval1))
  583.         {
  584.             u16IPOMS_Interval1 = u16IPOMS_Interval;
  585.         }
  586.         else
  587.         {
  588.             return(false);
  589.         }
  590.     }

  591. /*   for(i = 0;i < 2;i++)
  592.     {
  593.         j=u8IPOMS_DataKind[i];
  594.         if(0 == j)
  595.         {
  596.             continue;
  597.         }
  598.         u16IPOMS_GraphDataBuff[i][u16IPOMS_SaveDataCnt] = (Uint16)GetDataSubArray[j]();
  599.     }*/
  600.     for(i = 0;i < 4;i++)
  601.     {
  602.         j=u8IPOMS_DataKind[i];
  603.         if(0 == j)
  604.         {
  605.             continue;
  606.         }
  607.         u16IPOMS_GraphDataBuff[i][u16IPOMS_SaveDataCnt] = (Uint16)GetDataSubArray[j]();
  608.     }

  609.     ++u16IPOMS_SaveDataCnt;
  610.     if(u16IPOMS_SaveDataCnt == u16IPOMS_SnatchDataCnt)
  611.     {
  612.         u16IPOMS_TransmitCnt = u16IPOMS_SnatchDataCnt;
  613.         u16IPOMS_SnatchDataCnt = 0;
  614.         u16IPOMS_SaveDataCnt = 0;
  615.         u8IPOMS_wTrigger = 0;
  616.         u8IPOMS_wTriggerSource = 0;
  617.         u8IPOMS_SnatchGraphEnable = 1;
  618.         return(true);
  619.     }

  620.     return(false);
  621. }

  622. //=============================================================================
  623. // *  FUNCTION:  IPOMS_GraphicView_x()
  624. // *
  625. // *  PURPOSE :  Assign variable shown in Graphic View screen of IPOMS.
  626. //=============================================================================

  627. int16 IPOMS_GraphicView_0(void)
  628. {
  629.     return(uwScope[0]);
  630. }
  631. int16 IPOMS_GraphicView_1(void)
  632. {
  633.     return(uwScope[1]);
  634. }
  635. int16 IPOMS_GraphicView_2(void)
  636. {
  637.     return(uwScope[2]);
  638. }
  639. int16 IPOMS_GraphicView_3(void)
  640. {
  641.     return(uwScope[3]);
  642. }

  643. int16 IPOMS_GraphicView_4(void)
  644. {
  645.     return(uwScope[4]);
  646. }

  647. int16 IPOMS_GraphicView_5(void)
  648. {
  649.     return(uwScope[5]);
  650. }

  651. int16 IPOMS_GraphicView_6(void)

  652. {
  653.     return(uwScope[6]);
  654. }

  655. int16 IPOMS_GraphicView_7(void)
  656. {
  657.     return(uwScope[7]);
  658. }

  659. int16 IPOMS_GraphicView_8(void)
  660. {
  661.     return(uwScope[8]);
  662. }

  663. int16 IPOMS_GraphicView_9(void)
  664. {
  665.     return(uwScope[9]);
  666. }

  667. int16 IPOMS_GraphicView_10(void)
  668. {
  669.     return(uwScope[10]);
  670. }


  671. /*=============================================================================*
  672. * FUNCTION: sbNumToAscii
  673. * PURPOSE :  Convert input u16Number into max. 8 digital numbers including
  674. *            decimal represented byASCII code.
  675. * INPUT:
  676. *     void  // TRUE:disable the dog 锟斤拷 FALSE:enable the dog
  677. *
  678. * RETURN:
  679. *     void
  680. *
  681. * CALLS:
  682. *     void
  683. *
  684. * CALLED BY:
  685. *
  686. * IPOMS_Q1Command
  687. *
  688. *============================================================================*/
  689. Uint8 sbNumToAscii(Uint16 u16Number, int8 i8Exponent, Uint8 *pbBuffer)
  690. {
  691.     Uint8 u8No;
  692.     int8 i,j;
  693.     Uint8 bArrayTemp[8] = {0,0,0,0,0,0,0,0};
  694.     Uint32 u32NumberTemp;
  695.     j = 0;

  696.     if((0 == u16Number) || ((u16Number != 0) && (i8Exponent < -6)))
  697.     {
  698.         *pbBuffer = '0';
  699.         u8No = 1;
  700.     }
  701.     else
  702.     {
  703.         u32NumberTemp = (Uint32)u16Number;
  704.         for(i = 0;i < i8Exponent;i++)
  705.         {
  706.             if(u32NumberTemp <= 9999999)
  707.             {
  708.                 u32NumberTemp = u32NumberTemp * 10;
  709.             }
  710.             else
  711.             {
  712.                 u32NumberTemp = 99999999;
  713.             }
  714.         }
  715.         while ((u32NumberTemp > 0) || (i8Exponent < 0))
  716.         {
  717.             i = u32NumberTemp % 10;
  718.             u32NumberTemp = u32NumberTemp / 10;
  719.             bArrayTemp[j] = i + 0x30;
  720.             j++;

  721.             i8Exponent++;
  722.             if(0 == i8Exponent)
  723.             {
  724.                 bArrayTemp[j] = '.';
  725.                 j++;
  726.                 if(0 == u32NumberTemp)
  727.                 {
  728.                     bArrayTemp[j] = '0';
  729.                     j++;
  730.                 }
  731.             }
  732.         }
  733.         u8No = 0;
  734.         j--;
  735.         while (j >= 0)
  736.         {
  737.             *(pbBuffer + u8No) = bArrayTemp[j];
  738.             u8No++;
  739.             j--;
  740.         }
  741.     }/* end of number !=0 */
  742.     return(u8No);/* char length*/
  743. }

  744. /*=============================================================================*
  745. * FUNCTION:  IPOMS_WriteBinary
  746. * PURPOSE :  Write a 16bit data to  IPOMS Tx Port
  747. * INPUT:
  748. *     void  // TRUE:disable the dog 锟斤拷 FALSE:enable the dog
  749. *
  750. * RETURN:
  751. *     void
  752. *
  753. * CALLS:
  754. *    // void sSplit()
  755. *
  756. * CALLED BY:
  757. *     IPOMS_QDCommand()
  758. *
  759. *============================================================================*/
  760. void IPOMS_WriteBinary(Uint16 *pstart, Uint16 u16Length)
  761. {
  762.     int8 i;
  763.     int8 j;
  764.     Uint16 *pData;
  765.     Uint8  u8Data;

  766.     u8IPOMS_SendHighHalfByte = 1;
  767.     pData = pstart;
  768.     for(i = 0;i < u16Length;i++)
  769.     {
  770.         //split
  771.         //sSplit(*pData);

  772.         for(j = 0;j < 2;j++)
  773.         {
  774.             if(1 == u8IPOMS_SendHighHalfByte)
  775.             {   //write high 8bit
  776.                 u8Data = GET_HBYTE_OF_WORD(*pData);
  777.                 while(IPOMS_Write(ID_IPOMS, &u8Data, 1));
  778.                 u8IPOMS_SendHighHalfByte = 0;
  779.             }
  780.             else
  781.             {   //write low 8bit
  782.                 u8Data = GET_LBYTE_OF_WORD(*pData);
  783.                 while(IPOMS_Write(ID_IPOMS, &u8Data, 1));
  784.                 u8IPOMS_SendHighHalfByte = 1;
  785.             }
  786.         }

  787.         ++pData;
  788.     }
  789. }

  790. //=============================================================================
  791. // End of file.
  792. //=============================================================================

上位机和下位机动态联调(下位机发送4路正弦信号,上位机显示波形,并可以调整显示数据的长度)
上位机安装包和串口示波器驱动代码:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
hbzjt2011 发表于 2025-10-14 12:53 | 显示全部楼层
很详细的实践教程,谢谢分享
 楼主| 电笔小新 发表于 2025-10-14 14:50 | 显示全部楼层
本帖最后由 电笔小新 于 2025-10-14 14:57 编辑

6.控制策略设计
整个平衡车的控制,最重要的是平衡车角度的计算获取,如果角度没拟合好,就不用谈后面的车身平衡控制了;我是用上位机将原始角度数据跟滤波后的角度数据打印出来,通过观察波形来判断拟合结果;角度的拟合算法用了卡尔曼滤波和互补滤波;
通过上位机观察静止波形输出,通道一为原始数据,通道二为卡尔曼滤波,通道三为互补滤。
静止状态下两种滤波效果都挺好,把原始数据的噪音全滤掉了。
动态波形滤波测试,将车子直立,然后用手扶住平衡车,在水平方向使车子正负30度左右摇晃,记录输出波形。
从上面波形上看,动态时,角速度对滤波的加权值太大,需修改输入的角速度量程
调整输入角速度幅值,直到滤波效果比较正常,在晃动的条件下,滤波角度考虑到了角速度的惯量,加大了输出角度,更准确的输出了平衡车的当前状态。
角度拟合OK后,就可以给系统上控制代码,控制环路主要分车子平衡控制、车子速度控制、车子转向控制

车子平衡控制代码如下:通过输入车子的倾角和角加速度,计算需要将车子回到直立状态的驱动量;
  1. //============================================================================
  2. //* 函 数 名: Balance
  3. //* 功能说明: 直立PD控制
  4. //* 形    参: Angle->角度;Gyro->角速度
  5. //* 返 回 值: balance->直立控制PWM
  6. //============================================================================
  7. #define Middle_angle 0
  8. int Balance(float Angle,float Gyro)
  9. {
  10.     float Angle_bias,Gyro_bias;
  11.     int balance;
  12.     Angle_bias=Middle_angle-Angle;               //求出平衡的角度中值 和机械相关
  13.     Gyro_bias=0-Gyro;

  14.     balance=-Balance_Kp/B_Kp*Angle_bias-Gyro_bias*Balance_Kd/B_Kd; //计算平衡控制的电机PWM  PD控制   kp是P系数 kd是D系数
  15.     uwScope[1]=B_Kp;
  16.     uwScope[2]=B_Kd;

  17.     uwScope[8]=Balance_Kp/B_Kp*Angle_bias;
  18.     uwScope[9]=Gyro_bias*Balance_Kd/B_Kd;
  19.     return balance;
  20. }

车子速度控制代码如下:通过输入车子两个轮子的转速,计算需要将车子速度控制为零,稳定站立的驱动量;
  1. //============================================================================
  2. //* 函 数 名: Velocity
  3. //* 功能说明: 速度控制PWM
  4. //* 形    参: encoder_left->左轮编码器读数;encoder_right->右轮编码器读数
  5. //* 返 回 值: 速度控制PWM
  6. //============================================================================
  7. //修改前进后退速度,请修改Target_Velocity
  8. int Velocity(int encoder_left,int encoder_right)
  9. {
  10.     static float velocity,Encoder_Least,Encoder_bias,Movement;
  11.       static float Encoder_Integral,Target_Velocity;

  12.    //================速度PI控制器=====================//
  13.         Encoder_Least =0-(encoder_left+encoder_right);                    //获取最新速度偏差=目标速度(此处为零)-测量速度(左右编码器之和)
  14.         Encoder_bias *= 0.84;                                                 //一阶低通滤波器
  15.         Encoder_bias += Encoder_Least*0.16;                               //一阶低通滤波器,减缓速度变化
  16.         Encoder_Integral +=Encoder_bias;                                  //积分出位移 积分时间:10ms

  17.         Encoder_Integral=Encoder_Integral+Movement;                       //接收遥控器数据,控制前进后退
  18.         if(Encoder_Integral>2000)      Encoder_Integral=2000;             //积分限幅
  19.         if(Encoder_Integral<-2000)   Encoder_Integral=-2000;            //积分限幅

  20.         velocity=-Encoder_bias*Velocity_Kp/V_Kp-Encoder_Integral*Velocity_Ki/V_Ki;     //速度控制
  21.         if(Turn_Off(Angle_Balance,Bat_volt)==1||Flag_Stop==1) Encoder_Integral=0;//电机关闭后清除积分
  22.         uwScope[3]=V_Kp;
  23.         uwScope[4]=V_Kd;

  24.         uwScope[10]=Encoder_bias*Velocity_Kp/V_Kp;
  25.         uwScope[11]=Encoder_Integral*Velocity_Ki/V_Kd;
  26.       return velocity;
  27. }

车子转向控制代码如下:当前使用的是开环,强制给左右两个电机给定固定的偏移量。
  1. //============================================================================
  2. //* 函 数 名: Turn
  3. //* 功能说明: 转向控制
  4. //* 形    参: Z轴陀螺仪
  5. //* 返 回 值: 转向控制PWM
  6. //============================================================================
  7. int Turn(float gyro)
  8. {
  9.     static float Turn_Target,Turn_Amplitude=40;
  10.    
  11.     if(1==Flag_Left)        Turn_Target=-Turn_Amplitude/2;
  12.     else if(1==Flag_Right)  Turn_Target=Turn_Amplitude/2;
  13.     else                    Turn_Target=0;

  14.     return Turn_Target;             //转向环PWM右转为正,左转为负
  15. }

实际上调试只需要车子平衡控制和车子速度控制两个环路的总共四个参数;为了方便调试,直接将调试参数由上位机下发指令动态修改参数,根据反馈数据进行修改;
  1. void IPOMS_Parsing(void)
  2. {

  3.     switch(u8IPOMS_CommandBuffer0[0])
  4.     {
  5.     case 'Q':
  6.         {

  7.             if('1' == u8IPOMS_CommandBuffer0[1])
  8.             {
  9.                 IPOMS_Q1Command();
  10.             }
  11.              if('2' == u8IPOMS_CommandBuffer0[1])
  12.             {
  13.                 IPOMS_Q2Command();
  14.             }
  15.             if('3' == u8IPOMS_CommandBuffer0[1])
  16.             {
  17.                 IPOMS_Q3Command();
  18.             }
  19.             if('D' == u8IPOMS_CommandBuffer0[1])
  20.             {
  21.                 IPOMS_QDCommand();
  22.             }
  23.             if('4' == u8IPOMS_CommandBuffer0[1])
  24.             {
  25.                 B_Kp++;
  26.             }
  27.             if('5' == u8IPOMS_CommandBuffer0[1])
  28.             {
  29.                 B_Kp--;
  30.             }
  31.             if('6' == u8IPOMS_CommandBuffer0[1])
  32.             {
  33.                 B_Kd++;
  34.             }
  35.             if('7' == u8IPOMS_CommandBuffer0[1])
  36.             {
  37.                 B_Kd--;
  38.             }
  39.             if('8' == u8IPOMS_CommandBuffer0[1])
  40.             {
  41.                 V_Kp++;
  42.             }
  43.             if('9' == u8IPOMS_CommandBuffer0[1])
  44.             {
  45.                 V_Kp--;
  46.             }
  47.             if('H' == u8IPOMS_CommandBuffer0[1])
  48.               {
  49.                   V_Kd++;
  50.               }
  51.             if('I' == u8IPOMS_CommandBuffer0[1])
  52.               {
  53.                   V_Kd--;
  54.               }
  55.             if('L' == u8IPOMS_CommandBuffer0[1])
  56.             {
  57.                 Flag_Left=1;    //左转
  58.                 Flag_Right=0;
  59.                 Flag_front=0;
  60.                 Flag_back=0;
  61.             }
  62.             if('R' == u8IPOMS_CommandBuffer0[1])
  63.             {
  64.                 Flag_Left=0;
  65.                 Flag_Right=1;  //右转
  66.                 Flag_front=0;
  67.                 Flag_back=0;
  68.             }
  69.             if('F' == u8IPOMS_CommandBuffer0[1])
  70.             {
  71.                 Flag_Left=0;
  72.                 Flag_Right=0;
  73.                 Flag_front=1;  //前进
  74.                 Flag_back=0;
  75.             }
  76.             if('B' == u8IPOMS_CommandBuffer0[1])
  77.             {
  78.                 Flag_Left=0;
  79.                 Flag_Right=0;
  80.                 Flag_front=0;
  81.                 Flag_back=1;  //后退
  82.             }
  83.             if('S' == u8IPOMS_CommandBuffer0[1])
  84.             {
  85.                 Flag_Left=0;  //停止
  86.                 Flag_Right=0;
  87.                 Flag_front=0;
  88.                 Flag_back=0;
  89.             }
  90.         }
  91.     break;
  92.     default:
  93.     break;
  94.     }
  95. }

参数调试经验分享:对于四个参数个人的调试理解,先调试车子平衡控制的两个参数,然后在调试车子速度控制的两个参数;最后在微调四个参数,实现更理想控制;
对于车子平衡控制的两个参数,B_Kp是快速矫正车子状态回到直立;B_Kd是快速消除车子抖动;
先调试B_Kp值使得车子在外力干扰偏离直立状态,环路控制使得车子在直立状态左右状态(震荡幅度越小说明参数更合适);调试B_Kd值,可以先将B_Kp清零,调整B_Kd值使得用手晃动明显感觉车子非常僵硬;最后就是联合调试B_KpB_Kd值,使得车子能够直立;这时候车子是不可能固定站住的(最好是拿细绳绑住车子,吊起来调试,防止车子调试过程乱跑撞坏),车子可能有一个向前或向后的动力;这已经完成了车子平衡控制的初步控制;
对于车子速度控制的两个参数,V_Kp是快速减弱子速度;V_KI是控制车子回到原理静止位置;先调试V_Kp值使得车子在速度上升的时候有阻尼起到减速效果,后面调试V_KI使得车子能够在原地站住;
   最后就是微调四个参数,使得车子在收到前进、后退、左转、右转指令后能够更稳定运行;
调试结果展示:
稳定在原地,受外力干扰能快速稳定,回到原来位置:
用激光反馈黑线位置,控制车子跟巡黑线:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| 电笔小新 发表于 2025-10-14 16:26 | 显示全部楼层
7.线升级Bootloader
用烧录器下载程序,每次插拔下载接口都非常的费劲。就想着能够实现无线烧录程序就好;查了相关资料,需要先写一个Bootloader下载到F28035主控板,然后使用上位机通过无线串口对主控板实现升级;DSP内部存储器的调用得用官方提供的API库;安装C2000ware之后,在下面路径可以搜索到API_lib;而且有例程可以参考;

直接打开参考例程,进行修改即可得到一个F28035BootLoader;
  1. /******************************************************************************
  2. *    FILENAME : User_Bootloader.c
  3. *
  4. *    PURPOSE  : BootLoader APP Code.
  5. *
  6. *       Author: ���С��                      Created on: 2025��9��24��
  7. ******************************************************************************/

  8. #include "User_Include.h"
  9. #include "User_Bootloader.h"

  10. //=============================================================================
  11. // *  Variables & Function_Defines
  12. //=============================================================================
  13. Uint8   u8Program_Cnt=0;

  14. Uint32  u32ReadDataBuff[8];
  15. Uint8   u8CompanionRxbuffer[5];
  16. Uint16  UpdataFlag16[8]={0x0101,0x0202,0x0303,0x0404,0x0505,0x0606,0x0707,0x0808};
  17. Uint32  UpdataFlag32[4]={0x02020101,0x04040303,0x06060505,0x08080707};

  18. tSCIDataDef    tSCICom;
  19. tFlashDataDef  tUpdateFlash;
  20. /*--- Global variables used to interface to the flash routines */

  21. FLASH_ST FlashStatus;


  22. SECTOR Sector[8] = {
  23.         (Uint16 *) 0x3E8000,(Uint16 *) 0x3E9FFF,
  24.         (Uint16 *) 0x3EA000,(Uint16 *) 0x3EBFFF,
  25.         (Uint16 *) 0x3EC000,(Uint16 *) 0x3EDFFF,
  26.         (Uint16 *) 0x3EE000,(Uint16 *) 0x3EFFFF,
  27.         (Uint16 *) 0x3F0000,(Uint16 *) 0x3F1FFF,
  28.         (Uint16 *) 0x3F2000,(Uint16 *) 0x3F3FFF,
  29.         (Uint16 *) 0x3F4000,(Uint16 *) 0x3F5FFF,
  30.         (Uint16 *) 0x3F6000,(Uint16 *) 0x3F7FFF
  31. };

  32. Uint16 WriteVerfyData(void);
  33. void UpdateReplyData(void);

  34. //============================================================================
  35. // * Function    :  BootloaderAPP()
  36. // * Purpose     :  This function initializes the BootloaderAPP.
  37. // * Parameters  :  Null
  38. // * Return      :  Null
  39. //============================================================================
  40. #pragma CODE_SECTION(BootloaderAPP,"ramfuncs");
  41. void BootloaderAPP(void)
  42. {
  43.     Uint16 Status;
  44.     Uint32 u32OffsetAddress = 0;
  45.     Uint16 u16ProgramCount  = 0;
  46.     Uint16 u16VerityCount   = 0;
  47.     Uint32 u32TimerCount    = 0;

  48.     u8Program_Cnt=0;
  49.     memset(&tSCICom, 0, sizeof(tSCICom));
  50.     memset(&tSCICom.u8SCIRXBuffer, 0, (PacketSize+8));

  51.     while(1)
  52.     {

  53.         tUpdateFlash.u8UpdateDataFlag = 1;
  54.         while(tUpdateFlash.u8UpdateDataFlag == 1)
  55.         {
  56.             u32TimerCount++;
  57.             if(u32TimerCount > 15000000)
  58.             {
  59.                 u32TimerCount = 0;

  60.                 Status = Flash_Verify((Uint16 *)VerifyAddr,(Uint16 *)UpdataFlag16,8,&FlashStatus);
  61.                 if(Status == STATUS_SUCCESS)
  62.                 {
  63.                    return;
  64.                 }
  65.             }
  66.         }
  67.         if(tUpdateFlash.u8UpdateDataStatus == 0)
  68.         {
  69.             switch(tSCICom.u8SCIRXBuffer[Data_Commad])
  70.             {
  71.             case Update_Keep:
  72.                 tUpdateFlash.u8UpdateDataStatus = M_ACK;
  73.                 //============================OLED��ʾ==============================//
  74.                    OLED_Clear();
  75.                    OLED_ShowString(20,10,"<-BootAPP->");
  76.                    OLED_ShowString(0,30,"Link_Success");
  77.                    OLED_Refresh_Gram();
  78.                //================================================================//
  79.                 break;

  80.             case Update_EraseAll:
  81.                 DINT;
  82.                 Status = Flash_Erase((SECTORD|SECTORE|SECTORF|SECTORG|SECTORH),&FlashStatus);
  83.                 EINT;
  84.                 if(Status == STATUS_SUCCESS)
  85.                 {
  86.                     tUpdateFlash.u8UpdateDataStatus = EraseAll_Success;
  87.                 //============================OLED��ʾ==============================//
  88.                     OLED_Clear();
  89.                     OLED_ShowString(20,10,"<-BootAPP->");
  90.                     OLED_ShowString(0,30,"Erase_Success");
  91.                     OLED_Refresh_Gram();
  92.                 //================================================================//
  93.                 }
  94.                 else
  95.                 {
  96.                   tUpdateFlash.u8UpdateDataStatus = Com_Fail;
  97.                 }
  98.                 break;

  99.             case Update_ProgramCommand:
  100.                 u32OffsetAddress = ((long)tSCICom.u8SCIRXBuffer[4]<<8)+((long)tSCICom.u8SCIRXBuffer[5]);
  101.                 if(u32OffsetAddress <= 0xFFFF)
  102.                 {
  103.                     for(u16ProgramCount = 0; u16ProgramCount < (tSCICom.u16UpdateDataLength/2); u16ProgramCount++)
  104.                     {
  105.                         tSCICom.Update_Buffer[u16ProgramCount]=(tSCICom.u8SCIRXBuffer[2*u16ProgramCount+6] << 8)+(tSCICom.u8SCIRXBuffer[2*u16ProgramCount+7]);
  106.                     }

  107.                     for(u16VerityCount = 0; u16VerityCount < (tSCICom.u16UpdateDataLength/4); u16VerityCount++)
  108.                     {
  109.                         tSCICom.Verify_Buffer[u16VerityCount]=((Uint32)tSCICom.u8SCIRXBuffer[2*u16VerityCount+1] << 16)+((Uint32)tSCICom.u8SCIRXBuffer[2*u16VerityCount]);
  110.                     }


  111.                     Status = Flash_Program((Uint16 *)(u32OffsetAddress + Flash_ProjectStart),(Uint16 *)tSCICom.Update_Buffer,tSCICom.u16UpdateDataLength/2,&FlashStatus);

  112.                     if(Status == STATUS_SUCCESS)
  113.                     {
  114.                         tUpdateFlash.u8UpdateDataStatus = ProgramCommand_Success;
  115.                     //============================OLED��ʾ==============================//
  116.                         u8Program_Cnt++;
  117.                         OLED_ShowString(20,10,"<-BootAPP->");
  118.                         OLED_ShowString(0,30,"Programming  ");
  119.                         OLED_ShowNumber(96,30,u8Program_Cnt,3,12);
  120.                         OLED_Refresh_Gram();
  121.                     //================================================================//
  122.                     }
  123.                     else
  124.                    {
  125.                      tUpdateFlash.u8UpdateDataStatus = Com_Fail;
  126.                    }
  127.                 }
  128.                 else
  129.                 {
  130.                     tUpdateFlash.u8UpdateDataStatus = Com_Fail;
  131.                 }
  132.                 break;

  133.             case Update_End:
  134.                  tUpdateFlash.u8UpdateDataStatus = M_NACK;
  135.                  break;
  136.             }
  137.         }
  138.         UpdateReplyData();
  139.         tSCICom.u16BuffCountNum = 0;
  140.         u32TimerCount = 0;
  141.         if(tUpdateFlash.u8UpdateDataStatus == M_NACK)
  142.         {
  143.             WriteVerfyData();
  144.         //============================OLED��ʾ==============================//
  145.             u8Program_Cnt=0;
  146.             OLED_Clear();
  147.             OLED_ShowString(20,10,"<-BootAPP->");
  148.             OLED_ShowString(0,30,"Upgrade_Success");
  149.             OLED_Refresh_Gram();
  150.         //================================================================//
  151.             memset(&tSCICom , 0 , sizeof(tSCICom));
  152.             memset(tSCICom.u8SCIRXBuffer , 0xFF , (PacketSize+8));
  153.             break;
  154.         }


  155.    }
  156. }


  157. //=============================================================================
  158. // *  FUNCTION:  WriteVerfyData()
  159. // *
  160. // *  PURPOSE :
  161. //=============================================================================

  162.   Uint16 WriteVerfyData(void)
  163. {
  164.      Uint16  Status;

  165.      Status = Flash_Program((Uint16 *)VerifyAddr,(Uint16 *)UpdataFlag16,8,&FlashStatus);
  166.      if(Status != STATUS_SUCCESS)
  167.      {
  168.          return Status;
  169.      }
  170.      Status = Flash_Verify((Uint16 *)VerifyAddr,(Uint16 *)UpdataFlag16,8,&FlashStatus);
  171.      if(Status != STATUS_SUCCESS)
  172.      {
  173.          return Status;
  174.      }
  175.      return Status;
  176. }

  177. //=============================================================================
  178. // *  FUNCTION:  UpdateReplyData()
  179. // *
  180. // *  PURPOSE :
  181. //=============================================================================
  182. #pragma CODE_SECTION(UpdateReplyData,"ramfuncs");
  183. void UpdateReplyData(void)
  184. {
  185.   Uint8  u8ReplyData[5];
  186.   Uint16 u16CRCcheck;

  187.   u8ReplyData[0] = 0xAA;
  188.   u8ReplyData[1] = 0x00;
  189.   u8ReplyData[2] = tUpdateFlash.u8UpdateDataStatus;
  190.   u16CRCcheck = (u8ReplyData[0] + u8ReplyData[1] + u8ReplyData[2]);
  191.   u8ReplyData[3] = (Uint8)(u16CRCcheck >> 8);
  192.   u8ReplyData[4] = (Uint8)(u16CRCcheck & 0xFF);

  193.   LinaWrite(u8ReplyData, 5);

  194. }


  195. //=============================================================================
  196. // *  FUNCTION:  u8UpdateDataCheck()
  197. // *
  198. // *  PURPOSE :
  199. //=============================================================================
  200. Uint16 u8UpdateDataCheck(Uint16 u16Buffcount)
  201. {
  202.     Uint16 CRC_Check = 0;
  203.     Uint16 DataLength = 0;
  204.     if(u16Buffcount == Data_Start)
  205.     {
  206.         if(tSCICom.u8SCIRXBuffer[u16Buffcount] != Update_StartSign)
  207.         {
  208.             return 0;
  209.         }
  210.     }

  211.     if(u16Buffcount == Data_Lenght)                         //���ݳ���λ
  212.     {
  213.         DataLength = ((uint16_t)tSCICom.u8SCIRXBuffer[1]<<8)+((uint16_t)tSCICom.u8SCIRXBuffer[2]);
  214.         if(DataLength <= PacketSize)
  215.          {
  216.             tSCICom.u16UpdateDataLength = DataLength;
  217.          }
  218.         else
  219.         {
  220.             memset(tSCICom.u8SCIRXBuffer, 0, 3);
  221.             return 0;
  222.         }
  223.     }

  224.     if(u16Buffcount == (tSCICom.u16UpdateDataLength + 7))              //����У��λ
  225.          {

  226.              CRC_Check = Cal_CRC16(tSCICom.u8SCIRXBuffer, (u16Buffcount + 1));
  227.              if(CRC_Check == 0)
  228.              {
  229.                  tUpdateFlash.u8UpdateDataStatus = 0;
  230.                  tUpdateFlash.u8UpdateDataFlag   = 0;              //�����ȴ���¼whileѭ��
  231.              }
  232.              else
  233.              {
  234.                  tUpdateFlash.u8UpdateDataStatus = Check_Fail;
  235.                  return 0;
  236.              }
  237.          }

  238.     return 1;
  239. }

  240. //=============================================================================
  241. // End of file.
  242. //=============================================================================

BootLoader写好后,就要设计在线升级上位机,前面讲过C#,也已经打通了电脑对DSP之间的串口通信回路,上位机只要根据下位机在线升级的通讯协议进行通讯,即可对烧录文件进行解析,然后下发烧录到DSP内部的flash
烧录演示如下:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| 电笔小新 发表于 2025-10-14 18:02 | 显示全部楼层
8. 软件系统架构升级
以前做一些逆变器项目,系统比较复杂,任务之间的优先级需要严格考虑,常用的是CCS自带的Bios操作系统,这个系统比较封闭,只能用于CCS编程环境。对于既写MCUDSP两个平台的软件工程师来说,要是能够统一用FreeRTOS 或者Ucos操作系统,那管理软件代码的工作量就小很多了;所以在工作之余就有尝试自己去在CCS编程环境下去做FreeRTOSUcos的移植。在公司领导不让用,说非官方移植的说不定有很多软件Bug,要把控好软件风险。刚好这次就拿着个平衡车平台做一下验证。
其实下载最新版本的C2000ware,里面已经有FreeRTOSdemo例程,说明TI现在也开始服软,没那么强硬强推自己的BiosDemo例程只是提供了一些比较新的芯片,像F28335F28035就没有例程提供。
F28035_FreeRTOS的移植我在是官方C2000_F28003x_C28x_CCS_DEMO的工程上进行移植的,只需将相关的F28003X文件更换成F2803X文件即可:
使用FreeRTOS任务管理初始化如下:
  1. /******************************************************************************
  2. *    FILENAME : Main.c
  3. *
  4. *    PURPOSE  : FreeRTOS V10.4.6 + F28035 ������̬����
  5. *
  6. *    Author: ���С��                      Created on: 2025��9��12��
  7. ******************************************************************************/
  8. #include "User_Include.h"

  9. #include "FreeRTOS.h"
  10. #include "task.h"
  11. #include "semphr.h"

  12. //=============================================================================
  13. // *  Variables & Function_Defines
  14. //=============================================================================

  15. //Idle_task
  16. #define STACK_SIZE  256U
  17. static StaticTask_t idleTaskBuffer;
  18. static StackType_t  idleTaskStack[STACK_SIZE];

  19. //Start_task
  20. #define START_TASK_PRIO       1          //�������ȼ�
  21. #define START_STK_SIZE        128U        //�����ջ��С
  22. TaskHandle_t StartTask_Handle;           //������
  23. static StaticTask_t StartTaskBuffer;
  24. static StackType_t  StartTaskStack[START_STK_SIZE];
  25. void Start_task(void* pvParameters);     //������


  26. //App_task1
  27. #define APP_TASK1_PRIO        4
  28. #define TASK1_STK_SIZE        256U
  29. TaskHandle_t AppTask1_Handle;
  30. static StaticTask_t AppTask1Buffer;
  31. static StackType_t  AppTask1Stack[TASK1_STK_SIZE];
  32. void App_task1(void * pvParameters);

  33. //App_task2
  34. #define APP_TASK2_PRIO        2
  35. #define TASK2_STK_SIZE        256U
  36. TaskHandle_t AppTask2_Handle;
  37. static StaticTask_t AppTask2Buffer;
  38. static StackType_t  AppTask2Stack[TASK2_STK_SIZE];
  39. void App_task2(void* pvParameters);

  40. //App_task3
  41. #define APP_TASK3_PRIO        3
  42. #define TASK3_STK_SIZE        512U
  43. TaskHandle_t AppTask3_Handle;
  44. static StaticTask_t AppTask3Buffer;
  45. static StackType_t  AppTask3Stack[TASK3_STK_SIZE];
  46. void App_task3(void* pvParameters);

  47. //-------------------------------------------------------------------------------------------------
  48. void vApplicationStackOverflowHook( TaskHandle_t xTask, char *pcTaskName )
  49. {
  50.     while(1);
  51. }

  52. //-------------------------------------------------------------------------------------------------
  53. void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
  54. {
  55.     *ppxIdleTaskTCBBuffer = &idleTaskBuffer;
  56.     *ppxIdleTaskStackBuffer = idleTaskStack;
  57.     *pulIdleTaskStackSize = STACK_SIZE;
  58. }

  59. //============================================================================
  60. // * Function    :  main()
  61. // * Purpose     :  �������������.
  62. // * Parameters  :
  63. // * Return      :  Null
  64. //============================================================================
  65. void main(void)
  66. {
  67.     InitDSPSystem();
  68.     InitParameter();

  69. /* ����Start_task���� */
  70.     StartTask_Handle = xTaskCreateStatic((TaskFunction_t ) Start_task,           // Function that implements the task.
  71.                                          (const char*    ) "Start_task",         // Text name for the task.
  72.                                          (uint16_t       ) START_STK_SIZE,       // Number of indexes in the xStack array.
  73.                                          (void*          ) NULL,                 // Parameter passed into the task.
  74.                                          (UBaseType_t    ) START_TASK_PRIO,      // Priority at which the task is created.
  75.                                          (StackType_t*   ) StartTaskStack,       // Array to use as the task's stack.
  76.                                          (StaticTask_t*  ) &StartTaskBuffer );   // Variable to hold the task's data structure.
  77.     vTaskStartScheduler();
  78. }

  79. //============================================================================
  80. // * Function    :  Start_task()
  81. // * Purpose     :  Ϊ�˷���������е����񴴽����������������������.
  82. // * Parameters  :
  83. // * Return      :  Null
  84. //============================================================================
  85. void Start_task(void* pvParameters)
  86. {

  87.   taskENTER_CRITICAL(); //�����ٽ���

  88. /* ����APP_task1���� */
  89.   AppTask1_Handle = xTaskCreateStatic((TaskFunction_t ) App_task1,           // Function that implements the task.
  90.                                       (const char*    ) "App_task1",         // Text name for the task.
  91.                                       (uint16_t       ) TASK1_STK_SIZE,      // Number of indexes in the xStack array.
  92.                                       (void*          ) NULL,                // Parameter passed into the task.
  93.                                       (UBaseType_t    ) APP_TASK1_PRIO,      // Priority at which the task is created.
  94.                                       (StackType_t*   ) AppTask1Stack,       // Array to use as the task's stack.
  95.                                       (StaticTask_t*  ) &AppTask1Buffer );   // Variable to hold the task's data structure.

  96. /* ����APP_task2���� */
  97.   AppTask2_Handle = xTaskCreateStatic((TaskFunction_t ) App_task2,           // Function that implements the task.
  98.                                       (const char*    ) "App_task2",         // Text name for the task.
  99.                                       (uint16_t       ) TASK2_STK_SIZE,      // Number of indexes in the xStack array.
  100.                                       (void*          ) NULL,                // Parameter passed into the task.
  101.                                       (UBaseType_t    ) APP_TASK2_PRIO,      // Priority at which the task is created.
  102.                                       (StackType_t*   ) AppTask2Stack,       // Array to use as the task's stack.
  103.                                       (StaticTask_t*  ) &AppTask2Buffer );   // Variable to hold the task's data structure.

  104. /* ����APP_task3���� */
  105.   AppTask3_Handle = xTaskCreateStatic((TaskFunction_t ) App_task3,           // Function that implements the task.
  106.                                       (const char*    ) "App_task3",         // Text name for the task.
  107.                                       (uint16_t       ) TASK3_STK_SIZE,      // Number of indexes in the xStack array.
  108.                                       (void*          ) NULL,                // Parameter passed into the task.
  109.                                       (UBaseType_t    ) APP_TASK3_PRIO,      // Priority at which the task is created.
  110.                                       (StackType_t*   ) AppTask3Stack,       // Array to use as the task's stack.
  111.                                       (StaticTask_t*  ) &AppTask3Buffer );   // Variable to hold the task's data structure.


  112.   vTaskDelete(StartTask_Handle); //ɾ��AppTaskCreate����

  113.   taskEXIT_CRITICAL();    //�˳��ٽ���
  114. }

  115. //---------------------------------------------------------------------------
  116. // App_task1:
  117. //---------------------------------------------------------------------------
  118. void App_task1(void * pvParameters)
  119. {
  120.     int Balance_Pwm,Velocity_Pwm,Turn_Pwm;              //ƽ�⻷PWM�������ٶȻ�PWM������ת��PWM��

  121.     while (1)
  122.     {

  123.         TEST1_H;

  124.         Key_Scan();                                 //ɨ�谴��״̬
  125.         Laser_Scan();                               //��ȡ����ɨ����
  126.         Bat_volt=Get_battery_volt();                //��ȡ��ص�ѹ
  127.         UR_distance=Get_Distance();                 //��ȡ��������������ֵ
  128.         Encoder_Left=Read_Encoder(Left);            //��ȡ���ֱ�������ֵ��ǰ��Ϊ��������Ϊ��
  129.         Encoder_Right=Read_Encoder(Right);          //��ȡ���ֱ�������ֵ��ǰ��Ϊ��������Ϊ��
  130.         Get_Velocity_Form_Encoder(Encoder_Left,Encoder_Right);    //���㳵���ٶȣ�mm/s��
  131.         Get_Angle(Way_Angle);                       //������̬��10msһ�Σ����ߵIJ���Ƶ�ʿ��Ը��ƿ������˲��ͻ����˲���Ч��

  132.         Balance_Pwm=Balance(Angle_Balance,Gyro_Balance);    //ƽ��PID���� Gyro_Balanceƽ����ٶȼ��ԣ�ǰ��Ϊ��������Ϊ��
  133.         Velocity_Pwm=Velocity(Encoder_Left,Encoder_Right);  //�ٶȻ�PID����  ��ס���ٶȷ�����������������С�����ʱ��Ҫ����������Ҫ���ܿ�һ��
  134.         Turn_Pwm=Turn(Gyro_Turn);


  135.         Motor_Left=Balance_Pwm+Velocity_Pwm+Turn_Pwm;       //�������ֵ������PWM
  136.         Motor_Right=Balance_Pwm+Velocity_Pwm-Turn_Pwm;      //�������ֵ������PWM
  137.                                                             //PWMֵ����ʹС��ǰ��������ʹС������

  138.         Motor_Left=Output_Limit(Motor_Left,5800,-5800);
  139.         Motor_Right=Output_Limit(Motor_Right,5800,-5800);          //PWM�޷�

  140.         if(Pick_Up(Acceleration_Z,Angle_Balance,Encoder_Left,Encoder_Right))//����Ƿ�С��������
  141.         Flag_Stop=1;                                                //���������͹رյ��

  142.         Choose(Encoder_Left,Encoder_Right);                     //ת������ѡ��С��ģʽ
  143.         if(Turn_Off(Angle_Balance,Bat_volt)==0)                  //����������쳣
  144.         Set_Pwm(Motor_Left,Motor_Right);                    //��ֵ��PWM�Ĵ���


  145.         uwScope[5]=Balance_Pwm;
  146.         uwScope[6]=Velocity_Pwm;
  147.         uwScope[7]=Turn_Pwm;


  148.         IPOMS_SnatchGraph();
  149.         TEST1_L;

  150.         vTaskDelay(6 / portTICK_PERIOD_MS);
  151.     }
  152. }

  153. //---------------------------------------------------------------------------
  154. // App_task2:
  155. //---------------------------------------------------------------------------
  156. void App_task2(void * pvParameters)
  157. {
  158.     while (1)
  159.     {
  160.         LED1_TOGGLE;

  161.         oled_show();  //OLED��ʾ
  162.         APP_Show();   //�ֻ�APP��ʾ

  163.         vTaskDelay(100 / portTICK_PERIOD_MS);
  164.     }
  165. }

  166. //---------------------------------------------------------------------------
  167. // App_task3:
  168. //---------------------------------------------------------------------------
  169. void App_task3(void * pvParameters)
  170. {
  171.     while (1)
  172.     {
  173.         LED2_TOGGLE;
  174.         TEST2_H;
  175.         pIPOMS_CommandIn0 = u8IPOMS_CommandBuffer0;
  176.         IPOMS_SCI();    //������λ����ʾ
  177.         TEST2_L;
  178.         vTaskDelay(500 / portTICK_PERIOD_MS);
  179.     }
  180. }

  181. //============================================================================
  182. // End of file.
  183. //============================================================================

Ucos的移植也是一样的道理,在工程里面加入Ucos系统官方文件:
使用Ucos任务管理初始化如下:
  1. /******************************************************************************
  2. *    FILENAME : Main.c
  3. *
  4. *    PURPOSE  : uC/OS-II V2.9x + F28035 ������̬����
  5. *
  6. *    Author: ���С��                      Created on: 2025��9��12��
  7. ******************************************************************************/
  8. #include "User_Include.h"

  9. //=============================================================================
  10. // *  Variables & Function_Defines
  11. //=============================================================================

  12. CPU_STK_SIZE App_TaskStartStk[APP_TASK_STK_SIZE];

  13. CPU_STK_SIZE App_Task1Stk[APP_TASK1_STK_SIZE];
  14. CPU_STK_SIZE App_Task2Stk[APP_TASK2_STK_SIZE];
  15. CPU_STK_SIZE App_Task3Stk[APP_TASK3_STK_SIZE];

  16. //static OS_EVENT *AppTaskObjSem;

  17. static void App_TaskStart(void *p_arg);

  18. static void App_Task1(void *p_arg);
  19. static void App_Task2(void *p_arg);
  20. static void App_Task3(void *p_arg);

  21. /*============================================================================*/

  22. void main(void)
  23. {
  24.     InitDSPSystem();
  25.     InitParameter();

  26.         OSInit();
  27.         OSTaskCreateExt(App_TaskStart,
  28.                         (void     *)0,
  29.                         (CPU_STK  *)&App_TaskStartStk[0],
  30.                         (INT8U     )APP_TASK_START_PRIO,
  31.                         (INT16U    )APP_TASK_START_ID,
  32.                         (CPU_STK  *)&App_TaskStartStk[APP_TASK_STK_SIZE-1u],
  33.                         (INT32U    )APP_TASK_STK_SIZE,
  34.                         (void     *)0,
  35.                         (INT16U    )(OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR));
  36.         OSStart();

  37. }


  38. //---------------------------------------------------------------------------
  39. // App_Functions:
  40. //---------------------------------------------------------------------------
  41. void APP_Tick_Init(void)
  42. {
  43.     EALLOW;
  44.     PieVectTable.OS_CPU_RTOSINT = &OS_CPU_RTOSINT_Handler;   //RTOS
  45.     InitCpuTimers();
  46.     ConfigCpuTimer(&CpuTimer0, 60, 1000);   //��ʱ��0��ʼ�����ڶ�������ΪCPU SYSCLK = 60M�� ����������Ϊ�趨�����ڣ�us��λ
  47.     CpuTimer0Regs.TCR.bit.TSS = 0;          // To start or restart the CPU-timer, set TSS to 0

  48.     PieCtrlRegs.PIEIER1.bit.INTx7 = 1;      //Enable TINT0 in the PIE
  49.     IER |= M_INT1;                          //Enable group_1 interrupt

  50.     EINT;                                   //Enable Global interrupt INTM
  51.     ERTM;                                   //Enable Global interrupt DBGM
  52. }


  53. static void App_TaskStart(void *p_arg)
  54. {
  55. //   volatile CPU_INT08U os_err;

  56.    (void)&p_arg;
  57.    APP_Tick_Init();

  58. //   AppTaskObjSem = OSSemCreate(0);
  59.    OSTaskCreateExt(App_Task1,
  60.                       (void     *)0,
  61.                       (CPU_STK  *)&App_Task1Stk[0],
  62.                       (INT8U     )APP_TASK1_PRIO,
  63.                       (INT16U    )APP_TASK1_ID,
  64.                       (CPU_STK  *)&App_Task1Stk[APP_TASK1_STK_SIZE-1u],
  65.                       (INT32U    )APP_TASK_STK_SIZE,
  66.                       (void     *)0,
  67.                       (INT16U    )(OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR));

  68.    OSTaskCreateExt(App_Task2,
  69.                       (void     *)0,
  70.                       (CPU_STK  *)&App_Task2Stk[0],
  71.                       (INT8U     )APP_TASK2_PRIO,
  72.                       (INT16U    )APP_TASK2_ID,
  73.                       (CPU_STK  *)&App_Task2Stk[APP_TASK2_STK_SIZE-1u],
  74.                       (INT32U    )APP_TASK_STK_SIZE,
  75.                       (void     *)0,
  76.                       (INT16U    )(OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR));

  77.    OSTaskCreateExt(App_Task3,
  78.                       (void     *)0,
  79.                       (CPU_STK  *)&App_Task3Stk[0],
  80.                       (INT8U     )APP_TASK3_PRIO,
  81.                       (INT16U    )APP_TASK3_ID,
  82.                       (CPU_STK  *)&App_Task3Stk[APP_TASK3_STK_SIZE-1u],
  83.                       (INT32U    )APP_TASK3_STK_SIZE,
  84.                       (void     *)0,
  85.                       (INT16U    )(OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR));
  86. //   while(DEF_TRUE){
  87. // //        os_err = OSSemPost(AppTaskObjSem);
  88. //         OSTimeDlyHMSM(0, 0, 0, 1);
  89. //     }
  90.      OSTaskDel (APP_TASK_START_PRIO);  //ɾ��App_TaskStart����

  91. }

  92. //---------------------------------------------------------------------------
  93. // App_task1:
  94. //---------------------------------------------------------------------------
  95. static void App_Task1(void *p_arg)
  96. {
  97.     (void)&p_arg;
  98.     int Balance_Pwm,Velocity_Pwm,Turn_Pwm;              //ƽ�⻷PWM�������ٶȻ�PWM������ת��PWM��
  99.     while (DEF_TRUE){

  100.          TEST1_H;
  101.          Key_Scan();                                 //ɨ�谴��״̬
  102.          Laser_Scan();                               //��ȡ����ɨ����
  103.          Bat_volt=Get_battery_volt();                //��ȡ��ص�ѹ
  104.          UR_distance=Get_Distance();                 //��ȡ��������������ֵ
  105.          Encoder_Left=Read_Encoder(Left);            //��ȡ���ֱ�������ֵ��ǰ��Ϊ��������Ϊ��
  106.          Encoder_Right=Read_Encoder(Right);          //��ȡ���ֱ�������ֵ��ǰ��Ϊ��������Ϊ��
  107.          Get_Velocity_Form_Encoder(Encoder_Left,Encoder_Right);    //���㳵���ٶȣ�mm/s��
  108.          Get_Angle(Way_Angle);                       //������̬��10msһ�Σ����ߵIJ���Ƶ�ʿ��Ը��ƿ������˲��ͻ����˲���Ч��

  109.          Balance_Pwm=Balance(Angle_Balance,Gyro_Balance);    //ƽ��PID���� Gyro_Balanceƽ����ٶȼ��ԣ�ǰ��Ϊ��������Ϊ��
  110.          Velocity_Pwm=Velocity(Encoder_Left,Encoder_Right);  //�ٶȻ�PID����  ��ס���ٶȷ�����������������С�����ʱ��Ҫ����������Ҫ���ܿ�һ��
  111.          Turn_Pwm=Turn(Gyro_Turn);


  112.          Motor_Left=Balance_Pwm+Velocity_Pwm+Turn_Pwm;       //�������ֵ������PWM
  113.          Motor_Right=Balance_Pwm+Velocity_Pwm-Turn_Pwm;      //�������ֵ������PWM
  114.                                                              //PWMֵ����ʹС��ǰ��������ʹС������

  115.          Motor_Left=Output_Limit(Motor_Left,5800,-5800);
  116.          Motor_Right=Output_Limit(Motor_Right,5800,-5800);          //PWM�޷�

  117.          if(Pick_Up(Acceleration_Z,Angle_Balance,Encoder_Left,Encoder_Right))//����Ƿ�С��������
  118.          Flag_Stop=1;                                                //���������͹رյ��

  119.          Choose(Encoder_Left,Encoder_Right);                     //ת������ѡ��С��ģʽ
  120.          if(Turn_Off(Angle_Balance,Bat_volt)==0)                  //����������쳣
  121.          Set_Pwm(Motor_Left,Motor_Right);                    //��ֵ��PWM�Ĵ���


  122.          uwScope[5]=Balance_Pwm;
  123.          uwScope[6]=Velocity_Pwm;
  124.          uwScope[7]=Turn_Pwm;

  125.          IPOMS_SnatchGraph();
  126.          TEST1_L;

  127.         OSTimeDlyHMSM(0, 0, 0, 6);
  128.     }
  129. }

  130. //---------------------------------------------------------------------------
  131. // App_task2:
  132. //---------------------------------------------------------------------------
  133. static void App_Task2(void *p_arg)
  134. {
  135.     (void)&p_arg;
  136.     while (DEF_TRUE){
  137.         LED1_TOGGLE;
  138.         oled_show();  //OLED��ʾ
  139.         APP_Show();   //�ֻ�APP��ʾ
  140.         OSTimeDlyHMSM(0, 0, 0, 100);
  141.     }
  142. }

  143. //---------------------------------------------------------------------------
  144. // App_task3:
  145. //---------------------------------------------------------------------------
  146. static void App_Task3(void *p_arg)
  147. {
  148.     (void)&p_arg;
  149.     while (DEF_TRUE){

  150.         LED2_TOGGLE;
  151.         TEST2_H;
  152.         pIPOMS_CommandIn0 = u8IPOMS_CommandBuffer0;
  153.         IPOMS_SCI();    //������λ����ʾ
  154.         TEST2_L;

  155.         OSTimeDlyHMSM(0, 0, 0, 500);
  156.     }
  157. }
  158. //=============================================================================
  159. // End of file.
  160. //=============================================================================



下面两台车子分别用了Ucos和FreeRTOS,虽然没有用示波器去观看任务优先级,但是从两台车子运行的流畅度看,应该是没什么大问题,后续如果需要增加新功能可以直接在这两个系统上去增加管理任务。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
您需要登录后才可以回帖 登录 | 注册

本版积分规则

14

主题

23

帖子

4

粉丝
快速回复 在线客服 返回列表 返回顶部