simple pwm
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
#include "unit_pwmdim.h"
|
||||
|
||||
#define PWMDIM_INTERNAL
|
||||
#include "_pwmdim_internal.h"
|
||||
|
||||
error_t UPWMDIM_SetFreq(Unit *unit, uint32_t freq)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
uint16_t presc;
|
||||
uint32_t count;
|
||||
float real_freq;
|
||||
if (!hw_solve_timer(PLAT_APB1_HZ, freq, true, &presc, &count, &real_freq)) {
|
||||
dbg("Failed to resolve timer params.");
|
||||
return E_BAD_VALUE;
|
||||
}
|
||||
LL_TIM_SetPrescaler(priv->TIMx, (uint32_t) (presc - 1));
|
||||
LL_TIM_SetAutoReload(priv->TIMx, count - 1);
|
||||
|
||||
// we must re-calculate duty cycles because they are absolute related to the ARR which we just changed
|
||||
UPWMDIM_SetDuty(unit, 0, priv->duty1);
|
||||
UPWMDIM_SetDuty(unit, 1, priv->duty2);
|
||||
UPWMDIM_SetDuty(unit, 2, priv->duty3);
|
||||
UPWMDIM_SetDuty(unit, 3, priv->duty4);
|
||||
|
||||
// LL_TIM_GenerateEvent_UPDATE(priv->TIMx); // - this appears to cause jumpiness
|
||||
priv->freq = freq;
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
error_t UPWMDIM_SetDuty(Unit *unit, uint8_t ch, uint16_t duty1000)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
uint32_t cnt = (LL_TIM_GetAutoReload(priv->TIMx) + 1)*duty1000 / 1000;
|
||||
|
||||
if (ch == 0) {
|
||||
priv->duty1 = duty1000;
|
||||
LL_TIM_OC_SetCompareCH1(priv->TIMx, cnt);
|
||||
}
|
||||
else if (ch == 1) {
|
||||
priv->duty2 = duty1000;
|
||||
LL_TIM_OC_SetCompareCH2(priv->TIMx, cnt);
|
||||
}
|
||||
else if (ch == 2) {
|
||||
priv->duty3 = duty1000;
|
||||
LL_TIM_OC_SetCompareCH3(priv->TIMx, cnt);
|
||||
}
|
||||
else if (ch == 3) {
|
||||
priv->duty4 = duty1000;
|
||||
LL_TIM_OC_SetCompareCH4(priv->TIMx, cnt);
|
||||
} else {
|
||||
return E_BAD_VALUE;
|
||||
}
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
|
||||
#define PWMDIM_INTERNAL
|
||||
#include "_pwmdim_internal.h"
|
||||
|
||||
/** Allocate data structure and set defaults */
|
||||
error_t UPWMDIM_preInit(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data = calloc_ck(1, sizeof(struct priv));
|
||||
if (priv == NULL) return E_OUT_OF_MEM;
|
||||
|
||||
priv->cfg.freq = 1000;
|
||||
priv->cfg.ch1_choice = 1;
|
||||
priv->cfg.ch2_choice = 0;
|
||||
priv->cfg.ch3_choice = 0;
|
||||
priv->cfg.ch4_choice = 0;
|
||||
|
||||
priv->duty1 = 500;
|
||||
priv->duty2 = 500;
|
||||
priv->duty3 = 500;
|
||||
priv->duty4 = 500;
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/** Finalize unit set-up */
|
||||
error_t UPWMDIM_init(Unit *unit)
|
||||
{
|
||||
bool suc = true;
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
TRY(rsc_claim(unit, R_TIM3));
|
||||
priv->TIMx = TIM3;
|
||||
hw_periph_clock_enable(priv->TIMx);
|
||||
|
||||
// copy the default frequency
|
||||
priv->freq = priv->cfg.freq;
|
||||
|
||||
const Resource ch1_pins[] = { R_PA6, R_PB4, R_PC6 };
|
||||
const uint32_t ch1_af[] = { LL_GPIO_AF_1, LL_GPIO_AF_1, LL_GPIO_AF_0 };
|
||||
|
||||
const Resource ch2_pins[] = { R_PA7, R_PB5, R_PC7 };
|
||||
const uint32_t ch2_af[] = { LL_GPIO_AF_1, LL_GPIO_AF_1, LL_GPIO_AF_0 };
|
||||
|
||||
const Resource ch3_pins[] = { R_PB0, R_PC8 };
|
||||
const uint32_t ch3_af[] = { LL_GPIO_AF_1, LL_GPIO_AF_0 };
|
||||
|
||||
const Resource ch4_pins[] = { R_PB1, R_PC9 };
|
||||
const uint32_t ch4_af[] = { LL_GPIO_AF_1, LL_GPIO_AF_0 };
|
||||
|
||||
Resource r[4] = {};
|
||||
uint32_t af[4] = {};
|
||||
|
||||
// --- resolve pins and AFs ---
|
||||
if (priv->cfg.ch1_choice > 0) {
|
||||
if (priv->cfg.ch1_choice > 3) return E_BAD_CONFIG;
|
||||
r[0] = ch1_pins[priv->cfg.ch1_choice - 1];
|
||||
af[0] = ch1_af[priv->cfg.ch1_choice - 1];
|
||||
TRY(rsc_claim(unit, r[0]));
|
||||
}
|
||||
|
||||
if (priv->cfg.ch2_choice > 0) {
|
||||
if (priv->cfg.ch2_choice > 3) return E_BAD_CONFIG;
|
||||
r[1] = ch2_pins[priv->cfg.ch2_choice - 1];
|
||||
af[1] = ch2_af[priv->cfg.ch2_choice - 1];
|
||||
TRY(rsc_claim(unit, r[1]));
|
||||
}
|
||||
|
||||
if (priv->cfg.ch3_choice > 0) {
|
||||
if (priv->cfg.ch3_choice > 2) return E_BAD_CONFIG;
|
||||
r[2] = ch3_pins[priv->cfg.ch3_choice - 1];
|
||||
af[2] = ch3_af[priv->cfg.ch3_choice - 1];
|
||||
TRY(rsc_claim(unit, r[2]));
|
||||
}
|
||||
|
||||
if (priv->cfg.ch4_choice > 0) {
|
||||
if (priv->cfg.ch4_choice > 2) return E_BAD_CONFIG;
|
||||
r[3] = ch4_pins[priv->cfg.ch4_choice - 1];
|
||||
af[3] = ch4_af[priv->cfg.ch4_choice - 1];
|
||||
TRY(rsc_claim(unit, r[3]));
|
||||
}
|
||||
|
||||
// --- configure AF + timer ---
|
||||
LL_TIM_DeInit(priv->TIMx); // force a reset
|
||||
|
||||
uint16_t presc;
|
||||
uint32_t count;
|
||||
float real_freq;
|
||||
if (!hw_solve_timer(PLAT_APB1_HZ, priv->freq, true, &presc, &count, &real_freq)) {
|
||||
dbg("Failed to resolve timer params.");
|
||||
return E_BAD_VALUE;
|
||||
}
|
||||
LL_TIM_SetPrescaler(priv->TIMx, (uint32_t) (presc - 1));
|
||||
LL_TIM_SetAutoReload(priv->TIMx, count - 1);
|
||||
LL_TIM_EnableARRPreload(priv->TIMx);
|
||||
|
||||
dbg("Presc %d, cnt %d", (int)presc, (int)count);
|
||||
|
||||
// TODO this can probably be turned into a loop over an array of structs
|
||||
|
||||
|
||||
if (priv->cfg.ch1_choice > 0) {
|
||||
TRY(hw_configure_gpiorsc_af(r[0], af[0]));
|
||||
|
||||
LL_TIM_OC_EnablePreload(priv->TIMx, LL_TIM_CHANNEL_CH1);
|
||||
LL_TIM_OC_SetMode(priv->TIMx, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_PWM1);
|
||||
LL_TIM_OC_SetCompareCH1(priv->TIMx, count/2);
|
||||
LL_TIM_CC_EnablePreload(priv->TIMx);
|
||||
LL_TIM_CC_EnableChannel(priv->TIMx, LL_TIM_CHANNEL_CH1);
|
||||
}
|
||||
|
||||
if (priv->cfg.ch2_choice > 0) {
|
||||
TRY(hw_configure_gpiorsc_af(r[1], af[1]));
|
||||
|
||||
LL_TIM_OC_EnablePreload(priv->TIMx, LL_TIM_CHANNEL_CH2);
|
||||
LL_TIM_OC_SetMode(priv->TIMx, LL_TIM_CHANNEL_CH2, LL_TIM_OCMODE_PWM1);
|
||||
LL_TIM_OC_SetCompareCH2(priv->TIMx, count/2);
|
||||
LL_TIM_CC_EnableChannel(priv->TIMx, LL_TIM_CHANNEL_CH2);
|
||||
}
|
||||
|
||||
if (priv->cfg.ch3_choice > 0) {
|
||||
TRY(hw_configure_gpiorsc_af(r[2], af[2]));
|
||||
|
||||
LL_TIM_OC_EnablePreload(priv->TIMx, LL_TIM_CHANNEL_CH3);
|
||||
LL_TIM_OC_SetMode(priv->TIMx, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_PWM1);
|
||||
LL_TIM_OC_SetCompareCH3(priv->TIMx, count/2);
|
||||
LL_TIM_CC_EnableChannel(priv->TIMx, LL_TIM_CHANNEL_CH3);
|
||||
}
|
||||
|
||||
if (priv->cfg.ch4_choice > 0) {
|
||||
TRY(hw_configure_gpiorsc_af(r[3], af[3]));
|
||||
|
||||
LL_TIM_OC_EnablePreload(priv->TIMx, LL_TIM_CHANNEL_CH4);
|
||||
LL_TIM_OC_SetMode(priv->TIMx, LL_TIM_CHANNEL_CH4, LL_TIM_OCMODE_PWM1);
|
||||
LL_TIM_OC_SetCompareCH4(priv->TIMx, count/2);
|
||||
LL_TIM_CC_EnableChannel(priv->TIMx, LL_TIM_CHANNEL_CH4);
|
||||
}
|
||||
|
||||
LL_TIM_GenerateEvent_UPDATE(priv->TIMx);
|
||||
LL_TIM_EnableAllOutputs(priv->TIMx);
|
||||
|
||||
// postpone this for later - when user uses the start command.
|
||||
// prevents beeping right after restart if used for audio.
|
||||
// LL_TIM_EnableCounter(priv->TIMx);
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/** Tear down the unit */
|
||||
void UPWMDIM_deInit(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
// de-init peripherals
|
||||
if (unit->status == E_SUCCESS ) {
|
||||
LL_TIM_DeInit(priv->TIMx);
|
||||
}
|
||||
|
||||
// Release all resources, deinit pins
|
||||
rsc_teardown(unit);
|
||||
|
||||
// Free memory
|
||||
free_ck(unit->data);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_PWMDIM_INTERNAL_H
|
||||
#define GEX_F072_PWMDIM_INTERNAL_H
|
||||
|
||||
#ifndef PWMDIM_INTERNAL
|
||||
#error bad include!
|
||||
#endif
|
||||
|
||||
#include "unit_base.h"
|
||||
|
||||
/** Private data structure */
|
||||
struct priv {
|
||||
// settings
|
||||
struct {
|
||||
uint32_t freq;
|
||||
uint8_t ch1_choice;
|
||||
uint8_t ch2_choice;
|
||||
uint8_t ch3_choice;
|
||||
uint8_t ch4_choice;
|
||||
} cfg;
|
||||
|
||||
// internal state
|
||||
uint32_t freq;
|
||||
uint16_t duty1;
|
||||
uint16_t duty2;
|
||||
uint16_t duty3;
|
||||
uint16_t duty4;
|
||||
|
||||
TIM_TypeDef *TIMx;
|
||||
};
|
||||
|
||||
/** Allocate data structure and set defaults */
|
||||
error_t UPWMDIM_preInit(Unit *unit);
|
||||
|
||||
/** Load from a binary buffer stored in Flash */
|
||||
void UPWMDIM_loadBinary(Unit *unit, PayloadParser *pp);
|
||||
|
||||
/** Write to a binary buffer for storing in Flash */
|
||||
void UPWMDIM_writeBinary(Unit *unit, PayloadBuilder *pb);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Parse a key-value pair from the INI file */
|
||||
error_t UPWMDIM_loadIni(Unit *unit, const char *key, const char *value);
|
||||
|
||||
/** Generate INI file section for the unit */
|
||||
void UPWMDIM_writeIni(Unit *unit, IniWriter *iw);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Finalize unit set-up */
|
||||
error_t UPWMDIM_init(Unit *unit);
|
||||
|
||||
/** Tear down the unit */
|
||||
void UPWMDIM_deInit(Unit *unit);
|
||||
|
||||
|
||||
error_t UPWMDIM_SetFreq(Unit *unit, uint32_t freq);
|
||||
|
||||
error_t UPWMDIM_SetDuty(Unit *unit, uint8_t ch, uint16_t duty1000);
|
||||
|
||||
#endif //GEX_F072_PWMDIM_INTERNAL_H
|
||||
@@ -0,0 +1,89 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
|
||||
#define PWMDIM_INTERNAL
|
||||
#include "_pwmdim_internal.h"
|
||||
|
||||
/** Load from a binary buffer stored in Flash */
|
||||
void UPWMDIM_loadBinary(Unit *unit, PayloadParser *pp)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
uint8_t version = pp_u8(pp);
|
||||
(void)version;
|
||||
|
||||
priv->cfg.freq = pp_u32(pp);
|
||||
priv->cfg.ch1_choice = pp_u8(pp);
|
||||
priv->cfg.ch2_choice = pp_u8(pp);
|
||||
priv->cfg.ch3_choice = pp_u8(pp);
|
||||
priv->cfg.ch4_choice = pp_u8(pp);
|
||||
}
|
||||
|
||||
/** Write to a binary buffer for storing in Flash */
|
||||
void UPWMDIM_writeBinary(Unit *unit, PayloadBuilder *pb)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
pb_u8(pb, 0); // version
|
||||
|
||||
pb_u32(pb, priv->cfg.freq);
|
||||
pb_u8(pb, priv->cfg.ch1_choice);
|
||||
pb_u8(pb, priv->cfg.ch2_choice);
|
||||
pb_u8(pb, priv->cfg.ch3_choice);
|
||||
pb_u8(pb, priv->cfg.ch4_choice);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Parse a key-value pair from the INI file */
|
||||
error_t UPWMDIM_loadIni(Unit *unit, const char *key, const char *value)
|
||||
{
|
||||
bool suc = true;
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
if (streq(key, "frequency")) {
|
||||
priv->cfg.freq = cfg_u32_parse(value, &suc);
|
||||
}
|
||||
else if (streq(key, "ch1_pin")) {
|
||||
priv->cfg.ch1_choice = cfg_u8_parse(value, &suc);
|
||||
}
|
||||
else if (streq(key, "ch2_pin")) {
|
||||
priv->cfg.ch2_choice = cfg_u8_parse(value, &suc);
|
||||
}
|
||||
else if (streq(key, "ch3_pin")) {
|
||||
priv->cfg.ch3_choice = cfg_u8_parse(value, &suc);
|
||||
}
|
||||
else if (streq(key, "ch4_pin")) {
|
||||
priv->cfg.ch4_choice = cfg_u8_parse(value, &suc);
|
||||
}
|
||||
else {
|
||||
return E_BAD_KEY;
|
||||
}
|
||||
|
||||
if (!suc) return E_BAD_VALUE;
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/** Generate INI file section for the unit */
|
||||
void UPWMDIM_writeIni(Unit *unit, IniWriter *iw)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
iw_comment(iw, "Default pulse frequency (Hz)");
|
||||
iw_entry_d(iw, "frequency", priv->cfg.freq);
|
||||
|
||||
iw_comment(iw, "Pin mapping - 0=disabled");
|
||||
iw_comment(iw, "Channel1 - 1:PA6, 2:PB4, 3:PC6");
|
||||
iw_entry_d(iw, "ch1_pin", priv->cfg.ch1_choice);
|
||||
iw_comment(iw, "Channel2 - 1:PA7, 2:PB5, 3:PC7");
|
||||
iw_entry_d(iw, "ch2_pin", priv->cfg.ch2_choice);
|
||||
iw_comment(iw, "Channel3 - 1:PB0, 2:PC8");
|
||||
iw_entry_d(iw, "ch3_pin", priv->cfg.ch3_choice);
|
||||
iw_comment(iw, "Channel4 - 1:PB1, 2:PC9");
|
||||
iw_entry_d(iw, "ch4_pin", priv->cfg.ch4_choice);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// Created by MightyPork on 2017/11/25.
|
||||
//
|
||||
|
||||
#include "unit_base.h"
|
||||
#include "unit_pwmdim.h"
|
||||
|
||||
#define PWMDIM_INTERNAL
|
||||
#include "_pwmdim_internal.h"
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
enum PwmSimpleCmd_ {
|
||||
CMD_SET_FREQUENCY = 0,
|
||||
CMD_SET_DUTY = 1,
|
||||
CMD_STOP = 2,
|
||||
CMD_START = 3,
|
||||
};
|
||||
|
||||
/** Handle a request message */
|
||||
static error_t UPWMDIM_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
switch (command) {
|
||||
case CMD_SET_FREQUENCY:
|
||||
TRY(UPWMDIM_SetFreq(unit, pp_u32(pp)));
|
||||
return E_SUCCESS;
|
||||
|
||||
case CMD_SET_DUTY:
|
||||
for (; pp_length(pp) > 0;) {
|
||||
uint8_t ch = pp_u8(pp);
|
||||
uint16_t duty = pp_u16(pp);
|
||||
TRY(UPWMDIM_SetDuty(unit, ch, duty));
|
||||
}
|
||||
return E_SUCCESS;
|
||||
|
||||
case CMD_STOP:
|
||||
LL_TIM_DisableCounter(priv->TIMx);
|
||||
LL_TIM_SetCounter(priv->TIMx, 0);
|
||||
return E_SUCCESS;
|
||||
|
||||
case CMD_START:
|
||||
LL_TIM_EnableCounter(priv->TIMx);
|
||||
return E_SUCCESS;
|
||||
|
||||
default:
|
||||
return E_UNKNOWN_COMMAND;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Simple PWM dimming output */
|
||||
const UnitDriver UNIT_PWMDIM = {
|
||||
.name = "PWMDIM",
|
||||
.description = "Simple PWM output",
|
||||
// Settings
|
||||
.preInit = UPWMDIM_preInit,
|
||||
.cfgLoadBinary = UPWMDIM_loadBinary,
|
||||
.cfgWriteBinary = UPWMDIM_writeBinary,
|
||||
.cfgLoadIni = UPWMDIM_loadIni,
|
||||
.cfgWriteIni = UPWMDIM_writeIni,
|
||||
// Init
|
||||
.init = UPWMDIM_init,
|
||||
.deInit = UPWMDIM_deInit,
|
||||
// Function
|
||||
.handleRequest = UPWMDIM_handleRequest,
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// Created by MightyPork on 2017/11/25.
|
||||
//
|
||||
// Digital input unit; single or multiple pin read access on one port (A-F)
|
||||
//
|
||||
|
||||
#ifndef U_PWMDIM_H
|
||||
#define U_PWMDIM_H
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
extern const UnitDriver UNIT_PWMDIM;
|
||||
|
||||
// UU_ prototypes
|
||||
|
||||
#endif //U_PWMDIM_H
|
||||
Reference in New Issue
Block a user