PWM related events AWOL


#1

I have been programming a driver set for the NRF52840 as a test to see how much I really know. I’ve created a PWM class and an LED class which both work when used apart. (See program 1, below.) The problem came when I tried to make a PWM member object in class LED. (See program 2, below.) The second program compiles without errors but does not function and freezes as soon as an input is detected.

I’m using a USB dongle so debugging is problematic but I have observed that the events relating to the PWM are not occurring. I can gain limited functionality by commenting out the while(){} loops in the PWM::start and PWM::stop functions, as each wait for PWM events and are the cause of the freeze. Without the while(){} loops the program still faults. When the button is pressed, the LED should light with a 50% duty cycle and stay there until pin 3 goes low, at which point the duty cycle should change to 100%; however, the duty cycle only remains at 50% while the button is pressed and returns to its previous state as soon as the button is released. I believe this is because the shortcut meant to loop the PWM relies on…you guessed it, another PWM event.

Are there any known issues with event handling and object/class hierarchy? Can someone explain to me why program 1 works and program 2 does not? I’ve been staring at this code for so long that it all seems to run together and I think I need a fresh set of eyes.

Program 1
main.cpp

#include "rav_nrf52840_base.h"
#include "rav_nrf52840_led.h"
#include "rav_nrf52840_pwm.h"


int main(void){  //  TX

	setupClock (HF_64MHz_XTAL, START);
	setupClock (LF_32_768kHz_XTAL, START);

	setupPin (0, 3,INPUT);
	pullPin  (0, 3,PULLUP);
	setupPin (0,18,INPUT);  //  External (? Ohm) pullup resistor.
	setupPin (0,22,OUTPUT);
	writePin (0,22,HIGH);
	setupPin (0,23,OUTPUT);
	writePin (0,23,HIGH);
	setupPin (0,24,OUTPUT);
	writePin (0,24,HIGH);

 	int my_led_ports[3] = {0,0,0};
	int my_led_pins[3] = {23,22,24};  //  RED, GREEN, BLUE
	LED led(true,my_led_ports,my_led_pins);

	bool pwm_sequence_started_flag = 0;
	int  LED_portNumber[3] = {0,0,0};
	int  LED_pinNumber[3] = {23,22,24};
	unsigned int sequence_0[4];
	PWM pwm(PWM0,true,0,0x7FFF,DIV4);
	pwm.init(INDIVIDUAL,0,0xFFFF);
	pwm.sequence(0,sequence_0,4,0,0);
	pwm.pinSelect(0,LED_portNumber[0],LED_pinNumber[0],false);
	pwm.pinSelect(1,LED_portNumber[1],LED_pinNumber[1],false);
	pwm.pinSelect(2,LED_portNumber[2],LED_pinNumber[2],false);
	pwm.enable();


	for(;;){
		if (readPin(0,3)){
			;
		}
		else{
			if (pwm_sequence_started_flag){
				pwm.stop();
				pwm_sequence_started_flag = false;
			}
			led.on(WHITE,0xFFFFFFFF);
		}
		if (readPin(0,18)){
			;
		}
		else{
			led.off();
			if (pwm_sequence_started_flag){
				pwm.stop();
				pwm_sequence_started_flag = false;
			}
			int LED_intensity = 0xF000F000;
			sequence_0[0] = LED_intensity;
			sequence_0[1] = LED_intensity;
			sequence_0[2] = LED_intensity;
			pwm_sequence_started_flag = pwm.start();
		}
	}
	return -1;
}

led.h

#ifndef rav_nrf52840_led_h
#define rav_nrf52840_led_h

#include "rav_nrf52840_macros.h"

typedef enum {RED = 1,GREEN = 2,YELLOW = 3,BLUE = 4,MAGENTA = 5,CYAN = 6,WHITE = 7} ledState;

class LED {

private:

	bool LED_activeLow_flag;

	bool LED_RGB_flag;

	int  LED_portNumber[3];

	int  LED_pinNumber[3];
	
	int  LED_color;
	
	int  LED_intensity;

public:

	LED (bool activeLow,int portNumber[3],int pinNumber[3]);  //  Use this format for RGB LEDs. Port and pin numbers must be listed in order: red, green, then blue.

	void on (ledState color, int brightness);  //  Do not use this format with single color LEDs. Valid options for brightness are 0 - 100(%)

	void off (void);
};

#endif

led.cpp

#include "rav_nrf52840_led.h"

LED::LED (bool activeLow,int portNumber[3],int pinNumber[3]) :
LED_color(RED),  //  Default value is RED.
LED_intensity(0xFFFFFFFF),  //  Default value is 0xFFFFFFFF.
LED_RGB_flag(true)
{
	LED_activeLow_flag = activeLow;
	LED_portNumber[0] = portNumber[0];
	LED_portNumber[1] = portNumber[1];
	LED_portNumber[2] = portNumber[2];
	LED_pinNumber[0] = pinNumber[0];
	LED_pinNumber[1] = pinNumber[1];
	LED_pinNumber[2] = pinNumber[2];
}


void LED::on (ledState color, int brightness){
	LED_intensity = brightness;//(scale(brightness,0,100,0x8000,0xFFFF));
	if (LED_RGB_flag){
		LED_color = color;
		if (brightness >= 0xFFFFFFFF){//100){
			LED_activeLow_flag ? writePin(LED_portNumber[0],LED_pinNumber[0],readBit(LED_color,0) ? 0:1) : writePin(LED_portNumber[0],LED_pinNumber[0],readBit(LED_color,0) ? 1:0);
			LED_activeLow_flag ? writePin(LED_portNumber[1],LED_pinNumber[1],readBit(LED_color,1) ? 0:1) : writePin(LED_portNumber[1],LED_pinNumber[1],readBit(LED_color,1) ? 1:0);
			LED_activeLow_flag ? writePin(LED_portNumber[2],LED_pinNumber[2],readBit(LED_color,2) ? 0:1) : writePin(LED_portNumber[2],LED_pinNumber[2],readBit(LED_color,2) ? 1:0);
		}
	}
	else{
		if (brightness >= 0xFFFFFFFF){//100){
			LED_activeLow_flag ? writePin(LED_portNumber[0],LED_pinNumber[0],readBit(LED_color,0) ? 0:1) : writePin(LED_portNumber[0],LED_pinNumber[0],readBit(LED_color,0) ? 1:0);
		}
	}
}

void LED::off (void){
	if (LED_RGB_flag){
		LED_activeLow_flag ? writePin(LED_portNumber[1],LED_pinNumber[1],1) : writePin(LED_portNumber[1],LED_pinNumber[1],0);
		LED_activeLow_flag ? writePin(LED_portNumber[2],LED_pinNumber[2],1) : writePin(LED_portNumber[2],LED_pinNumber[2],0);
	}
	LED_activeLow_flag ? writePin(LED_portNumber[0],LED_pinNumber[0],1) : writePin(LED_portNumber[0],LED_pinNumber[0],0);
}

Program 2
main.cpp

#include "rav_nrf52840_base.h"
#include "rav_nrf52840_led.h"


int main(void){  //  TX

	setupClock (HF_64MHz_XTAL, START);
	setupClock (LF_32_768kHz_XTAL, START);

	setupPin (0, 3,INPUT);
	pullPin  (0, 3,PULLUP);
	setupPin (0,18,INPUT);  //  External (? Ohm) pullup resistor.
	setupPin (0,22,OUTPUT);
	writePin (0,22,HIGH);
	setupPin (0,23,OUTPUT);
	writePin (0,23,HIGH);
	setupPin (0,24,OUTPUT);
	writePin (0,24,HIGH);

 	int my_led_ports[3] = {0,0,0};
	int my_led_pins[3] = {23,22,24};  //  RED, GREEN, BLUE
	LED led(true,my_led_ports,my_led_pins);


	for(;;){
		if (readPin(0,3)){
			;
		}
		else{
			led.on(WHITE,0xFFFFFFFF);
		}
		if (readPin(0,18)){
			;
		}
		else{
			led.on(WHITE,0xF000F000);
		}
	}
	return -1;
}

led.h

#ifndef rav_nrf52840_led_h
#define rav_nrf52840_led_h

#include "rav_nrf52840_pwm.h"
#include "rav_nrf52840_macros.h"

typedef enum {RED = 1,GREEN = 2,YELLOW = 3,BLUE = 4,MAGENTA = 5,CYAN = 6,WHITE = 7} ledState;

class LED {

private:

	PWM pwm;

	unsigned int sequence_0[2];

	bool pwm_sequence_started_flag;

	bool LED_activeLow_flag;

	bool LED_RGB_flag;

	int LED_portNumber[3];

	int LED_pinNumber[3];

	ledState LED_color;

	int LED_intensity;

public:

	LED (bool activeLow,int portNumber[3],int pinNumber[3]);  //  Use this format for RGB LEDs. Port and pin numbers must be listed in order: red, green, then blue.

	void on (ledState color, int brightness);  //  Do not use this format with single color LEDs. Valid options for brightness are 0 - 100(%)

	void off (void);
};

#endif

led.cpp

