diff --git a/framework/rsc_enum.h b/framework/rsc_enum.h index 7a1957e..fdb21ea 100644 --- a/framework/rsc_enum.h +++ b/framework/rsc_enum.h @@ -12,7 +12,7 @@ X(SPI1) X(SPI2) X(SPI3) \ X(I2C1) X(I2C2) X(I2C3) \ X(ADC1) X(ADC2) X(ADC3) X(ADC4) \ - X(DAC1) X(DAC2) \ + X(DAC1) \ X(TSC) \ X(USART1) X(USART2) X(USART3) X(USART4) X(USART5) X(USART6) \ X(TIM1) X(TIM2) X(TIM3) X(TIM4) X(TIM5) \ diff --git a/gex.mk b/gex.mk index f0732f9..5921c08 100644 --- a/gex.mk +++ b/gex.mk @@ -18,6 +18,7 @@ GEX_SRC_DIR = \ User/units/fcap \ User/units/touch \ User/units/simple_pwm \ + User/units/dac \ User/TinyFrame \ User/CWPack \ User/tasks diff --git a/platform/platform.c b/platform/platform.c index 69db7c4..9279445 100644 --- a/platform/platform.c +++ b/platform/platform.c @@ -2,6 +2,7 @@ // Created by MightyPork on 2017/11/26. // +#include #include "platform.h" #include "usbd_core.h" #include "USB/usb_device.h" @@ -94,6 +95,7 @@ void plat_init_resources(void) ureg_add_type(&UNIT_FCAP); ureg_add_type(&UNIT_TOUCH); ureg_add_type(&UNIT_PWMDIM); + ureg_add_type(&UNIT_DAC); // Free all present resources { diff --git a/units/dac/_dac_api.c b/units/dac/_dac_api.c new file mode 100644 index 0000000..a02559f --- /dev/null +++ b/units/dac/_dac_api.c @@ -0,0 +1,40 @@ +// +// Created by MightyPork on 2018/02/03. +// + +#include "platform.h" +#include "unit_base.h" +#include "unit_dac.h" + +#define DAC_INTERNAL +#include "_dac_internal.h" + +/** + * Re-configure the + * @param unit + */ +void UDAC_Reconfigure(Unit *unit) +{ + struct priv *priv = unit->data; + + DAC->CR &= ~(DAC_CR_EN1 | DAC_CR_EN2); + uint32_t CR = 0; + if (priv->cfg.ch1.enable) { + CR |= + (priv->cfg.ch1.buffered ? 0 : DAC_CR_BOFF1) | + (priv->cfg.ch1.noise_type << DAC_CR_WAVE1_Pos) | + (priv->cfg.ch1.noise_level & 0xF) << DAC_CR_MAMP1_Pos | + (0b111 << DAC_CR_TSEL1_Pos); // software trigger; + + CR |= DAC_CR_EN1; + } + if (priv->cfg.ch2.enable) { + CR |= + (priv->cfg.ch2.buffered ? 0 : DAC_CR_BOFF2) | + (priv->cfg.ch2.noise_type << DAC_CR_WAVE2_Pos) | + (priv->cfg.ch2.noise_level & 0xF) << DAC_CR_MAMP2_Pos | + (0b111 << DAC_CR_TSEL2_Pos); // software trigger + CR |= DAC_CR_EN2; + } + DAC->CR = CR; +} diff --git a/units/dac/_dac_init.c b/units/dac/_dac_init.c new file mode 100644 index 0000000..847cb6e --- /dev/null +++ b/units/dac/_dac_init.c @@ -0,0 +1,99 @@ +// +// Created by MightyPork on 2018/02/03. +// + +#include "platform.h" +#include "unit_base.h" + +#define DAC_INTERNAL +#include "_dac_internal.h" + +/** Allocate data structure and set defaults */ +error_t UDAC_preInit(Unit *unit) +{ + struct priv *priv = unit->data = calloc_ck(1, sizeof(struct priv)); + if (priv == NULL) return E_OUT_OF_MEM; + + priv->cfg.ch1.buffered = true; + priv->cfg.ch1.enable = true; + priv->cfg.ch1.noise_level = 2; + priv->cfg.ch1.noise_type = NOISE_NONE; + + priv->ch1.noise_type = priv->cfg.ch1.noise_type; + priv->ch1.noise_level = priv->cfg.ch1.noise_level; + + priv->cfg.ch2.buffered = true; + priv->cfg.ch2.enable = true; + priv->cfg.ch2.noise_level = 2; + priv->cfg.ch2.noise_type = NOISE_NONE; + + priv->ch2.noise_type = priv->cfg.ch2.noise_type; + priv->ch2.noise_level = priv->cfg.ch2.noise_level; + + return E_SUCCESS; +} + +/** Finalize unit set-up */ +error_t UDAC_init(Unit *unit) +{ + bool suc = true; + struct priv *priv = unit->data; + + // this may change for different devices + const Resource r_ch1 = R_PA4; + const Resource r_ch2 = R_PA5; + + const bool e1 = priv->cfg.ch1.enable; + const bool e2 = priv->cfg.ch2.enable; + + + if (e1) { + TRY(rsc_claim(unit, r_ch1)); + } + + if (e2) { + TRY(rsc_claim(unit, r_ch2)); + } + + TRY(rsc_claim(unit, R_DAC1)); + + // ensure the peripheral is clean (this may be redundant) + __HAL_RCC_DAC1_FORCE_RESET(); + __HAL_RCC_DAC1_RELEASE_RESET(); + + hw_periph_clock_enable(DAC1); + + GPIO_TypeDef *port; + uint32_t ll; + if (e1) { + assert_param(hw_pinrsc2ll(r_ch1, &port, &ll)); + LL_GPIO_SetPinMode(port, ll, LL_GPIO_MODE_ANALOG); + } + if (e2) { + assert_param(hw_pinrsc2ll(r_ch1, &port, &ll)); + LL_GPIO_SetPinMode(port, ll, LL_GPIO_MODE_ANALOG); + } + + UDAC_Reconfigure(unit); + + return E_SUCCESS; +} + + +/** Tear down the unit */ +void UDAC_deInit(Unit *unit) +{ + struct priv *priv = unit->data; + + // de-init peripherals + if (unit->status == E_SUCCESS ) { + LL_DAC_DeInit(DAC); + hw_periph_clock_disable(DAC1); + } + + // Release all resources, deinit pins + rsc_teardown(unit); + + // Free memory + free_ck(unit->data); +} diff --git a/units/dac/_dac_internal.h b/units/dac/_dac_internal.h new file mode 100644 index 0000000..be376b2 --- /dev/null +++ b/units/dac/_dac_internal.h @@ -0,0 +1,73 @@ +// +// Created by MightyPork on 2018/02/03. +// + +#ifndef GEX_F072_DAC_INTERNAL_H +#define GEX_F072_DAC_INTERNAL_H + +#ifndef DAC_INTERNAL +#error bad include! +#endif + +#include "unit_base.h" + +enum UDAC_Noise { + NOISE_NONE = 0b00, + NOISE_WHITE = 0b01, + NOISE_TRIANGLE = 0b10, +}; + +struct udac_channel_cfg { + bool enable; + bool buffered; + enum UDAC_Noise noise_type; + uint8_t noise_level; // 0-11 +}; + +struct udac_channel_live { + enum UDAC_Noise noise_type; + uint8_t noise_level; // 0-11 +}; + +/** Private data structure */ +struct priv { + // settings + struct { + struct udac_channel_cfg ch1; + struct udac_channel_cfg ch2; + } cfg; + + // internal state + struct udac_channel_live ch1; + struct udac_channel_live ch2; + TIM_TypeDef *TIMx; // timer used for the DDS function +}; + +/** Allocate data structure and set defaults */ +error_t UDAC_preInit(Unit *unit); + +/** Load from a binary buffer stored in Flash */ +void UDAC_loadBinary(Unit *unit, PayloadParser *pp); + +/** Write to a binary buffer for storing in Flash */ +void UDAC_writeBinary(Unit *unit, PayloadBuilder *pb); + +// ------------------------------------------------------------------------ + +/** Parse a key-value pair from the INI file */ +error_t UDAC_loadIni(Unit *unit, const char *key, const char *value); + +/** Generate INI file section for the unit */ +void UDAC_writeIni(Unit *unit, IniWriter *iw); + +// ------------------------------------------------------------------------ + +/** Finalize unit set-up */ +error_t UDAC_init(Unit *unit); + +/** Tear down the unit */ +void UDAC_deInit(Unit *unit); + +void UDAC_Reconfigure(Unit *unit); + +#endif //GEX_F072_DAC_INTERNAL_H diff --git a/units/dac/_dac_settings.c b/units/dac/_dac_settings.c new file mode 100644 index 0000000..bbef170 --- /dev/null +++ b/units/dac/_dac_settings.c @@ -0,0 +1,131 @@ +// +// Created by MightyPork on 2018/02/03. +// + +#include "platform.h" +#include "unit_base.h" + +#define DAC_INTERNAL +#include "_dac_internal.h" + +/** Load from a binary buffer stored in Flash */ +void UDAC_loadBinary(Unit *unit, PayloadParser *pp) +{ + struct priv *priv = unit->data; + + uint8_t version = pp_u8(pp); + (void)version; + + priv->cfg.ch1.enable = pp_bool(pp); + priv->cfg.ch1.buffered = pp_bool(pp); + priv->cfg.ch1.noise_type = (enum UDAC_Noise) pp_u8(pp); + priv->cfg.ch1.noise_level = pp_u8(pp); + + priv->cfg.ch2.enable = pp_bool(pp); + priv->cfg.ch2.buffered = pp_bool(pp); + priv->cfg.ch2.noise_type = (enum UDAC_Noise) pp_u8(pp); + priv->cfg.ch2.noise_level = pp_u8(pp); +} + +/** Write to a binary buffer for storing in Flash */ +void UDAC_writeBinary(Unit *unit, PayloadBuilder *pb) +{ + struct priv *priv = unit->data; + + pb_u8(pb, 0); // version + + pb_bool(pb, priv->cfg.ch1.enable); + pb_bool(pb, priv->cfg.ch1.buffered); + pb_u8(pb, priv->cfg.ch1.noise_type); + pb_u8(pb, priv->cfg.ch1.noise_level); + + pb_bool(pb, priv->cfg.ch2.enable); + pb_bool(pb, priv->cfg.ch2.buffered); + pb_u8(pb, priv->cfg.ch2.noise_type); + pb_u8(pb, priv->cfg.ch2.noise_level); +} + +// ------------------------------------------------------------------------ + +/** Parse a key-value pair from the INI file */ +error_t UDAC_loadIni(Unit *unit, const char *key, const char *value) +{ + bool suc = true; + struct priv *priv = unit->data; + + // Ch1 + if (streq(key, "ch1_enable")) { + priv->cfg.ch1.enable = cfg_bool_parse(value, &suc); + } + else if (streq(key, "ch1_buff")) { + priv->cfg.ch1.buffered = cfg_bool_parse(value, &suc); + } + else if (streq(key, "ch1_noise")) { + priv->cfg.ch1.noise_type = + (enum UDAC_Noise) cfg_enum3_parse(value, + "NONE", NOISE_NONE, + "WHITE", NOISE_WHITE, + "TRIANGLE", NOISE_TRIANGLE, &suc); + } + else if (streq(key, "ch1_noise-level")) { + uint8_t x = cfg_u8_parse(value, &suc); + if (x == 0) x = 1; + if (x > 12) x = 12; + priv->cfg.ch1.noise_level = (uint8_t) (x - 1); + } + // Ch2 + else if (streq(key, "ch2_enable")) { + priv->cfg.ch2.enable = cfg_bool_parse(value, &suc); + } + else if (streq(key, "ch2_buff")) { + priv->cfg.ch2.buffered = cfg_bool_parse(value, &suc); + } + else if (streq(key, "ch2_noise")) { + priv->cfg.ch2.noise_type = + (enum UDAC_Noise) cfg_enum3_parse(value, + "NONE", NOISE_NONE, + "WHITE", NOISE_WHITE, + "TRIANGLE", NOISE_TRIANGLE, &suc); + } + else if (streq(key, "ch2_noise-level")) { + uint8_t x = cfg_u8_parse(value, &suc); + if (x == 0) x = 1; + if (x > 12) x = 12; + priv->cfg.ch2.noise_level = (uint8_t) (x - 1); + } + // end + else { + return E_BAD_KEY; + } + + if (!suc) return E_BAD_VALUE; + return E_SUCCESS; +} + +/** Generate INI file section for the unit */ +void UDAC_writeIni(Unit *unit, IniWriter *iw) +{ + struct priv *priv = unit->data; + + iw_comment(iw, "Enabled channels (1:A4, 2:A5)"); + iw_entry_s(iw, "ch1_enable", str_yn(priv->cfg.ch1.enable)); + iw_entry_s(iw, "ch2_enable", str_yn(priv->cfg.ch2.enable)); + + iw_comment(iw, "Output buffering"); + iw_entry_s(iw, "ch1_buff", str_yn(priv->cfg.ch1.buffered)); + iw_entry_s(iw, "ch2_buff", str_yn(priv->cfg.ch2.buffered)); + + iw_comment(iw, "Superimposed noise type (NONE,WHITE,TRIANGLE)"); + iw_entry_s(iw, "ch1_noise", cfg_enum3_encode(priv->cfg.ch1.noise_type, + NOISE_NONE, "NONE", + NOISE_WHITE, "WHITE", + NOISE_TRIANGLE, "TRIANGLE")); + iw_entry_s(iw, "ch2_noise", cfg_enum3_encode(priv->cfg.ch2.noise_type, + NOISE_NONE, "NONE", + NOISE_WHITE, "WHITE", + NOISE_TRIANGLE, "TRIANGLE")); + + iw_comment(iw, "Noise amplitude (nbr. of bits,1-12)"); + iw_entry_d(iw, "ch1_noise-level", priv->cfg.ch1.noise_level + 1); + iw_entry_d(iw, "ch2_noise-level", priv->cfg.ch2.noise_level + 1); +} diff --git a/units/dac/unit_dac.c b/units/dac/unit_dac.c new file mode 100644 index 0000000..9908204 --- /dev/null +++ b/units/dac/unit_dac.c @@ -0,0 +1,59 @@ +// +// Created by MightyPork on 2017/11/25. +// + +#include "unit_base.h" +#include "unit_dac.h" + +#define DAC_INTERNAL +#include "_dac_internal.h" + +// ------------------------------------------------------------------------ + +enum DacCmd_ { + CMD_SET_LEVEL, +}; + +/** Handle a request message */ +static error_t UDAC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, + PayloadParser *pp) +{ + switch (command) { + case CMD_SET_LEVEL: { + uint16_t ch1 = pp_u16(pp); + uint16_t ch2 = pp_u16(pp); + uint32_t dual_reg = DAC->DHR12RD; + if (ch1 != 0xFFFF) { + dual_reg = (dual_reg & 0xFFFF0000) | ch1; + } + if (ch2 != 0xFFFF) { + dual_reg = (dual_reg & 0xFFFF) | (ch2<<16); + } + DAC->DHR12RD = dual_reg; + // Trigger a conversion + DAC->SWTRIGR = (DAC_SWTR_CH1 | DAC_SWTR_CH2); + return E_SUCCESS; + } + default: + return E_UNKNOWN_COMMAND; + } +} + +// ------------------------------------------------------------------------ + +/** Unit template */ +const UnitDriver UNIT_DAC = { + .name = "DAC", + .description = "Analog output", + // Settings + .preInit = UDAC_preInit, + .cfgLoadBinary = UDAC_loadBinary, + .cfgWriteBinary = UDAC_writeBinary, + .cfgLoadIni = UDAC_loadIni, + .cfgWriteIni = UDAC_writeIni, + // Init + .init = UDAC_init, + .deInit = UDAC_deInit, + // Function + .handleRequest = UDAC_handleRequest, +}; diff --git a/units/dac/unit_dac.h b/units/dac/unit_dac.h new file mode 100644 index 0000000..b224d8f --- /dev/null +++ b/units/dac/unit_dac.h @@ -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_DAC_H +#define U_DAC_H + +#include "unit.h" + +extern const UnitDriver UNIT_DAC; + +// UU_ prototypes + +#endif //U_DAC_H