这里写的是TIM模块的大体作用
TIM单元是GTM模块里面对输入信号进行采集、处理的单元。常用语发动机位置管理,采集曲轴、凸轮轴传感器信号,并产生相应的事件。今天我们来看看TIM的原理。
死区PWM就是防止整流桥与逆变桥同时导通,当其中一个桥导通的时候另一个桥不会导通,如果同时导通会导致电路短路,这就是死区PWM设计的原因。
TOM模块的配置
3个TOM单元
每个TOM单元有16个通道,也就是有48路pwm通道
如图2所示,需要关注的点有三处。
①触发源:触发源有两个,一个TIM单元捕捉到的外部信号,一个是上一个通道产生的触发信号。
②触发方式:触发其实就是影响了CCU0单元计数器CN0的复位方式,可选的复位方式有两种,一种是CN0>=CM0时产生复位;另一种是触发信号使CN0复位。我们这里选第二种。
③输出触发信号:图2中清楚的显示了三个可选的触发信号输出:一是来自输入端的TIM捕捉信号,二同样是来自输入端的上个通道的触发信号,三则是本通道CCU0的CN0复位信号。
PWM的产生分为两种方式
(1)如图3(左),通道0作为定时器和触发源,产生6路PWM信号输出。
(2)如图3(右),通道8作为定时器,两两触发,可以产生3组PWM信号。
第一步是使能GTM模块,配置TOM输入时钟;
TOM可以配置为两种不同的工作模式:连续模式和单次模式。
连续模式是一旦启动后,每次CN0和CM0匹配后溢出,CN0归零并重新开始计数。也就是说会产生连续的PWM波。
单次模式是启动后,CN0和CM0匹配后溢出,CN0不会自动归零。必须再次经过复位后才会归零并开始计数。也就是说这种模式**每次触发启动都只会产生一个脉冲。**就是每次CN0溢出后都需要对他进行重新复位才能进行下次操作
配置寄存器CLC.DISS=0使能GTM模块,对应ILLD中函数
IfxGtm_isEnabled(Ifx_GTM *gtm);
上一帖我们讲过CMU单元的基本结构,系统时钟经过CFGU(可配置时钟产生单元)分频得到8个基础时钟,接着其中之一进入FXU分频5个TOM输入时钟。时钟初始化函数如下:
图2:TOM时钟初始化
在CFGU单元,将系统时钟进行初始分频得到GCLK,我这里将该时钟设为最大100MHz;接着还是在CFGU单元,将GCLK分频得到8个基础时钟ECLK,这里我们选择CMU_CLK_0,将其配置为最大100MHz。当然这个频率可以更具实际情况来设置,例如设置为1MHz,那么需将frequency改成1000000,该参数的单位是Hz。
现在我们就得到了FXU单元的输入时钟CMU_CLK_0;接下来使能FXU单元,然后选择一个FXU单元的输出时钟作为TOM单元的时钟即可(在配置TOM时选择)。
图3;TOM初始化配置
我们按照ILLD做好的封装直接进行配置,我们需要配置的参数为TOM0的channel0产生周期为10ms,占空比为10%的PWM波,并且使能周期中断。首先来看看需要配置的参数:
图4:参数配置函数内容
如图所示,在默认配置中,我们配置了TOM0,channel0,而且时钟选择了FXCLK0。根据上讲的内容,FXCLK0=CMU_CLK_0。按照我们的配置,那么这个时钟极为1MHz,所有tom的TIMER的时基为10us。也就是说,我们需要配置period=1000,表示10ms,dutycycle=10%*period=100,表示1ms。
将channel0的输出映射到相应的IO口,在文件GTM_PinMap.c中找到映射,如图5所示:
图5:TOM输出IO口映射
另外需配置立即启动,表示初始化完成后即可启动PWM波。
第三步是中断配置
首先我们需要在参数配置中使能中断,配置中断处理终端,中断优先级。
(1)中断使能
TOM中支持两种中断,一种是CN0=CM0时,对应的周期中断:ccu0中断
config->interrupt.ccu0Enabled = TRUE;//使能周期中断
另一种是CN0=CM1时,对应的是匹配中断:CCU1中断
config->interrupt.ccu1Enabled = TRUE;//使能匹配中断
CM0装载的是周期值,CM1装载的占空比的值
其变化的触发就是根据这两个值与CN0的比较
(2)中断处理终端
config->interrupt.isrProvider = IfxSrc_Tos_cpu0;//配置CPU0为中断处理中断;
(3)中断优先级
tomConfig.interrupt.isrPriority = TOM0_CH0_PRIO;//需自行定义
(4)装载中断handler,使能中断
IfxCpu_Irq_installInterruptHandler (TOM0Ch0_ISR, TOM0_CH0_PRIO);
包含两个参数:中断处理函数和中断优先级。
IFX_INTERRUPT(TOM0Ch0_ISR, 0, TOM0_CH0_PRIO)
{
}//对应的中断处理函数
经过这么个流程PWM便可以产生了,至于PWM的启停以及周期和占空比的更新,我们下回分解。
1、首先来看看PWM波的启停。
其实配置完某个通道产生PWM波之后,控制其停止的方式有很多种。可以禁用通道输出,也可以更改输出引脚配置,甚至直接将通道禁用即可。
我们这里直接用最根本的方式就是通道禁用。
通道禁用寄存器为ENDIS寄存器,假如写入TOM0_ENDIS_CTRL0=01,则表示通道0被禁用。通道禁用之后计数器CN0停止计数,并保持当前计数值。输出电平变成SL位的翻转值,就是说如果SL定义为高电平,那么通道禁用后的电平变为低电平。(这点需要注意,根据需要改变关断后的电平状态,保证外部器件是关断的)
代码如下:
void PWM_STOP(void)
{
GTM_TOM0_CH0_CTRL.B.SL = 0;
GTM_TOM0_TGC0_ENDIS_CTRL.B.ENDIS_CTRL0 = 1;
}
就是直接操作寄存器了,先给定一个电平状态,然后就禁用通道。
启用过程也是类似的,改回原来的电平状态,并使能通道即可。
void PWM_START(void)
{
GTM_TOM0_CH0_CTRL.B.SL = 1;
GTM_TOM0_TGC0_ENDIS_CTRL.B.ENDIS_CTRL0 = 2;
}
PWM通道在启停的时候会翻转当前的电平信号,需要注意
PWM的启停实际上也就是通过单片机的相应寄存器控制其使能与否
2、通道周期和占空比的更新
**周期和占空比的更新,就是更新CM0和CM1寄存器的值。**其更新流程如下:
(1)禁用寄存器更新
配置:GTM_TOM0_TGC0_GLB_CTRL.B.UPEN_CTRL0 = 1;
(2)写入相应的映射寄存器
寄存器CM0的值通过映射寄存器SR0的值来更新,寄存器CN1的值通过映射寄存器SR1的值来更新。输入时钟选择也可以更新,通过CLK_SRC_SR来更改。
代码如下:
GTM_TOM0_CH0_CTRL.B.CLK_SRC_SR = IfxGtm_Tom_Ch_ClkSrc_cmuFxclk0;
GTM_TOM0_CH0_SR0.B.SR0 = (2period);
GTM_TOM0_CH0_SR1.B.SR1 = (2period-dutycycle);
(3)使能寄存器的更新
配置:GTM_TOM0_TGC0_GLB_CTRL.B.UPEN_CTRL0 = 2;
通道周期和占空比的更新也就是根据相应的寄存器(CM0,CM1,CN0)这三个寄存器设置其相应的值,在实际工程的时候也就是通过程序中直接设置什么时候输出多少占空比的值,或者根据外部来的反馈输出相应的值。
以上便配置完了周期和占空比的更新,每次CN0复位后这些更改都会生效。
另一种方式是在同步应用的模式,写入相应的映射寄存器后寄存器的更改会立即生效,不需要等到CN0复位,这里便不再详述了。
在ADC模块中TOM模块也可以做为ADC单元的触发信号
TOM输出通道经过多路复用可对每个ADC group产生两个触发信号:ADC_TRIG0、ADC_TRIG1;分别对应TOM0和TOM1
所以我们的基本想法就是,在ADC单元采样结束后,产生中断;中断请求触发DMA传输,将ADC结果寄存器的值传输到定义好的寄存器中。
ADC模式分为,队列模式,扫描模式,背景调试模式
2.1、首先初始化ADC单元,我们可配置为自动扫描方式,通道配置为GROUP0的CH0-CH7,ADC采样为查询方式(软件请求)。
然后便是中断配置,包括中断类型,中断节点,中断优先级、中断处理终端;中断类型配置为结果中断:结果有效时产生中断;
中断节点:配置为Group0Line0即可;
中断处理终端:DMA;
中断优先级:需配置为DMA通道号,这里我们使用DMA通道1。
代码如下(ILLD版):
void ADC_init(void)
{
int chnIx;
// 创建配置变量
IfxVadc_Adc_Config adcConfig;
IfxVadc_Adc_initModuleConfig(&adcConfig, &MODULE_VADC);
//初始化ADC模块全局参数,采样周期和结果精度
IfxVadc_Adc_initModule(&vadc, &adcConfig);
// 创建组配置变量
IfxVadc_Adc_GroupConfig adcGroupConfig;
IfxVadc_Adc_initGroupConfig(&adcGroupConfig, &vadc);
// 定义组ID
int group_i = 0;
adcGroupConfig.groupId = group_i;
// 设置Master ID为自己的ID,即自己作为Master
adcGroupConfig.master = adcGroupConfig.groupId;
// 使能队列中断源
adcGroupConfig.arbiter.requestSlotQueueEnabled = TRUE;
// 使能扫描中断源
adcGroupConfig.arbiter.requestSlotScanEnabled = TRUE;
// 使能后台中断源
adcGroupConfig.arbiter.requestSlotBackgroundScanEnabled = TRUE;
//使能所有源的门
adcGroupConfig.queueRequest.triggerConfig.gatingMode = IfxVadc_GatingMode_always;
adcGroupConfig.scanRequest.triggerConfig.gatingMode = IfxVadc_GatingMode_always;
adcGroupConfig.backgroundScanRequest.triggerConfig.gatingMode = IfxVadc_GatingMode_always;
// 初始化组配置
IfxVadc_Adc_initGroup(&adcGroup[group_i], &adcGroupConfig);
// 创建通道配置变量
IfxVadc_Adc_ChannelConfig adcChannelConfig[8];
for(chnIx=0; chnIx<8; ++chnIx)
{
IfxVadc_Adc_initChannelConfig(&adcChannelConfig[chnIx], &adcGroup[group_i]);
//配置通道ID
adcChannelConfig[chnIx].channelId = (IfxVadc_ChannelId)(chnIx);
//配置通道结果寄存器,采用专用结果寄存器
adcChannelConfig[chnIx].resultRegister = (IfxVadc_ChannelResult)(chnIx);
//优先级定义为DMA通道号
adcChannelConfig[chnIx].resultPriority = 1;//VADC_DMA_CHANNEL
//中断处理终端为DMA
adcChannelConfig[chnIx].resultServProvider = IfxSrc_Tos_dma;
adcChannelConfig[chnIx].resultSrcNr = IfxVadc_SrcNr_group0;//Service node of the result trigger
// 初始化通道
IfxVadc_Adc_initChannel(&adcChannel[group_i][chnIx], &adcChannelConfig[chnIx]);
//添加到扫描源
unsigned channels = (1 << adcChannelConfig[chnIx].channelId);
unsigned mask = channels;
boolean continuous = TRUE;
IfxVadc_Adc_setScan(&adcGroup[group_i], channels, mask, continuous);
}
通过配置好ADC模块之后再设置DMA模块进行传输到特定地址,这里的ADC采集到的数据可以通过DMA模块或者是cpu控制的寄存器传输到相应的寄存器
2.2、DMA初始化
接下来就是DMA的初始化了,需要对DMA通道、请求源、请求源处理、DMA数据结构、地址空间、地址偏移、源地址、目标地址等内容进行定义。具体不再过多介绍了。
代码如下(ILLD版):
void Dma_channel_config(void)
{
IfxDma_Dma_Config dmaConfig;
IfxDma_Dma_initModuleConfig(&dmaConfig, &MODULE_DMA);
IfxDma_Dma_initModule(&dma, &dmaConfig);
{
//不光要配置adc的channel还要配置dma的channel
IfxDma_Dma_ChannelConfig chnCfg;
IfxDma_Dma_initChannelConfig(&chnCfg, &dma);
//选择要使用的DMA通道
chnCfg.channelId = IfxDma_ChannelId_1; //共64个通道
chnCfg.hardwareRequestEnabled = TRUE; //注意是硬件触发模式
//中断配置
chnCfg.channelInterruptEnabled = TRUE; //所有bit数据传送完之后DMA发出服务请求
chnCfg.channelInterruptControl = IfxDma_ChannelInterruptControl_transferCountDecremented;
chnCfg.channelInterruptPriority = DMA_CHANNEL1_ISR_PRIO;//优先级在Ifx_IntPrioDef.h中定义
// chnCfg.channelInterruptTypeOfService = (IfxSrc_Tos)IfxCpu_getCoreId(); //IfxCpu_Irq_getTos(IfxCpu_getCoreIndex());这是错的
chnCfg.channelInterruptTypeOfService = IfxSrc_Tos_cpu0;//这个地方dma通道的服务者还是cpu
//源地址与目标地址
chnCfg.sourceAddress = (uint32)&VADC_G0_RES1; // first result register
// chnCfg.sourceAddress = (uint32)&adcGroup.group->RES[0]; //第一个结果寄存器Group Result Register,注意上面的RCR是Group Result Control Reg
chnCfg.sourceCircularBufferEnabled = TRUE;
chnCfg.sourceAddressCircularRange = IfxDma_ChannelIncrementCircular_16; //保持这个地址
chnCfg.destinationAddress = (uint32)&(MEUN_adc[0]);
// chnCfg.destinationAddress = IFXCPU_GLB_ADDR_DSPR(IfxCpu_getCoreId(), &vadcResultBuffer[0]);//应该是把数据移动到这个result buffer中
chnCfg.destinationAddressIncrementStep= IfxDma_ChannelIncrementStep_1; //每次写数据时地址增量为2个byte
chnCfg.destinationCircularBufferEnabled= TRUE;
chnCfg.destinationAddressCircularRange= IfxDma_ChannelIncrementCircular_16;
//move size, transfer count and request/operation mode
chnCfg.moveSize = IfxDma_ChannelMoveSize_16bit;//1 DMA move is 16 bit wide
chnCfg.transferCount = 8;//依次transfer传输8个
chnCfg.requestMode = IfxDma_ChannelRequestMode_completeTransactionPerRequest;
chnCfg.operationMode = IfxDma_ChannelOperationMode_continuous; //硬件请求使能保持set在transaction
// channel stays enabled after transaction
IfxDma_Dma_initChannel(&dmaChn, &chnCfg);
IfxCpu_Irq_installInterruptHandler(&dmaCh0ISR, DMA_CHANNEL1_ISR_PRIO);//!!这是软件中断,要把Ifx_Cfg.h中的#define IFX_USE_SW_MANAGED_INT注释去掉,否则连接不上
IfxCpu_enableInterrupts();
// IfxVadc_Adc_startScan(&adcGroup);
}
}
这上面是DMA的初始化