Merge branch 'add-spi'

sipo
Ondřej Hruška 7 years ago
commit 766cfdba06
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 6
      comm/msg_bulkread.c
  2. 128
      framework/resources.c
  3. 76
      framework/resources.h
  4. 92
      framework/rsc_enum.h
  5. 8
      framework/settings.c
  6. 2
      framework/unit.c
  7. 13
      framework/unit.h
  8. 105
      framework/unit_registry.c
  9. 8
      framework/unit_registry.h
  10. 6
      freertos.c
  11. 3
      gex.mk
  12. 60
      platform/pin_utils.c
  13. 10
      platform/pin_utils.h
  14. 5
      platform/plat_compat.h
  15. 34
      platform/platform.c
  16. 3
      tasks/task_msg.c
  17. 20
      units/digital_in/unit_din.c
  18. 51
      units/digital_out/unit_dout.c
  19. 10
      units/digital_out/unit_dout.h
  20. 112
      units/i2c/unit_i2c.c
  21. 8
      units/neopixel/unit_neopixel.c
  22. 529
      units/spi/unit_spi.c
  23. 63
      units/spi/unit_spi.h
  24. 3
      utils/error.h

@ -11,7 +11,7 @@
#include "utils/payload_builder.h" #include "utils/payload_builder.h"
/** Buffer for preparing bulk chunks */ /** Buffer for preparing bulk chunks */
static uint8_t bulkread_buffer[BULKREAD_MAX_CHUNK]; static uint8_t bulkread_buffer[BULK_READ_BUF_LEN];
/** /**
* TF listener for the bulk read transaction * TF listener for the bulk read transaction
@ -36,7 +36,7 @@ static TF_Result bulkread_lst(TinyFrame *tf, TF_Msg *msg)
uint32_t chunk = pp_u32(&pp); uint32_t chunk = pp_u32(&pp);
chunk = MIN(chunk, bulk->len - bulk->offset); chunk = MIN(chunk, bulk->len - bulk->offset);
chunk = MIN(chunk, BULKREAD_MAX_CHUNK); chunk = MIN(chunk, BULK_READ_BUF_LEN);
// load data into the buffer // load data into the buffer
bulk->read(bulk, chunk, bulkread_buffer); bulk->read(bulk, chunk, bulkread_buffer);
@ -81,7 +81,7 @@ void bulkread_start(TinyFrame *tf, BulkRead *bulk)
uint8_t buf[8]; uint8_t buf[8];
PayloadBuilder pb = pb_start(buf, 4, NULL); PayloadBuilder pb = pb_start(buf, 4, NULL);
pb_u32(&pb, bulk->len); pb_u32(&pb, bulk->len);
pb_u32(&pb, BULKREAD_MAX_CHUNK); pb_u32(&pb, BULK_READ_BUF_LEN);
// We use userdata1 to hold a reference to the bulk transfer // We use userdata1 to hold a reference to the bulk transfer
TF_Msg msg = { TF_Msg msg = {

@ -6,32 +6,57 @@
#include "unit.h" #include "unit.h"
#include "resources.h" #include "resources.h"
#include "pin_utils.h" #include "pin_utils.h"
#include "unit_registry.h"
static bool rsc_initialized = false; static bool rsc_initialized = false;
// This takes quite a lot of space, we could use u8 and IDs instead if needed static ResourceMap global_rscmap;
struct resouce_slot {
const char *name;
Unit *owner;
} __attribute__((packed));
static struct resouce_slot resources[R_RESOURCE_COUNT]; // here are the resource names for better debugging
// here are the resource names for better debugging (could also be removed if absolutely necessary) // this list doesn't include GPIO names, they can be easily generated
const char *const rsc_names[] = { const char *const rsc_names[] = {
#define X(res_name) #res_name, #define X(res_name) #res_name,
XX_RESOURCES XX_RESOURCES
#undef X #undef X
}; };
/** Get rsc name */
const char * rsc_get_name(Resource rsc)
{
assert_param(rsc < RESOURCE_COUNT);
static char gpionamebuf[4];
if (rsc >= R_PA0) {
// we assume the returned value is not stored anywhere
// and is directly used in a sprintf call.
uint8_t index = rsc - R_PA0;
SNPRINTF(gpionamebuf, 4, "%c%d", 'A'+(index/16), index%16);
return gpionamebuf;
}
return rsc_names[rsc];
}
/** Get rsc owner name */
const char * rsc_get_owner_name(Resource rsc)
{
assert_param(rsc < RESOURCE_COUNT);
Unit *pUnit = ureg_get_rsc_owner(rsc);
if (pUnit == NULL) return "NULL";
return pUnit->name;
}
/** /**
* Initialize the resources registry * Initialize the resources registry
*/ */
void rsc_init_registry(void) void rsc_init_registry(void)
{ {
for (int i = 0; i < R_RESOURCE_COUNT; i++) { for(uint32_t i = 0; i < RSCMAP_LEN; i++) {
resources[i].owner = &UNIT_PLATFORM; UNIT_PLATFORM.resources[i] = global_rscmap[i] = 0xFF;
} }
rsc_initialized = true; rsc_initialized = true;
} }
@ -45,18 +70,31 @@ void rsc_init_registry(void)
error_t rsc_claim(Unit *unit, Resource rsc) error_t rsc_claim(Unit *unit, Resource rsc)
{ {
assert_param(rsc_initialized); assert_param(rsc_initialized);
assert_param(rsc > R_NONE && rsc < R_RESOURCE_COUNT); assert_param(rsc < RESOURCE_COUNT);
assert_param(unit != NULL); assert_param(unit != NULL);
if (resources[rsc].owner) { dbg("%s claims %s", unit->name, rsc_get_name(rsc));
//TODO properly report to user
if (RSC_IS_HELD(global_rscmap, rsc)) {
// this whole branch is just reporting the error
Unit *holder = ureg_get_rsc_owner(rsc);
assert_param(holder != NULL);
dbg("ERROR!! Unit %s failed to claim resource %s, already held by %s!", dbg("ERROR!! Unit %s failed to claim resource %s, already held by %s!",
unit->name, rsc_names[rsc], resources[rsc].owner->name); unit->name,
rsc_get_name(rsc),
holder->name);
unit->failed_rsc = rsc;
return E_RESOURCE_NOT_AVAILABLE; return E_RESOURCE_NOT_AVAILABLE;
} }
resources[rsc].owner = unit; // must claim both in global and in unit
RSC_CLAIM(global_rscmap, rsc);
RSC_CLAIM(unit->resources, rsc);
return E_SUCCESS; return E_SUCCESS;
} }
@ -71,8 +109,9 @@ error_t rsc_claim(Unit *unit, Resource rsc)
error_t rsc_claim_range(Unit *unit, Resource rsc0, Resource rsc1) error_t rsc_claim_range(Unit *unit, Resource rsc0, Resource rsc1)
{ {
assert_param(rsc_initialized); assert_param(rsc_initialized);
assert_param(rsc0 > R_NONE && rsc0 < R_RESOURCE_COUNT); assert_param(rsc0 < RESOURCE_COUNT);
assert_param(rsc1 > R_NONE && rsc1 < R_RESOURCE_COUNT); assert_param(rsc1 < RESOURCE_COUNT);
assert_param(rsc0 <= rsc1);
assert_param(unit != NULL); assert_param(unit != NULL);
for (int i = rsc0; i <= rsc1; i++) { for (int i = rsc0; i <= rsc1; i++) {
@ -97,6 +136,15 @@ error_t rsc_claim_gpios(Unit *unit, char port_name, uint16_t pins)
return E_SUCCESS; return E_SUCCESS;
} }
error_t rsc_claim_pin(Unit *unit, char port_name, uint8_t pin)
{
bool suc = true;
Resource rsc = pin2resource(port_name, pin, &suc);
if (!suc) return E_BAD_CONFIG;
TRY(rsc_claim(unit, rsc));
return E_SUCCESS;
}
/** /**
* Free a resource for other use * Free a resource for other use
* *
@ -106,11 +154,31 @@ error_t rsc_claim_gpios(Unit *unit, char port_name, uint16_t pins)
void rsc_free(Unit *unit, Resource rsc) void rsc_free(Unit *unit, Resource rsc)
{ {
assert_param(rsc_initialized); assert_param(rsc_initialized);
assert_param(rsc > R_NONE && rsc < R_RESOURCE_COUNT); assert_param(rsc < RESOURCE_COUNT);
dbg("Free resource %s", rsc_get_name(rsc));
if (RSC_IS_FREE(global_rscmap, rsc)) return;
if (unit == NULL || resources[rsc].owner == unit) { // free it in any unit that holds it
resources[rsc].owner = NULL; if (unit) {
if (RSC_IS_HELD(unit->resources, rsc)) {
RSC_FREE(unit->resources, rsc);
}
} else {
// Try to free it in any unit that may hold it
unit = ureg_get_rsc_owner(rsc);
if (unit == NULL) {
// try one of the built-in ones
if (RSC_IS_HELD(UNIT_SYSTEM.resources, rsc)) unit = &UNIT_SYSTEM;
else if (RSC_IS_HELD(UNIT_PLATFORM.resources, rsc)) unit = &UNIT_PLATFORM;
}
if (unit != NULL) RSC_FREE(unit->resources, rsc);
} }
// also free it in the global map
RSC_FREE(global_rscmap, rsc);
} }
/** /**
@ -123,18 +191,18 @@ void rsc_free(Unit *unit, Resource rsc)
void rsc_free_range(Unit *unit, Resource rsc0, Resource rsc1) void rsc_free_range(Unit *unit, Resource rsc0, Resource rsc1)
{ {
assert_param(rsc_initialized); assert_param(rsc_initialized);
assert_param(rsc0 > R_NONE && rsc0 < R_RESOURCE_COUNT); assert_param(rsc0 < RESOURCE_COUNT);
assert_param(rsc1 > R_NONE && rsc1 < R_RESOURCE_COUNT); assert_param(rsc1 < RESOURCE_COUNT);
assert_param(rsc0 <= rsc1);
for (int i = rsc0; i <= rsc1; i++) { for (int i = rsc0; i <= rsc1; i++) {
if (unit == NULL || resources[i].owner == unit) { rsc_free(unit, (Resource) i);
resources[i].owner = NULL;
}
} }
} }
/** /**
* Tear down a unit - release all resources owned by the unit * Tear down a unit - release all resources owned by the unit.
* Also de-init all GPIOs
* *
* @param unit - unit to tear down; free only resources claimed by this unit * @param unit - unit to tear down; free only resources claimed by this unit
*/ */
@ -143,9 +211,11 @@ void rsc_teardown(Unit *unit)
assert_param(rsc_initialized); assert_param(rsc_initialized);
assert_param(unit != NULL); assert_param(unit != NULL);
for (int i = R_NONE+1; i < R_RESOURCE_COUNT; i++) { dbg("Tearing down unit %s", unit->name);
if (resources[i].owner == unit) { deinit_unit_pins(unit);
resources[i].owner = NULL;
} for (uint32_t i = 0; i < RSCMAP_LEN; i++) {
global_rscmap[i] &= ~unit->resources[i];
unit->resources[i] = 0;
} }
} }

