Author Topic: SAMD10: What's the best way to set up a timer to trigger every 330ns (3MHz) ??  (Read 5751 times)

0 Members and 1 Guest are viewing this topic.

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1543
  • Country: lt
So far i've been unsuccesful trying to get PWM to work the way i want it to.
The problem is my lack of coding skill  :(
Now my code consists of a mix of ASF4 and bare metal.
I've also gained the power to summon hard faults on demand with code that checks out with 0 errors or warnings.  >:D
ASF4 isn't horribly bad it's just that there's soooo many layers to this onion.
Anyways i'll keep trying to get something, the goal is to set TC2 PWM output on PA10 with 1µs period.
Edit: through DMA of course.
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1543
  • Country: lt
After digging through i found the way to write to TC2 CC registers:  TC2->COUNT32.CC[0].reg. It may not the the correct way but it's something.
I got it from this line here in hri_tc_d10.h:
Code: [Select]
static inline void hri_tccount32_write_CC_reg(const void *const hw, uint8_t index, hri_tccount32_cc_reg_t data)
{
TC_CRITICAL_SECTION_ENTER();
((Tc *)hw)->COUNT32.CC[index].reg = data;
TC_CRITICAL_SECTION_LEAVE();
}
Writing to CC[0] changes the period, but weirdly enough, writing to CC[1] does nothing, when it's supposed to change the PWM ( at least according to ASF4).

Also i just noticed that this is not my first time setting PWM like this. I've previously done something similar with STM32:
Code: [Select]
#define Set_PWM htim22.Instance->CCMR1
Edit: i'm still working on the ASF4 generated template because i can't set up TC2 on bare metal yet. Could it be that my initial settings in Start.Atmel are incorrect?
« Last Edit: December 02, 2020, 04:28:36 pm by Refrigerator »
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1543
  • Country: lt
Also TC2->COUNT32.CCB[0].reg does not work and gives an error.
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11281
  • Country: us
    • Personal site
Also TC2->COUNT32.CCB[0].reg does not work and gives an error.
TCs don't have buffer registers, only TCCs do. And only TTCs can be used for this application.

How CC[ x] and PER registers affect the output depends on the mode the timer is configured for.

You need to read the datasheet to understand how all that works, you won't get far by poking random values into random registers.
Alex
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11281
  • Country: us
    • Personal site
I've also gained the power to summon hard faults on demand with code that checks out with 0 errors or warnings.  >:D
HFs are run-time errors, they can't be detected at compile-time, so compiler warnings are irrelevant here.
Alex
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1543
  • Country: lt
I've looked through the datasheet but i'm still struggling with TCC0, i guess i'm missing some details still.
As far as i can understand this piece of code right here:
Code: [Select]
void TCC_setup(void){
//HAL_GPIO_PWM_pmuxen(PORT_PMUX_PMUXE_F_Val);
// function F = WO[x]
// LED_data out on PA10
// PA10 = WO[2]

PM->APBCMASK.reg |= PM_APBCMASK_TCC0;

GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(TCC0_GCLK_ID) | GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0);

TCC0->CTRLA.reg = TCC_CTRLA_PRESCALER_DIV1 | TCC_CTRLA_PRESCSYNC_PRESC;

TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM;

TCC0->PER.reg = 7;      // at 8mhz makes period 1mhz
TCC0->CC[0].reg = 2;   // will generate ~375 µs pulse

TCC0->CTRLA.reg |= TCC_CTRLA_ENABLE;

}
Should work as long as i enable my PA10 as an output in F mode.
But i'm currently struggling with pin enabling, seems every library has it's own way of setting the pinmux etc.
So i'm still looking through the datasheet to see how to do it in bare metal.

Also i can't even get TCC0 PWM to work with Start.Atmel just to test it.  :-// TC1 and TC2 work fine.
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11281
  • Country: us
    • Personal site
WO[2] corresponds to CC[2]. So you need to set it instead of CC[2] as the code does.

