I'm using a NRF52810 to control an LED load and need to ramp up PWM frequency over the span of 6 seconds, like 0-1s : 6Hz, 1-2s : 7Hz, 2-3s : 8Hz, 4-5s : 9Hz, 5-6s : 10Hz.
At the moment, this is how I do PWM with fixed frequency:
#include "nrf.h"
#include "nrf_gpio.h"
#include "nrf_drv_gpiote.h"
#include "nrf_drv_rtc.h"
#include "nrf_drv_clock.h"
#include "nrf_delay.h"
#include "nrfx_rtc.h"
#include "app_timer.h"
#include "low_power_pwm.h"
#include "boards.h"
#include <stdint.h>
#include <stdbool.h>
#include "nrf_mtx.h"
#include "math.h"
int ch1_period;
int ch1_ontime;
float ch1_frequency;
float ch1_dutycycle;
float ch1_offset;
int ch1_int_offset;
int ch1_period_tmr; // keeps track of flash period
int ch1_ontime_tmr; // keeps track of LED on-time
bool ch1_on_flag = false;
bool ch1_enable = false;
const nrfx_rtc_t rtc1 = NRF_DRV_RTC_INSTANCE(1); // Create a handle that will point to the RTC1 of nrf device
void gpio_init(){
nrf_gpio_cfg_input(switch_mode1, NRF_GPIO_PIN_PULLUP); // configure the mode switch input, with pin pullup
nrf_gpio_cfg_output(pwm_1); // configure the CH1 PWM output pin
nrf_gpio_pin_clear(pwm_1); // init pin to low which disables the LED driver
}
void init_group1_flash_patterns(){
ch1_enable = true;
ch1_frequency = 6; //specify in Hertz. Decimals are OK.
ch1_dutycycle = 25; // specify in "% positive duty cycle". Example: 45 = 45% On, 55% Off
}
void calc_flash_parameters(){
float z;
//determine channel flash pattern periods and flash duty cycles ("ontimes")
ch1_period = round((1/ch1_frequency)/.001); // period = frequency X .01sec
ch1_ontime = round(1/ch1_frequency/.001 * ch1_dutycycle/100);
// reset timers
ch1_ontime_tmr = 0;
ch1_period_tmr = 0;
}
// Initialize the low frequency clock which drives the real time counter
void lfclk_config(void){
nrf_drv_clock_init(); // Initialize the low frequency clock
nrf_drv_clock_lfclk_request(NULL); // Request the clock to not generate events
}
// RTC1 interrupt handler which will be used to maintain the flash period and duty cycle for the led channel
void rtc1_handler(nrfx_rtc_int_type_t int_type){
if(ch1_enable == true){ // Channel 1 handling
ch1_period_tmr = ch1_period_tmr + 1; // increment the period timer
if(ch1_period_tmr == ch1_period && nrf_gpio_pin_out_read(pwm_1) == false ){ //turn on LED at start of each period
nrf_gpio_pin_set(pwm_1); // turn on the LED
ch1_ontime_tmr = 0;
}
ch1_ontime_tmr = ch1_ontime_tmr + 1;
if(ch1_ontime_tmr == ch1_ontime && nrf_gpio_pin_out_read(pwm_1) == true){
nrf_gpio_pin_clear(pwm_1);
}
}
}
// A function to configure and intialize the RTC1 which is used for generating the interrupt
void rtc1_config(void){
nrfx_rtc_config_t rtc1_config = NRFX_RTC_DEFAULT_CONFIG; // Create a struct of type nrfx_rtc_config_t and assign it default values
rtc1_config.prescaler = 32; // tick = 32768 / (32 + 1) = 16384Hz = 61usec (all approximate).
nrfx_rtc_init(&rtc1, &rtc1_config, rtc1_handler); // Initialize the rtc and pass the configurations along with the interrupt handler
nrfx_rtc_tick_enable(&rtc1, true); // Enable a tick interrupt on each tick
nrfx_rtc_overflow_disable(&rtc1); // don't know if this is necessary
nrfx_rtc_enable(&rtc1); // start the rtc
}
int main(void){
gpio_init(); // Initialize the gpio
if(nrf_gpio_pin_read(switch_mode1) == 0){ //if mode 1 selected on the mode switch
init_group1_flash_patterns(); //initialize the flash patterns
}
max_parameter_violation_check();
lfclk_config(); // low frequency low power clock configuration
nrfx_clock_lfclk_start();
rtc1_config(); // rtc1 configuration
}
I'm stuck on how to implement this dynamic frequency - mostly on the side of triggering when to change the period, a way I think it could be implemented: to reset the the timer every 1000ms, and adjusting parameters for the next frequency. Hence, using another timer as that counter?
I can't seem to wrap my head around how to implement this.
-
\$\begingroup\$ Just have it inside your existing interrupt handler \$\endgroup\$BeB00– BeB002024年01月31日 21:18:23 +00:00Commented Jan 31, 2024 at 21:18
-
\$\begingroup\$ @BeB00 hmm, seems like a fundamental solution that i can't wrap my head around, any hints? \$\endgroup\$roaibrain– roaibrain2024年01月31日 21:47:14 +00:00Commented Jan 31, 2024 at 21:47
1 Answer 1
Something like this may get you going in the right direction. I'm sure there are more compact ways to write this, but this is one way. In your higher level functions, you would then control when to change periods.
// RTC1 interrupt handler which will be used to maintain the flash period and duty cycle for the led channel
void rtc1_handler(nrfx_rtc_int_type_t int_type){
static int period = 0; // new
if( period == 0 )
{
if(ch1_enable == true){ // Channel 1 handling
ch1_period_tmr = ch1_period_tmr + 1; // increment the period timer
if(ch1_period_tmr == ch1_period && nrf_gpio_pin_out_read(pwm_1) == false ){ //turn on LED at start of each period
nrf_gpio_pin_set(pwm_1); // turn on the LED
ch1_ontime_tmr = 0;
}
ch1_ontime_tmr = ch1_ontime_tmr + 1;
if(ch1_ontime_tmr == ch1_ontime && nrf_gpio_pin_out_read(pwm_1) == true){
nrf_gpio_pin_clear(pwm_1);
}
}
}
else if (period == 1)
{ ....}
}
-
\$\begingroup\$ Thanks for the answer, this does help give direction. I think the main difficulty was in understanding how to control a change in periods at the higher levels - by matching the tick interrupt to the period and re-configuring rtc? \$\endgroup\$roaibrain– roaibrain2024年01月31日 22:12:24 +00:00Commented Jan 31, 2024 at 22:12