Go Down

Topic: Arduino/ESP8266 : Faster direct port write ? (Read 12157 times) previous topic - next topic

mcnobby

I am a new user of ESP8266 with Arduino...

On my Arduino boards, I use "PORTC |= 0b00000001;" to directly manipulate a port pin as it is much faster than digital.write(pin,HL);

Does anyone know of anything similar when using ESP8266/NodeMCU type boards with the Arduino Interface ?

I found a few articles on the binterweb but it seems I am getting things confused with lua

Thanks in advance

DrAzzy

#1
Sep 11, 2016, 09:21 am Last Edit: Sep 11, 2016, 09:22 am by DrAzzy
use the source, Luke (from the ESP8266 arduino core's implementation wiring_digital )

Code: [Select]
extern void ICACHE_RAM_ATTR __digitalWrite(uint8_t pin, uint8_t val) {
  pwm_stop_pin(pin);
  if(pin < 16){
    if(val) GPOS = (1 << pin);
    else GPOC = (1 << pin);
  } else if(pin == 16){
    if(val) GP16O |= 1;
    else GP16O &= ~1;
  }
}

extern int ICACHE_RAM_ATTR __digitalRead(uint8_t pin) {
  pwm_stop_pin(pin);
  if(pin < 16){
    return GPIP(pin);
  } else if(pin == 16){
    return GP16I & 0x01;
  }
  return 0;
}


You don't save much time by doing the direct port read/write here - the ESP8266 is faster in general, and the AVR implementation of digitalWrite and pinMode are rather bulky because of the pin-register lookup stuff, and because they have to turn off interrupts. These functions are almost trivial.
ATTinyCore and megaTinyCore for all ATtiny, DxCore for DA/DB-series! github.com/SpenceKonde
http://drazzy.com/package_drazzy.com_index.json
ATtiny breakouts, mosfets, awesome prototyping board in my store http://tindie.com/stores/DrAzzy

mcnobby

Cheers DrAzzy, I shall try that, in the meantime I found something (and had a lightbulb moment !)

This will run Blink-type sketch

Code: [Select]
#define myPin 15
#define myPinBit (1<<15)

void setup() {
  pinMode(myPin,OUTPUT);
}

void loop() {
  WRITE_PERI_REG( PERIPHS_GPIO_BASEADDR + 4, myPinBit );
  delay(1000);                     
  WRITE_PERI_REG( PERIPHS_GPIO_BASEADDR + 8, myPinBit );
  delay(1000);                     
}

mcnobby

#3
Sep 11, 2016, 11:30 am Last Edit: Sep 11, 2016, 11:53 am by mcnobby
I stumbled across this
http://tech.scargill.net/ws2812b-success-on-the-esp-12/

which basically taught me all I need to know

Quote
write to 0x6000304 to turn things on and 0x6000308 to turn things off
- although those numbers should be 0x60000304 and 0x60000308

so, actually a lot less complex than I thought :)

mcnobby

#4
Sep 11, 2016, 11:42 am Last Edit: Sep 11, 2016, 11:43 am by mcnobby
This gives me about 5MHz output @ 80MHz if I remove all the delays and double up on the bitbash, the 'loop' function obviously has some housekeeping tasks before the writes can start again

mcnobby

Code: [Select]
#define myPin 5
#define myPinBit (1<<myPin)

void setup() {
  pinMode(myPin,OUTPUT);
}

void loop() {
  os_intr_lock();
  WRITE_PERI_REG( 0x60000304, myPinBit );
  WRITE_PERI_REG( 0x60000308, myPinBit );
  WRITE_PERI_REG( 0x60000304, myPinBit );
  WRITE_PERI_REG( 0x60000308, myPinBit );
  WRITE_PERI_REG( 0x60000304, myPinBit );
  WRITE_PERI_REG( 0x60000308, myPinBit );
  WRITE_PERI_REG( 0x60000304, myPinBit );
  WRITE_PERI_REG( 0x60000308, myPinBit );
  os_intr_unlock();
}

J-M-L

to test performance, just put that in a while (true) loop to avoid the penalty of going through the loop() overhead - you'll still see a bit of the jump overhead but more limited than the loop()

With an AVR chip you could do something like this

