FRDM_K22F Port A LEDs unexpected behavior

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

FRDM_K22F Port A LEDs unexpected behavior

Jump to solution
1,548 Views
FlyingCzech
Contributor II

I have an issue with the FRDM_K22F development board that is really perplexing me and am hoping someone might have some insight.  I am writing a simple C blinky program using Keil MDK 5 with the CMSIS-Core.  I am not able to get either the Red or Green LEDs to change state (Port A) but the code works fine on the blue LED (Port D).  I've also hooked up an LED/Resistor to PTE0 and 1 and this functions as expected.  But..

When I run the blinky demo in Keil, the red LED behaves correctly.  I've also gone over to MCUExpresso and gotten the RED to work.  I don't think it is an electrical issue.  I've put PortA Pin 1 on the scope to see if it is switching too fast but the line has no oscillation, it stays constant high.  

I suspected that maybe there was a lock on the pin that would have to be turned off but can find not reference to this.  This was an issue when I was using a TI development board but was noted in the reference documents, I can't find a similar reference in the NXP documentation.  I see notes referring to PTA1 to 5 having a different configuration at reset but I don't see where this would be causing a problem.

Finally, I have run the working demo code through the debugger looking for something that would indicate something I've missed but do not see anything.  The code I am using is pretty simple and I'll paste it below.  I'm still fairly new to working with the ARM controllers so I do apologize if I'm missing something very obvious.  Any help would be greatly appreciated.

Added 2/21/2021 6:57 EST

I realized I had made an idiotic blunder and created the project with the wrong device.  So I recreated the project but got the same results.  This led me to try my more thing and this still has the issue.  What I did was to take the "led_blink(FRDM-K22F)" example from the Keil pack installer.  The example works as written.  When I remove the RED LED functions and replace them with the calls from the MK22F51212.h header, I do not get a blinking red LED.  When I replace the red LED configuration with the blue LED configuration, it works.  This convinces me more that there is something I need to turn on for the PortA lines.

