F28069M开发板笔记
所需的相关程序及代码库(头文件、样例):
- CCSTUDIO(CCS):代码编写及程序烧录。
- C2000WARE:代码与硬件关联所需的各种头文件及样例。
- MOTORWARE:用于电机控制的开发板的头文件及样例。
F28069M开发板
ti官网-LAUNCHXL-F28069M 包含如下信息:
- Software development:上述代码库都可以在其中找到
- Technical documentation:中能看到板子的参考文档 - LAUNCHXL-F28069M overview (Rev. B),参考文档中可以看到板子的线路图及一些注意事项。
这些信息对于代码开发细节完全不够,还需要找到处理器的相关文档,找到ti官网-TMS320F28069中的User guide文档TMS320x2806x Microcontrollers Technical Reference Manual (Rev. I)包含了所有的寄存器的定义的赋值含义,对后续看样程非常重要,如果有不理解的变量全部能在里面搜索到。
样程使用方法
进入到 C2000
的安装目录下,{C2000路径}/device_support/f2806x/
就是开发板对应的项目位置,我们将其记为 <base>
,下面分析其中每个文件的作用:
<base>/docs/
:参考文档,USER_GUIDE
为开发者手册,包含对本项目架构的介绍及使用方法。<base>/headers/
:底层外设头文件 (Peripheral header files),包含对开发板中各项内存参数位置的定义,要集成到项目中需要使用其中一部分的定义,项目中更多地使用common/
下的头文件定义(也是间接调用此头文件)<base>/common/
:样例中的常用头文件定义,样例中调用的头文件通常来自于此-
https://ecnhf41t16x1.feishu.cn/sync/TzS3d9wocsLU6XbTWrKcNqYNnAe
-
source/
:常用的*.c
文件 -
cmd/
:包含一些.cmd
文件,他们都是指定如何将编译后的代码和数据映射到处理及的内存或闪存中,二者的区别在于,*RAM*
不将编译后的代码烧录进闪存,断电后就会清空编译的代码;不带RAM
则是将代码烧录进闪存中,从而可以断电后,启动即使执行之前的代码(XIP, Execute in place)
-
<base>/examples/
:样例文件c28/
:CCS中的项目文件cla
:基于CLA中的C编译器(Control Law Accelerator, CLA 控制律加速器,TI板的功能,一个完全可编程的独立32位浮点硬件加速器),主要用于加速一些数学密集型计算
外设中断PIE处理流程
参考寄存器文档中的1.6节 Peripheral Interrupt Expansion(PIE), 有如下几个重要的流程图:
中断生成流程图1 |
中断生成流程图2 |
首先开发板将所有的外设中断信息划分为了12组每个组最多可处理8个中断, 简记为 x.y
, x表示从属的组号, y表示每组中的编号, 中断是否被启用看IFR(Interrupt Flag Register)是否被激活, 中断是否可以被接受看IER(Interrupt Enable Register), 前面带有PIE的就是在PIE部分处理的, 没有PIE的就是直接写在CPU内核中的寄存器. 左图展示了中断信号到达CPU的流程, 主要分为两大块:
- PIE部分: 中断首先激活PIEIFR, 判断是否启用了对应的PIEIER, 如果启用则通过MUX(multiplexer, 多路复用)转为按照组别转化为INTx (如果组x里面有一个中断被激活, 则对应的INTx就会被激活), 判断当前的信号是否正在被处理中PIEACK=1, 如果没被处理, 则自动置PIEACK=0, 进入CPU处理部分
- CPU部分: 获取到PIE处理后的INTx变量后, 类似的逻辑, 首先激活对应的IFR, 判断是否启用了对应的IER, 如果启用则通过MUX转化为统一中断指令, 判断INTM (全局中断激活)是否为0, 如果是0则处理中断, 中断的处理顺序为: 按照(x,y)二元组从小到大的顺序进行, 对于每个中断处理, 从PIEVecTable (这里Vector表示的就是C语言中的函数句柄)找到对应的函数所处内存中的位置, 对其进行执行.
综上, 处理中断的思路就是:
- 初始化CPU中断, 包括:
DINT;
关闭全局中断;IER, IFR
清空CPU中断信息;
- 初始化PIE中断, 包括:
InitPieCtrl();
清空PIE控制中断, 包括PIEIER, PIEIFR;InitPieVectTable();
清空PIEVecTable;
- 用户在自定义中断, 包括:
- 实现中断函数
PieVectTable.[中断名称] = &func
(中断函数细节: 函数类型为interrupt void
; 在函数结束时, 记得将对应的中断处理寄存器进行更新PieCtrlRegs.PIEACK.bit.ACKx = 1;
, 否则会一直等待当前处理, 不会触发下一个中断啦), 将中断函数配置到PieVectTable
中的对应位置; - 初始化各种所需的外设参数;
- 实现中断函数
- 启动中断检测:
- CPU:
IER |= M_INTx;
启动IER中对应的Group x检测; - PIE:
PieCtrlRegs.PIEIERx.bit.INTy = 1;
启动x.y对应的PIE中断检测; EINT;
启动全局中断检测.ERTM;
允许对中断进行DEBUG.
- CPU:
外设与中断的关系图如下 (更详细的外设与中断的关系表请见177面的Table 1-119, PIE Vec关系请见178面的Table 1-120):
通用输入输出接口GPIO使用方法
GPIO(General-Purpose Input/Output)就是板子上的各种针脚, 它们功能包含
- 作为输入/输出(默认全部都是input状态, 输入指的是测量电压变化, 输出指的是能够发出相对低/高电平), 对于用于输入的接口, 当我们加入一个采样窗口(sample window)对信号进行采样就可以对信号进行接收.
- 每个GPIO还具有多路复用的功能, 通过MUX可以选择三种不同的外设信号, 不同接口具有不同的外设功能, 功能包含:产生中断(PIE), 做联合测试JTAG(Joint Test Action Group), 接收ePWM, 每个接口的外设功能请见122面的表1-64.
GPIO接口被分为三大块GPIO0~31属于接口A, GPIO32~58属于接口B, 还有一个分析接口AIO0~15, 它们分别简称为GPIOA (GPA), GPIOB (GPB), AIOx, 前两个又能统称为GPX(X=A或B), 下面我们不考虑AIO接口, 用X.y
的格式表示从属第X组的接口y.
GPIO作为数字输出接口
由于默认是输入接口, 下面介绍输出接口的配置方法以及操作输出信号的方法:
- 配置接口模式, 通过
GpioCtrlRegs.GPXDIR.bit.GPIOy = 1
将该接口设置为输出. - 输出模式包含如下操作:
GpioDataRegs.GPXDAT.bit.GPIOy
直接对当前接口值进行修改, 0表示低电平, 1表示高电平. 当设置为输出时, 默认为低电平状态, 不建议直接对这个寄存器进行修改, 而是使用下面这些代表操作的寄存器 (因为多次直接对其修改, 可能会导致上下文状态变化产生延迟).- 下述寄存器只有赋值为1时才有作用, 在执行完成后会重新置为0, 并且操作不会干扰其他寄存器状态的延迟:
GpioDataRegs.GPXSET.bit.GPIOy = 1
将接口设置为高电平;GpioDataRegs.GPXCLEAR.bit.GPIOy = 1
将接口设置为低电平;GpioDataRegs.GPXTOGGLE.bit.GPIOy = 1
将接口状态进行变化, 做一次异或操作.
闪存Flash使用方法
烧到flash在代码上非常简单(如果不追求速度), 首先要了解烧录板子的逻辑, 编译逻辑:
- CCS中的C语言编译器将我们写的代码编译完成后, 得到
.obj
文件, 该文件包含了逻辑处理, 但还没有分配内存位置; - 通过TI版中的链接文件(
*.cmd
), 将所有的目标文件分配对应的位置, 得到可执行文件.out
, 对板子进行烧录.
Cmd文件简单理解可以参考TI 链接器命令文件入门
DSP开发板执行逻辑:
- 在DSP上电或者复位后, 处理器会跳转到启动地址
.reset
位置(在*.cmd
中进行了定义) - 启动
main
函数,main
函数的执行位置取决于.reset
定义的位置, 也就是说函数可以在Flash中运行的, 只是相对于RAM读取速度会降低, 后面就是正常执行函数了.
因此我们要将程序烧录到Flash只需修改*.cmd
文件, 将内存分配设置为Flash就行, 如果我们还想将某些重复调用的函数(通常是interrupt函数)从Flash转移到RAM中, 就需要加一些额外的内存转移和声明代码了.
Flash与RAM烧录方法
在例程的工程项目中一定可以找到F28069.cmd
或者28069_RAM_lnk.cmd
, 它们分别是烧录到Flash和RAM中的, RAM代码可以直接通过修改*.cmd
文件转为Flash, 反之, 在你的代码中没有Flash转移到RAM的前提下, 也是可以直接修改*.cmd
文件达到的.
首先我们找到这两个文件的所处文件夹: <base>\device_support\f2806x\common\cmd
, 修改*.cmd
方法很简单, 右键项目名称 -> Properties -> [Linker command file] Browse... (这里一定要选Browse, 不要点下三角选择, 因为CCS自带的cmd文件不正确, 要选择C2000中的cmd) -> 进入<base>\device_support\f2806x\common\cmd文件夹 -> 选择你想要的.cmd文件打开
, 就可以看到左侧原来的cmd文件被Exclude掉了, 新的cmd文件被加进去.
如果我们想重新启动之前的cmd, 只需把不用的cmd文件右键Exclude from Build
, 再将要用的cmd解除exclude就行了.
完成上述cmd文件修改后, 直接烧录程序就可以将文件烧录到RAM或Flash了, 检测是否烧到Flash只需要把数据线拔掉重插, DSP就会自动执行Flash中的程序, 看看是否和你烧录的一致.
注意: 如果使用
DELAY_US(delay)
对程序加入延迟, 就必须使用下面的memcpy
将该延迟函数转移到RAM中去.
将函数从Flash转移到RAM提高读取速度
代码在Flash中运行速度不及RAM的, 但我们可以手动将某些反复调用的函数(main函数不能通过这个方法放)从Flash放到RAM中, 方法如下:
这样程序在调用定义在ramfuncs
中的函数时, 就会直接去RAM中找, 而非Flash了, 因此必须在调用前执行memcpy
, 才能避免函数无法执行的问题.
Flash状态变换(降低功率)
通过如下代码可以控制Flash的开关:
注意: 如果是在子函数中进行的状态设置, 该函数必须在RAM中, 否则会报错. 这几种状态没看出来有什么区别, 文档中说是可以降低功率. 如果将Flash设置为待机, 下次从Flash中读取函数时候又会重启Flash, 但看不出什么延迟.
增强脉冲宽度调制器ePWM使用方法
ePWM(enhanced pulse width modulator)就是01信号脉冲, 可以通过宽度变换将数字波转换为模拟波, 是一种对波形的数模转换(DAC, digital-to-analog).
该开发板总共包含8个ePWM接口, 但是通过针脚引出去的只有13个(ePWM1~6, ePWM7B), 每个ePWM接口分为A,B两个波形分别对应两个接口, ePWM功能很多, 下面对其中的计数器和中断触发机制进行介绍.
计数器 (Time-base, TB)
参考文档中251页3.2.2节内容, 位于开发板中的每个ePWM都自带一个计数器, 称为PWMCTR (PWM counter), 首先介绍这块重要的英文缩写: SYNC同步, O (Output, 输出), I (Input, 输入), PHS (Phase, 相位), PRD (Period, 周期), CTR (counter, 计数器), CTL (control, 控制器).
每个计数器包含以下重要参数:
下文中的计数器同步只有在启动相位PHSEN时候才有意义, 同步就是当触发同步条件时, 将当前的计数器重置到相位对应的值上.
SYNCI
: 同步输入, 从下左图中可以看出, 第n个ePWM的同步输入就是第n-1的SYNCO
的同步状态.SYNCO
: 同步输出, 有四种选择:TB_SYNC_INTB_SYNC_IN
: 跳过当前计数器, 也就是直接将同步输入的信息继续往后面的ePWM传递;TB_CTR_ZERO
: 如果当前计数器为0时, 则产生同步信息;TB_CTR_CMPB
: 这个和计数器比较 (coutner-compare) 部分有关, 还不清楚具体是什么;TB_SYNC_DISABLE
: 直接关闭输出同步.
PHSEN
: 是否启动相位, 如上文所述, 只有启动相位计数器的同步才有作用.TBPHS
: 相位设置范围是uint16.TBPRD
: 计数器周期, 也就是计数器的上界, 范围也是uint16.CTRMODE
: 计数器模型, 有三种可选 (见下右图):TB_COUNT_UP
: 向上计数模式, 一直增加当前计数器, 当到达到上界(周期)时, 直接重置为0.TB_COUNT_DOWN
: 向下计数模式, 一直减少当前计数器, 当到达到0时, 直接重置为上界(周期).TB_COUNT_UPDOWN
: 向上后向下计数模式, 一直增加当前计数器, 当到达到上界时, 变为向下计数模式, 当到达0时, 再变为向上计数模式, 以此往复.
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC
: ePWM的全局时间同步, 当开启时候, 则将全部的ePWM同步到上边缘, 在设置上文内容前需要先关闭全局时间同步, 配置完成后, 再打开.
ePWM同步链逻辑 |
计数器三种模式对应的波形图 |
一个简单例子:
计数比较器 (Counter-Compare, CC)
通过与计数器当前的数字进行比较, 当数字与当前端口设定相同时, 会产生一次事件, 该事件会向下文中的AQ中传递, 用于控制PWM波生成. 每个EPWM端口可以生成A,B两种不同的波, 将第x个端口输出的波分别记为EPWMxA, EPWMxB, 处理逻辑图如下所示:
动作限定符 (Action-Qualifier, AQ)
动作限定符AQ需要和上述的比较器端口相配合, 从而控制PWM波形的构造生成, AQ可以被视为一个可编程的事件开关, 它的输入为各种不同的事件, 输出为PWM波的0,1信号.
AQ输入与输出(从上到下的输入事件分为别: 计数器到达周期值(上界), 计数器归零(下界), 计数器等于CMPA, 计数器等于CMPB, 由软件产生的事件) |
ePWM占空比控制方法1(271页) |
中断触发 (Event trigger, ET)
参考文档3.2.8第292页, 这个比较简单, 只需要根据计数器的状态触发中断即可, 包含以下参数:
INTSEL
: 触发模式选择, 包含如下这几种选项ET_CTR_ZERO
: 当计数器为0时;ET_CTR_PRD
: 当计数器达到上界(周期)时;ET_CTR_PRDZERO
: 当计时器为0或达到上界(周期)时;ET_CTR(U/D)_CMP(A/B)
: 与计数器比较相关的, 暂不清楚.
INTEN
: 启用中断.INTPRD
: 中断产生周期, 包含如下这几种选项ET_DISABLE
: 不产生(默认);ET_1ST
,ET_2ND
,ET_3RD
: 遇到一/二/三个触发就产生一次中断;
一个简单例子:
控制器区域网络CAN使用方法
该开发板包含32个mailbox, 一个mailbox可以定义消息的收或发, 每个消息的长度为8byte也就是64bit, 每个消息均定义在mailbox中, 这里就讨论基础的ecan消息, 这部分划分的内存如下
ECAN参数内存位置分布 |
中断逻辑 |
比较重要的参数包含, 以下参数每个mailbox都有其各自的对应位:
- Mailbox Enable, CANME: 使能位;
- Mailbox Direction, CANMD: 发送1/接收0, 默认为接收;
- Transmission Request Set, CANTRS: 发送请求位, 1表示启动一次信息发送, 默认为0;
- Transmission Acknowledgement, TA: 发送成功位, 如果是0则表示发送成功, 通过while死循环可以判断消息是否发送成功;
- Received Message Pending, CANRMP: 收到消息等待位, 此位需要手动判断赋值为1 (一般在can接收中断中赋值), 表示收到一次消息, 读取改值没有意义;
- Master Control, CANMC: CAN模式控制, 可以切换为self-test-mode;
- Mailbox Interrupt Mask, CANGIM: CAN中断使能位;
- Mailbox Interrupt Mask, CANMIM: 1表示启动该mailbox的中断, 当收发消息会触发中断 (默认为0);
- Mailbox Interrupt Level, CANMIL: can相关的中断仅有两个, 默认为INT0CAN, 当设置为1时, 触发INT1CAN;
每个mailbox的信息:
- Message Identifier, MSGID: 消息对应的id编号, 对于接收的mailbox, 只会接收对应ID的信息;
- Message Control, MSGCTRL: 对于当前mailbox的配置, 修改当前信息长度data length code(DLC)等;
- Message Data Low: mailbox信息的前4bytes;
- Message Data High: mailbox信息的后4bytes.
这里不再演示样例中的2806xECanBack2Back
自我测试模式, 假设我们有一个CAN交换器, 通过两根导线链接到dsp板子的can通讯针脚上, 通过如下代码实现一个简单的信息交换(用到的依赖文件与ECanBack2Back
相同):
单个DSP板: 中断函数处于RAM中, 收发延迟1000us, 处于Flash中, 收发延迟1600us. 因此还是推荐将函数转移到RAM中.
LEDBlink
参考开发手册中p177页的Interrupt Vector Table 和 p106页CPU Timer相关的TCR (Time Counter)配置.
初始化
样例位于 <base>/examples/c28/timed_led_blink
,开发板程序一般需要如下的配置:
在 main
函数中需要初始化以下信息:
这里查看InitCpuTimers()
的源代码可以发现,其将CpuTimer0Regs.TCR.TSS
设置为1,暂停了CPU计时功能,地18行则是将其置为0重新启动了计时功能,并将TIE设置为1。
自定义内容
经过上面4步的初始化设置后,下面就是用户自定义的各种功能,比如我们这想控制LED灯开关,我们从板子的参考文档的第14页可以发现下图:
LED相关的电路图 |
LED灯的位置 |
上图中LED灯为D9和D10(红色中间和蓝色上面),如果我们想通过CPU控制其亮灭,那么就要找到对应的GPIO编号,通过调整其电压高低,从而控制亮灭,默认情况下GPIO应该是Input模式,应该相当于一个通路,想要产生电势差就需要改为Output模型:
控制GPIO电平高低方法是通过修改寄存器GPBSET, GPBCLEAR, GPBTOGGLE
这三个均为1bit
变量(从属于GpioCtrlRegs
),含义分别为启动高电平、启动低电平、切换当前电平,因此如果我们想要灯光闪烁那么就只需要将GPBTOGGLE
设置为1
即可,因此我们只需要把中断触发函数(上文中step3中的func
)写成如下形式即可:
上述代码中,我们还需要确认TINT0
从属的组别,进入到F2806x_PieVect.h
下,找到TINT0
,不难发现其在Group1第7个,因此上文信号确认中对Group1进行确认。
最后将全部的中断检测重新打开,并进入死循环:
代码简化
综上我们可以将跑马灯代码简化如下,效果如下图所示:
创建新项目后,有一个空白的main.c
文件把下面代码拷贝进去,然后还需要把相关的.c
文件(直接从Example_2806xCpuTimer
里面拷贝)和头文件加载进来,头文件加载方式右键项目名->Properties->Build->C2000 Compiler->Include Options->Add dir to #include右边的加号->Browse->将``上文中提到的头文件``都加载一遍一共4个
):
下面代码只要按照Flash与RAM烧录方法中将cmd文件替换为
F28069.cmd
就可以直接烧录到Flash中了, 在断电后也可以继续执行跑马灯代码.
ePWM Timer from Flash
样例位于<base>\examples\c28\flash_f28069
, 这是一个将代码烧录到Flash中的程序,其还使用了ePWN(enhanced pulse width modulator 增强脉宽调制).
使用方法: 这个程序给出了通过ePWM的计时器产生中断的方法, 但是这个产生的频率非常高, 我们只能通过三个计数器的数值大小来看相对的关系. 使用debug模型运行代码, 启动代码后过一会, 按暂停按钮, 在变量列表中查看EPwm1TimerIntCount
,EPwm2TimerIntCount
,EPwm3TimerIntCount
的数值大小, 可以看到它们是3:2:1的关系, 主要是学习了将程序烧到Flash中的方法.
初始化
ePWM计数器以及中断产生的初始化
他将初始化函数定义为InitEPwmTimer
,对ePWM的初始化非常复杂,我们以一个ePWM初始化为例,主要工作就是初始化与外围模块的时钟信息,即TB(time base) 时钟的各种参数:
C/C++源码理解
简单看了下头文件的写法,记录一些有意思的东西
宏定义
在头文件 *.h
中主要就是进行各种内存、变量的位置定义,常见的一种写法
Union数据结构
在F2806x_Cputimers.h, F2806x_Gpio.h
可以看到一种通过struct
和union
结合的方式,这种union
数据类型通常包含两个共享内存的变量all, bit
它们分别表示整个分配的内存和对内存的划分,通过:位数
来对内存按位进行划分的方法,其使用方法如下