DSP中GPIO使用GPxDAT初始化问题分析

1 问题描述

GPIO配置为输出IO口,在使用GPxDAT连续给两个或两个以上IO口赋值时,出现第一个或者前几个赋值无效的情况。

GpioDataRegs.GPADAT.bit.GPIO1 = 1 ; 
GpioDataRegs.GPADAT.bit.GPIO2 = 1 ; 
// It gets the old value of GPIO1 due to the delay

2 问题分析

每个I/O端口有一个数据寄存器。数据寄存器中的每个位对应于一个GPIO引脚。不管管脚是如何配置的(GPIO或外围功能),数据寄存器中的相应位反映了合格后管脚的当前状态。写入GPxDAT寄存器将清除或设置相应的输出锁存器,如果该引脚被启用为通用输出(GPlO输出),则该引脚也将被驱动为低电平或高电平。
如果该引脚未配置为GPIO输出,则该值将被锁定,但该引脚不会被驱动。只有当该引脚后来被配置为GPlO输出时,锁存值才会被驱动到该引脚上。
当使用GPxDAT寄存器更改输出引脚的电平时,应小心不要意外更改另一个引脚的电平。例如,如果要通过使用读-改-写指令写入GPADAT寄存器位0来更改GPIOA1的输出锁存器电平,则如果另一个l/O端口a信号在该指令的读和写阶段之间改变电平,则可能会出现问题。
下面是对发生这种情况的原因的分析:GPxDAT寄存器反映的是pin的状态,而不是锁存器。这意味着寄存器反映了实际的pin值。但是,在写入寄存器的时间和新的pin值在寄存器中反映出来之间有一个延迟。当在随后的程序语句中使用这个寄存器来改变GPlO管脚的状态时,这可能会引起问题。
下面显示了一个例子,其中两个程序语句试图驱动两个不同的GPIO管脚,这些引脚目前处于低到高状态。

GpioDataRegs.GPADAT.bit.GPIO1 = 1 ; //11
GpioDataRegs.GPADAT.bit.GPIO2 = 1 ; //12
// It gets the old value of GPIO1 due to the delay

如果在GPxDAT寄存器上使用读-改-写操作,则由于第一条指令()的输出和输入之间的延迟,第二条指令(12)将读取旧值并将其写回。
第二条指令将等待第一条指令完成它的写入,这是因为在这个外围帧上,先写后读保护。但是,在(11)的写入和反映管脚上新值(1)的GPxDAT位之间会有一些延迟。在此延迟期间,第二条指令将读取GPIO1(0)的旧值,并将其与GPIO2(1)的新值一起写回。因此,GPIO1引脚保持低位。

3 解决方式

方式1:在使用GPxDAT赋值语句之间加入NOP空指令,保证输出锁存器电平被驱动到引脚上,并会读到GPxDAT寄存器中。

方式2:使用GPxSET/GPxCLEAR/GPxTOGGLE。

如果是在初始化中,官方的例子是这么进行的。

void Gpio_setup2(void)
{
   // Example 1:
   // Communications Pinout.
   // This basic communications pinout includes:
   // PWM1-3, CAP1, CAP2, SPI-A, SPI-B, CAN-A, SCI-A and I2C
   // and a number of I/O pins
   
    EALLOW;
    
   // Enable an GPIO output on GPIO6
   GpioCtrlRegs.GPAPUD.bit.GPIO6 = 0;   // Enable pullup on GPIO6
   GpioDataRegs.GPASET.bit.GPIO6 = 1;   // Load output latch
   GpioCtrlRegs.GPAMUX1.bit.GPIO6 = 0;  // GPIO6 = GPIO6
   GpioCtrlRegs.GPADIR.bit.GPIO6 = 1;   // GPIO6 = output

   // Enable eCAP1 on GPIO7
   GpioCtrlRegs.GPAPUD.bit.GPIO7 = 0;   // Enable pullup on GPIO7
   GpioCtrlRegs.GPAQSEL1.bit.GPIO7 = 0; // Synch to SYSCLKOUT
   GpioCtrlRegs.GPAMUX1.bit.GPIO7 = 3;  // GPIO7 = ECAP2

   // Enable GPIO outputs on GPIO8 - GPIO11
   GpioCtrlRegs.GPAPUD.bit.GPIO8 = 0;   // Enable pullup on GPIO8
   GpioDataRegs.GPASET.bit.GPIO8 = 1;   // Load output latch
   GpioCtrlRegs.GPAMUX1.bit.GPIO8 = 0;  // GPIO8 = GPIO8
   GpioCtrlRegs.GPADIR.bit.GPIO8 = 1;   // GPIO8 = output

   GpioCtrlRegs.GPAPUD.bit.GPIO9 = 0;   // Enable pullup on GPIO9
   GpioDataRegs.GPASET.bit.GPIO9 = 1;   // Load output latch
   GpioCtrlRegs.GPAMUX1.bit.GPIO9 = 0;  // GPIO9 = GPIO9
   GpioCtrlRegs.GPADIR.bit.GPIO9 = 1;   // GPIO9 = output

   GpioCtrlRegs.GPAPUD.bit.GPIO10 = 0;  // Enable pullup on GPIO10
   GpioDataRegs.GPASET.bit.GPIO10 = 1;  // Load output latch
   GpioCtrlRegs.GPAMUX1.bit.GPIO10 = 0; // GPIO10 = GPIO10
   GpioCtrlRegs.GPADIR.bit.GPIO10 = 1;  // GPIO10 = output

   GpioCtrlRegs.GPAPUD.bit.GPIO11 = 0;  // Enable pullup on GPIO11
   GpioDataRegs.GPASET.bit.GPIO11 = 1;  // Load output latch
   GpioCtrlRegs.GPAMUX1.bit.GPIO11 = 0; // GPIO11 = GPIO11
   GpioCtrlRegs.GPADIR.bit.GPIO11 = 1;  // GPIO11 = output

   // Make GPIO34 an input
   GpioCtrlRegs.GPBPUD.bit.GPIO32 = 0;   // Enable pullup on GPIO34
   GpioCtrlRegs.GPBMUX1.bit.GPIO34 = 0;  // GPIO34 = GPIO34
   GpioCtrlRegs.GPBDIR.bit.GPIO34 = 0;   // GPIO34 = input

   EDIS;
}

