Q: Toggling of the GPIO seems slower than it should be - Part 2 Consider the following code: //
// Toggle GPIO18 // for(;;) {
GpioDataRegs.GPADAT.bit.GPIO18 = 1; GpioDataRegs.GPADAT.bit.GPIO18 = 0; }
The above code has similar issues to that discussed before. This method uses a read-modify-write. Since the read portion is wait-stated and since the peripheral frame has \protection, the next read doesn't occur until the previous one completes.
Another issue is the for loop will introduce a branch which takes 4 cycles. So even if this code were changed to use the GPBTOGGLE register, you would see a overhead from the branch.
Q: How can I toggle a GPIO pin as fast as possible in C?
The fastest way is to perform back-to-back(连续的) writes to the TOGGLE registers:
If this is done in a for() loop, then the loop will introduce a branch instruction which takes 4 cycles at the start of each loop. On floating-point devices you can do something similar with a repeat block instruction (RPTB) instead of a branch which will eliminate the branch overhead. In addition, back-to-back writes to this peripheral frame will introduce a wait-state. That is one toggle will take a cycle, but back to back toggles will add an additional cycle between the writes.
for(;;) {
GpioDataRegs.GPBTOGGLE.all = 0x0002; GpioDataRegs.GPBTOGGLE.all = 0x0002; GpioDataRegs.GPBTOGGLE.all = 0x0002; GpioDataRegs.GPBTOGGLE.all = 0x0002; GpioDataRegs.GPBTOGGLE.all = 0x0002; GpioDataRegs.GPBTOGGLE.all = 0x0002; GpioDataRegs.GPBTOGGLE.all = 0x0002; GpioDataRegs.GPBTOGGLE.all = 0x0002; repeat...etc }
Q: How long will this C instruction to toggle a GPIO pin take? GpioDataRegs.GPBTOGGLE.all = 0x0002;
You can take a look at the disassembly generated in the Code Composer Studio window. In general there will be
an instruction to set the data page pointer
an AND instruction to write to the GPBTOGGLE register.
On their own, each of these instructions take one SYSCLKOUT cycle.
If another write is made to the GPBTOGGLE register then the data page does not have to be set for the second write. Consecutive (back-to-back) writes, however, to GPIO registers experience a 1-cycle pipeline hit (1-cycle delay).
Q: Where is the 1 wait-state for back-to-back writes documented?
This is documented in the device specific data manual in the table that describes the wait-states for the various spaces in the device's memory map area. It applies to all peripheral frame 1 registers. 对位操作涉及到“读—改—写”指令,某些外设帧中的寄存器受到“先写后读”流水线保护,当连续对这类寄存器进行位操作时,因为写指令比读指令耗费更多时钟周期,上一条写指令还未执行时下一条读指令已经执行,就会出错。为避免这种意外,通常在连续两个写操作中插入空操作以延时,直到上一条语句的写指令被冲出CPU流水线。如下所示: GpioDataRegs.GPADAT.bit.GPIO16 = 1; delay_loop();
GpioDataRegs.GPADAT.bit.GPIO17 = 0;
为提高代码效率,可以后续操作中除了“位操作”以外,又不依赖当前操作结果的其他操作提前执行。对某些寄存器来说,比如GPxDAT寄存器,还有其他解决办法,比如 GpioDataRegs.GPASET.bit.GPIO16 = 1; GpioDataRegs.GPACLEAR.bit.GPIO17 = 1;
使用上述指令时,就不需要在中间插入延时操作。当连续改变同一个GPIO输出引脚的状态时,比如
GpioDataRegs.GPACLEAR.bit.GPIO18 = 1; GpioDataRegs.GPASET.bit.GPIO18 = 1; GpioDataRegs.GPACLEAR.bit.GPIO18 = 1; GpioDataRegs.GPASET.bit.GPIO18 = 1; 使用如下指令要比使用上述指令更加迅速 GpioDataRegs.GPBTOGGLE.all = 0x0002; GpioDataRegs.GPBTOGGLE.all = 0x0002; 当然,如果用户清楚地知道当前要操作的寄存器中其他位的状态,那么直接对整个寄存器进行操作而非仅仅对一位进行操作,将会解除流水线保护,使得响应更快速。 (八) ADC
When the ADC pins are floating (ie no inputs are fed), the result registers are tend to oscillate between maximum and minimum values. This is bound to happen! So please don't alter the code and give some input to ADC (you can use the 3.3V & GND pins on docking station) to get respective digital value. Also, in real-time scenario - you need to connect the unused ADCs to ground.