int main(void)
{
/* Board pin init */
SIM->SCGC5 |= 0x1000;
PORTD->PCR[5] |=0x0100;
PTD->PDDR |= 0x20;

/* Set systick reload value to generate 1ms interrupt */
if (SysTick_Config(SystemCoreClock / 1000U))
{
while (1)
{
}
}

while (1)
{
/* Delay 1000 ms */
SysTick_DelayTicks(1000U);
PTD->PTOR |= 0x20;

 

 

 

#include "MK22F51212.h"                    // Device header

int SysTickDelay(int msec);

int main(void){                   // Changing to these lines works
	SIM->SCGC5 |= 0x200;          //SIM->SCGC5 |= 0x1000; 
	PORTA->PCR[1] |= 0x0100;      //PORTD->PCR[5] |=0x0100;
	PTA->PDDR |= 0x02;            //PTD->PDDR |= 0x20;
	while(1){
		PTA->PTOR |= 0x02;        //PTD->PTOR |= 0x20;
		SysTickDelay(1000);    
	}
}
	
int SysTickDelay(int msec){
  int i;
	SysTick->LOAD = 0x7530;
	SysTick->CTRL |= 0x05;
	for(i=0;i<msec;i++){
		while((SysTick->CTRL & 0x010000)==0){}
	}
return(0);
}

 

 

 

Labels (1)
Tags (3)
0 Kudos
1 Solution
1,517 Views
mjbcswitzerland
Specialist V

Hi

PORTA->PCR[1] |= 0x0100;

is incorrect. It should be
PORTA->PCR[1] = 0x0100;

Although not really wrong

PTA->PTOR |= 0x02;

would also be better with

PTA->PTOR = 0x02;

Regards

Mark

P.S. Running your code in the uTasker Kinetis simulator (available free at https://github.com/uTasker/uTasker-Kinetis) it showed that PTA1 was configured as JTAG_TDI before executingPORTA->PCR[1] |= 0x0100; and after the write it was "still" configured as JTAG_TDI.

View solution in original post

10 Replies
1,471 Views
FlyingCzech
Contributor II

Thank you both, you have given some great insight and also helped me me appreciate some of the complexity that I suspected was there but am not sure how to learn where it is.  You are correct, I meant a bitwise OR, I was thinking too far ahead of my typing and it got the better of me!  I can't say that I will not end up using the SDK, or Utasker, but for the screwed up way my brain works, I really need to approach it differently.  I started going down this path doing what you suggested and found it too overwhelming which is why I have taken a step back and approached it this way.  In general I have been able to do much of what I was hoping for and the code works well, however, I have had a couple of issues, like this one, where the behavior isn't as I expect and I think that Mark has hit on what I was missing. 

These issue have one thing in common and that is that they have  been when I have been trying to use ports that have some unique architecture (my words, but I'm sure there is a more proper way to say this).  I've not really thought about this as being a read-write-read versus an atomic issue but now much of this makes some amount of sense.  I knew there was something I was missing about PortA1-5 and I knew that it was designed to be JTAG default but couldn't find anything guiding me to how to approach it.  In a sense, this is my issue with the SDK because, while the SDK does do this for me, it is not obvious why and I really want to know why.  It is buried in the SDK I know, but trying to get to it through the layers of wrappers is not something my brain is really capable of doing.  If you'll forgive the slightly obscure reference, I would be one of the people who did not see the gorilla in the video! 

Speaking of video, I could not get get the YouTube link to the bit banding video to play but I did find this (82) ARM Cortex M4 Advanced Tips - Bit-Banding - single bit manipulation - YouTube, is this the video you were referring to?  I think this is something I really need to understand so thank you for steering me to it.  Timing, concurrency, race conditions, etc. are all issue that I have observed causing many problems and I am really wanting to understand how to remove them so I suspect this topic will be helpful to that end.

As you can probably guess, embedded controller software was not my career, that part of my life is in all likelihood over, but embedded controllers are something I have always been interested in but not had the time to work on it until recently.  But this also means that I am not doing this to get a product out, although that would be a longer term goal, but it is more of a new challenge that I have found very enjoyable.  I probably won't be comfortable with using the more efficient tools until I become more comfortable with just understanding how the product works.  That is just my own approach and I realize it is probably borderline Asperger's.  So please don't take anything I am saying as being critical to either of your opinions, I actually understand that much of what I think I know is probably wrong and it is very helpful to understand where both of you are coming from so that I can get better at this.  I will say this one last time, both of you had some great insight and you really did help me understand some of the complexity that I was missing so I am very appreciative to you both.  

0 Kudos
1,464 Views
mjbcswitzerland
Specialist V

Hi

I fixed the bit-banding video link.

It should have been  https://youtu.be/FZnkZ1h_EAQ

Regards

Mark

 

0 Kudos
1,522 Views
myke_predko
Senior Contributor III

Hey @FlyingCzech 

I just reviewed the code and it looks like you're:

  1. Enabling the PORTA Clock
  2. Setting the Pin Mux to PTA1
  3. Setting the direction to output
  4. Looping:
    1. Toggling the Output
    2. Delaying

Since this is "main" it appears that you're not initializing any clocks.  I *believe* that you're running at 32kHz as the initial clock speed using the Slow IR CLock.  Now, I haven't used the System Tick Timer, so I can't confirm that your 1ms delay loop is actually what you think it is (but if you're writing 0x7538 (30,008d) into a "LOAD" register, it seems like you might be if the System Tick Timer is incremented once every clock cycle).  

You're also not explicitly setting a state for the pin.  I don't think it's an issue, but when I look at the NXP SDK, it always sets the output state before setting the pin direction when initializing the pin.  I can't imagine that this is the problem, but I've see weirder things implemented in silicon.  

I would suggest that you single step through your code and:

  1. Verify that you're writing to the right registers the RM has the correct addresses.  Includes have been wrong before.
  2. Add a "PTA->PCOR |= 2;" instruction before the loop as the LED is negative active logic and this will turn on the LED.
  3. Confirm your SysTickDelay operation or at least confirm that you are returning from the method as well as falling out of the "while ((sysTick->CTRL & 0x010000)==0){}" loop.  

Sorry I can't be of more help.  

Good luck and let us know how you make out,

myke

1,508 Views
FlyingCzech
Contributor II

First, thank you for reading over my issue and you gave me some things to look at.  First I did try setting the state explicity as you suggested but that was not it, which is kind of what you thought as well.  But, as you say, strange things happen.  But you also gave me a great idea, I hadn't thought to put some kind of flag in the interrupt to see if it was actually working so that was worth doing.  I added a second led onto PortE line 1 and added it into the "led_blinky" example that uses the SDK.  Good and bad news though.  The good news is that the interrupt is working and the time seems spot on (I put the scope on PTE1 and the signal was exactly what I expected.  But that is also the bad news because it means I still haven't found the issue.  

I've gone through the SDK as well and I can't see anything in it that does anything beyond what I would expect, but I still think there is something going on with PTA1 and 2 that I can't find.  Even stepping through it in the debugger doesn't show anything different than how PTD1 gets initialized.

I should add that I did find a reference in the reference documentation, 10.2.1 regarding the way PTA1-5 are configured at reset but when I toggled those bits in the port control register during initializing, it did not seem to help and I don't see them being touched in the SDK so still hunting.

FlyingCzech_0-1614047985231.png

I want to express my gratitude for taking the time to read and respond to my post.  You gave me some good things to check and consider which is never unhelpful even if it doesn't solve the immediate problem.  Thank you very much.

0 Kudos
1,525 Views
nxf56274
NXP Employee
NXP Employee

Hi,

Please download the sdk in our website. SDK

It is more convenient than configuring the register directly.

Have a great day,
TIC

-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!

- We are following threads for 7 days after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------

0 Kudos
1,495 Views
FlyingCzech
Contributor II

I have and was able to use it to confirm that the board was functioning correctly.  I'm not sure I agree about the convenience but I can only speak for myself on that, I'm sure others do find it very convenient.  Personally, I like working with the registers and datasheets.   I will also say that for the work I am doing with the MCU, I would like to stay as close to the registers as possible just because I think it reduces the likelihood of unintended behavior and let me tweak things with less effort.  I think this issue with PTA1 illustrates that, the SDK worked but why did it work?  Probably not an issue if your are blinking an LED, and in reality I am reserving that port for JTAG on my board but I'd still like to know why I would need to write different bits to port A Pin 1 that Port E Pin 1.  Sometime it might matter and its always better to know why.  That's my 2 cents anyway, sorry for being wordy, I guess I don't get a chance to chat with others much since Covid isolation!

0 Kudos
1,518 Views
mjbcswitzerland
Specialist V

Hi

PORTA->PCR[1] |= 0x0100;

is incorrect. It should be
PORTA->PCR[1] = 0x0100;

Although not really wrong

PTA->PTOR |= 0x02;

would also be better with

PTA->PTOR = 0x02;

Regards

Mark

P.S. Running your code in the uTasker Kinetis simulator (available free at https://github.com/uTasker/uTasker-Kinetis) it showed that PTA1 was configured as JTAG_TDI before executingPORTA->PCR[1] |= 0x0100; and after the write it was "still" configured as JTAG_TDI.

1,501 Views
FlyingCzech
Contributor II

Quick question on setting the bits.  I generally use the logical NOT with many statements to insure I am not unintentionally changing other bits in the register.  It don't think that is really an issue with the port control register and I am thinking that the PTA->PTOR register is not an issue as long as you weren't accidently changing another pin's PTOR status from 1 to 0 during the time (I can't think of why that would be an issue though).

I ask because I am fairly new to working with 32bit MCU's and may be misunderstanding something basic.  I would think it is safer to use the NOT but I would welcome any thoughts to why this would be bad practice as I feel I'm still in the wading pool in this ocean of knowledge :).

Now to the second part of your note.  That is very interesting that you are not seeing the  the pin change.  So I had to try your advice, changing the PTA->PCR[1] = 0x0100;.  That solved it!  So I have to ask, do you know why that is?   I have two  guess , the first is that it forced bits 6,5,4,2,and 1 to be 0 which is where they were suppose to be.  The second is that it also forced bit 15, the lock register to 0 insure it was unlocked.  I did not see anything that implied that this register was locked.  But I also thought I had checked these two scenarios but I will freely admit that I may thought I had without actually having done it...

So that was a long winded reply and I am very appreciative for your advice and time.  I really appreciate the help and please do not mistake my questions about setting bits to be in any way argumentative, I really have much to learn so I am interested in getting fresh points of view.

Also regarding Utasker, I have spent a little time looking at it, even watched a few of the videos, and I'm a bit embarrassed to say this but I can't quite get my head wrapped around it.  It the same reason I haven't dived headlong into the SDK, I just get overwhelmed by the structural complexity of it all.  I personally find it easier to start with a very basic approach but that is just my own personal quirkiness.  I suspect that Utasker is an incredibly powerful tool but am not sure where to start when trying to learn it. 

OK, longwinded, but again, thank you so much for your help.  Have a great day.

Thomas 

0 Kudos
1,482 Views
mjbcswitzerland
Specialist V

Hi

PTOR

This is a special atomic register (as many are). It toggles bits that are '1' and doesn't change bits that are '0'.

PTOR |= 1;
will do the same thing as
PTOR = 1;

(but only because it always reads back 0 - otherwise it would in fact fail and toggle unwanted bits too) but will take about 100ns longer to execute so if you are toggling ports it will always be slower.

It won't (usually) fail if using always |= for such functions but registers that are designed to work as they do to achieve optimal speeds are being wasted - they are used slower and 3x the number of instructions are needed to do the same thing. See also this bit banding video to slow similar concept supported by the M4 for some RAM and most peripheral access.

https://youtu.be/FZnkZ1h_EAQ

The basic error that you had with PCR[1] is that its MUX value out of reset is 0x743, to select its JTAG mode with a pull-up resistor enabled. Most others default to 0x1XX to select their GPIO mode. This means that PCR[10] |= 0x100 is mostly a redundant command - it changes a GPIO into a GPIO, thus doing nothing at all.
In the case of some ports that default to non-GPIOs  PCR[1] |= 0x100 is something like 0x700 | 0x100 = 0x700 and thus also redundant but resulting in a value that is not that what was actually required at all.

In fact if you copied the technique from reference code the reference code was already dangerous (a bug waiting to fail) and only worked by chance. The correct way to set the mux function would never be with PCR[x] |= x but instead (classically) be PCR[x] = ((PCR[x] & ~0x700) | (x & 0x700)); This is fail safe but maybe a little over the top in many cases too.

The uTasker methods have been crafted to avoid users being able to make errors which cause loss of time. For example to do what you do it uses:

// Configure the port as output with initial state '0', low drive strength and slow slew rate (including enabling any clocks needed)
_CONFIG_DRIVE_PORT_OUTPUT(A, (PORTA_BIT1), (0), (PORT_SRE_SLOW | PORT_DSE_LOW));
_TOGGLE_PORT(A, PORTA_BIT1); // toggle the bit

Port macros are shown here:
https://www.utasker.com/forum/index.php?topic=1875.0

This video shows also that Kinetis projects can work on i.M RT parts with virtually no code changes: https://youtu.be/SmFTi8hlba0 also pin toggling is shown here: achieving 150MHz outputs (https://youtu.be/nLInUIboLR0) which is only possibly by using their registers as they were designed to be used most optimally.

In your case it would have worked first time with virtually no risk of user error. It would be optimal (using any bit banding techniques that are possible for faster operation and smallest code size). It works with IAR, Keil, MCUXpressor, Greenhills, Rowley Crossworks, etc. and also the uTasker simulator which shows whether the result works or not.

The simulator allowed me to find and fix your error in 30 seconds.

Therefore it allows most developers to improve their development efficiency and code reliability quite dramatically but does need a short period of learning and changing thinking from the traditional methods that many main stream developer use. Many developers are either set in their ways or are frightened to try what can be a paradigm shifting experience. That is however OK because it allows uTasker users who are prepared to invest a little time to gain a big competitive advantage over users of main-steam techniques.

Regards

Mark

0 Kudos
1,486 Views
myke_predko
Senior Contributor III

@FlyingCzech 

I'm sure Mark has his own comments but here are mine.  

First off, I *think* you're calling "|=" "logical NOT" and this is not correct, you're doing a bitwise OR on the contents of the register.  A logical NOT would look like "!=".  

With Mark's post and your results, I understand where the problem is.  

When the K22 is reset, the PORTA->PCR[1] register has a default "MUX" value of 0b111 (the default is "ALT7" or "JTAG_TDI" - which Mark reported that the pin was set to).  To select the DI/DO functionality ("PTA1") you want the MUX value to be 0b001 and your are doing a logical OR of the contents and not changing anything as you look at how the code executes:

  PORTA->PCR[1] |= 0x100; // Expands out to

  PORTA->PCR[1] = PORTA->PCR[1] | 0x100;  

//  Since MUX bits default to 0b111, ORing it with 0b001 doesn't change anything. 

//  The line of code is effectively:

  PORTA->PCR[1] = PORTA-PCR[1]; 

I just saw your reply regarding using the SDK and I highly recommend that you reconsider your attitude regarding using the SDK for your application coding.  I know where you're coming from; I started in the 8 bit world and I saw a lot of very poor drivers written by manufacturers that were often written for a specific purpose turned into an ApNote and then used as a reference for a driver as well as being compiled by a tool that was very primitive.  

This is 2021 and we're working with a very complex 32bit processor and integrated peripherals.  The SDK code that I have seen is quite well written.  I don't think Mark feels as positively about it as I do (he feels his code is better) but I haven't encountered any situations where there are execution errors and the examples are not bad.  Along with that, we're working with a very sophisticated and validated compiler so the code produced is quite well optimized.  Finally, we're working with processors with thousands of registers and a plethora of execution modes - understanding all of them (or even the ones critical to your application, even one as simple as this one) is simply not reasonable.  

Hopefully you can look over this thread and see the irony in your statement "I would like to stay as close to the registers as possible just because I think it reduces the likelihood of unintended behavior " - becasue this is exactly what happened, instead of letting the SDK code setup the IO pin, you ineffectively wrote to a register and ended up with behaviour that you didn't expect.  

Instead of starting your journey with the Kinetis by studying the 1,500 pages of the RM and the 80 or so pages of the datasheet, could I suggest that you download MCUXpresso and run through some of the example projects so that you can see how the SDK works and try your hand at some applications?  There is a ton of stuff to learn here and you can see how the tools makes your life easier and will get you running code faster with fewer problems.  

Trust me, there will be LOTS of cases where you need to take deep dives into the operation of the chip - let MCUXPresso with it's the SDK, GCC Compiler, GDB debugger make the initial steps much easier.  

0 Kudos