Writing 8 bit data on port in fastest way

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Writing 8 bit data on port in fastest way

2,793 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by aamir ali on Mon Aug 12 04:48:46 MST 2013
What is the fastest way to write 8 bit data on continuous port pins.

Can't I write in one go. I didn't find data register for that.Only way I can find faster is use RMW.

 temp = PIN& 0x000000ff;
temp = temp | (data & 0x000000ff);
PIN= data;


2. Also there is strct for GPIO in LPC13uxx.h  Now if I redine PIN[2] as

union
{
__IO uint32_t PIN[2]; 

struct
{
__IO uint8_t PIN_byte[8];           /* get it in 8 byte format */ 
};
};



Now write like:
PIN_byte[0] = (uint8_t)data;


I am not sure if PIN register can be accessed in 8 bits. Is it correct way.



Labels (1)
0 Kudos
Reply
8 Replies

2,341 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by mathseng on Thu Nov 07 17:47:24 MST 2013
Hi Takashi,
After my earlier post, I reduced my code complexity by using macros (attached). (My other I/O macros are similar.)
Using my 100MHz logic analyzer, I was able to determine that using my macros, data appears on the output as:
My code works for all port/pin combinations - pins can be consecutive or sparsely mapped, and cover 1 or both ports.

First IO port changes:    port 0
Second IO port changes:   port 1 - displaced by 10nS or 20nS (probably jitter at max resolution of my analyzer)
                        all test runs have port 1 displacement at identical times, ie all 10nS or all 20nS.

So, I am confident that only the setup time is the issue, but the PortIO writing times are almost as fast as possible.
ie 2 issues:
+ data setup       - mine is long but consistent time, using Encode/Decode routine
+ I/O bit update   - observed output shows effective IO operation.

Regards,
Bill

