Portmanipulation for Teensy 3.6

Status
Not open for further replies.

Erik_

Member
Hello dear Teensy-Geeks,

i have searched for a complete documentation for direct port access for the Teensy 3.6. but i couldn't found it.

For example, if i want to check if pin 2 is high or low and want to switch pin 12 high or low i can use this code:
if (PIND & (1<<2)) {
/* Pin D2 is High */
PORTB |= (1<<4 ); // set bit 4 in PORTB to 1 (PortB4 == pin12)

} else {
/* Pin D2 is Low */
PORTB &= ~(1<<4 ); // clear bit 4 in PORTB to 0 (PortB4 == pin12)
}

here is pin 2 the port number: D2 and pin 12 the port number: B4

What i have found (by measuring the pins) is this:

pin port

14 C0
15 C1
16 C2
17 C3
18 C4
19 C5

0 D0
1 D1
2 D3
4 D4
5 D5
6 D6
7 D7

8 B0
9 B1
10 B2
11 B3
12 B4

1. Are there Infos about the other pins and their port names?
2. Are there Infos about analogread, analogwrite with direct port access?
(for example i need direct port access for the 2 DACs A21 A22 and other)

Thank you.

with kind regards
Erik
 
Not sure why you wish to do things the Port way... But...

There is a ton of information in the original K66 beta thread, with a lot of it summarized in the posting #8: https://forum.pjrc.com/threads/34808-K66-Beta-Test?p=106291&viewfull=1#post106291 and posting #3 has links to PDF files for the processor manuals.

The posting #8 shows a sortof card with more of the options broken out including the actual processor pin number. Also there are links to postings with Excel documents with lots of stuff on them...

Note: there may also be differences you will notice between AVR emulation of ports and the actual ports on the processor. For example pin 0 on the T36 maps to the Processor pin B16...
So if you really want direct port manipulation, you should look at chapter 12 of the Processor PDF
 
Thank you KurtE,

i found this thread earlier, sorry that i didn't mentioned it in my first post here. The problem is that when i use for example this code:

PORTB |= (1<<4 ); // set bit 4 in PORTB to 1 (PortB4 == pin12)

that means set pin12 high and i look in the documentation in the original K66 thread, there the port name of pin12 should be C7.
I tried this, but i couldn't measure high on pin12. Instead of port C7 port B4 worked.
So i have difficulties to use this documentation. (Also i tried it with other pins and port names)
I just want to use the pins of the circuit board (not of the processor).
But in the pictures in the K66 thread and in the Excel documents there the port names don't work with the pins.

Maybe there is a better solution, but i want to give out 2 different frequencies of sine waves at the 2 DAC pins.
These sine waves are switched on and off also with different frequencies.
For this i don't want to use interrupts, because i also use the serial communication and i read that there
are time-problems when interrupts and serial are used together.
Additionally there are switches used on pins what are be read and LEDs on pins what are set.
So the timing is a great problem, if the frequencies of the sine waves and the switch-frequencies should be precise.
Therefore i need direct port access.
I used before the Arduino Due (with 84 MHz clock) with direct port access, but the Teensy 3.6 (with 180 MHz) is a lot of faster.

I found a way for direct port access for digitalwrite and digitalread (see the code in my previous post).
But i haven't found a way for the analogread and analogwrite.

with best wishes
Erik
 
So that begs the question...

@KurtE: So that being said, how would you do it? For example, assume you'd connected something like a 3-to-8-line decoder to a Teensy, but didn't want three digitalWriteFast(n)s, because for a very fast 3-to-8-line decoder, you'd get really wild outputs on the output pins until you'd done setting all your inputs up...?

