Undocumented behavior of HCS12 ECT compare 7

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

Undocumented behavior of HCS12 ECT compare 7

1,847 Views
Lundin
Senior Contributor IV

I'm looking for an explanation of how the output compare 7 feature of the HCS12 ECT timer is supposed to work. There is apparently some undocumented behavior for this timer, or at least I can't find any description explaining the below behavior it in the manual.

 

What I want to do, is to sync numerous timer outputs to go high at the same time, but to pull them low individually, based on their individual timer settings.

 

According to the manual, the OC7 timer will upon successful output compare on timer 7 set the pins selected through mask register OC7M to the value specified in OC7D. So far so good. This works well, upon OC7 interrupt, all pins are pulled high like I want.

 

What's not documented though, is that some sort of automatic trick is also pulling said pins specified through OC7M low, when their individual timers elapse. This overwrites the behavior specified in TCTL1.

 

Simplified example code that uses only channel 0 and channel 7:

 

//setup HPRIO = 0xE0; // CH7 interrupt high prio TSCR1 = TEN; TSCR2 = clk_div TIOS = IOS0 | IOS7; TCTL1 = 0x00; // timers disconnected from output logic TCTL2 = 0x00; // timers disconnected from output logic OC7M = 0x00;  OC7D = 0x01; // pull channel 1 high upon OC7 TC7 = TCNT + pwm_period; TIE = C7I; // enable timer interrupts on ch7 ...

 