4 结果验证

两种方式经过验证均可以有效的解决该问题。
在验证问题时,我在两个IO赋值之间加了37个NOP。但不知具体最少加多少个可以有效的解决该问题。

5 附件

附上TI的TMS320F2803x Piccolo Technical Reference Manual关于该段的描述原文。

If the pin is configured as an GPIO, specify the direction of the pin as either input or output in the GPADIR, GPBDIR, or AIODIR registers. By default, all GPIO pins are inputs. To change the pin from input to output, first load the output latch with the value to be driven by writing the appropriate value to the GPxCLEAR, GPxSET, or GPxTOGGLE (or AIOCLEAR, AIOSET, or AIOTOGGLE) registers. Once the output latch is loaded, change the pin direction from input to output via the GPxDIR registers. The output latch for all pins is cleared at reset.

Each l/O port has one data register. Each bit in the data register corresponds to one GPIO pin. Nomatter how the pin is configured (GPIO or peripheral function), the corresponding bit in the data register reflects the current state of the pin after qualification(This does not apply to AlOx pins). Writing to the GPxDAT/AIODAT register clears or sets the corresponding output latch and if the pin is enabled as a general purpose output (GPlO output) the pin will also be driven either low or high. If the pin is not configured as a GPIO output then the value will be latched, but the pin will not be driven.

Only if the pin is later configured as a GPlO output, will the latched value be driven onto the pin. When using the GPxDAT register to change the level of an output pin, you should be cautious not to accidentally change the level of another pin. For example, if you mean to change the output latch levelof GPIOA1 by writing to the GPADAT register bit 0 using a read-modify-write instruction,a problem can occur if another l/O port A signal changes level between the read and the write stage of the instruction.

Following is an analysis of why this happens: The GPxDAT registers reflect the state of the pin, not the latch. This means the register reflects the actual pin value. However, there is a lag between when the register is written to when the new pin value is reflected back in the register. This may pose a problem when this register is used in subsequent program statements to alter the state of GPlO pins. An example is shown below where two program statements attempt to drive two different GPIO pins that are currently low to a high state. If Read-Modify-Write operations are used on the GPxDAT registers, because of the delay between the output and the input of the first instruction (11), the second instruction(12) will read the old value and write it back.

Each l/O port has one data register. Each bit in the data register corresponds to one GPIO pin. Nomatter how the pin is configured (GPIO or peripheral function), the corresponding bit in the data register reflects the current state of the pin after qualification(This does not apply to AlOx pins). Writing to the GPxDAT/AIODAT register clears or sets the corresponding output latch and if the pin is enabled as a general purpose output (GPlO output) the pin will also be driven either low or high. If the pin is not configured as a GPIO output then the value will be latched, but the pin will not be driven.

Only if the pin is later configured as a GPlO output, will the latched value be driven onto the pin. When using the GPxDAT register to change the level of an output pin, you should be cautious not to accidentally change the level of another pin. For example, if you mean to change the output latch levelof GPIOA1 by writing to the GPADAT register bit 0 using a read-modify-write instruction,a problem can occur if another l/O port A signal changes level between the read and the write stage of the instruction.

Following is an analysis of why this happens: The GPxDAT registers reflect the state of the pin, not the latch. This means the register reflects the actual pin value. However, there is a lag between when the register is written to when the new pin value is reflected back in the register. This may pose a problem when this register is used in subsequent program statements to alter the state of GPlO pins. An example is shown below where two program statements attempt to drive two different GPIO pins that are currently low to a high state.

If Read-Modify-Write operations are used on the GPxDAT registers, because of the delay between the output and the input of the first instruction (11), the second instruction(12) will read the old value and write it back. GpioDataRegs.GPADAT.bit.GPIO1 = 1 ; I1 performs read-modify-write of GPADAT GpioDataRegs.GPADAT.bit.GPIO2 = 1 ; I2 also a read-modify-write of GPADAT. ; It gets the old value of GPIO1 due to the delay

The second instruction will wait for the first to finish its write due to the write-followed-by-read protection on this peripheral frame. There will be some lag, however, between the write of (11) and the GPxDAT bit reflecting the new value(1) on the pin. During this lag, the second instruction will read the old value of GPIO1(0) and write it back along with the new value of GPIO2(1). Therefore, GPIO1 pin stays low.

One solution is to put some NOP’s between instructions. A better solution is to use the GPxSET/GPxCLEAR/GPxTOGGLE registers instead of the GPxDAT registers…

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