Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PWM resolution increase in hardware timer #182

Open
ltwin8 opened this issue Jul 30, 2023 · 2 comments
Open

PWM resolution increase in hardware timer #182

ltwin8 opened this issue Jul 30, 2023 · 2 comments

Comments

@ltwin8
Copy link

ltwin8 commented Jul 30, 2023

could one increase PWM resolution to more than 8bit in megacorex on the 4809?
"The TCAn.PER register defines the PWM resolution. The minimum resolution is 2 bits (TCA.PER = 0x0002), and the
maximum resolution is 16 bits (TCA.PER = MAX-1)"

https://ww1.microchip.com/downloads/en/DeviceDoc/ATmega4808-4809-Data-Sheet-DS40002173A.pdf

page 191 to 194

@SpenceKonde
Copy link
Contributor

SpenceKonde commented Jul 31, 2023

Yeah - of course. This requires:

  1. Write TCA0.SPLIT.CTRLA = 0; - this stops the timer, allowing the next command to be executed.
  2. Write TCA0.SPLIT.CTRLESET = 0x0C; - this commands the timer to hard reset.
  3. Now the timer is back at the state it was in when the device was powered on (If MegaCoreX does millis the way DxCore and mTC does, this does not break millis. If it uses the implementation of millis from the stock core, millis will be completely hosed by this. You now write to it as TCA0.SINGLE.REGISTER.
  4. TCA0.SINGLE.PER = TOP-1
  5. create something like function like
void setDutyCycle(unt8_t channel, uint16_t duty)  {
  if (channel > 2) return;
  if (duty == 0 || duty == 1) {
    PORT_t* prt = portToPortStruct(PORTMUX.TCAROUTEA);
    if (duty) {
      prt->OUTSET = 1 << channel;
    } else {
      prt->OUTCLR = 1 << channel;
    }
  } else {
    
  volatile uint16_t * t=&TCA0_SINGLE_CMP0BUF; //Using the buffering is more commonly the reason one does something like this than needing more resolution. 
  *(t+channel) = duty;
  } 

(the buffering bit: If you use PER/CMP0/1/2 registers they take effect immediately and can result in glitches. If you use the ones ending in BUF, they are only updated at the start of the cycle to prevent glitches

One challenge on MegaCoreX is that there's no function like TakeOverTCA0() on DxCore which tells the core to pretend that a timer doesn't exist, so there's no way to tell it to make analogWrite() treat it like it had no PWM, and hence not try to fiddle with the timer settings, nor make digitalWrite() to the pins where the PWM was originally located try to turn off PWM channels. This is fine if you haven't changed portmux. But if you have changed portmux, you have to use direct port access or fast I/O to access it on MCX

DxC and mTC do have takeOverTCA0 (and takeOverTCA1 where appropriate) so on those cores you can skip the first 3 steps and instead call takeOverTCA0() and analogWrite() and digitalWrite() will not do anything funky no matter what you have also done to the portmux.

I don't think MCX still uses the stock millis implementation. The stock implementation gives one extra PWM channel on some parts, at the cost reduced timekeeping accuracy, increased timekeeping overhead, and makes the dreaded reverse time travel in micros() (which has consequences only slightly less severe than reverse time travel in the physical realm) much harder to avoid. (basically, on my cores, the only time that TCBs are configured automatically to use the clock from a TCA is when they're in PWM mode, and PWM mode is not used for timekeeping - we instead set a TCB clocked from CLK_PER/2 in periodic interrupt mode and the period set so it overflows once per millisecond, and I think MCX adopted this method too. I hope so, at least.

@ltwin8
Copy link
Author

ltwin8 commented Jul 31, 2023

Sadly I am not that familiar with the Atmega4809 but do I understand you correctly?

i would like to output more than 10b PWM on PC0, PC1, PC2 and PC3

is that possible?

but if I also would like to use millis() it is not possible? And there is no function in MCX to utilize this feature?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants