I am trying to implement PWM using Timer0 for Atmega328P in C++. Indeed, I have achieved this. But, I have another related problem.
I have a PWM abstract base class that provides an interface for PWM implementation.
// mcal_pwm_base.h
#ifndef MCAL_PWM_BASE_H_
#define MCAL_PWM_BASE_H_
namespace mcal
{
namespace pwm
{
class pwm_base
{
public:
~pwm_base() = default;
virtual bool init() noexcept = 0;
virtual void set_duty(const uint16_t duty_cycle) = 0;
virtual void pwm_ISR() noexcept = 0;
uint16_t get_duty() const noexcept { return pwm_duty_cycle; }
protected:
uint16_t pwm_duty_cycle;
pwm_base() : pwm_duty_cycle(0U) { }
pwm_base(const pwm_base&) = delete;
const pwm_base& operator=(const pwm_base&) = delete;
};
}
}
#endif /* MCAL_PWM_BASE_H_ */
I want to implement PWM with timer (8 bit timer). So, I created another class which derives from pwm_base class.
// mcal_pwm_8.h
#ifndef MCAL_PWM_8_H_
#define MCAL_PWM_8_H_
#include "mcal_pwm_base.h"
#include "mcal_reg_access_dynamic.h"
#include <avr/interrupt.h>
namespace mcal
{
namespace pwm
{
template<const uint8_t prescalar_val = UINT8_C(0U)>
class pwm_8 : public mcal::pwm::pwm_base
{
public:
~pwm_8() = default;
virtual bool init() noexcept
{
// set pwm related things
return true;
}
virtual void set_duty(const uint16_t duty_cycle)
{
pwm_duty_cycle = duty_cycle;
}
virtual void pwm_ISR() noexcept
{
uint8_t compare_value = (pwm_duty_cycle / 100) * 255;
mcal::reg::reg_access_dynamic<uint8_t, uint8_t>::reg_set(mcal::reg::ocr0a, compare_value);
}
};
}
}
ISR(TIMER0_OVF_vect)
{
// pwm_ISR();
}
#endif /* MCAL_PWM_8_H_ */
As you can see, I want to call pwm_ISR() member function in ISR (interrupt service routine). But, I don't know how to do it. In the book, it is written that make ISR friend function of pwm_8 class. Even if I did that, how will I reach the private member variable of pwm_duty_cycle in ISR? At this point, I need your help.
And finally, this is my main function.
// main.h
#define F_CPU 16000000ULL
#include <avr/io.h>
#include <util/delay.h>
#include "mcal_reg.h"
#include "mcal_reg_access_static.h"
#include "mcal_port.h"
#include "mcal_led_port.h"
#include "mcal_pwm_8.h"
int main(void)
{
mcal::pwm::pwm_8<UINT8_C(5U)> myPwm;
myPwm.init();
myPwm.set_duty(250); // This will be applied when ISR called
for(;;)
{
}
return 0;
}
Thank you very much beforehand.
It doesn't seem possible to supply user data (like a void*
) to the ISR
routine so you could make myPwm
a global variable:
Example:
Header file:
extern mcal::pwm::pwm_8<UINT8_C(5U)> myPwm;
ISR(TIMER0_OVF_vect)
{
myPwm.pwm_ISR();
}
... and in the .cpp
file:
mcal::pwm::pwm_8<UINT8_C(5U)> myPwm;
You could also hide the global variable in a function to get lazy initialization.
Header file:
mcal::pwm::pwm_8<UINT8_C(5U)>& myPwm(); // now a function
ISR(TIMER0_OVF_vect)
{
myPwm().pwm_ISR(); // calling function to get the instance
}
... and in the .cpp
file:
mcal::pwm::pwm_8<UINT8_C(5U)>& myPwm() {
static mcal::pwm::pwm_8<UINT8_C(5U)> instance;
// that returns a reference to the one instance you'll use:
return instance;
}