Aurix英飞凌TC334芯片有刷电机控制
大家好,我是左工,在上一节Aurix英飞凌TC334芯片ADC采样模块配置中我们已经运用ADC模块实现了测试板的对模拟量的采集。今天我们来聊聊嵌入式控制中非常非常关键的捕获比较模块(Capture/Compare Unit,CCU)模块。
前言
CCU模块是集成在定时器模块中的核心外设,核心用于精确测量外部信号(捕获)和生成 PWM(比较)。捕获模式(Capture Mode)的原理是:检测外部信号的上升沿 / 下降沿 / 双边沿,在事件发生时锁存定时器计数值(时间戳),主要用于测量脉宽、频率、周期(如编码器、传感器信号)。比较模式(Compare Mode)工作原理是:定时器计数值与预设比较值匹配时,触发中断、翻转输出或生成 PWM,用于精确时序控制与功率器件驱动。我们今天主要使用CCU模块的比较模式调制不同占空比的PWM,从而实现对有刷电机的控制。
一、代码编写
利用文章“搭建英飞凌Aurix系列TC334芯片开源免费开发工具链”中所描述的方法创建一个名为“5_MotorControl”的工程,如下图所示。
工程创建完成后,我们会在导引栏中看见如下文件和文件夹。其中文件“Cpu0_Main.c”就是我们本次需要操作的文件。
我们双击打开该文件,找到主函数“core0_main”,TC334运行后,将首先运行该函数。我们可以看见该函数里面有一个while循环。我们将把代码写在这个while循环附近。
用如下代码替换上图中被红色框标出的while循环。
GPTimer_init();
CAN_init();
Motor_init();
uint8 tx_buf[8]; // 发送缓存
while(1)
{
if(TimeCount>1)
{
TimeCount = 0;
Motor_ctl(rx_buf[0],10);//电机控制
tx_buf[0] = rx_buf[0];
CAN_send(0x123,tx_buf,8); // 报文发送
}
}
添加完成后,我们本次实验对主函数的所有操作就结束了。下面我们来添加一些必要头文件。
#include "IfxGpt12.h"
#include "IfxSrc.h"
#include "IfxCan_Can.h"
#include "IfxCcu6.h" //CCU6模块库头文件
函数“GPTimer_init()”的定义可以从文章Aurix英飞凌TC334芯片GPT模块定时器配置中查看。函数“CAN_init()”的定义可以从文章Aurix英飞凌TC334芯片CAN模块配置中查看。我们在主函数“core0_main()”的上面添加添加上“Motor_init()”和“Motor_ctl()”函数的具体定义以及CCU模块设定需要的变量。
/************************电机控制定义************************/
/*直流有刷电机初始化函数*/
void Motor_init(void)
{
/*端口部分*/
IfxPort_setPinMode(&MODULE_P00,2,IfxPort_Mode_outputPushPullAlt7); //将P00.2复用为输出7,对应CCU61的COUT60
IfxPort_setPinMode(&MODULE_P00,4,IfxPort_Mode_outputPushPullAlt7); //将P00.4复用为输出7,对应CCU61的COUT61
IfxPort_setPinMode(&MODULE_P00,6,IfxPort_Mode_outputPushPullAlt7); //将P00.6复用为输出7,对应CCU61的COUT62
IfxPort_setPinMode(&MODULE_P00,1,IfxPort_Mode_outputPushPullAlt7); //将P00.1复用为输出7,对应CCU61的CC60
IfxPort_setPinMode(&MODULE_P00,3,IfxPort_Mode_outputPushPullAlt7); //将P00.3复用为输出7,对应CCU61的CC61
IfxPort_setPinMode(&MODULE_P00,5,IfxPort_Mode_outputPushPullAlt7); //将P00.5复用为输出7,对应CCU61的CC62
IfxPort_setPinMode(&MODULE_P00,0,IfxPort_Mode_outputPushPullGeneral); //将P00.0设置为普通输出,对应于电机驱动芯片的使能控制
IfxPort_setPinState(&MODULE_P00,0,IfxPort_State_high); //使能电机驱动芯片
/*CCU6部分*/
IfxCcu6_enableModule(&MODULE_CCU61); //使能CCU61模块
/*配置T12定时器的时基参数*/
IfxCcu6_enableTimer(&MODULE_CCU61,IfxCcu6_TimerId_t12); //使能CCU6的T12定时器,以便对它进行操作
IfxCcu6_setInputClockFrequency(&MODULE_CCU61,IfxCcu6_TimerId_t12,IfxCcu6_TimerInputClock_fcc6By4); //CCU6使用SPB总线时钟作为时钟源,因此其模块频率为100MHz,经4分频得到25MHz的T12计数频率
IfxCcu6_setT12CountMode(&MODULE_CCU61,IfxCcu6_T12CountMode_centerAligned); //将T12的计数模式设为中心对齐模式
IfxCcu6_setT12PeriodValue(&MODULE_CCU61,1250-1); //周期值决定T12的输出频率,也即PWM的频率,设定为1250分频,则输出频率=25M/1.25K=20KHz,考虑到中心对齐的计数模式,那么输出的PWM就具有2分频特性,所以最终的输出频率为10KHz
IfxCcu6_setT12CounterValue(&MODULE_CCU61,0); //计数器先初始化为0
/*配置T12通道模式*/
IfxCcu6_setT12ChannelMode(&MODULE_CCU61,IfxCcu6_T12Channel_0,IfxCcu6_T12ChannelMode_compareMode); //CC60通道设为比较模式,产生PWM波
IfxCcu6_setT12CompareValue(&MODULE_CCU61,IfxCcu6_T12Channel_0,0); //比较值先设为0,产生0%的占空比
IfxCcu6_setT12ChannelMode(&MODULE_CCU61,IfxCcu6_T12Channel_1,IfxCcu6_T12ChannelMode_compareMode); //CC61通道设为比较模式,产生PWM波
IfxCcu6_setT12CompareValue(&MODULE_CCU61,IfxCcu6_T12Channel_1,0); //比较值先设为0,产生0%的占空比
IfxCcu6_setT12ChannelMode(&MODULE_CCU61,IfxCcu6_T12Channel_2,IfxCcu6_T12ChannelMode_compareMode); //CC62通道设为比较模式,产生PWM波
IfxCcu6_setT12CompareValue(&MODULE_CCU61,IfxCcu6_T12Channel_2,0); //比较值先设为0,产生0%的占空比
/*配置CC60通道参数*/
IfxCcu6_setOutputPassiveLevel(&MODULE_CCU61,IfxCcu6_ChannelOut_cc0,FALSE); //通道的默认电平为低电平
IfxCcu6_setOutputPassiveState(&MODULE_CCU61,IfxCcu6_ChannelOut_cc0,TRUE); //默认电平激活条件为计数器大于比较值时激活,大于比较值输出低电平,小于等于比较值输出高电平
IfxCcu6_disableModulationOutput(&MODULE_CCU61,IfxCcu6_TimerId_t12,IfxCcu6_ChannelOut_cc0); //先禁止CC60的通道输出
IfxCcu6_enableShadowTransfer(&MODULE_CCU61,TRUE,FALSE); //启动一次阴影传输以更新到实际寄存器
/*配置COUT60通道参数*/
IfxCcu6_setOutputPassiveLevel(&MODULE_CCU61,IfxCcu6_ChannelOut_cout0,TRUE); //通道的默认电平为高电平
IfxCcu6_setOutputPassiveState(&MODULE_CCU61,IfxCcu6_ChannelOut_cout0,TRUE); //默认电平激活条件为计数器大于比较值时激活,大于比较值输出低电平,小于等于比较值输出高电平
IfxCcu6_disableModulationOutput(&MODULE_CCU61,IfxCcu6_TimerId_t12,IfxCcu6_ChannelOut_cout0); //先禁止COUT0的通道输出
IfxCcu6_enableShadowTransfer(&MODULE_CCU61,TRUE,FALSE); //启动一次阴影传输以更新到实际寄存器
/*配置CC61通道参数*/
IfxCcu6_setOutputPassiveLevel(&MODULE_CCU61,IfxCcu6_ChannelOut_cc1,FALSE); //通道的默认电平为低电平
IfxCcu6_setOutputPassiveState(&MODULE_CCU61,IfxCcu6_ChannelOut_cc1,TRUE); //的默认电平激活条件为计数器大于比较值时激活,大于比较值输出低电平,小于等于比较值输出高电平
IfxCcu6_disableModulationOutput(&MODULE_CCU61,IfxCcu6_TimerId_t12,IfxCcu6_ChannelOut_cc1); //先禁止CC61的通道输出
IfxCcu6_enableShadowTransfer(&MODULE_CCU61,TRUE,FALSE); //启动一次阴影传输以更新到实际寄存器
/*配置COUT61通道参数*/
IfxCcu6_setOutputPassiveLevel(&MODULE_CCU61,IfxCcu6_ChannelOut_cout1,TRUE); //通道的默认电平为高电平
IfxCcu6_setOutputPassiveState(&MODULE_CCU61,IfxCcu6_ChannelOut_cout1,TRUE); //默认电平激活条件为计数器大于比较值时激活,大于比较值输出低电平,小于等于比较值输出高电平
IfxCcu6_disableModulationOutput(&MODULE_CCU61,IfxCcu6_TimerId_t12,IfxCcu6_ChannelOut_cout1); //先禁止COUT1的通道输出
IfxCcu6_enableShadowTransfer(&MODULE_CCU61,TRUE,FALSE); //启动一次阴影传输以更新到实际寄存器
/*配置CC62通道参数*/
IfxCcu6_setOutputPassiveLevel(&MODULE_CCU61,IfxCcu6_ChannelOut_cc2,FALSE); //通道的默认电平为低电平
IfxCcu6_setOutputPassiveState(&MODULE_CCU61,IfxCcu6_ChannelOut_cc2,TRUE); //的默认电平激活条件为计数器大于比较值时激活,大于比较值输出低电平,小于等于比较值输出高电平
IfxCcu6_disableModulationOutput(&MODULE_CCU61,IfxCcu6_TimerId_t12,IfxCcu6_ChannelOut_cc2); //先禁止CC62的通道输出
IfxCcu6_enableShadowTransfer(&MODULE_CCU61,TRUE,FALSE); //启动一次阴影传输以更新到实际寄存器
/*配置COUT62通道参数*/
IfxCcu6_setOutputPassiveLevel(&MODULE_CCU61,IfxCcu6_ChannelOut_cout2,TRUE); //通道的默认电平为高电平
IfxCcu6_setOutputPassiveState(&MODULE_CCU61,IfxCcu6_ChannelOut_cout2,TRUE); //默认电平激活条件为计数器大于比较值时激活,大于比较值输出低电平,小于等于比较值输出高电平
IfxCcu6_disableModulationOutput(&MODULE_CCU61,IfxCcu6_TimerId_t12,IfxCcu6_ChannelOut_cout2); //先禁止COUT2的通道输出
IfxCcu6_enableShadowTransfer(&MODULE_CCU61,TRUE,FALSE); //启动一次阴影传输以更新到实际寄存器
/*启动定时器*/
IfxCcu6_startTimer(&MODULE_CCU61,TRUE,FALSE);
}
/*直流有刷电机控制函数,控制电机的旋转方向及停止,占空比调节动作快慢,占空比范围0-100,对应0%-100%,调节精度1%*/
void Motor_ctl(uint8 ctl_dir,uint8 duty)
{
uint16 period_val = 0;
uint16 cmp_val = 0;
uint16 cc60_cmp_val = 0; //左半桥比较值
uint16 cc61_cmp_val = 0; //中半桥比较值
uint16 cc62_cmp_val = 0; //右半桥比较直
/*计算PWM*/
period_val = MODULE_CCU61.T12PR.B.T12PV + 1; //获取T12的周期寄存器值
cmp_val = (uint16)(((float)duty/100.0f) * (float)period_val);
/*设置各通道输出*/
switch(ctl_dir)
{
case 1: //电机正转,COUT60控制左上桥臂,CC61控制右下桥臂导通,使电机电压左正右负,电流由左向右
{
MODULE_CCU61.MODCTR.B.T12MODEN = 0x09; //左下管导通,中上管导通,右半桥关闭
cc60_cmp_val = cmp_val; //左半桥下管PWM调制
cc61_cmp_val = period_val; //中半桥上管常通
cc62_cmp_val = 0; //右半桥无PWM输出
break;
}
case 2: //电机反转,CC60控制左下桥臂,COUT61控制右上桥臂导通,使电机电压左负右正,电流由右向左
{
MODULE_CCU61.MODCTR.B.T12MODEN = 0x06; //左上管导通,中下管导通,右半桥关闭
cc60_cmp_val = period_val; //左半桥上管常通
cc61_cmp_val = cmp_val; //中半桥下管PWM调制
cc62_cmp_val = 0; //右半桥无PWM输出
break;
}
default://停止,下两个桥臂导通,利用电机的电感特性阻碍电机运动,使电机停止
{
MODULE_CCU61.MODCTR.B.T12MODEN = 0x15; //开启所有下管,关闭所有上管
cc60_cmp_val = period_val; //左半桥PWM输出100%,最大能耗制动
cc61_cmp_val = period_val; //中半桥PWM输出100%,最大能耗制动
cc62_cmp_val = period_val; //右半桥PWM输出100%,最大能耗制动
break;
}
}
/*写入PWM比较值*/
IfxCcu6_setT12CompareValue(&MODULE_CCU61,IfxCcu6_T12Channel_0,cc60_cmp_val); //写入CC60通道的新比较值
IfxCcu6_setT12CompareValue(&MODULE_CCU61,IfxCcu6_T12Channel_1,cc61_cmp_val); //写入CC61通道的新比较值
IfxCcu6_setT12CompareValue(&MODULE_CCU61,IfxCcu6_T12Channel_2,cc62_cmp_val); //写入CC62通道的新比较值
/*更新实际寄存器*/
IfxCcu6_enableShadowTransfer(&MODULE_CCU61,1,0); //启动一次阴影传输以更新到实际寄存器
}
现在所有代码编写完毕,整个Cpu0_Main.c的定义如下所示。
#include "Ifx_Types.h"
#include "IfxCpu.h"
#include "IfxScuWdt.h"
#include "IfxGpt12.h"
#include "IfxSrc.h"
#include "IfxCan_Can.h"
#include "IfxCcu6.h" //CCU6模块库头文件,用于配置CCU6模块
IFX_ALIGN(4) IfxCpu_syncEvent g_cpuSyncEvent = 0;
/************************GPT定时模块定义************************/
uint8 TimeCount = 0;
void GPTimer_init(void)
{
/*端口初始化*/
IfxPort_setPinMode(&MODULE_P02,5,IfxPort_Mode_outputPushPullGeneral ); //P02.5配置为输入模式
/*GPT120配置*/
IfxGpt12_enableModule(&MODULE_GPT120); //使能GPT120模块
IfxGpt12_setGpt1BlockPrescaler(&MODULE_GPT120,IfxGpt12_Gpt1BlockPrescaler_8); //GPT模块预分频,使用SPB总线时钟为100MHz
/*配置T3定时器用于产生周期性中断*/
IfxGpt12_T3_setMode(&MODULE_GPT120,IfxGpt12_Mode_timer); //设置T3定时器的模式为定时器模式
IfxGpt12_T3_setDirectionSource(&MODULE_GPT120,IfxGpt12_TimerDirectionSource_internal); //内部控制计数方向
IfxGpt12_T3_setTimerDirection(&MODULE_GPT120,IfxGpt12_TimerDirection_up); //T3的计数方向为向上计数
IfxGpt12_T3_setTimerPrescaler(&MODULE_GPT120,IfxGpt12_TimerInputPrescaler_128); //定时器计数时钟为模块时钟的128分频
IfxGpt12_T3_setTimerValue(&MODULE_GPT120,0xFFFF-50000+1); //计数50000个脉冲即会发生溢出事件
/*配置T3的溢出中断*/
IfxSrc_init(&SRC_GPT120T3,IfxSrc_Tos_cpu0,10); //配置GPT120模块的T3定时器溢出中断服务,中断优先级设为10
IfxSrc_enable(&SRC_GPT120T3); //使能GPT120模块的T3定时器溢出中断服务
/*启动定时器*/
IfxGpt12_T3_run(&MODULE_GPT120,IfxGpt12_TimerRun_start); //启动T3
}
IFX_INTERRUPT(GPTimer_isr,0,10); //声明定时中断服务函数
void GPTimer_isr(void)
{
TimeCount++;
}
/************************CAN模块定义************************/
volatile uint16 rx_id = 0; //存储接收报文ID
volatile uint8 rx_len = 0; //存储接收报文负载数据长度
volatile uint8 rx_buf[8] = {0}; //报文接收数据
/*CAN模块初始化*/
IfxCan_Can can_handle = {0}; //CAN模块的操作句柄
IfxCan_Can_Node node0_handle = {0}; //CAN第0节点的操作句柄
/*CAN模块初始化函数*/
void CAN_init(void)
{
/*模块配置*/
IfxCan_Can_Config can_config = {0}; //模块配置参数结构体变量
IfxCan_Can_initModuleConfig(&can_config,&MODULE_CAN0); //填充CAN模块配置参数结构体
IfxCan_Can_initModule(&can_handle,&can_config); //初始化CAN模块
/*端口配置*/
IfxCan_Node_initTxPin(&IfxCan_TXD00_P20_8_OUT,IfxPort_OutputMode_pushPull,IfxPort_PadDriver_cmosAutomotiveSpeed1);
IfxCan_Node_initRxPin(&MODULE_CAN0.N[IfxCan_NodeId_0],&IfxCan_RXD00B_P20_7_IN,IfxPort_InputMode_noPullDevice,IfxPort_PadDriver_cmosAutomotiveSpeed1);
/*节点0配置*/
IfxCan_Can_NodeConfig node_config = {0};
IfxCan_Can_initNodeConfig(&node_config,&can_handle); //填充节点默认配置
node_config.nodeId = IfxCan_NodeId_0; //要配置的节点为第0号节点
node_config.frame.type = IfxCan_FrameType_transmitAndReceive; //允许节点能接收和发送数据帧
node_config.baudRate.baudrate = 500000;//波特率
/*节点接收中断*/
node_config.interruptConfig.messageStoredToDedicatedRxBufferEnabled = TRUE; //使能接收成功中断
node_config.interruptConfig.reint.interruptLine = IfxCan_InterruptLine_0; //中断信号映射到0号中断线
node_config.interruptConfig.reint.priority = 11; //接收成功中断的优先级设为11
IfxCan_Can_initNode(&node0_handle,&node_config); //完成节点配置
/*节点0过滤配置*/
IfxCan_Filter node0_filter0 = {0};
node0_filter0.number = 0; //配置第0号过滤器
node0_filter0.elementConfiguration = IfxCan_FilterElementConfiguration_storeInRxBuffer; //过滤通过存到指定缓冲区
node0_filter0.id1 = 0x010; //在指定缓冲区模式下,配置id1为过滤id
node0_filter0.rxBufferOffset = IfxCan_RxBufferId_0; //过滤完成后存入0号接收缓冲区
IfxCan_Can_setStandardFilter(&node0_handle,&node0_filter0); //完成过滤器配置
}
/*根据报文ID发出报文,返回已完成发送的数据长度*/
void CAN_send(uint16 id,uint8 *buf,uint32 len)
{
IfxCan_Message tx_msg = {0};
IfxCan_Can_initMessage(&tx_msg); //向消息结构体填充初始参数
tx_msg.bufferNumber = IfxCan_TxBufferId_0; //使用第0号发送缓冲区发送
tx_msg.frameMode = IfxCan_FrameMode_standard; //此为标准CAN协议帧
tx_msg.dataLengthCode = len; //设定要发送的报文的负载数据长度
tx_msg.messageId = id; //设置本次报文的id
tx_msg.messageIdLength = IfxCan_MessageIdLength_standard; //本次报文ID长度为11位的ID
IfxCan_Can_sendMessage(&node0_handle,&tx_msg,(uint32 *)buf); //传入负载数据启动发送
}
/*CAN模块的接收中断处理函数*/
IFX_INTERRUPT(CAN_rx_isr,0,11); //声明CAN接收中断服务函数
void CAN_rx_isr(void)
{
IfxCan_Message rx_msg = {0};
uint32 data_buf[2] = {0}; //存数据的临时缓冲区
IfxCan_Node_clearInterruptFlag(node0_handle.node,IfxCan_Interrupt_messageStoredToDedicatedRxBuffer); //清除接收完成中断
rx_msg.bufferNumber = IfxCan_RxBufferId_0; //指定获取第0号接收缓冲区
IfxCan_Can_readMessage(&node0_handle,&rx_msg,data_buf); //读取消息,将数据存入缓冲区便于程序处理
rx_id = (uint16)rx_msg.messageId; //将接收到的报文ID保存下来
rx_len = (uint8)rx_msg.dataLengthCode; //获取接收到的数据长度
/*取出接收到的报文数据*/
for(uint32 cnt = 0; cnt < rx_len; cnt ++)
{
rx_buf[cnt] = ((uint8 *)data_buf)[cnt]; //以字节来读取数据
}
}
/************************电机控制定义************************/
/*直流有刷电机初始化函数*/
void Motor_init(void)
{
/*端口部分*/
IfxPort_setPinMode(&MODULE_P00,2,IfxPort_Mode_outputPushPullAlt7); //将P00.2复用为输出7,对应CCU61的COUT60
IfxPort_setPinMode(&MODULE_P00,4,IfxPort_Mode_outputPushPullAlt7); //将P00.4复用为输出7,对应CCU61的COUT61
IfxPort_setPinMode(&MODULE_P00,6,IfxPort_Mode_outputPushPullAlt7); //将P00.6复用为输出7,对应CCU61的COUT62
IfxPort_setPinMode(&MODULE_P00,1,IfxPort_Mode_outputPushPullAlt7); //将P00.1复用为输出7,对应CCU61的CC60
IfxPort_setPinMode(&MODULE_P00,3,IfxPort_Mode_outputPushPullAlt7); //将P00.3复用为输出7,对应CCU61的CC61
IfxPort_setPinMode(&MODULE_P00,5,IfxPort_Mode_outputPushPullAlt7); //将P00.5复用为输出7,对应CCU61的CC62
IfxPort_setPinMode(&MODULE_P00,0,IfxPort_Mode_outputPushPullGeneral); //将P00.0设置为普通输出,对应于电机驱动芯片的使能控制
IfxPort_setPinState(&MODULE_P00,0,IfxPort_State_high); //使能电机驱动芯片
/*CCU6部分*/
IfxCcu6_enableModule(&MODULE_CCU61); //使能CCU61模块
/*配置T12定时器的时基参数*/
IfxCcu6_enableTimer(&MODULE_CCU61,IfxCcu6_TimerId_t12); //使能CCU6的T12定时器,以便对它进行操作
IfxCcu6_setInputClockFrequency(&MODULE_CCU61,IfxCcu6_TimerId_t12,IfxCcu6_TimerInputClock_fcc6By4); //CCU6使用SPB总线时钟作为时钟源,因此其模块频率为100MHz,经4分频得到25MHz的T12计数频率
IfxCcu6_setT12CountMode(&MODULE_CCU61,IfxCcu6_T12CountMode_centerAligned); //将T12的计数模式设为中心对齐模式
IfxCcu6_setT12PeriodValue(&MODULE_CCU61,1250-1); //周期值决定T12的输出频率,也即PWM的频率,设定为1250分频,则输出频率=25M/1.25K=20KHz,考虑到中心对齐的计数模式,那么输出的PWM就具有2分频特性,所以最终的输出频率为10KHz
IfxCcu6_setT12CounterValue(&MODULE_CCU61,0); //计数器先初始化为0
/*配置T12通道模式*/
IfxCcu6_setT12ChannelMode(&MODULE_CCU61,IfxCcu6_T12Channel_0,IfxCcu6_T12ChannelMode_compareMode); //CC60通道设为比较模式,产生PWM波
IfxCcu6_setT12CompareValue(&MODULE_CCU61,IfxCcu6_T12Channel_0,0); //比较值先设为0,产生0%的占空比
IfxCcu6_setT12ChannelMode(&MODULE_CCU61,IfxCcu6_T12Channel_1,IfxCcu6_T12ChannelMode_compareMode); //CC61通道设为比较模式,产生PWM波
IfxCcu6_setT12CompareValue(&MODULE_CCU61,IfxCcu6_T12Channel_1,0); //比较值先设为0,产生0%的占空比
IfxCcu6_setT12ChannelMode(&MODULE_CCU61,IfxCcu6_T12Channel_2,IfxCcu6_T12ChannelMode_compareMode); //CC62通道设为比较模式,产生PWM波
IfxCcu6_setT12CompareValue(&MODULE_CCU61,IfxCcu6_T12Channel_2,0); //比较值先设为0,产生0%的占空比
/*配置CC60通道参数*/
IfxCcu6_setOutputPassiveLevel(&MODULE_CCU61,IfxCcu6_ChannelOut_cc0,FALSE); //通道的默认电平为低电平
IfxCcu6_setOutputPassiveState(&MODULE_CCU61,IfxCcu6_ChannelOut_cc0,TRUE); //默认电平激活条件为计数器大于比较值时激活,大于比较值输出低电平,小于等于比较值输出高电平
IfxCcu6_disableModulationOutput(&MODULE_CCU61,IfxCcu6_TimerId_t12,IfxCcu6_ChannelOut_cc0); //先禁止CC60的通道输出
IfxCcu6_enableShadowTransfer(&MODULE_CCU61,TRUE,FALSE); //启动一次阴影传输以更新到实际寄存器
/*配置COUT60通道参数*/
IfxCcu6_setOutputPassiveLevel(&MODULE_CCU61,IfxCcu6_ChannelOut_cout0,TRUE); //通道的默认电平为高电平
IfxCcu6_setOutputPassiveState(&MODULE_CCU61,IfxCcu6_ChannelOut_cout0,TRUE); //默认电平激活条件为计数器大于比较值时激活,大于比较值输出低电平,小于等于比较值输出高电平
IfxCcu6_disableModulationOutput(&MODULE_CCU61,IfxCcu6_TimerId_t12,IfxCcu6_ChannelOut_cout0); //先禁止COUT0的通道输出
IfxCcu6_enableShadowTransfer(&MODULE_CCU61,TRUE,FALSE); //启动一次阴影传输以更新到实际寄存器
/*配置CC61通道参数*/
IfxCcu6_setOutputPassiveLevel(&MODULE_CCU61,IfxCcu6_ChannelOut_cc1,FALSE); //通道的默认电平为低电平
IfxCcu6_setOutputPassiveState(&MODULE_CCU61,IfxCcu6_ChannelOut_cc1,TRUE); //的默认电平激活条件为计数器大于比较值时激活,大于比较值输出低电平,小于等于比较值输出高电平
IfxCcu6_disableModulationOutput(&MODULE_CCU61,IfxCcu6_TimerId_t12,IfxCcu6_ChannelOut_cc1); //先禁止CC61的通道输出
IfxCcu6_enableShadowTransfer(&MODULE_CCU61,TRUE,FALSE); //启动一次阴影传输以更新到实际寄存器
/*配置COUT61通道参数*/
IfxCcu6_setOutputPassiveLevel(&MODULE_CCU61,IfxCcu6_ChannelOut_cout1,TRUE); //通道的默认电平为高电平
IfxCcu6_setOutputPassiveState(&MODULE_CCU61,IfxCcu6_ChannelOut_cout1,TRUE); //默认电平激活条件为计数器大于比较值时激活,大于比较值输出低电平,小于等于比较值输出高电平
IfxCcu6_disableModulationOutput(&MODULE_CCU61,IfxCcu6_TimerId_t12,IfxCcu6_ChannelOut_cout1); //先禁止COUT1的通道输出
IfxCcu6_enableShadowTransfer(&MODULE_CCU61,TRUE,FALSE); //启动一次阴影传输以更新到实际寄存器
/*配置CC62通道参数*/
IfxCcu6_setOutputPassiveLevel(&MODULE_CCU61,IfxCcu6_ChannelOut_cc2,FALSE); //通道的默认电平为低电平
IfxCcu6_setOutputPassiveState(&MODULE_CCU61,IfxCcu6_ChannelOut_cc2,TRUE); //的默认电平激活条件为计数器大于比较值时激活,大于比较值输出低电平,小于等于比较值输出高电平
IfxCcu6_disableModulationOutput(&MODULE_CCU61,IfxCcu6_TimerId_t12,IfxCcu6_ChannelOut_cc2); //先禁止CC62的通道输出
IfxCcu6_enableShadowTransfer(&MODULE_CCU61,TRUE,FALSE); //启动一次阴影传输以更新到实际寄存器
/*配置COUT62通道参数*/
IfxCcu6_setOutputPassiveLevel(&MODULE_CCU61,IfxCcu6_ChannelOut_cout2,TRUE); //通道的默认电平为高电平
IfxCcu6_setOutputPassiveState(&MODULE_CCU61,IfxCcu6_ChannelOut_cout2,TRUE); //默认电平激活条件为计数器大于比较值时激活,大于比较值输出低电平,小于等于比较值输出高电平
IfxCcu6_disableModulationOutput(&MODULE_CCU61,IfxCcu6_TimerId_t12,IfxCcu6_ChannelOut_cout2); //先禁止COUT2的通道输出
IfxCcu6_enableShadowTransfer(&MODULE_CCU61,TRUE,FALSE); //启动一次阴影传输以更新到实际寄存器
/*启动定时器*/
IfxCcu6_startTimer(&MODULE_CCU61,TRUE,FALSE);
}
/*直流有刷电机控制函数,控制电机的旋转方向及停止,占空比调节动作快慢,占空比范围0-100,对应0%-100%,调节精度1%*/
void Motor_ctl(uint8 ctl_dir,uint8 duty)
{
uint16 period_val = 0;
uint16 cmp_val = 0;
uint16 cc60_cmp_val = 0; //左半桥比较值
uint16 cc61_cmp_val = 0; //中半桥比较值
uint16 cc62_cmp_val = 0; //右半桥比较直
/*计算PWM*/
period_val = MODULE_CCU61.T12PR.B.T12PV + 1; //获取T12的周期寄存器值
cmp_val = (uint16)(((float)duty/100.0f) * (float)period_val);
/*设置各通道输出*/
switch(ctl_dir)
{
case 1: //电机正转,COUT60控制左上桥臂,CC61控制右下桥臂导通,使电机电压左正右负,电流由左向右
{
MODULE_CCU61.MODCTR.B.T12MODEN = 0x09; //左下管导通,中上管导通,右半桥关闭
cc60_cmp_val = cmp_val; //左半桥下管PWM调制
cc61_cmp_val = period_val; //中半桥上管常通
cc62_cmp_val = 0; //右半桥无PWM输出
break;
}
case 2: //电机反转,CC60控制左下桥臂,COUT61控制右上桥臂导通,使电机电压左负右正,电流由右向左
{
MODULE_CCU61.MODCTR.B.T12MODEN = 0x06; //左上管导通,中下管导通,右半桥关闭
cc60_cmp_val = period_val; //左半桥上管常通
cc61_cmp_val = cmp_val; //中半桥下管PWM调制
cc62_cmp_val = 0; //右半桥无PWM输出
break;
}
default://停止,下两个桥臂导通,利用电机的电感特性阻碍电机运动,使电机停止
{
MODULE_CCU61.MODCTR.B.T12MODEN = 0x15; //开启所有下管,关闭所有上管
cc60_cmp_val = period_val; //左半桥PWM输出100%,最大能耗制动
cc61_cmp_val = period_val; //中半桥PWM输出100%,最大能耗制动
cc62_cmp_val = period_val; //右半桥PWM输出100%,最大能耗制动
break;
}
}
/*写入PWM比较值*/
IfxCcu6_setT12CompareValue(&MODULE_CCU61,IfxCcu6_T12Channel_0,cc60_cmp_val); //写入CC60通道的新比较值
IfxCcu6_setT12CompareValue(&MODULE_CCU61,IfxCcu6_T12Channel_1,cc61_cmp_val); //写入CC61通道的新比较值
IfxCcu6_setT12CompareValue(&MODULE_CCU61,IfxCcu6_T12Channel_2,cc62_cmp_val); //写入CC62通道的新比较值
/*更新实际寄存器*/
IfxCcu6_enableShadowTransfer(&MODULE_CCU61,1,0); //启动一次阴影传输以更新到实际寄存器
}
void core0_main(void)
{
IfxCpu_enableInterrupts();
/* !!WATCHDOG0 AND SAFETY WATCHDOG ARE DISABLED HERE!!
* Enable the watchdogs and service them periodically if it is required
*/
IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
IfxScuWdt_disableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());
/* Wait for CPU sync event */
IfxCpu_emitEvent(&g_cpuSyncEvent);
IfxCpu_waitEvent(&g_cpuSyncEvent, 1);
GPTimer_init();
CAN_init();
Motor_init();
uint8 tx_buf[8]; // 发送缓存
while(1)
{
if(TimeCount>1)
{
TimeCount = 0;
Motor_ctl(rx_buf[0],10);//电机控制
tx_buf[0] = rx_buf[0];
CAN_send(0x123,tx_buf,8); // 报文发送
}
}
}
二、代码解释
相比于前面文章Aurix英飞凌TC334芯片ADC采样模块配置中CAN模块的配置,CCU模块的配置稍微复杂了一点。主要是因为CCU模块涉及到多个端口按照时序输出。好消息是模块初始化中绝大部分代码不需要修改,只关注几个重要的配置即可。下面我们就对这段代码就行简单的分析和讲解。
IfxPort_setPinMode(&MODULE_P00,2,IfxPort_Mode_outputPushPullAlt7); //将P00.2复用为输出7,对应CCU61的COUT60
IfxPort_setPinMode(&MODULE_P00,4,IfxPort_Mode_outputPushPullAlt7); //将P00.4复用为输出7,对应CCU61的COUT61
IfxPort_setPinMode(&MODULE_P00,6,IfxPort_Mode_outputPushPullAlt7); //将P00.6复用为输出7,对应CCU61的COUT62
IfxPort_setPinMode(&MODULE_P00,1,IfxPort_Mode_outputPushPullAlt7); //将P00.1复用为输出7,对应CCU61的CC60
IfxPort_setPinMode(&MODULE_P00,3,IfxPort_Mode_outputPushPullAlt7); //将P00.3复用为输出7,对应CCU61的CC61
IfxPort_setPinMode(&MODULE_P00,5,IfxPort_Mode_outputPushPullAlt7); //将P00.5复用为输出7,对应CCU61的CC62
这几行代码是设置电机控制的端口。驱动一个有刷电机,使用下图所示的H桥电路即可实现。Q1和Q4通,Q2和Q3断,电机正转;Q1和Q4断,Q2和Q3通,电机反转;Q1和Q3断,Q2和Q4通,电机制动。因此一般情况配置四个端口就可以实现功能。由于CCU模块和测试板均具备可生成3路PWM的资源,可以用于控制BLDC(直流无刷)电机,所以本实例中就将三路六个端口全部进行了配置。实际上我们只使用了其中两路四个端口。
配置所需的端口可从电路原理图上寻找。
IfxPort_setPinMode(&MODULE_P00,0,IfxPort_Mode_outputPushPullGeneral);
IfxPort_setPinState(&MODULE_P00,0,IfxPort_State_high);
上面这行代码就使能电机驱动芯片。因为英飞凌单片机的驱动能力很弱,只能提供很小的电流,有时可能无法顺利实现MOS管的通断,所以我们使用了电机驱动芯片增大其驱动能力。电机驱动芯片要想工作,需要将P00.0端口置为高电平。
IfxCcu6_setT12CountMode(&MODULE_CCU61,IfxCcu6_T12CountMode_centerAligned); //将T12的计数模式设为中心对齐模式
这一行代码的主要作用是将定时器设置为中心对齐模式。中心对齐模式是相对于边缘对齐模式而言的。中心对齐模式下,如果定时器达到最大值后,不会置零而是会由最大值逐个递减。
IfxCcu6_setT12CompareValue(&MODULE_CCU61,IfxCcu6_T12Channel_0,0); //比较值先设为0,产生0%的占空比
这一行代码的主要作用是设定比较值。当定时器的值与比较值相等的时候,我们就说这时发生了一次“比较匹配”。当比较匹配发生时,我们前面配置的几个端口会出现一次电平翻转。因此我们可以通过更改不同的比较值,从而调制出不同占空比的PWM。通过设置定时器的初始值,可以调制出不同频率的PWM。
MODULE_CCU61.MODCTR.B.T12MODEN = 0x09; //左下管导通,中上管导通,右半桥关闭
上面这行代码是控制四个端口的通断,只有导通的端口才会输出PWM。
IfxCcu6_setT12CompareValue(&MODULE_CCU61,IfxCcu6_T12Channel_0,cc60_cmp_val); //写入CC60通道的新比较值
上面这行代码是通过设置比较值,实现PWM占空比变化。PWM的占空比越大,电机被驱动的时间就越长,累计的速度会更高。我们可以看见,这段程序的主要作用是周期性的获取CAN报文发送过来的电机旋向指令,然后控制电机做出响应的动作,最后通过CAN报文把电机的旋向发送出去。程序的结构如下图所示。
三、下载调试
程序编写完毕后,我们点击任务栏中的编译按钮。
编译完成后,在输出窗口可以看见“编译完成,无错误,无警告”的字样。
然后我们通过Type-C将测试板与你的电脑相连。
点击任务栏中的下载按钮。
下载完成后,在输出窗口可以看见“下载成功”的字样。
将测试板与有刷电机相连,将测试板的CAN_H(红色)和CAN_L(蓝色)接出来通过DB9接头与CAN卡相连。将CAN卡通过USB接口与电脑相连。如下图所示。
个人电脑的上位机我们使用TSMaster的CAN报文收发功能。
配置好TSMaster后,测试板已经在周期性的发送ID为0x123的报文了。同时我们也能看见有刷电机转动。
电机控制
在没有接到ID为0x10的指令报文前,电机是不转动的。当接收到ID0x10的第0字节为1时,电机正转;当接收到ID0x10的第0字节为0时,电机停止;当接收到ID0x10的第0字节为2时,电机反转。同时ID为0x123的报文也在周期性的外发电机旋向状态。至此我们完成了使用TC334芯片实现了有刷电机的控制。
总结
今天我们介绍了如何运用TC334芯片CCU模块实现有刷电机控制。我们到目前为止一直介绍的都是芯片的基础配置。后续我们将继续介绍该芯片的进阶应用,使用该芯片实现CCP协议,XCP协议,UDS协议,J1939协议和RTOS。敬请收藏关注,不迷路。
如需测试板,请点击。
https://store.weixin.qq.com/shop/b/feHVuMfOAmRCoHk?entrance_id=h5
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)