DSP学习笔记
一、28335资源
1.28335实际上不是DSP而是DSC
2.28335集成了DSP和微控制器的长处,主要用在控制,DSP是数字信号处理,能够在一个周期内完成3232位的乘法累加运算,而普通的单片机需要4个周期以上
3.DSP具有快速的中断响应
4.28335为32位浮点DSP,主频是150Mhz有epwm的设备,Flash256k16位,SRAM34k*16位,ADC12位,80ns转换时间,0-3V输入,注意ADC最大电压3V!!!转换频率可达到12.5M
5.SCI-串口,有三个串口
6.两个通道的CAN
7.28335一共有176个管脚但是只有88个通用IO
8.有一个看门狗电路
9.引脚分类JTAG引脚,时钟引脚,复位引脚,电源引脚,ADC引脚,GPIO和其他外设引脚
10.如果程序在RAM里面的话调电会丢失,要烧录到FLASH里面调电才不会丢失。
11.学会GPIO,外部中断,定时器,串口就入门了,但其他资源更重要如SPI,IIC,SCI,ECAN,EPWM,ADC等
二、寄存器和存储器
1.存储器不具有地址信息,她的地址是芯片厂家分配,给存储器分配地址的过程就是存储器映射,再分配一个地址就叫重映射。
2.28335使用多级流水线的增强的哈佛总线结构,能够并行访问程序和数据存储空间
3.OPT就是只能存一次的存储器
4.Boot Rom空间是存储启动的程序的空间
5.SARAM计算过程中间变量的值什么的都存在这里
6.代码安全模块,通过一个128位的密码对安全区来进行加密或者解密,相当于八个十六位的字,如果都是写1就没有保护,全是0就无法解密
7.寄存器是什么?寄存器就是对存储器的地址进行取名,那么我们要去操作这个存储器的时候只要操作这个名字就可以了。
8.通过#pragma预处理命令和DATA SECTION把定义的寄存器指定到对应的存储单元里面,然后我们就可以用C语言来进行操作。假如我们需要设置GPIO口的上拉,我们可以先从芯片数据手册了解到哪个存储单元是这个功能,然后找到这个地址0011,然后给这个单元再取一个gpio_up_init的别名,那么这个gpio_up_init就是一个寄存器,她的地址就是0011,这个过程就叫寄存器映射。
三、工程模板
略
四、28335系统时钟配置
1.时钟源:有外部系统时钟和使用外部晶振以及内部振荡器组成的时钟源
2.外部时钟接入的两个方式
3.外部晶振是30M晶振,DSP内核最高频率是150M,而PLLCR只有几个挡位可以选,所以我们选的是10倍频,然后再进行二分频得到150M
4.修改CPU时钟只要在InitSysCtrl();函数里面修改DSP28_PLLCR-倍频系数和DSP28_DIVSEL-分频系数即可:
5.当修改了其他文件之后一定要保存之后再烧录,不然是不会生效的!!!!!!!!!!!
五、GPIO
1.GPIO(general purpose input output)是同用输入输出端口的简称,可以通过软件来控制输入输出
2.28335有176个引脚
3.GPIO一共分为3组分别是A(0-31),B(32-63),C(64-87)
4.GPIO系统框图
5.Qual实际上就是一个滤波器,就是用在按键消抖的地方,当一个低电平过来,要经过6个或者3个时钟周期后才能算是一个低电平,但凡有一个高电平的干扰出现都会重新开始计数,而GPxCTRL就是设置一个时钟周期的长短。
6.配置过程
EALLOW;
SysCtrlRegs.PCLKCR3.bit.GPIOINENCLK = 1;// 开启GPIO时钟
//LED端口配置
GpioCtrlRegs.GPAMUX1.bit.GPIO0=0;//设置为通用GPIO功能
GpioCtrlRegs.GPADIR.bit.GPIO0=1;//设置GPIO方向为输出
GpioCtrlRegs.GPAPUD.bit.GPIO0=0;//使能GPIO上拉电阻
GpioCtrlRegs.GPAMUX2.bit.GPIO23=0;//设置为通用GPIO功能
GpioCtrlRegs.GPADIR.bit.GPIO23=1;//设置GPIO方向为输出
GpioCtrlRegs.GPAPUD.bit.GPIO23=0;//使能GPIO上拉电阻
GpioCtrlRegs.GPAMUX2.bit.GPIO22=0;//设置为通用GPIO功能
GpioCtrlRegs.GPADIR.bit.GPIO22=1;//设置GPIO方向为输出
GpioCtrlRegs.GPAPUD.bit.GPIO22=0;//使能GPIO上拉电阻
EDIS;
SysCtrlRegs.PCLKCR3.bit.GPIOINENCLK = 1;// 开启GPIO时钟
剩下的就是设置为通用IO模型,设置为输出,以及初始化上拉电阻
7.GPIO输入配置
EALLOW;
SysCtrlRegs.PCLKCR3.bit.GPIOINENCLK = 1;// 开启GPIO时钟
//KEY端口配置
GpioCtrlRegs.GPAMUX2.bit.GPIO22=0;
GpioCtrlRegs.GPADIR.bit.GPIO22=0;
GpioCtrlRegs.GPAPUD.bit.GPIO22=0;
EDIS;
GpioDataRegs.GPASET.bit.GPIO22=1;
和输出只有一个差别,就是GPxDIR寄存器被设置为0,对照框图就是设置为输入模式
我们可以从GPADAT寄存器获得输入状态,可以用宏定义来简化后续程序,譬如:
#define KEY_H1 (GpioDataRegs.GPADAT.bit.GPIO12)
六、中断系统
1.28335有16个中断线,当中包括了2个不可屏蔽中断和14个可屏蔽中断。INT1-12都是可屏蔽的,INT13和14
2.
首先INTx.1这个x有12个,也就是下面那一套一共有12个,图中只画了1个,如PIEIERx就有12个
这一个非常容易混淆!那就是一共有12*8=96个中断,一共12组,每一组有8个对应关系如下
3.中断向量是什么?
当触发中断时,系统会把当前的地址压入堆栈,然后去到中断服务程序对应的地址,处理完之后再继续运行,那么中断服务程序对应的地址就是中断向量,不同的中断向量组成的表称之为中断向量表。
5.中断的配置
①使能PIEIERx,我们可以查,比如我们要使能外部中断1就找到是第一条中断线的第四个。
PieCtrlRegs.PIEIER1.bit.INTx4 = 1; // 使能PIE组1的INT4
②使能外部中断1
我们在这里不仅要配置中断的使能,还要配置上升或者下降沿触发。
XIntruptRegs.XINT1CR.bit.POLARITY = 0; // 下降沿触发中断
XIntruptRegs.XINT1CR.bit.ENABLE= 1; // 使能XINT1
③使能CPU中断,因为我们是第一条中断线,所以很明显就是把第一位设置为1
IER |= M_INT1;
④设置发生中断后CPU指针要跳转的地址
PieVectTable.XINT1 = &EXTI1_IRQn;
这个地址可以随便改,不一定是这个
⑤打开全局中断(中断总开关),调试事件使能
EINT; // 开全局中断
ERTM;
这里的ERTM意思目前未明确,大概的意思就是如果不使能,仿真器无法在实时状态下访问内存或者寄存器。
⑥编写中断服务函数
interrupt void EXTI1_IRQn(void)
{
PieCtrlRegs.PIEACK.bit.ACK1=1;
}
interrupt必须要加!!!!!
PieCtrlRegs.PIEACK.bit.ACK1=1;这个意思就是把PIEACK1写一个1,按照数据手册中写一个1就可以清零这个寄存器,从数据手册中可以看出写1不仅可以清零这个寄存器即做好下次中断的准备同时也可以让CPU响应这一组尚未响应的中断。
置于为什么要手动clear,因为你一旦发生中断之后,这个东西会自己设置为1,而如果为1的话外部的中断是无法进来的,可以从控制框图看出,所以要手动把它clear,也就是写1,切记,写1就会帮你复位!
⑦外部中断线与GPIO建立联系
不同的GPIO范围属于不同的外部中断,我们设置的是GPIO22,那么可以选择XINT1或者XINT2,我们选择XINT1
GpioIntRegs.GPIOXINT1SEL.bit.GPIOSEL = 22; // XINT1是GPIO22
⑧在主函数清除所有中断的标志寄存器和清除所有中断向量
固定套路
InitPieCtrl();
IER = 0x0000;
IFR = 0x0000;
InitPieVectTable();
七、定时器
1.28335有三个定时器,分别为time0、1、2。这三个定时器中1和2的中断信号没有经过PIE是直接到内核中断里面的中断信号分别为TINT0,TINT1,TINT2。分别对应中断向量INT1,INT13,INT14。注意,定时器0是经过PIE管理的其他两个没有
2.定时器的配置过程
EALLOW;
SysCtrlRegs.PCLKCR3.bit.CPUTIMER1ENCLK = 1; // CPU Timer 1
EDIS;
EALLOW;
PieVectTable.TINT2 = &TIM2_IRQn;
EDIS;
// Initialize address pointers to respective timer registers:
CpuTimer2.RegsAddr = &CpuTimer2Regs;
// Initialize timer period to maximum:
CpuTimer2Regs.PRD.all = 0xFFFFFFFF;
// Initialize pre-scale counter to divide by 1 (SYSCLKOUT):
CpuTimer2Regs.TPR.all = 9;
CpuTimer2Regs.TPRH.all = 0;
// Make sure timers are stopped:
CpuTimer2Regs.TCR.bit.TSS = 1;
// Reload all counter register with period value:
CpuTimer2Regs.TCR.bit.TRB = 1;
// Reset interrupt counters:
CpuTimer2.InterruptCount = 0;
ConfigCpuTimer(&CpuTimer2, Freq, Period);
CpuTimer2Regs.TCR.bit.TSS=0;
IER |= M_INT14;
EINT;
ERTM;
PRD其实就是计数的寄存器,而TPR,TPRH其实就是分频器,我填个9实际上就是9+1=10分频,那么要注意的是,定时器1的中断向量不是TINT1,而是XINT13,其他都是TINT2,TINT0
Timer->RegsAddr->TCR.bit.SOFT = 1;
Timer->RegsAddr->TCR.bit.FREE = 1; // Timer Free Run Disabled
SOFE和FREE寄存器其实就是你在断点的时候,定时器是继续运行还是暂停
由于配置比较简单其他的就不再赘述。
八、PWM模块
1.28335有6对epwm,一共有12路epwm,还有6个APWM,一共有18路PWM输出
注意:这个章节是重点,是这款单片机最有价值的地方之一!
2.6个epwm模块互相独立,他们可以工作在不同步或者同步的模式下,注意同步和不同步是什么意思呢?就是他们干同一件事比如产生中断这个时间是不是一样,我们可以让他们的模块都是同一个时钟线就可以了,28335使用了级联的方法
每一个epwm模块里面都包括了时基模块(TB),比较计数模块(CC),动作模块(AQ),死区模块(DB),等等下面分别介绍
时基模块:
作用:这个模块决定了pwm的频率和相位
工作原理:
SYSCLKOUT经过分频之后形成TBCLK,时基时钟,然后Dir设置往上计数还是往下计数还是中间计数。上面那个就是一个控制PWM周期的寄存器,那么上面有一个影子寄存器,那么影子寄存器有什么用呢?影子寄存器就是起一个缓冲的作用,就是说我们可以在要对该寄存器的值进行修改的时候,先把值给影子寄存器,当这个这个周期的PWM发生完之后,影子寄存器再把值给到真正的寄存器,所以就可以避免软件和硬件不同步的问题,增强系统的稳定性。
Load是一个控制相位的开关,也就是说当load来一个上升沿的时候计数器的当前值被强制设置为相位著主寄存器的值,这样我们可以通过设置不同的值来设置不同的相位,那么要使用它就必须要闭合开关(PHSEN),并且!需要给一个同步信号,那么TBPHS的值和相位之间的关系数据手册中有讲述,关系大概为:相位=(TBPHS/TBPRD)x360。至此时基模块讲述完毕
作用:设置比较值,有比较值就可以在计数的时候产生对应的事件,然后根据这个事件产生对应的信号给AQ模块
工作原理:
由LOADxMODE选择加载方式,SHDWxMODE选择是否使用影子寄存器,给一个上升沿就会有加载进去,当CMPx的值等于TBCTR的值就触发,这个比时基会简单一点
这个模块其实也非常简单,左边就是TB和CC模块的一些信号,那么AQ模块就是设置这些信号发生的时候,PWM电平做什么变化,然后对于TB模块不同的计数模式有不同的优先级,在第3.2.4.3小节中会提及。比如
EPwm2Regs.AQCTLA.bit.ZRO = AQ_SET; // Set PWM1A on Zero
EPwm2Regs.AQCTLA.bit.CAU = AQ_CLEAR; // Clear PWM1A on event A, up count
就是向上计数模式的时候到了溢出值然后清零的时候产生的信号使pwm为1达到比较值的时候让它清零。
这里有点难理解的是,其实PWMA只对上升沿进行延时,PWMB只对下降沿延时,就比如我选了AH,也就是两个信号都不变,那么可以发现的是AH的PWMA和PWMB和上面的图中第2,3个是一样的,也就是上升沿延迟和下降沿延迟,而POLSEL只是对这两个进行取反而已,那么也就决定了死区时间内两个是高电平还是低电平以及是不是取反,切记切记,其实PWMA只对上升沿进行延时,PWMB只对下降沿延时。
配置代码如下:
ePWM1:
void EPWM1_Init(Uint16 tbprd)
{
EALLOW;
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1; // Disable TBCLK within the ePWM
SysCtrlRegs.PCLKCR1.bit.EPWM1ENCLK = 1; // ePWM1
EDIS;
InitEPwm1Gpio();
// Interrupts that are used in this example are re-mapped to
// ISR functions found within this file.
EALLOW; // This is needed to write to EALLOW protected registers
PieVectTable.EPWM1_INT = &epwm1_isr;
EDIS; // This is needed to disable write to EALLOW protected registers
EALLOW;
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0; // Stop all the TB clocks
EDIS;
// Setup Sync
EPwm1Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO; // Pass through
// Allow each timer to be sync’ed
EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE;
EPwm1Regs.TBPHS.half.TBPHS = 0;
EPwm1Regs.TBCTR = 0x0000; // Clear counter
EPwm1Regs.TBPRD = tbprd;
EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;
EPwm1Regs.TBCTL.bit.HSPCLKDIV=TB_DIV1;
EPwm1Regs.TBCTL.bit.CLKDIV=TB_DIV1;
// 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.half.CMPA = 0; // Set compare A value
EPwm1Regs.CMPB = 0; // 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_CLEAR; // Set PWM1B on Zero
EPwm1Regs.AQCTLB.bit.CBU = AQ_SET; // Clear PWM1B on event B, up count
// Active Low PWMs - Setup Deadband
EPwm1Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;
EPwm1Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;
EPwm1Regs.DBCTL.bit.IN_MODE = DBA_ALL;
EPwm1Regs.DBRED = 1000;
EPwm1Regs.DBFED = 1000;
EPwm1Regs.ETSEL.bit.INTSEL = ET_CTR_ZERO; // Select INT on Zero event
EPwm1Regs.ETSEL.bit.INTEN = 1; // Enable INT
EPwm1Regs.ETPS.bit.INTPRD = ET_1ST; // Generate INT on 1st event
EALLOW;
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1; // Start all the timers synced
EDIS;
// Enable CPU INT3 which is connected to EPWM1-3 INT:
IER |= M_INT3;
// Enable EPWM INTn in the PIE: Group 3 interrupt 1-3
PieCtrlRegs.PIEIER3.bit.INTx1 = 1;
// Enable global Interrupts and higher priority real-time debug events:
EINT; // Enable Global interrupt INTM
ERTM; // Enable Global realtime interrupt DBGM
}
interrupt void epwm1_isr(void)
{
static Uint16 cnt=0;
cnt++;
if(cnt==5000)
{
cnt=0;
LED3_TOGGLE;
}
// 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;
}
void EPwm1A_SetCompare(Uint16 val)
{
EPwm1Regs.CMPA.half.CMPA = val; //设置占空比
}
void EPwm1B_SetCompare(Uint16 val)
{
EPwm1Regs.CMPB = val; //设置占空比
}
那么epwm1的
EPwm1Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO; // Pass through
必须要设计不然下面的几个epwm模块没有同步信号的,这个就是同步信号源!
epwm6只做了一点点修改
void EPWM6_Init(Uint16 tbprd)
{
EALLOW;
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1; // Disable TBCLK within the ePWM
SysCtrlRegs.PCLKCR1.bit.EPWM1ENCLK = 1; // ePWM1
EDIS;
InitEPwm6Gpio();
// Interrupts that are used in this example are re-mapped to
// ISR functions found within this file.
EALLOW; // This is needed to write to EALLOW protected registers
PieVectTable.EPWM6_INT = &epwm6_isr;
EDIS; // This is needed to disable write to EALLOW protected registers
EALLOW;
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0; // Stop all the TB clocks
EDIS;
EPwm6Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN;
// Allow each timer to be sync’ed
EPwm6Regs.TBCTL.bit.PHSEN = TB_ENABLE;
EPwm6Regs.TBPHS.half.TBPHS = 5000;//延迟30度,(5000/15000)*360
EPwm6Regs.TBCTR = 0x0000; // Clear counter
EPwm6Regs.TBPRD = tbprd;
EPwm6Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;
EPwm6Regs.TBCTL.bit.HSPCLKDIV=TB_DIV1;
EPwm6Regs.TBCTL.bit.CLKDIV=TB_DIV1;
// Setup shadow register load on ZERO
EPwm6Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
EPwm6Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
EPwm6Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO;
EPwm6Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO;
// Set Compare values
EPwm6Regs.CMPA.half.CMPA = 0; // Set compare A value
EPwm6Regs.CMPB = 0; // Set Compare B value
// Set actions
EPwm6Regs.AQCTLA.bit.ZRO = AQ_SET; // Set PWM1A on Zero
EPwm6Regs.AQCTLA.bit.CAU = AQ_CLEAR; // Clear PWM1A on event A, up count
EPwm6Regs.AQCTLB.bit.ZRO = AQ_CLEAR; // Set PWM1B on Zero
EPwm6Regs.AQCTLB.bit.CBU = AQ_SET; // Clear PWM1B on event B, up count
// Active Low PWMs - Setup Deadband
EPwm6Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;
EPwm6Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;
EPwm6Regs.DBCTL.bit.IN_MODE = DBA_ALL;
EPwm6Regs.DBRED = 1000;
EPwm6Regs.DBFED = 1000;
EPwm6Regs.ETSEL.bit.INTSEL = ET_CTR_ZERO; // Select INT on Zero event
EPwm6Regs.ETSEL.bit.INTEN = 1; // Enable INT
EPwm6Regs.ETPS.bit.INTPRD = ET_1ST; // Generate INT on 1st event
EALLOW;
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1; // Start all the timers synced
EDIS;
// Enable CPU INT3 which is connected to EPWM1-3 INT:
IER |= M_INT3;
PieCtrlRegs.PIEIER3.bit.INTx6 = 1;
// Enable global Interrupts and higher priority real-time debug events:
EINT; // Enable Global interrupt INTM
ERTM; // Enable Global realtime interrupt DBGM
}
void EPwm6A_SetCompare(Uint16 val)
{
EPwm6Regs.CMPA.half.CMPA = val; //设置占空比
}
void EPwm6B_SetCompare(Uint16 val)
{
EPwm6Regs.CMPB = val; //设置占空比
}
interrupt void epwm6_isr(void)
{
EPwm6Regs.ETCLR.bit.INT = 1;
PieCtrlRegs.PIEACK.all = PIEACK_GROUP3;
}
想必能看出来修改的只有相位那一部分而已。
其他部分就不再讲述,那么至此epwm模块就讲到这里。
欢迎各位找我讨论,共同进步~