A specific use case (because someone will ask, anyway, and it's better to have specific hardware/pins to think about):
You have a 74HC138. You plan to use Teensy 3.6 pins 40, 41, and 42 as your A, B, and C inputs to the the 74HC138. Because that chip is pretty darn fast, setting each pin, one at a time, is going to cause confusion on the outputs of the 74HC138.
I'm also seeing a lot of conflicting posts about digital ports and pin assignments. Paul's page describing the Teensy (not sure if this is true for all versions, but I assume it is) is at odds with this one, third message which seems to show at least 16-bit-wide ports.
I will admit to being confused. Not like that's really difficult to do (sigh)...

Third edit: I found an example of someone doing exactly what I'm describing above. They're twiddling the bits, one at a time, using different pins, but the concept is the same:

Code:
#define SPICS       43
#define CSA2        48
#define CSA1        49
#define CSA0        50
pinMode( SPICS, OUTPUT );
pinMode( CSA0, OUTPUT );
pinMode( CSA1, OUTPUT );
pinMode( CSA2, OUTPUT );
digitalWrite( SPICS, 1 );
digitalWrite( CSA0, 0 );
digitalWrite( CSA1, 0 );
digitalWrite( CSA2, 0 );

So, I guess that's currently "how it's done". In this case, though causing "flutter" in the output lines of the 74HC138 by doing pin-at-a-time addressing would disrupt the devices I'm trying to monitor.

Not sure why you wish to do things the Port way... But...

There is a ton of information in the original K66 beta thread, with a lot of it summarized in the posting #8: https://forum.pjrc.com/threads/34808-K66-Beta-Test?p=106291&viewfull=1#post106291 and posting #3 has links to PDF files for the processor manuals.

The posting #8 shows a sortof card with more of the options broken out including the actual processor pin number. Also there are links to postings with Excel documents with lots of stuff on them...

Note: there may also be differences you will notice between AVR emulation of ports and the actual ports on the processor. For example pin 0 on the T36 maps to the Processor pin B16...
So if you really want direct port manipulation, you should look at chapter 12 of the Processor PDF
 
Last edited:
I don't know why I didn't get a notification that someone had replied. I think when I changed workstations I somehow didn't...something something. I thought nobody had replied so I did read that part of the datasheet, and then Paul's schematic. You know when a post begins with "therein lies a tale..." that it'll be interesting reading; I haven't had a chance yet but I'm saving that for this afternoon. I want a good cup of coffee to go with that one...

[Later, after the iced espresso:] Wow. I really love that thread. Thank you guys so much for showing me that...! So it looks like you don't have to port-mask then, if I'm reading that correctly; you can just set your output pins with pinMode(...) and you're good...

wow my hands are shaking...good luck connecting the logic analyzer pins lol
 
Last edited:
So, based on the schematic of the Teensy 3.6, I came up with this. It's not an exactly faithful replication of Paul's nice card that comes with the Teensy 3.6, but it helped me get something finished. I'm posting it here; others may find it useful...
TEENSY-3PT6-TOP.jpg
TEENSY-3PT6-BOTTOM.jpg
 
One of my test programs for port-at-a-time operation testing. You can use it to investigate just how non-atomic port operations can be (I will admit to being surprised by some of my results). Some things to investigate would be different clock rates, and running this on more than one Teensy 3.6.

Code:
/*
Written April 2018 by Richard Martin.
Inspired by https://forum.pjrc.com/threads/17532-Tutorial-on-digital-I-O-ATMega-PIN-PORT-DDR-D-B-registers-vs-ARM-GPIO_PDIR-_PDOR?highlight=slew+rate+limiting:
This program was written for the Teensy 3.6 for the purpose of evaluating non-atomic behavior in port-at-a-time operations.
What you will need:
	A Teensy 3.6 with both rows of 24 pins soldered in
	A handful of 3.3v-tolerant LEDs (and appropriate resistors, as necessary)
	A breadboard for all the parts
	A logic analyzer (Saleae, etc.)
*/

// Choose a port:
// 1 = A, 2 = B, 3 = C, 4 = D, 5 = E...
#define PORT 5

// Choose a test:
// 1 = every other, 2 = count with mask, 3 = count without mask...
#define TEST 2

// Choose a delay...
#define DELAY_MS 100

// Connect your logic analyzer, upload the code, and test away...

#if PORT==1
	#define PORT_PDOR GPIOA_PDOR
	#define pinCount 10
	// Not all the pins are included here! The first 4 bits of this port (0,1,2,3) are for the bootloader chip.
	uint8_t portPins[pinCount] = {25, 3, 4, 26, 27, 28, 39, 42, 40, 41}; // Port A bits 5, 12, 13, 14, 15, 16, 17, 26, 28, 29
	uint32_t portMask = 0xFFFFCFDF; // only the first 3 pins, not all 10 (0xCBFC0FDF)!
	
#elif PORT==2
	#define PORT_PDOR GPIOB_PDOR
	#define pinCount 16
	uint8_t portPins[pinCount] = {16, 17, 19, 18, 49, 50, 31, 32, 0, 1, 29, 30, 43, 46, 44, 45}; // Port B bits 0, 1, 2, 3, 4, 5, 10, 11, 16, 17, 18, 19, 20, 21, 22, 23
	uint32_t portMask = 0xFFFFFFF8; // only the first 3 pins, not all 16 (0xFF00F3C0)!
#elif PORT==3
	#define PORT_PDOR GPIOC_PDOR
	#define pinCount 12
	uint8_t portPins[pinCount] = {15, 22, 23, 9, 10, 13, 11, 12, 35, 36, 37, 38}; // Port C bits 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
	uint32_t portMask = 0xFFFFFFF8; // only the first 3 pins, not all 12 (0XFFFFF000)!
	
#elif PORT==4
	#define PORT_PDOR GPIOD_PDOR
	#define pinCount 15
	// Not all the pins are being brought out here! The first 6 bits of this port are for the 
	uint8_t portPins[pinCount] = {2, 14, 7, 8, 6, 20, 21, 5, 47, 48, 55, 53, 52, 51, 54}; // Port D bits 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15
	uint32_t portMask = 0xFFFFFFF8; // only the first 3 pins, not all 15 (0xFFF0400)!
#else
	#define PORT_PDOR GPIOE_PDOR
	#define pinCount 5
	// Not all the pins are included here! The first 6 bits of this port (0,1,2,3,4,5) are for the SD Card Reader.
	uint8_t portPins[pinCount] = {56, 57, 33, 34, 24}; // Port E bits 10, 11, 24, 25, 26 
	uint32_t portMask = 0xF8FFFFFF; // only the last 3 pins, not all 5 (0xF8FFF3FF)!
#endif

uint8_t count = 0;

uint32_t currentState;

void setup() {
	
	Serial.begin(250000);
	while(!Serial) {/*wait*/}
	
	Serial.println("Initializing pins...");
	
	// Turn all the brought-out pins in Port X on...
	for (int a=0; a<pinCount; a++) {
		pinMode(portPins[a], OUTPUT);
		digitalWrite(portPins[a], HIGH);
		delay(500);
	}
	
	// Save and print the current state of the port...
	currentState = PORT_PDOR;
	Serial.print("Current pin state is: "); Serial.println(currentState, HEX);
	
}

void loop() {
	
#if TEST==1
	// Set every other pin, then every *other* pin, HIGH...
	PORT_PDOR = 0xAAAAAAAA;
	delay(DELAY_MS);
	PORT_PDOR = ~PORT_PDOR;
	delay(DELAY_MS);
	// I observed that the shorter the delay, the more synchronously the pins "hit".
	// The longer the delay, the less synchronous they hit. At 1mS, all the pins being measured "hit" at the same time. 
	// With longer delays, some pins "hit" 10-20nS later than others.
	// Another observation: all the bits in Port B "hit" consistently synchronously, but Port E was wildly non-atomic, with
	// some pin "hits" more than 150nS from others...!
#elif TEST==2	
	// Count from 0-7 (first 3 pins) preserving the current state of the register, as recorded at
	// startup, except for the first 3 pins, which I'm displaying the count on...
#if PORT==1
	PORT_PDOR = (PORT_PDOR & portMask) | ((((count & 0x4) >> 2) << 5) | (((count & 0x2) >> 1) << 12) | ((count & 0x1) << 13));
#elif PORT==5
	PORT_PDOR = (PORT_PDOR & portMask) | ((((count & 0x4) >> 2) << 24) | (((count & 0x2) >> 1) << 25) | ((count & 0x1) << 26));
#else
	PORT_PDOR = (PORT_PDOR & portMask) | count;
#endif
	count++;
	if (count > 7) count = 0;
	delay(DELAY_MS);
	// I observed that the pin "hits" were consistently off by 10-20nS. Only when all 3 pins go back to 0
	// do they all appear to do this synchronously. In this case, longer delays between changes seem to help; 
	// at 250mS, only one pin was lagging 20nS behind the other two.
#else
	// Count from 0-7 (first 3 pins) but don't preserve the state of the register.
#if PORT==1
	PORT_PDOR = (((count & 0x4) >> 2) << 5) | (((count & 0x2) >> 1) << 12) | ((count & 0x1) << 13);
#elif PORT==5
	PORT_PDOR = (((count & 0x4) >> 2) << 24) | (((count & 0x2) >> 1) << 25) | ((count & 0x1) << 26);
//	PORT_PDOR = ((count & 0x4) << 22) | ((count & 0x2) << 24) | ((count & 0x1) << 26); // It can be written this way too, with the shifts compounded...
#else
	PORT_PDOR = count;
#endif
	count++;
	if (count > 7) count = 0;
	delay(DELAY_MS);
	// I observed that the pin "hits" were consistently more synchronous, although some of them were still
	// "off" by 10-20nS. Still, not using a mask seems to help the bits hit more synchronously.
#endif

}
 
Status
Not open for further replies.
Back
Top