HAL_GPIO_PWM_pmuxen() is the function that does it in bare-metal. It comes from here https://github.com/ataradov/mcu-starter-projects/blob/master/samd10/hal_gpio.h
Alex
 
The following users thanked this post: Refrigerator

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1543
  • Country: lt
WO[2] corresponds to CC[2]. So you need to set it instead of CC[2] as the code does.

I completely missed that one, now it's all working  :-+  :-+
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1543
  • Country: lt
I thought i was in the clear to go and set up DMA but looks like that's not the case.
Writing to TCC0->CC[2].reg works fine but writing to TCC0->CCB[2].reg does nothing, same with TCC0->CCB[0].reg.  :-\
At least i can finally make my LED's glow white.

Edit: watch window shows that the value i write does appear in TCC0->CCB[2].reg but pulse width does not change  :-//
« Last Edit: December 02, 2020, 11:49:56 pm by Refrigerator »
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11281
  • Country: us
    • Personal site
You need to enable buffered mode. Again, read the datasheet.
Alex
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1543
  • Country: lt
Try as i might i can not get CCB[2] to transfer over to CC[2].
I've looked through the datasheet and the registers:
Quote
29.6.2.6 Double Buffering
The Pattern (PATT), Waveform (WAVE), Period (PER) and the Compare Channels (CCx) registers are all double
buffered. Each buffer register has a Buffer Valid (PATTBV, WAVEBV, PERBV or CCBVx) bit in the STATUS register,
which indicates that the buffer register contains a value that can be copied into the corresponding register. When
double buffering is enabled by writing a one to the Lock Update bit in the Control B Clear register (CTRLBCLR.LUPD)
and the PER and CCx are used for a compare operation, the Buffer Valid bit is set when data has been written to a
buffer register and cleared on an update condition.
Quote
Bit 1 – LUPD: Lock Update
This bit controls the update operation of the TCC buffered registers. When this bit is set, no update of the buffered registers is performed, even though an UPDATE condition has occurred. Locking the update ensures that all buffers registers are valid before an update is performed. This bit has no effect when input capture operation is enabled.
1: The CCBx, PERB, PGVB, PGEB, and SWAPBx buffer registers value are not copied into the corresponding CCx, PER, PGV, PGE and SWAPx registers.
0: The CCBx, PERB, PGVB, PGEB, and SWAPBx buffer registers value are copied into the corresponding CCx, PER, PGV, PGE and SWAPx registers on counter update condition.
My CTRLBSET.LUPD is all 0 and i can set LUPD to 1 with TCC0->CTRLBSET.reg |= 0b10; and then reset it again with TCC0->CTRLBCLR.reg |= 0b10;, i can see the changes in watch window.
My LUPD is 0 so double buffering is enabled but the value from CCB[2] still does not transfer to CC[2].  |O
Am i missing something? TCC0 works if i set CC[2] manually.   :-//
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1543
  • Country: lt
Writing to:
Code: [Select]
TCC0->PERB.reg = 8; // set periodSets STATUS register to 129, which is 10000001, meaning STATUS.PERBV register is 1.
But writing to:
Code: [Select]
TCC0->CCB[2].reg = 6; // set counter compareDoes not change the  STATUS.CCBV2 register to 1, meaning no transfer. But why?
After the TCC0 is enabled STATUS register returns to 0, meaning that transfer from PERBV to PERB was sucessful, yet CCB is completely ignored.
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1543
  • Country: lt
Writing to:
Code: [Select]
TCC0->CCB[2].reg = 2;Before TCC is enabled or after TCC is enabled makes no difference to STATUS.CCBV
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1543
  • Country: lt
STATUS.CCBV[2] is R/W but when writing to it like this:
Code: [Select]
TCC0->STATUS.reg |= (uint32_t)0b1 << 18; // CCVB[2]Nothing happens  :-//