@ -7,80 +7,13 @@
#include "platform.h" #include "platform.h"
#include "unit.h" #include "unit.h"
#include "rsc_enum.h"
#define CHECK_SUC() do { if (!suc) return false; } while (0) #define CHECK_SUC() do { if (!suc) return false; } while (0)
// X macro: Resource name,
#define XX_RESOURCES \
X(NONE) \
X(PA0) X(PA1) X(PA2) X(PA3) X(PA4) X(PA5) X(PA6) X(PA7) \
X(PA8) X(PA9) X(PA10) X(PA11) X(PA12) X(PA13) X(PA14) X(PA15) \
X(PB0) X(PB1) X(PB2) X(PB3) X(PB4) X(PB5) X(PB6) X(PB7) \
X(PB8) X(PB9) X(PB10) X(PB11) X(PB12) X(PB13) X(PB14) X(PB15) \
X(PC0) X(PC1) X(PC2) X(PC3) X(PC4) X(PC5) X(PC6) X(PC7) \
X(PC8) X(PC9) X(PC10) X(PC11) X(PC12) X(PC13) X(PC14) X(PC15) \
X(PD0) X(PD1) X(PD2) X(PD3) X(PD4) X(PD5) X(PD6) X(PD7) \
X(PD8) X(PD9) X(PD10) X(PD11) X(PD12) X(PD13) X(PD14) X(PD15) \
X(PE0) X(PE1) X(PE2) X(PE3) X(PE4) X(PE5) X(PE6) X(PE7) \
X(PE8) X(PE9) X(PE10) X(PE11) X(PE12) X(PE13) X(PE14) X(PE15) \
X(PF0) X(PF1) X(PF2) X(PF3) X(PF4) X(PF5) X(PF6) X(PF7) \
X(PF8) X(PF9) X(PF10) X(PF11) X(PF12) X(PF13) X(PF14) X(PF15) \
X(SPI1) X(SPI2) X(SPI3) \
X(I2C1) X(I2C2) X(I2C3) \
X(I2S1) X(I2S2) X(I2S3) \
X(ADC1) X(ADC2) X(ADC3) X(ADC4) \
X(OPAMP1) X(OPAMP2) X(OPAMP3) X(OPAMP4) \
X(DAC1) X(DAC2) \
X(CAN1) X(CAN2) \
X(TSC) \
X(DCMI) \
X(ETH) \
X(FSMC) \
X(SDIO) \
X(COMP1) X(COMP2) X(COMP3) X(COMP4) X(COMP5) X(COMP6) X(COMP7) \
X(HDMI_CEC) \
X(USART1) X(USART2) X(USART3) X(USART4) X(USART5) X(USART6) \
X(TIM1) X(TIM2) X(TIM3) X(TIM4) X(TIM5) \
X(TIM6) X(TIM7) X(TIM8) X(TIM9) X(TIM10) X(TIM11) X(TIM12) X(TIM13) X(TIM14) \
X(TIM15) X(TIM16) X(TIM17) \
X(DMA1) X(DMA2) \
X(RNG) X(LCD)
// GPIOs are allocated whenever the pin is needed
// (e.g. when used for SPI, the R_SPI resource as well as the corresponding R_GPIO resources must be claimed)
// Peripheral blocks (IPs) - not all chips have all blocks, usually the 1 and 2 are present as a minimum, if any.
// It doesn't really make sense to expose multiple instances of buses that support addressing
// ADCs - some more advanced chips support differential input mode on some (not all!) inputs
// Usually only one or two instances are present
// DAC - often only one is present, or none.
// UARTs
// - 1 and 2 are present universally, 2 is connected to VCOM on Nucleo/Discovery boards, good for debug messages
// 4 and 5 don't support synchronous mode.
// Timers
// - some support quadrature input, probably all support external clock / gating / clock-out/PWM generation
// Not all chips have all timers and not all timers are equal.
// DMA - Direct memory access lines - TODO split those to channels, they can be used separately
// The resource registry will be pre-loaded with platform-specific config of which blocks are available - the rest will be "pre-claimed"
// (i.e. unavailable to functional modules)
typedef enum hw_resource Resource;
enum hw_resource {
#define X(res_name) R_##res_name,
XX_RESOURCES
#undef X
R_RESOURCE_COUNT
};
void rsc_init_registry(void); void rsc_init_registry(void);
error_t rsc_claim_pin(Unit *unit, char port_name, uint8_t pin);
error_t rsc_claim(Unit *unit, Resource rsc); error_t rsc_claim(Unit *unit, Resource rsc);
error_t rsc_claim_range(Unit *unit, Resource rsc0, Resource rsc1); error_t rsc_claim_range(Unit *unit, Resource rsc0, Resource rsc1);
/** /**
@ -98,4 +31,9 @@ void rsc_teardown(Unit *unit);
void rsc_free(Unit *unit, Resource rsc); void rsc_free(Unit *unit, Resource rsc);
void rsc_free_range(Unit *unit, Resource rsc0, Resource rsc1); void rsc_free_range(Unit *unit, Resource rsc0, Resource rsc1);
const char * rsc_get_name(Resource rsc);
/** Get rsc owner name */
const char * rsc_get_owner_name(Resource rsc);
#endif //GEX_RESOURCES_H #endif //GEX_RESOURCES_H

@ -0,0 +1,92 @@
//
// Created by MightyPork on 2018/01/06.
//
#ifndef GEX_F072_RSC_ENUM_H
#define GEX_F072_RSC_ENUM_H
// X macro: Resource name,
#define XX_RESOURCES \
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(USART1) X(USART2) X(USART3) X(USART4) X(USART5) X(USART6) \
X(TIM1) X(TIM2) X(TIM3) X(TIM4) X(TIM5) \
X(TIM6) X(TIM7) X(TIM8) X(TIM9) X(TIM10) X(TIM11) X(TIM12) X(TIM13) X(TIM14) \
X(TIM15) X(TIM16) X(TIM17) \
X(DMA1) X(DMA2)
// Resources not used anywhere:
// X(I2S1) X(I2S2) X(I2S3)
// X(OPAMP1) X(OPAMP2) X(OPAMP3) X(OPAMP4)
// X(CAN1) X(CAN2)
// X(TSC)
// X(DCMI)
// X(ETH)
// X(FSMC)
// X(SDIO)
// X(RNG) X(LCD)
// X(HDMI_CEC)
// X(COMP1) X(COMP2) X(COMP3) X(COMP4) X(COMP5) X(COMP6) X(COMP7)
#define XX_RESOURCES_GPIO \
X(PA0) X(PA1) X(PA2) X(PA3) X(PA4) X(PA5) X(PA6) X(PA7) \
X(PA8) X(PA9) X(PA10) X(PA11) X(PA12) X(PA13) X(PA14) X(PA15) \
X(PB0) X(PB1) X(PB2) X(PB3) X(PB4) X(PB5) X(PB6) X(PB7) \
X(PB8) X(PB9) X(PB10) X(PB11) X(PB12) X(PB13) X(PB14) X(PB15) \
X(PC0) X(PC1) X(PC2) X(PC3) X(PC4) X(PC5) X(PC6) X(PC7) \
X(PC8) X(PC9) X(PC10) X(PC11) X(PC12) X(PC13) X(PC14) X(PC15) \
X(PD0) X(PD1) X(PD2) X(PD3) X(PD4) X(PD5) X(PD6) X(PD7) \
X(PD8) X(PD9) X(PD10) X(PD11) X(PD12) X(PD13) X(PD14) X(PD15) \
X(PE0) X(PE1) X(PE2) X(PE3) X(PE4) X(PE5) X(PE6) X(PE7) \
X(PE8) X(PE9) X(PE10) X(PE11) X(PE12) X(PE13) X(PE14) X(PE15) \
X(PF0) X(PF1) X(PF2) X(PF3) X(PF4) X(PF5) X(PF6) X(PF7) \
X(PF8) X(PF9) X(PF10) X(PF11) X(PF12) X(PF13) X(PF14) X(PF15) \
// GPIOs are allocated whenever the pin is needed
// (e.g. when used for SPI, the R_SPI resource as well as the corresponding R_GPIO resources must be claimed)
// Peripheral blocks (IPs) - not all chips have all blocks, usually the 1 and 2 are present as a minimum, if any.
// It doesn't really make sense to expose multiple instances of buses that support addressing
// ADCs - some more advanced chips support differential input mode on some (not all!) inputs
// Usually only one or two instances are present
// DAC - often only one is present, or none.
// UARTs
// - 1 and 2 are present universally, 2 is connected to VCOM on Nucleo/Discovery boards, good for debug messages
// 4 and 5 don't support synchronous mode.
// Timers
// - some support quadrature input, probably all support external clock / gating / clock-out/PWM generation
// Not all chips have all timers and not all timers are equal.
// DMA - Direct memory access lines - TODO split those to channels, they can be used separately
// The resource registry will be pre-loaded with platform-specific config of which blocks are available - the rest will be "pre-claimed"
// (i.e. unavailable to functional modules)
typedef enum hw_resource Resource;
enum hw_resource {
#define X(res_name) R_##res_name,
XX_RESOURCES
XX_RESOURCES_GPIO
#undef X
R_NONE,
RESOURCE_COUNT = R_NONE,
};
#define RSCMAP_LEN ((RESOURCE_COUNT/8)+1)
typedef uint8_t ResourceMap[RSCMAP_LEN];
#define RSC_IS_FREE(rscmap, rsc) (0 == (rscmap[((rsc)>>3)&0xFF] & (1<<((rsc)&0x7))))
#define RSC_IS_HELD(rscmap, rsc) (!RSC_IS_FREE(rscmap, rsc))
#define RSC_CLAIM(rscmap, rsc) do { rscmap[((rsc)>>3)&0xFF] |= (1<<((rsc)&0x7)); } while(0)
#define RSC_FREE(rscmap, rsc) do { rscmap[((rsc)>>3)&0xFF] &= ~(1<<((rsc)&0x7)); } while(0)
#endif //GEX_F072_RSC_ENUM_H