#include "rav_nrf52840_led.h"

LED::LED (bool activeLow,int portNumber[3],int pinNumber[3]) :
pwm(PWM0,true,0,0x7FFF,DIV4),
LED_color(RED),				//  Default value is RED.
LED_intensity(0xFFFFFFFF),	//  Default value is 0xFFFFFFFF.
LED_RGB_flag(true)
{
	LED_activeLow_flag = activeLow;
	LED_portNumber[0] = portNumber[0];
	LED_portNumber[1] = portNumber[1];
	LED_portNumber[2] = portNumber[2];
	LED_pinNumber[0] = pinNumber[0];
	LED_pinNumber[1] = pinNumber[1];
	LED_pinNumber[2] = pinNumber[2];
	pwm.init(INDIVIDUAL,0,0xFFFF);
	pwm.sequence(0,sequence_0,4,0,0);
	pwm.pinSelect(0,LED_portNumber[0],LED_pinNumber[0],false);  //  Red
	pwm.pinSelect(1,LED_portNumber[1],LED_pinNumber[1],false);  //  Green
	pwm.pinSelect(2,LED_portNumber[2],LED_pinNumber[2],false);  //  Blue
	pwm.enable();
}

void LED::on (ledState color, int brightness){
	LED_intensity = brightness;//(scale(brightness,0,100,0x8000,0xFFFF));
	if (pwm_sequence_started_flag){
		pwm.stop();
		pwm_sequence_started_flag = false;
	}
	if (LED_RGB_flag){
		LED_color = color;
		if (brightness >= 0xFFFFFFFF){//100){
			LED_activeLow_flag ? writePin(LED_portNumber[0],LED_pinNumber[0],readBit(LED_color,0) ? 0:1) : writePin(LED_portNumber[0],LED_pinNumber[0],readBit(LED_color,0) ? 1:0);
			LED_activeLow_flag ? writePin(LED_portNumber[1],LED_pinNumber[1],readBit(LED_color,1) ? 0:1) : writePin(LED_portNumber[1],LED_pinNumber[1],readBit(LED_color,1) ? 1:0);
			LED_activeLow_flag ? writePin(LED_portNumber[2],LED_pinNumber[2],readBit(LED_color,2) ? 0:1) : writePin(LED_portNumber[2],LED_pinNumber[2],readBit(LED_color,2) ? 1:0);
		}
		else{
			sequence_0[0] = LED_intensity;  //  Red, Green
			sequence_0[1] = LED_intensity;  //  Blue, ?
			pwm_sequence_started_flag = pwm.start();
		}
	}
	else{
		if (brightness >= 0xFFFFFFFF){//100){
			LED_activeLow_flag ? writePin(LED_portNumber[0],LED_pinNumber[0],readBit(LED_color,0) ? 0:1) : writePin(LED_portNumber[0],LED_pinNumber[0],readBit(LED_color,0) ? 1:0);
		}
	}
}

void LED::off (void){
	if (pwm_sequence_started_flag){
		pwm.stop();
		pwm_sequence_started_flag = false;
	}
	if (LED_RGB_flag){
		LED_activeLow_flag ? writePin(LED_portNumber[1],LED_pinNumber[1],1) : writePin(LED_portNumber[1],LED_pinNumber[1],0);
		LED_activeLow_flag ? writePin(LED_portNumber[2],LED_pinNumber[2],1) : writePin(LED_portNumber[2],LED_pinNumber[2],0);
	}
	LED_activeLow_flag ? writePin(LED_portNumber[0],LED_pinNumber[0],1) : writePin(LED_portNumber[0],LED_pinNumber[0],0);
}

The PWM class code does not change between revisions.
pwm.h

#ifndef rav_nrf52840_pwm_h
#define rav_nrf52840_pwm_h

#include "rav_nrf52840_baseConst.h"

typedef enum {DIV1,DIV2,DIV4,DIV8,DIV16,DIV32,DIV64,DIV128} prescaler;
typedef enum {PWM0,PWM1,PWM2,PWM3} pwmModule;
typedef enum {COMMON,GROUPED,INDIVIDUAL,WAVEFORM} decoderLoad;

class PWM {

private:

	unsigned int base_address;

	void enable_pwm (bool en);

	bool start_pwm (void);

	void stop_pwm (void);

public:

	PWM (pwmModule module, bool looping, bool mode, int count, prescaler scale);
	
	void init (decoderLoad load, bool decoder_mode, int loop_count);
	
	void sequence (int seq_number, unsigned int *pointer, int count, int refresh, int enddelay);
	
	void pinSelect (int channel, int port, int pin, bool disconnect);
	