Edit: Writing 1 to STATUS.CCBV[2] clears it  :palm:
Quote
The bit is
cleared by writing a one to the corresponding location or automatically cleared on an UPDATE condition.
« Last Edit: December 03, 2020, 06:28:28 pm by Refrigerator »
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11281
  • Country: us
    • Personal site
Looking at CCBV bits while TCC is running is not very useful, since the bit is cleared on each cycle. You need to be looking at the actual TCC output or the corresponding CC values. The value may have been transferred by the time you are looking at it.

But PERB would also have been transferred to PER, so this is strange.

I don't have time to experiment right now, I'll try it later.

Also, there are SET and CLR set of registers specifically so that you would not need to to read-modify-write operation, so you need to use "TCC0->CTRLBSET.reg = 0b10;", not "TCC0->CTRLBSET.reg |= 0b10;"

Ah, I see, you are trying to write them while TCC is disabled, I have no idea how it will behave in this case. Butffer registers must be written when TCC is enabled and running.
« Last Edit: December 03, 2020, 06:35:58 pm by ataradov »
Alex
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1543
  • Country: lt
Looking at CCBV bits while TCC is running is not very useful, since the bit is cleared on each cycle. You need to be looking at the actual TCC output or the corresponding CC values. The value may have been transferred by the time you are looking at it.

But PERB would also have been transferred to PER, so this is strange.

I don't have time to experiment right now, I'll try it later.
The values do not transfer, also i tried both before and after enabling TCC0 and nothing happens.

I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11281
  • Country: us
    • Personal site
Does the actual output of the timer change? On the pin?

Reading back registers may not work due to synchronization or something like that.
Alex
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1543
  • Country: lt
Ah, I see, you are trying to write them while TCC is disabled, I have no idea how it will behave in this case. Butffer registers must be written when TCC is enabled and running.

Writing to TCC0->PERB (and PER) worked both when TCC was disabled and enabled, same with TCC->CC[2], which updated both when TCC0 was disabled and enabled.
But writing to TCC0->CCB[2] only updates the CCB[2] register, without ever enabling STATUS.CCBV[2] or transfering the value over to CC[2] no matter whether the TCC0 is enabled or not.
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1543
  • Country: lt
Does the actual output of the timer change? On the pin?

Reading back registers may not work due to synchronization or something like that.
Only writing directly to CC[2] works.
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11281
  • Country: us
    • Personal site
CCB can't be transferred to CC when the timer is not running. There can't be an UPDATE even if the timer is not running. Obviously writing the registers directly works regardless.

Also, what is your period at this time? If the period is not set, then there won't be an UPDATE event. For the NPWM mode UPDATE happens when the COUNT==PER. And if both COUNT and PER == 0, then there won't be an UPDATE event.
Alex
 

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1543
  • Country: lt
There has to be a catch somwhere.
According to:
Quote
Bits 19:16 – CCBVx [x=3..0]: Compare Channel x Buffer Valid
For a compare channel, the bit is set when a new value is written to the corresponding CCBx register. The bit is
cleared by writing a one to the corresponding location or automatically cleared on an UPDATE condition.
For a capture channel, the bit is set when a valid capture value is stored in the CCBx register. The bit is automatically cleared when the CCx register is read.
Clearly it does not.  :bullshit: :bullshit: :bullshit:
Double buffering is enabled as evidenced by TCC0->PERB.reg = 8; working and setting STATUS.PERBV register to 1. This is further confirmed by CTRLBCLR.LUPD register being 0.

The only clue i could find so far is in this sentence here:
Quote
When
double buffering is enabled by writing a one to the Lock Update bit in the Control B Clear register (CTRLBCLR.LUPD)
and the PER and CCx are used for a compare operation, the Buffer Valid bit is set when data has been written to a
buffer register and cleared on an update condition.
Also the way this sentence is worded is hard to read for me so i'll try to break it down:
:IF writing a one to the Lock Update bit in the Control B Clear register (CTRLBCLR.LUPD)
and the PER and CCx are used for a compare operation
:THEN the Buffer Valid bit is set when data has been written to a
buffer register.