Code: [Select]
void setup() {
  Serial.begin(115200);
top:
  // turn bits on and off here with PORT for example
  asm goto ("jmp %l0\n"::::top); // jump back to top label, infinite loop
}

void loop() {}



i've not tested it but this might do it - using an assembly line jump to be quick


Code: [Select]

void setup() {
top:
  WRITE_PERI_REG( 0x60000304, 0x20 );
  WRITE_PERI_REG( 0x60000308, 0x20 );
  asm goto ("XXX"::::top); //  XXX = right assembly language for the ESP chip to jump probably just "J"
}

void loop() {}
Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous

mcnobby

Thanks for your help there J-M-L, the looping is one thing, but at the moment I cant seem to get it to run any faster with adjacent writes (see my example), so with 4 in a row they come out at 200ns cycle time.. I thought with such a powerful bit of kit this would be alot faster than that at 80MHz

With my 8 bit arduino im sure I can match that at 16MHz :)

J-M-L

have you look at the assembly that gets generated?
Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous


J-M-L

#10
Sep 11, 2016, 03:29 pm Last Edit: Sep 11, 2016, 03:44 pm by J-M-L
Code: [Select]
void loop() {
  os_intr_lock();
  WRITE_PERI_REG( 0x60000304, myPinBit );
  WRITE_PERI_REG( 0x60000308, myPinBit );
  WRITE_PERI_REG( 0x60000304, myPinBit );
  WRITE_PERI_REG( 0x60000308, myPinBit );
  WRITE_PERI_REG( 0x60000304, myPinBit );
  WRITE_PERI_REG( 0x60000308, myPinBit );
  WRITE_PERI_REG( 0x60000304, myPinBit );
  WRITE_PERI_REG( 0x60000308, myPinBit );
  os_intr_unlock();
}


is compiled as

00000000 <loop-0x10>:
   0:   000304           excw
   3:   030860           excw
   6:   006000           rsil   a0, 0
   9:   000000           ill
   c:   000000           ill
   ...

00000010 <loop>:
  10:   f0c112           addi   a1, a1, -16
  13:   036102           s32i   a0, a1, 12
  16:   fffc01           l32r   a0, 8 <loop-0x8>
  19:   0000c0           callx0   a0
  1c:   fff941           l32r   a4, 0 <loop-0x10>
  1f:   022c             movi.n   a2, 32
  21:   fff831           l32r   a3, 4 <loop-0xc>
  24:   0020c0           memw
  27:   0429             s32i.n   a2, a4, 0
  29:   0020c0           memw
  2c:   0329             s32i.n   a2, a3, 0
  2e:   0020c0           memw
  31:   0429             s32i.n   a2, a4, 0
  33:   0020c0           memw
  36:   0329             s32i.n   a2, a3, 0
  38:   0020c0           memw
  3b:   0429             s32i.n   a2, a4, 0
  3d:   0020c0           memw
  40:   0329             s32i.n   a2, a3, 0
  42:   0020c0           memw
  45:   0429             s32i.n   a2, a4, 0
  47:   0020c0           memw
  4a:   0329             s32i.n   a2, a3, 0
  4c:   fff001           l32r   a0, c <loop-0x4>
  4f:   0000c0           callx0   a0
  52:   3108             l32i.n   a0, a1, 12
  54:   10c112           addi   a1, a1, 16
  57:   f00d             ret.n


or with code source mixing that reads as
void loop() {
  os_intr_lock();
  WRITE_PERI_REG( 0x60000304, myPinBit );
  24:   0020c0           memw
  27:   0429         s32i.n   a2, a4, 0
  WRITE_PERI_REG( 0x60000308, myPinBit );
  29:   0020c0           memw
  2c:   0329         s32i.n   a2, a3, 0
  WRITE_PERI_REG( 0x60000304, myPinBit );
  2e:   0020c0           memw
  31:   0429         s32i.n   a2, a4, 0
  WRITE_PERI_REG( 0x60000308, myPinBit );
  33:   0020c0           memw
  36:   0329         s32i.n   a2, a3, 0
  WRITE_PERI_REG( 0x60000304, myPinBit );
  38:   0020c0           memw
  3b:   0429         s32i.n   a2, a4, 0
  WRITE_PERI_REG( 0x60000308, myPinBit );
  3d:   0020c0           memw
  40:   0329         s32i.n   a2, a3, 0
  WRITE_PERI_REG( 0x60000304, myPinBit );
  42:   0020c0           memw
  45:   0429         s32i.n   a2, a4, 0
  WRITE_PERI_REG( 0x60000308, myPinBit );
  47:   0020c0           memw
  4a:   0329         s32i.n   a2, a3, 0
  os_intr_unlock();
  4c:   fff001           l32r   a0, c <loop-0x4>
  4f:   0000c0           callx0   a0
}
  52:   3108         l32i.n   a0, a1, 12
  54:   10c112           addi   a1, a1, 16
  57:   f00d         ret.n