void compare7_interrupt(void) {   uint16_t tc7;    __asm BSET TFLG1, $FF                          // clear all interrupt flags   tc7 = TC7;    if(duty == 0)   {     OC7M &= ~0x1;                                // disable ch7 output compare for this pin   }   else   {     OC7M |= 0x01;                           // enable ch7 output compare for this pin     TC0 = tc7 + duty;               // set output compare based on duty cycle   }    TC7 = tc7 + pwm_period; }

Through some undocumented feature of OC7, this apparently gives me a PWM on channel 0 based on the specified duty cycle. I can't find any documentation stating what causes the pin to get pulled low.

 

I don't want this behavior, because I need to trigger individual interrupts for each channel, where I read an ADC and then pull the pin low manually. The system is something very common, namely a PID regulator where the PWM generated by the timers is regulated by ADC reads. The ADC must read a current when the timer pin is still high.

 

So I thought, ok I will simply disable OC7M from inside the OC7 interrupt, when the pin should already have been pulled high, and then re-enable it from the individual interrupt, to give a PWM. To my surpise, doing this causes the PWM signal to get inverted! The pin is now pulled low upon OC7 compare and high upon the individual interrupt. Despite the value of OC7D which is 1. None of this makes any sense to me. I find nothing about it in the erratas. (Specific device is HCS12C32)

 

Example to reproduce this behavior:

 

void compare7_interrupt(void) {   uint16_t tc7;    __asm BSET TFLG1, $FF                          // clear all interrupt flags   tc7 = TC7;    OC7M = 0x00;    if(duty == 0)   {     TIE &= ~0x01;   }   else   {     TC0 = tc7 + duty;               // set output compare based on duty cycle     TIE |= 0x01;   }    TC7 = tc7 + pwm_period; }  void compare0_interrupt (void) {               __asm BSET TFLG1, C0F   OC7M |= 0x01; }
Labels (1)
0 Kudos
10 Replies

1,516 Views
Lundin
Senior Contributor IV

I was about to post a working code with how this was solved, but the Javascript code formatting of this side is a complete POS. As is the whole NXP site. Still waiting for the web people to fix my account so I can post service requests.

Both Freescale and NXP have actively been trying to get rid of me as customer for years. Now that they pull their efforts together, I think they will finally manage!

0 Kudos

1,516 Views
RadekS
NXP Employee
NXP Employee

Hi Daniel,

I am sorry for these troubles.

For attaching file you have to switch to advanced editor (I hate this aproach the same way as you.):

Use advanced editor.png

after that, you could attach file.

Community attachment.png

Sometimes, the random post must be moderated for spam preventing reason (as in case of my yesterday reply).

How to create a ticket (formerly service request):

https://community.freescale.com/docs/DOC-329745


I hope it helps you.

Have a great day,
RadekS

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

0 Kudos

1,516 Views
Lundin
Senior Contributor IV

I can't post support tickets, because according to NXP support, the site has screwed up my account, case number: 00072204.

Anyway this particular matter is solved, no thanks to NXP support nor the lacking manual. You have to ignore the manual and exploit the fact that the ECT takes precedence over GPIO, then you can get OC7 to work as it's supposed to do.

0 Kudos

1,516 Views
kef2
Senior Contributor IV

There's a bug in your code. Following line clears ALL TFLG1 flags!

__asm BSET TFLG1, C0F 

Please search the forums to find why this BSET is wrong with flags. It was mentioned zilion times. I'm not familiar with C0F define, but provided it equals 1, you need to BCLR with inverted one ~1 = 0xFE:

   __asm BCLR TFLG1,#~C0F

This is the same like correct C line

TFLG1 &= C0F;  // please note no ~ to the left from C0F, like one would expect clearing bits in regular variables.

0 Kudos

1,516 Views
Lundin
Senior Contributor IV

Umm... no? This register is cleared by writing a 1. This is the reason why I use inline assembler and BSET, to ensure that the write is a single atomic one. If I had written C code like TFLG1 |= C0F then I would risk a read-modify-write. And that would reset all flags.

0 Kudos

1,516 Views
RadekS
NXP Employee
NXP Employee

Hi Daniel,

As Edward correctly mentioned, instruction BSET is read/modify/write instruction.

According to S12X CPU RM: BSET operation is (M) | (Mask) M

If you do not want use assembler, you could simply use command:

TFLG1 = 0x01; //clear C0F flag

or

TFLG1 = TFLG1_C0F_MASK; //clear C0F flag

These commands will simply write into the register.

Note: typical wrong implementation is TFLG1_C0F = 1;


I hope it helps you.

Have a great day,
RadekS

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

0 Kudos

1,516 Views
kef2
Senior Contributor IV

Daniel,

are there really you or someone has taken your account?

For example TFLG1 reads as 3 (channels 1 and 0 are pending). You do BSET TFLG1,#1 to write one to and clear only channel 0. What happens? CPU reads 3, ORs it with 1, and writes 3 back to TFLG1.... Of course both channels 1 and 0 will be cleared. In case TFLG1 was 0xFF before BSET, all TFLG1 flags will be cleared, no matter what bitmask you are going to use with BSET. It is simple like a pencil. You need BCLR TFLG1, #0xFE to clear just channel 0. If it is still not clear, perhaps try getting HC11 reference manual PDF, where this is explained with S12 compatible assembly examples.

Edward

0 Kudos

1,516 Views
Lundin
Senior Contributor IV

Ah yeah I see what you mean now, yes it is a bug. Anyway, this isn't particularly related to the question - where can I find documentation over how the TC7 feature not only pulls the timer channel to the level requested, but also inverts it when the individual timers run out.

0 Kudos

1,516 Views
RadekS
NXP Employee
NXP Employee

Hi Daniel,

I am slightly confused. You complain about “the PWM signal to get inverted!”, but the registers TCTL1 with TCTL2 are both set to 0x00 = timers disconnected from output logic.

Additionally I do not see any pin output actions in your timer interrupt routine.

Could you please explain how you generate output PWM signal?

After that we could focus on root cause for inverted signal.

If PWM is generated by timer, this should not happen.

If PWM is generated by inversion of output pin value (like PORTA_BIT0 = ~PORTA_BIT0;), PWM output inversion may be caused by miss of one of interrupts (caused by clearing of all pending timer flags).


I hope it helps you.

Have a great day,
RadekS

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

0 Kudos

1,516 Views
Lundin
Senior Contributor IV

Particularly, why does this happen:

- If I have set both TC0 and TC7 to "don't do a thing with my outputs", then nothing happens as expected.

- In the TC7 interrupt, I then set up TC0 to my PWM duty cycle and TC7 to my PWM period. Still without enabling any outputs or the TC7 feature.

- Then from the TC0 interrupt, if I write to OC7M and OC7D from the TC0 interrupt (after ensuring no TC7 flag is set), the TC0 output pin immediately goes high, even though it is not time for TC7 to trigger yet.

Why does OC7M mess around with the output pin even though TC7 has not yet elapsed? Why does it mess around with the output pin when TC0 triggers? Where is this documented?

0 Kudos