parent
3fdd51ba2e
commit
d8bdaf7203
@ -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