@ -223,6 +223,8 @@ static void ini_preamble(IniWriter *iw, const char *filename)
iw_comment(iw, "Close the LOCK jumper to save them to Flash."); iw_comment(iw, "Close the LOCK jumper to save them to Flash.");
} }
extern osMutexId mutScratchBufferHandle;
/** /**
* Write system settings to INI (without section) * Write system settings to INI (without section)
*/ */
@ -230,7 +232,11 @@ void settings_build_units_ini(IniWriter *iw)
{ {
ini_preamble(iw, "UNITS.INI"); ini_preamble(iw, "UNITS.INI");
ureg_build_ini(iw); assert_param(osOK == osMutexWait(mutScratchBufferHandle, 5000));
{
ureg_build_ini(iw);
}
assert_param(osOK == osMutexRelease(mutScratchBufferHandle));
} }
/** /**

@ -6,7 +6,7 @@
#include "unit.h" #include "unit.h"
#include "resources.h" #include "resources.h"
char unit_tmp512[512]; char unit_tmp512[UNIT_TMP_LEN];
// Abort partly inited unit // Abort partly inited unit
void clean_failed_unit(Unit *unit) void clean_failed_unit(Unit *unit)

@ -10,13 +10,14 @@
#include "utils/ini_writer.h" #include "utils/ini_writer.h"
#include "utils/payload_builder.h" #include "utils/payload_builder.h"
#include "utils/payload_parser.h" #include "utils/payload_parser.h"
#include "rsc_enum.h"
#define CHECK_TYPE(_unit, _driver) do { \ #define CHECK_TYPE(_unit, _driver) do { \
if ((_unit->driver) != (_driver)) \ if ((_unit->driver) != (_driver)) \
return E_BAD_UNIT_TYPE; \ return E_BAD_UNIT_TYPE; \
} while (0) } while (0)
extern char unit_tmp512[512]; // temporary static buffer - not expected to be accessed asynchronously extern char unit_tmp512[UNIT_TMP_LEN]; // temporary static buffer - not expected to be accessed asynchronously
// TODO add mutex? // TODO add mutex?
typedef struct unit Unit; typedef struct unit Unit;
@ -33,11 +34,17 @@ struct unit {
*/ */
void *data; void *data;
/** Unit call sign for messages */
uint8_t callsign;
/** Unit init status */ /** Unit init status */
error_t status; error_t status;
/** Unit call sign for messages */ /** If RSC not avail. error is caught, the resource is stored here. */
uint8_t callsign; Resource failed_rsc;
/** Bit-map of held resources */
ResourceMap resources;
}; };
/** /**

@ -42,6 +42,7 @@ static int32_t unit_count = -1;
void ureg_add_type(const UnitDriver *driver) void ureg_add_type(const UnitDriver *driver)
{ {
bool suc = true;
assert_param(driver != NULL); assert_param(driver != NULL);
assert_param(driver->name != NULL); assert_param(driver->name != NULL);
@ -57,7 +58,9 @@ void ureg_add_type(const UnitDriver *driver)
assert_param(driver->deInit != NULL); assert_param(driver->deInit != NULL);
assert_param(driver->handleRequest != NULL); assert_param(driver->handleRequest != NULL);
UregEntry *re = malloc_s(sizeof(UregEntry)); UregEntry *re = calloc_ck(1, sizeof(UregEntry), &suc);
assert_param(suc);
re->driver = driver; re->driver = driver;
re->next = NULL; re->next = NULL;
@ -108,7 +111,7 @@ Unit *ureg_instantiate(const char *driver_name)
while (re != NULL) { while (re != NULL) {
if (streq(re->driver->name, driver_name)) { if (streq(re->driver->name, driver_name)) {
// Create new list entry // Create new list entry
UlistEntry *le = malloc_ck(sizeof(UlistEntry), &suc); UlistEntry *le = calloc_ck(1, sizeof(UlistEntry), &suc);
CHECK_SUC(); CHECK_SUC();
le->next = NULL; le->next = NULL;
@ -355,7 +358,22 @@ static void export_unit_do(UlistEntry *li, IniWriter *iw)
iw_section(iw, "%s:%s@%d", pUnit->driver->name, pUnit->name, (int)pUnit->callsign); iw_section(iw, "%s:%s@%d", pUnit->driver->name, pUnit->name, (int)pUnit->callsign);
if (pUnit->status != E_SUCCESS) { if (pUnit->status != E_SUCCESS) {
iw_comment(iw, "!!! %s", error_get_message(pUnit->status)); // temporarily force comments ON
bool sc = SystemSettings.ini_comments;
SystemSettings.ini_comments = true;
{
// special message for failed unit die to resource
if (pUnit->status == E_RESOURCE_NOT_AVAILABLE) {
iw_comment(iw, "!!! %s not available, already held by %s",
rsc_get_name(pUnit->failed_rsc),
rsc_get_owner_name(pUnit->failed_rsc));
}
else {
iw_comment(iw, "!!! %s", error_get_message(pUnit->status));
}
iw_cmt_newline(iw);
}
SystemSettings.ini_comments = sc;
} }
pUnit->driver->cfgWriteIni(pUnit, iw); pUnit->driver->cfgWriteIni(pUnit, iw);
} }
@ -431,42 +449,51 @@ uint32_t ureg_get_num_units(void)
return (uint32_t) unit_count; return (uint32_t) unit_count;
} }
extern osMutexId mutScratchBufferHandle;
/** Deliver message to it's destination unit */ /** Deliver message to it's destination unit */
void ureg_deliver_unit_request(TF_Msg *msg) void ureg_deliver_unit_request(TF_Msg *msg)
{ {
PayloadParser pp = pp_start(msg->data, msg->len, NULL); // we must claim the scratch buffer because it's used by many units internally
uint8_t callsign = pp_u8(&pp); assert_param(osOK == osMutexWait(mutScratchBufferHandle, 5000));
uint8_t command = pp_u8(&pp); {
PayloadParser pp = pp_start(msg->data, msg->len, NULL);
uint8_t callsign = pp_u8(&pp);
uint8_t command = pp_u8(&pp);
// highest bit indicates user wants an extra confirmation on success // highest bit indicates user wants an extra confirmation on success
bool confirmed = (bool) (command & 0x80); bool confirmed = (bool) (command & 0x80);
command &= 0x7F; command &= 0x7F;
if (callsign == 0 || !pp.ok) { if (callsign == 0 || !pp.ok) {
com_respond_error(msg->frame_id, E_MALFORMED_COMMAND); com_respond_error(msg->frame_id, E_MALFORMED_COMMAND);
return; goto quit;
} }
UlistEntry *li = ulist_head; UlistEntry *li = ulist_head;
while (li != NULL) { while (li != NULL) {
Unit *const pUnit = &li->unit; Unit *const pUnit = &li->unit;
if (pUnit->callsign == callsign && pUnit->status == E_SUCCESS) { if (pUnit->callsign == callsign && pUnit->status == E_SUCCESS) {
error_t rv = pUnit->driver->handleRequest(pUnit, msg->frame_id, command, &pp); error_t rv = pUnit->driver->handleRequest(pUnit, msg->frame_id, command, &pp);
// send extra SUCCESS confirmation message. // send extra SUCCESS confirmation message.
// error is expected to have already been reported. // error is expected to have already been reported.
if (rv == E_SUCCESS) { if (rv == E_SUCCESS) {
if (confirmed) com_respond_ok(msg->frame_id); if (confirmed) com_respond_ok(msg->frame_id);
} else { }
com_respond_error(msg->frame_id, rv); else {
com_respond_error(msg->frame_id, rv);
}
goto quit;
} }
return; li = li->next;
} }
li = li->next;
}
// Not found // Not found
com_respond_error(msg->frame_id, E_NO_SUCH_UNIT); com_respond_error(msg->frame_id, E_NO_SUCH_UNIT);
}
quit:
assert_param(osOK == osMutexRelease(mutScratchBufferHandle));
} }
/** Send a response for a unit-list request */ /** Send a response for a unit-list request */
@ -488,7 +515,7 @@ void ureg_report_active_units(TF_ID frame_id)
msglen += count; // one byte per message for the callsign msglen += count; // one byte per message for the callsign
bool suc = true; bool suc = true;
uint8_t *buff = malloc_ck(msglen, &suc); uint8_t *buff = calloc_ck(1, msglen, &suc);
if (!suc) { if (!suc) {
com_respond_error(frame_id, E_OUT_OF_MEM); com_respond_error(frame_id, E_OUT_OF_MEM);
return; return;
@ -514,3 +541,19 @@ void ureg_report_active_units(TF_ID frame_id)
} }
free(buff); free(buff);
} }
Unit *ureg_get_rsc_owner(Resource resource)
{
UlistEntry *li = ulist_head;
while (li != NULL) {
if (RSC_IS_HELD(li->unit.resources, resource)) {
return &li->unit;
}
li = li->next;
}
if (RSC_IS_HELD(UNIT_SYSTEM.resources, resource)) return &UNIT_SYSTEM;
if (RSC_IS_HELD(UNIT_PLATFORM.resources, resource)) return &UNIT_PLATFORM;
return NULL;
}

@ -127,4 +127,12 @@ void ureg_deliver_unit_request(TF_Msg *msg);
*/ */
void ureg_report_active_units(TF_ID frame_id); void ureg_report_active_units(TF_ID frame_id);
/**
* Get unit holding a resource, or NULL.
*
* @param resource
* @return unit
*/
Unit *ureg_get_rsc_owner(Resource resource);
#endif //GEX_UNIT_REGISTRY_H #endif //GEX_UNIT_REGISTRY_H

