上一篇文章主要是解决了2837xD之间的IPC交互问题,但是需要的目标是CPU2调用串口等通信功能进行信息发送到上位机的交互。因此本文主要是学习利用CPU2来驱动外设的主要流程。
因为手上没有2837xD的开发板,本次实验还是以学习例程为主(有点难受)。。。
代码目的:CPU1进行时钟初始化与GPIO初始化,CPU2进行驱动外设(ADC与EPWM)
其中,EPWM1工作在up-mode,GPIO0(EPWM1A)、GPIO1(EPWM1B)输出。
ADC工作在连续采样模式,可以将GPIO0接入ADCINA0读取输出结果。
void main(void)
{
InitSysCtrl();//初始化系统时钟等
InitGpio();//初始化GPIO(解锁所有GPIO等)
DINT;//禁止中断
InitPieCtrl();//恢复PIE控制器默认状态
IER = 0x0000;
IFR = 0x0000; //禁止中断与清除所有中断标志位
InitPieVectTable();//初始化中断向量表
EINT; // 使能全局中断
ERTM; // 使能实时中断
InitEPwm1Gpio(); //初始化EPWM1GPIO(只有CPU1能够进行GPIO中的MUX配置)
EALLOW;
DevCfgRegs.CPUSEL0.bit.EPWM1 = 1;//将外设控制权给CPU2
DevCfgRegs.CPUSEL11.bit.ADC_A = 1;//将控制权给CPU2
EDIS;
while(1)
{
asm(" nop");
}
}
下面先附上==InitEPwm1Gpio()==函数中的代码:
void InitEPwm1Gpio(void)
{
EALLOW;
GpioCtrlRegs.GPAPUD.bit.GPIO0 = 1;
GpioCtrlRegs.GPAPUD.bit.GPIO1 = 1; //设置GPIO0与GPIO1为上拉模式
GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 1;
GpioCtrlRegs.GPAMUX1.bit.GPIO1 = 1; // 设置GPIO0与GPIO1工作在EPWM模式
EDIS;
}
通过上面的函数可以看出,需要注意的有以下几点:
CPU1中内容不多,下面康康CPU2中的内容!
void main(void)
{
InitSysCtrl(); //初始化系统时钟(与CPU1中的函数的内容一模一样)
DINT;//关中断
InitPieCtrl(); //初始化PIE控制器(与CPU1中的函数的内容一模一样)
IER = 0x0000;
IFR = 0x0000; //禁止中断与清除中断标志
InitPieVectTable();//初始化中断向量表
EALLOW;
PieVectTable.EPWM1_INT = &epwm1_isr; //指定EPWM1的中断函数
EDIS;
EALLOW;
CpuSysRegs.PCLKCR2.bit.EPWM1=1;
CpuSysRegs.PCLKCR13.bit.ADC_A = 1;
EDIS; //开启相关外设对应的时钟
以上部分都是对工作背景以及外设的准备工作。
/接着上部分代码/
ConfigureADC(); //ADC设置函数[ADC工作在12位,单端采用模式,转换结束后产生中断,采样频率4分频]
SetupADCContinuous(0); //设置ADC
下面附上==SetupADCContinuous(0)==函数中的代码:
void SetupADCContinuous(Uint16 channel)
{
Uint16 acqps;
//determine minimum acquisition window (in SYSCLKS) based on resolution
if(ADC_RESOLUTION_12BIT == AdcaRegs.ADCCTL2.bit.RESOLUTION){
acqps = 14; //75ns
}
else { //resolution is 16-bit
acqps = 63; //320ns
}
EALLOW;
AdcaRegs.ADCSOC0CTL.bit.CHSEL = channel; //SOC will convert on channel
AdcaRegs.ADCSOC1CTL.bit.CHSEL = channel; //SOC will convert on channel
AdcaRegs.ADCSOC2CTL.bit.CHSEL = channel; //SOC will convert on channel
AdcaRegs.ADCSOC3CTL.bit.CHSEL = channel; //SOC will convert on channel
AdcaRegs.ADCSOC4CTL.bit.CHSEL = channel; //SOC will convert on channel
AdcaRegs.ADCSOC5CTL.bit.CHSEL = channel; //SOC will convert on channel
AdcaRegs.ADCSOC6CTL.bit.CHSEL = channel; //SOC will convert on channel
AdcaRegs.ADCSOC7CTL.bit.CHSEL = channel; //SOC will convert on channel
AdcaRegs.ADCSOC8CTL.bit.CHSEL = channel; //SOC will convert on channel
AdcaRegs.ADCSOC9CTL.bit.CHSEL = channel; //SOC will convert on channel
AdcaRegs.ADCSOC10CTL.bit.CHSEL = channel; //SOC will convert on channel
AdcaRegs.ADCSOC11CTL.bit.CHSEL = channel; //SOC will convert on channel
AdcaRegs.ADCSOC12CTL.bit.CHSEL = channel; //SOC will convert on channel
AdcaRegs.ADCSOC13CTL.bit.CHSEL = channel; //SOC will convert on channel
AdcaRegs.ADCSOC14CTL.bit.CHSEL = channel; //SOC will convert on channel
AdcaRegs.ADCSOC15CTL.bit.CHSEL = channel; //SOC will convert on channel
AdcaRegs.ADCSOC0CTL.bit.ACQPS = acqps; //sample window is acqps + 1 SYSCLK cycles
AdcaRegs.ADCSOC1CTL.bit.ACQPS = acqps; //sample window is acqps + 1 SYSCLK cycles
AdcaRegs.ADCSOC2CTL.bit.ACQPS = acqps; //sample window is acqps + 1 SYSCLK cycles
AdcaRegs.ADCSOC3CTL.bit.ACQPS = acqps; //sample window is acqps + 1 SYSCLK cycles
AdcaRegs.ADCSOC4CTL.bit.ACQPS = acqps; //sample window is acqps + 1 SYSCLK cycles
AdcaRegs.ADCSOC5CTL.bit.ACQPS = acqps; //sample window is acqps + 1 SYSCLK cycles
AdcaRegs.ADCSOC6CTL.bit.ACQPS = acqps; //sample window is acqps + 1 SYSCLK cycles
AdcaRegs.ADCSOC7CTL.bit.ACQPS = acqps; //sample window is acqps + 1 SYSCLK cycles
AdcaRegs.ADCSOC9CTL.bit.ACQPS = acqps; //sample window is acqps + 1 SYSCLK cycles
AdcaRegs.ADCSOC10CTL.bit.ACQPS = acqps; //sample window is acqps + 1 SYSCLK cycles
AdcaRegs.ADCSOC11CTL.bit.ACQPS = acqps; //sample window is acqps + 1 SYSCLK cycles
AdcaRegs.ADCSOC12CTL.bit.ACQPS = acqps; //sample window is acqps + 1 SYSCLK cycles
AdcaRegs.ADCSOC13CTL.bit.ACQPS = acqps; //sample window is acqps + 1 SYSCLK cycles
AdcaRegs.ADCSOC14CTL.bit.ACQPS = acqps; //sample window is acqps + 1 SYSCLK cycles
AdcaRegs.ADCSOC15CTL.bit.ACQPS = acqps; //sample window is acqps + 1 SYSCLK cycles
AdcaRegs.ADCINTSEL1N2.bit.INT1E = 0; //disable INT1 flag
AdcaRegs.ADCINTSEL1N2.bit.INT2E = 0; //disable INT2 flag
AdcaRegs.ADCINTSEL3N4.bit.INT3E = 0; //disable INT3 flag
AdcaRegs.ADCINTSEL3N4.bit.INT4E = 0; //disable INT4 flag
AdcaRegs.ADCINTSEL1N2.bit.INT1CONT = 0;
AdcaRegs.ADCINTSEL1N2.bit.INT2CONT = 0;
AdcaRegs.ADCINTSEL3N4.bit.INT3CONT = 0;
AdcaRegs.ADCINTSEL3N4.bit.INT4CONT = 0;
AdcaRegs.ADCINTSEL1N2.bit.INT1SEL = 6; //end of SOC6 will set INT1 flag
AdcaRegs.ADCINTSEL1N2.bit.INT2SEL = 14; //end of SOC14 will set INT2 flag
AdcaRegs.ADCINTSEL3N4.bit.INT3SEL = 7; //end of SOC7 will set INT3 flag
AdcaRegs.ADCINTSEL3N4.bit.INT4SEL = 15; //end of SOC15 will set INT4 flag
//ADCINT2 will trigger first 8 SOCs
AdcaRegs.ADCINTSOCSEL1.bit.SOC0 = 2;
AdcaRegs.ADCINTSOCSEL1.bit.SOC1 = 2;
AdcaRegs.ADCINTSOCSEL1.bit.SOC2 = 2;
AdcaRegs.ADCINTSOCSEL1.bit.SOC3 = 2;
AdcaRegs.ADCINTSOCSEL1.bit.SOC4 = 2;
AdcaRegs.ADCINTSOCSEL1.bit.SOC5 = 2;
AdcaRegs.ADCINTSOCSEL1.bit.SOC6 = 2;
AdcaRegs.ADCINTSOCSEL1.bit.SOC7 = 2;
//ADCINT1 will trigger second 8 SOCs
AdcaRegs.ADCINTSOCSEL2.bit.SOC8 = 1;
AdcaRegs.ADCINTSOCSEL2.bit.SOC9 = 1;
AdcaRegs.ADCINTSOCSEL2.bit.SOC10 = 1;
AdcaRegs.ADCINTSOCSEL2.bit.SOC11 = 1;
AdcaRegs.ADCINTSOCSEL2.bit.SOC12 = 1;
AdcaRegs.ADCINTSOCSEL2.bit.SOC13 = 1;
AdcaRegs.ADCINTSOCSEL2.bit.SOC14 = 1;
AdcaRegs.ADCINTSOCSEL2.bit.SOC15 = 1;
}
上述的设置完成了SOC0-SOC15都采集某一个通道,并且:
SOC0-SOC7由ADCINT2可以触发
SOC8-SOC15由ADCINT1可以触发
SOC6完成后触发ADCINT1中断
SOC14完成后触发ADCINT2中断
SOC7完成后触发ADCINT3中断
SOC15完成后触发ADCINT4中断
/接上述程序/
EALLOW;
CpuSysRegs.PCLKCR0.bit.TBCLKSYNC =0;//停止记述
EDIS;
InitEPwm1Example();
EALLOW;
CpuSysRegs.PCLKCR0.bit.TBCLKSYNC =1;//开始记述
EDIS;
下面附上==InitEPwm1Example()==函数中的代码:
void InitEPwm1Example()
{
// Setup TBCLK
EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP; // Count up
EPwm1Regs.TBPRD = EPWM1_TIMER_TBPRD; // Set timer period
EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE; // Disable phase loading
EPwm1Regs.TBPHS.bit.TBPHS = 0x0000; // Phase is 0
EPwm1Regs.TBCTR = 0x0000; // Clear counter
EPwm1Regs.TBCTL.bit.HSPCLKDIV = TB_DIV2; // Clock ratio to SYSCLKOUT
EPwm1Regs.TBCTL.bit.CLKDIV = TB_DIV2;
// Setup shadow register load on ZERO
EPwm1Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
EPwm1Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
EPwm1Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO;
EPwm1Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO;
// Set Compare values
EPwm1Regs.CMPA.bit.CMPA = EPWM1_MIN_CMPA; // Set compare A value
EPwm1Regs.CMPB.bit.CMPB = EPWM1_MIN_CMPB; // Set Compare B value
// Set actions
EPwm1Regs.AQCTLA.bit.ZRO = AQ_SET; // Set PWM1A on Zero
EPwm1Regs.AQCTLA.bit.CAU = AQ_CLEAR; // Clear PWM1A on event A, up count
EPwm1Regs.AQCTLB.bit.ZRO = AQ_SET; // Set PWM1B on Zero
EPwm1Regs.AQCTLB.bit.CBU = AQ_CLEAR; // Clear PWM1B on event B, up count
// Interrupt where we will change the Compare Values
EPwm1Regs.ETSEL.bit.INTSEL = ET_CTR_ZERO; // Select INT on Zero event
EPwm1Regs.ETSEL.bit.INTEN = 1; // Enable INT
EPwm1Regs.ETPS.bit.INTPRD = ET_3RD; // Generate INT on 3rd event
// Information this example uses to keep track
// of the direction the CMPA/CMPB values are
// moving, the min and max allowed values and
// a pointer to the correct ePWM registers
epwm1_info.EPwm_CMPA_Direction = EPWM_CMP_UP; // Start by increasing CMPA & CMPB
epwm1_info.EPwm_CMPB_Direction = EPWM_CMP_UP;
epwm1_info.EPwmTimerIntCount = 0; // Zero the interrupt counter
epwm1_info.EPwmRegHandle = &EPwm1Regs; // Set the pointer to the ePWM module
epwm1_info.EPwmMaxCMPA = EPWM1_MAX_CMPA; // Setup min/max CMPA/CMPB values
epwm1_info.EPwmMinCMPA = EPWM1_MIN_CMPA;
epwm1_info.EPwmMaxCMPB = EPWM1_MAX_CMPB;
epwm1_info.EPwmMinCMPB = EPWM1_MIN_CMPB;
}
上述的设置完成了EPWM1的设置:
/接上述主程序/
IER |= M_INT3;
PieCtrlRegs.PIEIER3.bit.INTx1 = 1;
EINT; // Enable Global interrupt INTM
ERTM; // Enable Global realtime interrupt DBGM
for(resultsIndex = 0; resultsIndex < RESULTS_BUFFER_SIZE; resultsIndex++)
{
AdcaResults[resultsIndex] = 0;
}
resultsIndex = 0; //将存储ADC转换结果的数组清空
EALLOW;
//take conversions indefinitely in loop
do{
//enable ADCINT flags
AdcaRegs.ADCINTSEL1N2.bit.INT1E = 1;
AdcaRegs.ADCINTSEL1N2.bit.INT2E = 1;
AdcaRegs.ADCINTSEL3N4.bit.INT3E = 1;
AdcaRegs.ADCINTSEL3N4.bit.INT4E = 1;
AdcaRegs.ADCINTFLGCLR.all = 0x000F; //开启中断,清除中断标志
//initialize results index
resultsIndex = 0;
//software force start SOC0 to SOC7
AdcaRegs.ADCSOCFRC1.all = 0x00FF; //启动SOC0-SOC7
//keep taking samples until the results buffer is full
while(resultsIndex < RESULTS_BUFFER_SIZE)
{
//wait for first set of 8 conversions to complete
while(0 == AdcaRegs.ADCINTFLG.bit.ADCINT3);
//clear both INT flags generated by first 8 conversions
AdcaRegs.ADCINTFLGCLR.bit.ADCINT1 = 1;
AdcaRegs.ADCINTFLGCLR.bit.ADCINT3 = 1;
//save results for first 8 conversions
//
//note that during this time, the second 8 conversions have
//already been triggered by EOC6->ADCIN1 and will be actively
//converting while first 8 results are being saved
AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT0;
AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT1;
AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT2;
AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT3;
AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT4;
AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT5;
AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT6;
AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT7;
//wait for the second set of 8 conversions to complete
while(0 == AdcaRegs.ADCINTFLG.bit.ADCINT4);
//clear both INT flags generated by second 8 conversions
AdcaRegs.ADCINTFLGCLR.bit.ADCINT2 = 1;
AdcaRegs.ADCINTFLGCLR.bit.ADCINT4 = 1;
//save results for second 8 conversions
//
//note that during this time, the first 8 conversions have
//already been triggered by EOC14->ADCIN2 and will be actively
//converting while second 8 results are being saved
AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT8;
AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT9;
AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT10;
AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT11;
AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT12;
AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT13;
AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT14;
AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT15;
}
//disable all ADCINT flags to stop sampling
AdcaRegs.ADCINTSEL1N2.bit.INT1E = 0;
AdcaRegs.ADCINTSEL1N2.bit.INT2E = 0;
AdcaRegs.ADCINTSEL3N4.bit.INT3E = 0;
AdcaRegs.ADCINTSEL3N4.bit.INT4E = 0;
//at this point, AdcaResults[] contains a sequence of conversions
//from the selected channel
//software breakpoint, hit run again to get updated conversions
// asm(" ESTOP0");
DELAY_US(1000);
}while(1);
}
__interrupt void epwm1_isr(void)
{
// Update the CMPA and CMPB values
update_compare(&epwm1_info);
// Clear INT flag for this timer
EPwm1Regs.ETCLR.bit.INT = 1;
// Acknowledge this interrupt to receive more interrupts from group 3
PieCtrlRegs.PIEACK.all = PIEACK_GROUP3;
}
update_compare(&epwm1_info)中改变EPWM1的相关参数(如CMPA和CMPB的值等)与本文的主要目的关系不大,这里不做主要介绍。
void update_compare(EPWM_INFO *epwm_info)
{
// Every 10'th interrupt, change the CMPA/CMPB values
if(epwm_info->EPwmTimerIntCount == 10)
{
epwm_info->EPwmTimerIntCount = 0;
// If we were increasing CMPA, check to see if
// we reached the max value. If not, increase CMPA
// else, change directions and decrease CMPA
if(epwm_info->EPwm_CMPA_Direction == EPWM_CMP_UP)
{
if(epwm_info->EPwmRegHandle->CMPA.bit.CMPA < epwm_info->EPwmMaxCMPA)
{
epwm_info->EPwmRegHandle->CMPA.bit.CMPA++;
}
else
{
epwm_info->EPwm_CMPA_Direction = EPWM_CMP_DOWN;
epwm_info->EPwmRegHandle->CMPA.bit.CMPA--;
}
}
// If we were decreasing CMPA, check to see if
// we reached the min value. If not, decrease CMPA
// else, change directions and increase CMPA
else
{
if(epwm_info->EPwmRegHandle->CMPA.bit.CMPA == epwm_info->EPwmMinCMPA)
{
epwm_info->EPwm_CMPA_Direction = EPWM_CMP_UP;
epwm_info->EPwmRegHandle->CMPA.bit.CMPA++;
}
else
{
epwm_info->EPwmRegHandle->CMPA.bit.CMPA--;
}
}
// If we were increasing CMPB, check to see if
// we reached the max value. If not, increase CMPB
// else, change directions and decrease CMPB
if(epwm_info->EPwm_CMPB_Direction == EPWM_CMP_UP)
{
if(epwm_info->EPwmRegHandle->CMPB.bit.CMPB < epwm_info->EPwmMaxCMPB)
{
epwm_info->EPwmRegHandle->CMPB.bit.CMPB++;
}
else
{
epwm_info->EPwm_CMPB_Direction = EPWM_CMP_DOWN;
epwm_info->EPwmRegHandle->CMPB.bit.CMPB--;
}
}
// If we were decreasing CMPB, check to see if
// we reached the min value. If not, decrease CMPB
// else, change directions and increase CMPB
else
{
if(epwm_info->EPwmRegHandle->CMPB.bit.CMPB == epwm_info->EPwmMinCMPB)
{
epwm_info->EPwm_CMPB_Direction = EPWM_CMP_UP;
epwm_info->EPwmRegHandle->CMPB.bit.CMPB++;
}
else
{
epwm_info->EPwmRegHandle->CMPB.bit.CMPB--;
}
}
}
else
{
epwm_info->EPwmTimerIntCount++;
}
return;
}
由以上程序可以看出,CPU2运行外设没有其他特别的步骤。主要还是需要在CPU1中对外设的归属进行声明以及初始化相关的GPIO。
/CPU1中程序/
InitEPwm1Gpio();//
EALLOW;
DevCfgRegs.CPUSEL0.bit.EPWM1 = 1;//connect to CPU2
DevCfgRegs.CPUSEL11.bit.ADC_A = 1;//connect to CPU2
EDIS;
代码均来自与TI公司例程,本人仅做了整理和分析。我所使用的例程链接贴一下:双核例程