20 MHz clock signal generation

Hello,
In my project I need my Portenta to generate a 20 MHz clock for a stepper motor controller.
I tried using the library from khoi_prog PortentaH7_TimerInterrupt but with this library you cannot generate signals higher than 100 kHz.
I tried modifying the library but it seems like the call to the ISR is not really compatible with the frequency I want to reach, when modifying the value at which the interrupt happens, the portenta gets stuck somewhere in the code.

From the portenta complete pinout it seems like some timers channel can generate signals on some pins (TIM1_CH2 on pin D14, TIM1_CH3 on pin D13).

I then tried to setup the timers so that they would toggle the pins at every interrupts but it doesn't seem to work.
Here is my code:

#define CLK16_PIN D14 //CLK16_PIN Timer pin
void setup()
{
  Serial.begin(115200); //init serial port and set baudrate
  while(!Serial); //wait for serial port to connect (needed for Leonardo only)
  Serial.println("\nStart...");
  //set pins
  pinMode(CLK16_PIN,OUTPUT);
  pinMode(D13,OUTPUT);
  
  TIM1->SR = 0; //Clear flags of status register
  TIM1->PSC = 0; //Set prescaler to 1
  TIM1->ARR = 1000; //Interrupt every 1000 clock cycle -> 200kHz
  TIM1->DIER |= 0x000f;//Enable interrupt on channels 1,2,3
  //TIM1->EGR |= 0x06;//Generate an interrupt on channel 1 and 2
  /*
  TIM1->CCMR1 |= TIM_CCMR1_OC1M_0 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2;
  TIM1->CCR1 = TIM1->ARR / 2;
  TIM1->CCER |= TIM_CCER_CC1E;*/
  TIM1->CCMR1  |= 0x3030;//Output compare on toggle, should toggle the pin for channel 1 and 2
  TIM1->CCMR2 |= 0x0030;//toggle output channel 3
  TIM1->CCER |= 0x0111; //Enable channels 1-3
  
  TIM1->CCR1 |= 10; //Not sure what those registers do, seems like they also determine the freq of the interrupt
  TIM1->CCR2 |= 1000;
  TIM1->CCR3 |= 100;
  TIM1->CR1 |= TIM_CR1_CEN; //Enable the timer
  Serial.println("Setup end");
}

void loop()
{
  digitalWrite(LEDR,HIGH);
  delay(500);
  digitalWrite(LEDR,LOW);
  delay(500);
}

I am checking the pins with a logic analyzer, but it doesn't seem to produce any signals.

Other "strange" facts. From the PortentaH7_TimerInterrupt it seems that my portenta is running at 200MHz. The timer also seem to operate at this frequency, from the result I obtained by setting the ARR value directly and checking the signal.

Any help is appreciated.

This is not done through interrupts, since the interrupt itself already takes dozens of controller cycles for starting handler and, the saddest thing, this time is unstable.
It is much easier to do this by setting the timer for toggle output pin on compare match, similar to how it is done in PWM mode

I tried using pwm, but it doesn't seem to be possible to set the period below 1 us, which gives a max frequency of 1 MHz.

I'm trying to find the files where the functions are defined to see if it is possible to modify them to increase the frequency. I found the PwmOut.h(C:\Users[ME]\AppData\Local\Arduino15\packages\arduino\hardware\mbed_portenta\3.1.1\cores\arduino\mbed\drivers\include\drivers\PwmOut.h) but can't find where the functions are actually written.

I'm not talking about the arduino PWM, I mean the timer PWM mode in your STM32 controller. See the datasheet for the controller, section timers. Unfortunately, I did not work with Potenta, but as I see by the registers, everything is very similar to STM32F401 and F411 .
With Timer Output Compare mode you can, theoretically, toggle the pin with frequency as much as half of system clock. The real frequency is limited by the GPIO response time, but still it should be at least 50 MHz with a system clock of 200MHz

Where did you get the code that you have in the first post? There is almost everything you need, it remains only to set the correct values ​​in the registers

I wrote the code by looking at the AN4776 STM32H747/757 - Dual-core Arm M7/M4 Microcontrollers (MCU) - STMicroelectronics and then going through the complete datasheet of the stm32h747 (3600 page long :frowning: on the same website under reference manual).
But regardless of what value I put in the ARR or CCR registers I can't obtain a signal on any pin. I'm not even sure that the pinout I am using is correct.

