Teensy 4.0 - How to access pins? (not with arduino)

Status
Not open for further replies.

javagedes

New member
Good morning,

With the new Teensy that came out, I've challenged myself to do bare-metal programming without the use of Arduino and I am slowly working through the set up. (I'm using rust but that is not important). I believe I've got the program loading onto my board, and I'm now attempting to access the pins (and some of the internal peripherals like the LED used in the blink program in Arduino). I've never done bare-metal programming... I'm used to FPGA's and accessing pins is much easier with that.. so I'm struggling to wrap my head around it.

I've been reading the documentation on how accessing GPIO works on the new I.MXRT1060, along with looking through the github page for the teensy4.0 to figure out how accessing the pins work. I believe the key sections in the datasheet are chapters 10 and 11.

From the documentation I have read, this is what i (THINK) I understand:

To set a pin mode to IN / OUT (I'm not even to the point of changing to I2C or something), I must:
find the particular IOMUX controlling the pin and set it to GPIO mode. I've seen GPR26 - GPR29(example: 10.4.27) lets me first select which GPIO i wish to access, then I get to the SW_MUX_CTL Register(example: 10.5.1 in the datasheet) that let me select GPIO mode.
go to that pin's GPIO and set the GDIR register to all zeros or all ones to set read / write, then I can just read / write to the DR depending on what mode its in.

Now I believe I'm misunderstanding that because there are a few things that don't add up...

*There are only 8 GPIO's (GPIO1 - GPIO9), so i don't understand how that can control 30+ pins on the board.
*Why are there so many IOMUXC's for each GPIO? I see that there is a mux for setting GPIO4_IO02, IO03, etc, and I have no idea what each of those are.
*What is the difference between SW_MUX_CTL 10.5.1 and SW_MUX_CTL in section 10.7.3 (and the many others like that).
*How are you suppose to know which GPIO / IOMUXC corresponds to what pin? I haven't found anything in the datasheet for that.


If anyone could help explain how this works to me, has a link to someone explaining it, or has some example code (doesn't matter what language really as long it is accessing memory and showing where / what to access), I would tremendously appreciate it. If I didn't make sense, just let me know and I'll try and explain better....

Also, can't wait to be told I'm completely misunderstanding how it works, because I'm sure I am LOL.
 
You might want to take look at t4 Arduino sources...

Likewise the t4 beta thread first page as there are listings showing what pin is connected to what
 
Understood, I'm looking at the beta thread now and its a little enlightening...

As an example, it says pin0 is GPIO1.3. This somewhat makes sense because I had seen mux's for GPIO1_IO03. is that what they mean by GPIO1.3? If so, this makes way more sense. That's because 10.7.46 is the IOMUXC that has control of GPIO1_IO03.

Does this also mean that for the 32 bit sections for each GPIO, it is [GPIOx_IO01, GPIOx_IO02, ..., GPIOx_IO32]?. That seems to make it a pain for pins that are in the same GPIO, because you have to access all 32 bits if you want to just change 1 pin. Does anyone know if there is a way around this? From previous readings, I know Teensy3.2 has a bitband such that each GPIO section is [u32; 32] instead of [u32]. This allows you to write to a single bit by writing a u32 bit 1 or 0 in software.

Thanks!!
 
Again you may want to take closer look at the code and at the different registers and the like>

I do not believe that they support Bitbanding however, Each of these Pots I believe have a PORTSET and a PORTCLEAR register.

So if you set a bit in the PORTSET register that pin will be turned to a 1, if you set a bit in the PORTCLEAR register that pins value will be set to zero...

Take a look at core_pins.h at the functions like: digitalWriteFast...
 
Hi,
I was interested in the same thing. Here is my working example using Digital Pin 20 on the Teensy 4.0. (i.e. connect an LED to ground, a resistor and digital pin 20).
I tried pretty hard on this code, although the datasheet is over 3600 pages, so if i did something wrong, feel free to correct me!
Hope this helps others who are interested in going deeper into the teensy 4.0 and register manipulation...

Code:
#include <Arduino.h>

// blink an LED on digital pin 20 using registers... teensy 4.0
// digital pin 20 is AD_B1_10 (based on Teensy 4.0 schematic on PJRC website)

// where this term occurs... getting to know the datasheet...
// GPIO_AD_B1_10		DATASHEET PAGE 3458, 3459, 3637, 290 twice, 291, 292, 295, 296, 299, 301, 305, 309, 310, 315 twice, 
//										400 twice, 407 twice, 498 ***, 530 ***, 537 ***, 553 ***, 678 ***, 786, .... , 1907 

// pad = pin.
// each pad can have up to 8 muxing options (ALT modes)
// IOMUXC controls each pad setting

// IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_10
// IOMUXC_SW_PAD_CTL_PAD_GPIO_SD_B1_10		// NO? This is something else?
// IOMUXC_SW_PAD_CTL_PAD_GPIO_B1_10
// IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_10

// UNDERSTANDING RANDOM stuff
// page 498: GPIO_AD_B1_10 is the pad (i.e. the pin). On the pin, GPIO1_IO26 is one option that can be multiplexed (ALT MODE 5)
// Therefore, we will use bit 26 for "stuff" when setting/clearing registers corresponding with GPIO pins...

void makeHigh();
void makeLow();

uint32_t b[32];

void setup() {
	
	for (int i = 0; i < 32; i++) b[i] = (1u << i);
	
	//gpio1_ipg_clk_s 	// page 319, 1078
	// IPG_CLK_ROOT		// page 319, 1087
	CCM_CCGR1 |= (3 << 26);						// page 1140 enable gpio1 clock 		(Not necessary - May be enabled by Arduino init?)
	CCM_CCGR4 |= (3 << 4);						// page 1144 enable iomuxc gpr clock 	(Not necessary - May be enabled by Arduino init?)
	
	// set IOMUX to GPIO mode, pg. 1019
	IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_10 |= 5;				// it is GPIO1_IO26
	IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_10 = 0 | (1 << 3);		// pad (pin) functionality, pg. 678, DRIVE STRENGTH
	IOMUXC_GPR_GPR26 &= ~b[26];								// page 371. Select GPIO1 (and not GPIO6) in mux 1 for bit 26. Read line 20-21 for why we are using bit 26.
	// IMPORTANT NOTE:	The number '26' in "GPR26" has NOTHING to do with using bit 26. COINCIDENCE.
	//					The GPR[0-34] are used for all sorts of random stuff... Register 26 happens to be for choosing GPIO1 OR GPIO6 for GPIO pins
	
	// set as output: GPIO_GDIR = 1 (page 1013)
	GPIO1_GDIR |= b[26];
	//pinMode(20, OUTPUT);
	
}

void loop() {
	makeHigh();
	delay(800);
	makeLow();
	delay(800);
}

void makeHigh(){
	GPIO1_DR |= b[26];				//GPIO_DR = 1				// DATA REGISTER
	//digitalWrite(20, HIGH);
}

void makeLow(){
	GPIO1_DR &= ~b[26];				// GPIO_DR = 0				// DATA REGISTER
	//digitalWrite(20, LOW);
}
 
@Mark_7 - Might work, but personally I would not do it this way.
Code:
void makeHigh(){
	GPIO1_DR |= b[26];				//GPIO_DR = 1				// DATA REGISTER
	//digitalWrite(20, HIGH);
}
You are directly potentially mucking with the state of several GPIO pins at the same time with that OR function. Example Suppose You read DPIO1_DR and then set it's state, but during the time between when you read the register and you wrote it back out. That some Interrupt happened, which changed the state of Teensy pin 21 (which is 1:27 GPIO).

When the ISR completes and your code returns, you then blindly write out the change back to the IO pin... Been there... Done that... And pull the few hairs I have left, trying to figure out why it does not work...

With the T3.x - you can use bitbanding stuff to do one bit at a time... Not on T4.
But with T4, there are 3 other interesting registers: DR_SET, DR_CLEAR, DR_TOGGLE
Code:
#define GPIO1_DR			(IMXRT_GPIO1.offset000)
...
#define GPIO1_EDGE_SEL			(IMXRT_GPIO1.offset01C)
#define GPIO1_DR_SET			(IMXRT_GPIO1.offset084)
#define GPIO1_DR_CLEAR			(IMXRT_GPIO1.offset088)
#define GPIO1_DR_TOGGLE			(IMXRT_GPIO1.offset08C)
These three registers only change the state of the pin if a value of 1 is passed in for a pin...
So you can do:
Code:
void makeHigh(){
	GPIO1_DR_SET = b[26];				//GPIO_DR = 1				// DATA REGISTER
	//digitalWrite(20, HIGH);
}

void makeLow(){
	GPIO1_DR_CLEAR = b[26];				// GPIO_DR = 0				// DATA REGISTER
	//digitalWrite(20, LOW);
}

note: the code for digitalWriteFast(20, HIGH);
and digitalWriteFast(20, LOW);

Will do just that.
 
Hi,

Wow, thanks for the reply. I read the datasheet just enough to get it working and looking back that makes complete sense. Yes, better to be safe than sorry. I remember learning about that same concept back in school and I completely agree.
Should've finished reading the datasheet...

Also, it finally just clicked in my why the Arduino Due only supports the set and clear registers and not direct access (in case anyone is familiar with the Arduino Due registers...)
 
Status
Not open for further replies.
Back
Top