@ -84,6 +84,9 @@ osStaticMutexDef_t mutTinyFrameTxControlBlock;
osSemaphoreId semVcomTxReadyHandle; osSemaphoreId semVcomTxReadyHandle;
osStaticSemaphoreDef_t semVcomTxReadyControlBlock; osStaticSemaphoreDef_t semVcomTxReadyControlBlock;
osMutexId mutScratchBufferHandle;
osStaticMutexDef_t mutScratchBufferControlBlock;
/* USER CODE BEGIN Variables */ /* USER CODE BEGIN Variables */
/* USER CODE END Variables */ /* USER CODE END Variables */
@ -142,6 +145,9 @@ void MX_FREERTOS_Init(void) {
osMutexStaticDef(mutTinyFrameTx, &mutTinyFrameTxControlBlock); osMutexStaticDef(mutTinyFrameTx, &mutTinyFrameTxControlBlock);
mutTinyFrameTxHandle = osMutexCreate(osMutex(mutTinyFrameTx)); mutTinyFrameTxHandle = osMutexCreate(osMutex(mutTinyFrameTx));
osMutexStaticDef(mutScratchBuffer, &mutScratchBufferControlBlock);
mutScratchBufferHandle = osMutexCreate(osMutex(mutScratchBuffer));
/* USER CODE BEGIN RTOS_MUTEX */ /* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */ /* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */ /* USER CODE END RTOS_MUTEX */

@ -11,6 +11,7 @@ GEX_SRC_DIR = \
User/units/digital_out \ User/units/digital_out \
User/units/digital_in \ User/units/digital_in \
User/units/i2c \ User/units/i2c \
User/units/spi \
User/TinyFrame \ User/TinyFrame \
User/CWPack \ User/CWPack \
User/tasks User/tasks
@ -82,7 +83,7 @@ GEX_CDEFS = $(GEX_CDEFS_BASE) \
-DVERBOSE_ASSERT=1 \ -DVERBOSE_ASSERT=1 \
-DDEBUG_VFS=0 \ -DDEBUG_VFS=0 \
-DDEBUG_FLASH_WRITE=0 \ -DDEBUG_FLASH_WRITE=0 \
-DVERBOSE_HARDFAULT=1 \ -DVERBOSE_HARDFAULT=0 \
-DUSE_STACK_MONITOR=0 \ -DUSE_STACK_MONITOR=0 \
-DUSE_DEBUG_UART=1 -DUSE_DEBUG_UART=1

@ -208,17 +208,17 @@ char * str_pinmask(uint16_t pins, char *buffer)
} else { } else {
if (on) { if (on) {
if (!first) { if (!first) {
b += sprintf(b, ","); b += SPRINTF(b, ",");
} }
if (start == (uint32_t)(i+1)) { if (start == (uint32_t)(i+1)) {
b += sprintf(b, "%"PRIu32, start); b += SPRINTF(b, "%"PRIu32, start);
} }
else if (start == (uint32_t)(i+2)) { else if (start == (uint32_t)(i+2)) {
// exception for 2-long ranges - don't show as range // exception for 2-long ranges - don't show as range
b += sprintf(b, "%"PRIu32",%"PRIu32, start, i + 1); b += SPRINTF(b, "%"PRIu32",%"PRIu32, start, i + 1);
} }
else { else {
b += sprintf(b, "%"PRIu32"-%"PRIu32, start, i + 1); b += SPRINTF(b, "%"PRIu32"-%"PRIu32, start, i + 1);
} }
first = false; first = false;
on = false; on = false;
@ -260,3 +260,55 @@ uint16_t port_pack(uint16_t spread, uint16_t mask)
} }
return result; return result;
} }
void deinit_unit_pins(Unit *unit)
{
for (uint32_t rsc = R_PA0; rsc <= R_PF15; rsc++) {
if (RSC_IS_HELD(unit->resources, rsc)) {
dbg("Freeing pin %s", rsc_get_name((Resource)rsc));
GPIO_TypeDef *port = port_periphs[(rsc-R_PA0) / 16];
uint32_t ll_pin = ll_pins[(rsc-R_PA0)%16];
LL_GPIO_SetPinMode(port, ll_pin, LL_GPIO_MODE_ANALOG);
}
}
}
error_t configure_gpio_alternate(char port_name, uint8_t pin_num, uint32_t af)
{
bool suc = true;
GPIO_TypeDef *port = port2periph(port_name, &suc);
uint32_t ll_pin = pin2ll(pin_num, &suc);
if (!suc) return E_BAD_CONFIG;
if (pin_num < 8)
LL_GPIO_SetAFPin_0_7(port, ll_pin, af);
else
LL_GPIO_SetAFPin_8_15(port, ll_pin, af);
LL_GPIO_SetPinMode(port, ll_pin, LL_GPIO_MODE_ALTERNATE);
return E_SUCCESS;
}
error_t configure_sparse_pins(char port_name, uint16_t mask, GPIO_TypeDef **port_dest, uint32_t mode, uint32_t otype)
{
bool suc = true;
GPIO_TypeDef *port = port2periph(port_name, &suc);
if (!suc) return E_BAD_CONFIG;
for (int i = 0; i < 16; i++) {
if (mask & (1<<i)) {
uint32_t ll_pin = pin2ll((uint8_t) i, &suc);
LL_GPIO_SetPinMode(port, ll_pin, mode);
LL_GPIO_SetPinOutputType(port, ll_pin, otype);
LL_GPIO_SetPinSpeed(port, ll_pin, LL_GPIO_SPEED_FREQ_HIGH);
}
}
if (port_dest != NULL) {
*port_dest = port;
}
return E_SUCCESS;
}

@ -68,4 +68,14 @@ uint16_t port_spread(uint16_t packed, uint16_t mask);
*/ */
uint16_t port_pack(uint16_t spread, uint16_t mask); uint16_t port_pack(uint16_t spread, uint16_t mask);
/**
* Set all GPIO resources held by unit to analog
*
* @param unit - holding unit
*/
void deinit_unit_pins(Unit *unit);
error_t configure_gpio_alternate(char port_name, uint8_t pin_num, uint32_t af);
error_t configure_sparse_pins(char port_name, uint16_t mask, GPIO_TypeDef **port_dest, uint32_t mode, uint32_t otype);
#endif //GEX_PIN_UTILS_H #endif //GEX_PIN_UTILS_H

@ -9,10 +9,11 @@
// -------- Static buffers --------- // -------- Static buffers ---------
#define TSK_STACK_MAIN 220 // USB / VFS task stack size #define TSK_STACK_MAIN 220 // USB / VFS task stack size
#define TSK_STACK_MSG 200 // TF message handler task stack size #define TSK_STACK_MSG 220 // TF message handler task stack size
#define TSK_STACK_JOBRUNNER 80 // Job runner task stack size #define TSK_STACK_JOBRUNNER 80 // Job runner task stack size
#define BULKREAD_MAX_CHUNK 256 // Bulk read buffer #define BULK_READ_BUF_LEN 256 // Buffer for TF bulk reads
#define UNIT_TMP_LEN 512 // Buffer for bulk read and varions internal unit operations
#define FLASH_SAVE_BUF_LEN 256 // Static buffer for saving to flash #define FLASH_SAVE_BUF_LEN 256 // Static buffer for saving to flash

