commit
e96ecceec9
@ -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
|
Loading…
Reference in new issue