标签:
杂谈 |
分类: DSP |
这一讲同上一讲一样,先讲解硬件部分,再讲解软件实验部分。
GPIO模块:(通用输入/输出模块)
TMS320F28335对所有数字量IO进行分组,分为A、B、C三组。A组包括GPIO0至GPIO31,B组包括GPIO32至GPIO63,C组包括GPIO64至GPIO87.需要注意的是,虽然GPIO每个引脚多个功能复用,但在同一时刻只能使用其中一个功能。GPIO多路复用(MUX)寄存器来选择对复用引脚的操作。
由于以下涉及到很多的寄存器内部单元与GPIO引脚间的对应关系,文字可能比较抽象。具体安排可以看关于GPIO模块内部各个寄存器的讲解图(从75页开始)。sprufb0d(TMS320x2833x, 2823x System Control and Interrupts)
GPIO MUX寄存器(多路复用寄存器)用来实现GPIO多功能复用引脚的功能模式选择,各个引脚可独立配置在GPIO模式或外设专用功能模式。比如GPIO0有I/O功能和PWM输出功能就可以通过配置MUX来选择其中的一个功能来使用。如果配置在GPIO模式,还需要通过GPIO的方向控制寄存器GPxDIR(x为A、B、C)设置GPIO模式作为输入还是作为输出。作为输入时,还可以限制输入信号的脉宽以消除不必要的噪声(通过GPxQSELn、GPACTRL和GPBCTRL寄存器)。
GPxMUXn(32位):多路复用寄存器。(x=A、B、C ,n= 1、2)
http://s13/middle/0034sXQLzy73jDUNRG45c&690
每一个引脚由2位控制:
GPAMUX1:负责GPIO0~GPIO15的引脚功能选择。
GPAMUX2:负责GPIO16~GPIO31的引脚功能选择。
GPBMUX1:负责GPIO32~GPIO47的引脚功能选择。
GPBMUX2:负责GPIO48~GPIO63的引脚功能选择。
GPCMUX1:负责GPIO64~GPIO79的引脚功能选择。
GPCMUX2:负责GPIO80~GPIO87的引脚功能选择。
GPxDIR(32位):方向控制寄存器(x为A、B、C)
每个引脚对应1位:
GPADIR:GPIO0~GPIO31
GPBDIR:GPIO32~GPIO63
GPCDIR:GPIO64~GPIO87
1.GPIO模式选择控制(带*目前只需了解即可)
引脚功能分配、输入信号限制和外部中断源(XINT1~XINT7、XNMI)都是由GPIO配置控制寄存器来控制的。
配置GPIO模式有以下几方面:
a.通过GPxMUXn配置引脚功能;
b.对于数字I/O口,通过配置GPxDIR来配置引脚的方向;
c.通过配置GPxCTRL、GPxQSELn来对输入引脚进行限制;
*d.通过配置GPxPUD相应位来使能或禁止内部上拉功能;
*e.通过配置GPIOLPMSEL寄存器来指定实现低功耗模式唤醒的I/O口;
*f.通过配置GPIOXINTxSEL(x=1~7)和GPIOXNMISEL来指定作为外部中断源的输入引脚。
2.GPIO数据控制(GPIO模式)
如果引脚配置成GPIO模式,有以下四种方式对GPIO引脚进行写操作:
下面就以上出现的几种寄存器简要说明一下:
1).数据寄存器GPxDAT(32位):(x=A、B、C)
每个引脚对应1位:
GPADAT:GPIO0~GPIO31
GPBDAT:GPIO32~GPIO63
GPCDAT:GPIO64~GPIO87
每个I/O口都有,数据寄存器中的每一位对应于一个GPIO引脚。无论引脚被配置成何种功能(GPIO模式或外设功能),数据寄存器中的相应位都反映了引脚当前状态。向GPxDAT写控制字可以清零或者置位相应的输出锁存器,但不一定被驱动到(被输出到)引脚上,只有当当前引脚为通用I/O口才能驱动引脚;若为外设功能时,则输出锁存器将锁存。
2).置位寄存器GPxSET(32位):(x=A、B、C)
每个引脚对应1位:
GPASET:GPIO0~GPIO31
GPBSET:GPIO32~GPIO63
GPCSET:GPIO64~GPIO87
用来驱动特定的引脚为高电平,而不干扰其他引脚。当然能否驱动取决于输出锁存器和当前引脚的功能,只有当前引脚为通用I/O口是才能驱动。
3).清零寄存器GPxCLEAR(32位):(x=A、B、C)
每个引脚对应1位:
GPACLEAR:GPIO0~GPIO31
GPBCLEAR:GPIO32~GPIO63
GPCCLEAR:GPIO64~GPIO87
用来驱动特定的引脚为低电平,而不干扰其他引脚。当然能否驱动取决于输出锁存器和当前引脚的功能,只有当前引脚为通用I/O口是才能驱动。
4).翻转寄存器GPxTOGGLE(32位):(x=A、B、C)
每个引脚对应1位:
GPATOGGLE:GPIO0~GPIO31
GPBTOGGLE:GPIO32~GPIO63
GPCTOGGLE:GPIO64~GPIO87
这个同上,只是会驱动指定引脚电平反转。
3.输入限制(采样滤波)
通过配置GPAQSEL1、GPAQSEL2、GPBQSEL1和GPBQSEL2来为每一个GPIO引脚设置输入限制的类型。对于通用GPIO输入引脚,输入限制可以设置成仅与SYSCLKOUT信号同步,或者通过一个采样窗限制。对于配置成外设输入的引脚,输入限制除了可以为与SYSCLKOUT信号同步或者由一个采样窗限制之外,还可以是异步的。
a.无同步(输入异步)
该模式用于外设不需要输入同步,或者外设可以提供同步的情况下。如果引脚作为通用GPIO输入引脚,则异步选项是无效的,且输入限制默认为与SYSCLKOUT同步。
b.仅与SYSCLKOUT同步
这是所有引脚复位时的默认限制模式。因为输入信号是异步的,所以需要一个SYSCLKOUT的延迟,DSP的输入才会发生变化。输入信号将不再受其他输入限制。
c.用采样窗限制
该模式中,信号首先与系统时钟SYSCLKOUT同步,然后在输入允许变化之前使用特定个数的时钟周期来作为输入限制。该类型的限制中需要指定两个参数分别为采样周期(信号多久被采样一次)和要采样的点数。下面具体介绍:
上面讲了有那么多,可能有些人不理解那些寄存器的含义,下面一 一解释一下:
a.GPxCTRL(32位):限制控制寄存器。(x=A、B)
http://s13/middle/0034sXQLzy73jDUQ61Cbc&690
GPxCTRL寄存器单元内部被分成4组,分别是QUALPRD0~QUALPRD3(8位),每组8位,该8位对应8个GPIO引脚,有着各自相同的时钟周期。不同组之间时钟周期可以相同可以不同。
主要负责控制采样周期,GPACTRL负责GPIO0~GPIO31的采样周期,具体由QUALPRDn(n=0~3)来控制采样周期;GPBCTRL负责GPIO32~GPIO63的采样周期,具体由QUALPRDn(n=0~3)来控制采样周期。
采样周期=2*QUALPRDn(转化为十进制)*SYSCLKOUT(系统时钟)
b.GPxQSELn(32位):限制选择寄存器。(x=A、B n=1、2)
http://s15/middle/0034sXQLzy73jDVBmZ8ee&690
http://s14/middle/0034sXQLzy73jDVCxqd9d&690
决定了输入引脚的输入限制类型。GPAQSEL1控制GPIO0~GPIO15的输入限制类型,GPAQSEL2控制GPIO16~GPIO31的输入限制类型,GPBQSEL1控制GPIO32~GPIO47的输入限制类型,GPBQSEL2控制GPIO48~GPIO63的输入限制类型。其中,每个GPxQSELn寄存器32位,每个GPIO引脚的输入限制字是2位:
*************************************************************************************************************************
上面讲完硬件理论部分,下面开始实验软件部分。
实验部分只是带大家了解一下这部分编程所需要的各种寄存器,这次以项目Example_2833xGpioToggle为例进行讲解
1.Example_2833xGpioToggle.c 【GPIO输出电平翻转实验】
// TI File $Revision: /main/8 $
// Checkin $Date: April 21, 2008 15:42:43 $
//###########################################################################
//
// FILE: Example_2833xGpioToggle.c
//
// TITLE: DSP2833x Device GPIO toggle test program.
//
// ASSUMPTIONS:
//
// This program requires the DSP2833x header files.
//
// ALL OF THE I/O'S TOGGLE IN THIS PROGRAM. MAKE SURE
// THIS WILL NOT DAMAGE YOUR HARDWARE BEFORE RUNNING THIS
// EXAMPLE.
//
// Monitor desired pins on an oscilloscope.
//
// As supplied, this project is configured for "boot to SARAM"
// operation. The 2833x Boot Mode table is shown below.
// For information on configuring the boot mode of an eZdsp,
// please refer to the documentation included with the eZdsp,
//
// $Boot_Table:
//
// GPIO87 GPIO86 GPIO85 GPIO84
// XA15 XA14 XA13 XA12
// PU PU PU PU
// ==========================================
// 1 1 1 1 Jump to Flash
// 1 1 1 0 SCI-A boot
// 1 1 0 1 SPI-A boot
// 1 1 0 0 I2C-A boot
// 1 0 1 1 eCAN-A boot
// 1 0 1 0 McBSP-A boot
// 1 0 0 1 Jump to XINTF x16
// 1 0 0 0 Jump to XINTF x32
// 0 1 1 1 Jump to OTP
// 0 1 1 0 Parallel GPIO I/O boot
// 0 1 0 1 Parallel XINTF boot
// 0 1 0 0 Jump to SARAM <- "boot to SARAM"
// 0 0 1 1 Branch to check boot mode
// 0 0 1 0 Boot to flash, bypass ADC cal
// 0 0 0 1 Boot to SARAM, bypass ADC cal
// 0 0 0 0 Boot to SCI-A, bypass ADC cal
// Boot_Table_End$
//
// DESCRIPTION:
//
// Three different examples are included. Select the example
// (data, set/clear or toggle) to execute before compiling using
// the #define statements found at the top of the code.
//
//
// Toggle all of the GPIO PORT pins
//
// The pins can be observed using Oscilloscope.
//
//
//###########################################################################
// $TI Release: DSP2833x/DSP2823x Header Files V1.20 $
// $Release Date: August 1, 2008 $
//###########################################################################
#include "DSP28x_Project.h" // Device Headerfile and Examples Include File
// Select the example to compile in. Only one example should be set as 1
// the rest should be set as 0.
#define EXAMPLE1 1 // Use DATA registers to toggle I/O's 1.//这部分学过C语言的都清楚
- #define EXAMPLE2 0 // Use SET/CLEAR registers to toggle I/O's //这部分是宏定义,那这三个
#define EXAMPLE3 0 // Use TOGGLE registers to toggle I/O's //
变量的作用可以在下面找// Prototype statements for functions found within this file.
void delay_loop(void); 10.//函数的声明
void Gpio_select(void);
void Gpio_example1(void);
void Gpio_example2(void);
void Gpio_example3(void);
void main(void)
{
// Step 1. Initialize System Control:
// PLL, WatchDog, enable Peripheral Clocks
// This example function is found in the DSP2833x_SysCtrl.c file.
InitSysCtrl();
// Step 2. Initalize GPIO:
// This example function is found in the DSP2833x_Gpio.c file and
// illustrates how to set the GPIO to it's default state.
// InitGpio(); // Skipped for this example
// For this example use the following configuration:
Gpio_select(); //对GPIO进行操作,具体可以看下面该函数的定义
// Step 3. Clear all interrupts and initialize PIE vector table:
// Disable CPU interrupts
DINT;
// Initialize PIE control registers to their default state.
// The default state is all PIE interrupts disabled and flags
// are cleared.
// This function is found in the DSP2833x_PieCtrl.c file.
InitPieCtrl();
// Disable CPU interrupts and clear all CPU interrupt flags:
IER = 0x0000;
IFR = 0x0000;
// Initialize the PIE vector table with pointers to the shell Interrupt
// Service Routines (ISR).
// This will populate the entire table, even if the interrupt
// is not used in this example. This is useful for debug purposes.
// The shell ISR routines are found in DSP2833x_DefaultIsr.c.
// This function is found in DSP2833x_PieVect.c.
InitPieVectTable();
// Step 4. Initialize all the Device Peripherals:
// This function is found in DSP2833x_InitPeripherals.c
// InitPeripherals(); // Not required for this example
// Step 5. User specific code:
#if EXAMPLE1 2.//这里开始对应上面的宏定义
- //由于上面只有EXAMPLE1=1,所以只执行EXAMPL
//E1对应的条件语句,从而条用函数Gpio_example1() // This example uses DATA registers to toggle I/O's
Gpio_example1();
#endif // - EXAMPLE1
#if EXAMPLE2
// This example uses SET/CLEAR registers to toggle I/O's
Gpio_example2();
#endif
#if EXAMPLE3
// This example uses TOGGLE registers to toggle I/O's
Gpio_example3();
#endif
}
void delay_loop()
{
short i;
for (i = 0; i < 1000; i++) {}
}
void Gpio_example1(void)
3.//这里EXAMPLE1跳转过来的函数{
// Example 1:
// Toggle I/Os using DATA registers
for(;;)
{
GpioDataRegs.GPADAT.all =0xAAAA; 4.//这里写入GPADAT,是对GPIO0~GPIO31作为通用I/O口时的引脚的状态。这里只写了低16位1010 1010 1010 1010,高16位为0000 0000 0000 0000
delay_loop();
GpioDataRegs.GPADAT.all =0x5555; //这里同上,将低16位写入0101 0101 0101 0101
delay_loop();
}
}
void Gpio_example2(void)
{
// Example 2:
// Toggle I/Os using SET/CLEAR registers
for(;;)
{
GpioDataRegs.GPASET.all =0xAAAA; 5.//这里是通过GPASET来驱动引脚GPIO0~GPIO31相应位为高电平
GpioDataRegs.GPACLEAR.all =0x5555; 6.//这里通过GPACLEAR来驱动引脚GPIO0~GPIO31相应位为低电平
delay_loop();
GpioDataRegs.GPACLEAR.all =0xAAAA;
GpioDataRegs.GPASET.all =0x5555;
delay_loop();
}
}
void Gpio_example3(void)
{
// Example 2:
// Toggle I/Os using TOGGLE registers
// Set pins to a known state
GpioDataRegs.GPASET.all =0xAAAA;
GpioDataRegs.GPACLEAR.all =0x5555;
// Use TOGGLE registers to flip the state of
// the pins.
// Any bit set to a 1 will flip state (toggle)
// Any bit set to a 0 will not toggle.
for(;;)
{
GpioDataRegs.GPATOGGLE.all =0xFFFF; 7.//通过利用GPATOGGLE 来驱动GPIO0~GPIO31使引脚低16位电平反转
delay_loop();
}
}
void Gpio_select(void)
{
EALLOW;
GpioCtrlRegs.GPAMUX1.all = 0x00000000; // All GPIO 8.//这部分是GPIO功能选择的配置,这里配置的全是作为通用GPIO口来使用
GpioCtrlRegs.GPAMUX2.all = 0x00000000; // All GPIO
GpioCtrlRegs.GPAMUX1.all = 0x00000000; // All GPIO
GpioCtrlRegs.GPADIR.all
= //0x0FFF; GPIO0~GPIO11 9.//这里配置通用I/O口的输入输出方向outputs EDIS;
}
//===========================================================================
// No more.
//===========================================================================
下面来实际操作一下,顺便再教大家一些观察变量的方法。
具体载入项目我就不多说了,这里我就直接演示以后需要做的
1.下面是我已经加载好的项目
2.我们前面已经看过主函数了,我们在函数里将所有GPIO全部作为通用I/O口来使用,同时将GPIO作为输出口,我们在GPxDAT里面定义了初值用来驱动GPIO,所以我们这里就要监测GPxDAT能否正确装入初值。
首先,按照以前实验所讲的,想要监测什么就将该变量加入到“Add Watch Expression”当中。我们这里监测的变量是“GpioDataRegs.GPADAT.all”
3.从上面能够看出,在还未运行之前,GpioDataRegs.GPACLEAR.all VALUE是一个随机数,为了更直观的观察,我们将VALUE的显示方式改成16进制。右键“VALUE”下面的值:
4.我们这里对GPxDAT进行了两次赋值,为了更好的观察变化,我们在两次赋值语句的地方加上断点(程序运行到断点时自动停下,再运行才能继续运行)。在该语句前双击就可以加上断点,再双击就取消断点。
http://s2/middle/0034sXQLzy73jDWuAyB01&690
5.下面开始运行,注意看“Watch”内的值
运行前:
第一次运行后:
http://s10/middle/0034sXQLzy73jDWVzRfa9&690
第二次运行后:
http://s8/middle/0034sXQLzy73jDWWEPd37&690
最后,在这里特别提醒一下,这也是我在写该教程时所遇到的,相信许多初学者都可能会遇到。虽然TMS320F28335开发板有着相同的特点,但是不同厂家生产的开发板内部GPIO等引脚的分布不一样(这一点需要看产品所附带的硬件原理图)。像我这里遇到的就是,我的GPIO12~GPIO15端口开发板上是作为四个独立按键,所以是作为通用输入的,默认输入高电平。而我原先用的程序里面(从上面也能看出来):
下面这是原来的:
GpioCtrlRegs.GPADIR.all
修改后:
GpioCtrlRegs.GPADIR.all
修改之前将GPIO12~GPIO15配置成输出,这个不太合理,因此我将GPIO12~GPIO15仍改成输入。(这就类似GPIO60、GPIO61、GPIO2~GPIO7作为LED1~8输出,而不能作为输入一样)因此
GpioDataRegs.GPADAT.all
GpioDataRegs.GPADAT.all
也改成:
GpioDataRegs.GPADAT.all
GpioDataRegs.GPADAT.all
由于加载的项目不一定完全符合自己的开发板,所以以后在学习中遇到问题,希望大家能够分析自己的开发板的硬件差异来解决问题。
好,这一讲结束~