@ -13,6 +13,7 @@
#include "units/neopixel/unit_neopixel.h" #include "units/neopixel/unit_neopixel.h"
#include "units/i2c/unit_i2c.h" #include "units/i2c/unit_i2c.h"
#include "units/test/unit_test.h" #include "units/test/unit_test.h"
#include "units/spi/unit_spi.h"
void plat_init_resources(void) void plat_init_resources(void)
{ {
@ -77,17 +78,18 @@ void plat_init_resources(void)
ureg_add_type(&UNIT_DIN); ureg_add_type(&UNIT_DIN);
ureg_add_type(&UNIT_NEOPIXEL); ureg_add_type(&UNIT_NEOPIXEL);
ureg_add_type(&UNIT_I2C); ureg_add_type(&UNIT_I2C);
ureg_add_type(&UNIT_SPI);
// Free all present resources // Free all present resources
{ {
rsc_free(NULL, R_ADC1); rsc_free(NULL, R_ADC1);
rsc_free(NULL, R_CAN1); // rsc_free(NULL, R_CAN1);
rsc_free_range(NULL, R_COMP1, R_COMP2); // rsc_free_range(NULL, R_COMP1, R_COMP2);
rsc_free(NULL, R_DAC1); rsc_free(NULL, R_DAC1);
rsc_free(NULL, R_HDMI_CEC); // rsc_free(NULL, R_HDMI_CEC);
rsc_free(NULL, R_TSC); // rsc_free(NULL, R_TSC);
rsc_free_range(NULL, R_I2C1, R_I2C2); rsc_free_range(NULL, R_I2C1, R_I2C2);
rsc_free_range(NULL, R_I2S1, R_I2S2); // rsc_free_range(NULL, R_I2S1, R_I2S2);
rsc_free_range(NULL, R_SPI1, R_SPI2); rsc_free_range(NULL, R_SPI1, R_SPI2);
rsc_free_range(NULL, R_TIM1, R_TIM3); rsc_free_range(NULL, R_TIM1, R_TIM3);
rsc_free_range(NULL, R_TIM6, R_TIM7); rsc_free_range(NULL, R_TIM6, R_TIM7);
@ -129,18 +131,18 @@ void plat_init_resources(void)
// Free all present resources // Free all present resources
{ {
rsc_free_range(NULL, R_ADC1, R_ADC4); rsc_free_range(NULL, R_ADC1, R_ADC4);
rsc_free(NULL, R_CAN1); // rsc_free(NULL, R_CAN1);
rsc_free_range(NULL, R_COMP1, R_COMP7); // rsc_free_range(NULL, R_COMP1, R_COMP7);
rsc_free(NULL, R_HDMI_CEC); // rsc_free(NULL, R_HDMI_CEC);
rsc_free(NULL, R_DAC1); rsc_free(NULL, R_DAC1);
rsc_free_range(NULL, R_I2C1, R_I2C2); rsc_free_range(NULL, R_I2C1, R_I2C2);
rsc_free_range(NULL, R_I2S2, R_I2S3); rsc_free_range(NULL, R_I2S2, R_I2S3);
rsc_free_range(NULL, R_OPAMP1, R_OPAMP4); // rsc_free_range(NULL, R_OPAMP1, R_OPAMP4);
rsc_free_range(NULL, R_SPI1, R_SPI3); rsc_free_range(NULL, R_SPI1, R_SPI3);
rsc_free_range(NULL, R_TIM1, R_TIM4); rsc_free_range(NULL, R_TIM1, R_TIM4);
rsc_free_range(NULL, R_TIM6, R_TIM8); rsc_free_range(NULL, R_TIM6, R_TIM8);
rsc_free_range(NULL, R_TIM15, R_TIM17); rsc_free_range(NULL, R_TIM15, R_TIM17);
rsc_free(NULL, R_TSC); // rsc_free(NULL, R_TSC);
rsc_free_range(NULL, R_USART1, R_USART5); rsc_free_range(NULL, R_USART1, R_USART5);
rsc_free_range(NULL, R_PA0, R_PA15); rsc_free_range(NULL, R_PA0, R_PA15);
@ -182,15 +184,15 @@ void plat_init_resources(void)
// Free all present resources // Free all present resources
{ {
rsc_free_range(NULL, R_ADC1, R_ADC3); rsc_free_range(NULL, R_ADC1, R_ADC3);
rsc_free_range(NULL, R_CAN1, R_CAN2); // rsc_free_range(NULL, R_CAN1, R_CAN2);
rsc_free_range(NULL, R_COMP1, R_COMP7); // rsc_free_range(NULL, R_COMP1, R_COMP7);
rsc_free(NULL, R_DAC1); rsc_free(NULL, R_DAC1);
rsc_free(NULL, R_DCMI); // rsc_free(NULL, R_DCMI);
rsc_free(NULL, R_ETH); // rsc_free(NULL, R_ETH);
rsc_free(NULL, R_FSMC); // rsc_free(NULL, R_FSMC);
rsc_free_range(NULL, R_I2C1, R_I2C3); rsc_free_range(NULL, R_I2C1, R_I2C3);
rsc_free_range(NULL, R_I2S2, R_I2S3); rsc_free_range(NULL, R_I2S2, R_I2S3);
rsc_free(NULL, R_SDIO); // rsc_free(NULL, R_SDIO);
rsc_free_range(NULL, R_SPI1, R_SPI3); rsc_free_range(NULL, R_SPI1, R_SPI3);
rsc_free_range(NULL, R_TIM1, R_TIM14); rsc_free_range(NULL, R_TIM1, R_TIM14);
rsc_free_range(NULL, R_USART1, R_USART3); rsc_free_range(NULL, R_USART1, R_USART3);

@ -26,6 +26,7 @@ void TaskMessaging(const void * argument)
xQueueReceive(queRxDataHandle, &slot, osWaitForever); xQueueReceive(queRxDataHandle, &slot, osWaitForever);
assert_param(slot.len>0 && slot.len<=64); // check the len is within bounds assert_param(slot.len>0 && slot.len<=64); // check the len is within bounds
// We need thr scratch buffer for many unit command handlers
TF_Accept(comm, slot.data, slot.len); TF_Accept(comm, slot.data, slot.len);
#if USE_STACK_MONITOR #if USE_STACK_MONITOR
@ -34,4 +35,4 @@ void TaskMessaging(const void * argument)
msgQueHighWaterMark = MAX(msgQueHighWaterMark, count); msgQueHighWaterMark = MAX(msgQueHighWaterMark, count);
#endif #endif
} }
} }

@ -161,23 +161,7 @@ static error_t DI_init(Unit *unit)
/** Tear down the unit */ /** Tear down the unit */
static void DI_deInit(Unit *unit) static void DI_deInit(Unit *unit)
{ {
struct priv *priv = unit->data; // pins are de-inited during teardown
if (unit->status == E_SUCCESS) {
assert_param(priv->port);
bool suc = true;
uint16_t mask = 1;
for (int i = 0; i < 16; i++, mask <<= 1) {
if (priv->pins & mask) {
uint32_t ll_pin = pin2ll((uint8_t) i, &suc);
assert_param(suc); // this should never fail if we got this far
// configure the pin as analog
LL_GPIO_SetPinMode(priv->port, ll_pin, LL_GPIO_MODE_ANALOG);
}
}
}
// Release all resources // Release all resources
rsc_teardown(unit); rsc_teardown(unit);
@ -210,7 +194,7 @@ static error_t DI_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, Pay
case CMD_READ:; case CMD_READ:;
TRY(UU_DI_Read(unit, &packed)); TRY(UU_DI_Read(unit, &packed));
PayloadBuilder pb = pb_start((uint8_t*)unit_tmp512, 64, NULL); PayloadBuilder pb = pb_start((uint8_t*)unit_tmp512, UNIT_TMP_LEN, NULL);
pb_u16(&pb, packed); pb_u16(&pb, packed);
com_respond_buf(frame_id, MSG_SUCCESS, (uint8_t *) unit_tmp512, pb_length(&pb)); com_respond_buf(frame_id, MSG_SUCCESS, (uint8_t *) unit_tmp512, pb_length(&pb));
return E_SUCCESS; return E_SUCCESS;

@ -62,7 +62,7 @@ static error_t DO_loadIni(Unit *unit, const char *key, const char *value)
else if (streq(key, "initial")) { else if (streq(key, "initial")) {
priv->initial = parse_pinmask(value, &suc); priv->initial = parse_pinmask(value, &suc);
} }
else if (streq(key, "opendrain")) { else if (streq(key, "open-drain")) {
priv->open_drain = parse_pinmask(value, &suc); priv->open_drain = parse_pinmask(value, &suc);
} }
else { else {
@ -88,7 +88,7 @@ static void DO_writeIni(Unit *unit, IniWriter *iw)
iw_entry(iw, "initial", "%s", str_pinmask(priv->initial, unit_tmp512)); iw_entry(iw, "initial", "%s", str_pinmask(priv->initial, unit_tmp512));
iw_comment(iw, "Open-drain pins"); iw_comment(iw, "Open-drain pins");
iw_entry(iw, "opendrain", "%s", str_pinmask(priv->open_drain, unit_tmp512)); iw_entry(iw, "open-drain", "%s", str_pinmask(priv->open_drain, unit_tmp512));
} }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -125,18 +125,14 @@ static error_t DO_init(Unit *unit)
// Claim all needed pins // Claim all needed pins
TRY(rsc_claim_gpios(unit, priv->port_name, priv->pins)); TRY(rsc_claim_gpios(unit, priv->port_name, priv->pins));
uint16_t mask = 1; for (int i = 0; i < 16; i++) {
for (int i = 0; i < 16; i++, mask <<= 1) { if (priv->pins & (1 << i)) {
if (priv->pins & mask) {
uint32_t ll_pin = pin2ll((uint8_t) i, &suc); uint32_t ll_pin = pin2ll((uint8_t) i, &suc);
// --- Init hardware --- // --- Init hardware ---
LL_GPIO_SetPinMode(priv->port, ll_pin, LL_GPIO_MODE_OUTPUT); LL_GPIO_SetPinMode(priv->port, ll_pin, LL_GPIO_MODE_OUTPUT);
LL_GPIO_SetPinOutputType(priv->port, ll_pin,
LL_GPIO_SetPinOutputType(priv->port, ll_pin, (priv->open_drain & mask) ? (priv->open_drain & (1 << i)) ? LL_GPIO_OUTPUT_OPENDRAIN : LL_GPIO_OUTPUT_PUSHPULL);
LL_GPIO_OUTPUT_OPENDRAIN :
LL_GPIO_OUTPUT_PUSHPULL);
LL_GPIO_SetPinSpeed(priv->port, ll_pin, LL_GPIO_SPEED_FREQ_HIGH); LL_GPIO_SetPinSpeed(priv->port, ll_pin, LL_GPIO_SPEED_FREQ_HIGH);
} }
} }
@ -151,25 +147,7 @@ static error_t DO_init(Unit *unit)
/** Tear down the unit */ /** Tear down the unit */
static void DO_deInit(Unit *unit) static void DO_deInit(Unit *unit)
{ {
struct priv *priv = unit->data; // pins are de-inited during teardown
// de-init the pins only if inited correctly
if (unit->status == E_SUCCESS) {
assert_param(priv->port);
bool suc = true;
uint16_t mask = 1;
for (int i = 0; i < 16; i++, mask <<= 1) {
if (priv->pins & mask) {
uint32_t ll_pin = pin2ll((uint8_t) i, &suc);
assert_param(suc); // this should never fail if we got this far
// configure the pin as analog
LL_GPIO_SetPinMode(priv->port, ll_pin, LL_GPIO_MODE_ANALOG);
}
}
}
// Release all resources // Release all resources
rsc_teardown(unit); rsc_teardown(unit);
@ -234,9 +212,18 @@ error_t UU_DO_Toggle(Unit *unit, uint16_t packed)
return E_SUCCESS; return E_SUCCESS;
} }
error_t UU_DO_GetPinCount(Unit *unit, uint8_t *count)
{
CHECK_TYPE(unit, &UNIT_DOUT);
struct priv *priv = unit->data;
uint32_t packed = port_pack(0xFFFF, priv->pins);
*count = (uint8_t)(32 - __CLZ(packed));
return E_SUCCESS;
}
enum PinCmd_ { enum PinCmd_ {
CMD_WRITE = 0, CMD_QUERY = 0,
CMD_SET = 1, CMD_SET = 1,
CMD_CLEAR = 2, CMD_CLEAR = 2,
CMD_TOGGLE = 3, CMD_TOGGLE = 3,
@ -245,12 +232,10 @@ enum PinCmd_ {
/** Handle a request message */ /** Handle a request message */
static error_t DO_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp) static error_t DO_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp)
{ {
struct priv *priv = unit->data;
uint16_t packed = pp_u16(pp); uint16_t packed = pp_u16(pp);
switch (command) { switch (command) {
case CMD_WRITE: case CMD_QUERY:
return UU_DO_Write(unit, packed); return UU_DO_Write(unit, packed);
case CMD_SET: case CMD_SET:

@ -9,4 +9,14 @@
extern const UnitDriver UNIT_DOUT; extern const UnitDriver UNIT_DOUT;
error_t UU_DO_Write(Unit *unit, uint16_t packed);
error_t UU_DO_Set(Unit *unit, uint16_t packed);
error_t UU_DO_Clear(Unit *unit, uint16_t packed);
error_t UU_DO_Toggle(Unit *unit, uint16_t packed);
error_t UU_DO_GetPinCount(Unit *unit, uint8_t *count);
#endif //U_DOUT_H #endif //U_DOUT_H

@ -2,6 +2,7 @@
// Created by MightyPork on 2018/01/02. // Created by MightyPork on 2018/01/02.
// //
#include <framework/system_settings.h>
#include "comm/messages.h" #include "comm/messages.h"
#include "unit_base.h" #include "unit_base.h"
#include "utils/avrlibc.h" #include "utils/avrlibc.h"
@ -12,15 +13,17 @@
/** Private data structure */ /** Private data structure */
struct priv { struct priv {
uint8_t periph_num; //!< 1 or 2 uint8_t periph_num; //!< 1 or 2
uint8_t remap; //!< I2C remap option
bool anf; //!< Enable analog noise filter bool anf; //!< Enable analog noise filter
uint8_t dnf; //!< Enable digital noise filter (1-15 ... max spike width) uint8_t dnf; //!< Enable digital noise filter (1-15 ... max spike width)
uint8_t speed; //!< 0 - Standard, 1 - Fast, 2 - Fast+ uint8_t speed; //!< 0 - Standard, 1 - Fast, 2 - Fast+
I2C_TypeDef *periph; I2C_TypeDef *periph;
GPIO_TypeDef *port; // GPIO_TypeDef *port;
uint32_t ll_pin_scl; // uint32_t ll_pin_scl;
uint32_t ll_pin_sda; // uint32_t ll_pin_sda;
}; };
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -37,6 +40,10 @@ static void UI2C_loadBinary(Unit *unit, PayloadParser *pp)
priv->anf = pp_bool(pp); priv->anf = pp_bool(pp);
priv->dnf = pp_u8(pp); priv->dnf = pp_u8(pp);
priv->speed = pp_u8(pp); priv->speed = pp_u8(pp);
if (version >= 1) {
priv->remap = pp_u8(pp);
}
} }
/** Write to a binary buffer for storing in Flash */ /** Write to a binary buffer for storing in Flash */
@ -44,12 +51,13 @@ static void UI2C_writeBinary(Unit *unit, PayloadBuilder *pb)
{ {
struct priv *priv = unit->data; struct priv *priv = unit->data;
pb_u8(pb, 0); // version pb_u8(pb, 1); // version
pb_u8(pb, priv->periph_num); pb_u8(pb, priv->periph_num);
pb_bool(pb, priv->anf); pb_bool(pb, priv->anf);
pb_u8(pb, priv->dnf); pb_u8(pb, priv->dnf);
pb_u8(pb, priv->speed); pb_u8(pb, priv->speed);
pb_u8(pb, priv->remap);
} }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -63,6 +71,9 @@ static error_t UI2C_loadIni(Unit *unit, const char *key, const char *value)
if (streq(key, "device")) { if (streq(key, "device")) {
priv->periph_num = (uint8_t) avr_atoi(value); priv->periph_num = (uint8_t) avr_atoi(value);
} }
else if (streq(key, "remap")) {
priv->remap = (uint8_t) avr_atoi(value);
}
else if (streq(key, "analog-filter")) { else if (streq(key, "analog-filter")) {
priv->anf = str_parse_yn(value, &suc); priv->anf = str_parse_yn(value, &suc);
} }
@ -88,6 +99,22 @@ static void UI2C_writeIni(Unit *unit, IniWriter *iw)
iw_comment(iw, "Peripheral number (I2Cx)"); iw_comment(iw, "Peripheral number (I2Cx)");
iw_entry(iw, "device", "%d", (int)priv->periph_num); iw_entry(iw, "device", "%d", (int)priv->periph_num);
iw_comment(iw, "Pin mappings (SCL,SDA)");
#if GEX_PLAT_F072_DISCOVERY
iw_comment(iw, " I2C1: (0) B6,B7 (1) B8,B9");
iw_comment(iw, " I2C2: (0) B10,B11 (1) B13,B14");
#elif GEX_PLAT_F103_BLUEPILL
#error "NO IMPL"
#elif GEX_PLAT_F303_DISCOVERY
#error "NO IMPL"
#elif GEX_PLAT_F407_DISCOVERY
#error "NO IMPL"
#else
#error "BAD PLATFORM!"
#endif
iw_entry(iw, "remap", "%d", (int)priv->remap);
iw_cmt_newline(iw);
iw_comment(iw, "Speed: 1-Standard, 2-Fast, 3-Fast+"); iw_comment(iw, "Speed: 1-Standard, 2-Fast, 3-Fast+");
iw_entry(iw, "speed", "%d", (int)priv->speed); iw_entry(iw, "speed", "%d", (int)priv->speed);
@ -157,17 +184,38 @@ static error_t UI2C_init(Unit *unit)
#if GEX_PLAT_F072_DISCOVERY #if GEX_PLAT_F072_DISCOVERY
// scl - 6 or 8 for I2C1, 10 for I2C2 // scl - 6 or 8 for I2C1, 10 for I2C2
// sda - 7 or 9 for I2C1, 11 for I2C2 // sda - 7 or 9 for I2C1, 11 for I2C2
portname = 'B';
if (priv->periph_num == 1) { if (priv->periph_num == 1) {
pin_scl = 8; // I2C1
pin_sda = 9; if (priv->remap == 0) {
af_i2c = LL_GPIO_AF_1;
portname = 'B';
pin_scl = 6;
pin_sda = 7;
} else if (priv->remap == 1) {
af_i2c = LL_GPIO_AF_1;
portname = 'B';
pin_scl = 8;
pin_sda = 9;
} else {
return E_BAD_CONFIG;
}
} else { } else {
pin_scl = 10; // I2C2
pin_sda = 12; if (priv->remap == 0) {
af_i2c = LL_GPIO_AF_1;
portname = 'B';
pin_scl = 10;
pin_sda = 11;
} else if (priv->remap == 1) {
af_i2c = LL_GPIO_AF_5;
portname = 'B';
pin_scl = 13;
pin_sda = 14;
} else {
return E_BAD_CONFIG;
}
} }
af_i2c = LL_GPIO_AF_1;
if (priv->speed == 1) if (priv->speed == 1)
timing = 0x00301D2B; // Standard timing = 0x00301D2B; // Standard
else if (priv->speed == 2) else if (priv->speed == 2)
@ -186,32 +234,11 @@ static error_t UI2C_init(Unit *unit)
#endif #endif
// first, we have to claim the pins // first, we have to claim the pins
Resource r_sda = pin2resource(portname, pin_sda, &suc); TRY(rsc_claim_pin(unit, portname, pin_sda));
Resource r_scl = pin2resource(portname, pin_scl, &suc); TRY(rsc_claim_pin(unit, portname, pin_scl));
if (!suc) return E_BAD_CONFIG;
TRY(rsc_claim(unit, r_sda));
TRY(rsc_claim(unit, r_scl));
priv->port = port2periph(portname, &suc);
uint32_t ll_pin_scl = pin2ll(pin_scl, &suc);
uint32_t ll_pin_sda = pin2ll(pin_sda, &suc);
if (!suc) return E_BAD_CONFIG;
// configure AF
if (pin_scl < 8) LL_GPIO_SetAFPin_0_7(priv->port, ll_pin_scl, af_i2c);
else LL_GPIO_SetAFPin_8_15(priv->port, ll_pin_scl, af_i2c);
if (pin_sda < 8) LL_GPIO_SetAFPin_0_7(priv->port, ll_pin_sda, af_i2c);
else LL_GPIO_SetAFPin_8_15(priv->port, ll_pin_sda, af_i2c);
LL_GPIO_SetPinMode(priv->port, ll_pin_scl, LL_GPIO_MODE_ALTERNATE);
LL_GPIO_SetPinMode(priv->port, ll_pin_sda, LL_GPIO_MODE_ALTERNATE);
// set as OpenDrain (this may not be needed - TODO check)
LL_GPIO_SetPinOutputType(priv->port, ll_pin_scl, LL_GPIO_OUTPUT_OPENDRAIN);
LL_GPIO_SetPinOutputType(priv->port, ll_pin_sda, LL_GPIO_OUTPUT_OPENDRAIN);
configure_gpio_alternate(portname, pin_sda, af_i2c);
configure_gpio_alternate(portname, pin_scl, af_i2c);
if (priv->periph_num == 1) { if (priv->periph_num == 1) {
__HAL_RCC_I2C1_CLK_ENABLE(); __HAL_RCC_I2C1_CLK_ENABLE();
@ -222,8 +249,7 @@ static error_t UI2C_init(Unit *unit)
/* Disable the selected I2Cx Peripheral */ /* Disable the selected I2Cx Peripheral */
LL_I2C_Disable(priv->periph); LL_I2C_Disable(priv->periph);
LL_I2C_ConfigFilters(priv->periph, LL_I2C_ConfigFilters(priv->periph,
priv->anf ? LL_I2C_ANALOGFILTER_ENABLE (priv->anf ? LL_I2C_ANALOGFILTER_ENABLE : LL_I2C_ANALOGFILTER_DISABLE),
: LL_I2C_ANALOGFILTER_DISABLE,
priv->dnf); priv->dnf);
LL_I2C_SetTiming(priv->periph, timing); LL_I2C_SetTiming(priv->periph, timing);
@ -244,7 +270,6 @@ static void UI2C_deInit(Unit *unit)
// de-init the pins & peripheral only if inited correctly // de-init the pins & peripheral only if inited correctly
if (unit->status == E_SUCCESS) { if (unit->status == E_SUCCESS) {
assert_param(priv->periph); assert_param(priv->periph);
assert_param(priv->port);
LL_I2C_DeInit(priv->periph); LL_I2C_DeInit(priv->periph);
@ -253,9 +278,6 @@ static void UI2C_deInit(Unit *unit)
} else { } else {
__HAL_RCC_I2C2_CLK_DISABLE(); __HAL_RCC_I2C2_CLK_DISABLE();
} }
LL_GPIO_SetPinMode(priv->port, priv->ll_pin_sda, LL_GPIO_MODE_ANALOG);
LL_GPIO_SetPinMode(priv->port, priv->ll_pin_scl, LL_GPIO_MODE_ANALOG);
} }
// Release all resources // Release all resources
@ -269,7 +291,7 @@ static void UI2C_deInit(Unit *unit)
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
enum PinCmd_ { enum PinCmd_ {
CMD_WRITE = 0, CMD_QUERY = 0,
CMD_READ = 1, CMD_READ = 1,
CMD_WRITE_REG = 2, CMD_WRITE_REG = 2,
CMD_READ_REG = 3, CMD_READ_REG = 3,
@ -380,7 +402,7 @@ error_t UU_I2C_WriteReg(Unit *unit, uint16_t addr, uint8_t regnum, const uint8_t
CHECK_TYPE(unit, &UNIT_I2C); CHECK_TYPE(unit, &UNIT_I2C);
// we have to insert the address first - needs a buffer (XXX realistically the buffer needs 1-4 bytes + addr) // we have to insert the address first - needs a buffer (XXX realistically the buffer needs 1-4 bytes + addr)
PayloadBuilder pb = pb_start((uint8_t*)unit_tmp512, 512, NULL); PayloadBuilder pb = pb_start((uint8_t*)unit_tmp512, UNIT_TMP_LEN, NULL);
pb_u8(&pb, regnum); pb_u8(&pb, regnum);
pb_buf(&pb, bytes, width); pb_buf(&pb, bytes, width);
@ -400,7 +422,7 @@ static error_t UI2C_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P
switch (command) { switch (command) {
/** Write byte(s) - addr:u16, byte(s) */ /** Write byte(s) - addr:u16, byte(s) */
case CMD_WRITE: case CMD_QUERY:
addr = pp_u16(pp); addr = pp_u16(pp);
const uint8_t *bb = pp_tail(pp, &len); const uint8_t *bb = pp_tail(pp, &len);

@ -121,13 +121,7 @@ static error_t Npx_init(Unit *unit)
/** Tear down the unit */ /** Tear down the unit */
static void Npx_deInit(Unit *unit) static void Npx_deInit(Unit *unit)
{ {
struct priv *priv = unit->data; // pins are de-inited during teardown
if (unit->status == E_SUCCESS) {
assert_param(priv->port);
// configure the pin as analog
LL_GPIO_SetPinMode(priv->port, priv->ll_pin, LL_GPIO_MODE_ANALOG);
}
// Release all resources // Release all resources
rsc_teardown(unit); rsc_teardown(unit);

@ -0,0 +1,529 @@
//
// Created by MightyPork on 2018/01/02.
//
#include <framework/system_settings.h>
#include <stm32f072xb.h>
#include "comm/messages.h"
#include "unit_base.h"
#include "utils/avrlibc.h"
#include "unit_spi.h"
// SPI master
/** Private data structure */
struct priv {
uint8_t periph_num; //!< 1 or 2
uint8_t remap; //!< SPI remap option
uint16_t prescaller; //!< Clock prescaller, stored as the dividing factor
bool cpol; //!< CPOL setting
bool cpha; //!< CPHA setting
bool tx_only; //!< If true, Enable only the MOSI line
bool lsb_first; //!< Option to send LSB first
char ssn_port_name; //!< SSN port
uint16_t ssn_pins; //!< SSN pin mask
SPI_TypeDef *periph;
GPIO_TypeDef *ssn_port;
};
// ------------------------------------------------------------------------
/** Load from a binary buffer stored in Flash */
static void USPI_loadBinary(Unit *unit, PayloadParser *pp)
{
struct priv *priv = unit->data;
uint8_t version = pp_u8(pp);
(void)version;
priv->periph_num = pp_u8(pp);
priv->prescaller = pp_u16(pp);
priv->remap = pp_u8(pp);
priv->cpol = pp_bool(pp);
priv->cpha = pp_bool(pp);
priv->tx_only = pp_bool(pp);
priv->lsb_first = pp_bool(pp);
priv->ssn_port_name = pp_char(pp);
priv->ssn_pins = pp_u16(pp);
}
/** Write to a binary buffer for storing in Flash */
static void USPI_writeBinary(Unit *unit, PayloadBuilder *pb)
{
struct priv *priv = unit->data;
pb_u8(pb, 0); // version
pb_u8(pb, priv->periph_num);
pb_u16(pb, priv->prescaller);
pb_u8(pb, priv->remap);
pb_bool(pb, priv->cpol);
pb_bool(pb, priv->cpha);
pb_bool(pb, priv->tx_only);
pb_bool(pb, priv->lsb_first);
pb_char(pb, priv->ssn_port_name);
pb_u16(pb, priv->ssn_pins);
}
// ------------------------------------------------------------------------
/** Parse a key-value pair from the INI file */
static error_t USPI_loadIni(Unit *unit, const char *key, const char *value)
{
bool suc = true;
struct priv *priv = unit->data;
if (streq(key, "device")) {
priv->periph_num = (uint8_t) avr_atoi(value);
}
else if (streq(key, "remap")) {
priv->remap = (uint8_t) avr_atoi(value);
}
else if (streq(key, "prescaller")) {
priv->prescaller = (uint16_t ) avr_atoi(value);
}
else if (streq(key, "cpol")) {
priv->cpol = (bool) avr_atoi(value);
}
else if (streq(key, "cpha")) {
priv->cpha = (bool) avr_atoi(value);
}
else if (streq(key, "tx-only")) {
priv->tx_only = str_parse_yn(value, &suc);
}
else if (streq(key, "lsb-first")) {
priv->lsb_first = str_parse_yn(value, &suc);
}
else if (streq(key, "port")) {
suc = parse_port(value, &priv->ssn_port_name);
}
else if (streq(key, "pins")) {
priv->ssn_pins = parse_pinmask(value, &suc);
}
else {
return E_BAD_KEY;
}
if (!suc) return E_BAD_VALUE;
return E_SUCCESS;
}
/** Generate INI file section for the unit */
static void USPI_writeIni(Unit *unit, IniWriter *iw)
{
struct priv *priv = unit->data;
iw_comment(iw, "Peripheral number (SPIx)");
iw_entry(iw, "device", "%d", (int)priv->periph_num);
// TODO show a legend for peripherals and remaps
iw_comment(iw, "Pin mappings (SCK,MISO,MOSI)");
#if GEX_PLAT_F072_DISCOVERY
iw_comment(iw, " SPI1: (0) A5,A6,A7 (1) B3,B4,B5 (2) E13,E14,E15");
iw_comment(iw, " SPI2: (0) B13,B14,B15 (1) D1,D3,D4");
#elif GEX_PLAT_F103_BLUEPILL
#error "NO IMPL"
#elif GEX_PLAT_F303_DISCOVERY
#error "NO IMPL"
#elif GEX_PLAT_F407_DISCOVERY
#error "NO IMPL"
#else
#error "BAD PLATFORM!"
#endif
iw_entry(iw, "remap", "%d", (int)priv->remap);
iw_cmt_newline(iw);
iw_comment(iw, "Prescaller: 2,4,8,...,256");
iw_entry(iw, "prescaller", "%d", (int)priv->prescaller);
iw_comment(iw, "Clock polarity: 0,1 (clock idle level)");
iw_entry(iw, "cpol", "%d", (int)priv->cpol);
iw_comment(iw, "Clock phase: 0,1 (active edge, 0-first, 1-second)");
iw_entry(iw, "cpha", "%d", (int)priv->cpha);
iw_comment(iw, "Transmit only, disable MISO");
iw_entry(iw, "tx-only", str_yn(priv->tx_only));
iw_comment(iw, "Use LSB-first bit order");
iw_entry(iw, "lsb-first", str_yn(priv->lsb_first));
iw_cmt_newline(iw);
iw_comment(iw, "SS port name");
iw_entry(iw, "port", "%c", priv->ssn_port_name);
iw_comment(iw, "SS pins (comma separated, supports ranges)");
iw_entry(iw, "pins", "%s", str_pinmask(priv->ssn_pins, unit_tmp512));
}
// ------------------------------------------------------------------------
/** Allocate data structure and set defaults */
static error_t USPI_preInit(Unit *unit)
{
bool suc = true;
struct priv *priv = unit->data = calloc_ck(1, sizeof(struct priv), &suc);
if (!suc) return E_OUT_OF_MEM;
// some defaults
priv->periph_num = 1;
priv->prescaller = 64;
priv->remap = 0;
priv->cpol = 0;
priv->cpha = 0;
priv->tx_only = false;
priv->lsb_first = false;
priv->ssn_port_name = 'A';
priv->ssn_pins = 0x0001;
return E_SUCCESS;
}
/** Finalize unit set-up */
static error_t USPI_init(Unit *unit)
{
bool suc = true;
struct priv *priv = unit->data;
if (!(priv->periph_num >= 1 && priv->periph_num <= 2)) {
dbg("!! Bad SPI periph");
// XXX some chips have also SPI3
return E_BAD_CONFIG;
}
// assign and claim the peripheral
if (priv->periph_num == 1) {
TRY(rsc_claim(unit, R_SPI1));
priv->periph = SPI1;
}
else if (priv->periph_num == 2) {
TRY(rsc_claim(unit, R_SPI2));
priv->periph = SPI2;
}
// This is written for F072, other platforms will need adjustments
// Configure SPI own pins (AF)
char spi_portname;
uint8_t pin_miso;
uint8_t pin_mosi;
uint8_t pin_sck;
uint32_t af_spi;
// TODO
#if GEX_PLAT_F072_DISCOVERY
// SPI1 - many options
// sck, miso, mosi, af
if (priv->periph_num == 1) {
// SPI1
if (priv->remap == 0) {
spi_portname = 'A';
af_spi = LL_GPIO_AF_0;
pin_sck = 5;
pin_miso = 6;
pin_mosi = 7;
}
else if (priv->remap == 1) {
spi_portname = 'B';
af_spi = LL_GPIO_AF_0;
pin_sck = 3;
pin_miso = 4;
pin_mosi = 5;
}
else if (priv->remap == 2) {
// large packages only
spi_portname = 'E';
af_spi = LL_GPIO_AF_1;
pin_sck = 13;
pin_miso = 14;
pin_mosi = 15;
}
else {
return E_BAD_CONFIG;
}
}
else {
// SPI2
if (priv->remap == 0) {
spi_portname = 'B';
af_spi = LL_GPIO_AF_0;
pin_sck = 13;
pin_miso = 14;
pin_mosi = 15;
}
else if (priv->remap == 1) {
// NOTE: the's also a incomplete remap in PB and PC
spi_portname = 'D';
af_spi = LL_GPIO_AF_0;
pin_sck = 1;
pin_miso = 3;
pin_mosi = 4;
}
else {
return E_BAD_CONFIG;
}
}
#elif GEX_PLAT_F103_BLUEPILL
#error "NO IMPL"
#elif GEX_PLAT_F303_DISCOVERY
#error "NO IMPL"
#elif GEX_PLAT_F407_DISCOVERY
#error "NO IMPL"
#else
#error "BAD PLATFORM!"
#endif
// first, we have to claim the pins
TRY(rsc_claim_pin(unit, spi_portname, pin_mosi));
TRY(rsc_claim_pin(unit, spi_portname, pin_miso));
TRY(rsc_claim_pin(unit, spi_portname, pin_sck));
configure_gpio_alternate(spi_portname, pin_mosi, af_spi);
configure_gpio_alternate(spi_portname, pin_miso, af_spi);
configure_gpio_alternate(spi_portname, pin_sck, af_spi);
if (priv->periph_num == 1) {
__HAL_RCC_SPI1_CLK_ENABLE();
} else {
__HAL_RCC_SPI2_CLK_ENABLE();
}
// configure SSN GPIOs
{
// Claim all needed pins
TRY(rsc_claim_gpios(unit, priv->ssn_port_name, priv->ssn_pins));
TRY(configure_sparse_pins(priv->ssn_port_name, priv->ssn_pins, &priv->ssn_port,
LL_GPIO_MODE_OUTPUT, LL_GPIO_OUTPUT_PUSHPULL));
// Set the initial state - all high
priv->ssn_port->BSRR = priv->ssn_pins;
}
// Configure SPI - must be configured under reset
LL_SPI_Disable(priv->periph);
{
uint32_t presc = priv->prescaller;
uint32_t lz = __CLZ(presc);
if (lz < 23) lz = 23;
if (lz > 30) lz = 30;
presc = (32 - lz - 2);
LL_SPI_SetBaudRatePrescaler(priv->periph, (presc<<SPI_CR1_BR_Pos)&SPI_CR1_BR_Msk);
LL_SPI_SetClockPolarity(priv->periph, priv->cpol ? LL_SPI_POLARITY_HIGH : LL_SPI_POLARITY_LOW);
LL_SPI_SetClockPhase(priv->periph, priv->cpha ? LL_SPI_PHASE_1EDGE : LL_SPI_PHASE_2EDGE);
LL_SPI_SetTransferDirection(priv->periph, priv->tx_only ? LL_SPI_HALF_DUPLEX_TX : LL_SPI_FULL_DUPLEX);
LL_SPI_SetTransferBitOrder(priv->periph, priv->lsb_first ? LL_SPI_LSB_FIRST : LL_SPI_MSB_FIRST);
LL_SPI_SetNSSMode(priv->periph, LL_SPI_NSS_SOFT);
LL_SPI_SetDataWidth(priv->periph, LL_SPI_DATAWIDTH_8BIT);
LL_SPI_SetRxFIFOThreshold(priv->periph, LL_SPI_RX_FIFO_TH_QUARTER); // trigger RXNE on 1 byte
LL_SPI_SetMode(priv->periph, LL_SPI_MODE_MASTER);
}
LL_SPI_Enable(priv->periph);
return E_SUCCESS;
}
/** Tear down the unit */
static void USPI_deInit(Unit *unit)
{
struct priv *priv = unit->data;
// de-init the pins & peripheral only if inited correctly
if (unit->status == E_SUCCESS) {
assert_param(priv->periph);
LL_SPI_DeInit(priv->periph);
if (priv->periph_num == 1) {
__HAL_RCC_SPI1_CLK_DISABLE();
} else {
__HAL_RCC_SPI2_CLK_DISABLE();
}
}
// Release all resources
rsc_teardown(unit);
// Free memory
free(unit->data);
unit->data = NULL;
}
// ------------------------------------------------------------------------
static error_t spi_wait_until_flag(struct priv *priv, uint32_t flag, bool stop_state)
{
uint32_t t_start = HAL_GetTick();
while (((priv->periph->SR & flag) != 0) != stop_state) {
if (HAL_GetTick() - t_start > 10) {
return E_HW_TIMEOUT;
}
}
return E_SUCCESS;
}
/**
* Perform a low level SPI transfer
*
* @param priv - private object of the SPI unit
* @param request - request buffer
* @param response - response buffer
* @param req_len - request len
* @param resp_skip - response skip bytes
* @param resp_len - response len
* @return success
*/
static error_t xfer_do(struct priv *priv, const uint8_t *request,
uint8_t *response,
uint32_t req_len,
uint32_t resp_skip,
uint32_t resp_len)
{
// TODO this is slow, use DMA
if (response == NULL) resp_len = 0;
// avoid skip causing stretch beyond tx window if nothing is to be read back
if (resp_len == 0) resp_skip = 0;
// in tx only mode, return zeros
if (priv->tx_only && resp_len>0) {
memset(response, 0, resp_len);
}
uint8_t tb;
uint32_t end = MAX(req_len, resp_len + resp_skip);
for (uint32_t i = 0; i < end; i++) {
if (i < req_len) tb = *request++;
else tb = 0;
TRY(spi_wait_until_flag(priv, SPI_SR_TXE, true));
LL_SPI_TransmitData8(priv->periph, tb);
if (!priv->tx_only) {
TRY(spi_wait_until_flag(priv, SPI_SR_RXNE, true));
uint8_t rb = LL_SPI_ReceiveData8(priv->periph);
if (resp_skip > 0) resp_skip--;
else if (resp_len > 0) {
resp_len--;
*response++ = rb;
}
}
}
return E_SUCCESS;
}
error_t UU_SPI_Multicast(Unit *unit, uint16_t slaves,
const uint8_t *request, uint32_t req_len)
{
struct priv *priv= unit->data;
uint16_t mask = port_spread(slaves, priv->ssn_pins);
priv->ssn_port->BRR = mask;
{
TRY(xfer_do(priv, request, NULL, req_len, 0, 0));
}
priv->ssn_port->BSRR = mask;
return E_SUCCESS;
}
error_t UU_SPI_Write(Unit *unit, uint8_t slave_num,
const uint8_t *request, uint8_t *response,
uint32_t req_len, uint32_t resp_skip, uint32_t resp_len)
{
struct priv *priv= unit->data;
uint16_t mask = port_spread((uint16_t) (1 << slave_num), priv->ssn_pins);
priv->ssn_port->BRR = mask;
{
TRY(xfer_do(priv, request, response, req_len, resp_len, resp_skip));
}
priv->ssn_port->BSRR = mask;
return E_SUCCESS;
}
enum PinCmd_ {
CMD_QUERY = 0,
CMD_MULTICAST = 1,
};
/** Handle a request message */
static error_t USPI_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp)
{
uint8_t slave;
uint16_t slaves;
uint16_t req_len;
uint16_t resp_skip;
uint16_t resp_len;
const uint8_t *bb;
uint32_t len;
switch (command) {
/** Write and read byte(s) - slave_num:u8, req_len:u16, resp_skip:u16, resp_len:u16, byte(s) */
case CMD_QUERY:
slave = pp_u8(pp);
resp_skip = pp_u16(pp);
resp_len = pp_u16(pp);
bb = pp_tail(pp, &len);
TRY(UU_SPI_Write(unit, slave,
bb, (uint8_t *) unit_tmp512,
len, resp_skip, resp_len));
// no response if we aren't reading
if (resp_len > 0) {
com_respond_buf(frame_id, MSG_SUCCESS, (uint8_t *) unit_tmp512, resp_len);
}
return E_SUCCESS;
/** Write byte(s) to multiple slaves - slaves:u16, req_len:u16, byte(s) */
case CMD_MULTICAST:
slaves = pp_u16(pp);
bb = pp_tail(pp, &len);
TRY(UU_SPI_Multicast(unit, slaves, bb, len));
return E_SUCCESS;
default:
return E_UNKNOWN_COMMAND;
}
}
// ------------------------------------------------------------------------
/** Unit template */
const UnitDriver UNIT_SPI = {
.name = "SPI",
.description = "SPI master",
// Settings
.preInit = USPI_preInit,
.cfgLoadBinary = USPI_loadBinary,
.cfgWriteBinary = USPI_writeBinary,
.cfgLoadIni = USPI_loadIni,
.cfgWriteIni = USPI_writeIni,
// Init
.init = USPI_init,
.deInit = USPI_deInit,
// Function
.handleRequest = USPI_handleRequest,
};

@ -0,0 +1,63 @@
//
// Created by MightyPork on 2018/01/02.
//
#ifndef GEX_F072_UNIT_SPI_H
#define GEX_F072_UNIT_SPI_H
#include "unit.h"
extern const UnitDriver UNIT_SPI;
// Unit-to-Unit API
/**
* Raw read/write via SPI.
* It's possible to simultaneously write and read, or skip bytes in either direction.
*
* Example scenarios:
*
* req 2, skip 2, read 3
* |<-- req_len --->|
* [ write ][ write ] . . . . . . . .
* . . . . . . . . . [ read ][ read ][ read ]
* |<-- resp_skip ->|<------ resp_len ----->|
*
* req 2, skip 0, read 2
* |<-- req_len --->|
* [ write ][ write ]
* [ read ][ read ]
* |<-- resp_len -->|
*
* @param unit - SPI unit
* @param slave_num - slave number (SS pin index, counted from least significant bit)
* @param request - request bytes buffer
* @param response - response bytes buffer
* @param req_len - number of bytes in the request. Will be right-padded with zeros.
* @param resp_skip - response bytes to discard before starting to capture them
* @param resp_len - number of bytes to capture, after discarding resp_skip received bytes
* @return success
*/
error_t UU_SPI_Write(Unit *unit, uint8_t slave_num,
const uint8_t *request,
uint8_t *response,
uint32_t req_len,
uint32_t resp_skip,
uint32_t resp_len);
/**
* Write to multiple slaves at once.
* This is similar to UU_SPI_Write, but performs no read and works only if the device
* is configured as tx-only.
*
* @param unit - SPI unit
* @param slaves - bitmap of slaves to write (packed bits representing the SSN pins)
* @param request - request bytes buffer
* @param req_len - length of the request buffer
* @return success
*/
error_t UU_SPI_Multicast(Unit *unit, uint16_t slaves,
const uint8_t *request,
uint32_t req_len);
#endif //GEX_F072_UNIT_SPI_H

@ -42,6 +42,7 @@
X(OVERRUN, NULL) /* used in bulk transfer */ \ X(OVERRUN, NULL) /* used in bulk transfer */ \
X(PROTOCOL_BREACH, NULL) /* eating with the wrong spoon */ \ X(PROTOCOL_BREACH, NULL) /* eating with the wrong spoon */ \
X(BAD_UNIT_TYPE, NULL) \ X(BAD_UNIT_TYPE, NULL) \
X(NOT_IMPLEMENTED, NULL) \
\ \
/* VFS user errors (those are meant to be shown to user) */ \ /* VFS user errors (those are meant to be shown to user) */ \
X(ERROR_DURING_TRANSFER, "Error during transfer") \ X(ERROR_DURING_TRANSFER, "Error during transfer") \
@ -57,7 +58,7 @@
X(BAD_KEY, "Unexpected config key") \ X(BAD_KEY, "Unexpected config key") \
X(BAD_VALUE, "Bad config value") \ X(BAD_VALUE, "Bad config value") \
X(OUT_OF_MEM, "Not enough RAM") \ X(OUT_OF_MEM, "Not enough RAM") \
X(RESOURCE_NOT_AVAILABLE, "Required pin / peripheral not available") X(RESOURCE_NOT_AVAILABLE, NULL)
// Keep in sync with the list error_message // Keep in sync with the list error_message
typedef enum { typedef enum {

Loading…
Cancel
Save