From f2d3202de9a5847923f72afe2969eb7ccd7342c7 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 28 Jan 2016 00:14:36 -0800 Subject: altos/chaoskey: Add support for flipping between raw and cooked bits Plug the 'force bootloader' thing onto the board while it's running and it will generate raw bits instead of running them through the CRC to whiten. Useful for validating the raw hardware. Signed-off-by: Keith Packard --- src/drivers/ao_trng_send.c | 55 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 11 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_trng_send.c b/src/drivers/ao_trng_send.c index bac6035c..64c016b2 100644 --- a/src/drivers/ao_trng_send.c +++ b/src/drivers/ao_trng_send.c @@ -19,17 +19,48 @@ #include #include #include +#include static void -ao_trng_send(void) +ao_trng_send_raw(uint16_t *buf) +{ + uint16_t i; + uint16_t t; + uint16_t *rnd = (uint16_t *) ao_adc_ring; + + t = ao_adc_get(AO_USB_IN_SIZE>>1); /* one 16-bit value per two output bytes */ + for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) { + *buf++ = rnd[t]; + t = (t + 1) & (AO_ADC_RING_SIZE - 1); + } +} + +static void +ao_trng_send_cooked(uint16_t *buf) { - static uint16_t *buffer[2]; - int usb_buf_id; uint16_t i; - uint16_t *buf; uint16_t t; uint32_t *rnd = (uint32_t *) ao_adc_ring; + t = ao_adc_get(AO_USB_IN_SIZE) >> 1; /* one 16-bit value per output byte */ + for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) { + *buf++ = ao_crc_in_32_out_16(rnd[t]); + t = (t + 1) & ((AO_ADC_RING_SIZE>>1) - 1); + } +} + +static inline int +ao_send_raw(void) +{ + return !ao_gpio_get(AO_RAW_PORT, AO_RAW_BIT, AO_RAW_PIN); +} + +static void +ao_trng_send(void) +{ + static uint16_t *buffer[2]; + int usb_buf_id; + if (!buffer[0]) { buffer[0] = ao_usb_alloc(); buffer[1] = ao_usb_alloc(); @@ -42,15 +73,16 @@ ao_trng_send(void) ao_crc_reset(); for (;;) { - ao_led_on(AO_LED_TRNG_ACTIVE); - t = ao_adc_get(AO_USB_IN_SIZE) >> 1; /* one 16-bit value per output byte */ - buf = buffer[usb_buf_id]; - for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) { - *buf++ = ao_crc_in_32_out_16(rnd[t]); - t = (t + 1) & ((AO_ADC_RING_SIZE>>1) - 1); + if (ao_send_raw()) { + ao_led_on(AO_LED_TRNG_RAW); + ao_trng_send_raw(buffer[usb_buf_id]); + ao_led_off(AO_LED_TRNG_RAW); + } else { + ao_led_on(AO_LED_TRNG_COOKED); + ao_trng_send_cooked(buffer[usb_buf_id]); + ao_led_off(AO_LED_TRNG_COOKED); } ao_adc_ack(AO_USB_IN_SIZE); - ao_led_off(AO_LED_TRNG_ACTIVE); ao_usb_write(buffer[usb_buf_id], AO_USB_IN_SIZE); usb_buf_id = 1-usb_buf_id; } @@ -61,5 +93,6 @@ static struct ao_task ao_trng_send_task; void ao_trng_send_init(void) { + ao_enable_input(AO_RAW_PORT, AO_RAW_BIT, AO_EXTI_MODE_PULL_UP); ao_add_task(&ao_trng_send_task, ao_trng_send, "trng_send"); } -- cgit v1.2.3 From 35407e664886bed21dcef7764843aac03be8490c Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 28 Jan 2016 13:58:43 -0800 Subject: altos/chaoskey: Delay ADC reading for 250ms at startup This lets the HV supply stabilize before we start sampling values. Signed-off-by: Keith Packard --- src/drivers/ao_trng_send.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/drivers') diff --git a/src/drivers/ao_trng_send.c b/src/drivers/ao_trng_send.c index 64c016b2..99994900 100644 --- a/src/drivers/ao_trng_send.c +++ b/src/drivers/ao_trng_send.c @@ -72,6 +72,11 @@ ao_trng_send(void) ao_crc_reset(); + /* Delay long enough for the HV power supply to stabilize so that the + * first bits we read aren't of poor quality + */ + ao_delay(AO_MS_TO_TICKS(250)); + for (;;) { if (ao_send_raw()) { ao_led_on(AO_LED_TRNG_RAW); -- cgit v1.2.3 From 235198b85f1583d2792c7028decace61d1b4229e Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 7 Feb 2016 00:14:22 +1100 Subject: altos: Add power management to TRNG driver Support suspend/resume of the TRNG power supply, delaying after resume to wait for it to stabilize. Signed-off-by: Keith Packard --- src/drivers/ao_trng_send.c | 46 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_trng_send.c b/src/drivers/ao_trng_send.c index 99994900..63366d3e 100644 --- a/src/drivers/ao_trng_send.c +++ b/src/drivers/ao_trng_send.c @@ -20,6 +20,9 @@ #include #include #include +#include + +static uint8_t trng_running; static void ao_trng_send_raw(uint16_t *buf) @@ -72,12 +75,15 @@ ao_trng_send(void) ao_crc_reset(); - /* Delay long enough for the HV power supply to stabilize so that the - * first bits we read aren't of poor quality - */ - ao_delay(AO_MS_TO_TICKS(250)); - for (;;) { + if (!trng_running) { + /* Delay long enough for the HV power supply + * to stabilize so that the first bits we read + * aren't of poor quality + */ + ao_delay(AO_MS_TO_TICKS(10)); + trng_running = TRUE; + } if (ao_send_raw()) { ao_led_on(AO_LED_TRNG_RAW); ao_trng_send_raw(buffer[usb_buf_id]); @@ -95,9 +101,39 @@ ao_trng_send(void) static struct ao_task ao_trng_send_task; +#if AO_POWER_MANAGEMENT + +static void ao_trng_suspend(void *arg) +{ + (void) arg; +#ifdef AO_TRNG_ENABLE_PORT + ao_gpio_set(AO_TRNG_ENABLE_PORT, AO_TRNG_ENABLE_BIT, AO_TRNG_ENABLE_PIN, 0); +#endif + trng_running = FALSE; +} + +static void ao_trng_resume(void *arg) +{ + (void) arg; +#ifdef AO_TRNG_ENABLE_PORT + ao_gpio_set(AO_TRNG_ENABLE_PORT, AO_TRNG_ENABLE_BIT, AO_TRNG_ENABLE_PIN, 1); +#endif +} + +static struct ao_power ao_trng_power = { + .suspend = ao_trng_suspend, + .resume = ao_trng_resume +}; + +#endif + void ao_trng_send_init(void) { +#ifdef AO_TRNG_ENABLE_PORT + ao_enable_output(AO_TRNG_ENABLE_PORT, AO_TRNG_ENABLE_BIT, AO_TRNG_ENABLE_PIN, 1); + ao_power_register(&ao_trng_power); +#endif ao_enable_input(AO_RAW_PORT, AO_RAW_BIT, AO_EXTI_MODE_PULL_UP); ao_add_task(&ao_trng_send_task, ao_trng_send, "trng_send"); } -- cgit v1.2.3 From 147f0df6a29b37fbfb0824ecd276482f0eecb397 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 7 Feb 2016 15:33:42 -0800 Subject: altos: Delay TRNG ADC long enough for HV supply to stabilize Looks like it takes about 70ms for the supply to start running right, so delay after powering it up for that long. Signed-off-by: Keith Packard --- src/drivers/ao_trng_send.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_trng_send.c b/src/drivers/ao_trng_send.c index 63366d3e..11f441f8 100644 --- a/src/drivers/ao_trng_send.c +++ b/src/drivers/ao_trng_send.c @@ -22,7 +22,8 @@ #include #include -static uint8_t trng_running; +static uint8_t trng_running; +static AO_TICK_TYPE trng_power_time; static void ao_trng_send_raw(uint16_t *buf) @@ -45,7 +46,7 @@ ao_trng_send_cooked(uint16_t *buf) uint16_t t; uint32_t *rnd = (uint32_t *) ao_adc_ring; - t = ao_adc_get(AO_USB_IN_SIZE) >> 1; /* one 16-bit value per output byte */ + t = ao_adc_get(AO_USB_IN_SIZE) >> 1; /* two 16-bit values per two output bytes */ for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) { *buf++ = ao_crc_in_32_out_16(rnd[t]); t = (t + 1) & ((AO_ADC_RING_SIZE>>1) - 1); @@ -73,15 +74,26 @@ ao_trng_send(void) usb_buf_id = 0; +#ifdef AO_TRNG_ENABLE_PORT + ao_gpio_set(AO_TRNG_ENABLE_PORT, AO_TRNG_ENABLE_BIT, AO_TRNG_ENABLE_PIN, 1); +#endif + trng_power_time = ao_time(); + ao_crc_reset(); for (;;) { if (!trng_running) { + AO_TICK_TYPE delay; + + delay = trng_power_time + AO_MS_TO_TICKS(100) - ao_time(); + if (delay > AO_MS_TO_TICKS(100)) + delay = AO_MS_TO_TICKS(100); + /* Delay long enough for the HV power supply * to stabilize so that the first bits we read * aren't of poor quality */ - ao_delay(AO_MS_TO_TICKS(10)); + ao_delay(delay); trng_running = TRUE; } if (ao_send_raw()) { @@ -118,6 +130,7 @@ static void ao_trng_resume(void *arg) #ifdef AO_TRNG_ENABLE_PORT ao_gpio_set(AO_TRNG_ENABLE_PORT, AO_TRNG_ENABLE_BIT, AO_TRNG_ENABLE_PIN, 1); #endif + trng_power_time = ao_time(); } static struct ao_power ao_trng_power = { @@ -131,7 +144,7 @@ void ao_trng_send_init(void) { #ifdef AO_TRNG_ENABLE_PORT - ao_enable_output(AO_TRNG_ENABLE_PORT, AO_TRNG_ENABLE_BIT, AO_TRNG_ENABLE_PIN, 1); + ao_enable_output(AO_TRNG_ENABLE_PORT, AO_TRNG_ENABLE_BIT, AO_TRNG_ENABLE_PIN, 0); ao_power_register(&ao_trng_power); #endif ao_enable_input(AO_RAW_PORT, AO_RAW_BIT, AO_EXTI_MODE_PULL_UP); -- cgit v1.2.3 From bab082605e3fca6b6d11447c45cd948ddfe74bb9 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 7 Feb 2016 22:28:34 -0800 Subject: altos: Add simple stats test to TRNG code This detects broken hardware by making sure the standard deviation in the raw values used to compute each buffer is at least 128. Signed-off-by: Keith Packard --- src/drivers/ao_trng_send.c | 76 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 14 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_trng_send.c b/src/drivers/ao_trng_send.c index 11f441f8..f8222993 100644 --- a/src/drivers/ao_trng_send.c +++ b/src/drivers/ao_trng_send.c @@ -25,32 +25,68 @@ static uint8_t trng_running; static AO_TICK_TYPE trng_power_time; -static void +/* Make sure there's at least 8 bits of variance in the samples */ +#define MIN_VARIANCE (128 * 128) + +#define DECLARE_STATS int32_t sum = 0, sum2 = 0 + +#define ADD_STATS(value) do { \ + sum += (value); \ + sum2 += (value) * (value); \ + } while(0) + +#define GOOD_STATS(i) (((sum2 - (sum * sum) / i) / i) >= MIN_VARIANCE) + +#define TRNG_ENABLE_DELAY AO_MS_TO_TICKS(100) + +static int ao_trng_send_raw(uint16_t *buf) { uint16_t i; uint16_t t; - uint16_t *rnd = (uint16_t *) ao_adc_ring; + uint16_t v; + + DECLARE_STATS; t = ao_adc_get(AO_USB_IN_SIZE>>1); /* one 16-bit value per two output bytes */ for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) { - *buf++ = rnd[t]; + v = ao_adc_ring[t]; + *buf++ = v; t = (t + 1) & (AO_ADC_RING_SIZE - 1); + + ADD_STATS(v); } + return GOOD_STATS(AO_USB_IN_SIZE / sizeof (uint16_t)); } -static void +static int ao_trng_send_cooked(uint16_t *buf) { uint16_t i; uint16_t t; uint32_t *rnd = (uint32_t *) ao_adc_ring; - t = ao_adc_get(AO_USB_IN_SIZE) >> 1; /* two 16-bit values per two output bytes */ + DECLARE_STATS; + + t = ao_adc_get(AO_USB_IN_SIZE) >> 1; /* one 16-bit value per output byte */ + for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) { - *buf++ = ao_crc_in_32_out_16(rnd[t]); - t = (t + 1) & ((AO_ADC_RING_SIZE>>1) - 1); + uint32_t v; + uint16_t v1, v2; + + /* Fetch two values in one operation */ + v = rnd[t]; + t = (t + 1) & ((AO_ADC_RING_SIZE >> 1) - 1); + + *buf++ = ao_crc_in_32_out_16(v); + + v1 = v; + v2 = v >> 16; + + ADD_STATS(v1); + ADD_STATS(v2); } + return GOOD_STATS(2 * AO_USB_IN_SIZE / sizeof (uint16_t)); } static inline int @@ -64,6 +100,8 @@ ao_trng_send(void) { static uint16_t *buffer[2]; int usb_buf_id; + int good_bits; + int failed = 0; if (!buffer[0]) { buffer[0] = ao_usb_alloc(); @@ -85,9 +123,9 @@ ao_trng_send(void) if (!trng_running) { AO_TICK_TYPE delay; - delay = trng_power_time + AO_MS_TO_TICKS(100) - ao_time(); - if (delay > AO_MS_TO_TICKS(100)) - delay = AO_MS_TO_TICKS(100); + delay = trng_power_time + TRNG_ENABLE_DELAY - ao_time(); + if (delay > TRNG_ENABLE_DELAY) + delay = TRNG_ENABLE_DELAY; /* Delay long enough for the HV power supply * to stabilize so that the first bits we read @@ -98,16 +136,26 @@ ao_trng_send(void) } if (ao_send_raw()) { ao_led_on(AO_LED_TRNG_RAW); - ao_trng_send_raw(buffer[usb_buf_id]); + good_bits = ao_trng_send_raw(buffer[usb_buf_id]); ao_led_off(AO_LED_TRNG_RAW); } else { ao_led_on(AO_LED_TRNG_COOKED); - ao_trng_send_cooked(buffer[usb_buf_id]); + good_bits = ao_trng_send_cooked(buffer[usb_buf_id]); ao_led_off(AO_LED_TRNG_COOKED); } ao_adc_ack(AO_USB_IN_SIZE); - ao_usb_write(buffer[usb_buf_id], AO_USB_IN_SIZE); - usb_buf_id = 1-usb_buf_id; + if (good_bits) { + ao_usb_write(buffer[usb_buf_id], AO_USB_IN_SIZE); + usb_buf_id = 1-usb_buf_id; + failed = 0; + } else { + failed++; + ao_delay(AO_MS_TO_TICKS(10)); + if (failed > 10) { + ao_usb_disable(); + ao_panic(AO_PANIC_DMA); + } + } } } -- cgit v1.2.3 From 05fcb717bfc44aba3c1cfd43281e323505a46402 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 2 Mar 2016 13:54:58 -0800 Subject: altos/chaoskey: Add another USB endpoint to read raw data This replaces having the single output switch based on a pin value and allows us to box the device and still fetch raw data. For now, this will use a special libusb2 program, ao-chaosread, to pull bits as I haven't figure out how to make linux provide two /dev entries for one USB device. Signed-off-by: Keith Packard --- src/chaoskey-v0.1/ao_pins.h | 2 + src/drivers/ao_trng_send.c | 149 +++++++++++++++++++++++---------- src/kernel/ao_product.c | 14 +++- src/kernel/ao_usb.h | 5 ++ src/stmf0/ao_arch_funcs.h | 3 + src/stmf0/ao_usb_stm.c | 195 +++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 321 insertions(+), 47 deletions(-) (limited to 'src/drivers') diff --git a/src/chaoskey-v0.1/ao_pins.h b/src/chaoskey-v0.1/ao_pins.h index 6ec582a9..73f76307 100644 --- a/src/chaoskey-v0.1/ao_pins.h +++ b/src/chaoskey-v0.1/ao_pins.h @@ -50,7 +50,9 @@ #define AO_USB_INTERFACE_CLASS_DATA 0xff #define AO_USB_HAS_OUT 0 #define AO_USB_HAS_IN 1 +#define AO_USB_HAS_IN2 1 #define AO_USB_HAS_INT 0 +#define USE_USB_STDIO 0 #define AO_USB_SELF_POWER 0 #define AO_USB_DEVICE_ID_SERIAL 1 diff --git a/src/drivers/ao_trng_send.c b/src/drivers/ao_trng_send.c index f8222993..171a345f 100644 --- a/src/drivers/ao_trng_send.c +++ b/src/drivers/ao_trng_send.c @@ -22,54 +22,97 @@ #include #include +static struct ao_task ao_trng_send_task, ao_trng_send_raw_task; static uint8_t trng_running; static AO_TICK_TYPE trng_power_time; -/* Make sure there's at least 8 bits of variance in the samples */ -#define MIN_VARIANCE (128 * 128) - -#define DECLARE_STATS int32_t sum = 0, sum2 = 0 - -#define ADD_STATS(value) do { \ - sum += (value); \ - sum2 += (value) * (value); \ - } while(0) - -#define GOOD_STATS(i) (((sum2 - (sum * sum) / i) / i) >= MIN_VARIANCE) - #define TRNG_ENABLE_DELAY AO_MS_TO_TICKS(100) -static int -ao_trng_send_raw(uint16_t *buf) +static uint8_t random_mutex; + +static void +ao_trng_get_raw(uint16_t *buf) { uint16_t i; uint16_t t; uint16_t v; - DECLARE_STATS; - t = ao_adc_get(AO_USB_IN_SIZE>>1); /* one 16-bit value per two output bytes */ for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) { v = ao_adc_ring[t]; *buf++ = v; t = (t + 1) & (AO_ADC_RING_SIZE - 1); + } + ao_adc_ack(AO_USB_IN_SIZE>>1); +} + +static void +ao_trng_send_raw(void) +{ + static uint16_t *buffer[2]; + int usb_buf_id; - ADD_STATS(v); + if (!buffer[0]) { + buffer[0] = ao_usb_alloc(); + buffer[1] = ao_usb_alloc(); + if (!buffer[0]) + ao_exit(); + } + + usb_buf_id = 0; + + for (;;) { + ao_mutex_get(&random_mutex); + if (!trng_running) { + AO_TICK_TYPE delay; + + delay = trng_power_time + TRNG_ENABLE_DELAY - ao_time(); + if (delay > TRNG_ENABLE_DELAY) + delay = TRNG_ENABLE_DELAY; + + /* Delay long enough for the HV power supply + * to stabilize so that the first bits we read + * aren't of poor quality + */ + ao_delay(delay); + trng_running = TRUE; + } +#ifdef AO_LED_TRNG_RAW + ao_led_on(AO_LED_TRNG_RAW); +#endif + ao_trng_get_raw(buffer[usb_buf_id]); +#ifdef AO_LED_TRNG_RAW + ao_led_off(AO_LED_TRNG_RAW); +#endif + ao_mutex_put(&random_mutex); + ao_usb_write2(buffer[usb_buf_id], AO_USB_IN_SIZE); + usb_buf_id = 1-usb_buf_id; } - return GOOD_STATS(AO_USB_IN_SIZE / sizeof (uint16_t)); } +/* Make sure there's at least 8 bits of variance in the samples */ +#define MIN_VARIANCE (128 * 128) + +/* Make sure the signal is spread around a bit */ +#define MAX_VARIANCE (512 * 512) + +#define ADD_STATS(value) do { \ + sum += (value); \ + sum2 += (value) * (value); \ + } while(0) + +#define VARIANCE(n) ((sum2 - (sum / (n) * sum)) / (n)) + static int -ao_trng_send_cooked(uint16_t *buf) +ao_trng_get_cooked(uint16_t *buf) { uint16_t i; uint16_t t; uint32_t *rnd = (uint32_t *) ao_adc_ring; + int32_t sum, sum2, var; - DECLARE_STATS; - + sum = sum2 = 0; t = ao_adc_get(AO_USB_IN_SIZE) >> 1; /* one 16-bit value per output byte */ - for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) { uint32_t v; uint16_t v1, v2; @@ -86,14 +129,13 @@ ao_trng_send_cooked(uint16_t *buf) ADD_STATS(v1); ADD_STATS(v2); } - return GOOD_STATS(2 * AO_USB_IN_SIZE / sizeof (uint16_t)); + ao_adc_ack(AO_USB_IN_SIZE); + var = VARIANCE(2 * AO_USB_IN_SIZE / sizeof (uint16_t)); + return var >= MIN_VARIANCE && var <= MAX_VARIANCE; } -static inline int -ao_send_raw(void) -{ - return !ao_gpio_get(AO_RAW_PORT, AO_RAW_BIT, AO_RAW_PIN); -} +#define AO_TRNG_START_WAIT 1024 +#define AO_TRNG_START_CHECK 32 static void ao_trng_send(void) @@ -101,13 +143,14 @@ ao_trng_send(void) static uint16_t *buffer[2]; int usb_buf_id; int good_bits; - int failed = 0; + int failed; + int s; if (!buffer[0]) { buffer[0] = ao_usb_alloc(); buffer[1] = ao_usb_alloc(); if (!buffer[0]) - return; + ao_exit(); } usb_buf_id = 0; @@ -119,7 +162,33 @@ ao_trng_send(void) ao_crc_reset(); + ao_delay(TRNG_ENABLE_DELAY); + + for (s = 0; s < AO_TRNG_START_WAIT; s++) { + if (ao_trng_get_cooked(buffer[0])) + break; + ao_delay(AO_MS_TO_TICKS(10)); + } + + /* Validate the hardware before enabling USB */ + failed = 0; + for (s = 0; s < AO_TRNG_START_CHECK; s++) { + if (!ao_trng_get_cooked(buffer[0])) { + failed++; + ao_delay(AO_MS_TO_TICKS(10)); + } + } + if (failed > AO_TRNG_START_CHECK / 4) + ao_panic(AO_PANIC_DMA); + + ao_add_task(&ao_trng_send_raw_task, ao_trng_send_raw, "trng_send_raw"); + +#ifdef AO_USB_START_DISABLED + ao_usb_enable(); +#endif + for (;;) { + ao_mutex_get(&random_mutex); if (!trng_running) { AO_TICK_TYPE delay; @@ -134,16 +203,14 @@ ao_trng_send(void) ao_delay(delay); trng_running = TRUE; } - if (ao_send_raw()) { - ao_led_on(AO_LED_TRNG_RAW); - good_bits = ao_trng_send_raw(buffer[usb_buf_id]); - ao_led_off(AO_LED_TRNG_RAW); - } else { - ao_led_on(AO_LED_TRNG_COOKED); - good_bits = ao_trng_send_cooked(buffer[usb_buf_id]); - ao_led_off(AO_LED_TRNG_COOKED); - } - ao_adc_ack(AO_USB_IN_SIZE); +#ifdef AO_LED_TRNG_COOKED + ao_led_on(AO_LED_TRNG_COOKED); +#endif + good_bits = ao_trng_get_cooked(buffer[usb_buf_id]); +#ifdef AO_LED_TRNG_COOKED + ao_led_off(AO_LED_TRNG_COOKED); +#endif + ao_mutex_put(&random_mutex); if (good_bits) { ao_usb_write(buffer[usb_buf_id], AO_USB_IN_SIZE); usb_buf_id = 1-usb_buf_id; @@ -159,8 +226,6 @@ ao_trng_send(void) } } -static struct ao_task ao_trng_send_task; - #if AO_POWER_MANAGEMENT static void ao_trng_suspend(void *arg) diff --git a/src/kernel/ao_product.c b/src/kernel/ao_product.c index 4769d86e..3a829b3a 100644 --- a/src/kernel/ao_product.c +++ b/src/kernel/ao_product.c @@ -54,7 +54,7 @@ const char ao_product[] = AO_iProduct_STRING; #define HEADER_LEN 9 #define CONTROL_CLASS_LEN 35 -#define DATA_LEN (9 + 7 * AO_USB_HAS_OUT + 7 * AO_USB_HAS_IN) +#define DATA_LEN (9 + 7 * AO_USB_HAS_OUT + 7 * AO_USB_HAS_IN + 7 * AO_USB_HAS_IN2) #define TOTAL_LENGTH (HEADER_LEN + AO_USB_HAS_INT * CONTROL_CLASS_LEN + DATA_LEN) #define NUM_INTERFACES (AO_USB_HAS_INT + 1) @@ -140,7 +140,7 @@ AO_ROMCONFIG_SYMBOL(0x00aa) uint8_t ao_usb_descriptors [] = AO_USB_DESC_INTERFACE, AO_USB_HAS_INT, /* bInterfaceNumber */ 0x00, /* bAlternateSetting */ - AO_USB_HAS_OUT + AO_USB_HAS_IN, /* bNumEndPoints */ + AO_USB_HAS_OUT + AO_USB_HAS_IN + AO_USB_HAS_IN2, /* bNumEndPoints */ AO_USB_INTERFACE_CLASS_DATA, /* bInterfaceClass = data */ 0x00, /* bInterfaceSubClass */ 0x00, /* bInterfaceProtocol */ @@ -166,6 +166,16 @@ AO_ROMCONFIG_SYMBOL(0x00aa) uint8_t ao_usb_descriptors [] = 0x00, /* bInterval */ #endif +#if AO_USB_HAS_IN2 + /* Data EP in 2 */ + 0x07, + AO_USB_DESC_ENDPOINT, + AO_USB_IN2_EP|0x80, /* bEndpointAddress */ + 0x02, /* bmAttributes = bulk */ + LE_WORD(AO_USB_IN_SIZE),/* wMaxPacketSize */ + 0x00, /* bInterval */ +#endif + /* String descriptors */ 0x04, AO_USB_DESC_STRING, diff --git a/src/kernel/ao_usb.h b/src/kernel/ao_usb.h index 773b5cb7..8f3e7813 100644 --- a/src/kernel/ao_usb.h +++ b/src/kernel/ao_usb.h @@ -105,6 +105,7 @@ extern __code __at (0x00aa) uint8_t ao_usb_descriptors []; #ifndef AO_USB_OUT_EP #define AO_USB_OUT_EP 4 #define AO_USB_IN_EP 5 +#define AO_USB_IN2_EP 6 #endif #ifndef AO_USB_HAS_OUT @@ -119,6 +120,10 @@ extern __code __at (0x00aa) uint8_t ao_usb_descriptors []; #define AO_USB_HAS_INT 1 #endif +#ifndef AO_USB_HAS_IN2 +#define AO_USB_HAS_IN2 0 +#endif + /* * USB bulk packets can only come in 8, 16, 32 and 64 * byte sizes, so we'll use 64 for everything diff --git a/src/stmf0/ao_arch_funcs.h b/src/stmf0/ao_arch_funcs.h index 8d585f80..4e3ef018 100644 --- a/src/stmf0/ao_arch_funcs.h +++ b/src/stmf0/ao_arch_funcs.h @@ -413,6 +413,9 @@ ao_usb_free(uint16_t *buffer); void ao_usb_write(uint16_t *buffer, uint16_t len); + +void +ao_usb_write2(uint16_t *buffer, uint16_t len); #endif /* AO_USB_DIRECTIO */ #endif /* _AO_ARCH_FUNCS_H_ */ diff --git a/src/stmf0/ao_usb_stm.c b/src/stmf0/ao_usb_stm.c index 17e8709c..e68da8eb 100644 --- a/src/stmf0/ao_usb_stm.c +++ b/src/stmf0/ao_usb_stm.c @@ -89,23 +89,42 @@ static uint16_t ao_usb_sram_addr; static uint16_t *ao_usb_ep0_tx_buffer; static uint16_t *ao_usb_ep0_rx_buffer; +#if AO_USB_HAS_INT /* Pointer to interrupt buffer in USB memory */ static uint16_t ao_usb_int_tx_offset; +#endif /* Pointer to bulk data tx/rx buffers in USB memory */ +#if AO_USB_HAS_IN static uint16_t ao_usb_in_tx_offset; static uint16_t *ao_usb_in_tx_buffer; -static uint16_t ao_usb_out_rx_offset; -static uint16_t *ao_usb_out_rx_buffer; /* System ram shadow of USB buffer; writing individual bytes is * too much of a pain (sigh) */ static uint8_t ao_usb_tx_buffer[AO_USB_IN_SIZE]; static uint8_t ao_usb_tx_count; +#endif +#if AO_USB_HAS_OUT +static uint16_t ao_usb_out_rx_offset; +static uint16_t *ao_usb_out_rx_buffer; + +/* System ram shadow of USB buffer; writing individual bytes is + * too much of a pain (sigh) */ static uint8_t ao_usb_rx_buffer[AO_USB_OUT_SIZE]; static uint8_t ao_usb_rx_count, ao_usb_rx_pos; +#endif +#if AO_USB_HAS_IN2 +static uint16_t ao_usb_in2_tx_offset; +static uint16_t *ao_usb_in2_tx_buffer; + +/* System ram shadow of USB buffer; writing individual bytes is + * too much of a pain (sigh) */ +static uint8_t ao_usb_tx2_buffer[AO_USB_IN_SIZE]; +static uint8_t ao_usb_tx2_count; +#endif + /* * End point register indices */ @@ -114,6 +133,7 @@ static uint8_t ao_usb_rx_count, ao_usb_rx_pos; #define AO_USB_INT_EPR 1 #define AO_USB_OUT_EPR 2 #define AO_USB_IN_EPR 3 +#define AO_USB_IN2_EPR 4 /* Marks when we don't need to send an IN packet. * This happens only when the last IN packet is not full, @@ -128,6 +148,16 @@ static uint8_t ao_usb_in_flushed; */ static uint8_t ao_usb_in_pending; +#if AO_USB_HAS_IN2 +/* Marks when we have delivered an IN packet to the hardware + * and it has not been received yet. ao_sleep on this address + * to wait for it to be delivered. + */ +static uint8_t ao_usb_in2_pending; +static uint16_t in2_count; +static uint8_t ao_usb_in2_flushed; +#endif + /* Marks when an OUT packet has been received by the hardware * but not pulled to the shadow buffer. */ @@ -343,16 +373,29 @@ ao_usb_alloc_buffers(void) ao_usb_ep0_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr); ao_usb_sram_addr += AO_USB_CONTROL_SIZE; + +#if AO_USB_HAS_INT ao_usb_int_tx_offset = ao_usb_sram_addr; ao_usb_sram_addr += AO_USB_INT_SIZE; +#endif +#if AO_USB_HAS_OUT ao_usb_out_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr); ao_usb_out_rx_offset = ao_usb_sram_addr; ao_usb_sram_addr += AO_USB_OUT_SIZE; +#endif +#if AO_USB_HAS_IN ao_usb_in_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr); ao_usb_in_tx_offset = ao_usb_sram_addr; ao_usb_sram_addr += AO_USB_IN_SIZE; +#endif + +#if AO_USB_HAS_IN2 + ao_usb_in2_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr); + ao_usb_in2_tx_offset = ao_usb_sram_addr; + ao_usb_sram_addr += AO_USB_IN_SIZE; +#endif } static void @@ -438,6 +481,18 @@ ao_usb_set_configuration(void) STM_USB_EPR_STAT_TX_NAK); #endif +#if AO_USB_HAS_IN2 + /* Set up the IN2 end point */ + ao_usb_bdt[AO_USB_IN2_EPR].single.addr_tx = ao_usb_in2_tx_offset; + ao_usb_bdt[AO_USB_IN2_EPR].single.count_tx = 0; + + ao_usb_init_ep(AO_USB_IN2_EPR, + AO_USB_IN2_EP, + STM_USB_EPR_EP_TYPE_BULK, + STM_USB_EPR_STAT_RX_DISABLED, + STM_USB_EPR_STAT_TX_NAK); +#endif + ao_usb_running = 1; #if AO_USB_DIRECTIO ao_wakeup(&ao_usb_running); @@ -581,7 +636,7 @@ hex_to_ucs2(uint32_t in, uint8_t *out) for (i = 28; i >= 0; i -= 4) { uint8_t bits = (in >> i) & 0xf; - *out++ = (bits < 10) ? ('0' + bits) : ('a' + bits); + *out++ = ((bits < 10) ? '0' : ('a' - 10)) + bits; *out++ = 0; } } @@ -835,6 +890,16 @@ stm_usb_isr(void) ao_wakeup(&ao_usb_in_pending); } break; +#if AO_USB_HAS_IN2 + case AO_USB_IN2_EPR: + ++in2_count; + _tx_dbg1("TX2 ISR", epr); + if (ao_usb_epr_ctr_tx(epr)) { + ao_usb_in2_pending = 0; + ao_wakeup(&ao_usb_in2_pending); + } + break; +#endif case AO_USB_INT_EPR: ++int_count; if (ao_usb_epr_ctr_tx(epr)) @@ -861,6 +926,7 @@ stm_usb_isr(void) #endif } +#if AO_USB_HAS_IN /* Queue the current IN buffer for transmission */ static void _ao_usb_in_send(void) @@ -939,7 +1005,90 @@ ao_usb_putchar(char c) } ao_arch_release_interrupts(); } +#endif + +#if AO_USB_HAS_IN +/* Queue the current IN buffer for transmission */ +static void +_ao_usb_in2_send(void) +{ + _tx_dbg0("in2_send start"); + debug ("send2 %d\n", ao_usb_tx_count); + while (ao_usb_in2_pending) + ao_sleep(&ao_usb_in2_pending); + ao_usb_in2_pending = 1; + if (ao_usb_tx2_count != AO_USB_IN_SIZE) + ao_usb_in2_flushed = 1; + ao_usb_copy_tx(ao_usb_tx2_buffer, ao_usb_in2_tx_buffer, ao_usb_tx2_count); + ao_usb_bdt[AO_USB_IN2_EPR].single.addr_tx = ao_usb_in_tx_offset; + ao_usb_bdt[AO_USB_IN2_EPR].single.count_tx = ao_usb_tx_count; + ao_usb_tx2_count = 0; + _ao_usb_set_stat_tx(AO_USB_IN2_EPR, STM_USB_EPR_STAT_TX_VALID); + _tx_dbg0("in2_send end"); +} + +/* Wait for a free IN buffer. Interrupts are blocked */ +static void +_ao_usb_in2_wait(void) +{ + for (;;) { + /* Check if the current buffer is writable */ + if (ao_usb_tx2_count < AO_USB_IN_SIZE) + break; + + _tx_dbg0("in2_wait top"); + /* Wait for an IN buffer to be ready */ + while (ao_usb_in2_pending) + ao_sleep(&ao_usb_in2_pending); + _tx_dbg0("in_wait bottom"); + } +} + +void +ao_usb_flush2(void) +{ + if (!ao_usb_running) + return; + + /* Anytime we've sent a character since + * the last time we flushed, we'll need + * to send a packet -- the only other time + * we would send a packet is when that + * packet was full, in which case we now + * want to send an empty packet + */ + ao_arch_block_interrupts(); + while (!ao_usb_in2_flushed) { + _tx_dbg0("flush2 top"); + _ao_usb_in2_send(); + _tx_dbg0("flush2 end"); + } + ao_arch_release_interrupts(); +} + +void +ao_usb_putchar2(char c) +{ + if (!ao_usb_running) + return; + + ao_arch_block_interrupts(); + _ao_usb_in2_wait(); + + ao_usb_in2_flushed = 0; + ao_usb_tx2_buffer[ao_usb_tx2_count++] = (uint8_t) c; + + /* Send the packet when full */ + if (ao_usb_tx2_count == AO_USB_IN_SIZE) { + _tx_dbg0("putchar2 full"); + _ao_usb_in2_send(); + _tx_dbg0("putchar2 flushed"); + } + ao_arch_release_interrupts(); +} +#endif +#if AO_USB_HAS_OUT static void _ao_usb_out_recv(void) { @@ -996,6 +1145,7 @@ ao_usb_getchar(void) ao_arch_release_interrupts(); return c; } +#endif #if AO_USB_DIRECTIO uint16_t * @@ -1050,6 +1200,43 @@ ao_usb_write(uint16_t *buffer, uint16_t len) _ao_usb_set_stat_tx(AO_USB_IN_EPR, STM_USB_EPR_STAT_TX_VALID); ao_arch_release_interrupts(); } + +#if AO_USB_HAS_IN2 +void +ao_usb_write2(uint16_t *buffer, uint16_t len) +{ + ao_arch_block_interrupts(); + + /* Wait for everything to be ready at the same time */ + for (;;) { + /* Make sure USB is connected */ + if (!ao_usb_running) { + ao_sleep(&ao_usb_running); + continue; + } + + /* Flush any pending regular I/O */ + if (ao_usb_tx2_count) { + _ao_usb_in2_send(); + continue; + } + + /* Wait for an idle IN buffer */ + if (ao_usb_in2_pending) { + ao_sleep(&ao_usb_in2_pending); + continue; + } + break; + } + + ao_usb_in2_pending = 1; + ao_usb_in2_flushed = (len != AO_USB_IN_SIZE); + ao_usb_bdt[AO_USB_IN2_EPR].single.addr_tx = ao_usb_packet_buffer_offset(buffer); + ao_usb_bdt[AO_USB_IN2_EPR].single.count_tx = len; + _ao_usb_set_stat_tx(AO_USB_IN2_EPR, STM_USB_EPR_STAT_TX_VALID); + ao_arch_release_interrupts(); +} +#endif #endif void @@ -1180,7 +1367,9 @@ ao_usb_init(void) /* Set PA11/PA12 remapping bit */ stm_syscfg.cfgr1 |= (AO_PA11_PA12_RMP << STM_SYSCFG_CFGR1_PA11_PA12_RMP); +#ifndef AO_USB_START_DISABLED ao_usb_enable(); +#endif #if AO_USB_DEVICE_ID_SERIAL ao_usb_serial_init(); -- cgit v1.2.3 From cfb91ec7ef6ef485d813af96a0f206bb7a2204dd Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 5 Apr 2016 22:03:37 -0700 Subject: altos/detherm: Add ms5607 to detherm Signed-off-by: Keith Packard --- src/detherm/ao_detherm.c | 2 +- src/detherm/ao_pins.h | 6 ++++-- src/drivers/ao_ms5607.c | 6 +++++- src/stmf0/ao_arch_funcs.h | 22 +++++++++++----------- 4 files changed, 21 insertions(+), 15 deletions(-) (limited to 'src/drivers') diff --git a/src/detherm/ao_detherm.c b/src/detherm/ao_detherm.c index cc013753..64dee586 100644 --- a/src/detherm/ao_detherm.c +++ b/src/detherm/ao_detherm.c @@ -35,8 +35,8 @@ void main(void) ao_usb_init(); ao_storage_init(); + ao_ms5607_init(); // ao_flight_init(); -// ao_ms5607_init(); ao_pwm_init(); ao_log_init(); ao_report_init(); diff --git a/src/detherm/ao_pins.h b/src/detherm/ao_pins.h index 9b623627..8cd4a9c4 100644 --- a/src/detherm/ao_pins.h +++ b/src/detherm/ao_pins.h @@ -72,11 +72,13 @@ /* MS5607 */ #define HAS_MS5607 1 -#define AO_MS5607_CS_PORT (&stm_gpiob) -#define AO_MS5607_CS_PIN 6 +#define AO_MS5607_CS_PORT (&stm_gpioa) +#define AO_MS5607_CS_PIN 0 #define AO_MS5607_SPI_INDEX AO_SPI_1_PB3_PB4_PB5 #define AO_MS5607_MISO_PORT (&stm_gpiob) #define AO_MS5607_MISO_PIN 4 +#define AO_MS5607_PRIVATE_PINS 1 +#define AO_MS5607_SPI_SPEED AO_SPI_SPEED_6MHz /* Flash */ diff --git a/src/drivers/ao_ms5607.c b/src/drivers/ao_ms5607.c index 6098699e..ef31882e 100644 --- a/src/drivers/ao_ms5607.c +++ b/src/drivers/ao_ms5607.c @@ -24,9 +24,13 @@ __xdata struct ao_ms5607_prom ao_ms5607_prom; static __xdata uint8_t ms5607_configured; +#ifndef AO_MS5607_SPI_SPEED +#define AO_MS5607_SPI_SPEED AO_SPI_SPEED_FAST +#endif + static void ao_ms5607_start(void) { - ao_spi_get_bit(AO_MS5607_CS_PORT, AO_MS5607_CS_PIN, AO_MS5607_CS, AO_MS5607_SPI_INDEX, AO_SPI_SPEED_FAST); + ao_spi_get_bit(AO_MS5607_CS_PORT, AO_MS5607_CS_PIN, AO_MS5607_CS, AO_MS5607_SPI_INDEX, AO_MS5607_SPI_SPEED); } static void diff --git a/src/stmf0/ao_arch_funcs.h b/src/stmf0/ao_arch_funcs.h index 4e3ef018..ccfa3fc7 100644 --- a/src/stmf0/ao_arch_funcs.h +++ b/src/stmf0/ao_arch_funcs.h @@ -23,22 +23,22 @@ /* ao_spi_stm.c */ -/* PCLK is set to 16MHz (HCLK 32MHz, APB prescaler 2) */ +/* PCLK is set to 48MHz (HCLK 48MHz, HPRE 1, PPRE 1) */ -#define AO_SPI_SPEED_8MHz STM_SPI_CR1_BR_PCLK_2 -#define AO_SPI_SPEED_4MHz STM_SPI_CR1_BR_PCLK_4 -#define AO_SPI_SPEED_2MHz STM_SPI_CR1_BR_PCLK_8 -#define AO_SPI_SPEED_1MHz STM_SPI_CR1_BR_PCLK_16 -#define AO_SPI_SPEED_500kHz STM_SPI_CR1_BR_PCLK_32 -#define AO_SPI_SPEED_250kHz STM_SPI_CR1_BR_PCLK_64 -#define AO_SPI_SPEED_125kHz STM_SPI_CR1_BR_PCLK_128 -#define AO_SPI_SPEED_62500Hz STM_SPI_CR1_BR_PCLK_256 +#define AO_SPI_SPEED_24MHz STM_SPI_CR1_BR_PCLK_2 +#define AO_SPI_SPEED_12MHz STM_SPI_CR1_BR_PCLK_4 +#define AO_SPI_SPEED_6MHz STM_SPI_CR1_BR_PCLK_8 +#define AO_SPI_SPEED_3MHz STM_SPI_CR1_BR_PCLK_16 +#define AO_SPI_SPEED_1500kHz STM_SPI_CR1_BR_PCLK_32 +#define AO_SPI_SPEED_750kHz STM_SPI_CR1_BR_PCLK_64 +#define AO_SPI_SPEED_375kHz STM_SPI_CR1_BR_PCLK_128 +#define AO_SPI_SPEED_187500Hz STM_SPI_CR1_BR_PCLK_256 -#define AO_SPI_SPEED_FAST AO_SPI_SPEED_8MHz +#define AO_SPI_SPEED_FAST AO_SPI_SPEED_24MHz /* Companion bus wants something no faster than 200kHz */ -#define AO_SPI_SPEED_200kHz AO_SPI_SPEED_125kHz +#define AO_SPI_SPEED_200kHz AO_SPI_SPEED_187500Hz #define AO_SPI_CONFIG_1 0x00 #define AO_SPI_1_CONFIG_PA5_PA6_PA7 AO_SPI_CONFIG_1 -- cgit v1.2.3 From b8a19e83b7b1b8e2a1fcbdd58e41f9f974ae28ff Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 13 Apr 2016 06:16:01 -0700 Subject: altos/detherm: Add servo driver This just provides commands to test the servo with. Signed-off-by: Keith Packard --- src/detherm/Makefile | 1 + src/detherm/ao_detherm.c | 2 + src/detherm/ao_pins.h | 16 +++++-- src/drivers/ao_servo.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++ src/drivers/ao_servo.h | 41 ++++++++++++++++ 5 files changed, 174 insertions(+), 4 deletions(-) create mode 100644 src/drivers/ao_servo.c create mode 100644 src/drivers/ao_servo.h (limited to 'src/drivers') diff --git a/src/detherm/Makefile b/src/detherm/Makefile index 35df6c96..6b0e0bf8 100644 --- a/src/detherm/Makefile +++ b/src/detherm/Makefile @@ -42,6 +42,7 @@ ALTOS_SRC = \ ao_ms5607.c \ ao_convert_pa.c \ ao_pwm.c \ + ao_servo.c \ ao_task.c \ ao_config.c \ ao_cmd.c \ diff --git a/src/detherm/ao_detherm.c b/src/detherm/ao_detherm.c index 64dee586..fba9195e 100644 --- a/src/detherm/ao_detherm.c +++ b/src/detherm/ao_detherm.c @@ -18,6 +18,7 @@ #include #include #include +#include void main(void) { @@ -38,6 +39,7 @@ void main(void) ao_ms5607_init(); // ao_flight_init(); ao_pwm_init(); + ao_servo_init(); ao_log_init(); ao_report_init(); ao_config_init(); diff --git a/src/detherm/ao_pins.h b/src/detherm/ao_pins.h index 8cd4a9c4..1c577b6e 100644 --- a/src/detherm/ao_pins.h +++ b/src/detherm/ao_pins.h @@ -98,10 +98,18 @@ #define AO_PWM_TIMER_ENABLE STM_RCC_APB1ENR_TIM3EN #define AO_PWM_TIMER_SCALE 32 -/* Motor */ +/* Servo */ -#define AO_MOTOR_DIR_GPIO (&stm_gpiob) -#define AO_MOTOR_DIR_PIN 0 -#define AO_MOTOR_SPEED_PWM 0 +#define AO_SERVO_DIR_PORT (&stm_gpiob) +#define AO_SERVO_DIR_BIT 0 +#define AO_SERVO_SPEED_PWM 0 + +/* limit 2 */ +#define AO_SERVO_LIMIT_FORE_PORT (&stm_gpiob) +#define AO_SERVO_LIMIT_FORE_BIT 6 + +/* limit 1 */ +#define AO_SERVO_LIMIT_BACK_PORT (&stm_gpiob) +#define AO_SERVO_LIMIT_BACK_BIT 7 #endif /* _AO_PINS_H_ */ diff --git a/src/drivers/ao_servo.c b/src/drivers/ao_servo.c new file mode 100644 index 00000000..b48a4112 --- /dev/null +++ b/src/drivers/ao_servo.c @@ -0,0 +1,118 @@ +/* + * Copyright © 2016 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" +#include "ao_servo.h" +#include "ao_pwm.h" +#include "ao_exti.h" + +static uint8_t limit_wakeup; + +static void +ao_limit_isr(void) +{ + ao_wakeup(&limit_wakeup); +} + +static void +ao_limit_enable(uint8_t dir) +{ + if (dir == AO_SERVO_FORE) + ao_exti_enable(AO_SERVO_LIMIT_FORE_PORT, AO_SERVO_LIMIT_FORE_BIT); + else + ao_exti_enable(AO_SERVO_LIMIT_BACK_PORT, AO_SERVO_LIMIT_BACK_BIT); +} + +static void +ao_limit_disable(uint8_t dir) +{ + if (dir == AO_SERVO_FORE) + ao_exti_disable(AO_SERVO_LIMIT_FORE_PORT, AO_SERVO_LIMIT_FORE_BIT); + else + ao_exti_disable(AO_SERVO_LIMIT_BACK_PORT, AO_SERVO_LIMIT_BACK_BIT); +} + +static uint8_t +ao_limit_get(uint8_t dir) +{ + if (dir == AO_SERVO_FORE) + return !ao_gpio_get(AO_SERVO_LIMIT_FORE_PORT, AO_SERVO_LIMIT_FORE_BIT, PIN); + else + return !ao_gpio_get(AO_SERVO_LIMIT_BACK_PORT, AO_SERVO_LIMIT_BACK_BIT, PIN); +} + +void +ao_servo_run(uint16_t speed, uint8_t dir, uint16_t timeout) +{ + printf ("speed %d dir %d\n", speed, dir); + + /* Turn on the motor */ + ao_gpio_set(AO_SERVO_DIR_PORT, AO_SERVO_DIR_BIT, AO_SERVO_DIR_PIN, dir); + ao_pwm_set(AO_SERVO_SPEED_PWM, speed); + + /* Wait until the limit sensor is triggered */ + ao_arch_block_interrupts(); + ao_limit_enable(dir); + while (!ao_limit_get(dir)) + if (ao_sleep_for(&limit_wakeup, timeout)) + break; + ao_limit_disable(dir); + ao_arch_release_interrupts(); + + /* Turn off the motor */ + ao_pwm_set(AO_SERVO_SPEED_PWM, 0); +} + +#define init_limit(p,b) do { \ + ao_enable_port(p); \ + ao_exti_setup(p, b, \ + AO_EXTI_MODE_PULL_UP|AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_MED, \ + ao_limit_isr); \ + } while (0) + + +static void +ao_servo_cmd(void) +{ + uint8_t dir; + uint16_t speed; + + ao_cmd_decimal(); + dir = ao_cmd_lex_u32; + ao_cmd_decimal(); + speed = ao_cmd_lex_u32; + if (ao_cmd_status != ao_cmd_success) + return; + + printf("Run servo %d\n", dir); + ao_servo_run(speed, dir, AO_MS_TO_TICKS(200)); +} + +static const struct ao_cmds ao_servo_cmds[] = { + { ao_servo_cmd, "S \0Run servo in indicated direction" }, + { 0, NULL }, +}; + + +void +ao_servo_init(void) +{ + init_limit(AO_SERVO_LIMIT_FORE_PORT, AO_SERVO_LIMIT_FORE_BIT); + init_limit(AO_SERVO_LIMIT_BACK_PORT, AO_SERVO_LIMIT_BACK_BIT); + ao_enable_output(AO_SERVO_DIR_PORT, AO_SERVO_DIR_BIT, AO_SERVO_DIR_PIN, 0); + ao_cmd_register(&ao_servo_cmds[0]); +} diff --git a/src/drivers/ao_servo.h b/src/drivers/ao_servo.h new file mode 100644 index 00000000..e1df347a --- /dev/null +++ b/src/drivers/ao_servo.h @@ -0,0 +1,41 @@ +/* + * Copyright © 2016 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _AO_SERVO_H_ +#define _AO_SERVO_H_ + +void +ao_servo_run(uint16_t speed, uint8_t dir, uint16_t timeout); + +void +ao_servo_init(void); + +#define AO_SERVO_FORE 0 +#define AO_SERVO_BACK 1 + +/* To configure the servo: + * + * #define AO_SERVO_PWM + * #define AO_SERVO_DIR_PORT + * #define AO_SERVO_DIR_PIN + * #define AO_SERVO_LIMIT_FORE_PORT + * #define AO_SERVO_LIMIT_FORE_PIN + * #define AO_SERVO_LIMIT_BACK_PORT + * #define AO_SERVO_LIMIT_BACK_PIN + */ + +#endif /* _AO_SERVO_H_ */ -- cgit v1.2.3 From 553c89f8c64398cb1a815b1fa248980cd3f62ef8 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 17 Dec 2015 19:29:36 -0800 Subject: altos: Support telefire products in cc1200 driver Need to disable the pad code while testing the radio. Signed-off-by: Keith Packard --- src/drivers/ao_cc1200.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/drivers') diff --git a/src/drivers/ao_cc1200.c b/src/drivers/ao_cc1200.c index 6547be39..6bccb188 100644 --- a/src/drivers/ao_cc1200.c +++ b/src/drivers/ao_cc1200.c @@ -20,6 +20,9 @@ #include #include #include +#if HAS_PAD +#include +#endif static uint8_t ao_radio_mutex; @@ -812,6 +815,9 @@ ao_radio_test_cmd(void) #endif #if PACKET_HAS_SLAVE ao_packet_slave_stop(); +#endif +#if HAS_PAD + ao_pad_disable(); #endif ao_radio_get(0xff); ao_radio_set_mode(AO_RADIO_MODE_TEST); @@ -837,6 +843,9 @@ ao_radio_test_cmd(void) radio_on = 0; #if HAS_MONITOR ao_monitor_enable(); +#endif +#if HAS_PAD + ao_pad_enable(); #endif } } -- cgit v1.2.3 From 26ce7a9a213bdd35a13937054988e7b8fa749632 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 17 Dec 2015 19:30:35 -0800 Subject: altos: Add pad support for new telefire versions Makes the voltage divider values configurable, and allows for pyro channels that don't have the resistors necessary to check for a stuck relay. Also supports STM32L processors with wider GPIO registers, and uses the new ao_gpio_set/clr_bits functions to fire igniters rather than cc1111-specific code. Signed-off-by: Keith Packard --- src/drivers/ao_pad.c | 57 +++++++++++++++++++++++++++++++++++++++------ src/telefire-v0.1/ao_pins.h | 2 ++ 2 files changed, 52 insertions(+), 7 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_pad.c b/src/drivers/ao_pad.c index 419ea8d3..e51d237b 100644 --- a/src/drivers/ao_pad.c +++ b/src/drivers/ao_pad.c @@ -50,7 +50,11 @@ ao_siren(uint8_t v) #ifdef AO_SIREN ao_gpio_set(AO_SIREN_PORT, AO_SIREN_PIN, AO_SIREN, v); #else +#if HAS_BEEP ao_beep(v ? AO_BEEP_MID : 0); +#else + (void) v; +#endif #endif } @@ -59,13 +63,15 @@ ao_strobe(uint8_t v) { #ifdef AO_STROBE ao_gpio_set(AO_STROBE_PORT, AO_STROBE_PIN, AO_STROBE, v); +#else + (void) v; #endif } static void ao_pad_run(void) { - uint8_t pins; + AO_PORT_TYPE pins; for (;;) { while (!ao_pad_ignite) @@ -90,18 +96,28 @@ ao_pad_run(void) if (ao_pad_ignite & (1 << 3)) pins |= (1 << AO_PAD_PIN_3); #endif - AO_PAD_PORT = (AO_PAD_PORT & (~AO_PAD_ALL_PINS)) | pins; + PRINTD("ignite pins 0x%x\n", pins); + ao_gpio_set_bits(AO_PAD_PORT, pins); while (ao_pad_ignite) { ao_pad_ignite = 0; ao_delay(AO_PAD_FIRE_TIME); } - AO_PAD_PORT &= ~(AO_PAD_ALL_PINS); + ao_gpio_clr_bits(AO_PAD_PORT, pins); + PRINTD("turn off pins 0x%x\n", pins); } } #define AO_PAD_ARM_SIREN_INTERVAL 200 +#ifndef AO_PYRO_R_PYRO_SENSE +#define AO_PYRO_R_PYRO_SENSE 100 +#define AO_PYRO_R_SENSE_GND 27 +#define AO_FIRE_R_POWER_FET 100 +#define AO_FIRE_R_FET_SENSE 100 +#define AO_FIRE_R_SENSE_GND 27 +#endif + static void ao_pad_monitor(void) { @@ -109,7 +125,7 @@ ao_pad_monitor(void) uint8_t sample; __pdata uint8_t prev = 0, cur = 0; __pdata uint8_t beeping = 0; - __xdata struct ao_data *packet; + __xdata volatile struct ao_data *packet; __pdata uint16_t arm_beep_time = 0; sample = ao_data_head; @@ -120,12 +136,18 @@ ao_pad_monitor(void) ao_sleep((void *) DATA_TO_XDATA(&ao_data_head)); ); + packet = &ao_data_ring[sample]; sample = ao_data_ring_next(sample); pyro = packet->adc.pyro; -#define VOLTS_TO_PYRO(x) ((int16_t) ((x) * 27.0 / 127.0 / 3.3 * 32767.0)) +#define VOLTS_TO_PYRO(x) ((int16_t) ((x) * ((1.0 * AO_PYRO_R_SENSE_GND) / \ + (1.0 * (AO_PYRO_R_SENSE_GND + AO_PYRO_R_PYRO_SENSE)) / 3.3 * AO_ADC_MAX))) + + +#define VOLTS_TO_FIRE(x) ((int16_t) ((x) * ((1.0 * AO_FIRE_R_SENSE_GND) / \ + (1.0 * (AO_FIRE_R_SENSE_GND + AO_FIRE_R_FET_SENSE)) / 3.3 * AO_ADC_MAX))) /* convert ADC value to voltage in tenths, then add .2 for the diode drop */ query.battery = (packet->adc.batt + 96) / 192 + 2; @@ -165,22 +187,39 @@ ao_pad_monitor(void) * 27k / * gnd --- * + * v_pyro \ + * 200k igniter + * output / + * 200k \ + * sense relay + * 22k / + * gnd --- + * * If the relay is closed, then sense will be 0 * If no igniter is present, then sense will be v_pyro * 27k/227k = pyro * 127 / 227 ~= pyro/2 * If igniter is present, then sense will be v_pyro * 27k/127k ~= v_pyro / 20 = pyro */ +#if AO_FIRE_R_POWER_FET if (sense <= pyro / 8) { status = AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_CLOSED; if ((ao_time() % 100) < 50) cur |= AO_LED_CONTINUITY(c); - } - else if (pyro / 8 * 3 <= sense && sense <= pyro / 8 * 5) + } else + if (pyro / 8 * 3 <= sense && sense <= pyro / 8 * 5) status = AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_OPEN; else if (pyro / 8 * 7 <= sense) { status = AO_PAD_IGNITER_STATUS_GOOD_IGNITER_RELAY_OPEN; cur |= AO_LED_CONTINUITY(c); } +#else + if (sense >= pyro / 8 * 5) { + status = AO_PAD_IGNITER_STATUS_GOOD_IGNITER_RELAY_OPEN; + cur |= AO_LED_CONTINUITY(c); + } else { + status = AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_OPEN; + } +#endif query.igniter_status[c] = status; } if (cur != prev) { @@ -244,6 +283,10 @@ ao_pad_read_box(void) #define ao_pad_read_box() 0 #endif +#ifdef PAD_BOX +#define ao_pad_read_box() PAD_BOX +#endif + static void ao_pad(void) { diff --git a/src/telefire-v0.1/ao_pins.h b/src/telefire-v0.1/ao_pins.h index 47ae663f..1087c7c9 100644 --- a/src/telefire-v0.1/ao_pins.h +++ b/src/telefire-v0.1/ao_pins.h @@ -39,6 +39,8 @@ #define PACKET_HAS_MASTER 0 #define PACKET_HAS_SLAVE 0 +#define PAD_BOX 0 + #define AO_LED_CONTINUITY(c) (1 << ((c) + 2)) #define AO_LED_CONTINUITY_MASK (0xc) #define AO_LED_ARMED 0x10 -- cgit v1.2.3 From 93100ae8d4c8bd8fd6bdeff2cdc87b613c5d8058 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 21 Dec 2015 21:47:10 -0800 Subject: altos: Allow for pad boxes with different sensor configurations This allows for a pad box without a resistor from power to each FET. That resistor is needed to detect welded relays, but in a solid-state system, that's not a possibility. Signed-off-by: Keith Packard --- src/drivers/ao_pad.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_pad.c b/src/drivers/ao_pad.c index e51d237b..99a90e77 100644 --- a/src/drivers/ao_pad.c +++ b/src/drivers/ao_pad.c @@ -155,14 +155,16 @@ ao_pad_monitor(void) if (pyro > VOLTS_TO_PYRO(10)) { query.arm_status = AO_PAD_ARM_STATUS_ARMED; cur |= AO_LED_ARMED; - } else if (pyro < VOLTS_TO_PYRO(5)) { - query.arm_status = AO_PAD_ARM_STATUS_DISARMED; - arm_beep_time = 0; - } else { +#if AO_FIRE_R_POWER_FET + } else if (pyro > VOLTS_TO_PYRO(5)) { if ((ao_time() % 100) < 50) cur |= AO_LED_ARMED; query.arm_status = AO_PAD_ARM_STATUS_UNKNOWN; arm_beep_time = 0; +#endif + } else { + query.arm_status = AO_PAD_ARM_STATUS_DISARMED; + arm_beep_time = 0; } if ((ao_time() - ao_pad_packet_time) > AO_SEC_TO_TICKS(2)) cur |= AO_LED_RED; @@ -279,8 +281,10 @@ ao_pad_read_box(void) l = byte & 0xf; return h * 10 + l; } -#else -#define ao_pad_read_box() 0 +#endif + +#if HAS_FIXED_PAD_BOX +#define ao_pad_read_box() ao_config.pad_box #endif #ifdef PAD_BOX -- cgit v1.2.3 From 4c2a7503373c0584de77f9b0e5632a58ab8bbd1f Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 25 Dec 2015 11:26:34 -0800 Subject: altos: Add ao_lco_two.c; alternate LCO interface code The LCO interface is likely to end up very device specific as the interactions depends on the input devices. Here's a version for TeleLCOTwo, which has two arming switches and a firing button. Signed-off-by: Keith Packard --- src/drivers/ao_lco_two.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 src/drivers/ao_lco_two.c (limited to 'src/drivers') diff --git a/src/drivers/ao_lco_two.c b/src/drivers/ao_lco_two.c new file mode 100644 index 00000000..0fd8e362 --- /dev/null +++ b/src/drivers/ao_lco_two.c @@ -0,0 +1,271 @@ +/* + * Copyright © 2012 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include +#include +#include +#include + +#define DEBUG 1 + +#if DEBUG +static uint8_t ao_lco_debug; +#define DEBUG_EVENT 1 +#define DEBUG_STATUS 2 +#define PRINTD(l, ...) do { if (!(ao_lco_debug & l)) break; printf ("\r%5u %s: ", ao_tick_count, __func__); printf(__VA_ARGS__); flush(); } while(0) +#else +#define PRINTD(l,...) +#endif + +#define AO_LCO_VALID_LAST 1 +#define AO_LCO_VALID_EVER 2 + +static uint8_t ao_lco_selected; +static uint8_t ao_lco_valid; +static uint8_t ao_lco_channels; +static uint16_t ao_lco_tick_offset; + +/* UI values */ +static uint8_t ao_lco_armed; +static uint8_t ao_lco_firing; +static uint8_t ao_lco_fire_down; + +#define ao_lco_box (ao_config.pad_box) + +static struct ao_pad_query ao_pad_query; + +#define MASK_SIZE(n) (((n) + 7) >> 3) +#define MASK_ID(n) ((n) >> 3) +#define MASK_SHIFT(n) ((n) & 7) + +static void +ao_lco_set_armed(int pad, int armed) +{ + uint8_t bit = (1 << pad); + + if (armed) { + ao_lco_selected |= bit; + ao_lco_armed |= bit; + } else { + ao_lco_selected &= ~bit; + ao_lco_armed &= ~bit; + } + PRINTD(DEBUG_EVENT, "pad %d bit 0x%x armed %d ao_lco_selected 0x%x ao_lco_armed 0x%x\n", + pad, bit, armed, ao_lco_selected, ao_lco_armed); + ao_wakeup(&ao_lco_armed); +} + +static void +ao_lco_input(void) +{ + static struct ao_event event; + + for (;;) { + ao_event_get(&event); + PRINTD(DEBUG_EVENT, "event type %d unit %d value %d\n", + event.type, event.unit, event.value); + switch (event.type) { + case AO_EVENT_BUTTON: + switch (event.unit) { + case AO_BUTTON_ARM_0: + ao_lco_set_armed(0, event.value); + break; +#if AO_BUTTON_ARM_NUM > 1 + case AO_BUTTON_ARM_1: + ao_lco_set_armed(1, event.value); + break; +#endif + case AO_BUTTON_FIRE: + if (ao_lco_armed) { + ao_lco_fire_down = 0; + ao_lco_firing = event.value; + PRINTD(DEBUG_EVENT, "Firing %d\n", ao_lco_firing); + ao_wakeup(&ao_lco_armed); + } + break; + } + break; + } + } +} + +static AO_LED_TYPE continuity_led[AO_LED_CONTINUITY_NUM] = { +#ifdef AO_LED_CONTINUITY_0 + AO_LED_CONTINUITY_0, +#endif +#ifdef AO_LED_CONTINUITY_1 + AO_LED_CONTINUITY_1, +#endif +#ifdef AO_LED_CONTINUITY_2 + AO_LED_CONTINUITY_2, +#endif +#ifdef AO_LED_CONTINUITY_3 + AO_LED_CONTINUITY_3, +#endif +#ifdef AO_LED_CONTINUITY_4 + AO_LED_CONTINUITY_4, +#endif +#ifdef AO_LED_CONTINUITY_5 + AO_LED_CONTINUITY_5, +#endif +#ifdef AO_LED_CONTINUITY_6 + AO_LED_CONTINUITY_6, +#endif +#ifdef AO_LED_CONTINUITY_7 + AO_LED_CONTINUITY_7, +#endif +}; + +static uint8_t +ao_lco_get_channels(void) +{ + int8_t r; + + r = ao_lco_query(ao_lco_box, &ao_pad_query, &ao_lco_tick_offset); + if (r == AO_RADIO_CMAC_OK) { + ao_lco_channels = ao_pad_query.channels; + ao_lco_valid = AO_LCO_VALID_LAST | AO_LCO_VALID_EVER; + } else + ao_lco_valid &= ~AO_LCO_VALID_LAST; + PRINTD(DEBUG_STATUS, "ao_lco_get_channels() rssi %d valid %d ret %d offset %d\n", ao_radio_cmac_rssi, ao_lco_valid, r, ao_lco_tick_offset); + ao_wakeup(&ao_pad_query); + return ao_lco_valid; +} + +static void +ao_lco_igniter_status(void) +{ + uint8_t c; + uint8_t t = 0; + + for (;;) { + ao_sleep(&ao_pad_query); + PRINTD(DEBUG_STATUS, "RSSI %d VALID %d\n", ao_radio_cmac_rssi, ao_lco_valid); + if (!(ao_lco_valid & AO_LCO_VALID_LAST)) { + ao_led_on(AO_LED_RED); + ao_led_off(AO_LED_GREEN|AO_LED_AMBER); + continue; + } + if (ao_radio_cmac_rssi < -90) { + ao_led_on(AO_LED_AMBER); + ao_led_off(AO_LED_RED|AO_LED_GREEN); + } else { + ao_led_on(AO_LED_GREEN); + ao_led_off(AO_LED_RED|AO_LED_AMBER); + } + if (ao_pad_query.arm_status) + ao_led_on(AO_LED_REMOTE_ARM); + else + ao_led_off(AO_LED_REMOTE_ARM); + + for (c = 0; c < AO_LED_CONTINUITY_NUM; c++) { + uint8_t status; + + if (ao_pad_query.channels & (1 << c)) + status = ao_pad_query.igniter_status[c]; + else + status = AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_OPEN; + PRINTD(DEBUG_STATUS, "\tchannel %d status %d\n", c, status); + if (status == AO_PAD_IGNITER_STATUS_GOOD_IGNITER_RELAY_OPEN) + ao_led_on(continuity_led[c]); + else + ao_led_off(continuity_led[c]); + } + t = 1-t; + } +} + +static void +ao_lco_arm_warn(void) +{ + int i; + for (;;) { + while (!ao_lco_armed) + ao_sleep(&ao_lco_armed); + for (i = 0; i < ao_lco_armed; i++) { + ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(100)); + ao_delay(AO_MS_TO_TICKS(100)); + } + ao_delay(AO_MS_TO_TICKS(300)); + } +} + +static struct ao_task ao_lco_input_task; +static struct ao_task ao_lco_monitor_task; +static struct ao_task ao_lco_arm_warn_task; +static struct ao_task ao_lco_igniter_status_task; + +static void +ao_lco_monitor(void) +{ + uint16_t delay; + + ao_add_task(&ao_lco_input_task, ao_lco_input, "lco input"); + ao_add_task(&ao_lco_arm_warn_task, ao_lco_arm_warn, "lco arm warn"); + ao_add_task(&ao_lco_igniter_status_task, ao_lco_igniter_status, "lco igniter status"); + ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200)); + for (;;) { + PRINTD(DEBUG_STATUS, "monitor armed %d firing %d\n", + ao_lco_armed, ao_lco_firing); + + if (ao_lco_armed && ao_lco_firing) { + ao_lco_ignite(); + } else { + ao_lco_get_channels(); + if (ao_lco_armed) { + if (ao_lco_selected) { + PRINTD(DEBUG_STATUS, "Arming pads %x\n", + ao_lco_selected); + if (ao_lco_valid & AO_LCO_VALID_EVER) { + ao_lco_arm(ao_lco_box, ao_lco_selected, ao_lco_tick_offset); + ao_delay(AO_MS_TO_TICKS(10)); + } + } + } + } + if (ao_lco_armed && ao_lco_firing) + delay = AO_MS_TO_TICKS(100); + else + delay = AO_SEC_TO_TICKS(1); + ao_sleep_for(&ao_lco_armed, delay); + } +} + +#if DEBUG +void +ao_lco_set_debug(void) +{ + ao_cmd_decimal(); + if (ao_cmd_status == ao_cmd_success) + ao_lco_debug = ao_cmd_lex_i; +} + +__code struct ao_cmds ao_lco_cmds[] = { + { ao_lco_set_debug, "D <0 off, 1 on>\0Debug" }, + { 0, NULL } +}; +#endif + +void +ao_lco_init(void) +{ + ao_add_task(&ao_lco_monitor_task, ao_lco_monitor, "lco monitor"); +#if DEBUG + ao_cmd_register(&ao_lco_cmds[0]); +#endif +} -- cgit v1.2.3 From aef5049cf8311927fada922730f85f31c8ddf177 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 25 Dec 2015 20:46:40 -0800 Subject: altos: Use simpler debounce logic for buttons Instead of waiting for a while after the transition to decide if it has stuck, signal the event right away and then ignore other transitions for the debounce interval. This seems to work just as reliably, but has the benefit of eliminating button latency at press time. Signed-off-by: Keith Packard --- src/drivers/ao_button.c | 52 ++++++++++++++++++++++++++------------------ src/telelco-v0.2/Makefile | 2 -- src/telelco-v0.3/Makefile | 2 -- src/telelcotwo-v0.1/Makefile | 2 -- 4 files changed, 31 insertions(+), 27 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_button.c b/src/drivers/ao_button.c index cdf07352..8e7dead7 100644 --- a/src/drivers/ao_button.c +++ b/src/drivers/ao_button.c @@ -18,7 +18,6 @@ #include #include #include -#include #if AO_EVENT #include #define ao_button_queue(b,v) ao_event_put_isr(AO_EVENT_BUTTON, b, v) @@ -26,9 +25,14 @@ #define ao_button_queue(b,v) #endif -#define AO_BUTTON_DEBOUNCE_HOLD 10 +#define AO_BUTTON_DEBOUNCE_INTERVAL AO_MS_TO_TICKS(50) -static struct ao_debounce ao_button_debounce[AO_BUTTON_COUNT]; +struct ao_button_state { + AO_TICK_TYPE time; + uint8_t value; +}; + +static struct ao_button_state ao_button_state[AO_BUTTON_COUNT]; #define port(q) AO_BUTTON_ ## q ## _PORT #define bit(q) AO_BUTTON_ ## q @@ -38,10 +42,8 @@ static struct ao_debounce ao_button_debounce[AO_BUTTON_COUNT]; #define ao_button_value(b) !ao_gpio_get(port(b), bit(b), pin(b)) static uint8_t -_ao_button_get(struct ao_debounce *debounce) +_ao_button_get(uint8_t b) { - uint8_t b = debounce - ao_button_debounce; - switch (b) { #if AO_BUTTON_COUNT > 0 case 0: return ao_button_value(0); @@ -63,22 +65,31 @@ _ao_button_get(struct ao_debounce *debounce) } static void -_ao_button_set(struct ao_debounce *debounce, uint8_t value) +_ao_button_check(uint8_t b) { - uint8_t b = debounce - ao_button_debounce; - - ao_button_queue(b, value); -} + uint8_t value = _ao_button_get(b); + if (value != ao_button_state[b].value) { + AO_TICK_TYPE now = ao_time(); -#define ao_button_update(b) ao_button_do(b, ao_gpio_get(port(b), bit(b), pin(b))) + if ((now - ao_button_state[b].time) >= AO_BUTTON_DEBOUNCE_INTERVAL) { + ao_button_state[b].value = value; + ao_button_queue(b, value); + } + ao_button_state[b].time = now; + } +} static void -ao_button_debounce_init(struct ao_debounce *debounce) { - ao_debounce_config(debounce, - _ao_button_get, - _ao_button_set, - AO_BUTTON_DEBOUNCE_HOLD); +_ao_button_init(uint8_t b) +{ + uint8_t m = ao_arch_irqsave(); + uint8_t value = _ao_button_get(b); + ao_button_state[b].value = value; + ao_button_state[b].time = ao_time(); + ao_button_queue(b, value); + ao_arch_irqrestore(m); + } static void @@ -87,17 +98,17 @@ ao_button_isr(void) uint8_t b; for (b = 0; b < AO_BUTTON_COUNT; b++) - _ao_debounce_start(&ao_button_debounce[b]); + _ao_button_check(b); } #define init(b) do { \ - ao_button_debounce_init(&ao_button_debounce[b]); \ ao_enable_port(port(b)); \ \ ao_exti_setup(port(b), bit(b), \ AO_BUTTON_MODE|AO_EXTI_MODE_FALLING|AO_EXTI_MODE_RISING|AO_EXTI_PRIORITY_MED, \ - ao_button_isr); \ + ao_button_isr); \ ao_exti_enable(port(b), bit(b)); \ + _ao_button_init(b); \ } while (0) void @@ -118,5 +129,4 @@ ao_button_init(void) #if AO_BUTTON_COUNT > 4 init(4); #endif - ao_debounce_init(); } diff --git a/src/telelco-v0.2/Makefile b/src/telelco-v0.2/Makefile index 7a21f099..8c1ced6c 100644 --- a/src/telelco-v0.2/Makefile +++ b/src/telelco-v0.2/Makefile @@ -22,7 +22,6 @@ INC = \ ao_radio_spi.h \ ao_radio_cmac.h \ ao_cc1120_CC1120.h \ - ao_debounce.h \ stm32l.h # @@ -61,7 +60,6 @@ ALTOS_SRC = \ ao_fec_tx.c \ ao_fec_rx.c \ ao_seven_segment.c \ - ao_debounce.c \ ao_quadrature.c \ ao_button.c \ ao_event.c \ diff --git a/src/telelco-v0.3/Makefile b/src/telelco-v0.3/Makefile index 83d3fc43..0bb0f9dc 100644 --- a/src/telelco-v0.3/Makefile +++ b/src/telelco-v0.3/Makefile @@ -23,7 +23,6 @@ INC = \ ao_radio_cmac.h \ ao_cc1200_CC1200.h \ ao_cc1200.h \ - ao_debounce.h \ stm32l.h # @@ -62,7 +61,6 @@ ALTOS_SRC = \ ao_fec_tx.c \ ao_fec_rx.c \ ao_seven_segment.c \ - ao_debounce.c \ ao_quadrature.c \ ao_button.c \ ao_event.c \ diff --git a/src/telelcotwo-v0.1/Makefile b/src/telelcotwo-v0.1/Makefile index 8ceb7d2c..42188bb2 100644 --- a/src/telelcotwo-v0.1/Makefile +++ b/src/telelcotwo-v0.1/Makefile @@ -19,7 +19,6 @@ INC = \ ao_radio_cmac.h \ ao_cc1200_CC1200.h \ ao_cc1200.h \ - ao_debounce.h \ stm32l.h # @@ -56,7 +55,6 @@ ALTOS_SRC = \ ao_aes_tables.c \ ao_fec_tx.c \ ao_fec_rx.c \ - ao_debounce.c \ ao_button.c \ ao_event.c \ ao_lco_two.c \ -- cgit v1.2.3 From 70e46100acf597014ce54cf3b642254ce1cba59b Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 5 Apr 2016 23:45:52 -0700 Subject: altos/telelcotwo: Add idle timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Puts TeleLCOTwo in a low power state (drawing about 80µA) after a timeout (default two minutes) to keep from killing the battery if the device is left turned on. Signed-off-by: Keith Packard --- src/drivers/ao_event.c | 16 +++++++++++++++ src/drivers/ao_event.h | 3 +++ src/drivers/ao_lco_two.c | 53 +++++++++++++++++++++++++++++++++++++++++++----- src/kernel/ao_config.c | 21 +++++++++++++++++++ src/kernel/ao_config.h | 3 ++- 5 files changed, 90 insertions(+), 6 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/ao_event.c b/src/drivers/ao_event.c index 5c0d2863..8f88d778 100644 --- a/src/drivers/ao_event.c +++ b/src/drivers/ao_event.c @@ -41,6 +41,22 @@ ao_event_get(struct ao_event *ev) ); } +uint8_t +ao_event_get_for(struct ao_event *ev, uint16_t timeout) +{ + uint8_t empty = 1; + ao_arch_critical( + while ((empty = ao_event_queue_empty())) + if (ao_sleep_for(&ao_event_queue, timeout)) + break; + if (!empty) { + *ev = ao_event_queue[ao_event_queue_remove]; + ao_event_queue_remove = ao_event_queue_next(ao_event_queue_remove); + } + ); + return empty; +} + /* called with interrupts disabled */ void ao_event_put_isr(uint8_t type, uint8_t unit, int32_t value) diff --git a/src/drivers/ao_event.h b/src/drivers/ao_event.h index 584a845a..ea89da23 100644 --- a/src/drivers/ao_event.h +++ b/src/drivers/ao_event.h @@ -32,6 +32,9 @@ struct ao_event { void ao_event_get(struct ao_event *ev); +uint8_t +ao_event_get_for(struct ao_event *ev, uint16_t timeout); + void ao_event_put_isr(uint8_t type, uint8_t unit, int32_t value); diff --git a/src/drivers/ao_lco_two.c b/src/drivers/ao_lco_two.c index 0fd8e362..f53fef7d 100644 --- a/src/drivers/ao_lco_two.c +++ b/src/drivers/ao_lco_two.c @@ -29,12 +29,13 @@ static uint8_t ao_lco_debug; #define DEBUG_STATUS 2 #define PRINTD(l, ...) do { if (!(ao_lco_debug & l)) break; printf ("\r%5u %s: ", ao_tick_count, __func__); printf(__VA_ARGS__); flush(); } while(0) #else -#define PRINTD(l,...) +#define PRINTD(l,...) #endif #define AO_LCO_VALID_LAST 1 #define AO_LCO_VALID_EVER 2 +static uint8_t ao_lco_suspended; static uint8_t ao_lco_selected; static uint8_t ao_lco_valid; static uint8_t ao_lco_channels; @@ -43,7 +44,6 @@ static uint16_t ao_lco_tick_offset; /* UI values */ static uint8_t ao_lco_armed; static uint8_t ao_lco_firing; -static uint8_t ao_lco_fire_down; #define ao_lco_box (ao_config.pad_box) @@ -70,13 +70,45 @@ ao_lco_set_armed(int pad, int armed) ao_wakeup(&ao_lco_armed); } +static void +ao_lco_suspend(void) +{ + if (!ao_lco_suspended) { + PRINTD(DEBUG_EVENT, "suspend\n"); + ao_lco_suspended = 1; + ao_lco_selected = 0; + ao_lco_armed = 0; + ao_wakeup(&ao_pad_query); + } +} + +static void +ao_lco_wakeup(void) +{ + if (ao_lco_suspended) { + ao_lco_suspended = 0; + ao_wakeup(&ao_lco_suspended); + } +} + static void ao_lco_input(void) { static struct ao_event event; + uint8_t timeout; + ao_config_get(); for (;;) { - ao_event_get(&event); + if (ao_config.pad_idle && !ao_lco_suspended) { + timeout = ao_event_get_for(&event, AO_SEC_TO_TICKS(ao_config.pad_idle)); + if (timeout) { + ao_lco_suspend(); + continue; + } + } else { + ao_event_get(&event); + } + ao_lco_wakeup(); PRINTD(DEBUG_EVENT, "event type %d unit %d value %d\n", event.type, event.unit, event.value); switch (event.type) { @@ -92,7 +124,6 @@ ao_lco_input(void) #endif case AO_BUTTON_FIRE: if (ao_lco_armed) { - ao_lco_fire_down = 0; ao_lco_firing = event.value; PRINTD(DEBUG_EVENT, "Firing %d\n", ao_lco_firing); ao_wakeup(&ao_lco_armed); @@ -155,6 +186,12 @@ ao_lco_igniter_status(void) for (;;) { ao_sleep(&ao_pad_query); + while (ao_lco_suspended) { + ao_led_off(AO_LED_GREEN|AO_LED_AMBER|AO_LED_RED|AO_LED_REMOTE_ARM); + for (c = 0; c < AO_LED_CONTINUITY_NUM; c++) + ao_led_off(continuity_led[c]); + ao_sleep(&ao_lco_suspended); + } PRINTD(DEBUG_STATUS, "RSSI %d VALID %d\n", ao_radio_cmac_rssi, ao_lco_valid); if (!(ao_lco_valid & AO_LCO_VALID_LAST)) { ao_led_on(AO_LED_RED); @@ -195,6 +232,8 @@ ao_lco_arm_warn(void) { int i; for (;;) { + while (ao_lco_suspended) + ao_sleep(&ao_lco_suspended); while (!ao_lco_armed) ao_sleep(&ao_lco_armed); for (i = 0; i < ao_lco_armed; i++) { @@ -220,6 +259,9 @@ ao_lco_monitor(void) ao_add_task(&ao_lco_igniter_status_task, ao_lco_igniter_status, "lco igniter status"); ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200)); for (;;) { + while (ao_lco_suspended) + ao_sleep(&ao_lco_suspended); + PRINTD(DEBUG_STATUS, "monitor armed %d firing %d\n", ao_lco_armed, ao_lco_firing); @@ -240,8 +282,9 @@ ao_lco_monitor(void) } if (ao_lco_armed && ao_lco_firing) delay = AO_MS_TO_TICKS(100); - else + else { delay = AO_SEC_TO_TICKS(1); + } ao_sleep_for(&ao_lco_armed, delay); } } diff --git a/src/kernel/ao_config.c b/src/kernel/ao_config.c index d51fbb41..f95ca893 100644 --- a/src/kernel/ao_config.c +++ b/src/kernel/ao_config.c @@ -227,6 +227,8 @@ _ao_config_get(void) #if HAS_FIXED_PAD_BOX if (minor < 22) ao_config.pad_box = 1; + if (minor < 23) + ao_config.pad_idle = 120; #endif ao_config.minor = AO_CONFIG_MINOR; ao_config_dirty = 1; @@ -920,6 +922,23 @@ ao_config_pad_box_set(void) ao_config.pad_box = ao_cmd_lex_i; _ao_config_edit_finish(); } + +void +ao_config_pad_idle_show(void) +{ + printf ("Idle timeout: %d\n", ao_config.pad_idle); +} + +void +ao_config_pad_idle_set(void) +{ + ao_cmd_decimal(); + if (ao_cmd_status != ao_cmd_success) + return; + _ao_config_edit_start(); + ao_config.pad_idle = ao_cmd_lex_i; + _ao_config_edit_finish(); +} #endif struct ao_config_var { @@ -1019,6 +1038,8 @@ __code struct ao_config_var ao_config_vars[] = { #if HAS_FIXED_PAD_BOX { "B \0Set pad box (1-99)", ao_config_pad_box_set, ao_config_pad_box_show }, + { "i \0Set idle timeout (0 disable)", + ao_config_pad_idle_set, ao_config_pad_idle_show }, #endif { "s\0Show", ao_config_show, 0 }, diff --git a/src/kernel/ao_config.h b/src/kernel/ao_config.h index f4e9af44..3c73ea49 100644 --- a/src/kernel/ao_config.h +++ b/src/kernel/ao_config.h @@ -57,7 +57,7 @@ #endif #define AO_CONFIG_MAJOR 1 -#define AO_CONFIG_MINOR 22 +#define AO_CONFIG_MINOR 23 #define AO_AES_LEN 16 @@ -120,6 +120,7 @@ struct ao_config { #endif #if HAS_FIXED_PAD_BOX uint8_t pad_box; /* minor version 22 */ + uint8_t pad_idle; /* minor version 23 */ #endif }; -- cgit v1.2.3 From 565404599fe9edf9ba16aec348eeb19ea31af743 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 25 Apr 2016 18:50:58 -0400 Subject: altos: Clear packet queue when starting packet master This avoids overfilling the packet buffer when disconnected. Applications using packet mode shouldn't expect that output be saved across master sessions. Signed-off-by: Keith Packard --- src/drivers/ao_packet_master.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/drivers') diff --git a/src/drivers/ao_packet_master.c b/src/drivers/ao_packet_master.c index 5e440db0..2beda4cb 100644 --- a/src/drivers/ao_packet_master.c +++ b/src/drivers/ao_packet_master.c @@ -117,6 +117,7 @@ ao_packet_forward(void) __reentrant { char c; ao_packet_enable = 1; + ao_packet_tx_used = 0; ao_cmd_white(); flush(); -- cgit v1.2.3