My program code consists of essentially 2 lines of code, the first to map the internal data (whatever width) to the 64 bits of the 2 ports. The macro as written works because the ports use consecutive addresses. (Can't use same macro for port setup, since port addressing is 0..23/0..31, and setup bits are different, preventing similar complexity reduction).
The INV section is to support my ActiveLOW and ActiveHIGH outputs - keeping the logic separate from physical.

<code>
//-----------------------------------------------------------
// LEDs are arranged in a particular order. This is the
// mapping of the LEDs to the I/O ports in a portable manner
//-----------------------------------------------------------
#defineoLED8         (ioBIT(1,31))
#defineoLED9         (ioBIT(1,16))
#defineoLEDg         (ioBIT(0,22))
#defineoLEDy         (ioBIT(0,21))
#define MASK_LED      ((oLED8 | oLED9 | oLEDg | oLEDy))
#define MASK_LEDINV   (oLEDy)   // bits which are inverted on output


#define ioBIT(port,bit)        ( 1ULL << (((port) ? (32) : (0)) + (bit)))
#define GPIO_PUT(mask, valu)   ((*((uint64_t *)&(LPC_GPIO->MASK)) = ~(mask)), (*((uint64_t *)&(LPC_GPIO->MPIN)) = (valu)))

Code is
uint64_t p01 = Encode(value);
GPIO_PUT(MASK_LED, p01 ^ MASK_LEDINV);

</code>
0 Kudos
Reply

2,341 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by mathseng on Fri Aug 23 04:11:07 MST 2013
Hi,
maybe this will help with deciphering the structure for the port bits, and give the understanding necessary to create macros/etc.

This mapping of bits to bytes/short/word is bit-banding - a one page reference can be found at http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0337h/Behcjiic.html.
A more in-depth reference is at http://www.scribd.com/doc/30063713/The-Definitive-Guide-to-the-ARM-Cortex-M3-2010 - Chapter 5 page 79. Fig 5.1 shows clearly the aliased regions, section 5.5 describes access to regions.

To see the effect of writing bits to the output ports, I set up a simple R-2R circuit to create a DAC (ie bit changes will be visible on a storage CRO) on a number of ports, some contiguous, some in different ports. This way, I don't need to know my assembler. (I haven't yet captured the output results to see which writes are on the same machine cycle - it's not important at this stage of our project, and the timing is only necessary to be accurate down to 1mS.)

Bill.



0 Kudos
Reply

2,341 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by tmune on Fri Aug 23 00:01:51 MST 2013
I found out the code doesn't use tables at all. The assembler code of chip_GPIO_WritePortBit() is:
[list]
             IP_GPIO_WritePortBit:
000004a0:    push {r7}
000004a2:    sub sp, #20
000004a4:    add r7, sp, #0
000004a6:    str r0, [r7, #12]
000004a8:    str r1, [r7, #8]
000004aa:    strb r2, [r7, #7]
000004ac:    strb r3, [r7, #6]
000004ae:    ldrb r3, [r7, #7]
000004b0:    ldrb r2, [r7, #6]
000004b2:    ldr r1, [r7, #12]
000004b4:    ldr r0, [r7, #8]
000004b6:    mov.w r0, r0, lsl #5
000004ba:    adds r3, r0, r3
000004bc:    adds r3, r1, r3
000004be:    strb r2, [r3, #0]
000004c0:    add.w r7, r7, #20
000004c4:    mov sp, r7
000004c6:    pop {r7}
000004c8:    bx lr
000004ca:    nop
[/list]
I'm not familiar with ARM assembly, but I think it write one byte data to one of Byte pin register.
Takashi
0 Kudos
Reply

2,341 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by tmune on Thu Aug 22 11:15:40 MST 2013
I think there is no API/Drivers to write multiple bits in one shot in lpcopen v1.03.
BTW regarding to writing bits to a port,I have one question on how lpcopen writes a bit, particularly Chip_GPIO_WritePortBit() in LPC13xx GPIO Driver.
I found out that it eventually leads to pGPIO->B[Port][Bit] = Setting; in gpio_001.h file.
This file also has a struct definition includes this definiton:
__IO uint8_t B[128][32];/*!< Offset 0x0000: Byte pin registers ports 0 to n; pins PIOn_0 to PIOn_31 */
My quetion(s) is "Is this a 128X32=4096 pointer table, which is 4096 X 4(bytes = 32 bits)=16k bytes?". Or is it just a code, not produce a table, and its size is very small?
If it is a table, can it be B[2][32] to just enough for port 0 and 1?
Thanks,
Takashi
0 Kudos
Reply

2,341 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by mathseng on Wed Aug 21 23:01:46 MST 2013
My code was truncated in display.
Attached is file containing the code sample.

Also, the reading should be done to the PIN register, not the MPIN register.
(This worked on my code because of other MASK settings I did :( )

Bill.
0 Kudos
Reply

2,341 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by mathseng on Wed Aug 21 16:58:34 MST 2013
If your 8 bits are either non-contiguous, or cross the PORT boundary, you can do the following, which uses the mask register and full word writes. The MASK is not required on READs, but is required on WRITEs.
I have yet to test whether the write occurs in the same system clock tick for both PORTs (0,1).

Since your bits are all contiguous, your code may be simpler, ie a whole byte could be inserted across word boundaries in single shift operation.
HTH,
Bill.

<code>
pXX# = port number for XX on LPC, bit #
bXX# = bit number on LPC
eg:
#define pSW0( 0 )
#define bSW0(  7 )
#define pSW1( 1 )
#define bSW1( 19 )

#define pLED0( 0)
#define bLED0( 23 )
#define pLED1( 1)
#define bLED1( 22 )
#define pLED9( 1 )
#define bLED9( 16 )

#define MASK_LEDp0(1<<bLED0 | 1<<bLEDg | 1<<bLEDy)
#define MASK_LEDp1(1<<bLED1 | 1<<bLED2 | 1<<bLED3 | 1<<bLED4 | 1<<bLED5 | 1<<bLED6 | 1<<bLED7 | 1<<bLED8 | 1<<bLED9)
#define MASK_LEDp01(((unsigned long long)MASK_LEDp1 << 32) | MASK_LEDp0)



//-----------------------------------------------------------
uint32_t getSwitchValuesW(void)
//-----------------------------------------------------------
{
uint64_t *pMPIN = (uint64_t *)&(LPC_GPIO->MPIN);
uint32_t sw = 0; // reading the bits into a word in correct bit order for use in program.

sw |= *pMPIN & ( 1ULL << ((pSW0 ? (32) : (0)) + bSW0)) ? ( 1 <<  0) : 0;
sw |= *pMPIN & ( 1ULL << ((pSW1 ? (32) : (0)) + bSW1)) ? ( 1 <<  1) : 0;
sw |= *pMPIN & ( 1ULL << ((pSW2 ? (32) : (0)) + bSW2)) ? ( 1 <<  2) : 0;
sw |= *pMPIN & ( 1ULL << ((pSW3 ? (32) : (0)) + bSW3)) ? ( 1 <<  3) : 0;
sw |= *pMPIN & ( 1ULL << ((pSW4 ? (32) : (0)) + bSW4)) ? ( 1 <<  4) : 0;
sw |= *pMPIN & ( 1ULL << ((pSW5 ? (32) : (0)) + bSW5)) ? ( 1 <<  5) : 0;
sw |= *pMPIN & ( 1ULL << ((pSW6 ? (32) : (0)) + bSW6)) ? ( 1 <<  6) : 0;
sw |= *pMPIN & ( 1ULL << ((pSW7 ? (32) : (0)) + bSW7)) ? ( 1 <<  7) : 0;

return sw;
}

//-----------------------------------------------------------
void ledsOnOffW(uint32_t values)
//-----------------------------------------------------------
{
uint64_t p01 = 0; // port bit accumulator, creating the bit pattern to be presented to LPC chip
uint64_t *pMPIN = (uint64_t *)&(LPC_GPIO->MPIN);
uint64_t *pMASK = (uint64_t *)&(LPC_GPIO->MASK);

p01 |= values & ( 1 <<  0) ? ( 1ULL << ((pLED0 ? (32) : (0)) + bLED0)) : 0;
p01 |= values & ( 1 <<  1) ? ( 1ULL << ((pLED1 ? (32) : (0)) + bLED1)) : 0;
p01 |= values & ( 1 <<  2) ? ( 1ULL << ((pLED2 ? (32) : (0)) + bLED2)) : 0;
p01 |= values & ( 1 <<  3) ? ( 1ULL << ((pLED3 ? (32) : (0)) + bLED3)) : 0;
p01 |= values & ( 1 <<  4) ? ( 1ULL << ((pLED4 ? (32) : (0)) + bLED4)) : 0;
p01 |= values & ( 1 <<  5) ? ( 1ULL << ((pLED5 ? (32) : (0)) + bLED5)) : 0;
p01 |= values & ( 1 <<  6) ? ( 1ULL << ((pLED6 ? (32) : (0)) + bLED6)) : 0;
p01 |= values & ( 1 <<  7) ? ( 1ULL << ((pLED7 ? (32) : (0)) + bLED7)) : 0;
p01 |= values & ( 1 <<  8) ? ( 1ULL << ((pLED8 ? (32) : (0)) + bLED8)) : 0;
p01 |= values & ( 1 <<  9) ? ( 1ULL << ((pLED9 ? (32) : (0)) + bLED9)) : 0;
p01 |= values & ( 1 << 10) ? ( 1ULL << ((pLEDg ? (32) : (0)) + bLEDg)) : 0;
p01 |= values & ( 1 << 11) ? ( 1ULL << ((pLEDy ? (32) : (0)) + bLEDy)) : 0;
*pMASK = ~MASK_LEDp01; // mask of all the LEDs bits which are defined in this array
*pMPIN = p01;
}

//-----------------------------------------------------------

</code>
0 Kudos
Reply

2,341 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by tmune on Tue Aug 13 19:37:02 MST 2013
My understanding of ARM addressing is that the processor can address, a word(32-bit), half word(16-bit), and byte.
If the 8-bit is the least significant byte of GPIO Port0, you can do the followings;
[list]
  // Setup a byte pointer to GPIO ls byte (of GPIO port pin registers)
unsigned char *Port0LSByte = (unsigned char *)0x50002100;
*Port0LSByte = (unsigned char)0xFF;// Turn one byte on
*Port0LSByte = (unsigned char)0x00;// all bits off
[/list]
Using LPC1347 xpresso board you'll see the LED turning on and off as you step through the code. GPIO0_4 and 0_5 do not give you high state because they are open drain outputs. The others toggle high and low.
You have to set the port pins as output at the start.
Takashi
0 Kudos
Reply

2,341 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by mathseng on Tue Aug 13 04:05:26 MST 2013
Hi,
Some thoughts:
a. Look at UM10524, section "9.6.5 Recommended practices".

b. Then, go back and look at whether using a MASK, by setting the mask register before writing the 'n' bits is the way you want to go, or if you can assemble bits and use the SET, CLR and NOT functionality.

This document shows that PIN operations work only on ONE i/o bit, while PORT operations operate on the 32 bits of the port, or some subset if MASKED (9.6.3 Masked I/O).

0 Kudos
Reply