Okay so I got to play around with things a little this weekend.
Code:
#define CPU_RESET_CYCLECOUNTER do { ARM_DEMCR |= ARM_DEMCR_TRCENA; \
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; \
ARM_DWT_CYCCNT = 0; } while(0)
//Number of cycles to have passed
int cycles;
//pin numbers to set input/output and read values
//PORT D
const int PIN_D00 = 2;
const int PIN_D01 = 14;
const int PIN_D02 = 7;
const int PIN_D03 = 8;
const int PIN_D04 = 6;
const int PIN_D05 = 20;
const int PIN_D06 = 21;
const int PIN_D07 = 5;
const int PINS_D[8] = {PIN_D00,PIN_D01,PIN_D02,PIN_D03,PIN_D04,PIN_D05,PIN_D06,PIN_D07};
//initialize individual pin read values
//PORT D
byte BYTE_D;
int BITS_D[8] = {0,0,0,0,0,0,0,0};
//number of iterations in for loop conparisons
const int numItr = 1000000;
//Turn all interupts off
void interuptsOFF(){
noInterrupts();//turn off interupts
cli();//turn off interupts
__disable_irq();
}
//Turn all interupts back on
void interuptsON(){
interrupts();//turn interupts back on
sei();//turn interupts back on
__enable_irq();
}
//print byte value to serial port for monitoring
void serialPrintByte(byte myByte,int byteLength=8){
for( int i=byteLength-1; i>=0; i--){
Serial.print(bitRead(myByte,i));
}
Serial.println("");
}
//print bits value to serial port for monitoring
void serialPrintBits(int myBits[],int bitsLength=8){
for( int i=bitsLength-1; i>=0; i--){
Serial.print(myBits[i]);
}
Serial.println("");
}
//Initialization for each port to be digital input
void initializePort_input(const int PINS[]){
for(int i=0; i<=int(sizeof(PINS)); i++){
pinMode(PINS[i],INPUT);
}
}
//Read ports as byte
//PORT D
int read_BYTE_D(){
CPU_RESET_CYCLECOUNTER;
BYTE_D = GPIOD_PDIR;
cycles = ARM_DWT_CYCCNT;
return cycles;
}
//Read ports as bits
//PORT D
int read_BITS_D(){
CPU_RESET_CYCLECOUNTER;
BITS_D[0] = digitalReadFast(PINS_D[0]);
BITS_D[1] = digitalReadFast(PINS_D[1]);
BITS_D[2] = digitalReadFast(PINS_D[2]);
BITS_D[3] = digitalReadFast(PINS_D[3]);
BITS_D[4] = digitalReadFast(PINS_D[4]);
BITS_D[5] = digitalReadFast(PINS_D[5]);
BITS_D[6] = digitalReadFast(PINS_D[6]);
BITS_D[7] = digitalReadFast(PINS_D[7]);
cycles = ARM_DWT_CYCCNT;
return cycles;
}
void setup() {
// put your setup code here, to run once:
//initialize each port to read as input
//PORT D
initializePort_input(PINS_D);
//allow serial com to terminate
while (!Serial);
delay(100);
}
void loop() {
//Method 1
//turn off interupts
interuptsOFF();
//read port D and count cycles
cycles = read_BYTE_D();
//allow interupts back on
interuptsON();
//print data to screen
Serial.println("---------------");
Serial.println("METHOD 1: PORT D BYTE READ, FUNCTION CALL");
Serial.print("cycles: ");
Serial.println(cycles);
serialPrintByte(BYTE_D);
Serial.println("---------------");
//stall before next read
delay(1000);
//Method 2
//turn off interupts
interuptsOFF();
//read port D and count cycles
CPU_RESET_CYCLECOUNTER;
BYTE_D = GPIOD_PDIR;
cycles = ARM_DWT_CYCCNT;
//allow interupts back on
interuptsON();
//print data to screen
Serial.println("---------------");
Serial.println("METHOD 2: PORT D BYTE READ, DIRECT");
Serial.print("cycles: ");
Serial.println(cycles);
serialPrintByte(BYTE_D);
Serial.println("---------------");
//stall before next read
delay(1000);
//Method 3
//turn off interupts
interuptsOFF();
//read port D and count cycles
cycles = read_BITS_D();
//allow interupts back on
interuptsON();
//print data to screen
Serial.println("---------------");
Serial.println("METHOD 3: PORT D BIT READ, FUNCTION CALL");
Serial.print("cycles: ");
Serial.println(cycles);
serialPrintBits(BITS_D);
Serial.println("---------------");
//stall before next read
delay(1000);
//Method 4
//turn off interupts
interuptsOFF();
//read port D and count cycles
CPU_RESET_CYCLECOUNTER;
BITS_D[0] = digitalReadFast(PINS_D[0]);
BITS_D[1] = digitalReadFast(PINS_D[1]);
BITS_D[2] = digitalReadFast(PINS_D[2]);
BITS_D[3] = digitalReadFast(PINS_D[3]);
BITS_D[4] = digitalReadFast(PINS_D[4]);
BITS_D[5] = digitalReadFast(PINS_D[5]);
BITS_D[6] = digitalReadFast(PINS_D[6]);
BITS_D[7] = digitalReadFast(PINS_D[7]);
cycles = ARM_DWT_CYCCNT;
//allow interupts back on
interuptsON();
//print data to screen
Serial.println("---------------");
Serial.println("METHOD 4: PORT D BIT READ, DIRECT");
Serial.print("cycles: ");
Serial.println(cycles);
serialPrintBits(BITS_D);
Serial.println("---------------");
//stall before next read
delay(1000);
}
I used the above code to check the number of cycles required to do a port read and compared that to the number of cycles to perform a digitalReadFast() on the same pins. I also made a comparison between wrapping these reads in a method just to see if that appears to affect things. Earlier I started comparing for loops to manual repeated queries also but I made changes as I went and lost exactly what that code looked like
Results:
METHOD 1: PORT D BYTE READ, FUNCTION CALL - 10 cycles
METHOD 2: PORT D BYTE READ, DIRECT - 8 cycles
METHOD 3: PORT D BIT READ, FUNCTION CALL - 56 cycles
METHOD 4: PORT D BIT READ, DIRECT - 49 cycles
Earlier when I was doing it slightly differently I was able to get the direct port (Byte) read down to 6 cycles. So now I am wondering what I can do to further reduce the number of cycles? Currently I am disabling interrupts and to be honest I am not sure the difference between cli(), noInterrupts(), and __disable_irq(), insight would be appreciated. Also if anyone can point out other non-essential things to turn off in order to reduce cycles between reads it would be appreciated. Does it make sense to also/alternatively use atomic blocks? (
http://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html)
I have not yet looked and how much overhead reading these into an array versus into int and byte variables as I am using now, but am planning to try that if I can get the number of cycles here down further.
Is there a good library merging i2c and lcd as in the ardafruit RGB LCD library? (
https://learn.adafruit.com/rgb-lcd-shield/using-the-rgb-lcd-shield) as I have this shield and would like to try
One other thing, I was checking that I was actually reading the pins as I expected using a dip switch to toggle the pin low or high by either grounding it out or connecting it to 3.3v.
I noticed that several of the higher bits (for example: const int PIN_D05 = 20; const int PIN_D06 = 21; const int PIN_D07 = 5; in the above code) would not change regardless of what I did. I was not using pullup/pulldown resistors, is it likely this is the cause or is there something else I need to do to be able to use all the pins of each port? I think I am just going to try to use INPUT_PULLUP as described here --
http://www.pjrc.com/teensy/td_digital.html and see if that doesnt sort it out.
I tested code having the same format as above for each of the ports and has simillar problems on the others as well.
The port setup for the other ports
Code:
//PORT B
const int PIN_B00 = 16;
const int PIN_B01 = 17;
const int PIN_B02 = 19;
const int PIN_B03 = 18;
const int PIN_B16 = 0;
const int PIN_B17 = 1;
const int PIN_B18 = 32;
const int PIN_B19 = 25;
const int PINS_B[8] = {PIN_B00,PIN_B01,PIN_B02,PIN_B03,PIN_B16,PIN_B17,PIN_B18,PIN_B19};
//PORT C
const int PIN_C00 = 15;
const int PIN_C01 = 22;
const int PIN_C02 = 23;
const int PIN_C03 = 9;
const int PIN_C04 = 10;
const int PIN_C05 = 13;
const int PIN_C06 = 11;
const int PIN_C07 = 12;
const int PIN_C08 = 28;
const int PIN_C09 = 27;
const int PIN_C10 = 29;
const int PIN_C11 = 30;
//const int PINS_C[12] = {PIN_C00,PIN_C01,PIN_C02,PIN_C03,PIN_C04,PIN_C05,PIN_C06,PIN_C07,PIN_C08,PIN_C09,PIN_C10,PIN_C11};
const int PINS_C[12] = {PIN_C00,PIN_C01,PIN_C02,PIN_C03,PIN_C04,PIN_C05,PIN_C06,PIN_C07};
Which leads me to my next question. If the register is only 8bits long how are the extra pins of port C handled? Also how are the non-sequential and or missing bits handled? For instance for port B
Pin Bit
16 PTB00
17 PTB01
19 PTB02
18 PTB03
49 PTB04
50 PTB05
31 PTB10
32 PTB11
0 PTB16
1 PTB17
29 PTB18
30 PTB19
43 PTB20
46 PTB21
44 PTB22
45 PTB23
(
Bold indiccated pin on the backside as a pad -- from
https://forum.pjrc.com/threads/34808-K66-Beta-Test?p=112462&viewfull=1#post112462)
Of which only 5 of the first 8 bits are present, then 2 of the next 8, then 8 of the last 8. So if I do
BYTE_B= GPIOB_PDIR;
exactly which pins/bits am I getting? and is it possible to read another (different) 8 in the same manner? I think the answer is no and this is why people reccommend using PORT D/C because they have sets of sequential bits. But D actually has two sets, can I read the second set the same way somehow?
And then there is this:
I have one quick question and one possibly more difficult one. This is my first time using Port Manipulation and normally the pins are set high or low using 8-bits (B11110000) but Teensy 3.1 uses 32 bit right? does that mean you should use something like B00000000111100001111000011110000 ?
from --
https://forum.pjrc.com/threads/1753...PORT-DDR-D-B-registers-vs-ARM-GPIO_PDIR-_PDOR
which leads me to believe I should be able to read 32 bits simultaneously, or is this only true of the 3.1?
Thanks