5. 调试上位机搭建 在两轮平衡车调试过程中,为确保系统稳定与响应精准,需对姿态角度进行滤波处理以抑制噪声干扰,同时通过上位机实时监测与调整各环PID(比例、积分、微分)控制参数,观察其对应波形变化,从而优化控制性能,实现平衡车的精准控制与动态调参。这就必须要用到可以监控数据的上位机,通过实时显示波形数据,反馈调整控制参数。上位机平台选用C#的WindowsForms,之前看视频学习的时候,老师讲过WindowsForms程序是基于事件触发的,能理解这句话的意思,去做WindowsForms上位机就很简单。这里我主要分享基于CAN通信和Uart通信的两个画波形的上位机。 (1) CanScope上位机设计 买CAN盒都会附带二次开发的源文件,直接在源文件上面打开即可进行二次开发。选择上位机开发环境平台C#,打开工程文件。 画波形,选用chart插件,点击工具箱,输入chart,即可找到该插件。 双击进入编程界面,对接收到的can口数据进行处理显示在chart 最终上位机界面如下: DSP下位机初始化CAN口,输出一个正弦波和余弦波 - /******************************************************************************
- * FILENAME : Bsp_Can.c
- *
- * PURPOSE : Can Initialization & Support Functions.
- *
- * Author: 电笔小新 Created on: 2025年9月12日
- ******************************************************************************/
- #include "User_Include.h"
- #include "Bsp_Can.h"
- //=============================================================================
- // * Variables & Function_Defines
- //=============================================================================
- Uint16 CAN_command0 = 0;
- Uint16 CAN_command1 = 0;
- CANFRAME CAN_TxBuf;
- CANFRAME CAN_RxBuf;
- int16 SinTabCnt = 0;
- int16 CosTabCnt = 100;
- const int16 SinCosTab[400]={
- 0, 64, 129, 193, 257, 321, 385, 449, 513, 577,
- 641, 704, 768, 831, 894, 956, 1019, 1081, 1143, 1204,
- 1266, 1327, 1387, 1448, 1508, 1567, 1627, 1686, 1744, 1802,
- 1860, 1917, 1973, 2029, 2085, 2140, 2195, 2249, 2302, 2355,
- 2408, 2459, 2510, 2561, 2611, 2660, 2709, 2757, 2804, 2850,
- 2896, 2941, 2986, 3030, 3072, 3115, 3156, 3197, 3236, 3276,
- 3314, 3351, 3388, 3423, 3458, 3492, 3526, 3558, 3589, 3620,
- 3650, 3678, 3706, 3733, 3759, 3784, 3808, 3832, 3854, 3875,
- 3896, 3915, 3933, 3951, 3967, 3983, 3997, 4011, 4023, 4035,
- 4046, 4055, 4064, 4071, 4078, 4083, 4088, 4091, 4094, 4095,
- 4096, 4095, 4094, 4091, 4088, 4083, 4078, 4071, 4064, 4055,
- 4046, 4035, 4023, 4011, 3997, 3983, 3967, 3951, 3933, 3915,
- 3896, 3875, 3854, 3832, 3808, 3784, 3759, 3733, 3706, 3678,
- 3650, 3620, 3589, 3558, 3526, 3492, 3458, 3423, 3388, 3351,
- 3314, 3276, 3236, 3197, 3156, 3115, 3072, 3030, 2986, 2941,
- 2896, 2850, 2804, 2757, 2709, 2660, 2611, 2561, 2510, 2459,
- 2408, 2355, 2302, 2249, 2195, 2140, 2085, 2029, 1973, 1917,
- 1860, 1802, 1744, 1686, 1627, 1567, 1508, 1448, 1387, 1327,
- 1266, 1204, 1143, 1081, 1019, 956, 894, 831, 768, 704,
- 641, 577, 513, 449, 385, 321, 257, 193, 129, 64,
- 0, -64, -129, -193, -257, -321, -385, -449, -513, -577,
- -641, -704, -768, -831, -894, -956,-1019,-1081,-1143,-1204,
- -1266,-1327,-1387,-1448,-1508,-1567,-1627,-1686,-1744,-1802,
- -1860,-1917,-1973,-2029,-2085,-2140,-2195,-2249,-2302,-2355,
- -2408,-2459,-2510,-2561,-2611,-2660,-2709,-2757,-2804,-2850,
- -2896,-2941,-2986,-3030,-3072,-3115,-3156,-3197,-3236,-3276,
- -3314,-3351,-3388,-3423,-3458,-3492,-3526,-3558,-3589,-3620,
- -3650,-3678,-3706,-3733,-3759,-3784,-3808,-3832,-3854,-3875,
- -3896,-3915,-3933,-3951,-3967,-3983,-3997,-4011,-4023,-4035,
- -4046,-4055,-4064,-4071,-4078,-4083,-4088,-4091,-4094,-4095,
- -4096,-4095,-4094,-4091,-4088,-4083,-4078,-4071,-4064,-4055,
- -4046,-4035,-4023,-4011,-3997,-3983,-3967,-3951,-3933,-3915,
- -3896,-3875,-3854,-3832,-3808,-3784,-3759,-3733,-3706,-3678,
- -3650,-3620,-3589,-3558,-3526,-3492,-3458,-3423,-3388,-3351,
- -3314,-3276,-3236,-3197,-3156,-3115,-3072,-3030,-2986,-2941,
- -2896,-2850,-2804,-2757,-2709,-2660,-2611,-2561,-2510,-2459,
- -2408,-2355,-2302,-2249,-2195,-2140,-2085,-2029,-1973,-1917,
- -1860,-1802,-1744,-1686,-1627,-1567,-1508,-1448,-1387,-1327,
- -1266,-1204,-1143,-1081,-1019, -956, -894, -831, -768, -704,
- -641, -577, -513, -449, -385, -321, -257, -193, -129, -64,
- };
- //=============================================================================
- // * FUNCTION: SetGpio_Cana()
- // *
- // * PURPOSE : Initializes the GPIOs for CANa.
- //=============================================================================
- //CAN的GPIO初始化
- //这里采用GPIO30 GPIO31
- //--------------------------------------------------------------------
- void SetGpio_Cana(void)
- {
- EALLOW;
- GpioCtrlRegs.GPAPUD.bit.GPIO30 = 0; // Enable pull-up for GPIO30 (CANRXA)
- GpioCtrlRegs.GPAPUD.bit.GPIO31 = 0; // Enable pull-up for GPIO31 (CANTXA)
- GpioCtrlRegs.GPAQSEL2.bit.GPIO30 = 3; // Asynch qual for GPIO30 (CANRXA)
- //IO30 IO21配置为CAN外设引脚
- GpioCtrlRegs.GPAMUX2.bit.GPIO30 = 1; // Configure GPIO30 for CANRXA operation
- GpioCtrlRegs.GPAMUX2.bit.GPIO31 = 1; // Configure GPIO31 for CANTXA operation
- EDIS;
- }
- //=============================================================================
- // * FUNCTION: SetERegs_Cana()
- // *
- // * PURPOSE : Initializes the Registers for CANa.
- //=============================================================================
- void SetERegs_Cana(void) // Initialize eCAN-A module
- {
- /* Create a shadow register structure for the CAN control registers. This is
- needed, since only 32-bit access is allowed to these registers. 16-bit access
- to these registers could potentially corrupt the register contents or return
- false data. */
- struct ECAN_REGS ECanaShadow;
- EALLOW; // EALLOW enables access to protected bits
- /* Configure eCAN RX and TX pins for CAN operation using eCAN regs*/
- ECanaShadow.CANTIOC.all = ECanaRegs.CANTIOC.all;
- ECanaShadow.CANTIOC.bit.TXFUNC = 1;
- ECanaRegs.CANTIOC.all = ECanaShadow.CANTIOC.all;
- ECanaShadow.CANRIOC.all = ECanaRegs.CANRIOC.all;
- ECanaShadow.CANRIOC.bit.RXFUNC = 1;
- ECanaRegs.CANRIOC.all = ECanaShadow.CANRIOC.all;
- /* Configure eCAN for HECC mode - (reqd to access mailboxes 16 thru 31) */
- // HECC mode also enables time-stamping feature
- ECanaShadow.CANMC.all = ECanaRegs.CANMC.all;
- ECanaShadow.CANMC.bit.SCB = 1; //P35 Select eCAN mode.
- ECanaRegs.CANMC.all = ECanaShadow.CANMC.all;
- /* Initialize all bits of 'Message Control Register' to zero */
- // Some bits of MSGCTRL register come up in an unknown state. For proper operation,
- // all bits (including reserved bits) of MSGCTRL must be initialized to zero
- ECanaMboxes.MBOX0.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX1.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX2.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX3.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX4.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX5.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX6.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX7.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX8.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX9.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX10.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX11.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX12.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX13.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX14.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX15.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX16.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX17.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX18.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX19.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX20.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX21.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX22.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX23.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX24.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX25.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX26.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX27.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX28.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX29.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX30.MSGCTRL.all = 0x00000000;
- ECanaMboxes.MBOX31.MSGCTRL.all = 0x00000000;
- // TAn, RMPn, GIFn bits are all zero upon reset and are cleared again
- // as a matter of precaution.
- ECanaRegs.CANTA.all = 0xFFFFFFFF; /* Clear all TAn bits */
- ECanaRegs.CANRMP.all = 0xFFFFFFFF; /* Clear all RMPn bits */
- ECanaRegs.CANGIF0.all = 0xFFFFFFFF; /* Clear all interrupt flag bits */
- ECanaRegs.CANGIF1.all = 0xFFFFFFFF;
- /* Configure bit timing parameters for eCANA*/
- ECanaShadow.CANMC.all = ECanaRegs.CANMC.all;
- ECanaShadow.CANMC.bit.CCR = 1 ; // Set CCR = 1
- ECanaRegs.CANMC.all = ECanaShadow.CANMC.all;
- // Wait until the CPU has been granted permission to change the configuration registers
- do
- {
- ECanaShadow.CANES.all = ECanaRegs.CANES.all;
- } while(ECanaShadow.CANES.bit.CCE != 1 ); // Wait for CCE bit to be set..
- ECanaShadow.CANBTC.all = 0;
- //-------------------------自定义的配置------------------------------------------
- //P47
- /* The following block is only for 60 MHz SYSCLKOUT. (30 MHz CAN module clock Bit rate = 1 Mbps
- See Note at end of file. */
- //rate Bit =(SYSCLKOUT/2)/(BRP * Bit-time )
- //BRP = BRPreg+1
- //Bit-time = (TSEG1reg + 1) + (TSEG2reg+ 1) + 1
- //配置TQ 和BRP ,
- //方案一 TQ为10, 则TSEG1REG = 6, BRP = 5 ,为500K
- //方案二,TQ为12,则TSEG1REG = 8, BRP = 4 ,为500K
- //500KHz
- ECanaShadow.CANBTC.bit.BRPREG = 5;
- ECanaShadow.CANBTC.bit.TSEG2REG = 1;
- ECanaShadow.CANBTC.bit.TSEG1REG = 6;
- ECanaShadow.CANBTC.bit.SAM = 1;
- ECanaRegs.CANBTC.all = ECanaShadow.CANBTC.all;
- ECanaShadow.CANMC.all = ECanaRegs.CANMC.all;
- ECanaShadow.CANMC.bit.CCR = 0; // Set CCR = 0
- ECanaShadow.CANMC.bit.STM = 0; // 0: normal mode/1: self-test mode
- ECanaRegs.CANMC.all = ECanaShadow.CANMC.all;
- // Wait until the CPU no longer has permission to change the configuration registers
- do
- {
- ECanaShadow.CANES.all = ECanaRegs.CANES.all;
- } while(ECanaShadow.CANES.bit.CCE != 0 ); // Wait for CCE bit to be cleared..
- /* Disable all Mailboxes */
- ECanaRegs.CANME.all = 0; // Required before writing the MSGIDs
- EDIS;
- }
- /**********************************************************************
- * FUNCION : SetMailbox_Cana()
- * PURPOSE : Initializes the Mailboxes for Can.
- //-----------------------------------------------
- //配置发送、接收邮箱等操作
- //数据区8个长度字节
- //发送邮箱 1到6
- //接收邮箱 16, ID为0x0A
- //-----------------------------------------------
- **********************************************************************/
- void SetMailbox_Cana(void)
- {
- struct ECAN_REGS ECanaShadow;
- EALLOW;
- ECanaShadow.CANGAM.all = ECanaRegs.CANGAM.all;
- ECanaShadow.CANGAM.bit.AMI=1; // Standard and extended frames can be received.
- ECanaRegs.CANGAM.all = ECanaShadow.CANGAM.all;
- EDIS;
- //P59
- //In standard identifier mode,if the IDE bit(MSGID.31) =0,the message identifier is storedin bits ID.28:18.
- //IDE =0:The RECEIVED message had a standard identifier
- //Auto answer mode bit. AAM =0 ; Normal transmit mode
- //AME = 1 ;The corresponding acceptance mask is used
- //MSGID.31=IDE; MSGID.30=AME; MSGID.29=AAM
- // Mailboxs can be written to 16-bits or 32-bits at a time
- // Mailboxes can be written to 16-bits or 32-bits at a time
- // Write to the MSGID field of TRANSMIT mailboxes MBOX0 - 15
- ECanaMboxes.MBOX0.MSGID.all = ( (TXCanId0_Std|0x10000000)<<18); // stand Identifier
- ECanaMboxes.MBOX1.MSGID.all = ( (TXCanId1_Std|0x10000000)<<18); // stand Identifier
- // Write to the MSGID field of RECEIVE mailboxes MBOX16 - 31
- ECanaMboxes.MBOX16.MSGID.all = ((RXCanId0_Std|0x10000000)<<18); // stand Identifier
- ECanaMboxes.MBOX17.MSGID.all = ((RXCanId1_Std|0x10000000)<<18); // stand Identifier
- // Configure Mailboxes 0-15 as Tx, 16-31 as Rx
- // Since this write is to the entire register (instead of a bit
- // field) a shadow register is not required.
- ECanaRegs.CANMD.all = 0xFFFF0000;
- // Specify that 8 bits will be sent/received //8字节数据
- ECanaMboxes.MBOX0.MSGCTRL.bit.DLC = 8;
- ECanaMboxes.MBOX1.MSGCTRL.bit.DLC = 8;
- ////////////////////////////////////////////////////////////////////////////////////
- EALLOW;
- ECanaShadow.CANMC.all = ECanaRegs.CANMC.all;
- ECanaShadow.CANMC.bit.DBO = 1;
- ECanaShadow.CANMC.bit.SCB = 1;//Select eCAN mode
- ECanaRegs.CANMC.all = ECanaShadow.CANMC.all;
- // Configure Mailboxes 0-15 as Tx, 16-31 as Rx
- ECanaRegs.CANMD.all = 0xFFFF0000;
- // Since this write is to the entire register (instead of a bit
- // field) a shadow register is not required.
- ECanaRegs.CANME.all = 0x00030003;
- EDIS;
- }
- //=============================================================================
- // * FUNCTION: SetInterrupt_Cana()
- // *
- // * PURPOSE : Initializes the ISR for CANa.
- //=============================================================================
- void SetInterrupt_Cana(void)
- {
- /* Create a shadow register structure for the CAN control registers. This is
- needed, since only 32-bit access is allowed to these registers. 16-bit access
- to these registers could potentially corrupt the register contents or return
- false data. */
- struct ECAN_REGS ECanaShadow;
- EALLOW; // EALLOW enables access to protected bits
- ECanaShadow.CANMIL.all = ECanaRegs.CANMIL.all;
- ECanaShadow.CANMIL.all = 0xFFFFFFFF ; //P76, mailbox interrupts to level 1
- ECanaRegs.CANMIL.all = ECanaShadow.CANMIL.all;
- ECanaShadow.CANMIM.all = ECanaRegs.CANMIM.all;
- ECanaShadow.CANMIM.all =0x00010000 ; //P48,相应的邮箱中断使能位 Mailbox interrupt is enabled.
- ECanaRegs.CANMIM.all = ECanaShadow.CANMIM.all;
- //1-32号邮箱中断在中断线0上产生
- ECanaShadow.CANMIL.all = ECanaRegs.CANMIL.all;
- ECanaShadow.CANMIL.all = 0;
- ECanaRegs.CANMIL.all = ECanaShadow.CANMIL.all;
- //------------中断配置步骤-----1
- //中断线0使能
- ECanaShadow.CANGIM.all = ECanaRegs.CANGIM.all;
- ECanaShadow.CANGIM.bit.I0EN = 1;
- // ECanaShadow.CANGIM.bit.I1EN = 1;
- ECanaRegs.CANGIM.all = ECanaShadow.CANGIM.all;
- // TAn, RMPn, GIFn bits are all zero upon reset and are cleared again
- // as a matter of precaution.
- ECanaRegs.CANTA.all = 0xFFFFFFFF; /* Clear all TAn bits */
- ECanaRegs.CANRMP.all = 0xFFFFFFFF; /* Clear all RMPn bits */
- ECanaRegs.CANGIF0.all = 0xFFFFFFFF; /* Clear all interrupt flag bits */
- ECanaRegs.CANGIF1.all = 0xFFFFFFFF;
- //------------中断配置步骤-----3
- // Enable CAN in PIE
- PieCtrlRegs.PIEIER9.bit.INTx5 = 1; // Enable INT 9.5 in the PIE
- // PieCtrlRegs.PIEIER9.bit.INTx6 = 1; // Enable INT 9.6 in the PIE
- //------------中断配置步骤-----4
- IER |= M_INT9; // Enable CPU Interrupt 9
- EINT;
- ERTM;
- EDIS;
- }
- //=============================================================================
- // * FUNCTION: InitCana()
- // *
- // * PURPOSE : Initializes the Enhanced Can modules.
- //=============================================================================
- void InitCana(void)
- {
- SetGpio_Cana();
- SetERegs_Cana();
- SetMailbox_Cana();
- SetInterrupt_Cana();
- }
- //=============================================================================
- // * FUNCTION: sCanHdRead()
- // *
- // * PURPOSE : Read Can Data From Registers And Save Into Buffer.
- //=============================================================================
- void sCanHdRead(const Uint8 ubMailBox,CANFRAME *pdata)
- {
- volatile struct MBOX *pMailbox;
- pMailbox = &ECanaMboxes.MBOX0 + ubMailBox;
- pdata->CanId.all = pMailbox->MSGID.all;
- pdata->CanData0 = pMailbox->MDL.word.LOW_WORD;
- pdata->CanData1 = pMailbox->MDL.word.HI_WORD;
- pdata->CanData2 = pMailbox->MDH.word.LOW_WORD;
- pdata->CanData3 = pMailbox->MDH.word.HI_WORD;
- }
- //=============================================================================
- // * FUNCTION: sCanHdSend()
- // *
- // * PURPOSE : Write Can Data Into Registers And Send.
- //=============================================================================
- void sCanHdSend(const Uint8 ubMailBox,const CANFRAME *pdata)
- {
- struct ECAN_REGS ECanShadow;
- volatile struct MBOX *pMailbox;
- pMailbox = &ECanaMboxes.MBOX0 + ubMailBox;
- /*step3: Load the message to the MSGID Registers of the object*/
- pMailbox->MSGID.all= pdata->CanId.all;
- /*step4: configure the data length*/
- pMailbox->MSGCTRL.bit.DLC = 8;
- /*step5: Write the message data to the mail box data field*/
- pMailbox->MDL.word.LOW_WORD = pdata->CanData0;
- pMailbox->MDL.word.HI_WORD = pdata->CanData1;
- pMailbox->MDH.word.LOW_WORD = pdata->CanData2;
- pMailbox->MDH.word.HI_WORD = pdata->CanData3;
- /* Enable transmit mailbox*/
- ECanShadow.CANME.all = ECanaRegs.CANME.all;
- ECanShadow.CANME.all |= ((Uint32)1 << ubMailBox);
- ECanaRegs.CANME.all = ECanShadow.CANME.all;
- ECanShadow.CANTRS.all=0;
- ECanShadow.CANTRS.all|= ((Uint32)1 << ubMailBox);
- ECanaRegs.CANTRS.all= ECanShadow.CANTRS.all;
- ECanShadow.CANTA.all = 0;
- ECanShadow.CANTA.all |= ((Uint32)1 << ubMailBox); // Clear all TAn
- ECanaRegs.CANTA.all=ECanShadow.CANTA.all;
- }
- //---------------------------------------------------------------------------
- // Can_Task:
- //---------------------------------------------------------------------------
- void Can_Task(void)
- {
- SinTabCnt++;
- if(SinTabCnt>399)
- {
- SinTabCnt=0;
- }
- CosTabCnt++;
- if(CosTabCnt>399)
- {
- CosTabCnt=0;
- }
- CAN_TxBuf.CanId.all = ((TXCanId0_Std|0x10000000)<<18); // stand Identifier
- CAN_TxBuf.CanData0 = SinCosTab[SinTabCnt];
- CAN_TxBuf.CanData1 = SinCosTab[CosTabCnt];
- CAN_TxBuf.CanData2 = 0x5432;
- CAN_TxBuf.CanData3 = 0x9876;
- sCanHdSend(0,&CAN_TxBuf);
- }
- //---------------------------------------------------------------------------
- // INT_ISR for Ecana_RX
- //---------------------------------------------------------------------------
- //// INT9.5
- interrupt void ECAN0INTA_ISR(void) // eCAN-A
- {
- struct ECAN_REGS ECanaShadow;
- // Insert ISR Code here
- ECanaShadow.CANRMP.all=ECanaRegs.CANRMP.all;
- if(ECanaShadow.CANRMP.bit.RMP16==1)
- {
- ECanaShadow.CANRMP.bit.RMP16=1;
- ECanaRegs.CANRMP.all=ECanaShadow.CANRMP.all;
- sCanHdRead(16,&CAN_RxBuf);
- }
- PieCtrlRegs.PIEACK.all = PIEACK_GROUP9; // Must acknowledge the PIE group
- }
- //=============================================================================
- // End of file.
- //=============================================================================
下位机跟上位机波形传输联调如下:
上位机安装包和CAN驱动代码:
|