	void enable (void);

	void disable (void);

	bool start (void);

	void stop (void);
};

#endif

pwm.cpp

#include "rav_nrf52840_pwm.h"
#include <cstdint>

PWM::PWM (pwmModule module, bool looping, bool mode, int count, prescaler scale){
	switch (module){
		
		default:
			;
		break;
		
		case PWM0:
			base_address = BASE_ADDRESS_PWM0;
		break;
		
		case PWM1:
			base_address = BASE_ADDRESS_PWM1;
		break;
		
		case PWM2:
			base_address = BASE_ADDRESS_PWM2;
		break;
		
		case PWM3:
			base_address = BASE_ADDRESS_PWM3;
		break;
	}
	unsigned int * pwm_mode_reg = (unsigned int *)(base_address + REGISTER_OFFSET_PWM_MODE);
	unsigned int * countertop_reg = (unsigned int *)(base_address + REGISTER_OFFSET_COUNTERTOP);
	unsigned int * prescaler_reg = (unsigned int *)(base_address + REGISTER_OFFSET_PRESCALER);
	unsigned int * shortcut_reg = (unsigned int *)(base_address + REGISTER_OFFSET_SHORTS);
	*pwm_mode_reg = mode;
	*countertop_reg = count;
	*prescaler_reg = scale;
	if (looping){
		*shortcut_reg = 0x04;  //  Enable looping
	}
}





// PRIVATE

void PWM::enable_pwm (bool en){
	unsigned int * pwm_enable_reg = (unsigned int *)(base_address + REGISTER_OFFSET_ENABLE);
	*pwm_enable_reg = en;
}

bool PWM::start_pwm (void){
	unsigned int * start_seq0_task = (unsigned int *)(base_address + TASK_OFFSET_SEQSTART_0);
	volatile unsigned int * seq0_started_event = (unsigned int *)(base_address + EVENT_OFFSET_SEQSTARTED_0);
	*start_seq0_task = true;
	while(!*seq0_started_event){}
	*seq0_started_event = false;
	return 1;
}

void PWM::stop_pwm (void){
	unsigned int * pwm_stop_task = (unsigned int *)(base_address + TASK_OFFSET_PWM_STOP);
	volatile unsigned int * pwm_stopped_event = (unsigned int *)(base_address + EVENT_OFFSET_STOPPED);
	*pwm_stop_task = true;
	while(!*pwm_stopped_event){}
	*pwm_stopped_event = false;
}





// PUBLIC

void PWM::init (decoderLoad load, bool decoder_mode, int loop_count){
	unsigned int * decoder_reg = (unsigned int *)(base_address + REGISTER_OFFSET_DECODER);
	unsigned int * loop_reg = (unsigned int *)(base_address + REGISTER_OFFSET_LOOP);
	*decoder_reg = load;
	if (decoder_mode){
		*decoder_reg |= 0x100;
	}
	*loop_reg = loop_count;
}
	
void PWM::sequence (int seq_number, unsigned int *pointer, int count, int refresh, int enddelay){
	unsigned int * seq_pointer_reg = (unsigned int *)(base_address + REGISTER_OFFSET_SEQ_0_PTR + (MODIFIER_SEQ * seq_number));
	unsigned int * seq_count_reg = (unsigned int *)(base_address + REGISTER_OFFSET_SEQ_0_CNT + (MODIFIER_SEQ * seq_number));
	unsigned int * seq_refresh_reg = (unsigned int *)(base_address + REGISTER_OFFSET_SEQ_0_REFRESH + (MODIFIER_SEQ * seq_number));
	unsigned int * seq_enddelay_reg = (unsigned int *)(base_address + REGISTER_OFFSET_SEQ_0_ENDDELAY + (MODIFIER_SEQ * seq_number));
	*seq_pointer_reg = reinterpret_cast<std::uintptr_t>(pointer);
	*seq_count_reg = count;
	*seq_refresh_reg = refresh;
	*seq_enddelay_reg = enddelay;
}

void PWM::pinSelect (int channel, int port, int pin, bool disconnect){
	unsigned int * pin_select_reg = (unsigned int *)(base_address + REGISTER_OFFSET_PSEL_OUT_0 + (MODIFIER_PSEL_OUT * channel));
	*pin_select_reg = ((disconnect << 31) | (port << 5) | pin);
}

void PWM::enable (void){
	enable_pwm(true);
}

void PWM::disable (void){
	enable_pwm(false);
}

bool PWM::start (void){
	bool pwm_seq_started = start_pwm();
	return pwm_seq_started;
}

void PWM::stop (void){
	stop_pwm();
}

closed #2