There are a lot of details. For example, on F411 you need to start the timer, set its frequency through the PSC and ARR, set the channel compare value as CCR1, setup timer output mode with control registers, allow the output of signals to the pins in general and allow the control of a specific pin .

I think that Potenta_H7_PWM Library source code can be a good example

Here a code exemple to generate a trigger with Timer :

void AC_TIMER1_Init(void) {
  //------------------------------Clock Timer1----------------------------------------//
  SET_BIT(RCC_C2->APB2ENR, RCC_APB2ENR_TIM1EN_Msk);
  //---------------------Prescaler----------------------//
  TIM1->PSC = 19; //PSC=1
  //----------------------------------------------------------TIM2 autoreload register--------------------------------------------//
  TIM1->ARR = 47;//200kHz
  //---------selectupdatevent (trigger)-----------------//
  SET_BIT(TIM1->CR2, TIM_CR2_MMS2_1);
  //-----------IT-----------------------//
  //  SET_BIT(TIM1->DIER,TIM_DIER_UIE_Msk);
  //  NVIC_SetPriority(TIM1_UP_IRQn,0);
  //  NVIC_SetVector(TIM1_UP_IRQn, (uint32_t)&TIM1_UP_IRQHandler);
  //  NVIC_EnableIRQ(TIM1_UP_IRQn);
  SET_BIT(TIM1->CR1, TIM_CR1_CEN_Msk);//CEN=1
}

Since I managed to use it to trigger conv on ADC you should be able to do the same for DAC.

Also your code from Post 1 doesn't have any interrupt code lines??

Hello,

@b707 i've looked at the library you are talking about and it has the same problem as the other one from the same author (PortentaH7_TimerInterrupt). The functions are made to generate frequencies up to 1 MHz.
I'm trying to find where the functions used by this library are defined(PwmOut.period_us()) but I can only find the header file where they are declared but not what actually happens inside them.

I managed to obtain something working by simply changing the value inside the prescaler, the autoreload and the CCR1 registers.

/****************************************************************************************************************************
  PWM_Single.ino
  For Portenta_H7 boards
  Written by Khoi Hoang

  Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_PWM
  Licensed under MIT license
*/
#define pinD0    D0       // PH15 / TIM8_CH3N
#define pinD1    D1       // PK1  / TIM1_CH1, TIM8_CH3
#define pinD2    D2       // PJ11 / TIM1_CH2, TIM8_CH2N
#define pinD3    D3       // PG7  / HRTIM_CHE2
#define pinD4    D4       // PC7  / TIM3_CH2, TIM8_CH2, HRTIM_CHA2
#define pinD5    D5       // PC6  / TIM3_CH1, TIM8_CH1, HRTIM_CHA1, LPTIM3_OUT
#define pinD6    D6       // PA8  / HRTIM_CHB2 (TIM1_CH1, TIM8_BKIN2)

// See https://www.st.com/resource/en/datasheet/stm32h747xi.pdf, Table 7, page 53
// Can't use pins with same TIMx. For example, 
// pinD1 and pinD2, using same TIM1, can't be used at the same time
// pinD4 and pinD5, using same TIM3, can't be used at the same time
// pinD3 and pinD6 are using HRTIM, can't be used at the same time and the minimum freq must be ~770Hz 
uint32_t myPin  = pinD1;

float dutyCycle = 50.0f; //This is only set for the init

float freq      = 100000.0f; //This is only set for the init

mbed::PwmOut* pwm   = NULL;

void startPWM()
{
  digitalWrite(LEDG, LED_ON);
  digitalWrite(LEDB, LED_OFF);
  digitalWrite(LEDR, LED_OFF);

  PWM_LOGERROR7("Freq = ", freq, ", DutyCycle % = ", dutyCycle, ", DutyCycle = ", dutyCycle / 100, ", Pin = ", myPin);
  setPWM(pwm, myPin, freq, dutyCycle);
}