But CC stands for capture compare, how would PWM work if PER and CCx aren't used for compare op?

CCB can't be transferred to CC when the timer is not running. There can't be an UPDATE even if the timer is not running. Obviously writing the registers directly works regardless.

Also, what is your period at this time? If the period is not set, then there won't be an UPDATE event. For the NPWM mode UPDATE happens when the COUNT==PER. And if both COUNT and PER == 0, then there won't be an UPDATE event.

PER is set to 8 and at 8MHz that gives me a pretty good waveform.
Also writing to CCB[2] even when running does nothing and i'll get a screenshot of the watch window that will show just how BS this whole situation is.  :-\
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11281
  • Country: us
    • Personal site
Ok, I don't know what you are doing wrong. Here is the code that was tested on D10 Xmini:

Code: [Select]
//-----------------------------------------------------------------------------
void tcc_init(void)
{
  HAL_GPIO_PWM_pmuxen(PORT_PMUX_PMUXE_F_Val);

  PM->APBCMASK.reg |= PM_APBCMASK_TCC0;

  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(TCC0_GCLK_ID) | GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0);

  TCC0->CTRLA.reg = TCC_CTRLA_PRESCALER_DIV1 | TCC_CTRLA_PRESCSYNC_PRESC;

  TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM;

  TCC0->PER.reg = 100;
  TCC0->CC[2].reg = 50;

  TCC0->CTRLA.reg |= TCC_CTRLA_ENABLE;
}

//-----------------------------------------------------------------------------
void tcc_test(void)
{
//  TCC0->CTRLBSET.reg = TCC_CTRLBSET_LUPD;

  TCC0->PERB.reg = 50;
  TCC0->CCB[2].reg = 10;

//  TCC0->CTRLBCLR.reg = TCC_CTRLBCLR_LUPD;
}

Initially frequency is higher and duty cycle is 50%. After I call tcc_test() from a button press, Frequency doubles and duty cycle changes to 20%.

The idea behind LUPD is to let you atomically update multiple values and have them applied at the same time. In the code LUPD access is commented out because you don't need it for things to work, but you need it if it was actual production code.

If you only update one register (as would be the case in your real application), then you don't need to worry about LUPD.

The code was tested observing the actual output. I don't know what happens to the registers, I don't have time to run the debugger.

Watching register is not a good indication of what actually happens, it may be IDE caching the values or something like this.  Observe the actual PWM output on the pin.
« Last Edit: December 03, 2020, 07:33:35 pm by ataradov »
Alex
 
The following users thanked this post: Refrigerator

Offline RefrigeratorTopic starter

  • Super Contributor
  • ***
  • Posts: 1543
  • Country: lt
I took screencaps of the code and the watch window step by step. if some of them appear to be at the same spot that's because i pressed break all after running the code to capture the watch window.
And it shows that PERB works and transfers to PER.
But not CCB to CC.   :rant:
step6debug1_the_bullshit.JPG sums it up basically.
CCB[2] is 2
CC[2] is 6
CCBV[2] is 0

CCB[2] was set to 2 in the EXTIRQ that's tied to the button while the MCU was running and i pressed the button twice for good measure. Yet CCBV[2] remains 0 even though CCB[2] and CC[2] do not match.

This MCU continues to be the bane of my existence.
« Last Edit: December 03, 2020, 07:38:15 pm by Refrigerator »
I have a blog at http://brimmingideas.blogspot.com/ . Now less empty than ever before !
An expert of making MOSFETs explode.
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11281
  • Country: us
    • Personal site
Ok, I have no idea what you are doing, Once more time. It is not correct to write buffer values while timer is disabled. I have no idea what would happen if you do. One of them may transfer and the other may not. It is literally not predictable.

Also, debugging this step by step also makes no sense. All of this is very timing sensitive.
Alex
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11281
  • Country: us
    • Personal site
Here is a complete  project I'm running. Observe PA10 before and after you press the button.
Alex
 
The following users thanked this post: Refrigerator


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf