Merge branch 'simple-pwm'

remotes/github/bad-doublebuf
Ondřej Hruška 6 years ago
commit e96ecceec9
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 3
      gex.mk
  2. 2
      platform/platform.c
  3. 64
      units/simple_pwm/_pwmdim_api.c
  4. 170
      units/simple_pwm/_pwmdim_init.c
  5. 65
      units/simple_pwm/_pwmdim_internal.h
  6. 89
      units/simple_pwm/_pwmdim_settings.c
  7. 69
      units/simple_pwm/unit_pwmdim.c
  8. 16
      units/simple_pwm/unit_pwmdim.h
  9. 3
      utils/ini_parser.h

@ -17,6 +17,7 @@ GEX_SRC_DIR = \
User/units/sipo \
User/units/fcap \
User/units/touch \
User/units/simple_pwm \
User/TinyFrame \
User/CWPack \
User/tasks
@ -92,7 +93,7 @@ GEX_CDEFS = $(GEX_CDEFS_BASE) \
-DASSERT_FILENAMES=1 \
-DDEBUG_VFS=0 \
-DDEBUG_FLASH_WRITE=0 \
-DVERBOSE_HARDFAULT=0 \
-DVERBOSE_HARDFAULT=1 \
-DUSE_STACK_MONITOR=1 \
-DUSE_DEBUG_UART=1 \
-DDEBUG_MALLOC=0 \

@ -20,6 +20,7 @@
#include "units/sipo/unit_sipo.h"
#include "units/fcap/unit_fcap.h"
#include "units/touch/unit_touch.h"
#include "units/simple_pwm/unit_pwmdim.h"
#include "hw_utils.h"
void plat_init_resources(void)
@ -92,6 +93,7 @@ void plat_init_resources(void)
ureg_add_type(&UNIT_SIPO);
ureg_add_type(&UNIT_FCAP);
ureg_add_type(&UNIT_TOUCH);
ureg_add_type(&UNIT_PWMDIM);
// Free all present resources
{

@ -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

@ -1,6 +1,5 @@
//
// INI file parser with a FSM generated by Ragel. This was originally written for ESPTerm
// Used to extract sections, keys and values from user-provided settings file
// INI file parser. Used to extract sections, keys and values from user-provided settings file
//
#ifndef INIPARSE_STREAM_H

Loading…
Cancel
Save