void setup()
{
  pinMode(LEDG, OUTPUT);
  pinMode(LEDB, OUTPUT);
  pinMode(LEDR, OUTPUT);

  digitalWrite(LEDG, LED_OFF);
  digitalWrite(LEDB, LED_OFF);
  digitalWrite(LEDR, LED_OFF);

  pinMode(myPin, OUTPUT);
  digitalWrite(myPin, LOW);

  Serial.begin(115200);
  while (!Serial);

  delay(100);

  Serial.print(F("\nStarting PWM_Single on ")); Serial.println(BOARD_NAME);
  Serial.println(PORTENTA_H7_PWM_VERSION);

  // Automatically retrieve TIM instance and channel associated to pin
  // This is used to be compatible with all STM32 series automatically.

  startPWM();
  TIM1->PSC = 0;
  TIM1->ARR = 25;
  TIM1->CCR1 = 13;
}

void loop()
{
  Serial.println(TIM1->CNT); //Print the value of the counter, shouldn't be greater than 25
  digitalWrite(LEDB,HIGH);
  delay(100);
  digitalWrite(LEDB,LOW);
  delay(100);
}

This way I am able to obtain a 8MHz output on the PWM and I need to get an oscilloscope to see if I can obtain higher frequencies (my logic analyzer seems pretty unreliable above 10 MHz).

Great. This is exactly what I meant - do not use the library itself, just see how it manages timers

I checked with my oscilloscope and it looks like I can get the pwm to run at 20MHz.

However, I'm still not sure what some functions are doing. In my current code I am using 2 functions from some internal libraries but I can't find their declaration.
Would anyone happens to know where their decalaration can be found?

#include "Arduino.h"
#include "PwmOut.h"
#include "pinDefinitions.h"
#include "PinNames.h"

void setup()
{
  Serial.begin(115200); //init serial port and set baudrate
  while(!Serial); //wait for serial port to connect (needed for Leonardo only)
  Serial.println("\nStart...");
  //set pins
  pinMode(D1,OUTPUT);

//PWM mode
  mbed::PwmOut* pwm = new mbed::PwmOut(digitalPinToPinName(D1));
  digitalPinToPwm(D1) = pwm;
  /*SET_BIT(RCC_C2->APB2ENR,RCC_APB2ENR_TIM1EN_Msk); //Enable the timer
  TIM1->SR = 0; //Clear the flags
  TIM1->CR1 |= 0x0081; //Enable the counter and set ARPE
  TIM1->EGR |= 0x01;//Only set Update Generation
  TIM1->CCMR1 |= 0b01101100; //Channel 1 as output (CC1S), Output compare fast enable, output capture preload enable, PWM mode
  TIM1->CCER |= 0x0001; //Capture compare enable*/
  TIM1->PSC = 0;
  TIM1-> ARR = 25;
  TIM1->CCR1 = 13;
  Serial.println("Setup end");
}

void loop()
{
  Serial.print("TIM1 : ");
  Serial.println(TIM1->CNT);
  delay(100);
}

What is your board clock frequency?

From what I've seen it should run at 200 MHz.

Quite far from what is advertised 480 MHz, but I've read on this forum that it is possible to change the CPU frequency.

so this code

should give you
F_CPU/ ((ARR+1)*(PSC +1)) = 200/26 = 7.69 MHz signal

For 20 MHz try to use ARR = 9 and CCR1 =5

Yes, this is what I get.
I managed to get an higher clock frequency too by reducing ARR and CCR1.

I just want to know what actually happens inside those 2 lines :

mbed::PwmOut* pwm = new mbed::PwmOut(digitalPinToPinName(D1));
digitalPinToPwm(D1) = pwm;

I can't find the definition of the functions nor can I find the definitions of all the functions declared in PwmOut.h.

What Potenta package do you use in arduino? have you a link?

The code is available here arduino/ArduinoCore-mbed (github.com)
And here is PwmOut.h
ArduinoCore-mbed/PwmOut.h at master · arduino/ArduinoCore-mbed (github.com)

I have the 3.0.0 version installed.

Thanks for the link, I'll take a look and post if I find it

From the pin number determines the timer, which can outputs PWM to this pin. Next, for this timer, the pvm mode is configured.
https://os.mbed.com/handbook/PwmOut

Sorry, I can not write in more detail, because the site os.mbed.com is closed for me

Ok I found the documentation on the mbed-os github ARMmbed/mbed-os: Arm Mbed OS is a platform operating system designed for the internet of things (github.com)
Thank you for the help.