and if I make it only 1 ON, 1 OFF

Code: [Select]
void loop() {
  os_intr_lock();
  WRITE_PERI_REG( 0x60000304, myPinBit );
  WRITE_PERI_REG( 0x60000308, myPinBit );
  os_intr_unlock();
}


then it's

00000000 <loop-0x10>:
   0:   000304           excw
   3:   030860           excw
   6:   006000           rsil   a0, 0
   9:   000000           ill
   c:   000000           ill
   ...

00000010 <loop>:
  10:   f0c112           addi   a1, a1, -16
  13:   036102           s32i   a0, a1, 12
  16:   fffc01           l32r   a0, 8 <loop-0x8>
  19:   0000c0           callx0   a0
  1c:   fff931           l32r   a3, 0 <loop-0x10>
  1f:   022c             movi.n   a2, 32
  21:   0020c0           memw
  24:   0329             s32i.n   a2, a3, 0
  26:   fff731           l32r   a3, 4 <loop-0xc>
  29:   0020c0           memw
  2c:   0329             s32i.n   a2, a3, 0
  2e:   fff701           l32r   a0, c <loop-0x4>
  31:   0000c0           callx0   a0
  34:   3108             l32i.n   a0, a1, 12
  36:   10c112           addi   a1, a1, 16
  39:   f00d             ret.n


while
Code: [Select]
void loop() {
  os_intr_lock();
  WRITE_PERI_REG( 0x60000304, 0x20 );
  WRITE_PERI_REG( 0x60000308, 0x20 );
  os_intr_unlock();
}


generates

00000000 <loop-0x10>:
   0:   000304           excw
   3:   030860           excw
   6:   006000           rsil   a0, 0
   9:   000000           ill
   c:   000000           ill
   ...

00000010 <loop>:
  10:   f0c112           addi   a1, a1, -16
  13:   036102           s32i   a0, a1, 12
  16:   fffc01           l32r   a0, 8 <loop-0x8>
  19:   0000c0           callx0   a0
  1c:   fff931           l32r   a3, 0 <loop-0x10>
  1f:   022c             movi.n   a2, 32
  21:   0020c0           memw
  24:   0329             s32i.n   a2, a3, 0
  26:   fff731           l32r   a3, 4 <loop-0xc>
  29:   0020c0           memw
  2c:   0329             s32i.n   a2, a3, 0
  2e:   fff701           l32r   a0, c <loop-0x4>
  31:   0000c0           callx0   a0
  34:   3108             l32i.n   a0, a1, 12
  36:   10c112           addi   a1, a1, 16
  39:   f00d             ret.n


and finally

Code: [Select]

void loop() {
  WRITE_PERI_REG( 0x60000304, 0x20 );
  WRITE_PERI_REG( 0x60000308, 0x20 );
}


is


00000000 <loop-0x8>:
   0:   000304           excw
   3:   030860           excw
   6:   316000           srai   a6, a0, 16

00000008 <loop>:
   8:   fffe31           l32r   a3, 0 <loop-0x8>
   b:   022c             movi.n   a2, 32
   d:   0020c0           memw
  10:   0329             s32i.n   a2, a3, 0
  12:   fffc31           l32r   a3, 4 <loop-0x4>
  15:   0020c0           memw
  18:   0329             s32i.n   a2, a3, 0
  1a:   f00d             ret.n
Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous


J-M-L

U might check out digitalWriteFast()

https://forum.arduino.cc/index.php?topic=243147.0
this one does not deal with ESP8266 - but yes for "standard" arduino that's a good reference
Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous

mcnobby

Thanks for all that J-M-L, what file did you look at to get that information ?

Go Up