【LAUNCHXL-F280049C】②初试driverlib驱动串口-电子产品世界论坛
接上回,体验到TI 基于sysconfig图形化参数设定,以及基于driverlib库函数程序编写方式,感觉TI终于找对了方向(就像ST那样)。
如果有一两句话就能搞定的功能,谁还去研究复杂的寄存器。
本篇进入到TI C2000的核心功能,也就是关于电机控制方向的eCAP及PWM模块。
PWM,顾名思义,脉冲宽度调制,主要用于电机调速。
eCAP,可能稍微陌生一点,(Enhanced Capture)模块是TI C2000系列DSP中极具特色的外设,在应用中扮演着精密时间测量的关键角色。它不仅能实现传统捕获功能,还支持APWM模式,在电机控制、电源管理等领域应用广泛。
本例主要用于精密时间测量,也就是捕获功能。
首先,新建功能,选择ecap_ex2_capture_pwm这个官方例程:
首先看ecap_ex2_capture_pwm.c程序
在介绍环节介绍得很清楚。
ePWM3A(GPIO4)产生PWM信号————>eCAP1(GPIO16)捕获信号并计时
我们打开sysconfig图形化设置界面,打开eCAP:
仔细阅读其中设定,主要关注以下
eCAP mode:Capture(捕获)
EVENT1/2/3/4 上升沿、下降沿、上升沿、下降沿捕获。
如eCAP模块介绍:
、
下面得设置很关键:
eCAP input(捕获输入源,最关键参数):GPIO crossbar Signal-7,这是个什么东西?
crossbar(交叉开关)
可以通过X-BAR将一个外设的输出信号发送给另一个系统外设(例如将ePWM的输出发送给eCAP并进行频率测量的工作)
简单理解就是一个万能导线,可以将任意一个IO连接至任意一个模块,实现了灵活的引脚复用。
如下图所示:
回到本例:我们看到INPUTXBAR INPUT设定
明确有 INPUT:XBAR_INPUT7(呼应上面的eCAP输入设定)
INPUT Source:GPIO16,这样就将GPIO16作为eCAP输入引脚连接起来。
回到程序:
main函数就是基本的初始化。
void main(void) { // // Initialize device clock and peripherals // Device_init(); // // Disable pin locks and enable internal pullups. // Device_initGPIO(); // // Initialize PIE and clear PIE registers. Disables CPU interrupts. // Interrupt_initModule(); // // Initialize the PIE vector table with pointers to the shell Interrupt // Service Routines (ISR). // Interrupt_initVectorTable(); // // Configure GPIO4/5 as ePWM3A/3B // GPIO_setPadConfig(4,GPIO_PIN_TYPE_STD); GPIO_setPinConfig(GPIO_4_EPWM3_A); GPIO_setPadConfig(5,GPIO_PIN_TYPE_STD); GPIO_setPinConfig(GPIO_5_EPWM3_B); // // Board initialization // Configure GPIO 16 as eCAP input // Enable interrupts required for this example // Board_init(); // // Configure ePWM // initEPWM(); // // Initialize counters: // cap2Count = 0U; cap3Count = 0U; cap4Count = 0U; ecap1IntCount = 0U; ecap1PassCount = 0U; epwm3PeriodCount = 0U; // // Enable Global Interrupt (INTM) and Real time interrupt (DBGM) // EINT; ERTM; // // Loop forever. Suspend or place breakpoints to observe the buffers. // for(;;) { NOP; } }
initEPWM是PWM的设定函数,本例设定PWM3A(GPIO4为PWM输出引脚)。
// // initEPWM - Configure ePWM // void initEPWM() { // // Disable sync(Freeze clock to PWM as well) // SysCtl_disablePeripheral(SYSCTL_PERIPH_CLK_TBCLKSYNC); // // Configure ePWM // Counter runs in up-count mode. // Action qualifier will toggle output on period match // EPWM_setTimeBaseCounterMode(EPWM3_BASE, EPWM_COUNTER_MODE_UP); EPWM_setTimeBasePeriod(EPWM3_BASE, PWM3_TIMER_MIN); EPWM_setPhaseShift(EPWM3_BASE, 0U); EPWM_setActionQualifierAction(EPWM3_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_TOGGLE, EPWM_AQ_OUTPUT_ON_TIMEBASE_PERIOD); EPWM_setClockPrescaler(EPWM3_BASE, EPWM_CLOCK_DIVIDER_1, EPWM_HSCLOCK_DIVIDER_2); epwm3TimerDirection = EPWM_TIMER_UP; // // Enable sync and clock to PWM // SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_TBCLKSYNC); }
最关键的函数或者功能是INT_myECAP0_ISR 中断函数。
// // myECAP0 ISR // __interrupt void INT_myECAP0_ISR(void) { // // Get the capture counts. Each capture should be 2x the ePWM count // because of the ePWM clock divider. // cap2Count = ECAP_getEventTimeStamp(myECAP0_BASE, ECAP_EVENT_2); cap3Count = ECAP_getEventTimeStamp(myECAP0_BASE, ECAP_EVENT_3); cap4Count = ECAP_getEventTimeStamp(myECAP0_BASE, ECAP_EVENT_4); // // Compare the period value with the captured count // epwm3PeriodCount = EPWM_getTimeBasePeriod(EPWM3_BASE); if(cap2Count > ((epwm3PeriodCount *2) + 2U) || cap2Count < ((epwm3PeriodCount *2) - 2U)) { error(); } if(cap3Count > ((epwm3PeriodCount *2) + 2U) || cap3Count < ((epwm3PeriodCount *2) - 2U)) { error(); } if(cap4Count > ((epwm3PeriodCount *2) + 2U) || cap4Count < ((epwm3PeriodCount *2) - 2U)) { error(); } ecap1IntCount++; // // Keep track of the ePWM direction and adjust period accordingly to // generate a variable frequency PWM. // if(epwm3TimerDirection == EPWM_TIMER_UP) { if(epwm3PeriodCount < PWM3_TIMER_MAX) { EPWM_setTimeBasePeriod(EPWM3_BASE, ++epwm3PeriodCount); } else { epwm3TimerDirection = EPWM_TIMER_DOWN; EPWM_setTimeBasePeriod(EPWM3_BASE, ++epwm3PeriodCount); } } else { if(epwm3PeriodCount > PWM3_TIMER_MIN) { EPWM_setTimeBasePeriod(EPWM3_BASE, --epwm3PeriodCount); } else { epwm3TimerDirection = EPWM_TIMER_UP; EPWM_setTimeBasePeriod(EPWM3_BASE, ++epwm3PeriodCount); } } // // Count correct captures // ecap1PassCount++; // // Clear interrupt flags for more interrupts. // ECAP_clearInterrupt(myECAP0_BASE,ECAP_ISR_SOURCE_CAPTURE_EVENT_4); ECAP_clearGlobalInterrupt(myECAP0_BASE); // // Start eCAP // ECAP_reArm(myECAP0_BASE); // // Acknowledge the group interrupt for more interrupts. // Interrupt_clearACKGroup(INT_myECAP0_INTERRUPT_ACK_GROUP); }
通过阅读程序,大概意思应该明白了,首先是对ePWM的脉冲进行捕获CAP2/CAP3/CAP4。
然后将其(*2)跟PWM的周期进行对比。如果异常则进入error。
然后就进行PWM可变周期环节。
根据PWM波形的方向:epwm3TimerDirection 以及他的Count的最大值PWM3_TIMER_MAX、最小值PWM3_TIMER_MIN,通过
EPWM_setTimeBasePeriod(EPWM3_BASE, ++epwm3PeriodCount);
EPWM_setTimeBasePeriod(EPWM3_BASE, --epwm3PeriodCount);
从而动态调制PWM周期。
我们用一根杜邦线将GPIO4与GPIO16连接起来。另外用逻辑分析仪的钩子接在GPIO16实测PWM波形。
逻辑分析仪截图如下:
可以看到周期的明显变化。WATCH窗口可以读取精准的测值(cap2就是一个脉冲的持续时长,需要乘以1/100MHZ,算出来大约36us,跟上述逻辑分析仪截图测值相当:52us》注意:不是同一时刻)。
接下来重点介绍本篇的电机测速小试牛刀
这里介绍下我使用的硬件:
淘宝上淘来的编码电机。
电机输出轴转一圈AB相各输出1152个脉冲。这个很关键。
实际本例只使用A相。
将编码相机的A相接在GPIO16(eCAP输入),同时接逻辑分析仪查看波形。
程序在上述官方例程的基础上进行了修改。
加入了波形时间检测:将计数Conut*(1/100MHz)转化为时间.
在根据电机转一圈输出1152个脉冲,计算电机转一圈所需时间。
__interrupt void INT_myECAP0_ISR(void) { // // Get the capture counts. Each capture should be 2x the ePWM count // because of the ePWM clock divider. // cap2Count = ECAP_getEventTimeStamp(myECAP0_BASE, ECAP_EVENT_2); cap3Count = ECAP_getEventTimeStamp(myECAP0_BASE, ECAP_EVENT_3); cap4Count = ECAP_getEventTimeStamp(myECAP0_BASE, ECAP_EVENT_4); motor_cycle1=cap2Count/100; //单个编码脉冲时间 单位us motor_cycle2=cap3Count/100; motor_cycle3=cap4Count/100; motor_cycle = motor_cycle1*2*1152/1000; //电机转一圈时间 单位ms (电机输出轴转一圈输出1152个脉冲) epwm3PeriodCount = EPWM_getTimeBasePeriod(EPWM3_BASE); ecap1IntCount++; // // Keep track of the ePWM direction and adjust period accordingly to // generate a variable frequency PWM. // // // Count correct captures // ecap1PassCount++; // // Clear interrupt flags for more interrupts. // ECAP_clearInterrupt(myECAP0_BASE,ECAP_ISR_SOURCE_CAPTURE_EVENT_4); ECAP_clearGlobalInterrupt(myECAP0_BASE); // // Start eCAP // ECAP_reArm(myECAP0_BASE); // // Acknowledge the group interrupt for more interrupts. // Interrupt_clearACKGroup(INT_myECAP0_INTERRUPT_ACK_GROUP); }
运行如下:
也就是电机转一圈2.1秒,跟我用秒表计时几乎一样,实验成功。
再用逻辑分析仪检测下波形。脉冲时间893us,跟上图的motor_cycle1完全一致。
测试成功!