Update: I deleted the original code and replaced with code that generates a PWM on pin 3. I'll need to scope it to see if it is 1MHZ. But progress, prior attempts resulted in no transitions on pin three. Now the monitor sees a output signal changing state.
I'm trying to configure PWM on Giga R1 Pin 3 at 1 MHz with a 50% duty cycle. I am not looking to use the HAL Libraries. I was hoping to find a library to do so. I've been looking for the past day or so without any success. Now I'm hoping someone very familiar with the included code can take a look and identify where this code goes wrong.
Keep in mind I'm a retired hardware engineer that is accustom to designing Verilog HDL and not a software engineer. Be kind.
I coded a pin monitor and tested it with a slow 2KHz PWM, analogWrite(PIN_3, 128); . The loop runs fast enough to easily detect a 1MHz PWM.
I used ChatGPT and a companion AI to code both the Monitor and below code. In doing so I realized the AI/s are not good at identify board resources unless specifically told exactly the resource to use. So it got it wrong, once realized I told it the correct PA pin and D pin. Then it got it right. For the Monitor it made a simple mistake. After telling it the error it fixed it correctly. This is Fn amazing.
#include <Arduino.h>
#define CLK_PIN PA2 // Define the clock output pin as PA2 (D3)
void setup() {
Serial.begin(115200);
while (!Serial) {
; // Wait for serial port to be ready
}
// Enable the clock for Timer 2
RCC->APB1LENR |= RCC_APB1LENR_TIM2EN;
// Configure PA2 as alternate function for TIM2_CH3
GPIOA->MODER &= ~(GPIO_MODER_MODE2); // Clear mode for PA2
GPIOA->MODER |= (GPIO_MODER_MODE2_1); // Set PA2 to alternate function mode (10)
GPIOA->AFR[0] &= ~(0xF << ((2) * 4)); // Clear alternate function for PA2
GPIOA->AFR[0] |= (1 << ((2) * 4)); // Set alternate function 1 (TIM2_CH3) for PA2
// Reset Timer 2 configuration
TIM2->CR1 = 0;
TIM2->CR2 = 0;
TIM2->SMCR = 0;
TIM2->DIER = 0;
TIM2->SR = 0;
TIM2->EGR = 0;
TIM2->CCMR1 = 0;
TIM2->CCMR2 = 0;
TIM2->CCER = 0;
TIM2->CNT = 0;
// Calculate the prescaler and the auto-reload register (ARR) values
uint32_t timer_clock = 480000000; // Timer clock frequency is 480 MHz
uint32_t frequency = 1e6; // 1 MHz frequency
uint32_t prescaler = (timer_clock / frequency) / 65536;
uint32_t arr = (timer_clock / (prescaler + 1)) / frequency - 1;
// Configure Timer 2
TIM2->PSC = prescaler; // Set prescaler
TIM2->ARR = arr; // Set auto-reload register
TIM2->CCR3 = arr / 2; // Set compare value for 50% duty cycle
// Configure Timer 2 Channel 3 for PWM mode 1
TIM2->CCMR2 |= TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1; // Set PWM mode 1 on channel 3 (OC3M = 110)
TIM2->CCMR2 |= TIM_CCMR2_OC3PE; // Enable preload register on CCR3
TIM2->CCER |= TIM_CCER_CC3E; // Enable the output for channel 3
TIM2->CR1 |= TIM_CR1_CEN; // Enable Timer 2
Serial.println("Clock configured to 1 MHz on pin 3.");
}
void loop() {
// Nothing in the main loop
}
The above code is almost correct. The clock was 8X the expected value. I verified the signal integrity. Reasonable up to about 12MHz. With the scope probe on 10X, D57 Jumper-ed to D2 input. D2 set as an input. The signal is adequate. More than that it gets to looking not so great. But I assume when connected to the camera module that is plugged in on J6 it may be significantly better. Looks much better with the Camera Plugged in. Frequency of 24MHz duty cycle suffers. Can fix perhaps by adjusting the clock source driving the timer. 12MHz will do nicely. Leave it for another day. 36MHz looks horrible. Again, fixable.
**
This code is correct, verified with an oscilloscope.
**
#include <Arduino.h>
void xclk_setup(float frequency ) {
// Initialize the clock pin as output with high speed
pinMode(XCLK_PIN, OUTPUT); // D57
// Configure GPIO pin for high speed
GPIOJ->OSPEEDR &= ~(3 << (9 * 2)); // Clear speed bits for PJ9
GPIOJ->OSPEEDR |= (3 << (9 * 2)); // Set PJ9 to very high speed (11)
// Set up the clock for a default frequency (e.g., 1 kHz)
XCLK_CONFIGURATION(frequency);
Serial.print("XCLK pin D57 configured to ");
Serial.print(frequency);
Serial.println("Hz");
}
// Subroutine to configure the clock with a specified frequency
void XCLK_CONFIGURATION(float frequency) {
if (frequency < 10 || frequency > 50e6) {
Serial.println("Frequency out of range. Must be between 10 Hz and 50 MHz.");
return;
}
// Enable the clock for Timer 1
RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
// Configure PJ9 as alternate function for TIM1_CH3
GPIOJ->MODER &= ~(GPIO_MODER_MODE9); // Clear mode for PJ9
GPIOJ->MODER |= (GPIO_MODER_MODE9_1); // Set PJ9 to alternate function mode (10)
GPIOJ->AFR[1] &= ~(0xF << ((9 - 8) * 4)); // Clear alternate function for PJ9
GPIOJ->AFR[1] |= (1 << ((9 - 8) * 4)); // Set alternate function 1 (TIM1_CH3) for PJ9
// Reset Timer 1 configuration
TIM1->CR1 = 0;
TIM1->CR2 = 0;
TIM1->SMCR = 0;
TIM1->DIER = 0;
TIM1->SR = 0;
TIM1->EGR = 0;
TIM1->CCMR1 = 0;
TIM1->CCMR2 = 0;
TIM1->CCER = 0;
TIM1->CNT = 0;
// Calculate the timer clock frequency based on APB2 prescaler
uint32_t apb2_prescaler = ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE1) >> RCC_D2CFGR_D2PPRE1_Pos) & 0x07;
uint32_t timer_clock;
// Compute the APB2 timer clock frequency
if (apb2_prescaler < 4) { // APB2 prescaler is 1
timer_clock = 480000000;
} else { // APB2 prescaler is greater than 1
timer_clock = 480000000 / (2 << (apb2_prescaler - 4));
}
Serial.print("APB2 prescaler: ");
Serial.println(apb2_prescaler);
Serial.print("Timer clock: ");
Serial.println(timer_clock);
uint32_t prescaler = 0;
uint32_t arr = 0;
if (frequency <= timer_clock / 65536) {
prescaler = (timer_clock / frequency) / 65536;
arr = (timer_clock / (prescaler + 1)) / frequency - 1;
} else {
arr = (timer_clock / frequency) - 1;
}
Serial.print("Prescaler: ");
Serial.println(prescaler);
Serial.print("ARR: ");
Serial.println(arr);
// Configure Timer 1
TIM1->PSC = prescaler; // Set prescaler
TIM1->ARR = arr; // Set auto-reload register
TIM1->CCR3 = arr / 2; // Set compare value for 50% duty cycle
// Configure Timer 1 Channel 3 for PWM mode 1
TIM1->CCMR2 |= TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1; // Set PWM mode 1 on channel 3 (OC3M = 110)
TIM1->CCMR2 |= TIM_CCMR2_OC3PE; // Enable preload register on CCR3
TIM1->CCER |= TIM_CCER_CC3E; // Enable the output for channel 3
TIM1->BDTR |= TIM_BDTR_MOE; // Main output enable
TIM1->CR1 |= TIM_CR1_CEN; // Enable Timer 1
Serial.print("Clock configured to ");
Serial.print(frequency);
Serial.println(" Hz on pin D57.");
}
1 Like