after using system_clock_override.cpp from this thread How to set up the Portenta with 480MHz Clock (at least more than 4MHz)
my SDMMC bus got a little faster 6.67Mhz to 8Mhz but i would like to go the the full 25Mhz. how do i do this?
after using system_clock_override.cpp from this thread How to set up the Portenta with 480MHz Clock (at least more than 4MHz)
my SDMMC bus got a little faster 6.67Mhz to 8Mhz but i would like to go the the full 25Mhz. how do i do this?
Hi, could you share:
This would help me provide more specific guidance for reaching the full 25MHz speed.
#include "SDMMCBlockDevice.h" for the block device lib and the system_clock_override.cpp file is already linked in the the thread i mentioned above.
Thanks!
Here's what you can do:
Try creating a custom version of the SDMMC initialization by adding this code to your project:
#include "SDMMCBlockDevice.h"
#include "mbed.h"
// Custom initialization for SDMMC with higher clock speed
bool initHighSpeedSDMMC() {
// Get handle to the SDMMC peripheral
SDMMC_TypeDef *SDMMCx = SDMMC1;
// Save current CR register value
uint32_t cr_reg = SDMMCx->CLKCR;
// Clear clock divider bits
cr_reg &= ~SDMMC_CLKCR_CLKDIV;
// Set clock divider for 25MHz (assuming 200MHz SDMMC kernel clock)
// Divider = (kernel_clock / target_freq) - 2
// For 25MHz: (200MHz / 25MHz) - 2 = 6
cr_reg |= (6 << SDMMC_CLKCR_CLKDIV_Pos);
// Enable high-speed mode
cr_reg |= SDMMC_CLKCR_NEGEDGE;
// Write back the modified register
SDMMCx->CLKCR = cr_reg;
return true;
}
SDMMCBlockDevice bd;
void setup() {
Serial.begin(9600);
while (!Serial) delay(10);
Serial.println("Initializing SD card...");
int err = bd.init();
if (err) {
Serial.print("SD card initialization failed: ");
Serial.println(err);
return;
}
// Apply high-speed configuration
if (initHighSpeedSDMMC()) {
Serial.println("High-speed SDMMC configuration applied");
} else {
Serial.println("Failed to apply high-speed SDMMC configuration");
}
// Your code continues...
}
void printSDMMCFreq() {
SDMMC_TypeDef *SDMMCx = SDMMC1;
uint32_t divider = ((SDMMCx->CLKCR & SDMMC_CLKCR_CLKDIV) >> SDMMC_CLKCR_CLKDIV_Pos) + 2;
uint32_t kernel_clock = 200000000; // Estimate based on system clock
float sdmmc_freq = (float)kernel_clock / divider / 1000000;
Serial.print("SDMMC frequency: ");
Serial.print(sdmmc_freq);
Serial.println(" MHz");
}
Thanks you, i will give this a try. i am struggling with another issue at this time. i need to have a 32bit timer with and ETR pin available to produce a high accuracy microsecond counter based on a CSAC's 10Mhz output. i had this working but now after integrating all my M7 testing with some of my M4 testing i am now seeing conflicts with i think the builtin us_ticker code and possibly the sdmmcblockdevice implementation. my sd card writing is being handled by the the M4 core and my ADC input and microsecond timing was being handled by the M7 with TIM2 but there was an issue with that when the sdcard was initialized and so i tried to change to TIM5 and am running into conflicts there as well. below is my timer code for both TIM2 and TIM5
volatile uint32_t secondsCounter = 0;
volatile uint8_t topSec = 0;
void setupTIM2() {
RCC->APB1LENR |= RCC_APB1LENR_TIM2EN; // Enable TIM2 clock
TIM2->CR1 = 0; // Reset control register
TIM2->PSC = 4; // Prescaler set to 5 (PSC + 1)
TIM2->ARR = 0xFFFFFFFF; // Max auto-reload value (free-running)
TIM2->CNT = 0; // Reset counter
TIM2->DIER |= TIM_DIER_UIE; // Enable update interrupt
// Configure PA0 as Alternate Function (AF1 for TIM2_ETR)
GPIOA->MODER &= ~(0b11 << (0 * 2)); // Clear mode bits
GPIOA->MODER |= (0b10 << (0 * 2)); // Set to Alternate Function mode
GPIOA->AFR[0] &= ~(0xF << (0 * 4)); // Clear AF selection
GPIOA->AFR[0] |= (0x1 << (0 * 4)); // Set AF1 (TIM2_ETR)
// Configure TIM2 to use external clock mode 1 on ETR (PA0)
TIM2->SMCR = (7 << TIM_SMCR_SMS_Pos) | // External Clock Mode 1
(7 << TIM_SMCR_TS_Pos) | // Trigger source = ETR (PA0)
(1 << 12); // Set ETR prescaler to divide by 2
// Ensure ETR is active high
TIM2->SMCR &= ~(1 << 15); // Clear ETP bit to select active high
// Disable digital filtering on ETR
//TIM2->SMCR &= ~(0b1111 << 8); // Clear ETR filter bits
TIM2->ARR = 999999; // Set rollover to 1,000,000 counts
TIM2->CR1 |= TIM_CR1_ARPE;
//TIM2->EGR = TIM_EGR_UG; // Manually trigger an update event
TIM2->CR1 |= TIM_CR1_CEN; // Enable the counter
NVIC_EnableIRQ(TIM2_IRQn); // Enable TIM2 interrupt in NVIC
}
extern "C" void TIM2_IRQHandler() {
if (TIM2->SR & TIM_SR_UIF) { // Check if update interrupt flag is set
TIM2->SR &= ~TIM_SR_UIF; // Clear the interrupt flag
secondsCounter++; // Increment seconds counter
topSec = 1;
}
}
void setupTIM5() {
RCC->APB1LENR |= RCC_APB1LENR_TIM5EN; // Enable TIM5 clock
TIM5->CR1 = 0; // Reset control register
TIM5->PSC = 4; // Prescaler set to 5 (PSC + 1)
TIM5->ARR = 0xFFFFFFFF; // Max auto-reload value (free-running)
TIM5->CNT = 0; // Reset counter
TIM5->DIER |= TIM_DIER_UIE; // Enable update interrupt
// Configure PA4 as Alternate Function (AF2 for TIM5_ETR)
GPIOA->MODER &= ~(0b11 << (4 * 2)); // Clear mode bits for PA4
GPIOA->MODER |= (0b10 << (4 * 2)); // Set to Alternate Function mode
GPIOA->AFR[0] &= ~(0xF << (4 * 4)); // Clear AF selection for PA4
GPIOA->AFR[0] |= (0x2 << (4 * 4)); // Set AF2 (TIM5_ETR)
// Configure TIM5 to use external clock mode 1 on ETR (PA4)
TIM5->SMCR = (7 << TIM_SMCR_SMS_Pos) | // External Clock Mode 1
(7 << TIM_SMCR_TS_Pos) | // Trigger source = ETR (PA4)
(1 << 12); // Set ETR prescaler to divide by 2
// Ensure ETR is active high
TIM5->SMCR &= ~(1 << 15); // Clear ETP bit to select active high
TIM5->ARR = 999999; // Set rollover to 1,000,000 counts
TIM5->CR1 |= TIM_CR1_ARPE;
//TIM5->EGR = TIM_EGR_UG; // Manually trigger an update event
TIM5->CR1 |= TIM_CR1_CEN; // Enable the counter
NVIC_EnableIRQ(TIM5_IRQn); // Enable TIM5 interrupt in NVIC
}
extern "C" void TIM5_IRQHandler(void) {
if (TIM5->SR & TIM_SR_UIF) {
TIM5->SR &= ~TIM_SR_UIF;
secondsCounter++;
topSec = 1;
}
}
I've created a program that has this changes:
getMicroseconds() function that handles potential rollover situations correctly when reading the timer.#include "mbed.h"
#include "SDMMCBlockDevice.h"
// Global variables for timing
volatile uint32_t secondsCounter = 0;
volatile uint8_t topSec = 0;
// Timer selection - TIM3 is less likely to conflict with us_ticker and SDMMC
// TIM3 has ETR on PD2, which is an alternative pin you can use
void setupTimerForCSAC() {
// Use TIM3 which is less likely to conflict with built-in functions
RCC->APB1LENR |= RCC_APB1LENR_TIM3EN; // Enable TIM3 clock
TIM3->CR1 = 0; // Reset control register
// Configure PD2 as Alternate Function (AF2 for TIM3_ETR)
RCC->AHB4ENR |= RCC_AHB4ENR_GPIODEN; // Enable GPIOD clock
GPIOD->MODER &= ~(0b11 << (2 * 2)); // Clear mode bits for PD2
GPIOD->MODER |= (0b10 << (2 * 2)); // Set to Alternate Function mode
GPIOD->AFR[0] &= ~(0xF << (2 * 4)); // Clear AF selection for PD2
GPIOD->AFR[0] |= (0x2 << (2 * 4)); // Set AF2 (TIM3_ETR)
// Configure TIM3 to use external clock mode 1 on ETR (PD2)
TIM3->SMCR = (7 << TIM_SMCR_SMS_Pos) | // External Clock Mode 1
(7 << TIM_SMCR_TS_Pos) | // Trigger source = ETR
(1 << 12); // Set ETR prescaler to divide by 2
// Ensure ETR is active high
TIM3->SMCR &= ~(1 << 15); // Clear ETP bit to select active high
// Set rollover to 1,000,000 counts for 1 second timing with 10MHz input รท 2
TIM3->ARR = 999999;
TIM3->CR1 |= TIM_CR1_ARPE;
// Set interrupt priority higher than default but below critical system interrupts
// This helps avoid conflicts with SDMMC operations
NVIC_SetPriority(TIM3_IRQn, 2);
// Enable counter and interrupt
TIM3->DIER |= TIM_DIER_UIE;
TIM3->CR1 |= TIM_CR1_CEN;
NVIC_EnableIRQ(TIM3_IRQn);
}
// TIM3 interrupt handler
extern "C" void TIM3_IRQHandler(void) {
if (TIM3->SR & TIM_SR_UIF) {
TIM3->SR &= ~TIM_SR_UIF; // Clear the interrupt flag
secondsCounter++; // Increment seconds counter
topSec = 1;
}
}
// Function to get microsecond time (combining seconds and microseconds)
uint64_t getMicroseconds() {
uint32_t seconds = secondsCounter;
uint32_t micropart = TIM3->CNT; // Current count represents microseconds
// Handle potential rollover during read
if (topSec && TIM3->CNT < 500000) {
seconds = secondsCounter; // Re-read after potential interrupt
topSec = 0;
}
return ((uint64_t)seconds * 1000000) + micropart;
}
// Enhanced SDMMC configuration for higher speed
void configureHighSpeedSDMMC() {
// Get handle to the SDMMC peripheral
SDMMC_TypeDef *SDMMCx = SDMMC1;
// Save current CR register value
uint32_t cr_reg = SDMMCx->CLKCR;
// Clear clock divider bits
cr_reg &= ~SDMMC_CLKCR_CLKDIV;
// Set clock divider for 25MHz (assuming 200MHz SDMMC kernel clock)
// Divider = (kernel_clock / target_freq) - 2
// For 25MHz: (200MHz / 25MHz) - 2 = 6
cr_reg |= (6 << SDMMC_CLKCR_CLKDIV_Pos);
// Enable high-speed mode
cr_reg |= SDMMC_CLKCR_NEGEDGE;
// Write back the modified register
SDMMCx->CLKCR = cr_reg;
}
// Example of how to initialize everything in your application
void setupTimingAndStorage() {
// Initialize SD card first
SDMMCBlockDevice sd;
int err = sd.init();
if (err) {
// Handle SD initialization error
return;
}
// Apply high-speed configuration to SDMMC
configureHighSpeedSDMMC();
// Set up timer for CSAC after SD initialization
// This ordering helps avoid conflicts
setupTimerForCSAC();
// Now both systems should work without conflicts
}
wow, thank you very much. however, isn't TIM3 a 16 bit timer? will it be able to count to 1,000,000?
My mistake there! I was thinking of a different STM32 chip, only tim2 an tim5 are 32 bit timers.
I hope the provided code can help you to develop the script report anyway
no worries and thanks for trying. i think the answer to my problem is going to be figuring out how to take back or disable what arduino or mbed is trying to use these timers for. like the us_ticker. i just don't know how far the rabbit hole goes. how many other bits of code and libraries may depend on the us_ticker functionality???