一个简单地用有限状态机实现GPIO控制的程序,翼志的开发板配了个往复流水灯的实例,略作修改,屏蔽了不用的管脚配置…… 流水灯情况工作良好,大概是最简单的GPIO控制了吧。
硬件上与单片机并无差异,六盏共阳极LED接在F2812的GPIOA0~GPIOA5上。流水灯实现程序如下:
content offlowled.c
//包含头文件
#include "DSP28_Device.h"
#include "DSP28_Globalprototypes.h"
#define ShiftDir 0 //定义流水的方向
#define Timelag 15 //定义状态切换的时间间隔
unsigned int MuxValue=0;
unsigned int DirValue=0;
unsigned int QualValue=0;
//声明延时函数、及GPIO功能选择函数
void delay_loop(void);
void Gpio_select(void);
void main(void)
{
unsigned int DisplayBuffer = 0x0001;//初值寄存器,设初始时点亮一盏小灯
unsigned char DelayTimes;//延时时间控制
unsigned char SFTDIR = ShiftDir; //环移方向
//初始化系统控制寄存器
InitSysCtrl();
// 禁止所有 CPU中断:
DINT;
IER = 0x0000;
IFR = 0x0000;
// 将Pie控制寄存器初始化为默认值:
InitPieCtrl();
InitPieVectTable();
// Run GPIO test
MuxValue= 0x0000;// 配置 GPIO Mux寄存器为I/O
DirValue= 0xFFFF;// GPIO 方向寄存器值,全部管脚设为输出
QualValue= 0x0000;// 输入采样值,在该例中没用,所以全部管脚qual设置为0
Gpio_select();
// Toggle I/Os using DATA register for ever
while(1)
{
//将DisplayBuffer的值取反后送GPIO——共阳极LED
GpioDataRegs.GPADAT.all = ~DisplayBuffer;
if(SFTDIR==1)
{
DisplayBuffer <<= 1; //LED状态左移
if(DisplayBuffer == 0x0040)DisplayBuffer = 0x0001; //恢复初值,实现环移
}
else
{
if(DisplayBuffer == 0x001)DisplayBuffer = 0x0040; //恢复初值,实现环移
DisplayBuffer >>= 1; //LED状态右移
}
for(DelayTimes=0;DelayTimes
{
delay_loop();
}
}
}
void delay_loop()
{ //简单而粗暴的实现软延时
long i;
for (i = 0; i < 400000; i++) {}
}
void Gpio_select(void)
{
EALLOW; //允许访问保护区寄存器
//初始化全部GPIO的MUX,其实该例中只需要初始化GPAMUX即可,这里0表示全部设为输出
GpioMuxRegs.GPAMUX.all=MuxValue;
//GpioMuxRegs.GPBMUX.all=MuxValue;
//GpioMuxRegs.GPDMUX.all=MuxValue;
//GpioMuxRegs.GPFMUX.all=MuxValue;
//GpioMuxRegs.GPEMUX.all=MuxValue;
//GpioMuxRegs.GPGMUX.all=MuxValue;
//初始化全部GPIO的DIR,其实该例中只需要初始化GPADIR即可
GpioMuxRegs.GPADIR.all=DirValue;// GPIO PORTs as output
//GpioMuxRegs.GPBDIR.all=DirValue; // GPIO DIR select GPIOs as output
//GpioMuxRegs.GPDDIR.all=DirValue;
//GpioMuxRegs.GPEDIR.all=DirValue;
//GpioMuxRegs.GPFDIR.all=DirValue;
//GpioMuxRegs.GPGDIR.all=DirValue;
//初始化全部GPIO的QUAL,其实该例中我怀疑不用初始化也没问题
GpioMuxRegs.GPAQUAL.all=QualValue; // Set GPIO input qualifier values
//GpioMuxRegs.GPBQUAL.all=QualValue;
//GpioMuxRegs.GPDQUAL.all=QualValue;
//GpioMuxRegs.GPEQUAL.all=QualValue;
EDIS; //使能保护
}
======================================EOF===================================================
调试过程没有任何阻力…… 呵呵,这就是修改例程的好处。
从例程中大致看出2812的GPIO有A到G六组,但并非全部都是16位的,具体每组需要对应着数据手册里的硬件管脚说明来看,这里开发板把小灯接在GPIOA0~5还有个好处,日后可以通过PWM输出做渐变的效果。
官方手册上关于GPIO的内部功能结构用一张图来描述
左上角的矩形里包含了GPIO全部相关寄存器
GPIOxMUX.bit : 0——GPIO, 1——第二功能
GPIOxDIR.bit :0——输入,1——输出
GPIOxQUAL只有低8位有用,用来定义输入采样周期,数值为n(n<>0)时,采样周期为系统时钟的2*n倍;取0时为与系统时钟同步采样
这些寄存器都是受EALLOW保护的
数据(位操作)寄存器是不受EALLOW保护的
GPIOxDAT:GPIO的数据锁存器,与单片机的Px原理大致相同;
GPIOxSET.bit: 置位寄存器,为1时相应位对应的外部管脚被置高电平,为0没用;
GPIOxCLEAR.bit:清零寄存器,为1时相应位对应的外部管脚被置低电平,为0没用;
GPIOxTOGGLE.bit:位翻转寄存器,为1时相应位对应的外部管脚电平状态取反,为0没用;
验证该实例时,只用了前面4个寄存器,后面3个的功能还没试验。
显然,所有控制都是以位为单位的……
如果需要整字赋值,如实例中一般GPIOxXXXX.all
目前还有一处没想明白的地方:在通用管脚输出状态下,既然 给GPIOxDAT赋值即可改变该管教的状态,为啥还要SET和CLEAR两个寄存器?难道是给第二功能的管脚状态初始化用?
实践证明,使用TOGGLE的好处是,闪烁灯效果不再需要中间变量取反后重赋值来实现。
GpioDataRegs.GPATOGGLE.all=0xFFFF; //可以 实现A口全部小灯闪烁效果
用置位指令和清零指令的好处暂时没感受到,不过流水灯也可以这么实现
if(SFTDIR==1)
{
DisplayBuffer <<= 1; //LED状态左移
if(DisplayBuffer == 0x0040)DisplayBuffer = 0x0001; //恢复初值,实现环移
GpioDataRegs.GPACLEAR.all = DisplayBuffer;
GpioDataRegs.GPASET.all = ~DisplayBuffer;
}
else
{
if(DisplayBuffer == 0x001)DisplayBuffer = 0x0040; //恢复初值,实现环移
DisplayBuffer >>= 1; //LED状态右移
GpioDataRegs.GPACLEAR.all = DisplayBuffer;
GpioDataRegs.GPASET.all = ~DisplayBuffer;