Merge branch 'add-spi'
This commit is contained in:
+3
-3
@@ -11,7 +11,7 @@
|
||||
#include "utils/payload_builder.h"
|
||||
|
||||
/** 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
|
||||
@@ -36,7 +36,7 @@ static TF_Result bulkread_lst(TinyFrame *tf, TF_Msg *msg)
|
||||
uint32_t chunk = pp_u32(&pp);
|
||||
|
||||
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
|
||||
bulk->read(bulk, chunk, bulkread_buffer);
|
||||
@@ -81,7 +81,7 @@ void bulkread_start(TinyFrame *tf, BulkRead *bulk)
|
||||
uint8_t buf[8];
|
||||
PayloadBuilder pb = pb_start(buf, 4, NULL);
|
||||
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
|
||||
TF_Msg msg = {
|
||||
|
||||
+99
-29
@@ -6,32 +6,57 @@
|
||||
#include "unit.h"
|
||||
#include "resources.h"
|
||||
#include "pin_utils.h"
|
||||
#include "unit_registry.h"
|
||||
|
||||
static bool rsc_initialized = false;
|
||||
|
||||
// This takes quite a lot of space, we could use u8 and IDs instead if needed
|
||||
struct resouce_slot {
|
||||
const char *name;
|
||||
Unit *owner;
|
||||
} __attribute__((packed));
|
||||
static ResourceMap global_rscmap;
|
||||
|
||||
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[] = {
|
||||
#define X(res_name) #res_name,
|
||||
XX_RESOURCES
|
||||
#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
|
||||
*/
|
||||
void rsc_init_registry(void)
|
||||
{
|
||||
for (int i = 0; i < R_RESOURCE_COUNT; i++) {
|
||||
resources[i].owner = &UNIT_PLATFORM;
|
||||
for(uint32_t i = 0; i < RSCMAP_LEN; i++) {
|
||||
UNIT_PLATFORM.resources[i] = global_rscmap[i] = 0xFF;
|
||||
}
|
||||
|
||||
rsc_initialized = true;
|
||||
}
|
||||
|
||||
@@ -45,18 +70,31 @@ void rsc_init_registry(void)
|
||||
error_t rsc_claim(Unit *unit, Resource rsc)
|
||||
{
|
||||
assert_param(rsc_initialized);
|
||||
assert_param(rsc > R_NONE && rsc < R_RESOURCE_COUNT);
|
||||
assert_param(rsc < RESOURCE_COUNT);
|
||||
assert_param(unit != NULL);
|
||||
|
||||
if (resources[rsc].owner) {
|
||||
//TODO properly report to user
|
||||
dbg("%s claims %s", unit->name, rsc_get_name(rsc));
|
||||
|
||||
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!",
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -71,8 +109,9 @@ error_t rsc_claim(Unit *unit, Resource rsc)
|
||||
error_t rsc_claim_range(Unit *unit, Resource rsc0, Resource rsc1)
|
||||
{
|
||||
assert_param(rsc_initialized);
|
||||
assert_param(rsc0 > R_NONE && rsc0 < R_RESOURCE_COUNT);
|
||||
assert_param(rsc1 > R_NONE && rsc1 < R_RESOURCE_COUNT);
|
||||
assert_param(rsc0 < RESOURCE_COUNT);
|
||||
assert_param(rsc1 < RESOURCE_COUNT);
|
||||
assert_param(rsc0 <= rsc1);
|
||||
assert_param(unit != NULL);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
*
|
||||
@@ -106,11 +154,31 @@ error_t rsc_claim_gpios(Unit *unit, char port_name, uint16_t pins)
|
||||
void rsc_free(Unit *unit, Resource rsc)
|
||||
{
|
||||
assert_param(rsc_initialized);
|
||||
assert_param(rsc > R_NONE && rsc < R_RESOURCE_COUNT);
|
||||
assert_param(rsc < RESOURCE_COUNT);
|
||||
|
||||
if (unit == NULL || resources[rsc].owner == unit) {
|
||||
resources[rsc].owner = NULL;
|
||||
dbg("Free resource %s", rsc_get_name(rsc));
|
||||
|
||||
if (RSC_IS_FREE(global_rscmap, rsc)) return;
|
||||
|
||||
// free it in any unit that holds it
|
||||
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)
|
||||
{
|
||||
assert_param(rsc_initialized);
|
||||
assert_param(rsc0 > R_NONE && rsc0 < R_RESOURCE_COUNT);
|
||||
assert_param(rsc1 > R_NONE && rsc1 < R_RESOURCE_COUNT);
|
||||
assert_param(rsc0 < RESOURCE_COUNT);
|
||||
assert_param(rsc1 < RESOURCE_COUNT);
|
||||
assert_param(rsc0 <= rsc1);
|
||||
|
||||
for (int i = rsc0; i <= rsc1; i++) {
|
||||
if (unit == NULL || resources[i].owner == unit) {
|
||||
resources[i].owner = NULL;
|
||||
}
|
||||
rsc_free(unit, (Resource) i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@@ -143,9 +211,11 @@ void rsc_teardown(Unit *unit)
|
||||
assert_param(rsc_initialized);
|
||||
assert_param(unit != NULL);
|
||||
|
||||
for (int i = R_NONE+1; i < R_RESOURCE_COUNT; i++) {
|
||||
if (resources[i].owner == unit) {
|
||||
resources[i].owner = NULL;
|
||||
}
|
||||
dbg("Tearing down unit %s", unit->name);
|
||||
deinit_unit_pins(unit);
|
||||
|
||||
for (uint32_t i = 0; i < RSCMAP_LEN; i++) {
|
||||
global_rscmap[i] &= ~unit->resources[i];
|
||||
unit->resources[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
+7
-69
@@ -7,80 +7,13 @@
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit.h"
|
||||
#include "rsc_enum.h"
|
||||
|
||||
#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);
|
||||
|
||||
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_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_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
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
extern osMutexId mutScratchBufferHandle;
|
||||
|
||||
/**
|
||||
* Write system settings to INI (without section)
|
||||
*/
|
||||
@@ -230,7 +232,11 @@ void settings_build_units_ini(IniWriter *iw)
|
||||
{
|
||||
ini_preamble(iw, "UNITS.INI");
|
||||
|
||||
ureg_build_ini(iw);
|
||||
assert_param(osOK == osMutexWait(mutScratchBufferHandle, 5000));
|
||||
{
|
||||
ureg_build_ini(iw);
|
||||
}
|
||||
assert_param(osOK == osMutexRelease(mutScratchBufferHandle));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
#include "unit.h"
|
||||
#include "resources.h"
|
||||
|
||||
char unit_tmp512[512];
|
||||
char unit_tmp512[UNIT_TMP_LEN];
|
||||
|
||||
// Abort partly inited unit
|
||||
void clean_failed_unit(Unit *unit)
|
||||
|
||||
+10
-3
@@ -10,13 +10,14 @@
|
||||
#include "utils/ini_writer.h"
|
||||
#include "utils/payload_builder.h"
|
||||
#include "utils/payload_parser.h"
|
||||
#include "rsc_enum.h"
|
||||
|
||||
#define CHECK_TYPE(_unit, _driver) do { \
|
||||
if ((_unit->driver) != (_driver)) \
|
||||
return E_BAD_UNIT_TYPE; \
|
||||
} 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?
|
||||
|
||||
typedef struct unit Unit;
|
||||
@@ -33,11 +34,17 @@ struct unit {
|
||||
*/
|
||||
void *data;
|
||||
|
||||
/** Unit call sign for messages */
|
||||
uint8_t callsign;
|
||||
|
||||
/** Unit init status */
|
||||
error_t status;
|
||||
|
||||
/** Unit call sign for messages */
|
||||
uint8_t callsign;
|
||||
/** If RSC not avail. error is caught, the resource is stored here. */
|
||||
Resource failed_rsc;
|
||||
|
||||
/** Bit-map of held resources */
|
||||
ResourceMap resources;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+76
-33
@@ -42,6 +42,7 @@ static int32_t unit_count = -1;
|
||||
|
||||
void ureg_add_type(const UnitDriver *driver)
|
||||
{
|
||||
bool suc = true;
|
||||
assert_param(driver != 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->handleRequest != NULL);
|
||||
|
||||
UregEntry *re = malloc_s(sizeof(UregEntry));
|
||||
UregEntry *re = calloc_ck(1, sizeof(UregEntry), &suc);
|
||||
assert_param(suc);
|
||||
|
||||
re->driver = driver;
|
||||
re->next = NULL;
|
||||
|
||||
@@ -108,7 +111,7 @@ Unit *ureg_instantiate(const char *driver_name)
|
||||
while (re != NULL) {
|
||||
if (streq(re->driver->name, driver_name)) {
|
||||
// Create new list entry
|
||||
UlistEntry *le = malloc_ck(sizeof(UlistEntry), &suc);
|
||||
UlistEntry *le = calloc_ck(1, sizeof(UlistEntry), &suc);
|
||||
CHECK_SUC();
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
@@ -431,42 +449,51 @@ uint32_t ureg_get_num_units(void)
|
||||
return (uint32_t) unit_count;
|
||||
}
|
||||
|
||||
extern osMutexId mutScratchBufferHandle;
|
||||
|
||||
/** Deliver message to it's destination unit */
|
||||
void ureg_deliver_unit_request(TF_Msg *msg)
|
||||
{
|
||||
PayloadParser pp = pp_start(msg->data, msg->len, NULL);
|
||||
uint8_t callsign = pp_u8(&pp);
|
||||
uint8_t command = pp_u8(&pp);
|
||||
// we must claim the scratch buffer because it's used by many units internally
|
||||
assert_param(osOK == osMutexWait(mutScratchBufferHandle, 5000));
|
||||
{
|
||||
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
|
||||
bool confirmed = (bool) (command & 0x80);
|
||||
command &= 0x7F;
|
||||
// highest bit indicates user wants an extra confirmation on success
|
||||
bool confirmed = (bool) (command & 0x80);
|
||||
command &= 0x7F;
|
||||
|
||||
if (callsign == 0 || !pp.ok) {
|
||||
com_respond_error(msg->frame_id, E_MALFORMED_COMMAND);
|
||||
return;
|
||||
}
|
||||
|
||||
UlistEntry *li = ulist_head;
|
||||
while (li != NULL) {
|
||||
Unit *const pUnit = &li->unit;
|
||||
if (pUnit->callsign == callsign && pUnit->status == E_SUCCESS) {
|
||||
error_t rv = pUnit->driver->handleRequest(pUnit, msg->frame_id, command, &pp);
|
||||
|
||||
// send extra SUCCESS confirmation message.
|
||||
// error is expected to have already been reported.
|
||||
if (rv == E_SUCCESS) {
|
||||
if (confirmed) com_respond_ok(msg->frame_id);
|
||||
} else {
|
||||
com_respond_error(msg->frame_id, rv);
|
||||
}
|
||||
return;
|
||||
if (callsign == 0 || !pp.ok) {
|
||||
com_respond_error(msg->frame_id, E_MALFORMED_COMMAND);
|
||||
goto quit;
|
||||
}
|
||||
li = li->next;
|
||||
}
|
||||
|
||||
// Not found
|
||||
com_respond_error(msg->frame_id, E_NO_SUCH_UNIT);
|
||||
UlistEntry *li = ulist_head;
|
||||
while (li != NULL) {
|
||||
Unit *const pUnit = &li->unit;
|
||||
if (pUnit->callsign == callsign && pUnit->status == E_SUCCESS) {
|
||||
error_t rv = pUnit->driver->handleRequest(pUnit, msg->frame_id, command, &pp);
|
||||
|
||||
// send extra SUCCESS confirmation message.
|
||||
// error is expected to have already been reported.
|
||||
if (rv == E_SUCCESS) {
|
||||
if (confirmed) com_respond_ok(msg->frame_id);
|
||||
}
|
||||
else {
|
||||
com_respond_error(msg->frame_id, rv);
|
||||
}
|
||||
goto quit;
|
||||
}
|
||||
li = li->next;
|
||||
}
|
||||
|
||||
// Not found
|
||||
com_respond_error(msg->frame_id, E_NO_SUCH_UNIT);
|
||||
}
|
||||
quit:
|
||||
assert_param(osOK == osMutexRelease(mutScratchBufferHandle));
|
||||
}
|
||||
|
||||
/** 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
|
||||
|
||||
bool suc = true;
|
||||
uint8_t *buff = malloc_ck(msglen, &suc);
|
||||
uint8_t *buff = calloc_ck(1, msglen, &suc);
|
||||
if (!suc) {
|
||||
com_respond_error(frame_id, E_OUT_OF_MEM);
|
||||
return;
|
||||
@@ -514,3 +541,19 @@ void ureg_report_active_units(TF_ID frame_id)
|
||||
}
|
||||
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);
|
||||
|
||||
/**
|
||||
* Get unit holding a resource, or NULL.
|
||||
*
|
||||
* @param resource
|
||||
* @return unit
|
||||
*/
|
||||
Unit *ureg_get_rsc_owner(Resource resource);
|
||||
|
||||
#endif //GEX_UNIT_REGISTRY_H
|
||||
|
||||
@@ -84,6 +84,9 @@ osStaticMutexDef_t mutTinyFrameTxControlBlock;
|
||||
osSemaphoreId semVcomTxReadyHandle;
|
||||
osStaticSemaphoreDef_t semVcomTxReadyControlBlock;
|
||||
|
||||
osMutexId mutScratchBufferHandle;
|
||||
osStaticMutexDef_t mutScratchBufferControlBlock;
|
||||
|
||||
/* USER CODE BEGIN Variables */
|
||||
|
||||
/* USER CODE END Variables */
|
||||
@@ -142,6 +145,9 @@ void MX_FREERTOS_Init(void) {
|
||||
osMutexStaticDef(mutTinyFrameTx, &mutTinyFrameTxControlBlock);
|
||||
mutTinyFrameTxHandle = osMutexCreate(osMutex(mutTinyFrameTx));
|
||||
|
||||
osMutexStaticDef(mutScratchBuffer, &mutScratchBufferControlBlock);
|
||||
mutScratchBufferHandle = osMutexCreate(osMutex(mutScratchBuffer));
|
||||
|
||||
/* USER CODE BEGIN RTOS_MUTEX */
|
||||
/* add mutexes, ... */
|
||||
/* USER CODE END RTOS_MUTEX */
|
||||
|
||||
@@ -11,6 +11,7 @@ GEX_SRC_DIR = \
|
||||
User/units/digital_out \
|
||||
User/units/digital_in \
|
||||
User/units/i2c \
|
||||
User/units/spi \
|
||||
User/TinyFrame \
|
||||
User/CWPack \
|
||||
User/tasks
|
||||
@@ -82,7 +83,7 @@ GEX_CDEFS = $(GEX_CDEFS_BASE) \
|
||||
-DVERBOSE_ASSERT=1 \
|
||||
-DDEBUG_VFS=0 \
|
||||
-DDEBUG_FLASH_WRITE=0 \
|
||||
-DVERBOSE_HARDFAULT=1 \
|
||||
-DVERBOSE_HARDFAULT=0 \
|
||||
-DUSE_STACK_MONITOR=0 \
|
||||
-DUSE_DEBUG_UART=1
|
||||
|
||||
|
||||
+56
-4
@@ -208,17 +208,17 @@ char * str_pinmask(uint16_t pins, char *buffer)
|
||||
} else {
|
||||
if (on) {
|
||||
if (!first) {
|
||||
b += sprintf(b, ",");
|
||||
b += SPRINTF(b, ",");
|
||||
}
|
||||
if (start == (uint32_t)(i+1)) {
|
||||
b += sprintf(b, "%"PRIu32, start);
|
||||
b += SPRINTF(b, "%"PRIu32, start);
|
||||
}
|
||||
else if (start == (uint32_t)(i+2)) {
|
||||
// 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 {
|
||||
b += sprintf(b, "%"PRIu32"-%"PRIu32, start, i + 1);
|
||||
b += SPRINTF(b, "%"PRIu32"-%"PRIu32, start, i + 1);
|
||||
}
|
||||
first = false;
|
||||
on = false;
|
||||
@@ -260,3 +260,55 @@ uint16_t port_pack(uint16_t spread, uint16_t mask)
|
||||
}
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
@@ -9,10 +9,11 @@
|
||||
|
||||
// -------- Static buffers ---------
|
||||
#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 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
|
||||
|
||||
|
||||
+18
-16
@@ -13,6 +13,7 @@
|
||||
#include "units/neopixel/unit_neopixel.h"
|
||||
#include "units/i2c/unit_i2c.h"
|
||||
#include "units/test/unit_test.h"
|
||||
#include "units/spi/unit_spi.h"
|
||||
|
||||
void plat_init_resources(void)
|
||||
{
|
||||
@@ -77,17 +78,18 @@ void plat_init_resources(void)
|
||||
ureg_add_type(&UNIT_DIN);
|
||||
ureg_add_type(&UNIT_NEOPIXEL);
|
||||
ureg_add_type(&UNIT_I2C);
|
||||
ureg_add_type(&UNIT_SPI);
|
||||
|
||||
// Free all present resources
|
||||
{
|
||||
rsc_free(NULL, R_ADC1);
|
||||
rsc_free(NULL, R_CAN1);
|
||||
rsc_free_range(NULL, R_COMP1, R_COMP2);
|
||||
// rsc_free(NULL, R_CAN1);
|
||||
// rsc_free_range(NULL, R_COMP1, R_COMP2);
|
||||
rsc_free(NULL, R_DAC1);
|
||||
rsc_free(NULL, R_HDMI_CEC);
|
||||
rsc_free(NULL, R_TSC);
|
||||
// rsc_free(NULL, R_HDMI_CEC);
|
||||
// rsc_free(NULL, R_TSC);
|
||||
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_TIM1, R_TIM3);
|
||||
rsc_free_range(NULL, R_TIM6, R_TIM7);
|
||||
@@ -129,18 +131,18 @@ void plat_init_resources(void)
|
||||
// Free all present resources
|
||||
{
|
||||
rsc_free_range(NULL, R_ADC1, R_ADC4);
|
||||
rsc_free(NULL, R_CAN1);
|
||||
rsc_free_range(NULL, R_COMP1, R_COMP7);
|
||||
rsc_free(NULL, R_HDMI_CEC);
|
||||
// rsc_free(NULL, R_CAN1);
|
||||
// rsc_free_range(NULL, R_COMP1, R_COMP7);
|
||||
// rsc_free(NULL, R_HDMI_CEC);
|
||||
rsc_free(NULL, R_DAC1);
|
||||
rsc_free_range(NULL, R_I2C1, R_I2C2);
|
||||
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_TIM1, R_TIM4);
|
||||
rsc_free_range(NULL, R_TIM6, R_TIM8);
|
||||
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_PA0, R_PA15);
|
||||
@@ -182,15 +184,15 @@ void plat_init_resources(void)
|
||||
// Free all present resources
|
||||
{
|
||||
rsc_free_range(NULL, R_ADC1, R_ADC3);
|
||||
rsc_free_range(NULL, R_CAN1, R_CAN2);
|
||||
rsc_free_range(NULL, R_COMP1, R_COMP7);
|
||||
// rsc_free_range(NULL, R_CAN1, R_CAN2);
|
||||
// rsc_free_range(NULL, R_COMP1, R_COMP7);
|
||||
rsc_free(NULL, R_DAC1);
|
||||
rsc_free(NULL, R_DCMI);
|
||||
rsc_free(NULL, R_ETH);
|
||||
rsc_free(NULL, R_FSMC);
|
||||
// rsc_free(NULL, R_DCMI);
|
||||
// rsc_free(NULL, R_ETH);
|
||||
// rsc_free(NULL, R_FSMC);
|
||||
rsc_free_range(NULL, R_I2C1, R_I2C3);
|
||||
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_TIM1, R_TIM14);
|
||||
rsc_free_range(NULL, R_USART1, R_USART3);
|
||||
|
||||
+2
-1
@@ -26,6 +26,7 @@ void TaskMessaging(const void * argument)
|
||||
xQueueReceive(queRxDataHandle, &slot, osWaitForever);
|
||||
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);
|
||||
|
||||
#if USE_STACK_MONITOR
|
||||
@@ -34,4 +35,4 @@ void TaskMessaging(const void * argument)
|
||||
msgQueHighWaterMark = MAX(msgQueHighWaterMark, count);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,23 +161,7 @@ static error_t DI_init(Unit *unit)
|
||||
/** Tear down the unit */
|
||||
static void DI_deInit(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
// pins are de-inited during teardown
|
||||
|
||||
// Release all resources
|
||||
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:;
|
||||
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);
|
||||
com_respond_buf(frame_id, MSG_SUCCESS, (uint8_t *) unit_tmp512, pb_length(&pb));
|
||||
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")) {
|
||||
priv->initial = parse_pinmask(value, &suc);
|
||||
}
|
||||
else if (streq(key, "opendrain")) {
|
||||
else if (streq(key, "open-drain")) {
|
||||
priv->open_drain = parse_pinmask(value, &suc);
|
||||
}
|
||||
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_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
|
||||
TRY(rsc_claim_gpios(unit, priv->port_name, priv->pins));
|
||||
|
||||
uint16_t mask = 1;
|
||||
for (int i = 0; i < 16; i++, mask <<= 1) {
|
||||
if (priv->pins & mask) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (priv->pins & (1 << i)) {
|
||||
uint32_t ll_pin = pin2ll((uint8_t) i, &suc);
|
||||
|
||||
// --- Init hardware ---
|
||||
LL_GPIO_SetPinMode(priv->port, ll_pin, LL_GPIO_MODE_OUTPUT);
|
||||
|
||||
LL_GPIO_SetPinOutputType(priv->port, ll_pin, (priv->open_drain & mask) ?
|
||||
LL_GPIO_OUTPUT_OPENDRAIN :
|
||||
LL_GPIO_OUTPUT_PUSHPULL);
|
||||
|
||||
LL_GPIO_SetPinOutputType(priv->port, ll_pin,
|
||||
(priv->open_drain & (1 << i)) ? LL_GPIO_OUTPUT_OPENDRAIN : LL_GPIO_OUTPUT_PUSHPULL);
|
||||
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 */
|
||||
static void DO_deInit(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
// pins are de-inited during teardown
|
||||
|
||||
// Release all resources
|
||||
rsc_teardown(unit);
|
||||
@@ -234,9 +212,18 @@ error_t UU_DO_Toggle(Unit *unit, uint16_t packed)
|
||||
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_ {
|
||||
CMD_WRITE = 0,
|
||||
CMD_QUERY = 0,
|
||||
CMD_SET = 1,
|
||||
CMD_CLEAR = 2,
|
||||
CMD_TOGGLE = 3,
|
||||
@@ -245,12 +232,10 @@ enum PinCmd_ {
|
||||
/** Handle a request message */
|
||||
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);
|
||||
|
||||
switch (command) {
|
||||
case CMD_WRITE:
|
||||
case CMD_QUERY:
|
||||
return UU_DO_Write(unit, packed);
|
||||
|
||||
case CMD_SET:
|
||||
|
||||
@@ -9,4 +9,14 @@
|
||||
|
||||
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
|
||||
|
||||
+67
-45
@@ -2,6 +2,7 @@
|
||||
// Created by MightyPork on 2018/01/02.
|
||||
//
|
||||
|
||||
#include <framework/system_settings.h>
|
||||
#include "comm/messages.h"
|
||||
#include "unit_base.h"
|
||||
#include "utils/avrlibc.h"
|
||||
@@ -12,15 +13,17 @@
|
||||
/** Private data structure */
|
||||
struct priv {
|
||||
uint8_t periph_num; //!< 1 or 2
|
||||
uint8_t remap; //!< I2C remap option
|
||||
|
||||
bool anf; //!< Enable analog noise filter
|
||||
uint8_t dnf; //!< Enable digital noise filter (1-15 ... max spike width)
|
||||
uint8_t speed; //!< 0 - Standard, 1 - Fast, 2 - Fast+
|
||||
|
||||
I2C_TypeDef *periph;
|
||||
|
||||
GPIO_TypeDef *port;
|
||||
uint32_t ll_pin_scl;
|
||||
uint32_t ll_pin_sda;
|
||||
// GPIO_TypeDef *port;
|
||||
// uint32_t ll_pin_scl;
|
||||
// uint32_t ll_pin_sda;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
@@ -37,6 +40,10 @@ static void UI2C_loadBinary(Unit *unit, PayloadParser *pp)
|
||||
priv->anf = pp_bool(pp);
|
||||
priv->dnf = 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 */
|
||||
@@ -44,12 +51,13 @@ static void UI2C_writeBinary(Unit *unit, PayloadBuilder *pb)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
pb_u8(pb, 0); // version
|
||||
pb_u8(pb, 1); // version
|
||||
|
||||
pb_u8(pb, priv->periph_num);
|
||||
pb_bool(pb, priv->anf);
|
||||
pb_u8(pb, priv->dnf);
|
||||
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")) {
|
||||
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")) {
|
||||
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_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_entry(iw, "speed", "%d", (int)priv->speed);
|
||||
|
||||
@@ -157,17 +184,38 @@ static error_t UI2C_init(Unit *unit)
|
||||
#if GEX_PLAT_F072_DISCOVERY
|
||||
// scl - 6 or 8 for I2C1, 10 for I2C2
|
||||
// sda - 7 or 9 for I2C1, 11 for I2C2
|
||||
portname = 'B';
|
||||
|
||||
if (priv->periph_num == 1) {
|
||||
pin_scl = 8;
|
||||
pin_sda = 9;
|
||||
// I2C1
|
||||
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 {
|
||||
pin_scl = 10;
|
||||
pin_sda = 12;
|
||||
// I2C2
|
||||
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)
|
||||
timing = 0x00301D2B; // Standard
|
||||
else if (priv->speed == 2)
|
||||
@@ -186,32 +234,11 @@ static error_t UI2C_init(Unit *unit)
|
||||
#endif
|
||||
|
||||
// first, we have to claim the pins
|
||||
Resource r_sda = pin2resource(portname, pin_sda, &suc);
|
||||
Resource r_scl = pin2resource(portname, pin_scl, &suc);
|
||||
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);
|
||||
TRY(rsc_claim_pin(unit, portname, pin_sda));
|
||||
TRY(rsc_claim_pin(unit, portname, pin_scl));
|
||||
|
||||
configure_gpio_alternate(portname, pin_sda, af_i2c);
|
||||
configure_gpio_alternate(portname, pin_scl, af_i2c);
|
||||
|
||||
if (priv->periph_num == 1) {
|
||||
__HAL_RCC_I2C1_CLK_ENABLE();
|
||||
@@ -222,8 +249,7 @@ static error_t UI2C_init(Unit *unit)
|
||||
/* Disable the selected I2Cx Peripheral */
|
||||
LL_I2C_Disable(priv->periph);
|
||||
LL_I2C_ConfigFilters(priv->periph,
|
||||
priv->anf ? LL_I2C_ANALOGFILTER_ENABLE
|
||||
: LL_I2C_ANALOGFILTER_DISABLE,
|
||||
(priv->anf ? LL_I2C_ANALOGFILTER_ENABLE : LL_I2C_ANALOGFILTER_DISABLE),
|
||||
priv->dnf);
|
||||
|
||||
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
|
||||
if (unit->status == E_SUCCESS) {
|
||||
assert_param(priv->periph);
|
||||
assert_param(priv->port);
|
||||
|
||||
LL_I2C_DeInit(priv->periph);
|
||||
|
||||
@@ -253,9 +278,6 @@ static void UI2C_deInit(Unit *unit)
|
||||
} else {
|
||||
__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
|
||||
@@ -269,7 +291,7 @@ static void UI2C_deInit(Unit *unit)
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
enum PinCmd_ {
|
||||
CMD_WRITE = 0,
|
||||
CMD_QUERY = 0,
|
||||
CMD_READ = 1,
|
||||
CMD_WRITE_REG = 2,
|
||||
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);
|
||||
|
||||
// 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_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) {
|
||||
/** Write byte(s) - addr:u16, byte(s) */
|
||||
case CMD_WRITE:
|
||||
case CMD_QUERY:
|
||||
addr = pp_u16(pp);
|
||||
const uint8_t *bb = pp_tail(pp, &len);
|
||||
|
||||
|
||||
@@ -121,13 +121,7 @@ static error_t Npx_init(Unit *unit)
|
||||
/** Tear down the unit */
|
||||
static void Npx_deInit(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
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);
|
||||
}
|
||||
// pins are de-inited during teardown
|
||||
|
||||
// Release all resources
|
||||
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
|
||||
+2
-1
@@ -42,6 +42,7 @@
|
||||
X(OVERRUN, NULL) /* used in bulk transfer */ \
|
||||
X(PROTOCOL_BREACH, NULL) /* eating with the wrong spoon */ \
|
||||
X(BAD_UNIT_TYPE, NULL) \
|
||||
X(NOT_IMPLEMENTED, NULL) \
|
||||
\
|
||||
/* VFS user errors (those are meant to be shown to user) */ \
|
||||
X(ERROR_DURING_TRANSFER, "Error during transfer") \
|
||||
@@ -57,7 +58,7 @@
|
||||
X(BAD_KEY, "Unexpected config key") \
|
||||
X(BAD_VALUE, "Bad config value") \
|
||||
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
|
||||
typedef enum {
|
||||
|
||||
Reference in New Issue
Block a user