summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.gitignore1
-rw-r--r--src/Makefile4
-rw-r--r--src/attiny/ao_arch.h2
-rw-r--r--src/avr/ao_arch.h2
-rw-r--r--src/cc1111/ao_arch.h2
-rw-r--r--src/drivers/ao_btm.c168
-rw-r--r--src/drivers/ao_cc1120.c9
-rw-r--r--src/drivers/ao_cc115l.c60
-rw-r--r--src/drivers/ao_cc1200.c1462
-rw-r--r--src/drivers/ao_cc1200.h632
-rw-r--r--src/drivers/ao_cc1200_CC1200.h122
-rw-r--r--src/drivers/ao_packet_master.c2
-rw-r--r--src/easymini-v1.0/ao_pins.h2
-rw-r--r--src/kernel/ao.h12
-rw-r--r--src/kernel/ao_config.c9
-rw-r--r--src/kernel/ao_gps_print.c6
-rw-r--r--src/kernel/ao_log.h17
-rw-r--r--src/kernel/ao_monitor.c11
-rw-r--r--src/kernel/ao_serial.h12
-rw-r--r--src/kernel/ao_stdio.c2
-rw-r--r--src/kernel/ao_telemetry.c173
-rw-r--r--src/kernel/ao_telemetry.h10
-rw-r--r--src/kernel/ao_tracker.c10
-rw-r--r--src/lpc/ao_arch.h3
-rw-r--r--src/lpc/ao_arch_funcs.h18
-rw-r--r--src/lpc/ao_led_lpc.c11
-rw-r--r--src/lpc/ao_spi_lpc.c44
-rw-r--r--src/lpc/ao_usb_lpc.c91
-rw-r--r--src/microsplash/.gitignore2
-rw-r--r--src/microsplash/Makefile (renamed from src/microwater/Makefile)4
-rw-r--r--src/microsplash/ao_pins.h (renamed from src/microwater/ao_pins.h)0
-rw-r--r--src/stm/ao_arch_funcs.h2
-rw-r--r--src/stm/ao_led.c2
-rw-r--r--src/stm/ao_serial_stm.c65
-rw-r--r--src/stm/ao_spi_stm.c4
-rw-r--r--src/stmf0/Makefile-flash.defs61
-rw-r--r--src/stmf0/Makefile-stmf0.defs47
-rw-r--r--src/stmf0/Makefile.defs9
-rw-r--r--src/stmf0/altos-loader.ld95
-rw-r--r--src/stmf0/altos.ld98
-rw-r--r--src/stmf0/ao_adc_fast.c190
-rw-r--r--src/stmf0/ao_adc_fast.h89
-rw-r--r--src/stmf0/ao_arch.h157
-rw-r--r--src/stmf0/ao_arch_funcs.h400
-rw-r--r--src/stmf0/ao_boot_chain.c67
-rw-r--r--src/stmf0/ao_boot_pin.c46
-rw-r--r--src/stmf0/ao_crc.h58
-rw-r--r--src/stmf0/ao_dma_stm.c141
-rw-r--r--src/stmf0/ao_flash.h27
-rw-r--r--src/stmf0/ao_flash_loader_stm.c39
-rw-r--r--src/stmf0/ao_flash_stm.c131
-rw-r--r--src/stmf0/ao_flash_stm_pins.h38
-rw-r--r--src/stmf0/ao_interrupt.c185
-rw-r--r--src/stmf0/ao_led.c125
-rw-r--r--src/stmf0/ao_romconfig.c27
-rw-r--r--src/stmf0/ao_timer.c266
-rw-r--r--src/stmf0/ao_usb_stm.c1150
-rw-r--r--src/stmf0/registers.ld57
-rw-r--r--src/stmf0/stm32f0.h1667
-rw-r--r--src/telebt-v3.0/Makefile94
-rw-r--r--src/telebt-v3.0/ao_pins.h208
-rw-r--r--src/telebt-v3.0/ao_telebt.c60
-rw-r--r--src/telebt-v3.0/flash-loader/Makefile8
-rw-r--r--src/telebt-v3.0/flash-loader/ao_pins.h34
-rw-r--r--src/teledongle-v1.8/.gitignore2
-rw-r--r--src/teledongle-v1.8/Makefile88
-rw-r--r--src/teledongle-v1.8/ao_pins.h147
-rw-r--r--src/teledongle-v1.8/ao_teledongle.c54
-rw-r--r--src/teledongle-v3.0/.gitignore2
-rw-r--r--src/teledongle-v3.0/Makefile79
-rw-r--r--src/teledongle-v3.0/ao_pins.h113
-rw-r--r--src/teledongle-v3.0/ao_teledongle.c53
-rw-r--r--src/teledongle-v3.0/flash-loader/Makefile7
-rw-r--r--src/teledongle-v3.0/flash-loader/ao_pins.h35
-rw-r--r--src/test/.gitignore1
-rw-r--r--src/test/ao_flight_test.c9
-rw-r--r--src/test/ao_gps_test_ublox.c1
-rw-r--r--src/usbtrng-v2.0/.gitignore (renamed from src/microwater/.gitignore)2
-rw-r--r--src/usbtrng-v2.0/Makefile68
-rw-r--r--src/usbtrng-v2.0/ao_pins.h63
-rw-r--r--src/usbtrng-v2.0/ao_usbtrng.c93
-rw-r--r--src/usbtrng-v2.0/flash-loader/.gitignore2
-rw-r--r--src/usbtrng-v2.0/flash-loader/Makefile8
-rw-r--r--src/usbtrng-v2.0/flash-loader/ao_pins.h31
-rw-r--r--src/usbtrng/ao_pins.h2
-rw-r--r--src/usbtrng/ao_usbtrng.c195
86 files changed, 9328 insertions, 277 deletions
diff --git a/src/.gitignore b/src/.gitignore
index a8628fae..aad18d4c 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -1,4 +1,5 @@
Makedefs
altitude.h
altitude-pa.h
+altitude-pa-small.h
ao_whiten.h
diff --git a/src/Makefile b/src/Makefile
index 20126de6..05e99c7f 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -35,7 +35,9 @@ ARMM3DIRS=\
telegps-v1.0 telegps-v1.0/flash-loader \
telelco-v0.2 telelco-v0.2/flash-loader \
telescience-v0.2 telescience-v0.2/flash-loader \
- teleballoon-v2.0
+ teledongle-v3.0 teledongle-v3.0/flash-loader \
+ teleballoon-v2.0 \
+ telebt-v3.0 telebt-v3.0/flash-loader
ARMM0DIRS=\
easymini-v1.0 easymini-v1.0/flash-loader
diff --git a/src/attiny/ao_arch.h b/src/attiny/ao_arch.h
index 8140dd30..6ca12af6 100644
--- a/src/attiny/ao_arch.h
+++ b/src/attiny/ao_arch.h
@@ -31,6 +31,8 @@
#define AO_STACK_SIZE 116
+#define AO_PORT_TYPE uint8_t
+
/* Various definitions to make GCC look more like SDCC */
#define ao_arch_naked_declare __attribute__((naked))
diff --git a/src/avr/ao_arch.h b/src/avr/ao_arch.h
index d626e830..f8c7f042 100644
--- a/src/avr/ao_arch.h
+++ b/src/avr/ao_arch.h
@@ -41,6 +41,8 @@
#define AO_STACK_SIZE 116
#endif
+#define AO_PORT_TYPE uint8_t
+
/* Various definitions to make GCC look more like SDCC */
#define ao_arch_naked_declare __attribute__((naked))
diff --git a/src/cc1111/ao_arch.h b/src/cc1111/ao_arch.h
index fcac331b..b3c6b5dc 100644
--- a/src/cc1111/ao_arch.h
+++ b/src/cc1111/ao_arch.h
@@ -40,6 +40,8 @@
#define AO_STACK_END 0xfe
#define AO_STACK_SIZE (AO_STACK_END - AO_STACK_START + 1)
+#define AO_PORT_TYPE uint8_t
+
#define ao_arch_reboot() do { \
WDCTL = WDCTL_EN | WDCTL_MODE_WATCHDOG | WDCTL_INT_64; \
ao_delay(AO_SEC_TO_TICKS(2)); \
diff --git a/src/drivers/ao_btm.c b/src/drivers/ao_btm.c
index 3b6028a0..e6b28688 100644
--- a/src/drivers/ao_btm.c
+++ b/src/drivers/ao_btm.c
@@ -16,13 +16,16 @@
*/
#include "ao.h"
+#ifdef AO_BTM_INT_PORT
+#include <ao_exti.h>
+#endif
#ifndef ao_serial_btm_getchar
#define ao_serial_btm_putchar ao_serial1_putchar
#define _ao_serial_btm_pollchar _ao_serial1_pollchar
+#define _ao_serial_btm_sleep() ao_sleep((void *) &ao_serial1_rx_fifo)
#define ao_serial_btm_set_speed ao_serial1_set_speed
#define ao_serial_btm_drain ao_serial1_drain
-#define ao_serial_btm_rx_fifo ao_serial1_rx_fifo
#endif
int8_t ao_btm_stdio;
@@ -32,7 +35,7 @@ __xdata uint8_t ao_btm_connected;
#if BT_DEBUG
__xdata char ao_btm_buffer[256];
-int ao_btm_ptr;
+uint16_t ao_btm_ptr;
char ao_btm_dir;
static void
@@ -81,6 +84,10 @@ ao_btm_dump(void)
putchar(ao_btm_buffer[i]);
}
putchar('\n');
+ ao_cmd_decimal();
+ if (ao_cmd_status == ao_cmd_success && ao_cmd_lex_i)
+ ao_btm_ptr = 0;
+ ao_cmd_status = ao_cmd_success;
}
static void
@@ -95,9 +102,44 @@ ao_btm_speed(void)
ao_cmd_status = ao_cmd_syntax_error;
}
+static uint8_t ao_btm_enable;
+
+static void
+ao_btm_do_echo(void)
+{
+ int c;
+ while (ao_btm_enable) {
+ ao_arch_block_interrupts();
+ while ((c = _ao_serial_btm_pollchar()) == AO_READ_AGAIN && ao_btm_enable)
+ _ao_serial_btm_sleep();
+ ao_arch_release_interrupts();
+ if (c != AO_READ_AGAIN) {
+ putchar(c);
+ flush();
+ }
+ }
+ ao_exit();
+}
+
+static struct ao_task ao_btm_echo_task;
+
+static void
+ao_btm_send(void)
+{
+ int c;
+ ao_btm_enable = 1;
+ ao_add_task(&ao_btm_echo_task, ao_btm_do_echo, "btm-echo");
+ while ((c = getchar()) != '~') {
+ ao_serial_btm_putchar(c);
+ }
+ ao_btm_enable = 0;
+ ao_wakeup((void *) &ao_serial_btm_rx_fifo);
+}
+
__code struct ao_cmds ao_btm_cmds[] = {
{ ao_btm_dump, "d\0Dump btm buffer." },
{ ao_btm_speed, "s <19200,57600>\0Set btm serial speed." },
+ { ao_btm_send, "S\0BTM interactive mode. ~ to exit." },
{ 0, NULL },
};
@@ -125,7 +167,7 @@ ao_btm_getchar(void)
ao_arch_block_interrupts();
while ((c = _ao_serial_btm_pollchar()) == AO_READ_AGAIN) {
ao_alarm(AO_MS_TO_TICKS(10));
- c = ao_sleep(&ao_serial_btm_rx_fifo);
+ c = _ao_serial_btm_sleep();
ao_clear_alarm();
if (c) {
c = AO_READ_AGAIN;
@@ -146,6 +188,7 @@ ao_btm_get_line(void)
{
uint8_t ao_btm_reply_len = 0;
int c;
+ uint8_t l;
while ((c = ao_btm_getchar()) != AO_READ_AGAIN) {
ao_btm_log_in_char(c);
@@ -154,8 +197,8 @@ ao_btm_get_line(void)
if (c == '\r' || c == '\n')
break;
}
- for (c = ao_btm_reply_len; c < sizeof (ao_btm_reply);)
- ao_btm_reply[c++] = '\0';
+ for (l = ao_btm_reply_len; l < sizeof (ao_btm_reply);)
+ ao_btm_reply[l++] = '\0';
return ao_btm_reply_len;
}
@@ -214,7 +257,7 @@ ao_btm_string(__code char *cmd)
{
char c;
- while (c = *cmd++)
+ while ((c = *cmd++) != '\0')
ao_btm_putchar(c);
}
@@ -256,6 +299,52 @@ ao_btm_try_speed(uint8_t speed)
return 0;
}
+#if BT_LINK_ON_P2
+#define BT_PICTL_ICON PICTL_P2ICON
+#define BT_PIFG P2IFG
+#define BT_PDIR P2DIR
+#define BT_PINP P2INP
+#define BT_IEN2_PIE IEN2_P2IE
+#define BT_CC1111 1
+#endif
+#if BT_LINK_ON_P1
+#define BT_PICTL_ICON PICTL_P1ICON
+#define BT_PIFG P1IFG
+#define BT_PDIR P1DIR
+#define BT_PINP P1INP
+#define BT_IEN2_PIE IEN2_P1IE
+#define BT_CC1111 1
+#endif
+
+void
+ao_btm_check_link()
+{
+#if BT_CC1111
+ ao_arch_critical(
+ /* Check the pin and configure the interrupt detector to wait for the
+ * pin to flip the other way
+ */
+ if (BT_LINK_PIN) {
+ ao_btm_connected = 0;
+ PICTL |= BT_PICTL_ICON;
+ } else {
+ ao_btm_connected = 1;
+ PICTL &= ~BT_PICTL_ICON;
+ }
+ );
+#else
+ ao_arch_block_interrupts();
+ if (ao_gpio_get(AO_BTM_INT_PORT, AO_BTM_INT_PIN, AO_BTM_INT) == 0) {
+ ao_btm_connected = 1;
+ } else {
+ ao_btm_connected = 0;
+ }
+ ao_arch_release_interrupts();
+#endif
+}
+
+__xdata struct ao_task ao_btm_task;
+
/*
* A thread to initialize the bluetooth device and
* hang around to blink the LED when connected
@@ -298,6 +387,13 @@ ao_btm(void)
NULL);
ao_btm_echo(0);
+ /* Check current pin state */
+ ao_btm_check_link();
+
+#ifdef AO_BTM_INT_PORT
+ ao_exti_enable(AO_BTM_INT_PORT, AO_BTM_INT_PIN);
+#endif
+
for (;;) {
while (!ao_btm_connected)
ao_sleep(&ao_btm_connected);
@@ -308,40 +404,8 @@ ao_btm(void)
}
}
-__xdata struct ao_task ao_btm_task;
-
-#if BT_LINK_ON_P2
-#define BT_PICTL_ICON PICTL_P2ICON
-#define BT_PIFG P2IFG
-#define BT_PDIR P2DIR
-#define BT_PINP P2INP
-#define BT_IEN2_PIE IEN2_P2IE
-#endif
-#if BT_LINK_ON_P1
-#define BT_PICTL_ICON PICTL_P1ICON
-#define BT_PIFG P1IFG
-#define BT_PDIR P1DIR
-#define BT_PINP P1INP
-#define BT_IEN2_PIE IEN2_P1IE
-#endif
-
-void
-ao_btm_check_link()
-{
- ao_arch_critical(
- /* Check the pin and configure the interrupt detector to wait for the
- * pin to flip the other way
- */
- if (BT_LINK_PIN) {
- ao_btm_connected = 0;
- PICTL |= BT_PICTL_ICON;
- } else {
- ao_btm_connected = 1;
- PICTL &= ~BT_PICTL_ICON;
- }
- );
-}
+#if BT_CC1111
void
ao_btm_isr(void)
#if BT_LINK_ON_P1
@@ -357,6 +421,16 @@ ao_btm_isr(void)
}
BT_PIFG = 0;
}
+#endif
+
+#ifdef AO_BTM_INT_PORT
+void
+ao_btm_isr(void)
+{
+ ao_btm_check_link();
+ ao_wakeup(&ao_btm_connected);
+}
+#endif
void
ao_btm_init (void)
@@ -365,6 +439,18 @@ ao_btm_init (void)
ao_serial_btm_set_speed(AO_SERIAL_SPEED_19200);
+#ifdef AO_BTM_RESET_PORT
+ ao_enable_output(AO_BTM_RESET_PORT,AO_BTM_RESET_PIN,AO_BTM_RESET,0);
+#endif
+
+#ifdef AO_BTM_INT_PORT
+ ao_enable_port(AO_BTM_INT_PORT);
+ ao_exti_setup(AO_BTM_INT_PORT, AO_BTM_INT_PIN,
+ AO_EXTI_MODE_FALLING|AO_EXTI_MODE_RISING|AO_EXTI_PRIORITY_LOW,
+ ao_btm_isr);
+#endif
+
+#if BT_CC1111
#if BT_LINK_ON_P1
/*
* Configure ser reset line
@@ -386,9 +472,7 @@ ao_btm_init (void)
/* Enable interrupts */
IEN2 |= BT_IEN2_PIE;
-
- /* Check current pin state */
- ao_btm_check_link();
+#endif
#if BT_LINK_ON_P2
/* Eable the pin interrupt */
diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c
index 3ea8b704..90d6cc75 100644
--- a/src/drivers/ao_cc1120.c
+++ b/src/drivers/ao_cc1120.c
@@ -1125,12 +1125,12 @@ ao_radio_recv(__xdata void *d, uint8_t size, uint8_t timeout)
ao_exti_set_mode(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN,
AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH);
- ao_exti_set_callback(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, ao_radio_rx_isr);
- ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
-
rx_starting = 1;
rx_task_id = ao_cur_task->task_id;
+ ao_exti_set_callback(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, ao_radio_rx_isr);
+ ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
+
ao_radio_strobe(CC1120_SRX);
if (timeout)
@@ -1148,8 +1148,9 @@ ao_radio_recv(__xdata void *d, uint8_t size, uint8_t timeout)
ao_clear_alarm();
if (ao_radio_abort) {
+ if (rx_task_id_save == 0)
+ ao_radio_burst_read_stop();
ret = 0;
- rx_task_id = 0;
goto abort;
}
diff --git a/src/drivers/ao_cc115l.c b/src/drivers/ao_cc115l.c
index cf61acfe..0246ba02 100644
--- a/src/drivers/ao_cc115l.c
+++ b/src/drivers/ao_cc115l.c
@@ -38,7 +38,7 @@ static uint8_t ao_radio_abort; /* radio operation should abort */
#define FOSC 26000000
-#define ao_radio_select() ao_spi_get_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS,AO_SPI_SPEED_1MHz)
+#define ao_radio_select() ao_spi_get_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS,AO_SPI_SPEED_6MHz)
#define ao_radio_deselect() ao_spi_put_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS)
#define ao_radio_spi_send(d,l) ao_spi_send((d), (l), AO_CC115L_SPI_BUS)
#define ao_radio_spi_send_fixed(d,l) ao_spi_send_fixed((d), (l), AO_CC115L_SPI_BUS)
@@ -246,6 +246,8 @@ ao_radio_idle(void)
}
/* Flush any pending TX bytes */
ao_radio_strobe(CC115L_SFTX);
+ /* Make sure the RF calibration is current */
+ ao_radio_strobe(CC115L_SCAL);
}
/*
@@ -325,23 +327,22 @@ static const struct {
static const uint16_t packet_setup[] = {
CC115L_MDMCFG3, (PACKET_DRATE_M),
- CC115L_MDMCFG2, (0x00 |
- (CC115L_MDMCFG2_MOD_FORMAT_GFSK << CC115L_MDMCFG2_MOD_FORMAT) |
+ CC115L_MDMCFG2, ((CC115L_MDMCFG2_MOD_FORMAT_GFSK << CC115L_MDMCFG2_MOD_FORMAT) |
(0 << CC115L_MDMCFG2_MANCHESTER_EN) |
(CC115L_MDMCFG2_SYNC_MODE_16BITS << CC115L_MDMCFG2_SYNC_MODE)),
};
/*
- * RDF deviation is 5kHz
+ * RDF deviation is 3kHz
*
* fdev = fosc >> 17 * (8 + dev_m) << dev_e
*
- * 26e6 / (2 ** 17) * (8 + 4) * (2 ** 1) = 4761Hz
+ * 26e6 / (2 ** 17) * (8 + 7) * (2 ** 0) = 2975
*/
-#define RDF_DEV_E 1
-#define RDF_DEV_M 4
+#define RDF_DEV_E 0
+#define RDF_DEV_M 7
/*
* For our RDF beacon, set the symbol rate to 2kBaud (for a 1kHz tone)
@@ -364,8 +365,7 @@ static const uint16_t rdf_setup[] = {
CC115L_MDMCFG4, ((0xf << 4) |
(RDF_DRATE_E << CC115L_MDMCFG4_DRATE_E)),
CC115L_MDMCFG3, (RDF_DRATE_M),
- CC115L_MDMCFG2, (0x00 |
- (CC115L_MDMCFG2_MOD_FORMAT_GFSK << CC115L_MDMCFG2_MOD_FORMAT) |
+ CC115L_MDMCFG2, ((CC115L_MDMCFG2_MOD_FORMAT_GFSK << CC115L_MDMCFG2_MOD_FORMAT) |
(0 << CC115L_MDMCFG2_MANCHESTER_EN) |
(CC115L_MDMCFG2_SYNC_MODE_NONE << CC115L_MDMCFG2_SYNC_MODE)),
};
@@ -401,8 +401,7 @@ static const uint16_t aprs_setup[] = {
CC115L_MDMCFG4, ((0xf << 4) |
(APRS_DRATE_E << CC115L_MDMCFG4_DRATE_E)),
CC115L_MDMCFG3, (APRS_DRATE_M),
- CC115L_MDMCFG2, (0x00 |
- (CC115L_MDMCFG2_MOD_FORMAT_GFSK << CC115L_MDMCFG2_MOD_FORMAT) |
+ CC115L_MDMCFG2, ((CC115L_MDMCFG2_MOD_FORMAT_GFSK << CC115L_MDMCFG2_MOD_FORMAT) |
(0 << CC115L_MDMCFG2_MANCHESTER_EN) |
(CC115L_MDMCFG2_SYNC_MODE_NONE << CC115L_MDMCFG2_SYNC_MODE)),
};
@@ -491,16 +490,21 @@ static const uint16_t radio_setup[] = {
AO_CC115L_DONE_INT_GPIO_IOCFG, CC115L_IOCFG_GPIO_CFG_PA_PD | (1 << CC115L_IOCFG_GPIO_INV),
CC115L_FIFOTHR, 0x47, /* TX FIFO Thresholds */
- CC115L_MDMCFG1, (0x00 |
- (CC115L_MDMCFG1_NUM_PREAMBLE_4 << CC115L_MDMCFG1_NUM_PREAMBLE) |
- (1 << CC115L_MDMCFG1_CHANSPC_E)),
+ CC115L_MDMCFG1, /* Modem Configuration */
+ ((CC115L_MDMCFG1_NUM_PREAMBLE_4 << CC115L_MDMCFG1_NUM_PREAMBLE) |
+ (1 << CC115L_MDMCFG1_CHANSPC_E)),
CC115L_MDMCFG0, 248, /* Channel spacing M value (100kHz channels) */
+ CC115L_MCSM1, 0x30, /* Main Radio Control State Machine Configuration */
CC115L_MCSM0, 0x38, /* Main Radio Control State Machine Configuration */
CC115L_RESERVED_0X20, 0xfb, /* Use setting from SmartRF Studio */
+ CC115L_FREND0, 0x10, /* Front End TX Configuration */
CC115L_FSCAL3, 0xe9, /* Frequency Synthesizer Calibration */
CC115L_FSCAL2, 0x2a, /* Frequency Synthesizer Calibration */
CC115L_FSCAL1, 0x00, /* Frequency Synthesizer Calibration */
CC115L_FSCAL0, 0x1f, /* Frequency Synthesizer Calibration */
+ CC115L_RESERVED_0X29, 0x59, /* RESERVED */
+ CC115L_RESERVED_0X2A, 0x7f, /* RESERVED */
+ CC115L_RESERVED_0X2B, 0x3f, /* RESERVED */
CC115L_TEST2, 0x81, /* Various Test Settings */
CC115L_TEST1, 0x35, /* Various Test Settings */
CC115L_TEST0, 0x09, /* Various Test Settings */
@@ -508,6 +512,18 @@ static const uint16_t radio_setup[] = {
static uint8_t ao_radio_configured = 0;
+#if HAS_RADIO_POWER
+#define RADIO_POWER ao_config.radio_power
+#else
+
+#if 0
+#define RADIO_POWER 0x03 /* -31.75dBm on the test board */
+#endif
+
+#define RADIO_POWER 0xc0 /* full power */
+
+#endif
+
static void
ao_radio_setup(void)
{
@@ -523,6 +539,8 @@ ao_radio_setup(void)
ao_config_get();
+ ao_radio_reg_write(CC115L_PA, RADIO_POWER);
+
ao_radio_strobe(CC115L_SCAL);
ao_radio_configured = 1;
@@ -553,6 +571,8 @@ ao_radio_get(void)
ao_radio_reg_write(CC115L_FREQ1, ao_config.radio_setting >> 8);
ao_radio_reg_write(CC115L_FREQ0, ao_config.radio_setting);
last_radio_setting = ao_config.radio_setting;
+ /* Make sure the RF calibration is current */
+ ao_radio_strobe(CC115L_SCAL);
}
if (ao_config.radio_rate != last_radio_rate) {
ao_radio_mode &= ~AO_RADIO_MODE_BITS_PACKET_TX;
@@ -666,23 +686,11 @@ ao_radio_rdf_abort(void)
#define POWER_STEP 0x08
-#if HAS_RADIO_POWER
-#define RADIO_POWER ao_config.radio_power
-#else
-#define RADIO_POWER 0xc0
-#endif
-
static void
ao_radio_stx(void)
{
- uint8_t power;
ao_radio_pa_on();
- ao_radio_reg_write(CC115L_PA, 0);
ao_radio_strobe(CC115L_STX);
- for (power = POWER_STEP; power < RADIO_POWER; power += POWER_STEP)
- ao_radio_reg_write(CC115L_PA, power);
- if (power != RADIO_POWER)
- ao_radio_reg_write(CC115L_PA, RADIO_POWER);
}
static void
diff --git a/src/drivers/ao_cc1200.c b/src/drivers/ao_cc1200.c
new file mode 100644
index 00000000..8546900e
--- /dev/null
+++ b/src/drivers/ao_cc1200.c
@@ -0,0 +1,1462 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * 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_cc1200.h>
+#include <ao_exti.h>
+#include <ao_fec.h>
+#include <ao_packet.h>
+
+static uint8_t ao_radio_mutex;
+
+static uint8_t ao_radio_wake; /* radio ready. Also used as sleep address */
+static uint8_t ao_radio_abort; /* radio operation should abort */
+
+int8_t ao_radio_rssi; /* Last received RSSI value */
+
+#ifndef CC1200_DEBUG
+#define CC1200_DEBUG 0
+#endif
+
+#ifndef CC1200_LOW_LEVEL_DEBUG
+#define CC1200_LOW_LEVEL_DEBUG 0
+#endif
+
+#define CC1200_TRACE 0
+#define CC1200_APRS_TRACE 0
+
+extern const uint32_t ao_radio_cal;
+
+#define FOSC 40000000
+
+#define ao_radio_select() ao_spi_get_mask(AO_CC1200_SPI_CS_PORT,(1 << AO_CC1200_SPI_CS_PIN),AO_CC1200_SPI_BUS,AO_SPI_SPEED_FAST)
+#define ao_radio_deselect() ao_spi_put_mask(AO_CC1200_SPI_CS_PORT,(1 << AO_CC1200_SPI_CS_PIN),AO_CC1200_SPI_BUS)
+#define ao_radio_spi_send(d,l) ao_spi_send((d), (l), AO_CC1200_SPI_BUS)
+#define ao_radio_spi_send_fixed(d,l) ao_spi_send_fixed((d), (l), AO_CC1200_SPI_BUS)
+#define ao_radio_spi_recv(d,l) ao_spi_recv((d), (l), AO_CC1200_SPI_BUS)
+#define ao_radio_duplex(o,i,l) ao_spi_duplex((o), (i), (l), AO_CC1200_SPI_BUS)
+
+static uint8_t
+ao_radio_reg_read(uint16_t addr)
+{
+ uint8_t data[2];
+ uint8_t d;
+
+#if CC1200_TRACE
+ printf("\t\tao_radio_reg_read (%04x): ", addr); flush();
+#endif
+ if (CC1200_IS_EXTENDED(addr)) {
+ data[0] = ((1 << CC1200_READ) |
+ (0 << CC1200_BURST) |
+ CC1200_EXTENDED);
+ data[1] = addr;
+ d = 2;
+ } else {
+ data[0] = ((1 << CC1200_READ) |
+ (0 << CC1200_BURST) |
+ addr);
+ d = 1;
+ }
+ ao_radio_select();
+ ao_radio_spi_send(data, d);
+ ao_radio_spi_recv(data, 1);
+ ao_radio_deselect();
+#if CC1200_TRACE
+ printf (" %02x\n", data[0]);
+#endif
+ return data[0];
+}
+
+static void
+ao_radio_reg_write(uint16_t addr, uint8_t value)
+{
+ uint8_t data[3];
+ uint8_t d;
+
+#if CC1200_TRACE
+ printf("\t\tao_radio_reg_write (%04x): %02x\n", addr, value);
+#endif
+ if (CC1200_IS_EXTENDED(addr)) {
+ data[0] = ((0 << CC1200_READ) |
+ (0 << CC1200_BURST) |
+ CC1200_EXTENDED);
+ data[1] = addr;
+ d = 2;
+ } else {
+ data[0] = ((0 << CC1200_READ) |
+ (0 << CC1200_BURST) |
+ addr);
+ d = 1;
+ }
+ data[d] = value;
+ ao_radio_select();
+ ao_radio_spi_send(data, d+1);
+ ao_radio_deselect();
+#if CC1200_TRACE
+ (void) ao_radio_reg_read(addr);
+#endif
+}
+
+static uint8_t
+ao_radio_strobe(uint8_t addr)
+{
+ uint8_t in;
+
+#if CC1200_TRACE
+ printf("\t\tao_radio_strobe (%02x): ", addr); flush();
+#endif
+ ao_radio_select();
+ ao_radio_duplex(&addr, &in, 1);
+ ao_radio_deselect();
+#if CC1200_TRACE
+ printf("%02x\n", in); flush();
+#endif
+ return in;
+}
+
+static uint8_t
+ao_radio_fifo_read(uint8_t *data, uint8_t len)
+{
+ uint8_t addr = ((1 << CC1200_READ) |
+ (1 << CC1200_BURST) |
+ CC1200_FIFO);
+ uint8_t status;
+
+ ao_radio_select();
+ ao_radio_duplex(&addr, &status, 1);
+ ao_radio_spi_recv(data, len);
+ ao_radio_deselect();
+ return status;
+}
+
+static uint8_t
+ao_radio_fifo_write_start(void)
+{
+ uint8_t addr = ((0 << CC1200_READ) |
+ (1 << CC1200_BURST) |
+ CC1200_FIFO);
+ uint8_t status;
+
+ ao_radio_select();
+ ao_radio_duplex(&addr, &status, 1);
+ return status;
+}
+
+static inline uint8_t ao_radio_fifo_write_stop(uint8_t status) {
+ ao_radio_deselect();
+ return status;
+}
+
+static uint8_t
+ao_radio_fifo_write(const uint8_t *data, uint8_t len)
+{
+ uint8_t status = ao_radio_fifo_write_start();
+ ao_radio_spi_send(data, len);
+ return ao_radio_fifo_write_stop(status);
+}
+
+static uint8_t
+ao_radio_fifo_write_fixed(uint8_t data, uint8_t len)
+{
+ uint8_t status = ao_radio_fifo_write_start();
+ ao_radio_spi_send_fixed(data, len);
+ return ao_radio_fifo_write_stop(status);
+}
+
+static uint8_t
+ao_radio_tx_fifo_space(void)
+{
+ return CC1200_FIFO_SIZE - ao_radio_reg_read(CC1200_NUM_TXBYTES);
+}
+
+static uint8_t
+ao_radio_status(void)
+{
+ return ao_radio_strobe (CC1200_SNOP);
+}
+
+void
+ao_radio_recv_abort(void)
+{
+ ao_radio_abort = 1;
+ ao_wakeup(&ao_radio_wake);
+}
+
+#define ao_radio_rdf_value 0x55
+
+static void
+ao_radio_isr(void)
+{
+ ao_exti_disable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN);
+ ao_radio_wake = 1;
+ ao_wakeup(&ao_radio_wake);
+}
+
+static void
+ao_radio_start_tx(void)
+{
+ ao_exti_enable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN);
+ ao_radio_strobe(CC1200_STX);
+}
+
+static void
+ao_radio_start_rx(void)
+{
+ ao_exti_enable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN);
+ ao_radio_strobe(CC1200_SRX);
+}
+
+static void
+ao_radio_idle(void)
+{
+ for (;;) {
+ uint8_t state = (ao_radio_strobe(CC1200_SIDLE) >> CC1200_STATUS_STATE) & CC1200_STATUS_STATE_MASK;
+ if (state == CC1200_STATUS_STATE_IDLE)
+ break;
+ if (state == CC1200_STATUS_STATE_TX_FIFO_ERROR)
+ ao_radio_strobe(CC1200_SFTX);
+ if (state == CC1200_STATUS_STATE_RX_FIFO_ERROR)
+ ao_radio_strobe(CC1200_SFRX);
+ }
+ /* Flush any pending data in the fifos */
+ ao_radio_strobe(CC1200_SFTX);
+ ao_radio_strobe(CC1200_SFRX);
+ /* Make sure the RF calibration is current */
+ ao_radio_strobe(CC1200_SCAL);
+}
+
+/*
+ * Packet deviation
+ *
+ * fdev = fosc >> 22 * (256 + dev_m) << dev_e
+ *
+ * Deviation for 38400 baud should be 20.5kHz:
+ *
+ * 40e6 / (2 ** 22) * (256 + 13) * (2 ** 3) = 20523Hz
+ *
+ * Deviation for 9600 baud should be 5.125kHz:
+ *
+ * 40e6 / (2 ** 22) * (256 + 13) * (2 ** 1) = 5131Hz
+ *
+ * Deviation for 2400 baud should be 1.28125kHz, but cc1111 and
+ * cc115l can't do that, so we'll use 1.5kHz instead:
+ *
+ * 40e6 / (2 ** 21) * (79) = 1506Hz
+ */
+
+#define PACKET_DEV_M_384 13
+#define PACKET_DEV_E_384 3
+
+#define PACKET_DEV_M_96 13
+#define PACKET_DEV_E_96 1
+
+#define PACKET_DEV_M_24 79
+#define PACKET_DEV_E_24 0
+
+/*
+ * For our packet data
+ *
+ * (2**20 + DATARATE_M) * 2 ** DATARATE_E
+ * Rdata = -------------------------------------- * fosc
+ * 2 ** 39
+ *
+ * Given the bit period of the baseband, T, the bandwidth of the
+ * baseband signal is B = 1/(2T). The overall bandwidth of the
+ * modulated signal is then Channel bandwidth = 2Δf + 2B.
+ *
+ * 38400 -- 2 * 20500 + 38400 = 79.4 kHz
+ * 9600 -- 2 * 5.125 + 9600 = 19.9 kHz
+ * 2400 -- 2 * 1.5 + 2400 = 5.4 khz
+ *
+ * Symbol rate 38400 Baud:
+ *
+ * DATARATE_M = 1013008
+ * DATARATE_E = 8
+ * CHANBW = 104.16667
+ *
+ * Symbol rate 9600 Baud:
+ *
+ * DATARATE_M = 1013008
+ * DATARATE_E = 6
+ * CHANBW = 26.042 (round to 19.8)
+ *
+ * Symbol rate 2400 Baud:
+ *
+ * DATARATE_M = 1013008
+ * DATARATE_E = 4
+ * CHANBW = 5.0 (round to 9.5)
+ */
+
+#define PACKET_SYMBOL_RATE_M 1013008
+
+#define PACKET_SYMBOL_RATE_E_384 8
+
+/* 200 / 2 = 100 */
+#define PACKET_CHAN_BW_384 ((CC1200_CHAN_BW_ADC_CIC_DECFACT_12 << CC1200_CHAN_BW_ADC_CIC_DECFACT) | \
+ (16 << CC1200_CHAN_BW_BB_CIC_DECFACT))
+
+#define PACKET_SYMBOL_RATE_E_96 6
+/* 200 / 10 = 20 */
+#define PACKET_CHAN_BW_96 ((CC1200_CHAN_BW_ADC_CIC_DECFACT_48 << CC1200_CHAN_BW_ADC_CIC_DECFACT) | \
+ (16 << CC1200_CHAN_BW_BB_CIC_DECFACT))
+
+#define PACKET_SYMBOL_RATE_E_24 4
+/* 200 / 25 = 8 */
+#define PACKET_CHAN_BW_24 ((CC1200_CHAN_BW_ADC_CIC_DECFACT_48 << CC1200_CHAN_BW_ADC_CIC_DECFACT) | \
+ (44 << CC1200_CHAN_BW_BB_CIC_DECFACT))
+
+static const uint16_t packet_setup[] = {
+ CC1200_SYMBOL_RATE1, ((PACKET_SYMBOL_RATE_M >> 8) & 0xff),
+ CC1200_SYMBOL_RATE0, ((PACKET_SYMBOL_RATE_M >> 0) & 0xff),
+ CC1200_PKT_CFG2, /* Packet Configuration Reg. 2 */
+ ((0 << CC1200_PKT_CFG2_FG_MODE_EN) |
+ (CC1200_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1200_PKT_CFG2_CCA_MODE) |
+ (CC1200_PKT_CFG2_PKT_FORMAT_NORMAL << CC1200_PKT_CFG2_PKT_FORMAT)),
+ CC1200_PKT_CFG1, /* Packet Configuration Reg. 1 */
+ ((1 << CC1200_PKT_CFG1_FEC_EN) |
+ (1 << CC1200_PKT_CFG1_WHITE_DATA) |
+ (0 << CC1200_PKT_CFG1_PN9_SWAP_EN) |
+ (CC1200_PKT_CFG1_ADDR_CHECK_CFG_NONE << CC1200_PKT_CFG1_ADDR_CHECK_CFG) |
+ (CC1200_PKT_CFG1_CRC_CFG_CRC16_INIT_ONES << CC1200_PKT_CFG1_CRC_CFG) |
+ (1 << CC1200_PKT_CFG1_APPEND_STATUS)),
+ CC1200_PREAMBLE_CFG1, ((CC1200_PREAMBLE_CFG1_NUM_PREAMBLE_4_BYTES << CC1200_PREAMBLE_CFG1_NUM_PREAMBLE) |
+ (CC1200_PREAMBLE_CFG1_PREAMBLE_WORD_AA << CC1200_PREAMBLE_CFG1_PREAMBLE_WORD)),
+};
+
+static const uint16_t packet_setup_384[] = {
+ CC1200_DEVIATION_M, PACKET_DEV_M_384,
+ CC1200_MODCFG_DEV_E, ((CC1200_MODCFG_DEV_E_MODEM_MODE_NORMAL << CC1200_MODCFG_DEV_E_MODEM_MODE) |
+ (CC1200_MODCFG_DEV_E_MOD_FORMAT_2_GFSK << CC1200_MODCFG_DEV_E_MOD_FORMAT) |
+ (PACKET_DEV_E_384 << CC1200_MODCFG_DEV_E_DEV_E)),
+ CC1200_SYMBOL_RATE2, ((PACKET_SYMBOL_RATE_E_384 << CC1200_SYMBOL_RATE2_DATARATE_E) |
+ (((PACKET_SYMBOL_RATE_M >> 16) & CC1200_SYMBOL_RATE2_DATARATE_M_19_16_MASK) << CC1200_SYMBOL_RATE2_DATARATE_M_19_16)),
+ CC1200_CHAN_BW, PACKET_CHAN_BW_384,
+ CC1200_MDMCFG2, /* General Modem Parameter Configuration Reg. 2 */
+ ((CC1200_MDMCFG2_ASK_SHAPE_8 << CC1200_MDMCFG2_ASK_SHAPE) |
+ (CC1200_MDMCFG2_SYMBOL_MAP_CFG_MODE_0 << CC1200_MDMCFG2_SYMBOL_MAP_CFG) |
+ (CC1200_MDMCFG2_UPSAMPLER_P_8 << CC1200_MDMCFG2_UPSAMPLER_P) |
+ (0 << CC1200_MDMCFG2_CFM_DATA_EN)),
+};
+
+static const uint16_t packet_setup_96[] = {
+ CC1200_DEVIATION_M, PACKET_DEV_M_96,
+ CC1200_MODCFG_DEV_E, ((CC1200_MODCFG_DEV_E_MODEM_MODE_NORMAL << CC1200_MODCFG_DEV_E_MODEM_MODE) |
+ (CC1200_MODCFG_DEV_E_MOD_FORMAT_2_GFSK << CC1200_MODCFG_DEV_E_MOD_FORMAT) |
+ (PACKET_DEV_E_96 << CC1200_MODCFG_DEV_E_DEV_E)),
+ CC1200_SYMBOL_RATE2, ((PACKET_SYMBOL_RATE_E_96 << CC1200_SYMBOL_RATE2_DATARATE_E) |
+ (((PACKET_SYMBOL_RATE_M >> 16) & CC1200_SYMBOL_RATE2_DATARATE_M_19_16_MASK) << CC1200_SYMBOL_RATE2_DATARATE_M_19_16)),
+ CC1200_CHAN_BW, PACKET_CHAN_BW_96,
+ CC1200_MDMCFG2, /* General Modem Parameter Configuration Reg. 2 */
+ ((CC1200_MDMCFG2_ASK_SHAPE_8 << CC1200_MDMCFG2_ASK_SHAPE) |
+ (CC1200_MDMCFG2_SYMBOL_MAP_CFG_MODE_0 << CC1200_MDMCFG2_SYMBOL_MAP_CFG) |
+ (CC1200_MDMCFG2_UPSAMPLER_P_32 << CC1200_MDMCFG2_UPSAMPLER_P) |
+ (0 << CC1200_MDMCFG2_CFM_DATA_EN)),
+};
+
+static const uint16_t packet_setup_24[] = {
+ CC1200_DEVIATION_M, PACKET_DEV_M_24,
+ CC1200_MODCFG_DEV_E, ((CC1200_MODCFG_DEV_E_MODEM_MODE_NORMAL << CC1200_MODCFG_DEV_E_MODEM_MODE) |
+ (CC1200_MODCFG_DEV_E_MOD_FORMAT_2_GFSK << CC1200_MODCFG_DEV_E_MOD_FORMAT) |
+ (PACKET_DEV_E_24 << CC1200_MODCFG_DEV_E_DEV_E)),
+ CC1200_SYMBOL_RATE2, ((PACKET_SYMBOL_RATE_E_24 << CC1200_SYMBOL_RATE2_DATARATE_E) |
+ (((PACKET_SYMBOL_RATE_M >> 16) & CC1200_SYMBOL_RATE2_DATARATE_M_19_16_MASK) << CC1200_SYMBOL_RATE2_DATARATE_M_19_16)),
+ CC1200_CHAN_BW, PACKET_CHAN_BW_24,
+ CC1200_MDMCFG2, /* General Modem Parameter Configuration Reg. 2 */
+ ((CC1200_MDMCFG2_ASK_SHAPE_8 << CC1200_MDMCFG2_ASK_SHAPE) |
+ (CC1200_MDMCFG2_SYMBOL_MAP_CFG_MODE_0 << CC1200_MDMCFG2_SYMBOL_MAP_CFG) |
+ (CC1200_MDMCFG2_UPSAMPLER_P_64 << CC1200_MDMCFG2_UPSAMPLER_P) |
+ (0 << CC1200_MDMCFG2_CFM_DATA_EN)),
+};
+
+/*
+ * RDF deviation is 3kHz
+ *
+ * fdev = fosc >> 22 * (256 + dev_m) << dev_e dev_e != 0
+ * fdev = fosc >> 21 * dev_m dev_e == 0
+ *
+ * 40e6 / (2 ** 21) * 157 = 2995Hz
+ */
+
+#define RDF_DEV_E 0
+#define RDF_DEV_M 157
+
+/*
+ * For our RDF beacon, set the symbol rate to 2kBaud (for a 1kHz tone)
+ *
+ * (2**20 + DATARATE_M) * 2 ** DATARATE_E
+ * Rdata = -------------------------------------- * fosc
+ * 2 ** 39
+ *
+ * DATARATE_M = 669411
+ * DATARATE_E = 4
+ *
+ * To make the tone last for 200ms, we need 2000 * .2 = 400 bits or 50 bytes
+ */
+#define RDF_SYMBOL_RATE_E 4
+#define RDF_SYMBOL_RATE_M 669411
+#define RDF_PACKET_LEN 50
+
+static const uint16_t rdf_setup[] = {
+ CC1200_DEVIATION_M, RDF_DEV_M,
+ CC1200_MODCFG_DEV_E, ((CC1200_MODCFG_DEV_E_MODEM_MODE_NORMAL << CC1200_MODCFG_DEV_E_MODEM_MODE) |
+ (CC1200_MODCFG_DEV_E_MOD_FORMAT_2_GFSK << CC1200_MODCFG_DEV_E_MOD_FORMAT) |
+ (RDF_DEV_E << CC1200_MODCFG_DEV_E_DEV_E)),
+ CC1200_SYMBOL_RATE2, ((RDF_SYMBOL_RATE_E << CC1200_SYMBOL_RATE2_DATARATE_E) |
+ (((RDF_SYMBOL_RATE_M >> 16) & CC1200_SYMBOL_RATE2_DATARATE_M_19_16_MASK) << CC1200_SYMBOL_RATE2_DATARATE_M_19_16)),
+ CC1200_SYMBOL_RATE1, ((RDF_SYMBOL_RATE_M >> 8) & 0xff),
+ CC1200_SYMBOL_RATE0, ((RDF_SYMBOL_RATE_M >> 0) & 0xff),
+ CC1200_PKT_CFG2, /* Packet Configuration Reg. 2 */
+ ((0 << CC1200_PKT_CFG2_FG_MODE_EN) |
+ (CC1200_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1200_PKT_CFG2_CCA_MODE) |
+ (CC1200_PKT_CFG2_PKT_FORMAT_NORMAL << CC1200_PKT_CFG2_PKT_FORMAT)),
+ CC1200_PKT_CFG1, /* Packet Configuration Reg. 1 */
+ ((0 << CC1200_PKT_CFG1_FEC_EN) |
+ (0 << CC1200_PKT_CFG1_WHITE_DATA) |
+ (0 << CC1200_PKT_CFG1_PN9_SWAP_EN) |
+ (CC1200_PKT_CFG1_ADDR_CHECK_CFG_NONE << CC1200_PKT_CFG1_ADDR_CHECK_CFG) |
+ (CC1200_PKT_CFG1_CRC_CFG_DISABLED << CC1200_PKT_CFG1_CRC_CFG) |
+ (0 << CC1200_PKT_CFG1_APPEND_STATUS)),
+ CC1200_PREAMBLE_CFG1,
+ ((CC1200_PREAMBLE_CFG1_NUM_PREAMBLE_NONE << CC1200_PREAMBLE_CFG1_NUM_PREAMBLE) |
+ (CC1200_PREAMBLE_CFG1_PREAMBLE_WORD_AA << CC1200_PREAMBLE_CFG1_PREAMBLE_WORD)),
+ CC1200_MDMCFG2, /* General Modem Parameter Configuration Reg. 2 */
+ ((0 << CC1200_MDMCFG2_ASK_SHAPE) |
+ (0 << CC1200_MDMCFG2_SYMBOL_MAP_CFG) |
+ (12 << CC1200_MDMCFG2_UPSAMPLER_P) |
+ (0 << CC1200_MDMCFG2_CFM_DATA_EN)),
+};
+
+/*
+ * APRS deviation is 3kHz
+ *
+ * fdev = fosc >> 22 * (256 + dev_m) << dev_e dev_e != 0
+ * fdev = fosc >> 21 * dev_m dev_e == 0
+ *
+ * 40e6 / (2 ** 21) * 157 = 2995Hz
+ */
+
+#define APRS_DEV_E 0
+#define APRS_DEV_M 157
+
+/*
+ * For our APRS beacon, set the symbol rate to 9.6kBaud (8x oversampling for 1200 baud data rate)
+ *
+ * (2**20 + DATARATE_M) * 2 ** DATARATE_E
+ * Rdata = -------------------------------------- * fosc
+ * 2 ** 39
+ *
+ * DATARATE_M = 1013008
+ * DATARATE_E = 6
+ *
+ * Rdata = 9599.998593330383301
+ *
+ */
+#define APRS_SYMBOL_RATE_E 6
+#define APRS_SYMBOL_RATE_M 1013008
+
+static const uint16_t aprs_setup[] = {
+ CC1200_DEVIATION_M, APRS_DEV_M,
+ CC1200_MODCFG_DEV_E, ((CC1200_MODCFG_DEV_E_MODEM_MODE_NORMAL << CC1200_MODCFG_DEV_E_MODEM_MODE) |
+ (CC1200_MODCFG_DEV_E_MOD_FORMAT_2_GFSK << CC1200_MODCFG_DEV_E_MOD_FORMAT) |
+ (APRS_DEV_E << CC1200_MODCFG_DEV_E_DEV_E)),
+ CC1200_SYMBOL_RATE2, ((APRS_SYMBOL_RATE_E << CC1200_SYMBOL_RATE2_DATARATE_E) |
+ (((APRS_SYMBOL_RATE_M >> 16) & CC1200_SYMBOL_RATE2_DATARATE_M_19_16_MASK) << CC1200_SYMBOL_RATE2_DATARATE_M_19_16)),
+ CC1200_SYMBOL_RATE1, ((APRS_SYMBOL_RATE_M >> 8) & 0xff),
+ CC1200_SYMBOL_RATE0, ((APRS_SYMBOL_RATE_M >> 0) & 0xff),
+ CC1200_PKT_CFG2, /* Packet Configuration Reg. 2 */
+ ((0 << CC1200_PKT_CFG2_FG_MODE_EN) |
+ (CC1200_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1200_PKT_CFG2_CCA_MODE) |
+ (CC1200_PKT_CFG2_PKT_FORMAT_NORMAL << CC1200_PKT_CFG2_PKT_FORMAT)),
+ CC1200_PKT_CFG1, /* Packet Configuration Reg. 1 */
+ ((0 << CC1200_PKT_CFG1_FEC_EN) |
+ (0 << CC1200_PKT_CFG1_WHITE_DATA) |
+ (0 << CC1200_PKT_CFG1_PN9_SWAP_EN) |
+ (CC1200_PKT_CFG1_ADDR_CHECK_CFG_NONE << CC1200_PKT_CFG1_ADDR_CHECK_CFG) |
+ (CC1200_PKT_CFG1_CRC_CFG_DISABLED << CC1200_PKT_CFG1_CRC_CFG) |
+ (0 << CC1200_PKT_CFG1_APPEND_STATUS)),
+ CC1200_PKT_CFG0, /* Packet Configuration Reg. 0 */
+ ((CC1200_PKT_CFG0_LENGTH_CONFIG_FIXED << CC1200_PKT_CFG0_LENGTH_CONFIG) |
+ (0 << CC1200_PKT_CFG0_PKG_BIT_LEN) |
+ (0 << CC1200_PKT_CFG0_UART_MODE_EN) |
+ (0 << CC1200_PKT_CFG0_UART_SWAP_EN)),
+ CC1200_PREAMBLE_CFG1,
+ ((CC1200_PREAMBLE_CFG1_NUM_PREAMBLE_NONE << CC1200_PREAMBLE_CFG1_NUM_PREAMBLE) |
+ (CC1200_PREAMBLE_CFG1_PREAMBLE_WORD_AA << CC1200_PREAMBLE_CFG1_PREAMBLE_WORD)),
+ CC1200_MDMCFG2, /* General Modem Parameter Configuration Reg. 2 */
+ ((CC1200_MDMCFG2_ASK_SHAPE_8 << CC1200_MDMCFG2_ASK_SHAPE) |
+ (CC1200_MDMCFG2_SYMBOL_MAP_CFG_MODE_0 << CC1200_MDMCFG2_SYMBOL_MAP_CFG) |
+ (CC1200_MDMCFG2_UPSAMPLER_P_8 << CC1200_MDMCFG2_UPSAMPLER_P) |
+ (0 << CC1200_MDMCFG2_CFM_DATA_EN)),
+};
+
+/*
+ * For Test mode, we want an unmodulated carrier. To do that, we
+ * set the deviation to zero and enable a preamble so that the radio
+ * turns on before we send any data
+ */
+
+static const uint16_t test_setup[] = {
+ CC1200_DEVIATION_M, 0,
+ CC1200_MODCFG_DEV_E, ((CC1200_MODCFG_DEV_E_MODEM_MODE_NORMAL << CC1200_MODCFG_DEV_E_MODEM_MODE) |
+ (CC1200_MODCFG_DEV_E_MOD_FORMAT_2_GFSK << CC1200_MODCFG_DEV_E_MOD_FORMAT) |
+ (0 << CC1200_MODCFG_DEV_E_DEV_E)),
+ CC1200_SYMBOL_RATE2, ((APRS_SYMBOL_RATE_E << CC1200_SYMBOL_RATE2_DATARATE_E) |
+ (((APRS_SYMBOL_RATE_M >> 16) & CC1200_SYMBOL_RATE2_DATARATE_M_19_16_MASK) << CC1200_SYMBOL_RATE2_DATARATE_M_19_16)),
+ CC1200_SYMBOL_RATE1, ((APRS_SYMBOL_RATE_M >> 8) & 0xff),
+ CC1200_SYMBOL_RATE0, ((APRS_SYMBOL_RATE_M >> 0) & 0xff),
+ CC1200_PKT_CFG2, ((CC1200_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1200_PKT_CFG2_CCA_MODE) |
+ (CC1200_PKT_CFG2_PKT_FORMAT_NORMAL << CC1200_PKT_CFG2_PKT_FORMAT)),
+ CC1200_PKT_CFG1, ((0 << CC1200_PKT_CFG1_WHITE_DATA) |
+ (CC1200_PKT_CFG1_ADDR_CHECK_CFG_NONE << CC1200_PKT_CFG1_ADDR_CHECK_CFG) |
+ (CC1200_PKT_CFG1_CRC_CFG_DISABLED << CC1200_PKT_CFG1_CRC_CFG) |
+ (0 << CC1200_PKT_CFG1_APPEND_STATUS)),
+ CC1200_PREAMBLE_CFG1, ((CC1200_PREAMBLE_CFG1_NUM_PREAMBLE_4_BYTES << CC1200_PREAMBLE_CFG1_NUM_PREAMBLE) |
+ (CC1200_PREAMBLE_CFG1_PREAMBLE_WORD_AA << CC1200_PREAMBLE_CFG1_PREAMBLE_WORD)),
+};
+
+#define AO_PKT_CFG0_INFINITE ((CC1200_PKT_CFG0_LENGTH_CONFIG_INFINITE << CC1200_PKT_CFG0_LENGTH_CONFIG) | \
+ (0 << CC1200_PKT_CFG0_PKG_BIT_LEN) | \
+ (0 << CC1200_PKT_CFG0_UART_MODE_EN) | \
+ (0 << CC1200_PKT_CFG0_UART_SWAP_EN))
+
+#define AO_PKT_CFG0_FIXED ((CC1200_PKT_CFG0_LENGTH_CONFIG_FIXED << CC1200_PKT_CFG0_LENGTH_CONFIG) | \
+ (0 << CC1200_PKT_CFG0_PKG_BIT_LEN) | \
+ (0 << CC1200_PKT_CFG0_UART_MODE_EN) | \
+ (0 << CC1200_PKT_CFG0_UART_SWAP_EN))
+
+static uint16_t ao_radio_mode;
+
+#define AO_RADIO_MODE_BITS_PACKET 1
+#define AO_RADIO_MODE_BITS_TX_BUF 4
+#define AO_RADIO_MODE_BITS_TX_FINISH 8
+#define AO_RADIO_MODE_BITS_RX 16
+#define AO_RADIO_MODE_BITS_RDF 32
+#define AO_RADIO_MODE_BITS_APRS 64
+#define AO_RADIO_MODE_BITS_TEST 128
+#define AO_RADIO_MODE_BITS_INFINITE 256
+#define AO_RADIO_MODE_BITS_FIXED 512
+
+#define AO_RADIO_MODE_NONE 0
+#define AO_RADIO_MODE_PACKET_TX (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_TX_FINISH)
+#define AO_RADIO_MODE_PACKET_RX (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_RX)
+#define AO_RADIO_MODE_RDF (AO_RADIO_MODE_BITS_RDF | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_TX_FINISH)
+#define AO_RADIO_MODE_APRS_BUF (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_INFINITE | AO_RADIO_MODE_BITS_TX_BUF)
+#define AO_RADIO_MODE_APRS_LAST_BUF (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_TX_BUF)
+#define AO_RADIO_MODE_APRS_FINISH (AO_RADIO_MODE_BITS_APRS | AO_RADIO_MODE_BITS_FIXED | AO_RADIO_MODE_BITS_TX_FINISH)
+#define AO_RADIO_MODE_TEST (AO_RADIO_MODE_BITS_TEST | AO_RADIO_MODE_BITS_INFINITE | AO_RADIO_MODE_BITS_TX_BUF)
+
+static void
+_ao_radio_set_regs(const uint16_t *regs, int nreg)
+{
+ int i;
+
+ for (i = 0; i < nreg; i++) {
+ ao_radio_reg_write(regs[0], regs[1]);
+ regs += 2;
+ }
+}
+
+#define ao_radio_set_regs(setup) _ao_radio_set_regs(setup, (sizeof (setup) / sizeof(setup[0])) >> 1)
+
+static void
+ao_radio_set_mode(uint16_t new_mode)
+{
+ uint16_t changes;
+
+ if (new_mode == ao_radio_mode)
+ return;
+
+ changes = new_mode & (~ao_radio_mode);
+
+ if (changes & AO_RADIO_MODE_BITS_PACKET) {
+ ao_radio_set_regs(packet_setup);
+
+ switch (ao_config.radio_rate) {
+ default:
+ case AO_RADIO_RATE_38400:
+ ao_radio_set_regs(packet_setup_384);
+ break;
+ case AO_RADIO_RATE_9600:
+ ao_radio_set_regs(packet_setup_96);
+ break;
+ case AO_RADIO_RATE_2400:
+ ao_radio_set_regs(packet_setup_24);
+ break;
+ }
+ }
+
+ if (changes & AO_RADIO_MODE_BITS_TX_BUF) {
+ ao_radio_reg_write(AO_CC1200_INT_GPIO_IOCFG, CC1200_IOCFG_GPIO_CFG_TXFIFO_THR);
+ ao_exti_set_mode(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN, AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH);
+ }
+
+ if (changes & AO_RADIO_MODE_BITS_TX_FINISH) {
+ ao_radio_reg_write(AO_CC1200_INT_GPIO_IOCFG, CC1200_IOCFG_GPIO_CFG_PKT_SYNC_RXTX);
+ ao_exti_set_mode(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN, AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH);
+ }
+
+ if (changes & AO_RADIO_MODE_BITS_RX) {
+ ao_radio_reg_write(AO_CC1200_INT_GPIO_IOCFG, CC1200_IOCFG_GPIO_CFG_MARC_MCU_WAKEUP);
+ ao_exti_set_mode(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN, AO_EXTI_MODE_RISING|AO_EXTI_PRIORITY_HIGH);
+ }
+
+ if (changes & AO_RADIO_MODE_BITS_RDF)
+ ao_radio_set_regs(rdf_setup);
+
+ if (changes & AO_RADIO_MODE_BITS_APRS)
+ ao_radio_set_regs(aprs_setup);
+
+ if (changes & AO_RADIO_MODE_BITS_TEST)
+ ao_radio_set_regs(test_setup);
+
+ if (changes & AO_RADIO_MODE_BITS_INFINITE)
+ ao_radio_reg_write(CC1200_PKT_CFG0, AO_PKT_CFG0_INFINITE);
+
+ if (changes & AO_RADIO_MODE_BITS_FIXED)
+ ao_radio_reg_write(CC1200_PKT_CFG0, AO_PKT_CFG0_FIXED);
+
+ ao_radio_mode = new_mode;
+}
+
+static const uint16_t radio_setup[] = {
+#include "ao_cc1200_CC1200.h"
+};
+
+static uint8_t ao_radio_configured = 0;
+
+static void
+ao_radio_setup(void)
+{
+ ao_radio_strobe(CC1200_SRES);
+
+ ao_radio_set_regs(radio_setup);
+
+ ao_radio_mode = 0;
+
+ ao_radio_idle();
+
+ ao_config_get();
+
+ ao_radio_configured = 1;
+}
+
+static void
+ao_radio_set_len(uint8_t len)
+{
+ static uint8_t last_len;
+
+ if (len != last_len) {
+ ao_radio_reg_write(CC1200_PKT_LEN, len);
+ last_len = len;
+ }
+}
+
+static void
+ao_radio_get(uint8_t len)
+{
+ static uint32_t last_radio_setting;
+ static uint8_t last_radio_rate;
+
+ ao_mutex_get(&ao_radio_mutex);
+
+ if (!ao_radio_configured)
+ ao_radio_setup();
+ if (ao_config.radio_setting != last_radio_setting) {
+ ao_radio_reg_write(CC1200_FREQ2, ao_config.radio_setting >> 16);
+ ao_radio_reg_write(CC1200_FREQ1, ao_config.radio_setting >> 8);
+ ao_radio_reg_write(CC1200_FREQ0, ao_config.radio_setting);
+ last_radio_setting = ao_config.radio_setting;
+ ao_radio_strobe(CC1200_SCAL);
+ }
+ if (ao_config.radio_rate != last_radio_rate) {
+ ao_radio_mode &= ~AO_RADIO_MODE_BITS_PACKET;
+ last_radio_rate = ao_config.radio_rate;
+ }
+ ao_radio_set_len(len);
+}
+
+#define ao_radio_put() ao_mutex_put(&ao_radio_mutex)
+
+static inline uint8_t
+ao_radio_state(void)
+{
+ return (ao_radio_status() >> CC1200_STATUS_STATE) & CC1200_STATUS_STATE_MASK;
+}
+
+#if CC1200_DEBUG
+void
+ao_radio_show_state(char *where)
+{
+ printf("%s: state %d len %d rxbytes %d\n",
+ where, ao_radio_state(),
+ ao_radio_reg_read(CC1200_PKT_LEN),
+ ao_radio_reg_read(CC1200_NUM_RXBYTES));
+}
+#else
+#define ao_radio_show_state(where)
+#endif
+
+/* Wait for the radio to signal an interrupt
+ */
+static void
+ao_radio_wait_isr(uint16_t timeout)
+{
+ if (timeout)
+ ao_alarm(timeout);
+
+ ao_arch_block_interrupts();
+ while (!ao_radio_wake && !ao_radio_abort)
+ if (ao_sleep(&ao_radio_wake))
+ ao_radio_abort = 1;
+ ao_arch_release_interrupts();
+
+ if (timeout)
+ ao_clear_alarm();
+}
+
+static void
+ao_rdf_start(uint8_t len)
+{
+ ao_radio_abort = 0;
+ ao_radio_get(len);
+
+ ao_radio_set_mode(AO_RADIO_MODE_RDF);
+ ao_radio_wake = 0;
+}
+
+static void
+ao_radio_run(void)
+{
+ ao_radio_wake = 0;
+ ao_radio_abort = 0;
+ ao_radio_start_tx();
+ ao_radio_wait_isr(0);
+ if (!ao_radio_wake)
+ ao_radio_idle();
+ ao_radio_put();
+}
+
+void
+ao_radio_rdf(void)
+{
+ ao_rdf_start(AO_RADIO_RDF_LEN);
+
+ ao_radio_fifo_write_fixed(ao_radio_rdf_value, AO_RADIO_RDF_LEN);
+
+ ao_radio_run();
+}
+
+void
+ao_radio_continuity(uint8_t c)
+{
+ uint8_t i;
+ uint8_t status;
+
+ ao_rdf_start(AO_RADIO_CONT_TOTAL_LEN);
+
+ status = ao_radio_fifo_write_start();
+ for (i = 0; i < 3; i++) {
+ ao_radio_spi_send_fixed(0x00, AO_RADIO_CONT_PAUSE_LEN);
+ if (i < c)
+ ao_radio_spi_send_fixed(ao_radio_rdf_value, AO_RADIO_CONT_TONE_LEN);
+ else
+ ao_radio_spi_send_fixed(0x00, AO_RADIO_CONT_TONE_LEN);
+ }
+ ao_radio_spi_send_fixed(0x00, AO_RADIO_CONT_PAUSE_LEN);
+ status = ao_radio_fifo_write_stop(status);
+ (void) status;
+ ao_radio_run();
+}
+
+void
+ao_radio_rdf_abort(void)
+{
+ ao_radio_abort = 1;
+ ao_wakeup(&ao_radio_wake);
+}
+
+static void
+ao_radio_test_cmd(void)
+{
+ uint8_t mode = 2;
+ static uint8_t radio_on;
+ ao_cmd_white();
+ if (ao_cmd_lex_c != '\n') {
+ ao_cmd_decimal();
+ mode = (uint8_t) ao_cmd_lex_u32;
+ }
+ mode++;
+ if ((mode & 2) && !radio_on) {
+#if HAS_MONITOR
+ ao_monitor_disable();
+#endif
+#if PACKET_HAS_SLAVE
+ ao_packet_slave_stop();
+#endif
+ ao_radio_get(0xff);
+ ao_radio_set_mode(AO_RADIO_MODE_TEST);
+ ao_radio_strobe(CC1200_STX);
+#if CC1200_TRACE
+ { int t;
+ for (t = 0; t < 10; t++) {
+ printf ("status: %02x\n", ao_radio_status());
+ ao_delay(AO_MS_TO_TICKS(100));
+ }
+ }
+#endif
+ radio_on = 1;
+ }
+ if (mode == 3) {
+ printf ("Hit a character to stop..."); flush();
+ getchar();
+ putchar('\n');
+ }
+ if ((mode & 1) && radio_on) {
+ ao_radio_idle();
+ ao_radio_put();
+ radio_on = 0;
+#if HAS_MONITOR
+ ao_monitor_enable();
+#endif
+ }
+}
+
+void
+ao_radio_send(const void *d, uint8_t size)
+{
+ ao_radio_get(size);
+ ao_radio_set_mode(AO_RADIO_MODE_PACKET_TX);
+
+ ao_radio_fifo_write(d, size);
+
+ ao_radio_run();
+}
+
+
+#define AO_RADIO_LOTS 64
+
+void
+ao_radio_send_aprs(ao_radio_fill_func fill)
+{
+ uint8_t buf[AO_RADIO_LOTS], *b;
+ int cnt;
+ int total = 0;
+ uint8_t done = 0;
+ uint8_t started = 0;
+ uint8_t fifo_space;
+
+ ao_radio_abort = 0;
+ ao_radio_get(0xff);
+ fifo_space = CC1200_FIFO_SIZE;
+ while (!done) {
+ cnt = (*fill)(buf, sizeof(buf));
+ if (cnt < 0) {
+ done = 1;
+ cnt = -cnt;
+ }
+#if CC1200_APRS_TRACE
+ printf("APRS fill %d bytes done %d\n", cnt, done);
+#endif
+ total += cnt;
+
+ /* At the last buffer, set the total length */
+ if (done)
+ ao_radio_set_len(total & 0xff);
+
+ b = buf;
+ while (cnt) {
+ uint8_t this_len = cnt;
+
+ /* Wait for some space in the fifo */
+ while (!ao_radio_abort && (fifo_space = ao_radio_tx_fifo_space()) == 0) {
+#if CC1200_APRS_TRACE
+ printf("APRS space %d cnt %d\n", fifo_space, cnt); flush();
+#endif
+ ao_radio_wake = 0;
+ ao_radio_wait_isr(AO_MS_TO_TICKS(1000));
+ }
+ if (ao_radio_abort)
+ break;
+ if (this_len > fifo_space)
+ this_len = fifo_space;
+
+ cnt -= this_len;
+
+ if (done) {
+ if (cnt)
+ ao_radio_set_mode(AO_RADIO_MODE_APRS_LAST_BUF);
+ else
+ ao_radio_set_mode(AO_RADIO_MODE_APRS_FINISH);
+ } else
+ ao_radio_set_mode(AO_RADIO_MODE_APRS_BUF);
+
+ ao_exti_enable(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN);
+
+ ao_radio_fifo_write(b, this_len);
+ b += this_len;
+#if CC1200_APRS_TRACE
+ printf("APRS write fifo %d space now %d\n", this_len, ao_radio_tx_fifo_space());
+#endif
+ if (!started) {
+#if CC1200_APRS_TRACE
+ printf("APRS start\n");
+#endif
+ ao_radio_strobe(CC1200_STX);
+#if CC1200_APRS_TRACE
+ { int t;
+ for (t = 0; t < 20; t++) {
+ uint8_t status = ao_radio_status();
+ uint8_t space = ao_radio_tx_fifo_space();
+ printf ("status: %02x fifo %d\n", status, space);
+ if ((status >> 4) == 2)
+ break;
+ ao_delay(AO_MS_TO_TICKS(0));
+ }
+ }
+#endif
+ started = 1;
+ }
+ }
+ if (ao_radio_abort) {
+ ao_radio_idle();
+ break;
+ }
+ }
+ /* Wait for the transmitter to go idle */
+ ao_radio_wake = 0;
+#if CC1200_APRS_TRACE
+ printf("APRS wait idle\n"); flush();
+#endif
+ ao_radio_wait_isr(AO_MS_TO_TICKS(1000));
+#if CC1200_APRS_TRACE
+ printf("APRS abort %d\n", ao_radio_abort);
+#endif
+ ao_radio_put();
+}
+
+#if 0
+static uint8_t
+ao_radio_marc_state(void)
+{
+ return ao_radio_reg_read(CC1200_MARCSTATE);
+}
+
+static uint8_t
+ao_radio_modem_status1(void)
+{
+ return ao_radio_reg_read(CC1200_MODEM_STATUS1);
+}
+
+static uint8_t
+ao_radio_modem_status0(void)
+{
+ return ao_radio_reg_read(CC1200_MODEM_STATUS0);
+}
+
+struct ao_radio_state {
+ char where[4];
+ uint8_t marc_state;
+ uint8_t marc_status1;
+ uint8_t marc_status0;
+ uint8_t modem_status1;
+ uint8_t modem_status0;
+};
+
+static void
+ao_radio_fill_state(char *where, struct ao_radio_state *s)
+{
+ strcpy(s->where, where);
+ s->marc_state = ao_radio_marc_state();
+ s->marc_status1 = ao_radio_reg_read(CC1200_MARC_STATUS1);
+ s->marc_status0 = ao_radio_reg_read(CC1200_MARC_STATUS0);
+ s->modem_status1 = ao_radio_modem_status1();
+ s->modem_status0 = ao_radio_modem_status0();
+}
+
+static void
+ao_radio_dump_state(struct ao_radio_state *s)
+{
+ printf ("%s: marc %2x marc1 %2x marc0 %2x modem1 %2x modem0 %2x\n",
+ s->where, s->marc_state, s->marc_status1, s->marc_status0, s->modem_status1, s->modem_status0);
+}
+#endif
+
+uint8_t
+ao_radio_recv(__xdata void *d, uint8_t size, uint8_t timeout)
+{
+ uint8_t success = 0;
+
+ ao_radio_abort = 0;
+ ao_radio_get(size - 2);
+ ao_radio_set_mode(AO_RADIO_MODE_PACKET_RX);
+ ao_radio_wake = 0;
+ ao_radio_start_rx();
+
+ while (!ao_radio_abort) {
+ ao_radio_wait_isr(timeout);
+ if (ao_radio_wake) {
+ uint8_t marc_status1 = ao_radio_reg_read(CC1200_MARC_STATUS1);
+
+ /* Check the receiver status to see what happened
+ */
+ switch (marc_status1) {
+ case CC1200_MARC_STATUS1_RX_FINISHED:
+ case CC1200_MARC_STATUS1_ADDRESS:
+ case CC1200_MARC_STATUS1_CRC:
+ /* Normal return, go fetch the bytes from the FIFO
+ * and give them back to the caller
+ */
+ success = 1;
+ break;
+ case CC1200_MARC_STATUS1_RX_TIMEOUT:
+ case CC1200_MARC_STATUS1_RX_TERMINATION:
+ case CC1200_MARC_STATUS1_EWOR_SYNC_LOST:
+ case CC1200_MARC_STATUS1_MAXIMUM_LENGTH:
+ case CC1200_MARC_STATUS1_RX_FIFO_OVERFLOW:
+ case CC1200_MARC_STATUS1_RX_FIFO_UNDERFLOW:
+ /* Something weird happened; reset the radio and
+ * return failure
+ */
+ success = 0;
+ break;
+ default:
+ /* some other status; go wait for the radio to do something useful
+ */
+ continue;
+ }
+ break;
+ } else {
+ uint8_t modem_status1 = ao_radio_reg_read(CC1200_MODEM_STATUS1);
+
+ /* Check to see if the packet header has been seen, in which case we'll
+ * want to keep waiting for the rest of the packet to appear
+ */
+ if (modem_status1 & (1 << CC1200_MODEM_STATUS1_SYNC_FOUND))
+ {
+ ao_radio_abort = 0;
+
+ /* Set a timeout based on the packet length so that we make sure to
+ * wait long enough to receive the whole thing.
+ *
+ * timeout = bits * FEC expansion / rate
+ */
+ switch (ao_config.radio_rate) {
+ default:
+ case AO_RADIO_RATE_38400:
+ timeout = AO_MS_TO_TICKS(size * (8 * 2 * 10) / 384) + 1;
+ break;
+ case AO_RADIO_RATE_9600:
+ timeout = AO_MS_TO_TICKS(size * (8 * 2 * 10) / 96) + 1;
+ break;
+ case AO_RADIO_RATE_2400:
+ timeout = AO_MS_TO_TICKS(size * (8 * 2 * 10) / 24) + 1;
+ break;
+ }
+ }
+ }
+ }
+
+ if (success) {
+ int8_t rssi;
+ uint8_t status;
+
+ status = ao_radio_fifo_read(d, size);
+ (void) status;
+ rssi = ((int8_t *) d)[size - 2];
+ ao_radio_rssi = rssi;
+
+ /* Bound it to the representable range */
+ if (rssi > -11)
+ rssi = -11;
+
+ /* Write it back to the packet */
+ ((int8_t *) d)[size-2] = AO_RADIO_FROM_RSSI(rssi);
+ } else {
+ ao_radio_idle();
+ ao_radio_rssi = 0;
+ }
+
+ ao_radio_put();
+ return success;
+}
+
+
+#if CC1200_DEBUG
+static char *cc1200_state_name[] = {
+ [CC1200_STATUS_STATE_IDLE] = "IDLE",
+ [CC1200_STATUS_STATE_RX] = "RX",
+ [CC1200_STATUS_STATE_TX] = "TX",
+ [CC1200_STATUS_STATE_FSTXON] = "FSTXON",
+ [CC1200_STATUS_STATE_CALIBRATE] = "CALIBRATE",
+ [CC1200_STATUS_STATE_SETTLING] = "SETTLING",
+ [CC1200_STATUS_STATE_RX_FIFO_ERROR] = "RX_FIFO_ERROR",
+ [CC1200_STATUS_STATE_TX_FIFO_ERROR] = "TX_FIFO_ERROR",
+};
+
+struct ao_cc1200_reg {
+ uint16_t addr;
+ char *name;
+};
+
+static const struct ao_cc1200_reg ao_cc1200_reg[] = {
+ { .addr = CC1200_IOCFG3, .name = "IOCFG3" },
+ { .addr = CC1200_IOCFG2, .name = "IOCFG2" },
+ { .addr = CC1200_IOCFG1, .name = "IOCFG1" },
+ { .addr = CC1200_IOCFG0, .name = "IOCFG0" },
+ { .addr = CC1200_SYNC3, .name = "SYNC3" },
+ { .addr = CC1200_SYNC2, .name = "SYNC2" },
+ { .addr = CC1200_SYNC1, .name = "SYNC1" },
+ { .addr = CC1200_SYNC0, .name = "SYNC0" },
+ { .addr = CC1200_SYNC_CFG1, .name = "SYNC_CFG1" },
+ { .addr = CC1200_SYNC_CFG0, .name = "SYNC_CFG0" },
+ { .addr = CC1200_DEVIATION_M, .name = "DEVIATION_M" },
+ { .addr = CC1200_MODCFG_DEV_E, .name = "MODCFG_DEV_E" },
+ { .addr = CC1200_DCFILT_CFG, .name = "DCFILT_CFG" },
+ { .addr = CC1200_PREAMBLE_CFG1, .name = "PREAMBLE_CFG1" },
+ { .addr = CC1200_PREAMBLE_CFG0, .name = "PREAMBLE_CFG0" },
+ { .addr = CC1200_IQIC, .name = "IQIC" },
+ { .addr = CC1200_CHAN_BW, .name = "CHAN_BW" },
+ { .addr = CC1200_MDMCFG2, .name = "MDMCFG2" },
+ { .addr = CC1200_MDMCFG1, .name = "MDMCFG1" },
+ { .addr = CC1200_MDMCFG0, .name = "MDMCFG0" },
+ { .addr = CC1200_SYMBOL_RATE2, .name = "SYMBOL_RATE2" },
+ { .addr = CC1200_SYMBOL_RATE1, .name = "SYMBOL_RATE1" },
+ { .addr = CC1200_SYMBOL_RATE0, .name = "SYMBOL_RATE0" },
+ { .addr = CC1200_AGC_REF, .name = "AGC_REF" },
+ { .addr = CC1200_AGC_CS_THR, .name = "AGC_CS_THR" },
+ { .addr = CC1200_AGC_GAIN_ADJUST, .name = "AGC_GAIN_ADJUST" },
+ { .addr = CC1200_AGC_CFG3, .name = "AGC_CFG3" },
+ { .addr = CC1200_AGC_CFG2, .name = "AGC_CFG2" },
+ { .addr = CC1200_AGC_CFG1, .name = "AGC_CFG1" },
+ { .addr = CC1200_AGC_CFG0, .name = "AGC_CFG0" },
+ { .addr = CC1200_FIFO_CFG, .name = "FIFO_CFG" },
+ { .addr = CC1200_DEV_ADDR, .name = "DEV_ADDR" },
+ { .addr = CC1200_SETTLING_CFG, .name = "SETTLING_CFG" },
+ { .addr = CC1200_FS_CFG, .name = "FS_CFG" },
+ { .addr = CC1200_WOR_CFG1, .name = "WOR_CFG1" },
+ { .addr = CC1200_WOR_CFG0, .name = "WOR_CFG0" },
+ { .addr = CC1200_WOR_EVENT0_MSB, .name = "WOR_EVENT0_MSB" },
+ { .addr = CC1200_WOR_EVENT0_LSB, .name = "WOR_EVENT0_LSB" },
+ { .addr = CC1200_RXDCM_TIME, .name = "RXDCM_TIME" },
+ { .addr = CC1200_PKT_CFG2, .name = "PKT_CFG2" },
+ { .addr = CC1200_PKT_CFG1, .name = "PKT_CFG1" },
+ { .addr = CC1200_PKT_CFG0, .name = "PKT_CFG0" },
+ { .addr = CC1200_RFEND_CFG1, .name = "RFEND_CFG1" },
+ { .addr = CC1200_RFEND_CFG0, .name = "RFEND_CFG0" },
+ { .addr = CC1200_PA_CFG1, .name = "PA_CFG1" },
+ { .addr = CC1200_PA_CFG0, .name = "PA_CFG0" },
+ { .addr = CC1200_PKT_LEN, .name = "PKT_LEN" },
+ { .addr = CC1200_IF_MIX_CFG, .name = "IF_MIX_CFG" },
+ { .addr = CC1200_FREQOFF_CFG, .name = "FREQOFF_CFG" },
+ { .addr = CC1200_TOC_CFG, .name = "TOC_CFG" },
+ { .addr = CC1200_MARC_SPARE, .name = "MARC_SPARE" },
+ { .addr = CC1200_ECG_CFG, .name = "ECG_CFG" },
+ { .addr = CC1200_EXT_CTRL, .name = "EXT_CTRL" },
+ { .addr = CC1200_RCCAL_FINE, .name = "RCCAL_FINE" },
+ { .addr = CC1200_RCCAL_COARSE, .name = "RCCAL_COARSE" },
+ { .addr = CC1200_RCCAL_OFFSET, .name = "RCCAL_OFFSET" },
+ { .addr = CC1200_FREQOFF1, .name = "FREQOFF1" },
+ { .addr = CC1200_FREQOFF0, .name = "FREQOFF0" },
+ { .addr = CC1200_FREQ2, .name = "FREQ2" },
+ { .addr = CC1200_FREQ1, .name = "FREQ1" },
+ { .addr = CC1200_FREQ0, .name = "FREQ0" },
+ { .addr = CC1200_IF_ADC2, .name = "IF_ADC2" },
+ { .addr = CC1200_IF_ADC1, .name = "IF_ADC1" },
+ { .addr = CC1200_IF_ADC0, .name = "IF_ADC0" },
+ { .addr = CC1200_FS_DIG1, .name = "FS_DIG1" },
+ { .addr = CC1200_FS_DIG0, .name = "FS_DIG0" },
+ { .addr = CC1200_FS_CAL3, .name = "FS_CAL3" },
+ { .addr = CC1200_FS_CAL2, .name = "FS_CAL2" },
+ { .addr = CC1200_FS_CAL1, .name = "FS_CAL1" },
+ { .addr = CC1200_FS_CAL0, .name = "FS_CAL0" },
+ { .addr = CC1200_FS_CHP, .name = "FS_CHP" },
+ { .addr = CC1200_FS_DIVTWO, .name = "FS_DIVTWO" },
+ { .addr = CC1200_FS_DSM1, .name = "FS_DSM1" },
+ { .addr = CC1200_FS_DSM0, .name = "FS_DSM0" },
+ { .addr = CC1200_FS_DVC1, .name = "FS_DVC1" },
+ { .addr = CC1200_FS_DVC0, .name = "FS_DVC0" },
+ { .addr = CC1200_FS_LBI, .name = "FS_LBI" },
+ { .addr = CC1200_FS_PFD, .name = "FS_PFD" },
+ { .addr = CC1200_FS_PRE, .name = "FS_PRE" },
+ { .addr = CC1200_FS_REG_DIV_CML, .name = "FS_REG_DIV_CML" },
+ { .addr = CC1200_FS_SPARE, .name = "FS_SPARE" },
+ { .addr = CC1200_FS_VCO4, .name = "FS_VCO4" },
+ { .addr = CC1200_FS_VCO3, .name = "FS_VCO3" },
+ { .addr = CC1200_FS_VCO2, .name = "FS_VCO2" },
+ { .addr = CC1200_FS_VCO1, .name = "FS_VCO1" },
+ { .addr = CC1200_FS_VCO0, .name = "FS_VCO0" },
+ { .addr = CC1200_GBIAS6, .name = "GBIAS6" },
+ { .addr = CC1200_GBIAS5, .name = "GBIAS5" },
+ { .addr = CC1200_GBIAS4, .name = "GBIAS4" },
+ { .addr = CC1200_GBIAS3, .name = "GBIAS3" },
+ { .addr = CC1200_GBIAS2, .name = "GBIAS2" },
+ { .addr = CC1200_GBIAS1, .name = "GBIAS1" },
+ { .addr = CC1200_GBIAS0, .name = "GBIAS0" },
+ { .addr = CC1200_IFAMP, .name = "IFAMP" },
+ { .addr = CC1200_LNA, .name = "LNA" },
+ { .addr = CC1200_RXMIX, .name = "RXMIX" },
+ { .addr = CC1200_XOSC5, .name = "XOSC5" },
+ { .addr = CC1200_XOSC4, .name = "XOSC4" },
+ { .addr = CC1200_XOSC3, .name = "XOSC3" },
+ { .addr = CC1200_XOSC2, .name = "XOSC2" },
+ { .addr = CC1200_XOSC1, .name = "XOSC1" },
+ { .addr = CC1200_XOSC0, .name = "XOSC0" },
+ { .addr = CC1200_ANALOG_SPARE, .name = "ANALOG_SPARE" },
+ { .addr = CC1200_PA_CFG3, .name = "PA_CFG3" },
+ { .addr = CC1200_WOR_TIME1, .name = "WOR_TIME1" },
+ { .addr = CC1200_WOR_TIME0, .name = "WOR_TIME0" },
+ { .addr = CC1200_WOR_CAPTURE1, .name = "WOR_CAPTURE1" },
+ { .addr = CC1200_WOR_CAPTURE0, .name = "WOR_CAPTURE0" },
+ { .addr = CC1200_BIST, .name = "BIST" },
+ { .addr = CC1200_DCFILTOFFSET_I1, .name = "DCFILTOFFSET_I1" },
+ { .addr = CC1200_DCFILTOFFSET_I0, .name = "DCFILTOFFSET_I0" },
+ { .addr = CC1200_DCFILTOFFSET_Q1, .name = "DCFILTOFFSET_Q1" },
+ { .addr = CC1200_DCFILTOFFSET_Q0, .name = "DCFILTOFFSET_Q0" },
+ { .addr = CC1200_IQIE_I1, .name = "IQIE_I1" },
+ { .addr = CC1200_IQIE_I0, .name = "IQIE_I0" },
+ { .addr = CC1200_IQIE_Q1, .name = "IQIE_Q1" },
+ { .addr = CC1200_IQIE_Q0, .name = "IQIE_Q0" },
+ { .addr = CC1200_RSSI1, .name = "RSSI1" },
+ { .addr = CC1200_RSSI0, .name = "RSSI0" },
+ { .addr = CC1200_MARCSTATE, .name = "MARCSTATE" },
+ { .addr = CC1200_LQI_VAL, .name = "LQI_VAL" },
+ { .addr = CC1200_PQT_SYNC_ERR, .name = "PQT_SYNC_ERR" },
+ { .addr = CC1200_DEM_STATUS, .name = "DEM_STATUS" },
+ { .addr = CC1200_FREQOFF_EST1, .name = "FREQOFF_EST1" },
+ { .addr = CC1200_FREQOFF_EST0, .name = "FREQOFF_EST0" },
+ { .addr = CC1200_AGC_GAIN3, .name = "AGC_GAIN3" },
+ { .addr = CC1200_AGC_GAIN2, .name = "AGC_GAIN2" },
+ { .addr = CC1200_AGC_GAIN1, .name = "AGC_GAIN1" },
+ { .addr = CC1200_AGC_GAIN0, .name = "AGC_GAIN0" },
+ { .addr = CC1200_SOFT_RX_DATA_OUT, .name = "SOFT_RX_DATA_OUT" },
+ { .addr = CC1200_SOFT_TX_DATA_IN, .name = "SOFT_TX_DATA_IN" },
+ { .addr = CC1200_ASK_SOFT_RX_DATA, .name = "ASK_SOFT_RX_DATA" },
+ { .addr = CC1200_RNDGEN, .name = "RNDGEN" },
+ { .addr = CC1200_MAGN2, .name = "MAGN2" },
+ { .addr = CC1200_MAGN1, .name = "MAGN1" },
+ { .addr = CC1200_MAGN0, .name = "MAGN0" },
+ { .addr = CC1200_ANG1, .name = "ANG1" },
+ { .addr = CC1200_ANG0, .name = "ANG0" },
+ { .addr = CC1200_CHFILT_I2, .name = "CHFILT_I2" },
+ { .addr = CC1200_CHFILT_I1, .name = "CHFILT_I1" },
+ { .addr = CC1200_CHFILT_I0, .name = "CHFILT_I0" },
+ { .addr = CC1200_CHFILT_Q2, .name = "CHFILT_Q2" },
+ { .addr = CC1200_CHFILT_Q1, .name = "CHFILT_Q1" },
+ { .addr = CC1200_CHFILT_Q0, .name = "CHFILT_Q0" },
+ { .addr = CC1200_GPIO_STATUS, .name = "GPIO_STATUS" },
+ { .addr = CC1200_FSCAL_CTRL, .name = "FSCAL_CTRL" },
+ { .addr = CC1200_PHASE_ADJUST, .name = "PHASE_ADJUST" },
+ { .addr = CC1200_PARTNUMBER, .name = "PARTNUMBER" },
+ { .addr = CC1200_PARTVERSION, .name = "PARTVERSION" },
+ { .addr = CC1200_SERIAL_STATUS, .name = "SERIAL_STATUS" },
+ { .addr = CC1200_MODEM_STATUS1, .name = "MODEM_STATUS1" },
+ { .addr = CC1200_MODEM_STATUS0, .name = "MODEM_STATUS0" },
+ { .addr = CC1200_MARC_STATUS1, .name = "MARC_STATUS1" },
+ { .addr = CC1200_MARC_STATUS0, .name = "MARC_STATUS0" },
+ { .addr = CC1200_PA_IFAMP_TEST, .name = "PA_IFAMP_TEST" },
+ { .addr = CC1200_FSRF_TEST, .name = "FSRF_TEST" },
+ { .addr = CC1200_PRE_TEST, .name = "PRE_TEST" },
+ { .addr = CC1200_PRE_OVR, .name = "PRE_OVR" },
+ { .addr = CC1200_ADC_TEST, .name = "ADC_TEST" },
+ { .addr = CC1200_DVC_TEST, .name = "DVC_TEST" },
+ { .addr = CC1200_ATEST, .name = "ATEST" },
+ { .addr = CC1200_ATEST_LVDS, .name = "ATEST_LVDS" },
+ { .addr = CC1200_ATEST_MODE, .name = "ATEST_MODE" },
+ { .addr = CC1200_XOSC_TEST1, .name = "XOSC_TEST1" },
+ { .addr = CC1200_XOSC_TEST0, .name = "XOSC_TEST0" },
+ { .addr = CC1200_RXFIRST, .name = "RXFIRST" },
+ { .addr = CC1200_TXFIRST, .name = "TXFIRST" },
+ { .addr = CC1200_RXLAST, .name = "RXLAST" },
+ { .addr = CC1200_TXLAST, .name = "TXLAST" },
+ { .addr = CC1200_NUM_TXBYTES, .name = "NUM_TXBYTES" },
+ { .addr = CC1200_NUM_RXBYTES, .name = "NUM_RXBYTES" },
+ { .addr = CC1200_FIFO_NUM_TXBYTES, .name = "FIFO_NUM_TXBYTES" },
+ { .addr = CC1200_FIFO_NUM_RXBYTES, .name = "FIFO_NUM_RXBYTES" },
+};
+
+#define AO_NUM_CC1200_REG (sizeof ao_cc1200_reg / sizeof ao_cc1200_reg[0])
+
+static uint8_t
+ao_radio_get_marc_status(void)
+{
+ return ao_radio_reg_read(CC1200_MARC_STATUS1);
+}
+
+static void ao_radio_show(void) {
+ uint8_t status;
+ unsigned int i;
+
+ ao_mutex_get(&ao_radio_mutex);
+ status = ao_radio_status();
+ printf ("Status: %02x\n", status);
+ printf ("CHIP_RDY: %d\n", (status >> CC1200_STATUS_CHIP_RDY) & 1);
+ printf ("STATE: %s\n", cc1200_state_name[(status >> CC1200_STATUS_STATE) & CC1200_STATUS_STATE_MASK]);
+ printf ("MARC: %02x\n", ao_radio_get_marc_status());
+
+ for (i = 0; i < AO_NUM_CC1200_REG; i++)
+ printf ("\t%02x %-20.20s\n", ao_radio_reg_read(ao_cc1200_reg[i].addr), ao_cc1200_reg[i].name);
+
+ ao_radio_put();
+}
+
+static void ao_radio_beep(void) {
+ ao_radio_rdf();
+}
+
+static void ao_radio_packet(void) {
+ static const uint8_t packet[] = {
+#if 1
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+#else
+ 3, 1, 2, 3
+#endif
+ };
+
+ ao_radio_send(packet, sizeof (packet));
+}
+
+void
+ao_radio_test_recv(void)
+{
+ uint8_t bytes[34];
+ uint8_t b;
+
+ if (ao_radio_recv(bytes, 34, 0)) {
+ if (bytes[33] & 0x80)
+ printf ("CRC OK");
+ else
+ printf ("CRC BAD");
+ printf (" RSSI %d", AO_RSSI_FROM_RADIO(bytes[32]));
+ for (b = 0; b < 32; b++)
+ printf (" %02x", bytes[b]);
+
+ printf (" RSSI %02x LQI %02x", bytes[32], bytes[33]);
+ printf ("\n");
+ }
+}
+
+#if HAS_APRS
+#include <ao_aprs.h>
+
+static void
+ao_radio_aprs(void)
+{
+#if PACKET_HAS_SLAVE
+ ao_packet_slave_stop();
+#endif
+ ao_aprs_send();
+}
+#endif
+#endif
+
+#if CC1200_LOW_LEVEL_DEBUG
+static void
+ao_radio_strobe_test(void)
+{
+ uint8_t r;
+
+ ao_cmd_hex();
+ if (ao_cmd_status != ao_cmd_success)
+ return;
+ r = ao_radio_strobe(ao_cmd_lex_i);
+ printf ("Strobe %02x -> %02x (rdy %d state %d)\n",
+ ao_cmd_lex_i,
+ r,
+ r >> 7,
+ (r >> 4) & 0x7);
+}
+
+static void
+ao_radio_write_test(void)
+{
+ uint16_t addr;
+ uint8_t data;
+
+ ao_cmd_hex();
+ if (ao_cmd_status != ao_cmd_success)
+ return;
+ addr = ao_cmd_lex_i;
+ ao_cmd_hex();
+ if (ao_cmd_status != ao_cmd_success)
+ return;
+ data = ao_cmd_lex_i;
+ printf ("Write %04x = %02x\n", addr, data);
+ ao_radio_reg_write(addr, data);
+}
+
+static void
+ao_radio_read_test(void)
+{
+ uint16_t addr;
+ uint8_t data;
+
+ ao_cmd_hex();
+ if (ao_cmd_status != ao_cmd_success)
+ return;
+ addr = ao_cmd_lex_i;
+ data = ao_radio_reg_read(addr);
+ printf ("Read %04x = %02x\n", addr, data);
+}
+#endif
+
+static const struct ao_cmds ao_radio_cmds[] = {
+ { ao_radio_test_cmd, "C <1 start, 0 stop, none both>\0Radio carrier test" },
+#if CC1200_DEBUG
+#if HAS_APRS
+ { ao_radio_aprs, "G\0Send APRS packet" },
+#endif
+ { ao_radio_show, "R\0Show CC1200 status" },
+ { ao_radio_beep, "b\0Emit an RDF beacon" },
+ { ao_radio_packet, "p\0Send a test packet" },
+ { ao_radio_test_recv, "q\0Recv a test packet" },
+#endif
+#if CC1200_LOW_LEVEL_DEBUG
+ { ao_radio_strobe_test, "A <value>\0Strobe radio" },
+ { ao_radio_write_test, "W <addr> <value>\0Write radio reg" },
+ { ao_radio_read_test, "B <addr>\0Read radio reg" },
+#endif
+ { 0, NULL }
+};
+
+void
+ao_radio_init(void)
+{
+ ao_radio_configured = 0;
+ ao_spi_init_cs (AO_CC1200_SPI_CS_PORT, (1 << AO_CC1200_SPI_CS_PIN));
+
+#if 0
+ AO_CC1200_SPI_CS_PORT->bsrr = ((uint32_t) (1 << AO_CC1200_SPI_CS_PIN));
+ for (i = 0; i < 10000; i++) {
+ if ((SPI_2_PORT->idr & (1 << SPI_2_MISO_PIN)) == 0)
+ break;
+ }
+ AO_CC1200_SPI_CS_PORT->bsrr = (1 << AO_CC1200_SPI_CS_PIN);
+ if (i == 10000)
+ ao_panic(AO_PANIC_SELF_TEST_CC1200);
+#endif
+
+ /* Enable the EXTI interrupt for the appropriate pin */
+ ao_enable_port(AO_CC1200_INT_PORT);
+ ao_exti_setup(AO_CC1200_INT_PORT, AO_CC1200_INT_PIN,
+ AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH,
+ ao_radio_isr);
+
+ ao_cmd_register(&ao_radio_cmds[0]);
+}
diff --git a/src/drivers/ao_cc1200.h b/src/drivers/ao_cc1200.h
new file mode 100644
index 00000000..b04775fd
--- /dev/null
+++ b/src/drivers/ao_cc1200.h
@@ -0,0 +1,632 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * 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_CC1200_H_
+#define _AO_CC1200_H_
+
+#define CC1200_READ (7)
+#define CC1200_BURST (6)
+
+/* Register space */
+#define CC1200_IOCFG3 0x00
+#define CC1200_IOCFG_GPIO_ATRAN 7
+#define CC1200_IOCFG_GPIO_INV 6
+#define CC1200_IOCFG_GPIO_CFG 0
+#define CC1200_IOCFG_GPIO_CFG_RXFIFO_THR 0
+#define CC1200_IOCFG_GPIO_CFG_RXFIFO_THR_PKT 1
+#define CC1200_IOCFG_GPIO_CFG_TXFIFO_THR 2
+#define CC1200_IOCFG_GPIO_CFG_TXFIFO_THR_PKT 3
+#define CC1200_IOCFG_GPIO_CFG_RXFIFO_OVERFLOW 4
+#define CC1200_IOCFG_GPIO_CFG_TXFIFO_UNDERFLOW 5
+#define CC1200_IOCFG_GPIO_CFG_PKT_SYNC_RXTX 6
+#define CC1200_IOCFG_GPIO_CFG_CRC_OK 7
+#define CC1200_IOCFG_GPIO_CFG_SERIAL_CLK 8
+#define CC1200_IOCFG_GPIO_CFG_SERIAL_RX 9
+#define CC1200_IOCFG_GPIO_CFG_PQT_REACHED 11
+#define CC1200_IOCFG_GPIO_CFG_PQT_VALID 12
+#define CC1200_IOCFG_GPIO_CFG_RSSI_VALID 13
+#define CC1200_IOCFG_GPIO3_CFG_RSSI_UPDATE 14
+#define CC1200_IOCFG_GPIO2_CFG_RSSI_UPDATE 14
+#define CC1200_IOCFG_GPIO1_CFG_AGC_HOLD 14
+#define CC1200_IOCFG_GPIO0_CFG_AGC_UPDATE 14
+#define CC1200_IOCFG_GPIO3_CFG_CGA_STATUS 15
+#define CC1200_IOCFG_GPIO2_CFG_TXONCCA_DONE 15
+#define CC1200_IOCFG_GPIO1_CFG_CCA_STATUS 15
+#define CC1200_IOCFG_GPIO0_CFG_TXONCCA_FAILED 15
+#define CC1200_IOCFG_GPIO_CFG_CARRIER_SENSE_VALID 16
+#define CC1200_IOCFG_GPIO_CFG_CARRIER_SENSE 17
+#define CC1200_IOCFG_GPIO3_CFG_DSSS_CLK 18
+#define CC1200_IOCFG_GPIO2_CFG_DSSS_DATA0 18
+#define CC1200_IOCFG_GPIO1_CFG_DSSS_CLK 18
+#define CC1200_IOCFG_GPIO0_CFG_DSSS_DATA1 18
+#define CC1200_IOCFG_GPIO_CFG_PKT_CRC_OK 19
+#define CC1200_IOCFG_GPIO_CFG_MARC_MCU_WAKEUP 20
+#define CC1200_IOCFG_GPIO_CFG_SYNC_LOW0_HIGH1 21
+#define CC1200_IOCFG_GPIO_CFG_LNA_PA_REG_PD 23
+#define CC1200_IOCFG_GPIO_CFG_LNA_PD 24
+#define CC1200_IOCFG_GPIO_CFG_PA_RD 25
+#define CC1200_IOCFG_GPIO_CFG_RX0TX1_CFG 26
+#define CC1200_IOCFG_GPIO_CFG_IMAGE_FOUND 28
+#define CC1200_IOCFG_GPIO_CFG_CLKEN_SOFT 29
+#define CC1200_IOCFG_GPIO_CFG_SOFT_TX_DATA_CLK 30
+#define CC1200_IOCFG_GPIO_CFG_RSSI_STEP_FOUND 33
+#define CC1200_IOCFG_GPIO_CFG_RSSI_STEP_EVENT 34
+#define CC1200_IOCFG_GPIO_CFG_ANTENNA_SELECT 36
+#define CC1200_IOCFG_GPIO_CFG_MARC_2PIN_STATUS1 37
+#define CC1200_IOCFG_GPIO_CFG_MARC_2PIN_STATUS0 38
+#define CC1200_IOCFG_GPIO2_CFG_TXFIFO_OVERFLOW 39
+#define CC1200_IOCFG_GPIO0_CFG_RXFIFO_UNDERFLOW 39
+#define CC1200_IOCFG_GPIO3_CFG_MAGN_VALID 40
+#define CC1200_IOCFG_GPIO2_CFG_CHFILT_VALID 40
+#define CC1200_IOCFG_GPIO1_CFG_RCC_CAL_VALID 40
+#define CC1200_IOCFG_GPIO0_CFG_CHFILTER_STARTUP_VALID 40
+#define CC1200_IOCFG_GPIO3_CFG_COLLISION_FOUND 41
+#define CC1200_IOCFG_GPIO2_CFG_SYNC_EVENT 41
+#define CC1200_IOCFG_GPIO1_CFG_COLLISION_FOUND 41
+#define CC1200_IOCFG_GPIO0_CFG_COLLISION_EVENT 41
+#define CC1200_IOCFG_GPIO_CFG_PA_RAMP_UP 42
+#define CC1200_IOCFG_GPIO3_CFG_CRC_FAILED 43
+#define CC1200_IOCFG_GPIO2_CFG_LENGTH_FAILED 43
+#define CC1200_IOCFG_GPIO1_CFG_ADDR_FAILED 43
+#define CC1200_IOCFG_GPIO0_CFG_UART_FRAMING_ERROR 43
+#define CC1200_IOCFG_GPIO_CFG_AGC_STABLE_GAIN 44
+#define CC1200_IOCFG_GPIO_CFG_AGC_UPDATE 45
+#define CC1200_IOCFG_GPIO3_CFG_ADC_CLOCK 46
+#define CC1200_IOCFG_GPIO2_CFG_ADC_Q_DATA_SAMPLE 46
+#define CC1200_IOCFG_GPIO1_CFG_ADC_CLOCK 46
+#define CC1200_IOCFG_GPIO0_CFG_ADC_I_DATA_SAMPLE 46
+#define CC1200_IOCFG_GPIO_CFG_HIGHZ 48
+#define CC1200_IOCFG_GPIO_CFG_EXT_CLOCK 49
+#define CC1200_IOCFG_GPIO_CFG_CHIP_RDY 50
+#define CC1200_IOCFG_GPIO_CFG_HW0 51
+#define CC1200_IOCFG_GPIO_CFG_CLOCK_32K 54
+#define CC1200_IOCFG_GPIO_CFG_WOR_EVENT0 55
+#define CC1200_IOCFG_GPIO_CFG_WOR_EVENT1 56
+#define CC1200_IOCFG_GPIO_CFG_WOR_EVENT2 57
+#define CC1200_IOCFG_GPIO_CFG_XOSC_STABLE 59
+#define CC1200_IOCFG_GPIO_CFG_EXT_OSC_EN 60
+#define CC1200_IOCFG_GPIO_CFG_MASK 0x3f
+
+#define CC1200_IOCFG2 0x01
+#define CC1200_IOCFG1 0x02
+#define CC1200_IOCFG0 0x03
+#define CC1200_SYNC3 0x04
+#define CC1200_SYNC2 0x05
+#define CC1200_SYNC1 0x06
+#define CC1200_SYNC0 0x07
+
+#define CC1200_SYNC_CFG1 0x08
+
+#define CC1200_SYNC_CFG1_SYNC_MODE 5
+#define CC1200_SYNC_CFG1_SYNC_MODE_NONE 0
+#define CC1200_SYNC_CFG1_SYNC_MODE_11_BITS 1
+#define CC1200_SYNC_CFG1_SYNC_MODE_16_BITS 2
+#define CC1200_SYNC_CFG1_SYNC_MODE_18_BITS 3
+#define CC1200_SYNC_CFG1_SYNC_MODE_24_BITS 4
+#define CC1200_SYNC_CFG1_SYNC_MODE_32_BITS 5
+#define CC1200_SYNC_CFG1_SYNC_MODE_16H_BITS 6
+#define CC1200_SYNC_CFG1_SYNC_MODE_16D_BITS 7
+#define CC1200_SYNC_CFG1_SYNC_MODE_MASK 7
+
+#define CC1200_SYNC_CFG1_SYNC_THR 0
+#define CC1200_SYNC_CFG1_SYNC_MASK 0x1f
+
+#define CC1200_SYNC_CFG0 0x09
+#define CC1200_SYNC_CFG0_AUTO_CLEAR 5
+#define CC1200_SYNC_CFG0_RX_CONFIG_LIMITATION 4
+#define CC1200_SYNC_CFG0_PQT_GATING_EN 3
+#define CC1200_SYNC_CFG0_EXT_SYNC_DETECT 2
+
+#define CC1200_SYNC_CFG0_SYNC_STRICT_SYNC_CHECK 0
+#define CC1200_SYNC_CFG0_SYNC_STRICT_SYNC_CHECK_LEVEL_1 0
+#define CC1200_SYNC_CFG0_SYNC_STRICT_SYNC_CHECK_LEVEL_2 1
+#define CC1200_SYNC_CFG0_SYNC_STRICT_SYNC_CHECK_LEVEL_3 2
+#define CC1200_SYNC_CFG0_SYNC_STRICT_SYNC_CHECK_DISABLED 3
+#define CC1200_SYNC_CFG0_SYNC_STRICT_SYNC_CHECK_MASK 3
+
+#define CC1200_DEVIATION_M 0x0a
+#define CC1200_MODCFG_DEV_E 0x0b
+#define CC1200_MODCFG_DEV_E_MODEM_MODE 6
+#define CC1200_MODCFG_DEV_E_MODEM_MODE_NORMAL 0
+#define CC1200_MODCFG_DEV_E_MODEM_MODE_DSSS_REPEAT 1
+#define CC1200_MODCFG_DEV_E_MODEM_MODE_DSSS_PN 2
+#define CC1200_MODCFG_DEV_E_MODEM_MODE_CARRIER_SENSE 3
+#define CC1200_MODCFG_DEV_E_MODEM_MODE_MASK 3
+#define CC1200_MODCFG_DEV_E_MOD_FORMAT 3
+#define CC1200_MODCFG_DEV_E_MOD_FORMAT_2_FSK 0
+#define CC1200_MODCFG_DEV_E_MOD_FORMAT_2_GFSK 1
+#define CC1200_MODCFG_DEV_E_MOD_FORMAT_ASK_OOK 3
+#define CC1200_MODCFG_DEV_E_MOD_FORMAT_4_FSK 4
+#define CC1200_MODCFG_DEV_E_MOD_FORMAT_4_GFSK 5
+#define CC1200_MODCFG_DEV_E_MOD_FORMAT_MASK 7
+#define CC1200_MODCFG_DEV_E_DEV_E 0
+#define CC1200_MODCFG_DEV_E_DEV_E_MASK 7
+
+#define CC1200_DCFILT_CFG 0x0c
+#define CC1200_PREAMBLE_CFG1 0x0d
+#define CC1200_PREAMBLE_CFG1_NUM_PREAMBLE 2
+#define CC1200_PREAMBLE_CFG1_NUM_PREAMBLE_NONE 0
+#define CC1200_PREAMBLE_CFG1_NUM_PREAMBLE_0_5_BYTE 1
+#define CC1200_PREAMBLE_CFG1_NUM_PREAMBLE_1_BYTE 2
+#define CC1200_PREAMBLE_CFG1_NUM_PREAMBLE_1_5_BYTE 3
+#define CC1200_PREAMBLE_CFG1_NUM_PREAMBLE_2_BYTES 4
+#define CC1200_PREAMBLE_CFG1_NUM_PREAMBLE_3_BYTES 5
+#define CC1200_PREAMBLE_CFG1_NUM_PREAMBLE_4_BYTES 6
+#define CC1200_PREAMBLE_CFG1_NUM_PREAMBLE_5_BYTES 7
+#define CC1200_PREAMBLE_CFG1_NUM_PREAMBLE_6_BYTES 8
+#define CC1200_PREAMBLE_CFG1_NUM_PREAMBLE_7_BYTES 9
+#define CC1200_PREAMBLE_CFG1_NUM_PREAMBLE_8_BYTES 10
+#define CC1200_PREAMBLE_CFG1_NUM_PREAMBLE_12_BYTES 11
+#define CC1200_PREAMBLE_CFG1_NUM_PREAMBLE_24_BYTES 12
+#define CC1200_PREAMBLE_CFG1_NUM_PREAMBLE_30_BYTES 13
+#define CC1200_PREAMBLE_CFG1_NUM_PREAMBLE_MASK 0xf
+
+#define CC1200_PREAMBLE_CFG1_PREAMBLE_WORD 0
+#define CC1200_PREAMBLE_CFG1_PREAMBLE_WORD_AA 0
+#define CC1200_PREAMBLE_CFG1_PREAMBLE_WORD_55 1
+#define CC1200_PREAMBLE_CFG1_PREAMBLE_WORD_33 2
+#define CC1200_PREAMBLE_CFG1_PREAMBLE_WORD_CC 3
+#define CC1200_PREAMBLE_CFG1_PREAMBLE_WORD_MASK 3
+
+#define CC1200_PREAMBLE_CFG0 0x0e
+#define CC1200_PREAMBLE_CFG0_PQT_EN 7
+#define CC1200_PREAMBLE_CFG0_PQT_VALID_TIMEOUT 4
+#define CC1200_PREAMBLE_CFG0_PQT_VALID_TIMEOUT_11 0
+#define CC1200_PREAMBLE_CFG0_PQT_VALID_TIMEOUT_12 1
+#define CC1200_PREAMBLE_CFG0_PQT_VALID_TIMEOUT_13 2
+#define CC1200_PREAMBLE_CFG0_PQT_VALID_TIMEOUT_15 3
+#define CC1200_PREAMBLE_CFG0_PQT_VALID_TIMEOUT_16 4
+#define CC1200_PREAMBLE_CFG0_PQT_VALID_TIMEOUT_17 5
+#define CC1200_PREAMBLE_CFG0_PQT_VALID_TIMEOUT_24 6
+#define CC1200_PREAMBLE_CFG0_PQT_VALID_TIMEOUT_32 7
+#define CC1200_PREAMBLE_CFG0_PQT 0
+#define CC1200_PREAMBLE_CFG0_PQT_MASK 0xf
+
+#define CC1200_IQIC 0x0f
+#define CC1200_CHAN_BW 0x10
+#define CC1200_CHAN_BW_ADC_CIC_DECFACT 6
+#define CC1200_CHAN_BW_ADC_CIC_DECFACT_12 0
+#define CC1200_CHAN_BW_ADC_CIC_DECFACT_24 1
+#define CC1200_CHAN_BW_ADC_CIC_DECFACT_48 2
+#define CC1200_CHAN_BW_BB_CIC_DECFACT 0
+
+#define CC1200_MDMCFG1 0x11
+#define CC1200_MDMCFG1_CARRIER_SENSE_GATE 7
+#define CC1200_MDMCFG1_FIFO_EN 6
+#define CC1200_MDMCFG1_MANCHESTER_EN 5
+#define CC1200_MDMCFG1_INVERT_DATA_EN 4
+#define CC1200_MDMCFG1_COLLISION_DETECT_EN 3
+#define CC1200_MDMCFG1_DVGA_GAIN 1
+#define CC1200_MDMCFG1_DVGA_GAIN_0 0
+#define CC1200_MDMCFG1_DVGA_GAIN_3 1
+#define CC1200_MDMCFG1_DVGA_GAIN_6 2
+#define CC1200_MDMCFG1_DVGA_GAIN_9 3
+#define CC1200_MDMCFG1_DVGA_GAIN_MASK 3
+#define CC1200_MDMCFG1_SINGLE_ADC_EN 0
+
+#define CC1200_MDMCFG0 0x12
+#define CC1200_MDMCFG0_TRANSPARENT_MODE_EN 6
+#define CC1200_MDMCFG0_TRANSPARENT_INTFACT 4
+#define CC1200_MDMCFG0_DATA_FILTER_EN 3
+#define CC1200_MDMCFG0_VITERBI_EN 2
+
+#define CC1200_SYMBOL_RATE2 0x13
+#define CC1200_SYMBOL_RATE2_DATARATE_E 4
+#define CC1200_SYMBOL_RATE2_DATARATE_E_MASK 0xf
+#define CC1200_SYMBOL_RATE2_DATARATE_M_19_16 0
+#define CC1200_SYMBOL_RATE2_DATARATE_M_19_16_MASK 0xf
+
+#define CC1200_SYMBOL_RATE1 0x14
+#define CC1200_SYMBOL_RATE0 0x15
+#define CC1200_AGC_REF 0x16
+#define CC1200_AGC_CS_THR 0x17
+#define CC1200_AGC_GAIN_ADJUST 0x18
+
+#define CC1200_AGC_CFG3 0x19
+#define CC1200_AGC_CFG3_RSSI_STEP_THR 7
+#define CC1200_AGC_CFG3_AGC_MIN_GAIN 0
+#define CC1200_AGC_CFG3_AGC_MIN_GAIN_MASK 0x1f
+
+#define CC1200_AGC_CFG2 0x1a
+#define CC1200_AGC_CFG2_START_PREVIOUS_GAIN_EN 7
+#define CC1200_AGC_CFG2_FE_PERFORMANCE_MODE 5
+#define CC1200_AGC_CFG2_FE_PERFORMANCE_MODE_OPTIMIZE_LINEARITY 0
+#define CC1200_AGC_CFG2_FE_PERFORMANCE_MODE_NORMAL 1
+#define CC1200_AGC_CFG2_FE_PERFORMANCE_MODE_LOW_POWER 2
+#define CC1200_AGC_CFG2_FE_PERFORMANCE_MODE_MASK 3
+#define CC1200_AGC_CFG2_AGC_MAX_GAIN 0
+#define CC1200_AGC_CFG2_AGC_MAX_MASK 0x1f
+
+#define CC1200_AGC_CFG1 0x1b
+#define CC1200_AGC_CFG1_RSSI_STEP_THR 6
+#define CC1200_AGC_CFG1_AGC_WIN_SIZE 3
+#define CC1200_AGC_CFG1_AGC_WIN_SIZE_8 0
+#define CC1200_AGC_CFG1_AGC_WIN_SIZE_16 1
+#define CC1200_AGC_CFG1_AGC_WIN_SIZE_32 2
+#define CC1200_AGC_CFG1_AGC_WIN_SIZE_64 3
+#define CC1200_AGC_CFG1_AGC_WIN_SIZE_128 4
+#define CC1200_AGC_CFG1_AGC_WIN_SIZE_256 5
+#define CC1200_AGC_CFG1_AGC_WIN_SIZE_MASK 7
+#define CC1200_AGC_CFG1_AGC_SETTLE_WAIT 0
+#define CC1200_AGC_CFG1_AGC_SETTLE_WAIT_24 0
+#define CC1200_AGC_CFG1_AGC_SETTLE_WAIT_32 1
+#define CC1200_AGC_CFG1_AGC_SETTLE_WAIT_40 2
+#define CC1200_AGC_CFG1_AGC_SETTLE_WAIT_48 3
+#define CC1200_AGC_CFG1_AGC_SETTLE_WAIT_64 4
+#define CC1200_AGC_CFG1_AGC_SETTLE_WAIT_80 5
+#define CC1200_AGC_CFG1_AGC_SETTLE_WAIT_96 6
+#define CC1200_AGC_CFG1_AGC_SETTLE_WAIT_127 7
+
+#define CC1200_AGC_CFG0 0x1c
+
+#define CC1200_AGC_CFG0_AGC_HYST_LEVEL 6
+#define CC1200_AGC_CFG0_AGC_HYST_LEVEL_2 0
+#define CC1200_AGC_CFG0_AGC_HYST_LEVEL_4 1
+#define CC1200_AGC_CFG0_AGC_HYST_LEVEL_7 2
+#define CC1200_AGC_CFG0_AGC_HYST_LEVEL_10 3
+
+#define CC1200_AGC_CFG0_AGC_SLEWRATE_LIMIT 4
+#define CC1200_AGC_CFG0_AGC_SLEWRATE_LIMIT_60 0
+#define CC1200_AGC_CFG0_AGC_SLEWRATE_LIMIT_30 1
+#define CC1200_AGC_CFG0_AGC_SLEWRATE_LIMIT_18 2
+#define CC1200_AGC_CFG0_AGC_SLEWRATE_LIMIT_9 3
+
+#define CC1200_AGC_CFG0_RSSI_VALID_CNT 2
+#define CC1200_AGC_CFG0_RSSI_VALID_CNT_2 0
+#define CC1200_AGC_CFG0_RSSI_VALID_CNT_3 1
+#define CC1200_AGC_CFG0_RSSI_VALID_CNT_5 2
+#define CC1200_AGC_CFG0_RSSI_VALID_CNT_9 3
+
+#define CC1200_AGC_CFG0_AGC_ASK_DECAY 0
+#define CC1200_AGC_CFG0_AGC_ASK_DECAY_1200 0
+#define CC1200_AGC_CFG0_AGC_ASK_DECAY_2400 1
+#define CC1200_AGC_CFG0_AGC_ASK_DECAY_4700 2
+#define CC1200_AGC_CFG0_AGC_ASK_DECAY_9500 3
+
+#define CC1200_FIFO_CFG 0x1d
+#define CC1200_FIFO_CFG_CRC_AUTOFLUSH 7
+#define CC1200_FIFO_CFG_FIFO_THR 0
+
+#define CC1200_DEV_ADDR 0x1e
+#define CC1200_SETTLING_CFG 0x1f
+#define CC1200_SETTLING_CFG_FS_AUTOCAL 3
+#define CC1200_SETTLING_CFG_FS_AUTOCAL_NEVER 0
+#define CC1200_SETTLING_CFG_FS_AUTOCAL_IDLE_TO_ON 1
+#define CC1200_SETTLING_CFG_FS_AUTOCAL_ON_TO_IDLE 2
+#define CC1200_SETTLING_CFG_FS_AUTOCAL_EVERY_4TH_TIME 3
+#define CC1200_SETTLING_CFG_FS_AUTOCAL_MASK 3
+#define CC1200_SETTLING_CFG_LOCK_TIME 1
+#define CC1200_SETTLING_CFG_LOCK_TIME_50_20 0
+#define CC1200_SETTLING_CFG_LOCK_TIME_75_30 1
+#define CC1200_SETTLING_CFG_LOCK_TIME_100_40 2
+#define CC1200_SETTLING_CFG_LOCK_TIME_150_60 3
+#define CC1200_SETTLING_CFG_LOCK_TIME_MASK 3
+#define CC1200_SETTLING_CFG_FSREG_TIME 0
+#define CC1200_SETTLING_CFG_FSREG_TIME_30 0
+#define CC1200_SETTLING_CFG_FSREG_TIME_60 1
+#define CC1200_SETTLING_CFG_FSREG_TIME_MASK 1
+
+#define CC1200_FS_CFG 0x20
+#define CC1200_FS_CFG_LOCK_EN 4
+#define CC1200_FS_CFG_FSD_BANDSELECT 0
+#define CC1200_FS_CFG_FSD_BANDSELECT_820_960 2
+#define CC1200_FS_CFG_FSD_BANDSELECT_410_480 4
+#define CC1200_FS_CFG_FSD_BANDSELECT_273_320 6
+#define CC1200_FS_CFG_FSD_BANDSELECT_205_240 8
+#define CC1200_FS_CFG_FSD_BANDSELECT_164_192 10
+#define CC1200_FS_CFG_FSD_BANDSELECT_136_160 11
+#define CC1200_FS_CFG_FSD_BANDSELECT_MASK 0xf
+
+#define CC1200_WOR_CFG1 0x21
+#define CC1200_WOR_CFG0 0x22
+#define CC1200_WOR_EVENT0_MSB 0x23
+#define CC1200_WOR_EVENT0_LSB 0x24
+#define CC1200_RXDCM_TIME 0x25
+#define CC1200_PKT_CFG2 0x26
+#define CC1200_PKT_CFG2_BYTE_SWAP_EN 6
+#define CC1200_PKT_CFG2_FG_MODE_EN 5
+#define CC1200_PKT_CFG2_CCA_MODE 2
+#define CC1200_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR 0
+#define CC1200_PKT_CFG2_CCA_MODE_RSSI_THRESHOLD 1
+#define CC1200_PKT_CFG2_CCA_MODE_NOT_RECEIVING 2
+#define CC1200_PKT_CFG2_CCA_MODE_RSSI_OR_NOT 3
+#define CC1200_PKT_CFG2_CCA_MODE_RSSI_AND_ETSI_LBT 4
+#define CC1200_PKT_CFG2_CCA_MODE_MASK 7
+#define CC1200_PKT_CFG2_PKT_FORMAT 0
+#define CC1200_PKT_CFG2_PKT_FORMAT_NORMAL 0
+#define CC1200_PKT_CFG2_PKT_FORMAT_SYNCHRONOUS_SERIAL 1
+#define CC1200_PKT_CFG2_PKT_FORMAT_RANDOM 2
+#define CC1200_PKT_CFG2_PKT_FORMAT_TRANSPARENT_SERIAL 3
+#define CC1200_PKT_CFG2_PKT_FORMAT_MASK 3
+
+#define CC1200_PKT_CFG1 0x27
+#define CC1200_PKT_CFG1_FEC_EN 7
+#define CC1200_PKT_CFG1_WHITE_DATA 6
+#define CC1200_PKT_CFG1_PN9_SWAP_EN 5
+#define CC1200_PKT_CFG1_ADDR_CHECK_CFG 3
+#define CC1200_PKT_CFG1_ADDR_CHECK_CFG_NONE 0
+#define CC1200_PKT_CFG1_ADDR_CHECK_CFG_CHECK 1
+#define CC1200_PKT_CFG1_ADDR_CHECK_CFG_00_BROADCAST 2
+#define CC1200_PKT_CFG1_ADDR_CHECK_CFG_00_FF_BROADCAST 3
+#define CC1200_PKT_CFG1_ADDR_CHECK_CFG_MASK 3
+#define CC1200_PKT_CFG1_CRC_CFG 1
+#define CC1200_PKT_CFG1_CRC_CFG_DISABLED 0
+#define CC1200_PKT_CFG1_CRC_CFG_CRC16_INIT_ONES 1
+#define CC1200_PKT_CFG1_CRC_CFG_CRC16_INIT_ZEROS 2
+#define CC1200_PKT_CFG1_CRC_CFG_MASK 3
+#define CC1200_PKT_CFG1_APPEND_STATUS 0
+
+#define CC1200_PKT_CFG0 0x28
+#define CC1200_PKT_CFG0_RESERVED7 7
+#define CC1200_PKT_CFG0_LENGTH_CONFIG 5
+#define CC1200_PKT_CFG0_LENGTH_CONFIG_FIXED 0
+#define CC1200_PKT_CFG0_LENGTH_CONFIG_VARIABLE 1
+#define CC1200_PKT_CFG0_LENGTH_CONFIG_INFINITE 2
+#define CC1200_PKT_CFG0_LENGTH_CONFIG_VARIABLE_5LSB 3
+#define CC1200_PKT_CFG0_LENGTH_CONFIG_MASK 3
+#define CC1200_PKT_CFG0_PKG_BIT_LEN 2
+#define CC1200_PKT_CFG0_PKG_BIT_LEN_MASK 7
+#define CC1200_PKT_CFG0_UART_MODE_EN 1
+#define CC1200_PKT_CFG0_UART_SWAP_EN 0
+
+#define CC1200_RFEND_CFG1 0x29
+#define CC1200_RFEND_CFG1_RXOFF_MODE 4
+#define CC1200_RFEND_CFG1_RXOFF_MODE_IDLE 0
+#define CC1200_RFEND_CFG1_RXOFF_MODE_FSTXON 1
+#define CC1200_RFEND_CFG1_RXOFF_MODE_TX 2
+#define CC1200_RFEND_CFG1_RXOFF_MODE_RX 3
+#define CC1200_RFEND_CFG1_RX_TIME 1
+#define CC1200_RFEND_CFG1_RX_TIME_INFINITE 7
+#define CC1200_RFEND_CFG1_RX_TIME_QUAL 0
+#define CC1200_RFEND_CFG0 0x2a
+#define CC1200_RFEND_CFG0_CAL_END_WAKE_UP_EN 6
+#define CC1200_RFEND_CFG0_TXOFF_MODE 4
+#define CC1200_RFEND_CFG0_TXOFF_MODE_IDLE 0
+#define CC1200_RFEND_CFG0_TXOFF_MODE_FSTXON 1
+#define CC1200_RFEND_CFG0_TXOFF_MODE_TX 2
+#define CC1200_RFEND_CFG0_TXOFF_MODE_RX 3
+#define CC1200_RFEND_CFG0_TERM_ON_BAD_PACKET_EN 3
+#define CC1200_RFEND_CFG0_ANT_DIV_RX_TERM_CFG 0
+#define CC1200_PA_CFG1 0x2b
+#define CC1200_PA_CFG0 0x2c
+#define CC1200_ASK_CFG 0x2d
+#define CC1200_PKT_LEN 0x2e
+
+#define CC1200_EXTENDED 0x2f
+
+/* Command strobes */
+#define CC1200_SRES 0x30
+#define CC1200_SFSTXON 0x31
+#define CC1200_SXOFF 0x32
+#define CC1200_SCAL 0x33
+#define CC1200_SRX 0x34
+#define CC1200_STX 0x35
+#define CC1200_SIDLE 0x36
+#define CC1200_SAFC 0x37
+#define CC1200_SWOR 0x38
+#define CC1200_SPWD 0x39
+#define CC1200_SFRX 0x3a
+#define CC1200_SFTX 0x3b
+#define CC1200_SWORRST 0x3c
+#define CC1200_SNOP 0x3d
+
+#define CC1200_DIRECT_FIFO 0x3e
+#define CC1200_FIFO 0x3f
+
+#define CC1200_FIFO_SIZE 128
+
+/* Extended register space */
+
+#define CC1200_EXTENDED_BIT 0x8000
+
+#define CC1200_IS_EXTENDED(r) ((r) & CC1200_EXTENDED_BIT)
+
+#define CC1200_IF_MIX_CFG (CC1200_EXTENDED_BIT | 0x00)
+#define CC1200_FREQOFF_CFG (CC1200_EXTENDED_BIT | 0x01)
+#define CC1200_TOC_CFG (CC1200_EXTENDED_BIT | 0x02)
+#define CC1200_MARC_SPARE (CC1200_EXTENDED_BIT | 0x03)
+#define CC1200_ECG_CFG (CC1200_EXTENDED_BIT | 0x04)
+#define CC1200_MDMCFG2 (CC1200_EXTENDED_BIT | 0x05)
+
+#define CC1200_MDMCFG2_ASK_SHAPE 6
+#define CC1200_MDMCFG2_ASK_SHAPE_8 0
+#define CC1200_MDMCFG2_ASK_SHAPE_16 1
+#define CC1200_MDMCFG2_ASK_SHAPE_32 2
+#define CC1200_MDMCFG2_ASK_SHAPE_128 3
+#define CC1200_MDMCFG2_SYMBOL_MAP_CFG 4
+#define CC1200_MDMCFG2_SYMBOL_MAP_CFG_MODE_0 0
+#define CC1200_MDMCFG2_SYMBOL_MAP_CFG_MODE_1 1
+#define CC1200_MDMCFG2_SYMBOL_MAP_CFG_MODE_2 2
+#define CC1200_MDMCFG2_SYMBOL_MAP_CFG_MODE_3 3
+#define CC1200_MDMCFG2_UPSAMPLER_P 1
+#define CC1200_MDMCFG2_UPSAMPLER_P_1 0
+#define CC1200_MDMCFG2_UPSAMPLER_P_2 1
+#define CC1200_MDMCFG2_UPSAMPLER_P_4 2
+#define CC1200_MDMCFG2_UPSAMPLER_P_8 3
+#define CC1200_MDMCFG2_UPSAMPLER_P_16 4
+#define CC1200_MDMCFG2_UPSAMPLER_P_32 5
+#define CC1200_MDMCFG2_UPSAMPLER_P_64 6
+#define CC1200_MDMCFG2_CFM_DATA_EN 0
+
+#define CC1200_EXT_CTRL (CC1200_EXTENDED_BIT | 0x06)
+#define CC1200_RCCAL_FINE (CC1200_EXTENDED_BIT | 0x07)
+#define CC1200_RCCAL_COARSE (CC1200_EXTENDED_BIT | 0x08)
+#define CC1200_RCCAL_OFFSET (CC1200_EXTENDED_BIT | 0x09)
+#define CC1200_FREQOFF1 (CC1200_EXTENDED_BIT | 0x0A)
+#define CC1200_FREQOFF0 (CC1200_EXTENDED_BIT | 0x0B)
+#define CC1200_FREQ2 (CC1200_EXTENDED_BIT | 0x0C)
+#define CC1200_FREQ1 (CC1200_EXTENDED_BIT | 0x0D)
+#define CC1200_FREQ0 (CC1200_EXTENDED_BIT | 0x0E)
+#define CC1200_IF_ADC2 (CC1200_EXTENDED_BIT | 0x0F)
+#define CC1200_IF_ADC1 (CC1200_EXTENDED_BIT | 0x10)
+#define CC1200_IF_ADC0 (CC1200_EXTENDED_BIT | 0x11)
+#define CC1200_FS_DIG1 (CC1200_EXTENDED_BIT | 0x12)
+#define CC1200_FS_DIG0 (CC1200_EXTENDED_BIT | 0x13)
+#define CC1200_FS_CAL3 (CC1200_EXTENDED_BIT | 0x14)
+#define CC1200_FS_CAL2 (CC1200_EXTENDED_BIT | 0x15)
+#define CC1200_FS_CAL1 (CC1200_EXTENDED_BIT | 0x16)
+#define CC1200_FS_CAL0 (CC1200_EXTENDED_BIT | 0x17)
+#define CC1200_FS_CHP (CC1200_EXTENDED_BIT | 0x18)
+#define CC1200_FS_DIVTWO (CC1200_EXTENDED_BIT | 0x19)
+#define CC1200_FS_DSM1 (CC1200_EXTENDED_BIT | 0x1A)
+#define CC1200_FS_DSM0 (CC1200_EXTENDED_BIT | 0x1B)
+#define CC1200_FS_DVC1 (CC1200_EXTENDED_BIT | 0x1C)
+#define CC1200_FS_DVC0 (CC1200_EXTENDED_BIT | 0x1D)
+#define CC1200_FS_LBI (CC1200_EXTENDED_BIT | 0x1E)
+#define CC1200_FS_PFD (CC1200_EXTENDED_BIT | 0x1F)
+#define CC1200_FS_PRE (CC1200_EXTENDED_BIT | 0x20)
+#define CC1200_FS_REG_DIV_CML (CC1200_EXTENDED_BIT | 0x21)
+#define CC1200_FS_SPARE (CC1200_EXTENDED_BIT | 0x22)
+#define CC1200_FS_VCO4 (CC1200_EXTENDED_BIT | 0x23)
+#define CC1200_FS_VCO3 (CC1200_EXTENDED_BIT | 0x24)
+#define CC1200_FS_VCO2 (CC1200_EXTENDED_BIT | 0x25)
+#define CC1200_FS_VCO1 (CC1200_EXTENDED_BIT | 0x26)
+#define CC1200_FS_VCO0 (CC1200_EXTENDED_BIT | 0x27)
+#define CC1200_GBIAS6 (CC1200_EXTENDED_BIT | 0x28)
+#define CC1200_GBIAS5 (CC1200_EXTENDED_BIT | 0x29)
+#define CC1200_GBIAS4 (CC1200_EXTENDED_BIT | 0x2A)
+#define CC1200_GBIAS3 (CC1200_EXTENDED_BIT | 0x2B)
+#define CC1200_GBIAS2 (CC1200_EXTENDED_BIT | 0x2C)
+#define CC1200_GBIAS1 (CC1200_EXTENDED_BIT | 0x2D)
+#define CC1200_GBIAS0 (CC1200_EXTENDED_BIT | 0x2E)
+#define CC1200_IFAMP (CC1200_EXTENDED_BIT | 0x2F)
+#define CC1200_LNA (CC1200_EXTENDED_BIT | 0x30)
+#define CC1200_RXMIX (CC1200_EXTENDED_BIT | 0x31)
+#define CC1200_XOSC5 (CC1200_EXTENDED_BIT | 0x32)
+#define CC1200_XOSC4 (CC1200_EXTENDED_BIT | 0x33)
+#define CC1200_XOSC3 (CC1200_EXTENDED_BIT | 0x34)
+#define CC1200_XOSC2 (CC1200_EXTENDED_BIT | 0x35)
+#define CC1200_XOSC1 (CC1200_EXTENDED_BIT | 0x36)
+#define CC1200_XOSC0 (CC1200_EXTENDED_BIT | 0x37)
+#define CC1200_ANALOG_SPARE (CC1200_EXTENDED_BIT | 0x38)
+#define CC1200_PA_CFG3 (CC1200_EXTENDED_BIT | 0x39)
+#define CC1200_WOR_TIME1 (CC1200_EXTENDED_BIT | 0x64)
+#define CC1200_WOR_TIME0 (CC1200_EXTENDED_BIT | 0x65)
+#define CC1200_WOR_CAPTURE1 (CC1200_EXTENDED_BIT | 0x66)
+#define CC1200_WOR_CAPTURE0 (CC1200_EXTENDED_BIT | 0x67)
+#define CC1200_BIST (CC1200_EXTENDED_BIT | 0x68)
+#define CC1200_DCFILTOFFSET_I1 (CC1200_EXTENDED_BIT | 0x69)
+#define CC1200_DCFILTOFFSET_I0 (CC1200_EXTENDED_BIT | 0x6A)
+#define CC1200_DCFILTOFFSET_Q1 (CC1200_EXTENDED_BIT | 0x6B)
+#define CC1200_DCFILTOFFSET_Q0 (CC1200_EXTENDED_BIT | 0x6C)
+#define CC1200_IQIE_I1 (CC1200_EXTENDED_BIT | 0x6D)
+#define CC1200_IQIE_I0 (CC1200_EXTENDED_BIT | 0x6E)
+#define CC1200_IQIE_Q1 (CC1200_EXTENDED_BIT | 0x6f)
+#define CC1200_IQIE_Q0 (CC1200_EXTENDED_BIT | 0x70)
+#define CC1200_RSSI1 (CC1200_EXTENDED_BIT | 0x71)
+#define CC1200_RSSI0 (CC1200_EXTENDED_BIT | 0x72)
+#define CC1200_MARCSTATE (CC1200_EXTENDED_BIT | 0x73)
+#define CC1200_LQI_VAL (CC1200_EXTENDED_BIT | 0x74)
+#define CC1200_PQT_SYNC_ERR (CC1200_EXTENDED_BIT | 0x75)
+#define CC1200_DEM_STATUS (CC1200_EXTENDED_BIT | 0x76)
+#define CC1200_FREQOFF_EST1 (CC1200_EXTENDED_BIT | 0x77)
+#define CC1200_FREQOFF_EST0 (CC1200_EXTENDED_BIT | 0x78)
+#define CC1200_AGC_GAIN3 (CC1200_EXTENDED_BIT | 0x79)
+#define CC1200_AGC_GAIN2 (CC1200_EXTENDED_BIT | 0x7a)
+#define CC1200_AGC_GAIN1 (CC1200_EXTENDED_BIT | 0x7b)
+#define CC1200_AGC_GAIN0 (CC1200_EXTENDED_BIT | 0x7c)
+#define CC1200_SOFT_RX_DATA_OUT (CC1200_EXTENDED_BIT | 0x7d)
+#define CC1200_SOFT_TX_DATA_IN (CC1200_EXTENDED_BIT | 0x7e)
+#define CC1200_ASK_SOFT_RX_DATA (CC1200_EXTENDED_BIT | 0x7f)
+#define CC1200_RNDGEN (CC1200_EXTENDED_BIT | 0x80)
+#define CC1200_MAGN2 (CC1200_EXTENDED_BIT | 0x81)
+#define CC1200_MAGN1 (CC1200_EXTENDED_BIT | 0x82)
+#define CC1200_MAGN0 (CC1200_EXTENDED_BIT | 0x83)
+#define CC1200_ANG1 (CC1200_EXTENDED_BIT | 0x84)
+#define CC1200_ANG0 (CC1200_EXTENDED_BIT | 0x85)
+#define CC1200_CHFILT_I2 (CC1200_EXTENDED_BIT | 0x86)
+#define CC1200_CHFILT_I1 (CC1200_EXTENDED_BIT | 0x87)
+#define CC1200_CHFILT_I0 (CC1200_EXTENDED_BIT | 0x88)
+#define CC1200_CHFILT_Q2 (CC1200_EXTENDED_BIT | 0x89)
+#define CC1200_CHFILT_Q1 (CC1200_EXTENDED_BIT | 0x8a)
+#define CC1200_CHFILT_Q0 (CC1200_EXTENDED_BIT | 0x8b)
+#define CC1200_GPIO_STATUS (CC1200_EXTENDED_BIT | 0x8c)
+#define CC1200_FSCAL_CTRL (CC1200_EXTENDED_BIT | 0x8d)
+#define CC1200_PHASE_ADJUST (CC1200_EXTENDED_BIT | 0x8e)
+#define CC1200_PARTNUMBER (CC1200_EXTENDED_BIT | 0x8f)
+#define CC1200_PARTVERSION (CC1200_EXTENDED_BIT | 0x90)
+#define CC1200_SERIAL_STATUS (CC1200_EXTENDED_BIT | 0x91)
+#define CC1200_MODEM_STATUS1 (CC1200_EXTENDED_BIT | 0x92)
+#define CC1200_MODEM_STATUS1_SYNC_FOUND 7
+#define CC1200_MODEM_STATUS1_RXFIFO_FULL 6
+#define CC1200_MODEM_STATUS1_RXFIFO_THR 5
+#define CC1200_MODEM_STATUS1_RXFIFO_EMPTY 4
+#define CC1200_MODEM_STATUS1_RXFIFO_OVERFLOW 3
+#define CC1200_MODEM_STATUS1_RXFIFO_UNDERFLOW 2
+#define CC1200_MODEM_STATUS1_PQT_REACHED 1
+#define CC1200_MODEM_STATUS1_PQT_VALID 0
+
+#define CC1200_MODEM_STATUS0 (CC1200_EXTENDED_BIT | 0x93)
+#define CC1200_MODEM_STATUS0_FEC_RX_OVERFLOW 6
+#define CC1200_MODEM_STATUS0_SYNC_SENT 4
+#define CC1200_MODEM_STATUS0_TXFIFO_FULL 3
+#define CC1200_MODEM_STATUS0_TXFIFO_THR 2
+#define CC1200_MODEM_STATUS0_TXFIFO_OVERFLOW 1
+#define CC1200_MODEM_STATUS0_TXFIFO_UNDERFLOW 0
+
+#define CC1200_MARC_STATUS1 (CC1200_EXTENDED_BIT | 0x94)
+#define CC1200_MARC_STATUS1_NO_FAILURE 0
+#define CC1200_MARC_STATUS1_RX_TIMEOUT 1
+#define CC1200_MARC_STATUS1_RX_TERMINATION 2
+#define CC1200_MARC_STATUS1_EWOR_SYNC_LOST 3
+#define CC1200_MARC_STATUS1_MAXIMUM_LENGTH 4
+#define CC1200_MARC_STATUS1_ADDRESS 5
+#define CC1200_MARC_STATUS1_CRC 6
+#define CC1200_MARC_STATUS1_TX_FIFO_OVERFLOW 7
+#define CC1200_MARC_STATUS1_TX_FIFO_UNDERFLOW 8
+#define CC1200_MARC_STATUS1_RX_FIFO_OVERFLOW 9
+#define CC1200_MARC_STATUS1_RX_FIFO_UNDERFLOW 10
+#define CC1200_MARC_STATUS1_TX_ON_CCA_FAILED 11
+#define CC1200_MARC_STATUS1_TX_FINISHED 0x40
+#define CC1200_MARC_STATUS1_RX_FINISHED 0x80
+#define CC1200_MARC_STATUS0 (CC1200_EXTENDED_BIT | 0x95)
+#define CC1200_PA_IFAMP_TEST (CC1200_EXTENDED_BIT | 0x96)
+#define CC1200_FSRF_TEST (CC1200_EXTENDED_BIT | 0x97)
+#define CC1200_PRE_TEST (CC1200_EXTENDED_BIT | 0x98)
+#define CC1200_PRE_OVR (CC1200_EXTENDED_BIT | 0x99)
+#define CC1200_ADC_TEST (CC1200_EXTENDED_BIT | 0x9a)
+#define CC1200_DVC_TEST (CC1200_EXTENDED_BIT | 0x9b)
+#define CC1200_ATEST (CC1200_EXTENDED_BIT | 0x9c)
+#define CC1200_ATEST_LVDS (CC1200_EXTENDED_BIT | 0x9d)
+#define CC1200_ATEST_MODE (CC1200_EXTENDED_BIT | 0x9e)
+#define CC1200_XOSC_TEST1 (CC1200_EXTENDED_BIT | 0x9f)
+#define CC1200_XOSC_TEST0 (CC1200_EXTENDED_BIT | 0xa0)
+#define CC1200_RXFIRST (CC1200_EXTENDED_BIT | 0xd2)
+#define CC1200_TXFIRST (CC1200_EXTENDED_BIT | 0xd3)
+#define CC1200_RXLAST (CC1200_EXTENDED_BIT | 0xd4)
+#define CC1200_TXLAST (CC1200_EXTENDED_BIT | 0xd5)
+#define CC1200_NUM_TXBYTES (CC1200_EXTENDED_BIT | 0xd6)
+#define CC1200_NUM_RXBYTES (CC1200_EXTENDED_BIT | 0xd7)
+#define CC1200_FIFO_NUM_TXBYTES (CC1200_EXTENDED_BIT | 0xd8)
+#define CC1200_FIFO_NUM_RXBYTES (CC1200_EXTENDED_BIT | 0xd9)
+#define CC1200_RXFIFO_PRE_BUF (CC1200_EXTENDED_BIT | 0xda)
+#define CC1200_AES_WORKSPACE_0 (CC1200_EXTENDED_BIT | 0xe0)
+
+/* Status byte */
+#define CC1200_STATUS_CHIP_RDY 7
+#define CC1200_STATUS_STATE 4
+#define CC1200_STATUS_STATE_IDLE 0
+#define CC1200_STATUS_STATE_RX 1
+#define CC1200_STATUS_STATE_TX 2
+#define CC1200_STATUS_STATE_FSTXON 3
+#define CC1200_STATUS_STATE_CALIBRATE 4
+#define CC1200_STATUS_STATE_SETTLING 5
+#define CC1200_STATUS_STATE_RX_FIFO_ERROR 6
+#define CC1200_STATUS_STATE_TX_FIFO_ERROR 7
+#define CC1200_STATUS_STATE_MASK 7
+
+#endif /* _AO_CC1200_H_ */
diff --git a/src/drivers/ao_cc1200_CC1200.h b/src/drivers/ao_cc1200_CC1200.h
new file mode 100644
index 00000000..35673123
--- /dev/null
+++ b/src/drivers/ao_cc1200_CC1200.h
@@ -0,0 +1,122 @@
+/***************************************************************
+ * SmartRF Studio(tm) Export
+ *
+ * Radio register settings specifed with address, value
+ *
+ * RF device: CC1200
+ *
+ ***************************************************************/
+
+/*
+ * Values affecting receive sensitivity:
+ *
+ *
+ * PQT - sets how good the preamble needs to look before
+ * we start looking for a sync word
+ * SYNC_THR - sets how good the sync needs to be before we
+ * start decoding a packet
+ */
+
+/* Values depending on data rate
+ *
+ * DCFILT_BW_SETTLE
+ * DCFILT_BW
+ */
+
+#ifndef AO_CC1200_AGC_GAIN_ADJUST
+#define AO_CC1200_AGC_GAIN_ADJUST -94
+#endif
+
+ CC1200_IOCFG2, 0x06, /* GPIO2 IO Pin Configuration */
+ CC1200_SYNC3, 0xD3, /* Sync Word Configuration [23:16] */
+ CC1200_SYNC2, 0x91, /* Sync Word Configuration [23:16] */
+ CC1200_SYNC1, 0xD3, /* Sync Word Configuration [15:8] */
+ CC1200_SYNC0, 0x91, /* Sync Word Configuration [7:0] */
+ CC1200_SYNC_CFG1, /* Sync Word Detection Configuration Reg. 1 */
+ ((CC1200_SYNC_CFG1_SYNC_MODE_16_BITS << CC1200_SYNC_CFG1_SYNC_MODE) |
+ (11 << CC1200_SYNC_CFG1_SYNC_THR)),
+ CC1200_SYNC_CFG0, /* Sync Word Detection Configuration Reg. 0 */
+ ((1 << CC1200_SYNC_CFG0_AUTO_CLEAR) |
+ (0 << CC1200_SYNC_CFG0_RX_CONFIG_LIMITATION) |
+ (1 << CC1200_SYNC_CFG0_PQT_GATING_EN) |
+ (0 << CC1200_SYNC_CFG0_EXT_SYNC_DETECT) |
+ (CC1200_SYNC_CFG0_SYNC_STRICT_SYNC_CHECK_DISABLED << CC1200_SYNC_CFG0_SYNC_STRICT_SYNC_CHECK)),
+ CC1200_DEVIATION_M, 0x50, /* Frequency Deviation Configuration */
+ CC1200_DCFILT_CFG, 0x5d, /* Digital DC Removal Configuration */
+ CC1200_PREAMBLE_CFG0, /* Preamble Detection Configuration Reg. 0 */
+ ((1 << CC1200_PREAMBLE_CFG0_PQT_EN) |
+ (CC1200_PREAMBLE_CFG0_PQT_VALID_TIMEOUT_11 << CC1200_PREAMBLE_CFG0_PQT_VALID_TIMEOUT) |
+ (15 << CC1200_PREAMBLE_CFG0_PQT)),
+ CC1200_IQIC, 0xcb, /* Digital Image Channel Compensation Configuration */
+ CC1200_CHAN_BW, 0x11, /* Channel Filter Configuration */
+ CC1200_MDMCFG1, 0x40, /* General Modem Parameter Configuration Reg. 1 */
+ CC1200_MDMCFG0, 0x05, /* General Modem Parameter Configuration Reg. 0 */
+ CC1200_SYMBOL_RATE2, 0x93, /* Symbol Rate Configuration Exponent and Mantissa [1.. */
+ CC1200_AGC_REF, 0x27, /* AGC Reference Level Configuration */
+ CC1200_AGC_CS_THR, 0xec, /* Carrier Sense Threshold Configuration */
+ CC1200_AGC_GAIN_ADJUST, /* RSSI adjustment */
+ AO_CC1200_AGC_GAIN_ADJUST,
+ CC1200_AGC_CFG1, 0x51, /* Automatic Gain Control Configuration Reg. 1 */
+ CC1200_AGC_CFG0, 0x87, /* Automatic Gain Control Configuration Reg. 0 */
+ CC1200_FIFO_CFG, 0x40, /* FIFO Configuration */
+ CC1200_SETTLING_CFG, /* Frequency Synthesizer Calibration and Settling Configuration */
+ ((CC1200_SETTLING_CFG_FS_AUTOCAL_EVERY_4TH_TIME << CC1200_SETTLING_CFG_FS_AUTOCAL) |
+ (CC1200_SETTLING_CFG_LOCK_TIME_75_30 << CC1200_SETTLING_CFG_LOCK_TIME) |
+ (CC1200_SETTLING_CFG_FSREG_TIME_60 << CC1200_SETTLING_CFG_FSREG_TIME)),
+ CC1200_FS_CFG, /* Frequency Synthesizer Configuration */
+ ((1 << CC1200_FS_CFG_LOCK_EN) |
+ (CC1200_FS_CFG_FSD_BANDSELECT_410_480 << CC1200_FS_CFG_FSD_BANDSELECT)),
+ CC1200_PKT_CFG2, /* Packet Configuration Reg. 2 */
+ ((0 << CC1200_PKT_CFG2_FG_MODE_EN) |
+ (CC1200_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1200_PKT_CFG2_CCA_MODE) |
+ (CC1200_PKT_CFG2_PKT_FORMAT_NORMAL << CC1200_PKT_CFG2_PKT_FORMAT)),
+ CC1200_PKT_CFG1, /* Packet Configuration Reg. 1 */
+ ((1 << CC1200_PKT_CFG1_FEC_EN) |
+ (1 << CC1200_PKT_CFG1_WHITE_DATA) |
+ (0 << CC1200_PKT_CFG1_PN9_SWAP_EN) |
+ (CC1200_PKT_CFG1_ADDR_CHECK_CFG_NONE << CC1200_PKT_CFG1_ADDR_CHECK_CFG) |
+ (CC1200_PKT_CFG1_CRC_CFG_CRC16_INIT_ONES << CC1200_PKT_CFG1_CRC_CFG) |
+ (1 << CC1200_PKT_CFG1_APPEND_STATUS)),
+ CC1200_PKT_CFG0, /* Packet Configuration Reg. 0 */
+ ((CC1200_PKT_CFG0_LENGTH_CONFIG_FIXED << CC1200_PKT_CFG0_LENGTH_CONFIG) |
+ (0 << CC1200_PKT_CFG0_PKG_BIT_LEN) |
+ (0 << CC1200_PKT_CFG0_UART_MODE_EN) |
+ (0 << CC1200_PKT_CFG0_UART_SWAP_EN)),
+ CC1200_RFEND_CFG1, /* RFEND Configuration Reg. 1 */
+ ((CC1200_RFEND_CFG1_RXOFF_MODE_IDLE << CC1200_RFEND_CFG1_RXOFF_MODE) |
+ (CC1200_RFEND_CFG1_RX_TIME_INFINITE << CC1200_RFEND_CFG1_RX_TIME) |
+ (0 << CC1200_RFEND_CFG1_RX_TIME_QUAL)),
+ CC1200_RFEND_CFG0, /* RFEND Configuration Reg. 0 */
+ ((0 << CC1200_RFEND_CFG0_CAL_END_WAKE_UP_EN) |
+ (CC1200_RFEND_CFG0_TXOFF_MODE_IDLE << CC1200_RFEND_CFG0_TXOFF_MODE) |
+ (1 << CC1200_RFEND_CFG0_TERM_ON_BAD_PACKET_EN) |
+ (0 << CC1200_RFEND_CFG0_ANT_DIV_RX_TERM_CFG)),
+ CC1200_PA_CFG1, 0x3f, /* Power Amplifier Configuration Reg. 1 */
+ CC1200_PA_CFG0, 0x53, /* Power Amplifier Configuration Reg. 0 */
+ CC1200_PKT_LEN, 0xff, /* Packet Length Configuration */
+ CC1200_IF_MIX_CFG, 0x1c, /* IF Mix Configuration */
+ CC1200_FREQOFF_CFG, 0x22, /* Frequency Offset Correction Configuration */
+ CC1200_MDMCFG2, /* General Modem Parameter Configuration Reg. 2 */
+ ((CC1200_MDMCFG2_ASK_SHAPE_8 << CC1200_MDMCFG2_ASK_SHAPE) |
+ (CC1200_MDMCFG2_SYMBOL_MAP_CFG_MODE_0 << CC1200_MDMCFG2_SYMBOL_MAP_CFG) |
+ (CC1200_MDMCFG2_UPSAMPLER_P_8 << CC1200_MDMCFG2_UPSAMPLER_P) |
+ (0 << CC1200_MDMCFG2_CFM_DATA_EN)),
+ CC1200_FREQ2, 0x6c, /* Frequency Configuration [23:16] */
+ CC1200_FREQ1, 0xa3, /* Frequency Configuration [15:8] */
+ CC1200_FREQ0, 0x33, /* Frequency Configuration [7:0] */
+ CC1200_IF_ADC1, 0xee, /* Analog to Digital Converter Configuration Reg. 1 */
+ CC1200_IF_ADC0, 0x10, /* Analog to Digital Converter Configuration Reg. 0 */
+ CC1200_FS_DIG1, 0x07, /* Frequency Synthesizer Digital Reg. 1 */
+ CC1200_FS_DIG0, 0xaf, /* Frequency Synthesizer Digital Reg. 0 */
+ CC1200_FS_CAL1, 0x40, /* Frequency Synthesizer Calibration Reg. 1 */
+ CC1200_FS_CAL0, 0x0e, /* Frequency Synthesizer Calibration Reg. 0 */
+ CC1200_FS_DIVTWO, 0x03, /* Frequency Synthesizer Divide by 2 */
+ CC1200_FS_DSM0, 0x33, /* FS Digital Synthesizer Module Configuration Reg. 0 */
+ CC1200_FS_DVC0, 0x17, /* Frequency Synthesizer Divider Chain Configuration .. */
+ CC1200_FS_PFD, 0x00, /* Frequency Synthesizer Phase Frequency Detector Con.. */
+ CC1200_FS_PRE, 0x6e, /* Frequency Synthesizer Prescaler Configuration */
+ CC1200_FS_REG_DIV_CML, 0x1c, /* Frequency Synthesizer Divider Regulator Configurat.. */
+ CC1200_FS_SPARE, 0xac, /* Frequency Synthesizer Spare */
+ CC1200_FS_VCO0, 0xb5, /* FS Voltage Controlled Oscillator Configuration Reg.. */
+ CC1200_XOSC5, 0x0e, /* Crystal Oscillator Configuration Reg. 5 */
+ CC1200_XOSC1, 0x03, /* Crystal Oscillator Configuration Reg. 1 */
diff --git a/src/drivers/ao_packet_master.c b/src/drivers/ao_packet_master.c
index 23545049..42a4f5bf 100644
--- a/src/drivers/ao_packet_master.c
+++ b/src/drivers/ao_packet_master.c
@@ -17,7 +17,7 @@
#include "ao.h"
-static char
+static int
ao_packet_getchar(void)
{
int c;
diff --git a/src/easymini-v1.0/ao_pins.h b/src/easymini-v1.0/ao_pins.h
index 0edde5a2..1d794497 100644
--- a/src/easymini-v1.0/ao_pins.h
+++ b/src/easymini-v1.0/ao_pins.h
@@ -18,7 +18,7 @@
#define HAS_BEEP 1
#define HAS_BATTERY_REPORT 1
-#define AO_STACK_SIZE 384
+#define AO_STACK_SIZE 376
#define IS_FLASH_LOADER 0
diff --git a/src/kernel/ao.h b/src/kernel/ao.h
index ad5bbf8e..59a469ae 100644
--- a/src/kernel/ao.h
+++ b/src/kernel/ao.h
@@ -43,10 +43,6 @@
#define HAS_TASK 1
#endif
-#ifndef AO_PORT_TYPE
-#define AO_PORT_TYPE uint8_t
-#endif
-
typedef AO_PORT_TYPE ao_port_t;
#if HAS_TASK
@@ -76,6 +72,7 @@ typedef AO_PORT_TYPE ao_port_t;
#define AO_PANIC_BUFIO 15 /* Mis-using bufio API */
#define AO_PANIC_EXTI 16 /* Mis-using exti API */
#define AO_PANIC_FAST_TIMER 17 /* Mis-using fast timer API */
+#define AO_PANIC_ADC 18 /* Mis-using ADC interface */
#define AO_PANIC_SELF_TEST_CC1120 0x40 | 1 /* Self test failure */
#define AO_PANIC_SELF_TEST_HMC5883 0x40 | 2 /* Self test failure */
#define AO_PANIC_SELF_TEST_MPU6000 0x40 | 3 /* Self test failure */
@@ -518,15 +515,9 @@ struct ao_telemetry_raw_recv {
/* Set delay between telemetry reports (0 to disable) */
-#ifdef AO_SEND_ALL_BARO
-#define AO_TELEMETRY_INTERVAL_PAD AO_MS_TO_TICKS(100)
-#define AO_TELEMETRY_INTERVAL_FLIGHT AO_MS_TO_TICKS(100)
-#define AO_TELEMETRY_INTERVAL_RECOVER AO_MS_TO_TICKS(100)
-#else
#define AO_TELEMETRY_INTERVAL_PAD AO_MS_TO_TICKS(1000)
#define AO_TELEMETRY_INTERVAL_FLIGHT AO_MS_TO_TICKS(100)
#define AO_TELEMETRY_INTERVAL_RECOVER AO_MS_TO_TICKS(1000)
-#endif
void
ao_telemetry_reset_interval(void);
@@ -662,6 +653,7 @@ union ao_monitor {
extern __xdata union ao_monitor ao_monitor_ring[AO_MONITOR_RING];
#define ao_monitor_ring_next(n) (((n) + 1) & (AO_MONITOR_RING - 1))
+#define ao_monitor_ring_prev(n) (((n) - 1) & (AO_MONITOR_RING - 1))
extern __data uint8_t ao_monitoring;
extern __data uint8_t ao_monitor_head;
diff --git a/src/kernel/ao_config.c b/src/kernel/ao_config.c
index 6b8a1813..8dab7c42 100644
--- a/src/kernel/ao_config.c
+++ b/src/kernel/ao_config.c
@@ -557,10 +557,10 @@ ao_config_radio_rate_set(void) __reentrant
}
_ao_config_edit_start();
ao_config.radio_rate = ao_cmd_lex_i;
+ _ao_config_edit_finish();
#if HAS_TELEMETRY
ao_telemetry_reset_interval();
#endif
- _ao_config_edit_finish();
#if HAS_RADIO_RECV
ao_radio_recv_abort();
#endif
@@ -684,6 +684,9 @@ ao_config_radio_enable_set(void) __reentrant
_ao_config_edit_start();
ao_config.radio_enable = ao_cmd_lex_i;
_ao_config_edit_finish();
+#if HAS_TELEMETRY && HAS_RADIO_RATE
+ ao_telemetry_reset_interval();
+#endif
}
#endif /* HAS_RADIO */
@@ -735,6 +738,7 @@ ao_config_aprs_set(void)
_ao_config_edit_start();
ao_config.aprs_interval = ao_cmd_lex_i;
_ao_config_edit_finish();
+ ao_telemetry_reset_interval();
}
#endif /* HAS_APRS */
@@ -825,6 +829,9 @@ ao_config_tracker_set(void)
ao_config.tracker_motion = m;
ao_config.tracker_interval = i;
_ao_config_edit_finish();
+#if HAS_TELEMETRY
+ ao_telemetry_reset_interval();
+#endif
}
#endif /* HAS_TRACKER */
diff --git a/src/kernel/ao_gps_print.c b/src/kernel/ao_gps_print.c
index d26021da..6d9ee346 100644
--- a/src/kernel/ao_gps_print.c
+++ b/src/kernel/ao_gps_print.c
@@ -20,8 +20,8 @@
#endif
#include "ao_telem.h"
-#ifndef AO_TELEMETRY_LOCATION_ALTITUDE
-#define AO_TELEMETRY_LOCATION_ALTITUDE(l) ((l)->altitude)
+#ifndef AO_GPS_ORIG_ALTITUDE
+#define AO_GPS_ORIG_ALTITUDE(l) ((l)->altitude)
#endif
void
@@ -46,7 +46,7 @@ ao_gps_print(__xdata struct ao_gps_orig *gps_data) __reentrant
AO_TELEM_GPS_ALTITUDE " %d ",
(long) gps_data->latitude,
(long) gps_data->longitude,
- AO_TELEMETRY_LOCATION_ALTITUDE(gps_data));
+ AO_GPS_ORIG_ALTITUDE(gps_data));
if (gps_data->flags & AO_GPS_DATE_VALID)
printf(AO_TELEM_GPS_YEAR " %d "
diff --git a/src/kernel/ao_log.h b/src/kernel/ao_log.h
index c13a2580..f86fb359 100644
--- a/src/kernel/ao_log.h
+++ b/src/kernel/ao_log.h
@@ -43,11 +43,12 @@ extern __pdata enum ao_flight_state ao_log_state;
#define AO_LOG_FORMAT_TINY 2 /* two byte state/baro records */
#define AO_LOG_FORMAT_TELEMETRY 3 /* 32 byte ao_telemetry records */
#define AO_LOG_FORMAT_TELESCIENCE 4 /* 32 byte typed telescience records */
-#define AO_LOG_FORMAT_TELEMEGA 5 /* 32 byte typed telemega records */
+#define AO_LOG_FORMAT_TELEMEGA_OLD 5 /* 32 byte typed telemega records */
#define AO_LOG_FORMAT_EASYMINI 6 /* 16-byte MS5607 baro only, 3.0V supply */
#define AO_LOG_FORMAT_TELEMETRUM 7 /* 16-byte typed telemetrum records */
#define AO_LOG_FORMAT_TELEMINI 8 /* 16-byte MS5607 baro only, 3.3V supply */
#define AO_LOG_FORMAT_TELEGPS 9 /* 32 byte telegps records */
+#define AO_LOG_FORMAT_TELEMEGA 10 /* 32 byte typed telemega records with 32 bit gyro cal */
#define AO_LOG_FORMAT_NONE 127 /* No log at all */
extern __code uint8_t ao_log_format;
@@ -215,10 +216,22 @@ struct ao_log_mega {
int16_t ground_accel_along; /* 12 */
int16_t ground_accel_across; /* 14 */
int16_t ground_accel_through; /* 16 */
+ int16_t pad_18; /* 18 */
+ int32_t ground_roll; /* 20 */
+ int32_t ground_pitch; /* 24 */
+ int32_t ground_yaw; /* 28 */
+ } flight; /* 32 */
+ struct {
+ uint16_t flight; /* 4 */
+ int16_t ground_accel; /* 6 */
+ uint32_t ground_pres; /* 8 */
+ int16_t ground_accel_along; /* 12 */
+ int16_t ground_accel_across; /* 14 */
+ int16_t ground_accel_through; /* 16 */
int16_t ground_roll; /* 18 */
int16_t ground_pitch; /* 20 */
int16_t ground_yaw; /* 22 */
- } flight; /* 24 */
+ } flight_old; /* 24 */
/* AO_LOG_STATE */
struct {
uint16_t state;
diff --git a/src/kernel/ao_monitor.c b/src/kernel/ao_monitor.c
index 2d75c41c..cba0d80a 100644
--- a/src/kernel/ao_monitor.c
+++ b/src/kernel/ao_monitor.c
@@ -94,9 +94,18 @@ __xdata struct ao_task ao_monitor_blink_task;
void
ao_monitor_blink(void)
{
+#ifdef AO_MONITOR_BAD
+ uint8_t *recv;
+#endif
for (;;) {
ao_sleep(DATA_TO_XDATA(&ao_monitor_head));
- ao_led_for(AO_MONITOR_LED, AO_MS_TO_TICKS(100));
+#ifdef AO_MONITOR_BAD
+ recv = (uint8_t *) &ao_monitor_ring[ao_monitor_ring_prev(ao_monitor_head)];
+ if (ao_monitoring && !(recv[ao_monitoring + 1] & AO_RADIO_STATUS_CRC_OK))
+ ao_led_for(AO_MONITOR_BAD, AO_MS_TO_TICKS(100));
+ else
+#endif
+ ao_led_for(AO_MONITOR_LED, AO_MS_TO_TICKS(100));
}
}
#endif
diff --git a/src/kernel/ao_serial.h b/src/kernel/ao_serial.h
index baf213c0..dbc9f8e4 100644
--- a/src/kernel/ao_serial.h
+++ b/src/kernel/ao_serial.h
@@ -34,6 +34,9 @@ ao_serial0_getchar(void);
int
_ao_serial0_pollchar(void);
+uint8_t
+_ao_serial0_sleep(void);
+
void
ao_serial0_putchar(char c);
@@ -54,6 +57,9 @@ ao_serial1_getchar(void);
int
_ao_serial1_pollchar(void);
+uint8_t
+_ao_serial1_sleep(void);
+
void
ao_serial1_putchar(char c);
@@ -74,6 +80,9 @@ ao_serial2_getchar(void);
int
_ao_serial2_pollchar(void);
+uint8_t
+_ao_serial2_sleep(void);
+
void
ao_serial2_putchar(char c);
@@ -94,6 +103,9 @@ ao_serial3_getchar(void);
int
_ao_serial3_pollchar(void);
+uint8_t
+_ao_serial3_sleep(void);
+
void
ao_serial3_putchar(char c);
diff --git a/src/kernel/ao_stdio.c b/src/kernel/ao_stdio.c
index 99118137..1d65fcf5 100644
--- a/src/kernel/ao_stdio.c
+++ b/src/kernel/ao_stdio.c
@@ -142,10 +142,8 @@ ao_add_stdio(int (*_pollchar)(void),
void (*putchar)(char),
void (*flush)(void)) __reentrant
{
-#if AO_NUM_STDIOS > 1
if (ao_num_stdios == AO_NUM_STDIOS)
ao_panic(AO_PANIC_STDIO);
-#endif
ao_stdios[ao_num_stdios]._pollchar = _pollchar;
ao_stdios[ao_num_stdios].putchar = putchar;
ao_stdios[ao_num_stdios].flush = flush;
diff --git a/src/kernel/ao_telemetry.c b/src/kernel/ao_telemetry.c
index 27306a34..e2197f7a 100644
--- a/src/kernel/ao_telemetry.c
+++ b/src/kernel/ao_telemetry.c
@@ -19,19 +19,32 @@
#include "ao_log.h"
#include "ao_product.h"
-#ifndef HAS_RDF
-#define HAS_RDF 1
-#endif
-
static __pdata uint16_t ao_telemetry_interval;
#if HAS_RADIO_RATE
static __xdata uint16_t ao_telemetry_desired_interval;
#endif
+/* TeleMetrum v1.0 just doesn't have enough space to
+ * manage the more complicated telemetry scheduling, so
+ * it loses the ability to disable telem/rdf separately
+ */
+
+#if defined(TELEMETRUM_V_1_0)
+#define SIMPLIFY
+#endif
+
+#ifdef SIMPLIFY
+#define ao_telemetry_time time
+#define RDF_SPACE __pdata
+#else
+#define RDF_SPACE __xdata
+static __pdata uint16_t ao_telemetry_time;
+#endif
+
#if HAS_RDF
-static __pdata uint8_t ao_rdf = 0;
-static __pdata uint16_t ao_rdf_time;
+static RDF_SPACE uint8_t ao_rdf = 0;
+static RDF_SPACE uint16_t ao_rdf_time;
#endif
#if HAS_APRS
@@ -120,7 +133,9 @@ ao_send_mega_sensor(void)
telemetry.generic.tick = packet->tick;
telemetry.generic.type = AO_TELEMETRY_MEGA_SENSOR;
+#if HAS_MPU6000
telemetry.mega_sensor.orient = ao_sample_orient;
+#endif
telemetry.mega_sensor.accel = ao_data_accel(packet);
telemetry.mega_sensor.pres = ao_data_pres(packet);
telemetry.mega_sensor.temp = ao_data_temp(packet);
@@ -269,30 +284,6 @@ ao_send_mini(void)
#endif /* AO_SEND_MINI */
-#ifdef AO_SEND_ALL_BARO
-static uint8_t ao_baro_sample;
-
-static void
-ao_send_baro(void)
-{
- uint8_t sample = ao_sample_data;
- uint8_t samples = (sample - ao_baro_sample) & (AO_DATA_RING - 1);
-
- if (samples > 12) {
- ao_baro_sample = (ao_baro_sample + (samples - 12)) & (AO_DATA_RING - 1);
- samples = 12;
- }
- telemetry.generic.tick = ao_data_ring[sample].tick;
- telemetry.generic.type = AO_TELEMETRY_BARO;
- telemetry.baro.samples = samples;
- for (sample = 0; sample < samples; sample++) {
- telemetry.baro.baro[sample] = ao_data_ring[ao_baro_sample].adc.pres;
- ao_baro_sample = ao_data_ring_next(ao_baro_sample);
- }
- ao_radio_send(&telemetry, sizeof (telemetry));
-}
-#endif
-
static __pdata int8_t ao_telemetry_config_max;
static __pdata int8_t ao_telemetry_config_cur;
@@ -332,6 +323,7 @@ ao_send_configuration(void)
#if HAS_GPS
+static __pdata int8_t ao_telemetry_gps_max;
static __pdata int8_t ao_telemetry_loc_cur;
static __pdata int8_t ao_telemetry_sat_cur;
@@ -348,7 +340,7 @@ ao_send_location(void)
telemetry.location.tick = ao_gps_tick;
ao_mutex_put(&ao_gps_mutex);
ao_radio_send(&telemetry, sizeof (telemetry));
- ao_telemetry_loc_cur = ao_telemetry_config_max;
+ ao_telemetry_loc_cur = ao_telemetry_gps_max;
}
}
@@ -365,7 +357,7 @@ ao_send_satellite(void)
AO_MAX_GPS_TRACKING * sizeof (struct ao_telemetry_satellite_info));
ao_mutex_put(&ao_gps_mutex);
ao_radio_send(&telemetry, sizeof (telemetry));
- ao_telemetry_sat_cur = ao_telemetry_config_max;
+ ao_telemetry_sat_cur = ao_telemetry_gps_max;
}
}
#endif
@@ -411,6 +403,7 @@ ao_telemetry(void)
while (ao_telemetry_interval == 0)
ao_sleep(&telemetry);
time = ao_time();
+ ao_telemetry_time = time;
#if HAS_RDF
ao_rdf_time = time;
#endif
@@ -418,79 +411,85 @@ ao_telemetry(void)
ao_aprs_time = time;
#endif
while (ao_telemetry_interval) {
-#if HAS_APRS
+ time = ao_time() + AO_SEC_TO_TICKS(100);
+#ifndef SIMPLIFY
if (!(ao_config.radio_enable & AO_RADIO_DISABLE_TELEMETRY))
#endif
{
-#ifdef AO_SEND_ALL_BARO
- ao_send_baro();
+#ifndef SIMPLIFY
+ if ( (int16_t) (ao_time() - ao_telemetry_time) >= 0)
#endif
-
-#if HAS_FLIGHT
+ {
+ ao_telemetry_time = ao_time() + ao_telemetry_interval;
# ifdef AO_SEND_MEGA
- ao_send_mega_sensor();
- ao_send_mega_data();
+ ao_send_mega_sensor();
+ ao_send_mega_data();
# endif
# ifdef AO_SEND_METRUM
- ao_send_metrum_sensor();
- ao_send_metrum_data();
+ ao_send_metrum_sensor();
+ ao_send_metrum_data();
# endif
# ifdef AO_SEND_MINI
- ao_send_mini();
+ ao_send_mini();
# endif
# ifdef AO_TELEMETRY_SENSOR
- ao_send_sensor();
+ ao_send_sensor();
# endif
-#endif /* HAS_FLIGHT */
-
#if HAS_COMPANION
- if (ao_companion_running)
- ao_send_companion();
+ if (ao_companion_running)
+ ao_send_companion();
#endif
- ao_send_configuration();
#if HAS_GPS
- ao_send_location();
- ao_send_satellite();
+ ao_send_location();
+ ao_send_satellite();
+#endif
+ ao_send_configuration();
+ }
+#ifndef SIMPLIFY
+ time = ao_telemetry_time;
#endif
}
-#ifndef AO_SEND_ALL_BARO
#if HAS_RDF
- if (ao_rdf &&
-#if HAS_APRS
- !(ao_config.radio_enable & AO_RADIO_DISABLE_RDF) &&
-#endif /* HAS_APRS */
- (int16_t) (ao_time() - ao_rdf_time) >= 0)
+ if (ao_rdf
+#ifndef SIMPLIFY
+ && !(ao_config.radio_enable & AO_RADIO_DISABLE_RDF)
+#endif
+ )
{
+ if ((int16_t) (ao_time() - ao_rdf_time) >= 0) {
#if HAS_IGNITE_REPORT
- uint8_t c;
-#endif /* HAS_IGNITE_REPORT */
- ao_rdf_time = ao_time() + AO_RDF_INTERVAL_TICKS;
+ uint8_t c;
+#endif
+ ao_rdf_time = ao_time() + AO_RDF_INTERVAL_TICKS;
#if HAS_IGNITE_REPORT
- if (ao_flight_state == ao_flight_pad && (c = ao_report_igniter()))
- ao_radio_continuity(c);
- else
-#endif /* HAS_IGNITE_REPORT*/
- ao_radio_rdf();
+ if (ao_flight_state == ao_flight_pad && (c = ao_report_igniter()))
+ ao_radio_continuity(c);
+ else
+#endif
+ ao_radio_rdf();
+ }
+#ifndef SIMPLIFY
+ if ((int16_t) (time - ao_rdf_time) > 0)
+ time = ao_rdf_time;
+#endif
}
#endif /* HAS_RDF */
#if HAS_APRS
- if (ao_config.aprs_interval != 0 &&
- (int16_t) (ao_time() - ao_aprs_time) >= 0)
- {
- ao_aprs_time = ao_time() + AO_SEC_TO_TICKS(ao_config.aprs_interval);
- ao_aprs_send();
+ if (ao_config.aprs_interval != 0) {
+ if ((int16_t) (ao_time() - ao_aprs_time) >= 0) {
+ ao_aprs_time = ao_time() + AO_SEC_TO_TICKS(ao_config.aprs_interval);
+ ao_aprs_send();
+ }
+ if ((int16_t) (time - ao_aprs_time) > 0)
+ time = ao_aprs_time;
}
#endif /* HAS_APRS */
-#endif /* !AO_SEND_ALL_BARO */
- time += ao_telemetry_interval;
delay = time - ao_time();
if (delay > 0) {
ao_alarm(delay);
ao_sleep(&telemetry);
ao_clear_alarm();
}
- else
- time = ao_time();
}
}
}
@@ -547,21 +546,31 @@ ao_telemetry_set_interval(uint16_t interval)
ao_telemetry_companion_cur = cur;
#endif
- ao_telemetry_config_max = AO_SEC_TO_TICKS(5) / interval;
-#if HAS_COMPANION
- if (ao_telemetry_config_max > cur)
- cur++;
- ao_telemetry_config_cur = cur;
-#endif
-
#if HAS_GPS
- if (ao_telemetry_config_max > cur)
+ ao_telemetry_gps_max = AO_SEC_TO_TICKS(1) / interval;
+ if (ao_telemetry_gps_max > cur)
cur++;
ao_telemetry_loc_cur = cur;
- if (ao_telemetry_config_max > cur)
+ if (ao_telemetry_gps_max > cur)
cur++;
ao_telemetry_sat_cur = cur;
#endif
+
+ ao_telemetry_config_max = AO_SEC_TO_TICKS(5) / interval;
+ if (ao_telemetry_config_max > cur)
+ cur++;
+ ao_telemetry_config_cur = cur;
+
+#ifndef SIMPLIFY
+ ao_telemetry_time =
+#if HAS_RDF
+ ao_rdf_time =
+#endif
+#if HAS_APRS
+ ao_aprs_time =
+#endif
+ ao_time();
+#endif
ao_wakeup(&telemetry);
}
diff --git a/src/kernel/ao_telemetry.h b/src/kernel/ao_telemetry.h
index 83d432cf..711e0d36 100644
--- a/src/kernel/ao_telemetry.h
+++ b/src/kernel/ao_telemetry.h
@@ -116,12 +116,16 @@ struct ao_telemetry_location {
/* 32 */
};
-#if HAS_GPS
-
#ifndef HAS_WIDE_GPS
#define HAS_WIDE_GPS 1
#endif
+#ifdef HAS_TELEMETRY
+#ifndef HAS_RDF
+#define HAS_RDF 1
+#endif
+#endif
+
#if HAS_WIDE_GPS
typedef int32_t gps_alt_t;
#define AO_TELEMETRY_LOCATION_ALTITUDE(l) (((gps_alt_t) (l)->altitude_high << 16) | ((l)->altitude_low))
@@ -135,8 +139,6 @@ typedef int16_t gps_alt_t;
(l)->altitude_low = (a)))
#endif /* HAS_WIDE_GPS */
-#endif /* HAS_GPS */
-
#define AO_TELEMETRY_SATELLITE 0x06
struct ao_telemetry_satellite_info {
diff --git a/src/kernel/ao_tracker.c b/src/kernel/ao_tracker.c
index 9b007af8..962f145d 100644
--- a/src/kernel/ao_tracker.c
+++ b/src/kernel/ao_tracker.c
@@ -132,7 +132,7 @@ ao_tracker(void)
if (height < 0)
height = -height;
- if (ao_tracker_force_telem)
+ if (ao_tracker_force_telem > 1)
printf("head %d ring %d ground_distance %d height %d\n", gps_head, ring, ground_distance, height);
if (ground_distance > ao_config.tracker_motion ||
height > (ao_config.tracker_motion << 1))
@@ -141,7 +141,7 @@ ao_tracker(void)
break;
}
}
- if (ao_tracker_force_telem) {
+ if (ao_tracker_force_telem > 1) {
printf ("moving %d started %d\n", moving, log_started);
flush();
}
@@ -191,11 +191,9 @@ static struct ao_task ao_tracker_task;
static void
ao_tracker_set_telem(void)
{
- uint8_t telem;
ao_cmd_hex();
- telem = ao_cmd_lex_i;
if (ao_cmd_status == ao_cmd_success)
- ao_tracker_force_telem = telem;
+ ao_tracker_force_telem = ao_cmd_lex_i;
ao_cmd_status = ao_cmd_success;
printf ("flight: %d\n", ao_flight_number);
printf ("force_telem: %d\n", ao_tracker_force_telem);
@@ -211,7 +209,7 @@ ao_tracker_set_telem(void)
}
static const struct ao_cmds ao_tracker_cmds[] = {
- { ao_tracker_set_telem, "t <d>\0Set telem on USB" },
+ { ao_tracker_set_telem, "t <d>\0Set telem on USB (0 off, 1 on, 2 dbg)" },
{ 0, NULL },
};
diff --git a/src/lpc/ao_arch.h b/src/lpc/ao_arch.h
index 5fbb8dfa..42faf06f 100644
--- a/src/lpc/ao_arch.h
+++ b/src/lpc/ao_arch.h
@@ -130,12 +130,15 @@ ao_serial_init(void);
/* SPI definitions */
#define AO_SPI_SPEED_12MHz 4
+#define AO_SPI_SPEED_8MHz 6
#define AO_SPI_SPEED_6MHz 8
#define AO_SPI_SPEED_4MHz 12
#define AO_SPI_SPEED_2MHz 24
#define AO_SPI_SPEED_1MHz 48
#define AO_SPI_SPEED_500kHz 96
#define AO_SPI_SPEED_250kHz 192
+#define AO_SPI_SPEED_125kHz 384
+#define AO_SPI_SPEED_62500Hz 768
#define AO_SPI_SPEED_FAST AO_SPI_SPEED_12MHz
diff --git a/src/lpc/ao_arch_funcs.h b/src/lpc/ao_arch_funcs.h
index 21a7a8e5..fbe641d8 100644
--- a/src/lpc/ao_arch_funcs.h
+++ b/src/lpc/ao_arch_funcs.h
@@ -30,8 +30,19 @@
#define ao_gpio_get(port, bit, pin) (lpc_gpio.byte[lpc_all_bit(port,bit)])
+#define PORT0_JTAG_REGS ((1 << 11) | (1 << 12) | (1 << 14))
+
+static inline void lpc_set_gpio(int port, int bit) {
+ if (port == 0 && (1 << bit) & (PORT0_JTAG_REGS)) {
+ vuint32_t *_ioconf = &lpc_ioconf.pio0_0 + ((port)*24+(bit));
+
+ *_ioconf = (*_ioconf & ~LPC_IOCONF_FUNC_MASK) | LPC_IOCONF_FUNC_PIO0_11;
+ }
+}
+
#define ao_enable_output(port,bit,pin,v) do { \
ao_enable_port(port); \
+ lpc_set_gpio(port,bit); \
ao_gpio_set(port, bit, pin, v); \
lpc_gpio.dir[port] |= (1 << bit); \
} while (0)
@@ -52,6 +63,7 @@
#define ao_enable_input(port,bit,mode) do { \
ao_enable_port(port); \
+ lpc_set_gpio(port,bit); \
lpc_gpio.dir[port] &= ~(1 << bit); \
ao_gpio_set_mode(port,bit,mode); \
} while (0)
@@ -201,7 +213,7 @@ void
ao_spi_put(uint8_t spi_index);
void
-ao_spi_send(void *block, uint16_t len, uint8_t spi_index);
+ao_spi_send(const void *block, uint16_t len, uint8_t spi_index);
void
ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t spi_index);
@@ -210,9 +222,7 @@ void
ao_spi_recv(void *block, uint16_t len, uint8_t spi_index);
void
-ao_spi_duplex(void *out, void *in, uint16_t len, uint8_t spi_index);
-
-extern uint16_t ao_spi_speed[LPC_NUM_SPI];
+ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t spi_index);
void
ao_spi_init(void);
diff --git a/src/lpc/ao_led_lpc.c b/src/lpc/ao_led_lpc.c
index d983437c..a0b293b9 100644
--- a/src/lpc/ao_led_lpc.c
+++ b/src/lpc/ao_led_lpc.c
@@ -59,6 +59,15 @@ void
ao_led_init(AO_PORT_TYPE enable)
{
ao_led_enable = enable;
- lpc_scb.sysahbclkctrl |= (1 << LPC_SCB_SYSAHBCLKCTRL_GPIO);
+ ao_enable_port(LED_PORT);
+ if (LED_PORT == 0) {
+ if (enable & (1 << 11))
+ lpc_ioconf.pio0_11 = LPC_IOCONF_FUNC_PIO0_11 | (1 << LPC_IOCONF_ADMODE);
+ if (enable & (1 << 12))
+ lpc_ioconf.pio0_12 = LPC_IOCONF_FUNC_PIO0_12 | (1 << LPC_IOCONF_ADMODE);
+ if (enable & (1 << 14))
+ lpc_ioconf.pio0_14 = LPC_IOCONF_FUNC_PIO0_14 | (1 << LPC_IOCONF_ADMODE);
+ }
lpc_gpio.dir[LED_PORT] |= enable;
+ ao_led_off(enable);
}
diff --git a/src/lpc/ao_spi_lpc.c b/src/lpc/ao_spi_lpc.c
index e72b8286..f091c89c 100644
--- a/src/lpc/ao_spi_lpc.c
+++ b/src/lpc/ao_spi_lpc.c
@@ -21,34 +21,27 @@ static uint8_t ao_spi_mutex[LPC_NUM_SPI];
static struct lpc_ssp * const ao_lpc_ssp[LPC_NUM_SPI] = { &lpc_ssp0, &lpc_ssp1 };
-#define tx_busy(lpc_ssp) (lpc_ssp->sr & ((1 << LPC_SSP_SR_BSY) | (1 << LPC_SSP_SR_TNF))) != (1 << LPC_SSP_SR_TNF)
-#define rx_busy(lpc_ssp) (lpc_ssp->sr & ((1 << LPC_SSP_SR_BSY) | (1 << LPC_SSP_SR_RNE))) != (1 << LPC_SSP_SR_RNE)
-
#define spi_loop(len, put, get) do { \
while (len--) { \
- /* Wait for space in the fifo */ \
- while (tx_busy(lpc_ssp)) \
- ; \
- \
/* send a byte */ \
lpc_ssp->dr = put; \
- \
- /* Wait for byte to appear in the fifo */ \
- while (rx_busy(lpc_ssp)) \
+ /* wait for the received byte to appear */ \
+ while ((lpc_ssp->sr & (1 << LPC_SSP_SR_RNE)) == 0) \
; \
- \
- /* recv a byte */ \
+ /* receive a byte */ \
get lpc_ssp->dr; \
} \
+ /* Wait for the SSP to go idle (it already should be) */ \
+ while (lpc_ssp->sr & (1 << LPC_SSP_SR_BSY)); \
} while (0)
void
-ao_spi_send(void *block, uint16_t len, uint8_t id)
+ao_spi_send(const void *block, uint16_t len, uint8_t id)
{
- uint8_t *b = block;
struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
+ const uint8_t *o = block;
- spi_loop(len, *b++, (void));
+ spi_loop(len, *o++, (void));
}
void
@@ -62,18 +55,18 @@ ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t id)
void
ao_spi_recv(void *block, uint16_t len, uint8_t id)
{
- uint8_t *b = block;
struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
+ uint8_t *i = block;
- spi_loop(len, 0xff, *b++ =);
+ spi_loop(len, 0xff, *i++ =);
}
void
-ao_spi_duplex(void *out, void *in, uint16_t len, uint8_t id)
+ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t id)
{
- uint8_t *o = out;
- uint8_t *i = in;
struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
+ const uint8_t *o = out;
+ uint8_t *i = in;
spi_loop(len, *o++, *i++ =);
}
@@ -84,7 +77,7 @@ ao_spi_get(uint8_t id, uint32_t speed)
struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
ao_mutex_get(&ao_spi_mutex[id]);
-
+
/* Set the clock prescale */
lpc_ssp->cpsr = speed;
}
@@ -101,6 +94,11 @@ ao_spi_channel_init(uint8_t id)
struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
uint8_t d;
+ /* Clear interrupt registers */
+ lpc_ssp->imsc = 0;
+ lpc_ssp->ris = 0;
+ lpc_ssp->mis = 0;
+
lpc_ssp->cr0 = ((LPC_SSP_CR0_DSS_8 << LPC_SSP_CR0_DSS) |
(LPC_SSP_CR0_FRF_SPI << LPC_SSP_CR0_FRF) |
(0 << LPC_SSP_CR0_CPOL) |
@@ -151,7 +149,7 @@ ao_spi_init(void)
lpc_scb.presetctrl &= ~(1 << LPC_SCB_PRESETCTRL_SSP0_RST_N);
lpc_scb.presetctrl |= (1 << LPC_SCB_PRESETCTRL_SSP0_RST_N);
ao_spi_channel_init(0);
-#endif
+#endif
#if HAS_SPI_1
@@ -190,7 +188,7 @@ ao_spi_init(void)
#ifndef HAS_MOSI1
#error "No pin specified for MOSI1"
#endif
-
+
/* Enable the device */
lpc_scb.sysahbclkctrl |= (1 << LPC_SCB_SYSAHBCLKCTRL_SSP1);
diff --git a/src/lpc/ao_usb_lpc.c b/src/lpc/ao_usb_lpc.c
index 12f5d8e6..0dfaece4 100644
--- a/src/lpc/ao_usb_lpc.c
+++ b/src/lpc/ao_usb_lpc.c
@@ -80,14 +80,12 @@ static uint8_t *ao_usb_ep0_setup_buffer;
static uint8_t *ao_usb_ep0_rx_buffer;
/* Pointer to bulk data tx/rx buffers in USB memory */
-static uint8_t *ao_usb_in_tx_buffer;
-static uint8_t *ao_usb_out_rx_buffer;
-
-/* Our data buffers */
-static uint8_t ao_usb_tx_buffer[AO_USB_IN_SIZE];
+static uint8_t *ao_usb_in_tx_buffer[2];
+static uint8_t ao_usb_in_tx_cur;
static uint8_t ao_usb_tx_count;
-static uint8_t ao_usb_rx_buffer[AO_USB_OUT_SIZE];
+static uint8_t *ao_usb_out_rx_buffer[2];
+static uint8_t ao_usb_out_rx_cur;
static uint8_t ao_usb_rx_count, ao_usb_rx_pos;
extern struct lpc_usb_endpoint lpc_usb_endpoint;
@@ -234,15 +232,15 @@ ao_usb_ep0_in(void)
}
static inline vuint32_t *
-ao_usb_epn_out(uint8_t n)
+ao_usb_epn_out(uint8_t n, uint8_t i)
{
- return &lpc_usb_endpoint.epn[n-1].out[0];
+ return &lpc_usb_endpoint.epn[n-1].out[i];
}
static inline vuint32_t *
-ao_usb_epn_in(uint8_t n)
+ao_usb_epn_in(uint8_t n, uint8_t i)
{
- return &lpc_usb_endpoint.epn[n-1].in[0];
+ return &lpc_usb_endpoint.epn[n-1].in[i];
}
#if UNUSED
@@ -256,26 +254,26 @@ ao_usb_set_epn_in(uint8_t n, uint8_t *addr, uint16_t nbytes)
static void
ao_usb_set_epn_out(uint8_t n, uint8_t *addr, uint16_t nbytes)
{
- ao_usb_set_ep(ao_usb_epn_out(n), addr, nbytes);
+ ao_usb_set_ep(ao_usb_epn_out(n, 0), addr, nbytes);
}
static inline uint16_t
ao_usb_epn_out_count(uint8_t n)
{
- return ao_usb_ep_count(ao_usb_epn_out(n));
+ return ao_usb_ep_count(ao_usb_epn_out(n, 0));
}
static inline uint16_t
ao_usb_epn_in_count(uint8_t n)
{
- return ao_usb_ep_count(ao_usb_epn_in(n));
+ return ao_usb_ep_count(ao_usb_epn_in(n, 0));
}
static uint8_t *
ao_usb_enable_ep(vuint32_t *ep, uint16_t nbytes, uint16_t set_nbytes)
{
uint8_t *addr = ao_usb_alloc_sram(nbytes);
-
+
ao_usb_set_ep(ep, addr, set_nbytes);
return addr;
}
@@ -294,28 +292,34 @@ ao_usb_disable_ep(vuint32_t *ep)
}
static void
-ao_usb_enable_epn(uint8_t n, uint16_t out_bytes, uint8_t **out_addr, uint16_t in_bytes, uint8_t **in_addr)
+ao_usb_enable_epn(uint8_t n,
+ uint16_t out_bytes, uint8_t *out_addrs[2],
+ uint16_t in_bytes, uint8_t *in_addrs[2])
{
uint8_t *addr;
- addr = ao_usb_enable_ep(ao_usb_epn_out(n), out_bytes, out_bytes);
- if (out_addr)
- *out_addr = addr;
- ao_usb_disable_ep(&lpc_usb_endpoint.epn[n-1].out[1]);
+ addr = ao_usb_enable_ep(ao_usb_epn_out(n, 0), out_bytes * 2, out_bytes);
+ if (out_addrs) {
+ out_addrs[0] = addr;
+ out_addrs[1] = addr + out_bytes;
+ }
+ ao_usb_disable_ep(ao_usb_epn_out(n, 1));
- addr = ao_usb_enable_ep(ao_usb_epn_in(n), in_bytes, 0);
- if (in_addr)
- *in_addr = addr;
- ao_usb_disable_ep(&lpc_usb_endpoint.epn[n-1].in[1]);
+ addr = ao_usb_enable_ep(ao_usb_epn_in(n, 0), in_bytes * 2, 0);
+ if (in_addrs) {
+ in_addrs[0] = addr;
+ in_addrs[1] = addr + in_bytes;
+ }
+ ao_usb_disable_ep(ao_usb_epn_in(n, 1));
}
static void
ao_usb_disable_epn(uint8_t n)
{
- ao_usb_disable_ep(ao_usb_epn_out(n));
- ao_usb_disable_ep(&lpc_usb_endpoint.epn[n-1].out[1]);
- ao_usb_disable_ep(ao_usb_epn_in(n));
- ao_usb_disable_ep(&lpc_usb_endpoint.epn[n-1].in[1]);
+ ao_usb_disable_ep(ao_usb_epn_out(n, 0));
+ ao_usb_disable_ep(ao_usb_epn_out(n, 1));
+ ao_usb_disable_ep(ao_usb_epn_in(n, 0));
+ ao_usb_disable_ep(ao_usb_epn_in(n, 1));
}
static void
@@ -362,12 +366,18 @@ ao_usb_set_configuration(void)
/* Set up the INT end point */
ao_usb_enable_epn(AO_USB_INT_EP, 0, NULL, 0, NULL);
-
+
/* Set up the OUT end point */
- ao_usb_enable_epn(AO_USB_OUT_EP, AO_USB_OUT_SIZE, &ao_usb_out_rx_buffer, 0, NULL);
+ ao_usb_enable_epn(AO_USB_OUT_EP, AO_USB_OUT_SIZE, ao_usb_out_rx_buffer, 0, NULL);
+
+ /* Set the current RX pointer to the *other* buffer so that when buffer 0 gets
+ * data, we'll switch to it and pull bytes from there
+ */
+ ao_usb_out_rx_cur = 1;
/* Set up the IN end point */
- ao_usb_enable_epn(AO_USB_IN_EP, 0, NULL, AO_USB_IN_SIZE, &ao_usb_in_tx_buffer);
+ ao_usb_enable_epn(AO_USB_IN_EP, 0, NULL, AO_USB_IN_SIZE, ao_usb_in_tx_buffer);
+ ao_usb_in_tx_cur = 0;
ao_usb_running = 1;
}
@@ -716,8 +726,8 @@ _ao_usb_in_send(void)
ao_usb_in_pending = 1;
if (ao_usb_tx_count != AO_USB_IN_SIZE)
ao_usb_in_flushed = 1;
- memcpy(ao_usb_in_tx_buffer, ao_usb_tx_buffer, ao_usb_tx_count);
- ao_usb_set_ep(ao_usb_epn_in(AO_USB_IN_EP), ao_usb_in_tx_buffer, ao_usb_tx_count);
+ ao_usb_set_ep(ao_usb_epn_in(AO_USB_IN_EP, 0), ao_usb_in_tx_buffer[ao_usb_in_tx_cur], ao_usb_tx_count);
+ ao_usb_in_tx_cur = 1 - ao_usb_in_tx_cur;
ao_usb_tx_count = 0;
_tx_dbg0("in_send end");
}
@@ -771,7 +781,7 @@ ao_usb_putchar(char c)
_ao_usb_in_wait();
ao_usb_in_flushed = 0;
- ao_usb_tx_buffer[ao_usb_tx_count++] = (uint8_t) c;
+ ao_usb_in_tx_buffer[ao_usb_in_tx_cur][ao_usb_tx_count++] = (uint8_t) c;
/* Send the packet when full */
if (ao_usb_tx_count == AO_USB_IN_SIZE) {
@@ -792,13 +802,12 @@ _ao_usb_out_recv(void)
_rx_dbg1("out_recv count", ao_usb_rx_count);
debug ("recv %d\n", ao_usb_rx_count);
- debug_data("Fill OUT len %d:", ao_usb_rx_count);
- memcpy(ao_usb_rx_buffer, ao_usb_out_rx_buffer, ao_usb_rx_count);
- debug_data("\n");
+ debug_data("Fill OUT len %d\n", ao_usb_rx_count);
ao_usb_rx_pos = 0;
+ ao_usb_out_rx_cur = 1 - ao_usb_out_rx_cur;
/* ACK the packet */
- ao_usb_set_epn_out(AO_USB_OUT_EP, ao_usb_out_rx_buffer, AO_USB_OUT_SIZE);
+ ao_usb_set_epn_out(AO_USB_OUT_EP, ao_usb_out_rx_buffer[1-ao_usb_out_rx_cur], AO_USB_OUT_SIZE);
}
int
@@ -823,7 +832,7 @@ _ao_usb_pollchar(void)
}
/* Pull a character out of the fifo */
- c = ao_usb_rx_buffer[ao_usb_rx_pos++];
+ c = ao_usb_out_rx_buffer[ao_usb_out_rx_cur][ao_usb_rx_pos++];
return c;
}
@@ -897,7 +906,7 @@ ao_usb_enable(void)
/* Enable USB PHY */
lpc_scb.pdruncfg &= ~(1 << LPC_SCB_PDRUNCFG_USBPAD_PD);
-
+
/* Turn on USB PLL */
lpc_scb.pdruncfg &= ~(1 << LPC_SCB_PDRUNCFG_USBPLL_PD);
@@ -1044,7 +1053,7 @@ static void _dbg(int line, char *msg, uint32_t value)
dbg[dbg_i].primask = primask;
#if TX_DBG
dbg[dbg_i].in_count = in_count;
- dbg[dbg_i].in_ep = *ao_usb_epn_in(AO_USB_IN_EP);
+ dbg[dbg_i].in_ep = *ao_usb_epn_in(AO_USB_IN_EP, 0);
dbg[dbg_i].in_pending = ao_usb_in_pending;
dbg[dbg_i].tx_count = ao_usb_tx_count;
dbg[dbg_i].in_flushed = ao_usb_in_flushed;
@@ -1053,7 +1062,7 @@ static void _dbg(int line, char *msg, uint32_t value)
dbg[dbg_i].rx_count = ao_usb_rx_count;
dbg[dbg_i].rx_pos = ao_usb_rx_pos;
dbg[dbg_i].out_avail = ao_usb_out_avail;
- dbg[dbg_i].out_ep = *ao_usb_epn_out(AO_USB_OUT_EP);
+ dbg[dbg_i].out_ep = *ao_usb_epn_out(AO_USB_OUT_EP, 0);
#endif
if (++dbg_i == NUM_USB_DBG)
dbg_i = 0;
diff --git a/src/microsplash/.gitignore b/src/microsplash/.gitignore
new file mode 100644
index 00000000..5f6fe3b2
--- /dev/null
+++ b/src/microsplash/.gitignore
@@ -0,0 +1,2 @@
+ao_product.h
+microsplash-*
diff --git a/src/microwater/Makefile b/src/microsplash/Makefile
index a49cda4b..10cb825b 100644
--- a/src/microwater/Makefile
+++ b/src/microsplash/Makefile
@@ -48,14 +48,14 @@ INC=\
altitude-pa.h
IDPRODUCT=0
-PRODUCT=MicroWater-v0.1
+PRODUCT=MicroSplash-v0.1
PRODUCT_DEF=-DMICROPEAK
CFLAGS = $(PRODUCT_DEF) -I. -I../attiny -I../kernel -I.. -I../drivers -I../product
CFLAGS += -g -mmcu=$(MCU) -Wall -Wstrict-prototypes -O2 -mcall-prologues -DATTINY
NICKLE=nickle
-PROG=microwater-v0.1
+PROG=microsplash-v1.0
SRC=$(ALTOS_SRC)
OBJ=$(SRC:.c=.o)
diff --git a/src/microwater/ao_pins.h b/src/microsplash/ao_pins.h
index 37885ec2..37885ec2 100644
--- a/src/microwater/ao_pins.h
+++ b/src/microsplash/ao_pins.h
diff --git a/src/stm/ao_arch_funcs.h b/src/stm/ao_arch_funcs.h
index 7ad3b4b8..42f1a2e5 100644
--- a/src/stm/ao_arch_funcs.h
+++ b/src/stm/ao_arch_funcs.h
@@ -74,7 +74,7 @@ void
ao_spi_put(uint8_t spi_index);
void
-ao_spi_send(void *block, uint16_t len, uint8_t spi_index);
+ao_spi_send(const void *block, uint16_t len, uint8_t spi_index);
void
ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t spi_index);
diff --git a/src/stm/ao_led.c b/src/stm/ao_led.c
index 0acab106..9b61cf62 100644
--- a/src/stm/ao_led.c
+++ b/src/stm/ao_led.c
@@ -86,7 +86,7 @@ ao_led_for(uint16_t colors, uint16_t ticks) __reentrant
stm_moder_set(port, bit, STM_MODER_OUTPUT); \
stm_otyper_set(port, bit, STM_OTYPER_PUSH_PULL); \
} while (0)
-
+
void
ao_led_init(uint16_t enable)
{
diff --git a/src/stm/ao_serial_stm.c b/src/stm/ao_serial_stm.c
index 2133c584..2568cf43 100644
--- a/src/stm/ao_serial_stm.c
+++ b/src/stm/ao_serial_stm.c
@@ -63,7 +63,7 @@ int
_ao_usart_pollchar(struct ao_stm_usart *usart)
{
int c;
-
+
if (ao_fifo_empty(usart->rx_fifo))
c = AO_READ_AGAIN;
else {
@@ -85,6 +85,12 @@ ao_usart_getchar(struct ao_stm_usart *usart)
return (char) c;
}
+static inline uint8_t
+_ao_usart_sleep(struct ao_stm_usart *usart)
+{
+ return ao_sleep(&usart->rx_fifo);
+}
+
void
ao_usart_putchar(struct ao_stm_usart *usart, char c)
{
@@ -179,6 +185,13 @@ ao_usart_init(struct ao_stm_usart *usart)
ao_usart_set_speed(usart, AO_SERIAL_SPEED_9600);
}
+void
+ao_usart_set_flow(struct ao_stm_usart *usart)
+{
+ usart->reg->cr3 |= ((1 << STM_USART_CR3_CTSE) |
+ (1 << STM_USART_CR3_RTSE));
+}
+
#if HAS_SERIAL_1
struct ao_stm_usart ao_stm_usart1;
@@ -203,6 +216,18 @@ _ao_serial1_pollchar(void)
return _ao_usart_pollchar(&ao_stm_usart1);
}
+uint8_t
+_ao_serial1_sleep(void)
+{
+ return _ao_usart_sleep(&ao_stm_usart1);
+}
+
+void
+ao_serial1_drain(void)
+{
+ ao_usart_drain(&ao_stm_usart1);
+}
+
void
ao_serial1_set_speed(uint8_t speed)
{
@@ -234,6 +259,18 @@ _ao_serial2_pollchar(void)
return _ao_usart_pollchar(&ao_stm_usart2);
}
+uint8_t
+_ao_serial2_sleep(void)
+{
+ return _ao_usart_sleep(&ao_stm_usart2);
+}
+
+void
+ao_serial2_drain(void)
+{
+ ao_usart_drain(&ao_stm_usart2);
+}
+
void
ao_serial2_set_speed(uint8_t speed)
{
@@ -265,6 +302,12 @@ _ao_serial3_pollchar(void)
return _ao_usart_pollchar(&ao_stm_usart3);
}
+uint8_t
+_ao_serial3_sleep(void)
+{
+ return _ao_usart_sleep(&ao_stm_usart3);
+}
+
void
ao_serial3_set_speed(uint8_t speed)
{
@@ -305,7 +348,7 @@ ao_serial_init(void)
stm_nvic_set_enable(STM_ISR_USART1_POS);
stm_nvic_set_priority(STM_ISR_USART1_POS, 4);
-#if USE_SERIAL_1_STDIN
+#if USE_SERIAL_1_STDIN && !DELAY_SERIAL_1_STDIN
ao_add_stdio(_ao_serial1_pollchar,
ao_serial1_putchar,
NULL);
@@ -324,25 +367,35 @@ ao_serial_init(void)
stm_afr_set(&stm_gpioa, 2, STM_AFR_AF7);
stm_afr_set(&stm_gpioa, 3, STM_AFR_AF7);
+#if USE_SERIAL_2_FLOW
+ stm_afr_set(&stm_gpioa, 0, STM_AFR_AF7);
+ stm_afr_set(&stm_gpioa, 1, STM_AFR_AF7);
+#endif
#else
#if SERIAL_2_PD5_PD6
stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIODEN);
stm_afr_set(&stm_gpiod, 5, STM_AFR_AF7);
stm_afr_set(&stm_gpiod, 6, STM_AFR_AF7);
+#if USE_SERIAL_2_FLOW
+#error "Don't know how to set flowcontrol for serial 2 on PD"
+#endif
#else
#error "No SERIAL_2 port configuration specified"
-#endif
+#endif
#endif
/* Enable USART */
stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_USART2EN);
ao_stm_usart2.reg = &stm_usart2;
ao_usart_init(&ao_stm_usart2);
+#if USE_SERIAL_2_FLOW
+ ao_usart_set_flow(&ao_stm_usart2);
+#endif
stm_nvic_set_enable(STM_ISR_USART2_POS);
stm_nvic_set_priority(STM_ISR_USART2_POS, 4);
-#if USE_SERIAL_2_STDIN
+#if USE_SERIAL_2_STDIN && !DELAY_SERIAL_2_STDIN
ao_add_stdio(_ao_serial2_pollchar,
ao_serial2_putchar,
NULL);
@@ -386,12 +439,10 @@ ao_serial_init(void)
stm_nvic_set_enable(STM_ISR_USART3_POS);
stm_nvic_set_priority(STM_ISR_USART3_POS, 4);
-#if USE_SERIAL_3_STDIN
+#if USE_SERIAL_3_STDIN && !DELAY_SERIAL_3_STDIN
ao_add_stdio(_ao_serial3_pollchar,
ao_serial3_putchar,
NULL);
#endif
#endif
}
-
-
diff --git a/src/stm/ao_spi_stm.c b/src/stm/ao_spi_stm.c
index 885af544..7eaa3924 100644
--- a/src/stm/ao_spi_stm.c
+++ b/src/stm/ao_spi_stm.c
@@ -42,7 +42,7 @@ static const struct ao_spi_stm_info ao_spi_stm_info[STM_NUM_SPI] = {
static uint8_t spi_dev_null;
void
-ao_spi_send(void *block, uint16_t len, uint8_t spi_index)
+ao_spi_send(const void *block, uint16_t len, uint8_t spi_index)
{
struct stm_spi *stm_spi = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].stm_spi;
uint8_t mosi_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].mosi_dma_index;
@@ -51,7 +51,7 @@ ao_spi_send(void *block, uint16_t len, uint8_t spi_index)
/* Set up the transmit DMA to deliver data */
ao_dma_set_transfer(mosi_dma_index,
&stm_spi->dr,
- block,
+ (void *) block,
len,
(0 << STM_DMA_CCR_MEM2MEM) |
(STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
diff --git a/src/stmf0/Makefile-flash.defs b/src/stmf0/Makefile-flash.defs
new file mode 100644
index 00000000..706b93ee
--- /dev/null
+++ b/src/stmf0/Makefile-flash.defs
@@ -0,0 +1,61 @@
+include $(TOPDIR)/stmf0/Makefile-stmf0.defs
+
+INC = \
+ ao.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_flash_pins.h \
+ ao_flash_stm_pins.h \
+ ao_flash_task.h \
+ ao_pins.h \
+ ao_product.h \
+ Makefile
+
+#
+# Common AltOS sources
+#
+SRC = \
+ ao_interrupt.c \
+ ao_romconfig.c \
+ ao_boot_chain.c \
+ ao_boot_pin.c \
+ ao_product.c \
+ ao_notask.c \
+ ao_timer.c \
+ ao_usb_stm.c \
+ ao_flash_stm.c \
+ ao_flash_task.c \
+ ao_flash_loader_stm.c
+
+OBJ=$(SRC:.c=.o)
+
+PRODUCT=AltosFlash
+PRODUCT_DEF=-DALTOS_FLASH
+IDPRODUCT=0x000a
+
+CFLAGS = $(PRODUCT_DEF) $(STMF0_CFLAGS) -g -Os
+
+LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stmf0 -Wl,-Taltos-loader.ld
+
+PROGNAME=altos-flash
+PROG=$(HARDWARE)-$(PROGNAME)-$(VERSION).elf
+
+$(PROG): Makefile $(OBJ) altos-loader.ld
+ $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+ao_product.h: ao-make-product.5c $(TOPDIR)/Version
+ $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+$(OBJ): $(INC)
+
+all: $(PROG)
+
+distclean: clean
+
+clean:
+ rm -f *.o $(HARDWARE)-$(PROGNAME)-*.elf
+ rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/stmf0/Makefile-stmf0.defs b/src/stmf0/Makefile-stmf0.defs
new file mode 100644
index 00000000..4862f46e
--- /dev/null
+++ b/src/stmf0/Makefile-stmf0.defs
@@ -0,0 +1,47 @@
+ifndef TOPDIR
+TOPDIR=..
+endif
+
+include $(TOPDIR)/Makedefs
+
+vpath % $(TOPDIR)/stmf0:$(TOPDIR)/product:$(TOPDIR)/drivers:$(TOPDIR)/kernel:$(TOPDIR)/util:$(TOPDIR)/kalman:$(TOPDIR/aes):$(TOPDIR):$(TOPDIR)/math
+vpath make-altitude $(TOPDIR)/util
+vpath make-kalman $(TOPDIR)/util
+vpath kalman.5c $(TOPDIR)/kalman
+vpath kalman_filter.5c $(TOPDIR)/kalman
+vpath load_csv.5c $(TOPDIR)/kalman
+vpath matrix.5c $(TOPDIR)/kalman
+vpath ao-make-product.5c $(TOPDIR)/util
+
+.SUFFIXES: .elf .ihx
+
+.elf.ihx:
+ $(ELFTOHEX) --output=$@ $*.elf
+
+ifndef VERSION
+include $(TOPDIR)/Version
+endif
+
+ELFTOHEX=$(TOPDIR)/../ao-tools/ao-elftohex/ao-elftohex
+CC=$(ARM_CC)
+
+WARN_FLAGS=-Wall -Wextra -Werror
+
+AO_CFLAGS=-I. -I$(TOPDIR)/stmf0 -I$(TOPDIR)/kernel -I$(TOPDIR)/drivers -I$(TOPDIR)/product -I$(TOPDIR) -I$(TOPDIR)/math $(PDCLIB_INCLUDES)
+STMF0_CFLAGS=-std=gnu99 -mlittle-endian -mcpu=cortex-m0 -mthumb\
+ -ffreestanding -nostdlib $(AO_CFLAGS) $(WARN_FLAGS)
+
+NICKLE=nickle
+
+LIBS=$(PDCLIB_LIBS_M0) -lgcc
+
+V=0
+# The user has explicitly enabled quiet compilation.
+ifeq ($(V),0)
+quiet = @printf " $1 $2 $@\n"; $($1)
+endif
+# Otherwise, print the full command line.
+quiet ?= $($1)
+
+.c.o:
+ $(call quiet,CC) -c $(CFLAGS) -o $@ $<
diff --git a/src/stmf0/Makefile.defs b/src/stmf0/Makefile.defs
new file mode 100644
index 00000000..a1d93eb5
--- /dev/null
+++ b/src/stmf0/Makefile.defs
@@ -0,0 +1,9 @@
+ifndef TOPDIR
+TOPDIR=..
+endif
+
+include $(TOPDIR)/stmf0/Makefile-stmf0.defs
+
+LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stmf0 -Wl,-Taltos.ld
+
+.DEFAULT_GOAL=all
diff --git a/src/stmf0/altos-loader.ld b/src/stmf0/altos-loader.ld
new file mode 100644
index 00000000..86cf1838
--- /dev/null
+++ b/src/stmf0/altos-loader.ld
@@ -0,0 +1,95 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * 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.
+ */
+
+MEMORY {
+ rom : ORIGIN = 0x08000000, LENGTH = 4K
+ ram : ORIGIN = 0x20000000, LENGTH = 6K
+}
+
+INCLUDE registers.ld
+
+EXTERN (stm_interrupt_vector)
+
+SECTIONS {
+ /*
+ * Rom contents
+ */
+
+ .interrupt : {
+ __text_start__ = .;
+ *(.interrupt) /* Interrupt vectors */
+ } > rom
+
+ .text ORIGIN(rom) + 0x100 : {
+ ao_romconfig.o(.romconfig*)
+ ao_product.o(.romconfig*)
+
+ *(.text*) /* Executable code */
+ *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+ *(.rodata*) /* Constants */
+ } > rom
+ __text_end__ = .;
+
+ /* Boot data which must live at the start of ram so that
+ * the application and bootloader share the same addresses.
+ * This must be all uninitialized data
+ */
+ .boot ORIGIN(ram) + SIZEOF(.interrupt) (NOLOAD) : {
+ __boot_start__ = .;
+ *(.boot)
+ __boot_end__ = .;
+ } >ram
+
+ /* Functions placed in RAM (required for flashing)
+ *
+ * Align to 8 bytes as that's what the ARM likes text
+ * segment alignments to be, and if we don't, then
+ * we end up with a mismatch between the location in
+ * ROM and the desired location in RAM. I don't
+ * entirely understand this, but at least this appears
+ * to work...
+ */
+
+ .textram BLOCK(8): {
+ __data_start__ = .;
+ __text_ram_start__ = .;
+ *(.ramtext)
+ __text_ram_end = .;
+ } >ram AT>rom
+
+ /* Data -- relocated to RAM, but written to ROM
+ */
+ .data : {
+ *(.data) /* initialized data */
+ __data_end__ = .;
+ } >ram AT>rom
+
+
+ .bss : {
+ __bss_start__ = .;
+ *(.bss)
+ *(COMMON)
+ __bss_end__ = .;
+ } >ram
+
+ PROVIDE(__stack__ = ORIGIN(ram) + LENGTH(ram));
+ PROVIDE(end = .);
+}
+
+ENTRY(start);
+
+
diff --git a/src/stmf0/altos.ld b/src/stmf0/altos.ld
new file mode 100644
index 00000000..9dbc83d0
--- /dev/null
+++ b/src/stmf0/altos.ld
@@ -0,0 +1,98 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * 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.
+ */
+
+MEMORY {
+ rom (rx) : ORIGIN = 0x08001000, LENGTH = 28K
+ ram (!w) : ORIGIN = 0x20000000, LENGTH = 6k - 128
+ stack (!w) : ORIGIN = 0x20000000 + 6k - 128, LENGTH = 128
+}
+
+INCLUDE registers.ld
+
+EXTERN (stm_interrupt_vector)
+
+SECTIONS {
+ /*
+ * Rom contents
+ */
+
+ .interrupt ORIGIN(ram) : AT (ORIGIN(rom)) {
+ __interrupt_start__ = .;
+ __interrupt_rom__ = ORIGIN(rom);
+ *(.interrupt) /* Interrupt vectors */
+ __interrupt_end__ = .;
+ } > ram
+
+ .text ORIGIN(rom) + 0x100 : {
+ __text_start__ = .;
+
+ /* Ick. What I want is to specify the
+ * addresses of some global constants so
+ * that I can find them across versions
+ * of the application. I can't figure out
+ * how to make gnu ld do that, so instead
+ * we just load the two files that include
+ * these defines in the right order here and
+ * expect things to 'just work'. Don't change
+ * the contents of those files, ok?
+ */
+ ao_romconfig.o(.romconfig*)
+ ao_product.o(.romconfig*)
+
+ *(.text*) /* Executable code */
+ *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+ *(.rodata*) /* Constants */
+
+ } > rom
+ __text_end__ = .;
+
+ /* Boot data which must live at the start of ram so that
+ * the application and bootloader share the same addresses.
+ * This must be all uninitialized data
+ */
+ .boot (NOLOAD) : {
+ __boot_start__ = .;
+ *(.boot)
+ . = ALIGN(4);
+ __boot_end__ = .;
+ } >ram
+
+ /* Data -- relocated to RAM, but written to ROM
+ */
+ .data : {
+ __data_start__ = .;
+ *(.data) /* initialized data */
+ . = ALIGN(4);
+ __data_end__ = .;
+ } >ram AT>rom
+
+ .bss : {
+ __bss_start__ = .;
+ *(.bss)
+ *(COMMON)
+ . = ALIGN(4);
+ __bss_end__ = .;
+ } >ram
+
+ PROVIDE(end = .);
+
+ PROVIDE(__stack__ = ORIGIN(stack) + LENGTH(stack));
+}
+
+ENTRY(start);
+
+
diff --git a/src/stmf0/ao_adc_fast.c b/src/stmf0/ao_adc_fast.c
new file mode 100644
index 00000000..be9b5986
--- /dev/null
+++ b/src/stmf0/ao_adc_fast.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * 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_adc_fast.h>
+
+uint16_t ao_adc_ring[AO_ADC_RING_SIZE];
+
+uint16_t ao_adc_ring_head, ao_adc_ring_tail;
+uint8_t ao_adc_running;
+
+/*
+ * Callback from DMA ISR
+ *
+ * Mark time in ring, shut down DMA engine
+ */
+static void ao_adc_dma_done(int index)
+{
+ (void) index;
+ ao_adc_ring_head += AO_ADC_RING_CHUNK;
+ if (ao_adc_ring_head == AO_ADC_RING_SIZE)
+ ao_adc_ring_head = 0;
+ ao_adc_running = 0;
+ ao_wakeup(&ao_adc_ring_head);
+ ao_dma_done_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1));
+}
+
+void
+_ao_adc_start(void)
+{
+ uint16_t *buf;
+
+ if (ao_adc_running)
+ return;
+ if (_ao_adc_space() < AO_ADC_RING_CHUNK)
+ return;
+ ao_adc_running = 1;
+ buf = ao_adc_ring + ao_adc_ring_head;
+ stm_adc.isr = 0;
+ ao_dma_set_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1),
+ &stm_adc.dr,
+ buf,
+ AO_ADC_RING_CHUNK,
+ (0 << STM_DMA_CCR_MEM2MEM) |
+ (STM_DMA_CCR_PL_HIGH << STM_DMA_CCR_PL) |
+ (STM_DMA_CCR_MSIZE_16 << STM_DMA_CCR_MSIZE) |
+ (STM_DMA_CCR_PSIZE_16 << STM_DMA_CCR_PSIZE) |
+ (1 << STM_DMA_CCR_MINC) |
+ (0 << STM_DMA_CCR_PINC) |
+ (0 << STM_DMA_CCR_CIRC) |
+ (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
+ ao_dma_set_isr(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1), ao_adc_dma_done);
+ ao_dma_start(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1));
+
+ stm_adc.cr |= (1 << STM_ADC_CR_ADSTART);
+}
+
+void
+ao_adc_init(void)
+{
+ uint32_t chselr;
+ int i;
+
+ /* Reset ADC */
+ stm_rcc.apb2rstr |= (1 << STM_RCC_APB2RSTR_ADCRST);
+ stm_rcc.apb2rstr &= ~(1 << STM_RCC_APB2RSTR_ADCRST);
+
+ /* Turn on ADC pins */
+ stm_rcc.ahbenr |= AO_ADC_RCC_AHBENR;
+
+#ifdef AO_ADC_PIN0_PORT
+ stm_moder_set(AO_ADC_PIN0_PORT, AO_ADC_PIN0_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN1_PORT
+ stm_moder_set(AO_ADC_PIN1_PORT, AO_ADC_PIN1_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN2_PORT
+ stm_moder_set(AO_ADC_PIN2_PORT, AO_ADC_PIN2_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN3_PORT
+ stm_moder_set(AO_ADC_PIN3_PORT, AO_ADC_PIN3_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN4_PORT
+ stm_moder_set(AO_ADC_PIN4_PORT, AO_ADC_PIN4_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN5_PORT
+ stm_moder_set(AO_ADC_PIN5_PORT, AO_ADC_PIN5_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN6_PORT
+ stm_moder_set(AO_ADC_PIN6_PORT, AO_ADC_PIN6_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN7_PORT
+ stm_moder_set(AO_ADC_PIN7_PORT, AO_ADC_PIN7_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN24_PORT
+ #error "Too many ADC ports"
+#endif
+
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_ADCEN);
+
+ chselr = 0;
+#if AO_NUM_ADC > 0
+ chselr |= (1 << AO_ADC_PIN0_CH);
+#endif
+#if AO_NUM_ADC > 1
+ chselr |= (1 << AO_ADC_PIN1_CH);
+#endif
+#if AO_NUM_ADC > 2
+ chselr |= (1 << AO_ADC_PIN2_CH);
+#endif
+#if AO_NUM_ADC > 3
+ chselr |= (1 << AO_ADC_PIN3_CH);
+#endif
+#if AO_NUM_ADC > 4
+ chselr |= (1 << AO_ADC_PIN4_CH);
+#endif
+#if AO_NUM_ADC > 5
+ chselr |= (1 << AO_ADC_PIN5_CH);
+#endif
+#if AO_NUM_ADC > 6
+ chselr |= (1 << AO_ADC_PIN6_CH);
+#endif
+#if AO_NUM_ADC > 7
+ chselr |= (1 << AO_ADC_PIN7_CH);
+#endif
+#if AO_NUM_ADC > 8
+#error Need more ADC defines
+#endif
+ stm_adc.chselr = chselr;
+
+ /* Set the clock */
+ stm_adc.cfgr2 = STM_ADC_CFGR2_CKMODE_PCLK_2 << STM_ADC_CFGR2_CKMODE;
+
+ /* Shortest sample time */
+ stm_adc.smpr = STM_ADC_SMPR_SMP_1_5 << STM_ADC_SMPR_SMP;
+
+ /* Calibrate */
+ stm_adc.cr |= (1 << STM_ADC_CR_ADCAL);
+ for (i = 0; i < 0xf000; i++) {
+ if ((stm_adc.cr & (1 << STM_ADC_CR_ADCAL)) == 0)
+ break;
+ }
+
+ /* Enable */
+ stm_adc.cr |= (1 << STM_ADC_CR_ADEN);
+ while ((stm_adc.isr & (1 << STM_ADC_ISR_ADRDY)) == 0)
+ ;
+
+ stm_adc.cfgr1 = ((0 << STM_ADC_CFGR1_AWDCH) |
+ (0 << STM_ADC_CFGR1_AWDEN) |
+ (0 << STM_ADC_CFGR1_AWDSGL) |
+ (0 << STM_ADC_CFGR1_DISCEN) |
+ (0 << STM_ADC_CFGR1_AUTOOFF) |
+ (1 << STM_ADC_CFGR1_WAIT) |
+ (1 << STM_ADC_CFGR1_CONT) |
+ (0 << STM_ADC_CFGR1_OVRMOD) |
+ (STM_ADC_CFGR1_EXTEN_DISABLE << STM_ADC_CFGR1_EXTEN) |
+ (0 << STM_ADC_CFGR1_ALIGN) |
+ (STM_ADC_CFGR1_RES_12 << STM_ADC_CFGR1_RES) |
+ (STM_ADC_CFGR1_SCANDIR_UP << STM_ADC_CFGR1_SCANDIR) |
+ (STM_ADC_CFGR1_DMACFG_ONESHOT << STM_ADC_CFGR1_DMACFG) |
+ (1 << STM_ADC_CFGR1_DMAEN));
+ stm_adc.ccr = 0;
+
+ /* Clear any stale status bits */
+ stm_adc.isr = 0;
+
+ /* Turn on syscfg */
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SYSCFGCOMPEN);
+
+ /* Set ADC to use DMA channel 1 (option 1) */
+ stm_syscfg.cfgr1 &= ~(1 << STM_SYSCFG_CFGR1_ADC_DMA_RMP);
+
+ ao_dma_alloc(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1));
+ ao_dma_set_isr(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1), ao_adc_dma_done);
+}
diff --git a/src/stmf0/ao_adc_fast.h b/src/stmf0/ao_adc_fast.h
new file mode 100644
index 00000000..eec45505
--- /dev/null
+++ b/src/stmf0/ao_adc_fast.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * 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_ADC_FAST_H_
+#define _AO_ADC_FAST_H_
+
+void
+ao_adc_read(uint16_t *dest, int len);
+
+void
+ao_adc_init(void);
+
+/* Total ring size in samples */
+#define AO_ADC_RING_SIZE 256
+/* Number of samples fetched per ao_adc_start call */
+#define AO_ADC_RING_CHUNK (AO_ADC_RING_SIZE >> 1)
+
+extern uint16_t ao_adc_ring[AO_ADC_RING_SIZE];
+
+#define ao_adc_ring_step(pos,inc) (((pos) + (inc)) & (AO_ADC_RING_SIZE - 1))
+
+extern uint16_t ao_adc_ring_head, ao_adc_ring_tail;
+extern uint8_t ao_adc_running;
+
+void
+_ao_adc_start(void);
+
+static inline uint16_t
+_ao_adc_remain(void)
+{
+ if (ao_adc_ring_tail > ao_adc_ring_head)
+ return AO_ADC_RING_SIZE - ao_adc_ring_tail;
+ return ao_adc_ring_head - ao_adc_ring_tail;
+}
+
+static inline uint16_t
+_ao_adc_space(void)
+{
+ if (ao_adc_ring_head == ao_adc_ring_tail)
+ return AO_ADC_RING_SIZE;
+ if (ao_adc_ring_head > ao_adc_ring_tail)
+ return AO_ADC_RING_SIZE - ao_adc_ring_head;
+ return ao_adc_ring_tail - ao_adc_ring_head;
+}
+
+static inline uint16_t *
+ao_adc_get(uint16_t n)
+{
+ if (ao_adc_ring_tail + n > AO_ADC_RING_SIZE)
+ ao_panic(AO_PANIC_ADC);
+ ao_arch_block_interrupts();
+ while (_ao_adc_remain() < n) {
+ if (!ao_adc_running)
+ _ao_adc_start();
+ ao_sleep(&ao_adc_ring_head);
+ }
+ ao_arch_release_interrupts();
+ return &ao_adc_ring[ao_adc_ring_tail];
+}
+
+static inline void
+ao_adc_ack(uint16_t n)
+{
+ if (ao_adc_ring_tail + n > AO_ADC_RING_SIZE)
+ ao_panic(AO_PANIC_ADC);
+ ao_arch_block_interrupts();
+ ao_adc_ring_tail += n;
+ if (ao_adc_ring_tail == AO_ADC_RING_SIZE)
+ ao_adc_ring_tail = 0;
+ if (!ao_adc_running && _ao_adc_space() >= AO_ADC_RING_CHUNK)
+ _ao_adc_start();
+ ao_arch_release_interrupts();
+}
+
+#endif /* _AO_ADC_FAST_H_ */
diff --git a/src/stmf0/ao_arch.h b/src/stmf0/ao_arch.h
new file mode 100644
index 00000000..6ee71ef9
--- /dev/null
+++ b/src/stmf0/ao_arch.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * 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_ARCH_H_
+#define _AO_ARCH_H_
+
+#include <stdio.h>
+#include <stm32f0.h>
+
+/*
+ * STM32F0 definitions and code fragments for AltOS
+ */
+
+#define AO_STACK_SIZE 512
+
+#define AO_LED_TYPE uint16_t
+
+#ifndef AO_TICK_TYPE
+#define AO_TICK_TYPE uint16_t
+#define AO_TICK_SIGNED int16_t
+#endif
+
+#define AO_PORT_TYPE uint16_t
+
+/* Various definitions to make GCC look more like SDCC */
+
+#define ao_arch_naked_declare __attribute__((naked))
+#define ao_arch_naked_define
+#define __pdata
+#define __data
+#define __xdata
+#define __code const
+#define __reentrant
+#define __interrupt(n)
+#define __at(n)
+
+#define ao_arch_reboot() \
+ (stm_scb.aircr = ((STM_SCB_AIRCR_VECTKEY_KEY << STM_SCB_AIRCR_VECTKEY) | \
+ (1 << STM_SCB_AIRCR_SYSRESETREQ)))
+
+#define ao_arch_nop() asm("nop")
+
+#define ao_arch_interrupt(n) /* nothing */
+
+#undef putchar
+#undef getchar
+#define putchar(c) ao_putchar(c)
+#define getchar ao_getchar
+
+extern void putchar(char c);
+extern char getchar(void);
+extern void ao_avr_stdio_init(void);
+
+
+/*
+ * ao_romconfig.c
+ */
+
+#define AO_ROMCONFIG_VERSION 2
+
+#define AO_ROMCONFIG_SYMBOL(a) __attribute__((section(".romconfig"))) const
+
+extern const uint16_t ao_romconfig_version;
+extern const uint16_t ao_romconfig_check;
+extern const uint16_t ao_serial_number;
+extern const uint32_t ao_radio_cal;
+
+#define ao_arch_task_members\
+ uint32_t *sp; /* saved stack pointer */
+
+#define ao_arch_block_interrupts() asm("cpsid i")
+#define ao_arch_release_interrupts() asm("cpsie i")
+
+/*
+ * ao_timer.c
+ *
+ * For the stm32f042, we want to use the USB-based HSI48 clock
+ */
+
+#if AO_HSI48
+
+#define AO_SYSCLK 48000000
+#define AO_HCLK (AO_SYSCLK / AO_AHB_PRESCALER)
+
+#endif
+
+#if AO_HSE || AO_HSI
+
+#if AO_HSE
+#define AO_PLLSRC AO_HSE
+#else
+#define AO_PLLSRC STM_HSI_FREQ
+#endif
+
+#define AO_PLLVCO (AO_PLLSRC * AO_PLLMUL)
+#define AO_SYSCLK (AO_PLLVCO / AO_PLLDIV)
+
+#endif
+
+#define AO_HCLK (AO_SYSCLK / AO_AHB_PRESCALER)
+#define AO_PCLK1 (AO_HCLK / AO_APB1_PRESCALER)
+#define AO_PCLK2 (AO_HCLK / AO_APB2_PRESCALER)
+#define AO_SYSTICK (AO_HCLK)
+
+#if AO_APB1_PRESCALER == 1
+#define AO_TIM23467_CLK AO_PCLK1
+#else
+#define AO_TIM23467_CLK (2 * AO_PCLK1)
+#endif
+
+#if AO_APB2_PRESCALER == 1
+#define AO_TIM91011_CLK AO_PCLK2
+#else
+#define AO_TIM91011_CLK (2 * AO_PCLK2)
+#endif
+
+#define AO_STM_NVIC_HIGH_PRIORITY 4
+#define AO_STM_NVIC_CLOCK_PRIORITY 6
+#define AO_STM_NVIC_MED_PRIORITY 8
+#define AO_STM_NVIC_LOW_PRIORITY 10
+
+void ao_lcd_stm_init(void);
+
+void ao_lcd_font_init(void);
+
+void ao_lcd_font_string(char *s);
+
+extern const uint32_t ao_radio_cal;
+
+void
+ao_adc_init();
+
+/* ADC maximum reported value */
+#define AO_ADC_MAX 4095
+
+#define AO_BOOT_APPLICATION_BASE ((uint32_t *) 0x08001000)
+#define AO_BOOT_APPLICATION_BOUND ((uint32_t *) (0x08000000 + stm_flash_size()))
+#define AO_BOOT_LOADER_BASE ((uint32_t *) 0x08000000)
+#define HAS_BOOT_LOADER 1
+
+#endif /* _AO_ARCH_H_ */
+
+
diff --git a/src/stmf0/ao_arch_funcs.h b/src/stmf0/ao_arch_funcs.h
new file mode 100644
index 00000000..3db96be2
--- /dev/null
+++ b/src/stmf0/ao_arch_funcs.h
@@ -0,0 +1,400 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * 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_ARCH_FUNCS_H_
+#define _AO_ARCH_FUNCS_H_
+
+/* ao_spi_stm.c
+ */
+
+/* PCLK is set to 16MHz (HCLK 32MHz, APB prescaler 2) */
+
+#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_FAST AO_SPI_SPEED_8MHz
+
+/* Companion bus wants something no faster than 200kHz */
+
+#define AO_SPI_SPEED_200kHz AO_SPI_SPEED_125kHz
+
+#define AO_SPI_CONFIG_1 0x00
+#define AO_SPI_1_CONFIG_PA5_PA6_PA7 AO_SPI_CONFIG_1
+#define AO_SPI_2_CONFIG_PB13_PB14_PB15 AO_SPI_CONFIG_1
+
+#define AO_SPI_CONFIG_2 0x04
+#define AO_SPI_1_CONFIG_PB3_PB4_PB5 AO_SPI_CONFIG_2
+#define AO_SPI_2_CONFIG_PD1_PD3_PD4 AO_SPI_CONFIG_2
+
+#define AO_SPI_CONFIG_3 0x08
+#define AO_SPI_1_CONFIG_PE13_PE14_PE15 AO_SPI_CONFIG_3
+
+#define AO_SPI_CONFIG_NONE 0x0c
+
+#define AO_SPI_INDEX_MASK 0x01
+#define AO_SPI_CONFIG_MASK 0x0c
+
+#define AO_SPI_1_PA5_PA6_PA7 (STM_SPI_INDEX(1) | AO_SPI_1_CONFIG_PA5_PA6_PA7)
+#define AO_SPI_1_PB3_PB4_PB5 (STM_SPI_INDEX(1) | AO_SPI_1_CONFIG_PB3_PB4_PB5)
+#define AO_SPI_1_PE13_PE14_PE15 (STM_SPI_INDEX(1) | AO_SPI_1_CONFIG_PE13_PE14_PE15)
+
+#define AO_SPI_2_PB13_PB14_PB15 (STM_SPI_INDEX(2) | AO_SPI_2_CONFIG_PB13_PB14_PB15)
+#define AO_SPI_2_PD1_PD3_PD4 (STM_SPI_INDEX(2) | AO_SPI_2_CONFIG_PD1_PD3_PD4)
+
+#define AO_SPI_INDEX(id) ((id) & AO_SPI_INDEX_MASK)
+#define AO_SPI_CONFIG(id) ((id) & AO_SPI_CONFIG_MASK)
+
+uint8_t
+ao_spi_try_get(uint8_t spi_index, uint32_t speed, uint8_t task_id);
+
+void
+ao_spi_get(uint8_t spi_index, uint32_t speed);
+
+void
+ao_spi_put(uint8_t spi_index);
+
+void
+ao_spi_send(const void *block, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_send_sync(void *block, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_recv(void *block, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_duplex(void *out, void *in, uint16_t len, uint8_t spi_index);
+
+extern uint16_t ao_spi_speed[STM_NUM_SPI];
+
+void
+ao_spi_init(void);
+
+#define ao_spi_set_cs(reg,mask) ((reg)->bsrr = ((uint32_t) (mask)) << 16)
+#define ao_spi_clr_cs(reg,mask) ((reg)->bsrr = (mask))
+
+#define ao_spi_get_mask(reg,mask,bus, speed) do { \
+ ao_spi_get(bus, speed); \
+ ao_spi_set_cs(reg,mask); \
+ } while (0)
+
+static inline uint8_t
+ao_spi_try_get_mask(struct stm_gpio *reg, uint16_t mask, uint8_t bus, uint32_t speed, uint8_t task_id)
+{
+ if (!ao_spi_try_get(bus, speed, task_id))
+ return 0;
+ ao_spi_set_cs(reg, mask);
+ return 1;
+}
+
+#define ao_spi_put_mask(reg,mask,bus) do { \
+ ao_spi_clr_cs(reg,mask); \
+ ao_spi_put(bus); \
+ } while (0)
+
+#define ao_spi_get_bit(reg,bit,pin,bus,speed) ao_spi_get_mask(reg,(1<<bit),bus,speed)
+#define ao_spi_put_bit(reg,bit,pin,bus) ao_spi_put_mask(reg,(1<<bit),bus)
+
+#define ao_enable_port(port) do { \
+ if ((port) == &stm_gpioa) \
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_IOPAEN); \
+ else if ((port) == &stm_gpiob) \
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_IOPBEN); \
+ else if ((port) == &stm_gpioc) \
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_IOPCEN); \
+ else if ((port) == &stm_gpiof) \
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_IOPFEN); \
+ } while (0)
+
+#define ao_disable_port(port) do { \
+ if ((port) == &stm_gpioa) \
+ stm_rcc.ahbenr &= ~(1 << STM_RCC_AHBENR_IOPAEN); \
+ else if ((port) == &stm_gpiob) \
+ stm_rcc.ahbenr &= ~(1 << STM_RCC_AHBENR_IOPBEN); \
+ else if ((port) == &stm_gpioc) \
+ stm_rcc.ahbenr &= ~(1 << STM_RCC_AHBENR_IOPCEN); \
+ else if ((port) == &stm_gpiof) \
+ stm_rcc.ahbenr &= ~(1 << STM_RCC_AHBENR_IOPFEN); \
+ } while (0)
+
+#define ao_gpio_set(port, bit, pin, v) stm_gpio_set(port, bit, v)
+
+#define ao_gpio_get(port, bit, pin) stm_gpio_get(port, bit)
+
+#define ao_enable_output(port,bit,pin,v) do { \
+ ao_enable_port(port); \
+ ao_gpio_set(port, bit, pin, v); \
+ stm_moder_set(port, bit, STM_MODER_OUTPUT);\
+ } while (0)
+
+#define ao_gpio_set_mode(port,bit,mode) do { \
+ if (mode == AO_EXTI_MODE_PULL_UP) \
+ stm_pupdr_set(port, bit, STM_PUPDR_PULL_UP); \
+ else if (mode == AO_EXTI_MODE_PULL_DOWN) \
+ stm_pupdr_set(port, bit, STM_PUPDR_PULL_DOWN); \
+ else \
+ stm_pupdr_set(port, bit, STM_PUPDR_NONE); \
+ } while (0)
+
+#define ao_enable_input(port,bit,mode) do { \
+ ao_enable_port(port); \
+ stm_moder_set(port, bit, STM_MODER_INPUT); \
+ ao_gpio_set_mode(port, bit, mode); \
+ } while (0)
+
+#define ao_enable_cs(port,bit) do { \
+ stm_gpio_set((port), bit, 1); \
+ stm_moder_set((port), bit, STM_MODER_OUTPUT); \
+ } while (0)
+
+#define ao_spi_init_cs(port, mask) do { \
+ ao_enable_port(port); \
+ if ((mask) & 0x0001) ao_enable_cs(port, 0); \
+ if ((mask) & 0x0002) ao_enable_cs(port, 1); \
+ if ((mask) & 0x0004) ao_enable_cs(port, 2); \
+ if ((mask) & 0x0008) ao_enable_cs(port, 3); \
+ if ((mask) & 0x0010) ao_enable_cs(port, 4); \
+ if ((mask) & 0x0020) ao_enable_cs(port, 5); \
+ if ((mask) & 0x0040) ao_enable_cs(port, 6); \
+ if ((mask) & 0x0080) ao_enable_cs(port, 7); \
+ if ((mask) & 0x0100) ao_enable_cs(port, 8); \
+ if ((mask) & 0x0200) ao_enable_cs(port, 9); \
+ if ((mask) & 0x0400) ao_enable_cs(port, 10);\
+ if ((mask) & 0x0800) ao_enable_cs(port, 11);\
+ if ((mask) & 0x1000) ao_enable_cs(port, 12);\
+ if ((mask) & 0x2000) ao_enable_cs(port, 13);\
+ if ((mask) & 0x4000) ao_enable_cs(port, 14);\
+ if ((mask) & 0x8000) ao_enable_cs(port, 15);\
+ } while (0)
+
+/* ao_dma_stm.c
+ */
+
+extern uint8_t ao_dma_done[STM_NUM_DMA];
+
+void
+ao_dma_set_transfer(uint8_t index,
+ volatile void *peripheral,
+ void *memory,
+ uint16_t count,
+ uint32_t ccr);
+
+void
+ao_dma_set_isr(uint8_t index, void (*isr)(int index));
+
+void
+ao_dma_start(uint8_t index);
+
+void
+ao_dma_done_transfer(uint8_t index);
+
+void
+ao_dma_abort(uint8_t index);
+
+void
+ao_dma_alloc(uint8_t index);
+
+void
+ao_dma_init(void);
+
+/* ao_i2c_stm.c */
+
+void
+ao_i2c_get(uint8_t i2c_index);
+
+uint8_t
+ao_i2c_start(uint8_t i2c_index, uint16_t address);
+
+void
+ao_i2c_put(uint8_t i2c_index);
+
+uint8_t
+ao_i2c_send(void *block, uint16_t len, uint8_t i2c_index, uint8_t stop);
+
+uint8_t
+ao_i2c_recv(void *block, uint16_t len, uint8_t i2c_index, uint8_t stop);
+
+void
+ao_i2c_init(void);
+
+/* ao_serial_stm.c */
+struct ao_stm_usart {
+ struct ao_fifo rx_fifo;
+ struct ao_fifo tx_fifo;
+ struct stm_usart *reg;
+ uint8_t tx_started;
+};
+
+#if HAS_SERIAL_1
+extern struct ao_stm_usart ao_stm_usart1;
+#endif
+
+#if HAS_SERIAL_2
+extern struct ao_stm_usart ao_stm_usart2;
+#endif
+
+#if HAS_SERIAL_3
+extern struct ao_stm_usart ao_stm_usart3;
+#endif
+
+#define ARM_PUSH32(stack, val) (*(--(stack)) = (val))
+
+typedef uint32_t ao_arch_irq_t;
+
+static inline uint32_t
+ao_arch_irqsave(void) {
+ uint32_t primask;
+ asm("mrs %0,primask" : "=&r" (primask));
+ ao_arch_block_interrupts();
+ return primask;
+}
+
+static inline void
+ao_arch_irqrestore(uint32_t primask) {
+ asm("msr primask,%0" : : "r" (primask));
+}
+
+static inline void
+ao_arch_memory_barrier() {
+ asm volatile("" ::: "memory");
+}
+
+#if HAS_TASK
+static inline void
+ao_arch_init_stack(struct ao_task *task, void *start)
+{
+ uint32_t *sp = (uint32_t *) (task->stack + AO_STACK_SIZE);
+ uint32_t a = (uint32_t) start;
+ int i;
+
+ /* Return address (goes into LR) */
+ ARM_PUSH32(sp, a);
+
+ /* Clear register values r0-r7 */
+ i = 8;
+ while (i--)
+ ARM_PUSH32(sp, 0);
+
+ /* APSR */
+ ARM_PUSH32(sp, 0);
+
+ /* PRIMASK with interrupts enabled */
+ ARM_PUSH32(sp, 0);
+
+ task->sp = sp;
+}
+
+static inline void ao_arch_save_regs(void) {
+ /* Save general registers */
+ asm("push {r0-r7,lr}\n");
+
+ /* Save APSR */
+ asm("mrs r0,apsr");
+ asm("push {r0}");
+
+ /* Save PRIMASK */
+ asm("mrs r0,primask");
+ asm("push {r0}");
+}
+
+static inline void ao_arch_save_stack(void) {
+ uint32_t *sp;
+ asm("mov %0,sp" : "=&r" (sp) );
+ ao_cur_task->sp = (sp);
+ if ((uint8_t *) sp < &ao_cur_task->stack[0])
+ ao_panic (AO_PANIC_STACK);
+}
+
+static inline void ao_arch_restore_stack(void) {
+ uint32_t sp;
+ sp = (uint32_t) ao_cur_task->sp;
+
+ /* Switch stacks */
+ asm("mov sp, %0" : : "r" (sp) );
+
+ /* Restore PRIMASK */
+ asm("pop {r0}");
+ asm("msr primask,r0");
+
+ /* Restore APSR */
+ asm("pop {r0}");
+ asm("msr apsr_nczvq,r0");
+
+ /* Restore general registers */
+ asm("pop {r0-r7,pc}\n");
+}
+
+#ifndef HAS_SAMPLE_PROFILE
+#define HAS_SAMPLE_PROFILE 0
+#endif
+
+#if !HAS_SAMPLE_PROFILE
+#define HAS_ARCH_START_SCHEDULER 1
+
+static inline void ao_arch_start_scheduler(void) {
+ uint32_t sp;
+ uint32_t control;
+
+ asm("mrs %0,msp" : "=&r" (sp));
+ asm("msr psp,%0" : : "r" (sp));
+ asm("mrs %0,control" : "=&r" (control));
+ control |= (1 << 1);
+ asm("msr control,%0" : : "r" (control));
+ asm("isb");
+}
+#endif
+
+#define ao_arch_isr_stack()
+
+#endif
+
+#define ao_arch_wait_interrupt() do { \
+ asm("\twfi\n"); \
+ ao_arch_release_interrupts(); \
+ asm(".global ao_idle_loc\nao_idle_loc:"); \
+ ao_arch_block_interrupts(); \
+ } while (0)
+
+#define ao_arch_critical(b) do { \
+ uint32_t __mask = ao_arch_irqsave(); \
+ do { b } while (0); \
+ ao_arch_irqrestore(__mask); \
+ } while (0)
+
+/* ao_usb_stm.c */
+
+#if AO_USB_DIRECTIO
+uint16_t *
+ao_usb_alloc(void);
+
+void
+ao_usb_free(uint16_t *buffer);
+
+void
+ao_usb_write(uint16_t *buffer, uint16_t len);
+#endif /* AO_USB_DIRECTIO */
+
+#endif /* _AO_ARCH_FUNCS_H_ */
diff --git a/src/stmf0/ao_boot_chain.c b/src/stmf0/ao_boot_chain.c
new file mode 100644
index 00000000..83a543a0
--- /dev/null
+++ b/src/stmf0/ao_boot_chain.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * 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_boot.h>
+
+void
+ao_boot_chain(uint32_t *base)
+{
+ uint32_t sp;
+ uint32_t pc;
+
+ sp = base[0];
+ pc = base[1];
+ if (0x08000100 <= pc && pc <= 0x08200000 && (pc & 1) == 1) {
+ asm ("mov sp, %0" : : "r" (sp));
+ asm ("mov lr, %0" : : "r" (pc));
+ asm ("bx lr");
+ }
+}
+
+#define AO_BOOT_SIGNAL 0x5a5aa5a5
+#define AO_BOOT_CHECK 0xc3c33c3c
+
+struct ao_boot {
+ uint32_t *base;
+ uint32_t signal;
+ uint32_t check;
+};
+
+static struct ao_boot __attribute__ ((section(".boot"))) ao_boot;
+
+int
+ao_boot_check_chain(void)
+{
+ if (ao_boot.signal == AO_BOOT_SIGNAL && ao_boot.check == AO_BOOT_CHECK) {
+ ao_boot.signal = 0;
+ ao_boot.check = 0;
+ if (ao_boot.base == AO_BOOT_FORCE_LOADER)
+ return 0;
+ ao_boot_chain(ao_boot.base);
+ }
+ return 1;
+}
+
+void
+ao_boot_reboot(uint32_t *base)
+{
+ ao_boot.base = base;
+ ao_boot.signal = AO_BOOT_SIGNAL;
+ ao_boot.check = AO_BOOT_CHECK;
+ ao_arch_reboot();
+}
diff --git a/src/stmf0/ao_boot_pin.c b/src/stmf0/ao_boot_pin.c
new file mode 100644
index 00000000..e825b618
--- /dev/null
+++ b/src/stmf0/ao_boot_pin.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * 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_boot.h>
+#include <ao_exti.h>
+
+void
+ao_boot_check_pin(void)
+{
+ uint16_t v;
+
+ /* Enable power interface clock */
+ stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_PWREN);
+
+ /* Enable the input pin */
+ ao_enable_input(&AO_BOOT_APPLICATION_GPIO, AO_BOOT_APPLICATION_PIN,
+ AO_BOOT_APPLICATION_MODE);
+
+ for (v = 0; v < 100; v++)
+ ao_arch_nop();
+
+ /* Read the value */
+ v = stm_gpio_get(&AO_BOOT_APPLICATION_GPIO, AO_BOOT_APPLICATION_PIN);
+
+ /* Reset the chip to turn off the port and the power interface clock */
+ ao_gpio_set_mode(&AO_BOOT_APPLICATION_GPIO, AO_BOOT_APPLICATION_PIN, 0);
+ ao_disable_port(&AO_BOOT_APPLICATION_GPIO);
+ stm_rcc.apb1enr &= ~(1 << STM_RCC_APB1ENR_PWREN);
+ if (v == AO_BOOT_APPLICATION_VALUE)
+ ao_boot_chain(AO_BOOT_APPLICATION_BASE);
+}
diff --git a/src/stmf0/ao_crc.h b/src/stmf0/ao_crc.h
new file mode 100644
index 00000000..cd011d3a
--- /dev/null
+++ b/src/stmf0/ao_crc.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * 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_CRC_H_
+#define _AO_CRC_H_
+
+#define AO_CRC_16_CCITT 0x1021
+#define AO_CRC_16_CDMA2000 0xc867
+#define AO_CRC_16_DECT 0x0589
+#define AO_CRC_16_T10_DIF 0x8bb7
+#define AO_CRC_16_DNP 0x3d65
+#define AO_CRC_16_ANSI 0x8005
+#define AO_CRC_16_DEFAULT AO_CRC_16_ANSI
+
+#define AO_CRC_32_ANSI 0x04c11db7
+#define AO_CRC_32_C 0x1edc6f41
+
+#define AO_CRC_32_DEFAULT AO_CRC_32_ANSI
+
+static inline uint16_t
+ao_crc_in_32_out_16(uint32_t v) {
+ stm_crc.dr.u32 = v;
+ return stm_crc.dr.u16;
+}
+
+static inline uint16_t
+ao_crc_in_16_out_16(uint16_t v) {
+ stm_crc.dr.u16 = v;
+ return stm_crc.dr.u16;
+}
+
+static inline uint16_t
+ao_crc_in_8_out_16(uint8_t v) {
+ stm_crc.dr.u8 = v;
+ return stm_crc.dr.u16;
+}
+
+void
+ao_crc_reset(void);
+
+void
+ao_crc_init(void);
+
+#endif /* _AO_CRC_H_ */
diff --git a/src/stmf0/ao_dma_stm.c b/src/stmf0/ao_dma_stm.c
new file mode 100644
index 00000000..78fabe18
--- /dev/null
+++ b/src/stmf0/ao_dma_stm.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * 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"
+
+struct ao_dma_config {
+ void (*isr)(int index);
+};
+
+uint8_t ao_dma_done[STM_NUM_DMA];
+
+static struct ao_dma_config ao_dma_config[STM_NUM_DMA];
+static uint8_t ao_dma_allocated[STM_NUM_DMA];
+static uint8_t ao_dma_mutex[STM_NUM_DMA];
+static uint8_t ao_dma_active;
+
+#define id(ch) STM_DMA_INDEX(ch)
+#define id_mask(id) (STM_DMA_ISR_MASK << (id))
+#define ch_mask(ch) id_mask(id(ch))
+
+static void
+ao_dma_isr(uint8_t low_index, uint8_t high_index, uint32_t mask) {
+ /* Get channel interrupt bits */
+ uint32_t isr = stm_dma.isr & mask;
+ uint8_t index;
+
+ /* Ack them */
+ stm_dma.ifcr = isr;
+ for (index = low_index; index <= high_index; index++) {
+ if (isr & id_mask(index)) {
+ if (ao_dma_config[index].isr)
+ (*ao_dma_config[index].isr)(index);
+ else {
+ ao_dma_done[index] = 1;
+ ao_wakeup(&ao_dma_done[index]);
+ }
+ }
+ }
+}
+
+void stm_dma_ch1_isr(void) { ao_dma_isr(id(1), id(1), ch_mask(1)); }
+void stm_dma_ch2_3_isr(void) { ao_dma_isr(id(2), id(3), ch_mask(2) | ch_mask(3)); }
+void stm_dma1_ch4_5_6_isr(void) { ao_dma_isr(id(4), id(6), ch_mask(4) | ch_mask(5) | ch_mask(6)); }
+
+void
+ao_dma_set_transfer(uint8_t index,
+ volatile void *peripheral,
+ void *memory,
+ uint16_t count,
+ uint32_t ccr)
+{
+ if (ao_dma_allocated[index]) {
+ if (ao_dma_mutex[index])
+ ao_panic(AO_PANIC_DMA);
+ ao_dma_mutex[index] = 1;
+ } else
+ ao_mutex_get(&ao_dma_mutex[index]);
+ ao_arch_critical(
+ if (ao_dma_active++ == 0)
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_DMAEN);
+ );
+ stm_dma.channel[index].ccr = ccr | (1 << STM_DMA_CCR_TCIE);
+ stm_dma.channel[index].cndtr = count;
+ stm_dma.channel[index].cpar = peripheral;
+ stm_dma.channel[index].cmar = memory;
+ ao_dma_config[index].isr = NULL;
+}
+
+void
+ao_dma_set_isr(uint8_t index, void (*isr)(int))
+{
+ ao_dma_config[index].isr = isr;
+}
+
+void
+ao_dma_start(uint8_t index)
+{
+ ao_dma_done[index] = 0;
+ stm_dma.channel[index].ccr |= (1 << STM_DMA_CCR_EN);
+}
+
+void
+ao_dma_done_transfer(uint8_t index)
+{
+ stm_dma.channel[index].ccr &= ~(1 << STM_DMA_CCR_EN);
+ ao_arch_critical(
+ if (--ao_dma_active == 0)
+ stm_rcc.ahbenr &= ~(1 << STM_RCC_AHBENR_DMAEN);
+ );
+ if (ao_dma_allocated[index])
+ ao_dma_mutex[index] = 0;
+ else
+ ao_mutex_put(&ao_dma_mutex[index]);
+}
+
+void
+ao_dma_abort(uint8_t index)
+{
+ stm_dma.channel[index].ccr &= ~(1 << STM_DMA_CCR_EN);
+ ao_wakeup(&ao_dma_done[index]);
+}
+
+void
+ao_dma_alloc(uint8_t index)
+{
+ if (ao_dma_allocated[index])
+ ao_panic(AO_PANIC_DMA);
+ ao_dma_allocated[index] = 1;
+}
+
+#define STM_NUM_DMA_ISR 3
+
+void
+ao_dma_init(void)
+{
+ int isr_id;
+ int index;
+
+ for (isr_id = 0; isr_id < STM_NUM_DMA_ISR; isr_id++) {
+ stm_nvic_set_enable(STM_ISR_DMA_CH1_POS + isr_id);
+ stm_nvic_set_priority(STM_ISR_DMA_CH1_POS + isr_id, 4);
+ }
+ for (index = 0; index < STM_NUM_DMA; index++) {
+ ao_dma_allocated[index] = 0;
+ ao_dma_mutex[index] = 0;
+ }
+}
diff --git a/src/stmf0/ao_flash.h b/src/stmf0/ao_flash.h
new file mode 100644
index 00000000..09ca5ac1
--- /dev/null
+++ b/src/stmf0/ao_flash.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * 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_FLASH_STM_H_
+#define _AO_FLASH_STM_H_
+
+void
+ao_flash_erase_page(uint32_t *page);
+
+void
+ao_flash_page(uint32_t *page, uint32_t *src);
+
+#endif /* _AO_FLASH_STM_H_ */
diff --git a/src/stmf0/ao_flash_loader_stm.c b/src/stmf0/ao_flash_loader_stm.c
new file mode 100644
index 00000000..6bf89234
--- /dev/null
+++ b/src/stmf0/ao_flash_loader_stm.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * 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_exti.h>
+#include <ao_boot.h>
+#include <ao_flash_task.h>
+
+int
+main(void)
+{
+ ao_clock_init();
+
+ ao_usb_init();
+
+#if HAS_TICK
+ ao_timer_init();
+#endif
+
+#ifdef AO_FLASH_LOADER_INIT
+ AO_FLASH_LOADER_INIT;
+#endif
+ ao_flash_task();
+ return 0;
+}
diff --git a/src/stmf0/ao_flash_stm.c b/src/stmf0/ao_flash_stm.c
new file mode 100644
index 00000000..5fe0e619
--- /dev/null
+++ b/src/stmf0/ao_flash_stm.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * 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_flash.h>
+
+static uint8_t
+ao_flash_is_locked(void)
+{
+ return (stm_flash.cr & (1 << STM_FLASH_CR_LOCK)) != 0;
+}
+
+static void
+ao_flash_unlock(void)
+{
+ if (!ao_flash_is_locked())
+ return;
+
+ /* Unlock FLASH_CR register */
+ stm_flash.keyr = STM_FLASH_KEYR_KEY1;
+ stm_flash.keyr = STM_FLASH_KEYR_KEY2;
+ if (ao_flash_is_locked())
+ ao_panic(AO_PANIC_FLASH);
+}
+
+static void
+ao_flash_lock(void)
+{
+ stm_flash.cr |= (1 << STM_FLASH_CR_LOCK);
+}
+
+static void
+ao_flash_wait_bsy(void)
+{
+ while (stm_flash.sr & (1 << STM_FLASH_SR_BSY))
+ ;
+}
+
+static void __attribute__ ((section(".ramtext"),noinline))
+_ao_flash_erase_page(uint32_t *page)
+{
+ stm_flash.cr |= (1 << STM_FLASH_CR_PER);
+
+ stm_flash.ar = (uintptr_t) page;
+
+ stm_flash.cr |= (1 << STM_FLASH_CR_STRT);
+
+ ao_flash_wait_bsy();
+
+ stm_flash.cr &= ~(1 << STM_FLASH_CR_PER);
+}
+
+static uint32_t
+stm_flash_page_size(void)
+{
+ uint16_t dev_id = stm_dev_id();
+
+ switch (dev_id) {
+ case 0x440: /* stm32f05x */
+ case 0x444: /* stm32f03x */
+ case 0x445: /* stm32f04x */
+ return 1024;
+ case 0x442: /* stm32f09x */
+ case 0x448: /* stm32f07x */
+ return 2048;
+ }
+ ao_panic(AO_PANIC_FLASH);
+ return 0;
+}
+
+void
+ao_flash_erase_page(uint32_t *page)
+{
+ /* Erase the whole page at the start. This assumes we'll be flashing things
+ * in memory order
+ */
+
+ if ((uintptr_t) page & (stm_flash_page_size() - 1))
+ return;
+
+ ao_arch_block_interrupts();
+ ao_flash_unlock();
+
+ _ao_flash_erase_page(page);
+
+ ao_flash_lock();
+ ao_arch_release_interrupts();
+}
+
+static void __attribute__ ((section(".ramtext"), noinline))
+_ao_flash_page(uint16_t *dst, uint16_t *src)
+{
+ uint8_t i;
+
+ stm_flash.cr |= (1 << STM_FLASH_CR_PG);
+
+ for (i = 0; i < 128; i++) {
+ *dst++ = *src++;
+ ao_flash_wait_bsy();
+ }
+
+ stm_flash.cr &= ~(1 << STM_FLASH_CR_PG);
+}
+
+void
+ao_flash_page(uint32_t *page, uint32_t *src)
+{
+ ao_flash_erase_page(page);
+
+ ao_arch_block_interrupts();
+ ao_flash_unlock();
+
+ _ao_flash_page((uint16_t *) page, (uint16_t *) src);
+
+ ao_flash_lock();
+ ao_arch_release_interrupts();
+}
diff --git a/src/stmf0/ao_flash_stm_pins.h b/src/stmf0/ao_flash_stm_pins.h
new file mode 100644
index 00000000..ab60b4f3
--- /dev/null
+++ b/src/stmf0/ao_flash_stm_pins.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * 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_FLASH_STM_PINS_H_
+#define _AO_FLASH_STM_PINS_H_
+
+#include <ao_flash_pins.h>
+
+/* 48MHz clock based on USB */
+#define AO_HSI48 1
+
+/* HCLK = 48MHz */
+#define AO_AHB_PRESCALER 1
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+
+/* APB = 48MHz */
+#define AO_APB_PRESCALER 1
+#define AO_RCC_CFGR_PPRE_DIV STM_RCC_CFGR_PPRE_DIV_1
+
+#define HAS_USB 1
+
+#define AO_NEED_HSI 1
+
+#endif /* _AO_FLASH_STM_PINS_H_ */
diff --git a/src/stmf0/ao_interrupt.c b/src/stmf0/ao_interrupt.c
new file mode 100644
index 00000000..c6d8ef34
--- /dev/null
+++ b/src/stmf0/ao_interrupt.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * 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 "stm32f0.h"
+#include <string.h>
+#include <ao_boot.h>
+
+#ifndef IS_FLASH_LOADER
+#error Should define IS_FLASH_LOADER
+#define IS_FLASH_LOADER 0
+#endif
+
+#if !IS_FLASH_LOADER
+#define RELOCATE_INTERRUPT 1
+#endif
+
+extern void main(void);
+extern char __stack__;
+extern char __text_start__, __text_end__;
+extern char __data_start__, __data_end__;
+extern char __bss_start__, __bss_end__;
+#if RELOCATE_INTERRUPT
+extern char __interrupt_rom__, __interrupt_start__, __interrupt_end__;
+#endif
+
+/* Interrupt functions */
+
+void stm_halt_isr(void)
+{
+ ao_panic(AO_PANIC_CRASH);
+}
+
+void stm_ignore_isr(void)
+{
+}
+
+const void *stm_interrupt_vector[];
+
+uint32_t
+stm_flash_size(void) {
+ uint16_t dev_id = stm_dev_id();
+ uint16_t kbytes = 0;
+
+ switch (dev_id) {
+ case 0x445:
+ kbytes = stm_flash_size_04x.f_size;
+ break;
+ }
+ return (uint32_t) kbytes * 1024;
+}
+
+void start(void)
+{
+#ifdef AO_BOOT_CHAIN
+ if (ao_boot_check_chain()) {
+#ifdef AO_BOOT_PIN
+ ao_boot_check_pin();
+#endif
+ }
+#endif
+#if RELOCATE_INTERRUPT
+ /* Turn on syscfg */
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SYSCFGCOMPEN);
+
+ memcpy(&__interrupt_start__, &__interrupt_rom__, &__interrupt_end__ - &__interrupt_start__);
+ stm_syscfg.cfgr1 = (stm_syscfg.cfgr1 & ~(STM_SYSCFG_CFGR1_MEM_MODE_MASK << STM_SYSCFG_CFGR1_MEM_MODE)) |
+ (STM_SYSCFG_CFGR1_MEM_MODE_SRAM << STM_SYSCFG_CFGR1_MEM_MODE);
+#endif
+ memcpy(&__data_start__, &__text_end__, &__data_end__ - &__data_start__);
+ memset(&__bss_start__, '\0', &__bss_end__ - &__bss_start__);
+ main();
+}
+
+#define STRINGIFY(x) #x
+
+#define isr(name) \
+ void __attribute__ ((weak)) stm_ ## name ## _isr(void); \
+ _Pragma(STRINGIFY(weak stm_ ## name ## _isr = stm_ignore_isr))
+
+#define isr_halt(name) \
+ void __attribute__ ((weak)) stm_ ## name ## _isr(void); \
+ _Pragma(STRINGIFY(weak stm_ ## name ## _isr = stm_halt_isr))
+
+isr(nmi)
+isr_halt(hardfault)
+isr_halt(memmanage)
+isr_halt(busfault)
+isr_halt(usagefault)
+isr(svc)
+isr(debugmon)
+isr(pendsv)
+isr(systick)
+isr(wwdg)
+isr(pvd)
+isr(rtc)
+isr(flash)
+isr(rcc_crs)
+isr(exti0_1)
+isr(exti2_3)
+isr(exti4_15)
+isr(tsc)
+isr(dma_ch1)
+isr(dma_ch2_3)
+isr(dma_ch4_5_6)
+isr(adc_comp)
+isr(tim1_brk_up_trg_com)
+isr(tim1_cc)
+isr(tim2)
+isr(tim3)
+isr(tim6_dac)
+isr(tim7)
+isr(tim14)
+isr(tim15)
+isr(tim16)
+isr(tim17)
+isr(i2c1)
+isr(i2c2)
+isr(spi1)
+isr(spi2)
+isr(usart1)
+isr(usart2)
+isr(usart3_4_5_6_7_8)
+isr(cec_can)
+isr(usb)
+
+#define i(addr,name) [(addr)/4] = stm_ ## name ## _isr
+
+__attribute__ ((section(".interrupt")))
+const void *stm_interrupt_vector[] = {
+ [0] = &__stack__,
+ [1] = start,
+ i(0x08, nmi),
+ i(0x0c, hardfault),
+ i(0x2c, svc),
+ i(0x30, debugmon),
+ i(0x38, pendsv),
+ i(0x3c, systick),
+ i(0x40, wwdg), /* IRQ0 */
+ i(0x44, pvd),
+ i(0x48, rtc),
+ i(0x4c, flash),
+ i(0x50, rcc_crs),
+ i(0x54, exti0_1),
+ i(0x58, exti2_3),
+ i(0x5c, exti4_15),
+ i(0x60, tsc),
+ i(0x64, dma_ch1),
+ i(0x68, dma_ch2_3),
+ i(0x6c, dma_ch4_5_6),
+ i(0x70, adc_comp),
+ i(0x74, tim1_brk_up_trg_com),
+ i(0x78, tim1_cc),
+ i(0x7c, tim2),
+ i(0x80, tim3),
+ i(0x84, tim6_dac),
+ i(0x88, tim7),
+ i(0x8c, tim14),
+ i(0x90, tim15),
+ i(0x94, tim16),
+ i(0x98, tim17),
+ i(0x9c, i2c1),
+ i(0xa0, i2c2),
+ i(0xa4, spi1),
+ i(0xa8, spi2),
+ i(0xac, usart1),
+ i(0xb0, usart2),
+ i(0xb4, usart3_4_5_6_7_8),
+ i(0xb8, cec_can),
+ i(0xbc, usb),
+};
diff --git a/src/stmf0/ao_led.c b/src/stmf0/ao_led.c
new file mode 100644
index 00000000..9b61cf62
--- /dev/null
+++ b/src/stmf0/ao_led.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * 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"
+
+__pdata uint16_t ao_led_enable;
+
+void
+ao_led_on(uint16_t colors)
+{
+#ifdef LED_PORT
+ LED_PORT->bsrr = (colors & ao_led_enable);
+#else
+#ifdef LED_PORT_0
+ LED_PORT_0->bsrr = ((colors & ao_led_enable) & LED_PORT_0_MASK) << LED_PORT_0_SHIFT;
+#endif
+#ifdef LED_PORT_1
+ LED_PORT_1->bsrr = ((colors & ao_led_enable) & LED_PORT_1_MASK) << LED_PORT_1_SHIFT;
+#endif
+#endif
+}
+
+void
+ao_led_off(uint16_t colors)
+{
+#ifdef LED_PORT
+ LED_PORT->bsrr = (uint32_t) (colors & ao_led_enable) << 16;
+#else
+#ifdef LED_PORT_0
+ LED_PORT_0->bsrr = ((uint32_t) (colors & ao_led_enable) & LED_PORT_0_MASK) << (LED_PORT_0_SHIFT + 16);
+#endif
+#ifdef LED_PORT_1
+ LED_PORT_1->bsrr = ((uint32_t) (colors & ao_led_enable) & LED_PORT_1_MASK) << (LED_PORT_1_SHIFT + 16);
+#endif
+#endif
+}
+
+void
+ao_led_set(uint16_t colors)
+{
+ uint16_t on = colors & ao_led_enable;
+ uint16_t off = ~colors & ao_led_enable;
+
+ ao_led_off(off);
+ ao_led_on(on);
+}
+
+void
+ao_led_toggle(uint16_t colors)
+{
+#ifdef LED_PORT
+ LED_PORT->odr ^= (colors & ao_led_enable);
+#else
+#ifdef LED_PORT_0
+ LED_PORT_0->odr ^= ((colors & ao_led_enable) & LED_PORT_0_MASK) << LED_PORT_0_SHIFT;
+#endif
+#ifdef LED_PORT_1
+ LED_PORT_1->odr ^= ((colors & ao_led_enable) & LED_PORT_1_MASK) << LED_PORT_1_SHIFT;
+#endif
+#endif
+}
+
+void
+ao_led_for(uint16_t colors, uint16_t ticks) __reentrant
+{
+ ao_led_on(colors);
+ ao_delay(ticks);
+ ao_led_off(colors);
+}
+
+#define init_led_pin(port, bit) do { \
+ stm_moder_set(port, bit, STM_MODER_OUTPUT); \
+ stm_otyper_set(port, bit, STM_OTYPER_PUSH_PULL); \
+ } while (0)
+
+void
+ao_led_init(uint16_t enable)
+{
+ int bit;
+
+ ao_led_enable = enable;
+#ifdef LED_PORT
+ stm_rcc.ahbenr |= (1 << LED_PORT_ENABLE);
+ LED_PORT->odr &= ~enable;
+#else
+#ifdef LED_PORT_0
+ stm_rcc.ahbenr |= (1 << LED_PORT_0_ENABLE);
+ LED_PORT_0->odr &= ~((enable & ao_led_enable) & LED_PORT_0_MASK) << LED_PORT_0_SHIFT;
+#endif
+#ifdef LED_PORT_1
+ stm_rcc.ahbenr |= (1 << LED_PORT_1_ENABLE);
+ LED_PORT_1->odr &= ~((enable & ao_led_enable) & LED_PORT_1_MASK) << LED_PORT_1_SHIFT;
+#endif
+#endif
+ for (bit = 0; bit < 16; bit++) {
+ if (enable & (1 << bit)) {
+#ifdef LED_PORT
+ init_led_pin(LED_PORT, bit);
+#else
+#ifdef LED_PORT_0
+ if (LED_PORT_0_MASK & (1 << bit))
+ init_led_pin(LED_PORT_0, bit + LED_PORT_0_SHIFT);
+#endif
+#ifdef LED_PORT_1
+ if (LED_PORT_1_MASK & (1 << bit))
+ init_led_pin(LED_PORT_1, bit + LED_PORT_1_SHIFT);
+#endif
+#endif
+ }
+ }
+}
diff --git a/src/stmf0/ao_romconfig.c b/src/stmf0/ao_romconfig.c
new file mode 100644
index 00000000..5da15072
--- /dev/null
+++ b/src/stmf0/ao_romconfig.c
@@ -0,0 +1,27 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * 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"
+
+AO_ROMCONFIG_SYMBOL (0) uint16_t ao_romconfig_version = AO_ROMCONFIG_VERSION;
+AO_ROMCONFIG_SYMBOL (0) uint16_t ao_romconfig_check = ~AO_ROMCONFIG_VERSION;
+AO_ROMCONFIG_SYMBOL (0) uint16_t ao_serial_number = 0;
+#ifndef AO_RADIO_CAL_DEFAULT
+#define AO_RADIO_CAL_DEFAULT 0x01020304
+#endif
+AO_ROMCONFIG_SYMBOL (0) uint32_t ao_radio_cal = AO_RADIO_CAL_DEFAULT;
+
diff --git a/src/stmf0/ao_timer.c b/src/stmf0/ao_timer.c
new file mode 100644
index 00000000..3aae7e55
--- /dev/null
+++ b/src/stmf0/ao_timer.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * 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_task.h>
+#if HAS_FAKE_FLIGHT
+#include <ao_fake_flight.h>
+#endif
+
+#ifndef HAS_TICK
+#define HAS_TICK 1
+#endif
+
+#if HAS_TICK
+volatile AO_TICK_TYPE ao_tick_count;
+
+AO_TICK_TYPE
+ao_time(void)
+{
+ return ao_tick_count;
+}
+
+#if AO_DATA_ALL
+volatile __data uint8_t ao_data_interval = 1;
+volatile __data uint8_t ao_data_count;
+#endif
+
+void stm_systick_isr(void)
+{
+ if (stm_systick.csr & (1 << STM_SYSTICK_CSR_COUNTFLAG)) {
+ ++ao_tick_count;
+#if HAS_TASK_QUEUE
+ if (ao_task_alarm_tick && (int16_t) (ao_tick_count - ao_task_alarm_tick) >= 0)
+ ao_task_check_alarm((uint16_t) ao_tick_count);
+#endif
+#if AO_DATA_ALL
+ if (++ao_data_count == ao_data_interval) {
+ ao_data_count = 0;
+#if HAS_FAKE_FLIGHT
+ if (ao_fake_flight_active)
+ ao_fake_flight_poll();
+ else
+#endif
+ ao_adc_poll();
+#if (AO_DATA_ALL & ~(AO_DATA_ADC))
+ ao_wakeup((void *) &ao_data_count);
+#endif
+ }
+#endif
+#ifdef AO_TIMER_HOOK
+ AO_TIMER_HOOK;
+#endif
+ }
+}
+
+#if HAS_ADC
+void
+ao_timer_set_adc_interval(uint8_t interval)
+{
+ ao_arch_critical(
+ ao_data_interval = interval;
+ ao_data_count = 0;
+ );
+}
+#endif
+
+#define SYSTICK_RELOAD (AO_SYSTICK / 100 - 1)
+
+void
+ao_timer_init(void)
+{
+ stm_systick.rvr = SYSTICK_RELOAD;
+ stm_systick.cvr = 0;
+ stm_systick.csr = ((1 << STM_SYSTICK_CSR_ENABLE) |
+ (1 << STM_SYSTICK_CSR_TICKINT) |
+ (STM_SYSTICK_CSR_CLKSOURCE_HCLK_8 << STM_SYSTICK_CSR_CLKSOURCE));
+}
+
+#endif
+
+static void
+ao_clock_enable_crs(void)
+{
+ /* Enable crs interface clock */
+ stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_CRSEN);
+
+ /* Disable error counter */
+ stm_crs.cr = ((stm_crs.cr & (1 << 4)) |
+ (32 << STM_CRS_CR_TRIM) |
+ (0 << STM_CRS_CR_SWSYNC) |
+ (0 << STM_CRS_CR_AUTOTRIMEN) |
+ (0 << STM_CRS_CR_CEN) |
+ (0 << STM_CRS_CR_ESYNCIE) |
+ (0 << STM_CRS_CR_ERRIE) |
+ (0 << STM_CRS_CR_SYNCWARNIE) |
+ (0 << STM_CRS_CR_SYNCOKIE));
+
+ /* Configure for USB source */
+ stm_crs.cfgr = ((stm_crs.cfgr & ((1 << 30) | (1 << 27))) |
+ (0 << STM_CRS_CFGR_SYNCPOL) |
+ (STM_CRS_CFGR_SYNCSRC_USB << STM_CRS_CFGR_SYNCSRC) |
+ (STM_CRS_CFGR_SYNCDIV_1 << STM_CRS_CFGR_SYNCDIV) |
+ (0x22 << STM_CRS_CFGR_FELIM) |
+ (((48000000 / 1000) - 1) << STM_CRS_CFGR_RELOAD));
+
+ /* Enable error counter, set auto trim */
+ stm_crs.cr = ((stm_crs.cr & (1 << 4)) |
+ (32 << STM_CRS_CR_TRIM) |
+ (0 << STM_CRS_CR_SWSYNC) |
+ (1 << STM_CRS_CR_AUTOTRIMEN) |
+ (1 << STM_CRS_CR_CEN) |
+ (0 << STM_CRS_CR_ESYNCIE) |
+ (0 << STM_CRS_CR_ERRIE) |
+ (0 << STM_CRS_CR_SYNCWARNIE) |
+ (0 << STM_CRS_CR_SYNCOKIE));
+
+}
+
+void
+ao_clock_init(void)
+{
+ uint32_t cfgr;
+
+ /* Switch to HSI while messing about */
+ stm_rcc.cr |= (1 << STM_RCC_CR_HSION);
+ while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSIRDY)))
+ ao_arch_nop();
+
+ stm_rcc.cfgr = (stm_rcc.cfgr & ~(STM_RCC_CFGR_SW_MASK << STM_RCC_CFGR_SW)) |
+ (STM_RCC_CFGR_SW_HSI << STM_RCC_CFGR_SW);
+
+ /* wait for system to switch to HSI */
+ while ((stm_rcc.cfgr & (STM_RCC_CFGR_SWS_MASK << STM_RCC_CFGR_SWS)) !=
+ (STM_RCC_CFGR_SWS_HSI << STM_RCC_CFGR_SWS))
+ ao_arch_nop();
+
+ /* reset the clock config, leaving us running on the HSI */
+ stm_rcc.cfgr &= (uint32_t)0x0000000f;
+
+ /* reset PLLON, CSSON, HSEBYP, HSEON */
+ stm_rcc.cr &= 0x0000ffff;
+
+ /* Disable all interrupts */
+ stm_rcc.cir = 0;
+
+#if AO_HSE
+#define STM_RCC_CFGR_SWS_TARGET_CLOCK STM_RCC_CFGR_SWS_HSE
+#define STM_RCC_CFGR_SW_TARGET_CLOCK STM_RCC_CFGR_SW_HSE
+#define STM_PLLSRC AO_HSE
+#define STM_RCC_CFGR_PLLSRC_TARGET_CLOCK 1
+
+#if AO_HSE_BYPASS
+ stm_rcc.cr |= (1 << STM_RCC_CR_HSEBYP);
+#else
+ stm_rcc.cr &= ~(1 << STM_RCC_CR_HSEBYP);
+#endif
+ /* Enable HSE clock */
+ stm_rcc.cr |= (1 << STM_RCC_CR_HSEON);
+ while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSERDY)))
+ asm("nop");
+#endif
+
+
+#if AO_HSI48
+#define STM_RCC_CFGR_SWS_TARGET_CLOCK STM_RCC_CFGR_SWS_HSI48
+#define STM_RCC_CFGR_SW_TARGET_CLOCK STM_RCC_CFGR_SW_HSI48
+
+ /* Turn HSI48 clock on */
+ stm_rcc.cr2 |= (1 << STM_RCC_CR2_HSI48ON);
+
+ /* Wait for clock to stabilize */
+ while ((stm_rcc.cr2 & (1 << STM_RCC_CR2_HSI48RDY)) == 0)
+ ao_arch_nop();
+
+ ao_clock_enable_crs();
+#endif
+
+#ifndef STM_RCC_CFGR_SWS_TARGET_CLOCK
+#define STM_HSI 16000000
+#define STM_RCC_CFGR_SWS_TARGET_CLOCK STM_RCC_CFGR_SWS_HSI
+#define STM_RCC_CFGR_SW_TARGET_CLOCK STM_RCC_CFGR_SW_HSI
+#define STM_PLLSRC STM_HSI
+#define STM_RCC_CFGR_PLLSRC_TARGET_CLOCK 0
+#endif
+
+#ifdef STM_PLLSRC
+#error No code for PLL initialization yet
+#endif
+
+ /* Set flash latency to tolerate 48MHz SYSCLK -> 1 wait state */
+
+ /* Enable prefetch */
+ stm_flash.acr |= (1 << STM_FLASH_ACR_PRFTBE);
+
+ /* Enable 1 wait state so the CPU can run at 48MHz */
+ stm_flash.acr |= (STM_FLASH_ACR_LATENCY_1 << STM_FLASH_ACR_LATENCY);
+
+ /* Enable power interface clock */
+ stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_PWREN);
+
+ /* HCLK to 48MHz -> AHB prescaler = /1 */
+ cfgr = stm_rcc.cfgr;
+ cfgr &= ~(STM_RCC_CFGR_HPRE_MASK << STM_RCC_CFGR_HPRE);
+ cfgr |= (AO_RCC_CFGR_HPRE_DIV << STM_RCC_CFGR_HPRE);
+ stm_rcc.cfgr = cfgr;
+ while ((stm_rcc.cfgr & (STM_RCC_CFGR_HPRE_MASK << STM_RCC_CFGR_HPRE)) !=
+ (AO_RCC_CFGR_HPRE_DIV << STM_RCC_CFGR_HPRE))
+ ao_arch_nop();
+
+ /* APB Prescaler = AO_APB_PRESCALER */
+ cfgr = stm_rcc.cfgr;
+ cfgr &= ~(STM_RCC_CFGR_PPRE_MASK << STM_RCC_CFGR_PPRE);
+ cfgr |= (AO_RCC_CFGR_PPRE_DIV << STM_RCC_CFGR_PPRE);
+ stm_rcc.cfgr = cfgr;
+
+ /* Switch to the desired system clock */
+
+ cfgr = stm_rcc.cfgr;
+ cfgr &= ~(STM_RCC_CFGR_SW_MASK << STM_RCC_CFGR_SW);
+ cfgr |= (STM_RCC_CFGR_SW_TARGET_CLOCK << STM_RCC_CFGR_SW);
+ stm_rcc.cfgr = cfgr;
+ for (;;) {
+ uint32_t c, part, mask, val;
+
+ c = stm_rcc.cfgr;
+ mask = (STM_RCC_CFGR_SWS_MASK << STM_RCC_CFGR_SWS);
+ val = (STM_RCC_CFGR_SWS_TARGET_CLOCK << STM_RCC_CFGR_SWS);
+ part = c & mask;
+ if (part == val)
+ break;
+ }
+
+ /* Clear reset flags */
+ stm_rcc.csr |= (1 << STM_RCC_CSR_RMVF);
+
+#if !AO_HSI && !AO_NEED_HSI
+ /* Turn off the HSI clock */
+ stm_rcc.cr &= ~(1 << STM_RCC_CR_HSION);
+#endif
+#if DEBUG_THE_CLOCK
+ /* Output SYSCLK on PA8 for measurments */
+
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
+
+ stm_afr_set(&stm_gpioa, 8, STM_AFR_AF0);
+ stm_moder_set(&stm_gpioa, 8, STM_MODER_ALTERNATE);
+ stm_ospeedr_set(&stm_gpioa, 8, STM_OSPEEDR_40MHz);
+
+ stm_rcc.cfgr |= (STM_RCC_CFGR_MCOPRE_DIV_1 << STM_RCC_CFGR_MCOPRE);
+ stm_rcc.cfgr |= (STM_RCC_CFGR_MCOSEL_HSE << STM_RCC_CFGR_MCOSEL);
+#endif
+}
diff --git a/src/stmf0/ao_usb_stm.c b/src/stmf0/ao_usb_stm.c
new file mode 100644
index 00000000..3ea7da5e
--- /dev/null
+++ b/src/stmf0/ao_usb_stm.c
@@ -0,0 +1,1150 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * 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_usb.h"
+#include "ao_product.h"
+
+#define USB_DEBUG 0
+#define USB_DEBUG_DATA 0
+#define USB_ECHO 0
+
+#ifndef AO_PA11_PA12_RMP
+#error "must define AO_PA11_PA12_RMP"
+#endif
+
+#ifndef USE_USB_STDIO
+#define USE_USB_STDIO 1
+#endif
+
+#if USE_USB_STDIO
+#define AO_USB_OUT_SLEEP_ADDR (&ao_stdin_ready)
+#else
+#define AO_USB_OUT_SLEEP_ADDR (&ao_usb_out_avail)
+#endif
+
+#if USB_DEBUG
+#define debug(format, args...) printf(format, ## args);
+#else
+#define debug(format, args...)
+#endif
+
+#if USB_DEBUG_DATA
+#define debug_data(format, args...) printf(format, ## args);
+#else
+#define debug_data(format, args...)
+#endif
+
+struct ao_usb_setup {
+ uint8_t dir_type_recip;
+ uint8_t request;
+ uint16_t value;
+ uint16_t index;
+ uint16_t length;
+} ao_usb_setup;
+
+static uint8_t ao_usb_ep0_state;
+
+/* Pending EP0 IN data */
+static const uint8_t *ao_usb_ep0_in_data; /* Remaining data */
+static uint8_t ao_usb_ep0_in_len; /* Remaining amount */
+
+/* Temp buffer for smaller EP0 in data */
+static uint8_t ao_usb_ep0_in_buf[2];
+
+/* Pending EP0 OUT data */
+static uint8_t *ao_usb_ep0_out_data;
+static uint8_t ao_usb_ep0_out_len;
+
+/*
+ * Objects allocated in special USB memory
+ */
+
+/* Buffer description tables */
+static union stm_usb_bdt *ao_usb_bdt;
+/* USB address of end of allocated storage */
+static uint16_t ao_usb_sram_addr;
+
+/* Pointer to ep0 tx/rx buffers in USB memory */
+static uint16_t *ao_usb_ep0_tx_buffer;
+static uint16_t *ao_usb_ep0_rx_buffer;
+
+/* Pointer to bulk data tx/rx buffers in USB memory */
+static uint16_t ao_usb_in_tx_offset;
+static uint16_t *ao_usb_in_tx_buffer;
+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;
+
+static uint8_t ao_usb_rx_buffer[AO_USB_OUT_SIZE];
+static uint8_t ao_usb_rx_count, ao_usb_rx_pos;
+
+/*
+ * End point register indices
+ */
+
+#define AO_USB_CONTROL_EPR 0
+#define AO_USB_INT_EPR 1
+#define AO_USB_OUT_EPR 2
+#define AO_USB_IN_EPR 3
+
+/* Marks when we don't need to send an IN packet.
+ * This happens only when the last IN packet is not full,
+ * otherwise the host will expect to keep seeing packets.
+ * Send a zero-length packet as required
+ */
+static uint8_t ao_usb_in_flushed;
+
+/* 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_in_pending;
+
+/* Marks when an OUT packet has been received by the hardware
+ * but not pulled to the shadow buffer.
+ */
+static uint8_t ao_usb_out_avail;
+uint8_t ao_usb_running;
+static uint8_t ao_usb_configuration;
+
+#define AO_USB_EP0_GOT_RESET 1
+#define AO_USB_EP0_GOT_SETUP 2
+#define AO_USB_EP0_GOT_RX_DATA 4
+#define AO_USB_EP0_GOT_TX_ACK 8
+
+static uint8_t ao_usb_ep0_receive;
+static uint8_t ao_usb_address;
+static uint8_t ao_usb_address_pending;
+
+static inline uint32_t set_toggle(uint32_t current_value,
+ uint32_t mask,
+ uint32_t desired_value)
+{
+ return (current_value ^ desired_value) & mask;
+}
+
+static inline uint16_t *ao_usb_packet_buffer_addr(uint16_t sram_addr)
+{
+ return (uint16_t *) (stm_usb_sram + sram_addr);
+}
+
+#if AO_USB_DIRECTIO
+static inline uint16_t ao_usb_packet_buffer_offset(uint16_t *addr)
+{
+ return (uint16_t) ((uint8_t *) addr - stm_usb_sram);
+}
+#endif
+
+static inline uint32_t ao_usb_epr_stat_rx(uint32_t epr) {
+ return (epr >> STM_USB_EPR_STAT_RX) & STM_USB_EPR_STAT_RX_MASK;
+}
+
+static inline uint32_t ao_usb_epr_stat_tx(uint32_t epr) {
+ return (epr >> STM_USB_EPR_STAT_TX) & STM_USB_EPR_STAT_TX_MASK;
+}
+
+static inline uint32_t ao_usb_epr_ctr_rx(uint32_t epr) {
+ return (epr >> STM_USB_EPR_CTR_RX) & 1;
+}
+
+static inline uint32_t ao_usb_epr_ctr_tx(uint32_t epr) {
+ return (epr >> STM_USB_EPR_CTR_TX) & 1;
+}
+
+static inline uint32_t ao_usb_epr_setup(uint32_t epr) {
+ return (epr >> STM_USB_EPR_SETUP) & 1;
+}
+
+static inline uint32_t ao_usb_epr_dtog_rx(uint32_t epr) {
+ return (epr >> STM_USB_EPR_DTOG_RX) & 1;
+}
+
+static inline uint32_t ao_usb_epr_dtog_tx(uint32_t epr) {
+ return (epr >> STM_USB_EPR_DTOG_TX) & 1;
+}
+
+/*
+ * Set current device address and mark the
+ * interface as active
+ */
+void
+ao_usb_set_address(uint8_t address)
+{
+ debug("ao_usb_set_address %02x\n", address);
+ stm_usb.daddr = (1 << STM_USB_DADDR_EF) | address;
+ ao_usb_address_pending = 0;
+}
+
+/*
+ * Write these values to preserve register contents under HW changes
+ */
+
+#define STM_USB_EPR_INVARIANT ((1 << STM_USB_EPR_CTR_RX) | \
+ (STM_USB_EPR_DTOG_RX_WRITE_INVARIANT << STM_USB_EPR_DTOG_RX) | \
+ (STM_USB_EPR_STAT_RX_WRITE_INVARIANT << STM_USB_EPR_STAT_RX) | \
+ (1 << STM_USB_EPR_CTR_TX) | \
+ (STM_USB_EPR_DTOG_TX_WRITE_INVARIANT << STM_USB_EPR_DTOG_TX) | \
+ (STM_USB_EPR_STAT_TX_WRITE_INVARIANT << STM_USB_EPR_STAT_TX))
+
+#define STM_USB_EPR_INVARIANT_MASK ((1 << STM_USB_EPR_CTR_RX) | \
+ (STM_USB_EPR_DTOG_RX_MASK << STM_USB_EPR_DTOG_RX) | \
+ (STM_USB_EPR_STAT_RX_MASK << STM_USB_EPR_STAT_RX) | \
+ (1 << STM_USB_EPR_CTR_TX) | \
+ (STM_USB_EPR_DTOG_TX_MASK << STM_USB_EPR_DTOG_TX) | \
+ (STM_USB_EPR_STAT_TX_MASK << STM_USB_EPR_STAT_TX))
+
+/*
+ * These bits are purely under sw control, so preserve them in the
+ * register by re-writing what was read
+ */
+#define STM_USB_EPR_PRESERVE_MASK ((STM_USB_EPR_EP_TYPE_MASK << STM_USB_EPR_EP_TYPE) | \
+ (1 << STM_USB_EPR_EP_KIND) | \
+ (STM_USB_EPR_EA_MASK << STM_USB_EPR_EA))
+
+#define TX_DBG 0
+#define RX_DBG 0
+
+#if TX_DBG
+#define _tx_dbg0(msg) _dbg(__LINE__,msg,0)
+#define _tx_dbg1(msg,value) _dbg(__LINE__,msg,value)
+#else
+#define _tx_dbg0(msg)
+#define _tx_dbg1(msg,value)
+#endif
+
+#if RX_DBG
+#define _rx_dbg0(msg) _dbg(__LINE__,msg,0)
+#define _rx_dbg1(msg,value) _dbg(__LINE__,msg,value)
+#else
+#define _rx_dbg0(msg)
+#define _rx_dbg1(msg,value)
+#endif
+
+#if TX_DBG || RX_DBG
+static void _dbg(int line, char *msg, uint32_t value);
+#endif
+
+/*
+ * Set the state of the specified endpoint register to a new
+ * value. This is tricky because the bits toggle where the new
+ * value is one, and we need to write invariant values in other
+ * spots of the register. This hardware is strange...
+ */
+static void
+_ao_usb_set_stat_tx(int ep, uint32_t stat_tx)
+{
+ uint16_t epr_write, epr_old;
+
+ _tx_dbg1("set_stat_tx top", stat_tx);
+ epr_old = epr_write = stm_usb.epr[ep].r;
+ epr_write &= STM_USB_EPR_PRESERVE_MASK;
+ epr_write |= STM_USB_EPR_INVARIANT;
+ epr_write |= set_toggle(epr_old,
+ STM_USB_EPR_STAT_TX_MASK << STM_USB_EPR_STAT_TX,
+ stat_tx << STM_USB_EPR_STAT_TX);
+ stm_usb.epr[ep].r = epr_write;
+ _tx_dbg1("set_stat_tx bottom", epr_write);
+}
+
+static void
+ao_usb_set_stat_tx(int ep, uint32_t stat_tx)
+{
+ ao_arch_block_interrupts();
+ _ao_usb_set_stat_tx(ep, stat_tx);
+ ao_arch_release_interrupts();
+}
+
+static void
+_ao_usb_set_stat_rx(int ep, uint32_t stat_rx) {
+ uint16_t epr_write, epr_old;
+
+ epr_write = epr_old = stm_usb.epr[ep].r;
+ epr_write &= STM_USB_EPR_PRESERVE_MASK;
+ epr_write |= STM_USB_EPR_INVARIANT;
+ epr_write |= set_toggle(epr_old,
+ STM_USB_EPR_STAT_RX_MASK << STM_USB_EPR_STAT_RX,
+ stat_rx << STM_USB_EPR_STAT_RX);
+ stm_usb.epr[ep].r = epr_write;
+}
+
+static void
+ao_usb_set_stat_rx(int ep, uint32_t stat_rx) {
+ ao_arch_block_interrupts();
+ _ao_usb_set_stat_rx(ep, stat_rx);
+ ao_arch_release_interrupts();
+}
+
+/*
+ * Set just endpoint 0, for use during startup
+ */
+
+static void
+ao_usb_init_ep(uint8_t ep, uint32_t addr, uint32_t type, uint32_t stat_rx, uint32_t stat_tx)
+{
+ uint16_t epr;
+
+ ao_arch_block_interrupts();
+ epr = stm_usb.epr[ep].r;
+ epr = ((0 << STM_USB_EPR_CTR_RX) |
+ (epr & (1 << STM_USB_EPR_DTOG_RX)) |
+ set_toggle(epr,
+ (STM_USB_EPR_STAT_RX_MASK << STM_USB_EPR_STAT_RX),
+ (stat_rx << STM_USB_EPR_STAT_RX)) |
+ (type << STM_USB_EPR_EP_TYPE) |
+ (0 << STM_USB_EPR_EP_KIND) |
+ (0 << STM_USB_EPR_CTR_TX) |
+ (epr & (1 << STM_USB_EPR_DTOG_TX)) |
+ set_toggle(epr,
+ (STM_USB_EPR_STAT_TX_MASK << STM_USB_EPR_STAT_TX),
+ (stat_tx << STM_USB_EPR_STAT_TX)) |
+ (addr << STM_USB_EPR_EA));
+ stm_usb.epr[ep].r = epr;
+ ao_arch_release_interrupts();
+ debug ("writing epr[%d] 0x%04x wrote 0x%04x\n",
+ ep, epr, stm_usb.epr[ep].r);
+}
+
+static void
+ao_usb_init_btable(void)
+{
+ ao_usb_sram_addr = 0;
+
+ ao_usb_bdt = (void *) stm_usb_sram;
+
+ ao_usb_sram_addr += 8 * STM_USB_BDT_SIZE;
+
+ /* Set up EP 0 - a Control end point with 32 bytes of in and out buffers */
+
+ ao_usb_bdt[0].single.addr_tx = ao_usb_sram_addr;
+ ao_usb_bdt[0].single.count_tx = 0;
+ ao_usb_ep0_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+ ao_usb_sram_addr += AO_USB_CONTROL_SIZE;
+
+ ao_usb_bdt[0].single.addr_rx = ao_usb_sram_addr;
+ ao_usb_bdt[0].single.count_rx = ((1 << STM_USB_BDT_COUNT_RX_BL_SIZE) |
+ (((AO_USB_CONTROL_SIZE / 32) - 1) << STM_USB_BDT_COUNT_RX_NUM_BLOCK));
+ ao_usb_ep0_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+ ao_usb_sram_addr += AO_USB_CONTROL_SIZE;
+
+}
+
+static void
+ao_usb_set_ep0(void)
+{
+ int e;
+
+ ao_usb_init_btable();
+
+ /* buffer table is at the start of USB memory */
+ stm_usb.btable = 0;
+
+ ao_usb_init_ep(AO_USB_CONTROL_EPR, AO_USB_CONTROL_EP,
+ STM_USB_EPR_EP_TYPE_CONTROL,
+ STM_USB_EPR_STAT_RX_VALID,
+ STM_USB_EPR_STAT_TX_NAK);
+
+ /* Clear all of the other endpoints */
+ for (e = 1; e < 8; e++) {
+ ao_usb_init_ep(e, 0,
+ STM_USB_EPR_EP_TYPE_CONTROL,
+ STM_USB_EPR_STAT_RX_DISABLED,
+ STM_USB_EPR_STAT_TX_DISABLED);
+ }
+
+ ao_usb_set_address(0);
+}
+
+static void
+ao_usb_set_configuration(void)
+{
+ debug ("ao_usb_set_configuration\n");
+
+ /* Set up the INT end point */
+ ao_usb_bdt[AO_USB_INT_EPR].single.addr_tx = ao_usb_sram_addr;
+ ao_usb_bdt[AO_USB_INT_EPR].single.count_tx = 0;
+ ao_usb_sram_addr += AO_USB_INT_SIZE;
+
+ ao_usb_init_ep(AO_USB_INT_EPR,
+ AO_USB_INT_EP,
+ STM_USB_EPR_EP_TYPE_INTERRUPT,
+ STM_USB_EPR_STAT_RX_DISABLED,
+ STM_USB_EPR_STAT_TX_NAK);
+
+ /* Set up the OUT end point */
+ ao_usb_bdt[AO_USB_OUT_EPR].single.addr_rx = ao_usb_sram_addr;
+ ao_usb_bdt[AO_USB_OUT_EPR].single.count_rx = ((1 << STM_USB_BDT_COUNT_RX_BL_SIZE) |
+ (((AO_USB_OUT_SIZE / 32) - 1) << STM_USB_BDT_COUNT_RX_NUM_BLOCK));
+ ao_usb_out_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+ ao_usb_sram_addr += AO_USB_OUT_SIZE;
+
+ ao_usb_init_ep(AO_USB_OUT_EPR,
+ AO_USB_OUT_EP,
+ STM_USB_EPR_EP_TYPE_BULK,
+ STM_USB_EPR_STAT_RX_VALID,
+ STM_USB_EPR_STAT_TX_DISABLED);
+
+ /* Set up the IN end point */
+ ao_usb_bdt[AO_USB_IN_EPR].single.addr_tx = ao_usb_sram_addr;
+ ao_usb_bdt[AO_USB_IN_EPR].single.count_tx = 0;
+ ao_usb_in_tx_offset = ao_usb_sram_addr;
+ ao_usb_in_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_in_tx_offset);
+ ao_usb_sram_addr += AO_USB_IN_SIZE;
+
+ ao_usb_init_ep(AO_USB_IN_EPR,
+ AO_USB_IN_EP,
+ STM_USB_EPR_EP_TYPE_BULK,
+ STM_USB_EPR_STAT_RX_DISABLED,
+ STM_USB_EPR_STAT_TX_NAK);
+
+ ao_usb_running = 1;
+}
+
+static uint16_t control_count;
+static uint16_t int_count;
+static uint16_t in_count;
+static uint16_t out_count;
+static uint16_t reset_count;
+
+/* The USB memory must be accessed in 16-bit units
+ */
+
+static void
+ao_usb_copy_tx(const uint8_t *src, uint16_t *base, uint16_t bytes)
+{
+ while (bytes >= 2) {
+ *base++ = src[0] | (src[1] << 8);
+ src += 2;
+ bytes -= 2;
+ }
+ if (bytes)
+ *base = *src;
+}
+
+static void
+ao_usb_copy_rx(uint8_t *dst, uint16_t *base, uint16_t bytes)
+{
+ while (bytes >= 2) {
+ uint16_t s = *base++;
+ dst[0] = s;
+ dst[1] = s >> 8;
+ dst += 2;
+ bytes -= 2;
+ }
+ if (bytes)
+ *dst = *base;
+}
+
+/* Send an IN data packet */
+static void
+ao_usb_ep0_flush(void)
+{
+ uint8_t this_len;
+
+ /* Check to see if the endpoint is still busy */
+ if (ao_usb_epr_stat_tx(stm_usb.epr[0].r) == STM_USB_EPR_STAT_TX_VALID) {
+ debug("EP0 not accepting IN data\n");
+ return;
+ }
+
+ this_len = ao_usb_ep0_in_len;
+ if (this_len > AO_USB_CONTROL_SIZE)
+ this_len = AO_USB_CONTROL_SIZE;
+
+ if (this_len < AO_USB_CONTROL_SIZE)
+ ao_usb_ep0_state = AO_USB_EP0_IDLE;
+
+ ao_usb_ep0_in_len -= this_len;
+
+ debug_data ("Flush EP0 len %d:", this_len);
+ ao_usb_copy_tx(ao_usb_ep0_in_data, ao_usb_ep0_tx_buffer, this_len);
+ debug_data ("\n");
+ ao_usb_ep0_in_data += this_len;
+
+ /* Mark the endpoint as TX valid to send the packet */
+ ao_usb_bdt[AO_USB_CONTROL_EPR].single.count_tx = this_len;
+ ao_usb_set_stat_tx(AO_USB_CONTROL_EPR, STM_USB_EPR_STAT_TX_VALID);
+ debug ("queue tx. epr 0 now %08x\n", stm_usb.epr[AO_USB_CONTROL_EPR]);
+}
+
+/* Read data from the ep0 OUT fifo */
+static void
+ao_usb_ep0_fill(void)
+{
+ uint16_t len = ao_usb_bdt[0].single.count_rx & STM_USB_BDT_COUNT_RX_COUNT_RX_MASK;
+
+ if (len > ao_usb_ep0_out_len)
+ len = ao_usb_ep0_out_len;
+ ao_usb_ep0_out_len -= len;
+
+ /* Pull all of the data out of the packet */
+ debug_data ("Fill EP0 len %d:", len);
+ ao_usb_copy_rx(ao_usb_ep0_out_data, ao_usb_ep0_rx_buffer, len);
+ debug_data ("\n");
+ ao_usb_ep0_out_data += len;
+
+ /* ACK the packet */
+ ao_usb_set_stat_rx(0, STM_USB_EPR_STAT_RX_VALID);
+}
+
+static void
+ao_usb_ep0_in_reset(void)
+{
+ ao_usb_ep0_in_data = ao_usb_ep0_in_buf;
+ ao_usb_ep0_in_len = 0;
+}
+
+static void
+ao_usb_ep0_in_queue_byte(uint8_t a)
+{
+ if (ao_usb_ep0_in_len < sizeof (ao_usb_ep0_in_buf))
+ ao_usb_ep0_in_buf[ao_usb_ep0_in_len++] = a;
+}
+
+static void
+ao_usb_ep0_in_set(const uint8_t *data, uint8_t len)
+{
+ ao_usb_ep0_in_data = data;
+ ao_usb_ep0_in_len = len;
+}
+
+static void
+ao_usb_ep0_out_set(uint8_t *data, uint8_t len)
+{
+ ao_usb_ep0_out_data = data;
+ ao_usb_ep0_out_len = len;
+}
+
+static void
+ao_usb_ep0_in_start(uint16_t max)
+{
+ /* Don't send more than asked for */
+ if (ao_usb_ep0_in_len > max)
+ ao_usb_ep0_in_len = max;
+ ao_usb_ep0_flush();
+}
+
+static struct ao_usb_line_coding ao_usb_line_coding = {115200, 0, 0, 8};
+
+/* Walk through the list of descriptors and find a match
+ */
+static void
+ao_usb_get_descriptor(uint16_t value)
+{
+ const uint8_t *descriptor;
+ uint8_t type = value >> 8;
+ uint8_t index = value;
+
+ descriptor = ao_usb_descriptors;
+ while (descriptor[0] != 0) {
+ if (descriptor[1] == type && index-- == 0) {
+ uint8_t len;
+ if (type == AO_USB_DESC_CONFIGURATION)
+ len = descriptor[2];
+ else
+ len = descriptor[0];
+ ao_usb_ep0_in_set(descriptor, len);
+ break;
+ }
+ descriptor += descriptor[0];
+ }
+}
+
+static void
+ao_usb_ep0_setup(void)
+{
+ /* Pull the setup packet out of the fifo */
+ ao_usb_ep0_out_set((uint8_t *) &ao_usb_setup, 8);
+ ao_usb_ep0_fill();
+ if (ao_usb_ep0_out_len != 0) {
+ debug ("invalid setup packet length\n");
+ return;
+ }
+
+ if ((ao_usb_setup.dir_type_recip & AO_USB_DIR_IN) || ao_usb_setup.length == 0)
+ ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
+ else
+ ao_usb_ep0_state = AO_USB_EP0_DATA_OUT;
+
+ ao_usb_ep0_in_reset();
+
+ switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_TYPE_MASK) {
+ case AO_USB_TYPE_STANDARD:
+ debug ("Standard setup packet\n");
+ switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_RECIP_MASK) {
+ case AO_USB_RECIP_DEVICE:
+ debug ("Device setup packet\n");
+ switch(ao_usb_setup.request) {
+ case AO_USB_REQ_GET_STATUS:
+ debug ("get status\n");
+ ao_usb_ep0_in_queue_byte(0);
+ ao_usb_ep0_in_queue_byte(0);
+ break;
+ case AO_USB_REQ_SET_ADDRESS:
+ debug ("set address %d\n", ao_usb_setup.value);
+ ao_usb_address = ao_usb_setup.value;
+ ao_usb_address_pending = 1;
+ break;
+ case AO_USB_REQ_GET_DESCRIPTOR:
+ debug ("get descriptor %d\n", ao_usb_setup.value);
+ ao_usb_get_descriptor(ao_usb_setup.value);
+ break;
+ case AO_USB_REQ_GET_CONFIGURATION:
+ debug ("get configuration %d\n", ao_usb_configuration);
+ ao_usb_ep0_in_queue_byte(ao_usb_configuration);
+ break;
+ case AO_USB_REQ_SET_CONFIGURATION:
+ ao_usb_configuration = ao_usb_setup.value;
+ debug ("set configuration %d\n", ao_usb_configuration);
+ ao_usb_set_configuration();
+ break;
+ }
+ break;
+ case AO_USB_RECIP_INTERFACE:
+ debug ("Interface setup packet\n");
+ switch(ao_usb_setup.request) {
+ case AO_USB_REQ_GET_STATUS:
+ ao_usb_ep0_in_queue_byte(0);
+ ao_usb_ep0_in_queue_byte(0);
+ break;
+ case AO_USB_REQ_GET_INTERFACE:
+ ao_usb_ep0_in_queue_byte(0);
+ break;
+ case AO_USB_REQ_SET_INTERFACE:
+ break;
+ }
+ break;
+ case AO_USB_RECIP_ENDPOINT:
+ debug ("Endpoint setup packet\n");
+ switch(ao_usb_setup.request) {
+ case AO_USB_REQ_GET_STATUS:
+ ao_usb_ep0_in_queue_byte(0);
+ ao_usb_ep0_in_queue_byte(0);
+ break;
+ }
+ break;
+ }
+ break;
+ case AO_USB_TYPE_CLASS:
+ debug ("Class setup packet\n");
+ switch (ao_usb_setup.request) {
+ case AO_USB_SET_LINE_CODING:
+ debug ("set line coding\n");
+ ao_usb_ep0_out_set((uint8_t *) &ao_usb_line_coding, 7);
+ break;
+ case AO_USB_GET_LINE_CODING:
+ debug ("get line coding\n");
+ ao_usb_ep0_in_set((const uint8_t *) &ao_usb_line_coding, 7);
+ break;
+ case AO_USB_SET_CONTROL_LINE_STATE:
+ break;
+ }
+ break;
+ }
+
+ /* If we're not waiting to receive data from the host,
+ * queue an IN response
+ */
+ if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN)
+ ao_usb_ep0_in_start(ao_usb_setup.length);
+}
+
+static void
+ao_usb_ep0_handle(uint8_t receive)
+{
+ ao_usb_ep0_receive = 0;
+ if (receive & AO_USB_EP0_GOT_RESET) {
+ debug ("\treset\n");
+ ao_usb_set_ep0();
+ return;
+ }
+ if (receive & AO_USB_EP0_GOT_SETUP) {
+ debug ("\tsetup\n");
+ ao_usb_ep0_setup();
+ }
+ if (receive & AO_USB_EP0_GOT_RX_DATA) {
+ debug ("\tgot rx data\n");
+ if (ao_usb_ep0_state == AO_USB_EP0_DATA_OUT) {
+ ao_usb_ep0_fill();
+ if (ao_usb_ep0_out_len == 0) {
+ ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
+ ao_usb_ep0_in_start(0);
+ }
+ }
+ }
+ if (receive & AO_USB_EP0_GOT_TX_ACK) {
+ debug ("\tgot tx ack\n");
+
+#if HAS_FLIGHT && AO_USB_FORCE_IDLE
+ ao_flight_force_idle = 1;
+#endif
+ /* Wait until the IN packet is received from addr 0
+ * before assigning our local address
+ */
+ if (ao_usb_address_pending)
+ ao_usb_set_address(ao_usb_address);
+ if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN)
+ ao_usb_ep0_flush();
+ }
+}
+
+void
+stm_usb_isr(void)
+{
+ uint32_t istr = stm_usb.istr;
+
+ stm_usb.istr = ~istr;
+ if (istr & (1 << STM_USB_ISTR_CTR)) {
+ uint8_t ep = istr & STM_USB_ISTR_EP_ID_MASK;
+ uint16_t epr, epr_write;
+
+ /* Preserve the SW write bits, don't mess with most HW writable bits,
+ * clear the CTR_RX and CTR_TX bits
+ */
+ epr = stm_usb.epr[ep].r;
+ epr_write = epr;
+ epr_write &= STM_USB_EPR_PRESERVE_MASK;
+ epr_write |= STM_USB_EPR_INVARIANT;
+ epr_write &= ~(1 << STM_USB_EPR_CTR_RX);
+ epr_write &= ~(1 << STM_USB_EPR_CTR_TX);
+ stm_usb.epr[ep].r = epr_write;
+
+ switch (ep) {
+ case 0:
+ ++control_count;
+ if (ao_usb_epr_ctr_rx(epr)) {
+ if (ao_usb_epr_setup(epr))
+ ao_usb_ep0_receive |= AO_USB_EP0_GOT_SETUP;
+ else
+ ao_usb_ep0_receive |= AO_USB_EP0_GOT_RX_DATA;
+ }
+ if (ao_usb_epr_ctr_tx(epr))
+ ao_usb_ep0_receive |= AO_USB_EP0_GOT_TX_ACK;
+ ao_usb_ep0_handle(ao_usb_ep0_receive);
+ break;
+ case AO_USB_OUT_EPR:
+ ++out_count;
+ if (ao_usb_epr_ctr_rx(epr)) {
+ _rx_dbg1("RX ISR", epr);
+ ao_usb_out_avail = 1;
+ _rx_dbg0("out avail set");
+ ao_wakeup(AO_USB_OUT_SLEEP_ADDR);
+ _rx_dbg0("stdin awoken");
+ }
+ break;
+ case AO_USB_IN_EPR:
+ ++in_count;
+ _tx_dbg1("TX ISR", epr);
+ if (ao_usb_epr_ctr_tx(epr)) {
+ ao_usb_in_pending = 0;
+ ao_wakeup(&ao_usb_in_pending);
+ }
+ break;
+ case AO_USB_INT_EPR:
+ ++int_count;
+ if (ao_usb_epr_ctr_tx(epr))
+ _ao_usb_set_stat_tx(AO_USB_INT_EPR, STM_USB_EPR_STAT_TX_NAK);
+ break;
+ }
+ return;
+ }
+
+ if (istr & (1 << STM_USB_ISTR_RESET)) {
+ ++reset_count;
+ ao_usb_ep0_receive |= AO_USB_EP0_GOT_RESET;
+ ao_usb_ep0_handle(ao_usb_ep0_receive);
+ }
+
+}
+
+/* Queue the current IN buffer for transmission */
+static void
+_ao_usb_in_send(void)
+{
+ _tx_dbg0("in_send start");
+ debug ("send %d\n", ao_usb_tx_count);
+ while (ao_usb_in_pending)
+ ao_sleep(&ao_usb_in_pending);
+ ao_usb_in_pending = 1;
+ if (ao_usb_tx_count != AO_USB_IN_SIZE)
+ ao_usb_in_flushed = 1;
+ ao_usb_copy_tx(ao_usb_tx_buffer, ao_usb_in_tx_buffer, ao_usb_tx_count);
+ ao_usb_bdt[AO_USB_IN_EPR].single.addr_tx = ao_usb_in_tx_offset;
+ ao_usb_bdt[AO_USB_IN_EPR].single.count_tx = ao_usb_tx_count;
+ ao_usb_tx_count = 0;
+ _ao_usb_set_stat_tx(AO_USB_IN_EPR, STM_USB_EPR_STAT_TX_VALID);
+ _tx_dbg0("in_send end");
+}
+
+/* Wait for a free IN buffer. Interrupts are blocked */
+static void
+_ao_usb_in_wait(void)
+{
+ for (;;) {
+ /* Check if the current buffer is writable */
+ if (ao_usb_tx_count < AO_USB_IN_SIZE)
+ break;
+
+ _tx_dbg0("in_wait top");
+ /* Wait for an IN buffer to be ready */
+ while (ao_usb_in_pending)
+ ao_sleep(&ao_usb_in_pending);
+ _tx_dbg0("in_wait bottom");
+ }
+}
+
+void
+ao_usb_flush(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_in_flushed) {
+ _tx_dbg0("flush top");
+ _ao_usb_in_send();
+ _tx_dbg0("flush end");
+ }
+ ao_arch_release_interrupts();
+}
+
+void
+ao_usb_putchar(char c)
+{
+ if (!ao_usb_running)
+ return;
+
+ ao_arch_block_interrupts();
+ _ao_usb_in_wait();
+
+ ao_usb_in_flushed = 0;
+ ao_usb_tx_buffer[ao_usb_tx_count++] = (uint8_t) c;
+
+ /* Send the packet when full */
+ if (ao_usb_tx_count == AO_USB_IN_SIZE) {
+ _tx_dbg0("putchar full");
+ _ao_usb_in_send();
+ _tx_dbg0("putchar flushed");
+ }
+ ao_arch_release_interrupts();
+}
+
+static void
+_ao_usb_out_recv(void)
+{
+ _rx_dbg0("out_recv top");
+ ao_usb_out_avail = 0;
+
+ ao_usb_rx_count = ao_usb_bdt[AO_USB_OUT_EPR].single.count_rx & STM_USB_BDT_COUNT_RX_COUNT_RX_MASK;
+
+ _rx_dbg1("out_recv count", ao_usb_rx_count);
+ debug ("recv %d\n", ao_usb_rx_count);
+ debug_data("Fill OUT len %d:", ao_usb_rx_count);
+ ao_usb_copy_rx(ao_usb_rx_buffer, ao_usb_out_rx_buffer, ao_usb_rx_count);
+ debug_data("\n");
+ ao_usb_rx_pos = 0;
+
+ /* ACK the packet */
+ _ao_usb_set_stat_rx(AO_USB_OUT_EPR, STM_USB_EPR_STAT_RX_VALID);
+}
+
+int
+_ao_usb_pollchar(void)
+{
+ uint8_t c;
+
+ if (!ao_usb_running)
+ return AO_READ_AGAIN;
+
+ for (;;) {
+ if (ao_usb_rx_pos != ao_usb_rx_count)
+ break;
+
+ _rx_dbg0("poll check");
+ /* Check to see if a packet has arrived */
+ if (!ao_usb_out_avail) {
+ _rx_dbg0("poll none");
+ return AO_READ_AGAIN;
+ }
+ _ao_usb_out_recv();
+ }
+
+ /* Pull a character out of the fifo */
+ c = ao_usb_rx_buffer[ao_usb_rx_pos++];
+ return c;
+}
+
+char
+ao_usb_getchar(void)
+{
+ int c;
+
+ ao_arch_block_interrupts();
+ while ((c = _ao_usb_pollchar()) == AO_READ_AGAIN)
+ ao_sleep(AO_USB_OUT_SLEEP_ADDR);
+ ao_arch_release_interrupts();
+ return c;
+}
+
+#if AO_USB_DIRECTIO
+uint16_t *
+ao_usb_alloc(void)
+{
+ uint16_t *buffer;
+
+ if (!ao_usb_running)
+ return NULL;
+ buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+ ao_usb_sram_addr += AO_USB_IN_SIZE;
+ return buffer;
+}
+
+void
+ao_usb_free(uint16_t *addr)
+{
+ uint16_t offset = ao_usb_packet_buffer_offset(addr);
+ if (offset < ao_usb_sram_addr)
+ ao_usb_sram_addr = offset;
+}
+
+void
+ao_usb_write(uint16_t *buffer, uint16_t len)
+{
+ ao_arch_block_interrupts();
+
+ /* Flush any pending regular */
+ if (ao_usb_tx_count)
+ _ao_usb_in_send();
+
+ while (ao_usb_in_pending)
+ ao_sleep(&ao_usb_in_pending);
+ ao_usb_in_pending = 1;
+ ao_usb_in_flushed = (len != AO_USB_IN_SIZE);
+ ao_usb_bdt[AO_USB_IN_EPR].single.addr_tx = ao_usb_packet_buffer_offset(buffer);
+ ao_usb_bdt[AO_USB_IN_EPR].single.count_tx = len;
+ _ao_usb_set_stat_tx(AO_USB_IN_EPR, STM_USB_EPR_STAT_TX_VALID);
+ ao_arch_release_interrupts();
+}
+#endif
+
+void
+ao_usb_disable(void)
+{
+ ao_arch_block_interrupts();
+ stm_usb.cntr = (1 << STM_USB_CNTR_FRES);
+ stm_usb.istr = 0;
+
+ /* Disable USB pull-up */
+ stm_usb.bcdr &= ~(1 << STM_USB_BCDR_DPPU);
+
+ /* Switch off the device */
+ stm_usb.cntr = (1 << STM_USB_CNTR_PDWN) | (1 << STM_USB_CNTR_FRES);
+
+ /* Disable the interface */
+ stm_rcc.apb1enr &= ~(1 << STM_RCC_APB1ENR_USBEN);
+ ao_arch_release_interrupts();
+}
+
+void
+ao_usb_enable(void)
+{
+ int t;
+
+ /* Select HSI48 as USB clock source */
+ stm_rcc.cfgr3 &= ~(1 << STM_RCC_CFGR3_USBSW);
+
+ /* Enable USB device */
+ stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_USBEN);
+
+ /* Clear reset condition */
+ stm_rcc.apb1rstr &= ~(1 << STM_RCC_APB1RSTR_USBRST);
+
+ /* Disable USB pull-up */
+ stm_usb.bcdr &= ~(1 << STM_USB_BCDR_DPPU);
+
+ /* Do not touch the GPIOA configuration; USB takes priority
+ * over GPIO on pins A11 and A12, but if you select alternate
+ * input 10 (the documented correct selection), then USB is
+ * pulled low and doesn't work at all
+ */
+
+ ao_arch_block_interrupts();
+
+ /* Route interrupts */
+ stm_nvic_set_enable(STM_ISR_USB_POS);
+ stm_nvic_set_priority(STM_ISR_USB_POS, 3);
+
+ ao_usb_configuration = 0;
+
+ /* Set up buffer descriptors */
+ ao_usb_init_btable();
+
+ /* Reset the USB controller */
+ stm_usb.cntr = (1 << STM_USB_CNTR_FRES);
+
+ /* Clear the reset bit */
+ stm_usb.cntr = 0;
+
+ /* Clear any spurious interrupts */
+ stm_usb.istr = 0;
+
+ ao_usb_set_ep0();
+
+ debug ("ao_usb_enable\n");
+
+ /* Enable interrupts */
+ stm_usb.cntr = ((1 << STM_USB_CNTR_CTRM) |
+ (0 << STM_USB_CNTR_PMAOVRM) |
+ (0 << STM_USB_CNTR_ERRM) |
+ (0 << STM_USB_CNTR_WKUPM) |
+ (0 << STM_USB_CNTR_SUSPM) |
+ (1 << STM_USB_CNTR_RESETM) |
+ (0 << STM_USB_CNTR_SOFM) |
+ (0 << STM_USB_CNTR_ESOFM) |
+ (0 << STM_USB_CNTR_RESUME) |
+ (0 << STM_USB_CNTR_FSUSP) |
+ (0 << STM_USB_CNTR_LP_MODE) |
+ (0 << STM_USB_CNTR_PDWN) |
+ (0 << STM_USB_CNTR_FRES));
+
+ ao_arch_release_interrupts();
+
+ for (t = 0; t < 1000; t++)
+ ao_arch_nop();
+
+ /* Enable USB pull-up */
+ stm_usb.bcdr |= (1 << STM_USB_BCDR_DPPU);
+}
+
+#if USB_ECHO
+struct ao_task ao_usb_echo_task;
+
+static void
+ao_usb_echo(void)
+{
+ char c;
+
+ for (;;) {
+ c = ao_usb_getchar();
+ ao_usb_putchar(c);
+ ao_usb_flush();
+ }
+}
+#endif
+
+#if USB_DEBUG
+static void
+ao_usb_irq(void)
+{
+ printf ("control: %d out: %d in: %d int: %d reset: %d\n",
+ control_count, out_count, in_count, int_count, reset_count);
+}
+
+__code struct ao_cmds ao_usb_cmds[] = {
+ { ao_usb_irq, "I\0Show USB interrupt counts" },
+ { 0, NULL }
+};
+#endif
+
+void
+ao_usb_init(void)
+{
+ /* Turn on syscfg */
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SYSCFGCOMPEN);
+
+ /* Set PA11/PA12 remapping bit */
+ stm_syscfg.cfgr1 |= (AO_PA11_PA12_RMP << STM_SYSCFG_CFGR1_PA11_PA12_RMP);
+
+ ao_usb_enable();
+
+ debug ("ao_usb_init\n");
+ ao_usb_ep0_state = AO_USB_EP0_IDLE;
+#if USB_ECHO
+ ao_add_task(&ao_usb_echo_task, ao_usb_echo, "usb echo");
+#endif
+#if USB_DEBUG
+ ao_cmd_register(&ao_usb_cmds[0]);
+#endif
+#if !USB_ECHO
+#if USE_USB_STDIO
+ ao_add_stdio(_ao_usb_pollchar, ao_usb_putchar, ao_usb_flush);
+#endif
+#endif
+}
+
+#if TX_DBG || RX_DBG
+
+struct ao_usb_dbg {
+ int line;
+ char *msg;
+ uint32_t value;
+ uint32_t primask;
+#if TX_DBG
+ uint16_t in_count;
+ uint32_t in_epr;
+ uint32_t in_pending;
+ uint32_t tx_count;
+ uint32_t in_flushed;
+#endif
+#if RX_DBG
+ uint8_t rx_count;
+ uint8_t rx_pos;
+ uint8_t out_avail;
+ uint32_t out_epr;
+#endif
+};
+
+#define NUM_USB_DBG 128
+
+static struct ao_usb_dbg dbg[128];
+static int dbg_i;
+
+static void _dbg(int line, char *msg, uint32_t value)
+{
+ uint32_t primask;
+ dbg[dbg_i].line = line;
+ dbg[dbg_i].msg = msg;
+ dbg[dbg_i].value = value;
+ asm("mrs %0,primask" : "=&r" (primask));
+ dbg[dbg_i].primask = primask;
+#if TX_DBG
+ dbg[dbg_i].in_count = in_count;
+ dbg[dbg_i].in_epr = stm_usb.epr[AO_USB_IN_EPR];
+ dbg[dbg_i].in_pending = ao_usb_in_pending;
+ dbg[dbg_i].tx_count = ao_usb_tx_count;
+ dbg[dbg_i].in_flushed = ao_usb_in_flushed;
+#endif
+#if RX_DBG
+ dbg[dbg_i].rx_count = ao_usb_rx_count;
+ dbg[dbg_i].rx_pos = ao_usb_rx_pos;
+ dbg[dbg_i].out_avail = ao_usb_out_avail;
+ dbg[dbg_i].out_epr = stm_usb.epr[AO_USB_OUT_EPR];
+#endif
+ if (++dbg_i == NUM_USB_DBG)
+ dbg_i = 0;
+}
+#endif
diff --git a/src/stmf0/registers.ld b/src/stmf0/registers.ld
new file mode 100644
index 00000000..598fc1af
--- /dev/null
+++ b/src/stmf0/registers.ld
@@ -0,0 +1,57 @@
+stm_gpiof = 0x48001400;
+stm_gpioc = 0x48000800;
+stm_gpiob = 0x48000400;
+stm_gpioa = 0x48000000;
+
+stm_tsc = 0x40024000;
+stm_crc = 0x40023000;
+stm_flash = 0x40022000;
+stm_rcc = 0x40021000;
+stm_dma = 0x40020000;
+stm_dbgmcu = 0x40015800;
+stm_tim17 = 0x40014800;
+stm_tim16 = 0x40014400;
+stm_usart1 = 0x40013800;
+stm_spi1 = 0x40013000;
+stm_tim1 = 0x40012c00;
+stm_adc = 0x40012400;
+
+stm_exti = 0x40010400;
+stm_syscfg = 0x40010000;
+
+stm_cec = 0x40007800;
+
+stm_pwr = 0x40007000;
+stm_crs = 0x40006c00;
+
+stm_bxcan = 0x40006400;
+stm_usb_sram = 0x40006000;
+stm_usb = 0x40005c00;
+
+stm_i2c1 = 0x40005400;
+
+stm_usart2 = 0x40004400;
+
+stm_spi2 = 0x40003800; /* docs are broken here */
+
+stm_iwdg = 0x40003000;
+stm_wwdg = 0x40002c00;
+stm_rtc = 0x40002800;
+
+stm_tim14 = 0x40002000;
+
+stm_tim3 = 0x40000400;
+stm_tim2 = 0x40000000;
+
+stm_systick = 0xe000e010;
+
+stm_nvic = 0xe000e100;
+
+stm_scb = 0xe000ed00;
+
+stm_mpu = 0xe000ed90;
+
+/* calibration data in system memory */
+stm_cal = 0x1ffff7b8;
+stm_flash_size_04x = 0x1ffff7cc;
+stm_device_id = 0x1ff80050;
diff --git a/src/stmf0/stm32f0.h b/src/stmf0/stm32f0.h
new file mode 100644
index 00000000..ce8ca456
--- /dev/null
+++ b/src/stmf0/stm32f0.h
@@ -0,0 +1,1667 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * 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 _STM32F0_H_
+#define _STM32F0_H_
+
+#include <stdint.h>
+
+typedef volatile uint32_t vuint32_t;
+typedef volatile void * vvoid_t;
+typedef volatile uint16_t vuint16_t;
+typedef volatile uint8_t vuint8_t;
+
+struct stm_gpio {
+ vuint32_t moder;
+ vuint32_t otyper;
+ vuint32_t ospeedr;
+ vuint32_t pupdr;
+
+ vuint32_t idr;
+ vuint32_t odr;
+ vuint32_t bsrr;
+ vuint32_t lckr;
+
+ vuint32_t afrl;
+ vuint32_t afrh;
+ vuint32_t brr;
+};
+
+#define STM_MODER_SHIFT(pin) ((pin) << 1)
+#define STM_MODER_MASK 3
+#define STM_MODER_INPUT 0
+#define STM_MODER_OUTPUT 1
+#define STM_MODER_ALTERNATE 2
+#define STM_MODER_ANALOG 3
+
+static inline void
+stm_moder_set(struct stm_gpio *gpio, int pin, vuint32_t value) {
+ gpio->moder = ((gpio->moder &
+ ~(STM_MODER_MASK << STM_MODER_SHIFT(pin))) |
+ value << STM_MODER_SHIFT(pin));
+}
+
+static inline uint32_t
+stm_moder_get(struct stm_gpio *gpio, int pin) {
+ return (gpio->moder >> STM_MODER_SHIFT(pin)) & STM_MODER_MASK;
+}
+
+#define STM_OTYPER_SHIFT(pin) (pin)
+#define STM_OTYPER_MASK 1
+#define STM_OTYPER_PUSH_PULL 0
+#define STM_OTYPER_OPEN_DRAIN 1
+
+static inline void
+stm_otyper_set(struct stm_gpio *gpio, int pin, vuint32_t value) {
+ gpio->otyper = ((gpio->otyper &
+ ~(STM_OTYPER_MASK << STM_OTYPER_SHIFT(pin))) |
+ value << STM_OTYPER_SHIFT(pin));
+}
+
+static inline uint32_t
+stm_otyper_get(struct stm_gpio *gpio, int pin) {
+ return (gpio->otyper >> STM_OTYPER_SHIFT(pin)) & STM_OTYPER_MASK;
+}
+
+#define STM_OSPEEDR_SHIFT(pin) ((pin) << 1)
+#define STM_OSPEEDR_MASK 3
+#define STM_OSPEEDR_LOW 0 /* 2MHz */
+#define STM_OSPEEDR_MEDIUM 1 /* 10MHz */
+#define STM_OSPEEDR_HIGH 3 /* 10-50MHz */
+
+static inline void
+stm_ospeedr_set(struct stm_gpio *gpio, int pin, vuint32_t value) {
+ gpio->ospeedr = ((gpio->ospeedr &
+ ~(STM_OSPEEDR_MASK << STM_OSPEEDR_SHIFT(pin))) |
+ value << STM_OSPEEDR_SHIFT(pin));
+}
+
+static inline uint32_t
+stm_ospeedr_get(struct stm_gpio *gpio, int pin) {
+ return (gpio->ospeedr >> STM_OSPEEDR_SHIFT(pin)) & STM_OSPEEDR_MASK;
+}
+
+#define STM_PUPDR_SHIFT(pin) ((pin) << 1)
+#define STM_PUPDR_MASK 3
+#define STM_PUPDR_NONE 0
+#define STM_PUPDR_PULL_UP 1
+#define STM_PUPDR_PULL_DOWN 2
+#define STM_PUPDR_RESERVED 3
+
+static inline void
+stm_pupdr_set(struct stm_gpio *gpio, int pin, uint32_t value) {
+ gpio->pupdr = ((gpio->pupdr &
+ ~(STM_PUPDR_MASK << STM_PUPDR_SHIFT(pin))) |
+ value << STM_PUPDR_SHIFT(pin));
+}
+
+static inline uint32_t
+stm_pupdr_get(struct stm_gpio *gpio, int pin) {
+ return (gpio->pupdr >> STM_PUPDR_SHIFT(pin)) & STM_PUPDR_MASK;
+}
+
+#define STM_AFR_SHIFT(pin) ((pin) << 2)
+#define STM_AFR_MASK 0xf
+#define STM_AFR_NONE 0
+#define STM_AFR_AF0 0x0
+#define STM_AFR_AF1 0x1
+#define STM_AFR_AF2 0x2
+#define STM_AFR_AF3 0x3
+#define STM_AFR_AF4 0x4
+#define STM_AFR_AF5 0x5
+#define STM_AFR_AF6 0x6
+#define STM_AFR_AF7 0x7
+
+static inline void
+stm_afr_set(struct stm_gpio *gpio, int pin, uint32_t value) {
+ /*
+ * Set alternate pin mode too
+ */
+ stm_moder_set(gpio, pin, STM_MODER_ALTERNATE);
+ if (pin < 8)
+ gpio->afrl = ((gpio->afrl &
+ ~(STM_AFR_MASK << STM_AFR_SHIFT(pin))) |
+ value << STM_AFR_SHIFT(pin));
+ else {
+ pin -= 8;
+ gpio->afrh = ((gpio->afrh &
+ ~(STM_AFR_MASK << STM_AFR_SHIFT(pin))) |
+ value << STM_AFR_SHIFT(pin));
+ }
+}
+
+static inline uint32_t
+stm_afr_get(struct stm_gpio *gpio, int pin) {
+ if (pin < 8)
+ return (gpio->afrl >> STM_AFR_SHIFT(pin)) & STM_AFR_MASK;
+ else {
+ pin -= 8;
+ return (gpio->afrh >> STM_AFR_SHIFT(pin)) & STM_AFR_MASK;
+ }
+}
+
+static inline void
+stm_gpio_set(struct stm_gpio *gpio, int pin, uint8_t value) {
+ /* Use the bit set/reset register to do this atomically */
+ gpio->bsrr = ((uint32_t) (value ^ 1) << (pin + 16)) | ((uint32_t) value << pin);
+}
+
+static inline uint8_t
+stm_gpio_get(struct stm_gpio *gpio, int pin) {
+ return (gpio->idr >> pin) & 1;
+}
+
+static inline uint16_t
+stm_gpio_get_all(struct stm_gpio *gpio) {
+ return gpio->idr;
+}
+
+/*
+ * We can't define these in registers.ld or our fancy
+ * ao_enable_gpio macro will expand into a huge pile of code
+ * as the compiler won't do correct constant folding and
+ * dead-code elimination
+ */
+
+extern struct stm_gpio stm_gpioa;
+extern struct stm_gpio stm_gpiob;
+extern struct stm_gpio stm_gpioc;
+extern struct stm_gpio stm_gpiof;
+
+#define stm_gpiof (*((struct stm_gpio *) 0x48001400))
+#define stm_gpioc (*((struct stm_gpio *) 0x48000800))
+#define stm_gpiob (*((struct stm_gpio *) 0x48000400))
+#define stm_gpioa (*((struct stm_gpio *) 0x48000000))
+
+/* Flash interface */
+
+struct stm_flash {
+ vuint32_t acr;
+ vuint32_t keyr;
+ vuint32_t optkeyr;
+ vuint32_t sr;
+
+ vuint32_t cr;
+ vuint32_t ar;
+ vuint32_t unused_0x18;
+ vuint32_t obr;
+
+ vuint32_t wrpr;
+};
+
+extern struct stm_flash stm_flash;
+
+#define STM_FLASH_ACR_PRFTBS (5)
+#define STM_FLASH_ACR_PRFTBE (4)
+#define STM_FLASH_ACR_LATENCY (0)
+#define STM_FLASH_ACR_LATENCY_0 0
+#define STM_FLASH_ACR_LATENCY_1 1
+
+#define STM_FLASH_PECR_OBL_LAUNCH 18
+#define STM_FLASH_PECR_ERRIE 17
+#define STM_FLASH_PECR_EOPIE 16
+#define STM_FLASH_PECR_FPRG 10
+#define STM_FLASH_PECR_ERASE 9
+#define STM_FLASH_PECR_FTDW 8
+#define STM_FLASH_PECR_DATA 4
+#define STM_FLASH_PECR_PROG 3
+#define STM_FLASH_PECR_OPTLOCK 2
+#define STM_FLASH_PECR_PRGLOCK 1
+#define STM_FLASH_PECR_PELOCK 0
+
+#define STM_FLASH_SR_EOP 5
+#define STM_FLASH_SR_WRPRTERR 4
+#define STM_FLASH_SR_PGERR 2
+#define STM_FLASH_SR_BSY 0
+
+#define STM_FLASH_CR_OBL_LAUNCH 13
+#define STM_FLASH_CR_EOPIE 12
+#define STM_FLASH_CR_ERRIE 10
+#define STM_FLASH_CR_OPTWRE 9
+#define STM_FLASH_CR_LOCK 7
+#define STM_FLASH_CR_STRT 6
+#define STM_FLASH_CR_OPTER 5
+#define STM_FLASH_CR_OPTPG 4
+#define STM_FLASH_CR_MER 2
+#define STM_FLASH_CR_PER 1
+#define STM_FLASH_CR_PG 0
+
+#define STM_FLASH_OBR_DATA1 24
+#define STM_FLASH_OBR_DATA0 16
+#define STM_FLASH_OBR_BOOT_SEL 15
+#define STM_FLASH_OBR_RAM_PARITY_CHECK 14
+#define STM_FLASH_OBR_VDDA_MONITOR 13
+#define STM_FLASH_OBR_NBOOT1 12
+#define STM_FLASH_OBR_NBOOT0 11
+#define STM_FLASH_OBR_NRST_STDBY 10
+#define STM_FLASH_OBR_NRST_STOP 9
+#define STM_FLASH_OBR_WDG_SW 8
+#define STM_FLASH_OBR_RDPRT 1
+#define STM_FLASH_OBR_RDPRT_LEVEL0 0
+#define STM_FLASH_OBR_RDPRT_LEVEL1 1
+#define STM_FLASH_OBR_RDPRT_LEVEL2 3
+#define STM_FLASH_OBR_OPTERR 0
+
+#define STM_FLASH_KEYR_KEY1 0x45670123
+#define STM_FLASH_KEYR_KEY2 0xcdef89ab
+
+struct stm_rcc {
+ vuint32_t cr;
+ vuint32_t cfgr;
+ vuint32_t cir;
+ vuint32_t apb2rstr;
+
+ vuint32_t apb1rstr;
+ vuint32_t ahbenr;
+ vuint32_t apb2enr;
+ vuint32_t apb1enr;
+
+ vuint32_t bdcr;
+ vuint32_t csr;
+ vuint32_t ahbrstr;
+ vuint32_t cfgr2;
+
+ vuint32_t cfgr3;
+ vuint32_t cr2;
+};
+
+extern struct stm_rcc stm_rcc;
+
+/* Nominal high speed internal oscillator frequency is 16MHz */
+#define STM_HSI_FREQ 16000000
+
+#define STM_RCC_CR_PLLRDY (25)
+#define STM_RCC_CR_PLLON (24)
+#define STM_RCC_CR_CSSON (19)
+#define STM_RCC_CR_HSEBYP (18)
+#define STM_RCC_CR_HSERDY (17)
+#define STM_RCC_CR_HSEON (16)
+#define STM_RCC_CR_HSICAL (8)
+#define STM_RCC_CR_HSITRIM (3)
+#define STM_RCC_CR_HSIRDY (1)
+#define STM_RCC_CR_HSION (0)
+
+#define STM_RCC_CFGR_PLL_NODIV (31)
+#define STM_RCC_CFGR_PLL_NODIV_DIV_1 1
+#define STM_RCC_CFGR_PLL_NODIV_DIV_2 0
+
+#define STM_RCC_CFGR_MCOPRE (28)
+#define STM_RCC_CFGR_MCOPRE_DIV_1 0
+#define STM_RCC_CFGR_MCOPRE_DIV_2 1
+#define STM_RCC_CFGR_MCOPRE_DIV_4 2
+#define STM_RCC_CFGR_MCOPRE_DIV_8 3
+#define STM_RCC_CFGR_MCOPRE_DIV_16 4
+#define STM_RCC_CFGR_MCOPRE_DIV_32 5
+#define STM_RCC_CFGR_MCOPRE_DIV_64 6
+#define STM_RCC_CFGR_MCOPRE_DIV_128 7
+#define STM_RCC_CFGR_MCOPRE_DIV_MASK 7
+
+#define STM_RCC_CFGR_MCO (24)
+# define STM_RCC_CFGR_MCO_DISABLE 0
+
+#define STM_RCC_CFGR_PLLMUL (18)
+#define STM_RCC_CFGR_PLLMUL_2 0
+#define STM_RCC_CFGR_PLLMUL_3 1
+#define STM_RCC_CFGR_PLLMUL_4 2
+#define STM_RCC_CFGR_PLLMUL_5 3
+#define STM_RCC_CFGR_PLLMUL_6 4
+#define STM_RCC_CFGR_PLLMUL_7 5
+#define STM_RCC_CFGR_PLLMUL_8 6
+#define STM_RCC_CFGR_PLLMUL_9 7
+#define STM_RCC_CFGR_PLLMUL_10 8
+#define STM_RCC_CFGR_PLLMUL_11 9
+#define STM_RCC_CFGR_PLLMUL_12 10
+#define STM_RCC_CFGR_PLLMUL_13 11
+#define STM_RCC_CFGR_PLLMUL_14 12
+#define STM_RCC_CFGR_PLLMUL_15 13
+#define STM_RCC_CFGR_PLLMUL_16 14
+#define STM_RCC_CFGR_PLLMUL_MASK 0xf
+
+#define STM_RCC_CFGR_PLLXTPRE (17)
+
+#define STM_RCC_CFGR_PLLSRC (15)
+# define STM_RCC_CFGR_PLLSRC_HSI_DIV_2 0
+# define STM_RCC_CFGR_PLLSRC_HSI 1
+# define STM_RCC_CFGR_PLLSRC_HSE 2
+# define STM_RCC_CFGR_PLLSRC_HSI48 3
+
+#define STM_RCC_CFGR_ADCPRE (14)
+
+#define STM_RCC_CFGR_PPRE (8)
+#define STM_RCC_CFGR_PPRE_DIV_1 0
+#define STM_RCC_CFGR_PPRE_DIV_2 4
+#define STM_RCC_CFGR_PPRE_DIV_4 5
+#define STM_RCC_CFGR_PPRE_DIV_8 6
+#define STM_RCC_CFGR_PPRE_DIV_16 7
+#define STM_RCC_CFGR_PPRE_MASK 7
+
+#define STM_RCC_CFGR_HPRE (4)
+#define STM_RCC_CFGR_HPRE_DIV_1 0
+#define STM_RCC_CFGR_HPRE_DIV_2 8
+#define STM_RCC_CFGR_HPRE_DIV_4 9
+#define STM_RCC_CFGR_HPRE_DIV_8 0xa
+#define STM_RCC_CFGR_HPRE_DIV_16 0xb
+#define STM_RCC_CFGR_HPRE_DIV_64 0xc
+#define STM_RCC_CFGR_HPRE_DIV_128 0xd
+#define STM_RCC_CFGR_HPRE_DIV_256 0xe
+#define STM_RCC_CFGR_HPRE_DIV_512 0xf
+#define STM_RCC_CFGR_HPRE_MASK 0xf
+
+#define STM_RCC_CFGR_SWS (2)
+#define STM_RCC_CFGR_SWS_HSI 0
+#define STM_RCC_CFGR_SWS_HSE 1
+#define STM_RCC_CFGR_SWS_PLL 2
+#define STM_RCC_CFGR_SWS_HSI48 3
+#define STM_RCC_CFGR_SWS_MASK 3
+
+#define STM_RCC_CFGR_SW (0)
+#define STM_RCC_CFGR_SW_HSI 0
+#define STM_RCC_CFGR_SW_HSE 1
+#define STM_RCC_CFGR_SW_PLL 2
+#define STM_RCC_CFGR_SW_HSI48 3
+#define STM_RCC_CFGR_SW_MASK 3
+
+#define STM_RCC_APB2RSTR_DBGMCURST 22
+#define STM_RCC_APB2RSTR_TIM17RST 18
+#define STM_RCC_APB2RSTR_TIM16RST 17
+#define STM_RCC_APB2RSTR_TIM15RST 16
+#define STM_RCC_APB2RSTR_USART1RST 14
+#define STM_RCC_APB2RSTR_SPI1RST 12
+#define STM_RCC_APB2RSTR_TIM1RST 11
+#define STM_RCC_APB2RSTR_ADCRST 9
+#define STM_RCC_APB2RSTR_USART8RST 7
+#define STM_RCC_APB2RSTR_USART7RST 6
+#define STM_RCC_APB2RSTR_USART6RST 5
+#define STM_RCC_APB2RSTR_SYSCFGRST 1
+
+#define STM_RCC_APB1RSTR_CECRST 30
+#define STM_RCC_APB1RSTR_DACRST 29
+#define STM_RCC_APB1RSTR_PWRRST 28
+#define STM_RCC_APB1RSTR_CRSRST 27
+#define STM_RCC_APB1RSTR_CANRST 25
+#define STM_RCC_APB1RSTR_USBRST 23
+#define STM_RCC_APB1RSTR_I2C2RST 22
+#define STM_RCC_APB1RSTR_I1C1RST 21
+#define STM_RCC_APB1RSTR_USART5RST 20
+#define STM_RCC_APB1RSTR_USART4RST 19
+#define STM_RCC_APB1RSTR_USART3RST 18
+#define STM_RCC_APB1RSTR_USART2RST 17
+#define STM_RCC_APB1RSTR_SPI2RST 14
+#define STM_RCC_APB1RSTR_WWDGRST 11
+#define STM_RCC_APB1RSTR_TIM14RST 8
+#define STM_RCC_APB1RSTR_TIM7RST 5
+#define STM_RCC_APB1RSTR_TIM6RST 4
+#define STM_RCC_APB1RSTR_TIM3RST 1
+#define STM_RCC_APB1RSTR_TIM2RST 0
+
+#define STM_RCC_AHBENR_TSCEN 24
+#define STM_RCC_AHBENR_IOPFEN 22
+#define STM_RCC_AHBENR_IOPEEN 21
+#define STM_RCC_AHBENR_IOPDEN 20
+#define STM_RCC_AHBENR_IOPCEN 19
+#define STM_RCC_AHBENR_IOPBEN 18
+#define STM_RCC_AHBENR_IOPAEN 17
+#define STM_RCC_AHBENR_CRCEN 6
+#define STM_RCC_AHBENR_FLITFEN 4
+#define STM_RCC_AHBENR_SRAMEN 2
+#define STM_RCC_AHBENR_DMA2EN 1
+#define STM_RCC_AHBENR_DMAEN 0
+
+#define STM_RCC_APB2ENR_DBGMCUEN 22
+#define STM_RCC_APB2ENR_TIM17EN 18
+#define STM_RCC_APB2ENR_TIM16EN 17
+#define STM_RCC_APB2ENR_TIM15EN 16
+#define STM_RCC_APB2ENR_USART1EN 14
+#define STM_RCC_APB2ENR_SPI1EN 12
+#define STM_RCC_APB2ENR_TIM1EN 11
+#define STM_RCC_APB2ENR_ADCEN 9
+#define STM_RCC_APB2ENR_USART8EN 7
+#define STM_RCC_APB2ENR_USART7EN 6
+#define STM_RCC_APB2ENR_USART6EN 5
+#define STM_RCC_APB2ENR_SYSCFGCOMPEN 0
+
+#define STM_RCC_APB1ENR_CECEN 30
+#define STM_RCC_APB1ENR_DACEN 29
+#define STM_RCC_APB1ENR_PWREN 28
+#define STM_RCC_APB1ENR_CRSEN 27
+#define STM_RCC_APB1ENR_CANEN 25
+#define STM_RCC_APB1ENR_USBEN 23
+#define STM_RCC_APB1ENR_I2C2EN 22
+#define STM_RCC_APB1ENR_IC21EN 21
+#define STM_RCC_APB1ENR_USART5EN 20
+#define STM_RCC_APB1ENR_USART4EN 19
+#define STM_RCC_APB1ENR_USART3EN 18
+#define STM_RCC_APB1ENR_USART2EN 17
+#define STM_RCC_APB1ENR_SPI2EN 14
+#define STM_RCC_APB1ENR_WWDGEN 11
+#define STM_RCC_APB1ENR_TIM14EN 8
+#define STM_RCC_APB1ENR_TIM7EN 5
+#define STM_RCC_APB1ENR_TIM6EN 4
+#define STM_RCC_APB1ENR_TIM3EN 1
+#define STM_RCC_APB1ENR_TIM2EN 0
+
+#define STM_RCC_CSR_LPWRRSTF (31)
+#define STM_RCC_CSR_WWDGRSTF (30)
+#define STM_RCC_CSR_IWDGRSTF (29)
+#define STM_RCC_CSR_SFTRSTF (28)
+#define STM_RCC_CSR_PORRSTF (27)
+#define STM_RCC_CSR_PINRSTF (26)
+#define STM_RCC_CSR_OBLRSTF (25)
+#define STM_RCC_CSR_RMVF (24)
+#define STM_RCC_CSR_V18PWRRSTF (23)
+#define STM_RCC_CSR_LSIRDY (1)
+#define STM_RCC_CSR_LSION (0)
+
+#define STM_RCC_CR2_HSI48CAL 24
+#define STM_RCC_CR2_HSI48RDY 17
+#define STM_RCC_CR2_HSI48ON 16
+#define STM_RCC_CR2_HSI14CAL 8
+#define STM_RCC_CR2_HSI14TRIM 3
+#define STM_RCC_CR2_HSI14DIS 2
+#define STM_RCC_CR2_HSI14RDY 1
+#define STM_RCC_CR2_HSI14ON 0
+
+#define STM_RCC_CFGR3_USART3SW 18
+#define STM_RCC_CFGR3_USART2SW 16
+#define STM_RCC_CFGR3_ADCSW 8
+#define STM_RCC_CFGR3_USBSW 7
+#define STM_RCC_CFGR3_CECSW 6
+#define STM_RCC_CFGR3_I2C1SW 4
+#define STM_RCC_CFGR3_USART1SW 0
+
+struct stm_crs {
+ vuint32_t cr;
+ vuint32_t cfgr;
+ vuint32_t isr;
+ vuint32_t icr;
+};
+
+extern struct stm_crs stm_crs;
+
+#define STM_CRS_CR_TRIM 8
+#define STM_CRS_CR_SWSYNC 7
+#define STM_CRS_CR_AUTOTRIMEN 6
+#define STM_CRS_CR_CEN 5
+#define STM_CRS_CR_ESYNCIE 3
+#define STM_CRS_CR_ERRIE 2
+#define STM_CRS_CR_SYNCWARNIE 1
+#define STM_CRS_CR_SYNCOKIE 0
+
+#define STM_CRS_CFGR_SYNCPOL 31
+#define STM_CRS_CFGR_SYNCSRC 28
+#define STM_CRS_CFGR_SYNCSRC_GPIO 0
+#define STM_CRS_CFGR_SYNCSRC_LSE 1
+#define STM_CRS_CFGR_SYNCSRC_USB 2
+#define STM_CRS_CFGR_SYNCDIV 24
+#define STM_CRS_CFGR_SYNCDIV_1 0
+#define STM_CRS_CFGR_SYNCDIV_2 1
+#define STM_CRS_CFGR_SYNCDIV_4 2
+#define STM_CRS_CFGR_SYNCDIV_8 3
+#define STM_CRS_CFGR_SYNCDIV_16 4
+#define STM_CRS_CFGR_SYNCDIV_32 5
+#define STM_CRS_CFGR_SYNCDIV_64 6
+#define STM_CRS_CFGR_SYNCDIV_128 7
+#define STM_CRS_CFGR_FELIM 16
+#define STM_CRS_CFGR_RELOAD 0
+
+#define STM_CRS_ISR_FECAP 16
+#define STM_CRS_ISR_FEDIR 15
+#define STM_CRS_ISR_TRIMOVF 10
+#define STM_CRS_ISR_SYNCMISS 9
+#define STM_CRS_ISR_SYNCERR 8
+#define STM_CRS_ISR_ESYNCF 3
+#define STM_CRS_ISR_ERRF 2
+#define STM_CRS_ISR_SYNCWARNF 1
+#define STM_CRS_ISR_SYNCOKF 0
+
+#define STM_CRS_ICR_ESYNCC 3
+#define STM_CRS_ICR_ERRC 2
+#define STM_CRS_ICR_SYNCWARNC 1
+#define STM_CRS_ICR_SYNCOKC 0
+
+struct stm_pwr {
+ vuint32_t cr;
+ vuint32_t csr;
+};
+
+extern struct stm_pwr stm_pwr;
+
+#define STM_PWR_CR_DBP (8)
+
+#define STM_PWR_CR_PLS (5)
+#define STM_PWR_CR_PLS_2_0 0
+#define STM_PWR_CR_PLS_2_1 1
+#define STM_PWR_CR_PLS_2_2 2
+#define STM_PWR_CR_PLS_2_3 3
+#define STM_PWR_CR_PLS_2_4 4
+#define STM_PWR_CR_PLS_2_5 5
+#define STM_PWR_CR_PLS_2_6 6
+#define STM_PWR_CR_PLS_EXT 7
+#define STM_PWR_CR_PLS_MASK 7
+
+#define STM_PWR_CR_PVDE (4)
+#define STM_PWR_CR_CSBF (3)
+#define STM_PWR_CR_CWUF (2)
+#define STM_PWR_CR_PDDS (1)
+#define STM_PWR_CR_LPSDSR (0)
+
+#define STM_PWR_CSR_EWUP3 (10)
+#define STM_PWR_CSR_EWUP2 (9)
+#define STM_PWR_CSR_EWUP1 (8)
+#define STM_PWR_CSR_REGLPF (5)
+#define STM_PWR_CSR_VOSF (4)
+#define STM_PWR_CSR_VREFINTRDYF (3)
+#define STM_PWR_CSR_PVDO (2)
+#define STM_PWR_CSR_SBF (1)
+#define STM_PWR_CSR_WUF (0)
+
+struct stm_crc {
+ union {
+ vuint32_t u32;
+ vuint16_t u16;
+ vuint8_t u8;
+ } dr;
+ vuint32_t idr;
+ vuint32_t cr;
+ uint32_t _0c;
+
+ vuint32_t init;
+ vuint32_t pol;
+};
+
+extern struct stm_crc stm_crc;
+
+#define stm_crc (*((struct stm_crc *) 0x40023000))
+
+#define STM_CRC_CR_REV_OUT 7
+#define STM_CRC_CR_REV_IN 5
+#define STM_CRC_CR_REV_IN_NONE 0
+#define STM_CRC_CR_REV_IN_BY_BYTE 1
+#define STM_CRC_CR_REV_IN_BY_HALF_WORD 2
+#define STM_CRC_CR_REV_IN_BY_WORD 3
+#define STM_CRC_CR_POLYSIZE 3
+#define STM_CRC_CR_POLYSIZE_32 0
+#define STM_CRC_CR_POLYSIZE_16 1
+#define STM_CRC_CR_POLYSIZE_8 2
+#define STM_CRC_CR_POLYSIZE_7 3
+#define STM_CRC_CR_RESET 0
+
+/* The SYSTICK starts at 0xe000e010 */
+
+struct stm_systick {
+ vuint32_t csr;
+ vuint32_t rvr;
+ vuint32_t cvr;
+ vuint32_t calib;
+};
+
+extern struct stm_systick stm_systick;
+
+#define STM_SYSTICK_CSR_ENABLE 0
+#define STM_SYSTICK_CSR_TICKINT 1
+#define STM_SYSTICK_CSR_CLKSOURCE 2
+#define STM_SYSTICK_CSR_CLKSOURCE_EXTERNAL 0
+#define STM_SYSTICK_CSR_CLKSOURCE_HCLK_8 1
+#define STM_SYSTICK_CSR_COUNTFLAG 16
+
+/* The NVIC starts at 0xe000e100, so add that to the offsets to find the absolute address */
+
+struct stm_nvic {
+ vuint32_t iser; /* 0x000 0xe000e100 Set Enable Register */
+
+ uint8_t _unused020[0x080 - 0x004];
+
+ vuint32_t icer; /* 0x080 0xe000e180 Clear Enable Register */
+
+ uint8_t _unused0a0[0x100 - 0x084];
+
+ vuint32_t ispr; /* 0x100 0xe000e200 Set Pending Register */
+
+ uint8_t _unused120[0x180 - 0x104];
+
+ vuint32_t icpr; /* 0x180 0xe000e280 Clear Pending Register */
+
+ uint8_t _unused1a0[0x300 - 0x184];
+
+ vuint32_t ipr[8]; /* 0x300 0xe000e400 Priority Register */
+};
+
+extern struct stm_nvic stm_nvic;
+
+#define IRQ_MASK(irq) (1 << (irq))
+#define IRQ_BOOL(v,irq) (((v) >> (irq)) & 1)
+
+static inline void
+stm_nvic_set_enable(int irq) {
+ stm_nvic.iser = IRQ_MASK(irq);
+}
+
+static inline void
+stm_nvic_clear_enable(int irq) {
+ stm_nvic.icer = IRQ_MASK(irq);
+}
+
+static inline int
+stm_nvic_enabled(int irq) {
+ return IRQ_BOOL(stm_nvic.iser, irq);
+}
+
+static inline void
+stm_nvic_set_pending(int irq) {
+ stm_nvic.ispr = IRQ_MASK(irq);
+}
+
+static inline void
+stm_nvic_clear_pending(int irq) {
+ stm_nvic.icpr = IRQ_MASK(irq);
+}
+
+static inline int
+stm_nvic_pending(int irq) {
+ return IRQ_BOOL(stm_nvic.ispr, irq);
+}
+
+#define IRQ_PRIO_REG(irq) ((irq) >> 2)
+#define IRQ_PRIO_BIT(irq) (((irq) & 3) << 3)
+#define IRQ_PRIO_MASK(irq) (0xff << IRQ_PRIO_BIT(irq))
+
+static inline void
+stm_nvic_set_priority(int irq, uint8_t prio) {
+ int n = IRQ_PRIO_REG(irq);
+ uint32_t v;
+
+ v = stm_nvic.ipr[n];
+ v &= ~IRQ_PRIO_MASK(irq);
+ v |= (prio) << IRQ_PRIO_BIT(irq);
+ stm_nvic.ipr[n] = v;
+}
+
+static inline uint8_t
+stm_nvic_get_priority(int irq) {
+ return (stm_nvic.ipr[IRQ_PRIO_REG(irq)] >> IRQ_PRIO_BIT(irq)) & IRQ_PRIO_MASK(0);
+}
+
+struct stm_scb {
+ vuint32_t cpuid;
+ vuint32_t icsr;
+ vuint32_t vtor;
+ vuint32_t aircr;
+
+ vuint32_t scr;
+ vuint32_t ccr;
+ vuint32_t shpr1;
+ vuint32_t shpr2;
+
+ vuint32_t shpr3;
+ vuint32_t shcrs;
+ vuint32_t cfsr;
+ vuint32_t hfsr;
+
+ uint32_t unused_30;
+ vuint32_t mmfar;
+ vuint32_t bfar;
+};
+
+extern struct stm_scb stm_scb;
+
+#define STM_SCB_AIRCR_VECTKEY 16
+#define STM_SCB_AIRCR_VECTKEY_KEY 0x05fa
+#define STM_SCB_AIRCR_PRIGROUP 8
+#define STM_SCB_AIRCR_SYSRESETREQ 2
+#define STM_SCB_AIRCR_VECTCLRACTIVE 1
+#define STM_SCB_AIRCR_VECTRESET 0
+
+#define isr(name) void stm_ ## name ## _isr(void);
+
+isr(nmi)
+isr(hardfault)
+isr(memmanage)
+isr(busfault)
+isr(usagefault)
+isr(svc)
+isr(debugmon)
+isr(pendsv)
+isr(systick)
+isr(wwdg)
+isr(pvd)
+isr(tamper_stamp)
+isr(rtc_wkup)
+isr(flash)
+isr(rcc)
+isr(exti0)
+isr(exti1)
+isr(exti2)
+isr(exti3)
+isr(exti4)
+isr(dma1_channel1)
+isr(dma1_channel2)
+isr(dma1_channel3)
+isr(dma1_channel4)
+isr(dma1_channel5)
+isr(dma1_channel6)
+isr(dma1_channel7)
+isr(adc1)
+isr(usb_hp)
+isr(usb_lp)
+isr(dac)
+isr(comp)
+isr(exti9_5)
+isr(lcd)
+isr(tim9)
+isr(tim10)
+isr(tim11)
+isr(tim2)
+isr(tim3)
+isr(tim4)
+isr(i2c1_ev)
+isr(i2c1_er)
+isr(i2c2_ev)
+isr(i2c2_er)
+isr(spi1)
+isr(spi2)
+isr(usart1)
+isr(usart2)
+isr(usart3)
+isr(exti15_10)
+isr(rtc_alarm)
+isr(usb_fs_wkup)
+isr(tim6)
+isr(tim7)
+
+#undef isr
+
+#define STM_ISR_WWDG_POS 0
+#define STM_ISR_PVD_VDDIO2_POS 1
+#define STM_ISR_RTC_POS 2
+#define STM_ISR_FLASH_POS 3
+#define STM_ISR_RCC_CRS_POS 4
+#define STM_ISR_EXTI0_1_POS 5
+#define STM_ISR_EXTI2_3_POS 6
+#define STM_ISR_EXTI4_15_POS 7
+#define STM_ISR_TSC_POS 8
+#define STM_ISR_DMA_CH1_POS 9
+#define STM_ISR_DMA_CH2_3_DMA2_CH1_2_POS 10
+#define STM_ISR_DMA_CH44_5_6_7_DMA2_CH3_4_5_POS 11
+#define STM_ISR_ADC_COMP_POS 12
+#define STM_ISR_TIM1_BRK_UP_TRG_COM_POS 13
+#define STM_ISR_TIM1_CC_POS 14
+#define STM_ISR_TIM2_POS 15
+#define STM_ISR_TIM3_POS 16
+#define STM_ISR_TIM6_DAC_POS 17
+#define STM_ISR_TIM7_POS 18
+#define STM_ISR_TIM14_POS 19
+#define STM_ISR_TIM15_POS 20
+#define STM_ISR_TIM16_POS 21
+#define STM_ISR_TIM17_POS 22
+#define STM_ISR_I2C1_POS 23
+#define STM_ISR_I2C2_POS 24
+#define STM_ISR_SPI1_POS 25
+#define STM_ISR_SPI2_POS 26
+#define STM_ISR_USART1_POS 27
+#define STM_ISR_USART2_POS 28
+#define STM_ISR_UASART3_4_5_6_7_8_POS 29
+#define STM_ISR_CEC_CAN_POS 30
+#define STM_ISR_USB_POS 31
+
+struct stm_syscfg {
+ vuint32_t cfgr1;
+ vuint32_t exticr[4];
+ vuint32_t cfgr2;
+};
+
+extern struct stm_syscfg stm_syscfg;
+
+#define STM_SYSCFG_CFGR1_TIM3_DMA_RMP 30
+#define STM_SYSCFG_CFGR1_TIM2_DMA_RMP 29
+#define STM_SYSCFG_CFGR1_TIM1_DMA_RMP 28
+#define STM_SYSCFG_CFGR1_I2C1_DMA_RMP 27
+#define STM_SYSCFG_CFGR1_USART3_DMA_RMP 26
+#define STM_SYSCFG_CFGR1_USART2_DMA_RMP 25
+#define STM_SYSCFG_CFGR1_SPI2_DMA_RMP 24
+#define STM_SYSCFG_CFGR1_I2C_PA10_FMP 23
+#define STM_SYSCFG_CFGR1_I2C_PA9_FMP 22
+#define STM_SYSCFG_CFGR1_I2C2_FMP 21
+#define STM_SYSCFG_CFGR1_I2C1_FMP 20
+#define STM_SYSCFG_CFGR1_I2C_PB9_FMP 19
+#define STM_SYSCFG_CFGR1_I2C_PB8_FMP 18
+#define STM_SYSCFG_CFGR1_I2C_PB7_FMP 17
+#define STM_SYSCFG_CFGR1_I2C_PB6_FMP 16
+#define STM_SYSCFG_CFGR1_TIM17_DMA_RMP2 14
+#define STM_SYSCFG_CFGR1_TIM16_DMA_RMP2 13
+#define STM_SYSCFG_CFGR1_TIM17_DMA_RMP 12
+#define STM_SYSCFG_CFGR1_TIM16_DMA_RMP 11
+#define STM_SYSCFG_CFGR1_USART1_RX_DMA_RMP 10
+#define STM_SYSCFG_CFGR1_USART1_TX_DMA_RMP 9
+#define STM_SYSCFG_CFGR1_ADC_DMA_RMP 8
+#define STM_SYSCFG_CFGR1_IRDA_ENV_SEL 6
+#define STM_SYSCFG_CFGR1_IRDA_ENV_SEL_TIMER16 0
+#define STM_SYSCFG_CFGR1_IRDA_ENV_SEL_USART1 1
+#define STM_SYSCFG_CFGR1_IRDA_ENV_SEL_USART4 2
+#define STM_SYSCFG_CFGR1_PA11_PA12_RMP 4
+#define STM_SYSCFG_CFGR1_MEM_MODE 0
+#define STM_SYSCFG_CFGR1_MEM_MODE_MAIN_FLASH 0
+#define STM_SYSCFG_CFGR1_MEM_MODE_SYSTEM_FLASH 1
+#define STM_SYSCFG_CFGR1_MEM_MODE_SRAM 3
+#define STM_SYSCFG_CFGR1_MEM_MODE_MASK 3
+
+#if 0
+static inline void
+stm_exticr_set(struct stm_gpio *gpio, int pin) {
+ uint8_t reg = pin >> 2;
+ uint8_t shift = (pin & 3) << 2;
+ uint8_t val = 0;
+
+ /* Enable SYSCFG */
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SYSCFGCOMPEN);
+
+ if (gpio == &stm_gpioa)
+ val = STM_SYSCFG_EXTICR_PA;
+ else if (gpio == &stm_gpiob)
+ val = STM_SYSCFG_EXTICR_PB;
+ else if (gpio == &stm_gpioc)
+ val = STM_SYSCFG_EXTICR_PC;
+ else if (gpio == &stm_gpiof)
+ val = STM_SYSCFG_EXTICR_PF;
+
+ stm_syscfg.exticr[reg] = (stm_syscfg.exticr[reg] & ~(0xf << shift)) | val << shift;
+}
+#endif
+
+
+struct stm_dma_channel {
+ vuint32_t ccr;
+ vuint32_t cndtr;
+ vvoid_t cpar;
+ vvoid_t cmar;
+ vuint32_t reserved;
+};
+
+#define STM_NUM_DMA 6
+
+struct stm_dma {
+ vuint32_t isr;
+ vuint32_t ifcr;
+ struct stm_dma_channel channel[STM_NUM_DMA];
+};
+
+extern struct stm_dma stm_dma;
+
+/* DMA channels go from 1 to 6, instead of 0 to 5 (sigh)
+ */
+
+#define STM_DMA_INDEX(channel) ((channel) - 1)
+
+#define STM_DMA_ISR(index) ((index) << 2)
+#define STM_DMA_ISR_MASK 0xf
+#define STM_DMA_ISR_TEIF 3
+#define STM_DMA_ISR_HTIF 2
+#define STM_DMA_ISR_TCIF 1
+#define STM_DMA_ISR_GIF 0
+
+#define STM_DMA_IFCR(index) ((index) << 2)
+#define STM_DMA_IFCR_MASK 0xf
+#define STM_DMA_IFCR_CTEIF 3
+#define STM_DMA_IFCR_CHTIF 2
+#define STM_DMA_IFCR_CTCIF 1
+#define STM_DMA_IFCR_CGIF 0
+
+#define STM_DMA_CCR_MEM2MEM (14)
+
+#define STM_DMA_CCR_PL (12)
+#define STM_DMA_CCR_PL_LOW (0)
+#define STM_DMA_CCR_PL_MEDIUM (1)
+#define STM_DMA_CCR_PL_HIGH (2)
+#define STM_DMA_CCR_PL_VERY_HIGH (3)
+#define STM_DMA_CCR_PL_MASK (3)
+
+#define STM_DMA_CCR_MSIZE (10)
+#define STM_DMA_CCR_MSIZE_8 (0)
+#define STM_DMA_CCR_MSIZE_16 (1)
+#define STM_DMA_CCR_MSIZE_32 (2)
+#define STM_DMA_CCR_MSIZE_MASK (3)
+
+#define STM_DMA_CCR_PSIZE (8)
+#define STM_DMA_CCR_PSIZE_8 (0)
+#define STM_DMA_CCR_PSIZE_16 (1)
+#define STM_DMA_CCR_PSIZE_32 (2)
+#define STM_DMA_CCR_PSIZE_MASK (3)
+
+#define STM_DMA_CCR_MINC (7)
+#define STM_DMA_CCR_PINC (6)
+#define STM_DMA_CCR_CIRC (5)
+#define STM_DMA_CCR_DIR (4)
+#define STM_DMA_CCR_DIR_PER_TO_MEM 0
+#define STM_DMA_CCR_DIR_MEM_TO_PER 1
+#define STM_DMA_CCR_TEIE (3)
+#define STM_DMA_CCR_HTIE (2)
+#define STM_DMA_CCR_TCIE (1)
+#define STM_DMA_CCR_EN (0)
+
+/* DMA channel assignments. When a peripheral has multiple channels
+ * (indicated with _<number>), then it can be configured to either
+ * channel using syscfg.cfgr1
+ */
+
+#define STM_DMA_CHANNEL_ADC_1 1
+#define STM_DMA_CHANNEL_ADC_2 2
+
+#define STM_DMA_CHANNEL_SPI1_RX 2
+#define STM_DMA_CHANNEL_SPI1_TX 3
+
+#define STM_DMA_CHANNEL_SPI2_RX 4
+#define STM_DMA_CHANNEL_SPI2_TX 5
+
+#define STM_DMA_CHANNEL_USART1_TX_1 2
+#define STM_DMA_CHANNEL_USART1_RX_1 3
+#define STM_DMA_CHANNEL_USART1_TX_2 4
+#define STM_DMA_CHANNEL_USART1_RX_2 5
+
+#define STM_DMA_CHANNEL_USART2_RX 4
+#define STM_DMA_CHANNEL_USART2_TX 5
+
+#define STM_DMA_CHANNEL_I2C1_TX 2
+#define STM_DMA_CHANNEL_I2C1_RX 3
+
+#define STM_DMA_CHANNEL_I2C2_TX 4
+#define STM_DMA_CHANNEL_I2C2_RX 5
+
+#define STM_DMA_CHANNEL_TIM1_CH1 2
+#define STM_DMA_CHANNEL_TIM1_CH2 3
+#define STM_DMA_CHANNEL_TIM1_CH4 4
+#define STM_DMA_CHANNEL_TIM1_TRIG 4
+#define STM_DMA_CHANNEL_TIM1_COM 4
+#define STM_DMA_CHANNEL_TIM1_CH3 5
+#define STM_DMA_CHANNEL_TIM1_UP 5
+
+#define STM_DMA_CHANNEL_TIM2_CH3 1
+#define STM_DMA_CHANNEL_TIM2_UP 2
+#define STM_DMA_CHANNEL_TIM2_CH2 3
+#define STM_DMA_CHANNEL_TIM2_CH4 4
+#define STM_DMA_CHANNEL_TIM2_CH1 5
+
+#define STM_DMA_CHANNEL_TIM3_CH3 2
+#define STM_DMA_CHANNEL_TIM3_CH4 3
+#define STM_DMA_CHANNEL_TIM3_UP 3
+#define STM_DMA_CHANNEL_TIM3_CH1 4
+#define STM_DMA_CHANNEL_TIM3_TRIG 4
+
+#define STM_DMA_CHANNEL_TIM6_UP_DAC 2
+
+#define STM_DMA_CHANNEL_TIM15_CH1 5
+#define STM_DMA_CHANNEL_TIM15_UP 5
+#define STM_DMA_CHANNEL_TIM15_TRIG 5
+#define STM_DMA_CHANNEL_TIM15_COM 5
+
+#define STM_DMA_CHANNEL_TIM16_CH1_1 3
+#define STM_DMA_CHANNEL_TIM16_UP_1 3
+#define STM_DMA_CHANNEL_TIM16_CH1_2 4
+#define STM_DMA_CHANNEL_TIM16_UP_2 4
+
+#define STM_DMA_CHANNEL_TIM17_CH1_1 1
+#define STM_DMA_CHANNEL_TIM17_UP_1 1
+#define STM_DMA_CHANNEL_TIM17_CH1_2 2
+#define STM_DMA_CHANNEL_TIM17_UP_2 2
+
+/*
+ * Only spi channel 1 and 2 can use DMA
+ */
+#define STM_NUM_SPI 2
+
+struct stm_spi {
+ vuint32_t cr1;
+ vuint32_t cr2;
+ vuint32_t sr;
+ vuint32_t dr;
+ vuint32_t crcpr;
+ vuint32_t rxcrcr;
+ vuint32_t txcrcr;
+};
+
+extern struct stm_spi stm_spi1, stm_spi2, stm_spi3;
+
+/* SPI channels go from 1 to 3, instead of 0 to 2 (sigh)
+ */
+
+#define STM_SPI_INDEX(channel) ((channel) - 1)
+
+#define STM_SPI_CR1_BIDIMODE 15
+#define STM_SPI_CR1_BIDIOE 14
+#define STM_SPI_CR1_CRCEN 13
+#define STM_SPI_CR1_CRCNEXT 12
+#define STM_SPI_CR1_DFF 11
+#define STM_SPI_CR1_RXONLY 10
+#define STM_SPI_CR1_SSM 9
+#define STM_SPI_CR1_SSI 8
+#define STM_SPI_CR1_LSBFIRST 7
+#define STM_SPI_CR1_SPE 6
+#define STM_SPI_CR1_BR 3
+#define STM_SPI_CR1_BR_PCLK_2 0
+#define STM_SPI_CR1_BR_PCLK_4 1
+#define STM_SPI_CR1_BR_PCLK_8 2
+#define STM_SPI_CR1_BR_PCLK_16 3
+#define STM_SPI_CR1_BR_PCLK_32 4
+#define STM_SPI_CR1_BR_PCLK_64 5
+#define STM_SPI_CR1_BR_PCLK_128 6
+#define STM_SPI_CR1_BR_PCLK_256 7
+#define STM_SPI_CR1_BR_MASK 7
+
+#define STM_SPI_CR1_MSTR 2
+#define STM_SPI_CR1_CPOL 1
+#define STM_SPI_CR1_CPHA 0
+
+#define STM_SPI_CR2_TXEIE 7
+#define STM_SPI_CR2_RXNEIE 6
+#define STM_SPI_CR2_ERRIE 5
+#define STM_SPI_CR2_SSOE 2
+#define STM_SPI_CR2_TXDMAEN 1
+#define STM_SPI_CR2_RXDMAEN 0
+
+#define STM_SPI_SR_BSY 7
+#define STM_SPI_SR_OVR 6
+#define STM_SPI_SR_MODF 5
+#define STM_SPI_SR_CRCERR 4
+#define STM_SPI_SR_TXE 1
+#define STM_SPI_SR_RXNE 0
+
+struct stm_adc {
+ vuint32_t isr;
+ vuint32_t ier;
+ vuint32_t cr;
+ vuint32_t cfgr1;
+
+ vuint32_t cfgr2;
+ vuint32_t smpr;
+ vuint32_t r_18;
+ vuint32_t r_1c;
+
+ vuint32_t tr;
+ vuint32_t r_24;
+ vuint32_t chselr;
+ vuint32_t r_2c;
+
+ vuint32_t r_30[4];
+
+ vuint32_t dr;
+
+ uint8_t r_44[0x308 - 0x44];
+ vuint32_t ccr;
+};
+
+extern struct stm_adc stm_adc;
+
+#define STM_ADC_ISR_AWD 7
+#define STM_ADC_ISR_OVR 4
+#define STM_ADC_ISR_EOSEQ 3
+#define STM_ADC_ISR_EOC 2
+#define STM_ADC_ISR_EOSMP 1
+#define STM_ADC_ISR_ADRDY 0
+
+#define STM_ADC_IER_AWDIE 7
+#define STM_ADC_IER_OVRIE 4
+#define STM_ADC_IER_EOSEQIE 3
+#define STM_ADC_IER_EOCIE 2
+#define STM_ADC_IER_EOSMPIE 1
+#define STM_ADC_IER_ADRDYIE 0
+
+#define STM_ADC_CR_ADCAL 31
+#define STM_ADC_CR_ADSTP 4
+#define STM_ADC_CR_ADSTART 2
+#define STM_ADC_CR_ADDIS 1
+#define STM_ADC_CR_ADEN 0
+
+#define STM_ADC_CFGR1_AWDCH 26
+#define STM_ADC_CFGR1_AWDEN 23
+#define STM_ADC_CFGR1_AWDSGL 22
+#define STM_ADC_CFGR1_DISCEN 16
+#define STM_ADC_CFGR1_AUTOOFF 15
+#define STM_ADC_CFGR1_WAIT 14
+#define STM_ADC_CFGR1_CONT 13
+#define STM_ADC_CFGR1_OVRMOD 12
+#define STM_ADC_CFGR1_EXTEN 10
+#define STM_ADC_CFGR1_EXTEN_DISABLE 0
+#define STM_ADC_CFGR1_EXTEN_RISING 1
+#define STM_ADC_CFGR1_EXTEN_FALLING 2
+#define STM_ADC_CFGR1_EXTEN_BOTH 3
+#define STM_ADC_CFGR1_EXTEN_MASK 3
+
+#define STM_ADC_CFGR1_EXTSEL 6
+#define STM_ADC_CFGR1_ALIGN 5
+#define STM_ADC_CFGR1_RES 3
+#define STM_ADC_CFGR1_RES_12 0
+#define STM_ADC_CFGR1_RES_10 1
+#define STM_ADC_CFGR1_RES_8 2
+#define STM_ADC_CFGR1_RES_6 3
+#define STM_ADC_CFGR1_RES_MASK 3
+#define STM_ADC_CFGR1_SCANDIR 2
+#define STM_ADC_CFGR1_SCANDIR_UP 0
+#define STM_ADC_CFGR1_SCANDIR_DOWN 1
+#define STM_ADC_CFGR1_DMACFG 1
+#define STM_ADC_CFGR1_DMACFG_ONESHOT 0
+#define STM_ADC_CFGR1_DMACFG_CIRCULAR 1
+#define STM_ADC_CFGR1_DMAEN 0
+
+#define STM_ADC_CFGR2_CKMODE 30
+#define STM_ADC_CFGR2_CKMODE_ADCCLK 0
+#define STM_ADC_CFGR2_CKMODE_PCLK_2 1
+#define STM_ADC_CFGR2_CKMODE_PCLK_4 2
+
+#define STM_ADC_SMPR_SMP 0
+#define STM_ADC_SMPR_SMP_1_5 0
+#define STM_ADC_SMPR_SMP_7_5 1
+#define STM_ADC_SMPR_SMP_13_5 2
+#define STM_ADC_SMPR_SMP_28_5 3
+#define STM_ADC_SMPR_SMP_41_5 4
+#define STM_ADC_SMPR_SMP_55_5 5
+#define STM_ADC_SMPR_SMP_71_5 6
+#define STM_ADC_SMPR_SMP_239_5 7
+
+#define STM_ADC_TR_HT 16
+#define STM_ADC_TR_LT 0
+
+#define STM_ADC_CCR_VBATEN 24
+#define STM_ADC_CCR_TSEN 23
+#define STM_ADC_CCR_VREFEN 22
+
+struct stm_cal {
+ uint16_t ts_cal_cold; /* 30°C */
+ uint16_t vrefint_cal;
+ uint16_t unused_c0;
+ uint16_t ts_cal_hot; /* 110°C */
+};
+
+extern struct stm_cal stm_cal;
+
+#define stm_temp_cal_cold 30
+#define stm_temp_cal_hot 110
+
+struct stm_dbgmcu {
+ uint32_t idcode;
+};
+
+extern struct stm_dbgmcu stm_dbgmcu;
+
+static inline uint16_t
+stm_dev_id(void) {
+ return stm_dbgmcu.idcode & 0xfff;
+}
+
+struct stm_flash_size {
+ uint16_t f_size;
+};
+
+extern struct stm_flash_size stm_flash_size_04x;
+
+/* Returns flash size in bytes */
+extern uint32_t
+stm_flash_size(void);
+
+struct stm_device_id {
+ uint32_t u_id0;
+ uint32_t u_id1;
+ uint32_t u_id2;
+};
+
+extern struct stm_device_id stm_device_id;
+
+#define STM_NUM_I2C 2
+
+#define STM_I2C_INDEX(channel) ((channel) - 1)
+
+struct stm_i2c {
+ vuint32_t cr1;
+ vuint32_t cr2;
+ vuint32_t oar1;
+ vuint32_t oar2;
+ vuint32_t dr;
+ vuint32_t sr1;
+ vuint32_t sr2;
+ vuint32_t ccr;
+ vuint32_t trise;
+};
+
+extern struct stm_i2c stm_i2c1, stm_i2c2;
+
+#define STM_I2C_CR1_SWRST 15
+#define STM_I2C_CR1_ALERT 13
+#define STM_I2C_CR1_PEC 12
+#define STM_I2C_CR1_POS 11
+#define STM_I2C_CR1_ACK 10
+#define STM_I2C_CR1_STOP 9
+#define STM_I2C_CR1_START 8
+#define STM_I2C_CR1_NOSTRETCH 7
+#define STM_I2C_CR1_ENGC 6
+#define STM_I2C_CR1_ENPEC 5
+#define STM_I2C_CR1_ENARP 4
+#define STM_I2C_CR1_SMBTYPE 3
+#define STM_I2C_CR1_SMBUS 1
+#define STM_I2C_CR1_PE 0
+
+#define STM_I2C_CR2_LAST 12
+#define STM_I2C_CR2_DMAEN 11
+#define STM_I2C_CR2_ITBUFEN 10
+#define STM_I2C_CR2_ITEVTEN 9
+#define STM_I2C_CR2_ITERREN 8
+#define STM_I2C_CR2_FREQ 0
+#define STM_I2C_CR2_FREQ_2_MHZ 2
+#define STM_I2C_CR2_FREQ_4_MHZ 4
+#define STM_I2C_CR2_FREQ_8_MHZ 8
+#define STM_I2C_CR2_FREQ_16_MHZ 16
+#define STM_I2C_CR2_FREQ_32_MHZ 32
+#define STM_I2C_CR2_FREQ_MASK 0x3f
+
+#define STM_I2C_SR1_SMBALERT 15
+#define STM_I2C_SR1_TIMEOUT 14
+#define STM_I2C_SR1_PECERR 12
+#define STM_I2C_SR1_OVR 11
+#define STM_I2C_SR1_AF 10
+#define STM_I2C_SR1_ARLO 9
+#define STM_I2C_SR1_BERR 8
+#define STM_I2C_SR1_TXE 7
+#define STM_I2C_SR1_RXNE 6
+#define STM_I2C_SR1_STOPF 4
+#define STM_I2C_SR1_ADD10 3
+#define STM_I2C_SR1_BTF 2
+#define STM_I2C_SR1_ADDR 1
+#define STM_I2C_SR1_SB 0
+
+#define STM_I2C_SR2_PEC 8
+#define STM_I2C_SR2_PEC_MASK 0xff00
+#define STM_I2C_SR2_DUALF 7
+#define STM_I2C_SR2_SMBHOST 6
+#define STM_I2C_SR2_SMBDEFAULT 5
+#define STM_I2C_SR2_GENCALL 4
+#define STM_I2C_SR2_TRA 2
+#define STM_I2C_SR2_BUSY 1
+#define STM_I2C_SR2_MSL 0
+
+#define STM_I2C_CCR_FS 15
+#define STM_I2C_CCR_DUTY 14
+#define STM_I2C_CCR_CCR 0
+#define STM_I2C_CCR_MASK 0x7ff
+
+struct stm_tim234 {
+ vuint32_t cr1;
+ vuint32_t cr2;
+ vuint32_t smcr;
+ vuint32_t dier;
+
+ vuint32_t sr;
+ vuint32_t egr;
+ vuint32_t ccmr1;
+ vuint32_t ccmr2;
+
+ vuint32_t ccer;
+ vuint32_t cnt;
+ vuint32_t psc;
+ vuint32_t arr;
+
+ uint32_t reserved_30;
+ vuint32_t ccr1;
+ vuint32_t ccr2;
+ vuint32_t ccr3;
+
+ vuint32_t ccr4;
+ uint32_t reserved_44;
+ vuint32_t dcr;
+ vuint32_t dmar;
+
+ uint32_t reserved_50;
+};
+
+extern struct stm_tim234 stm_tim2, stm_tim3, stm_tim4;
+
+#define STM_TIM234_CR1_CKD 8
+#define STM_TIM234_CR1_CKD_1 0
+#define STM_TIM234_CR1_CKD_2 1
+#define STM_TIM234_CR1_CKD_4 2
+#define STM_TIM234_CR1_CKD_MASK 3
+#define STM_TIM234_CR1_ARPE 7
+#define STM_TIM234_CR1_CMS 5
+#define STM_TIM234_CR1_CMS_EDGE 0
+#define STM_TIM234_CR1_CMS_CENTER_1 1
+#define STM_TIM234_CR1_CMS_CENTER_2 2
+#define STM_TIM234_CR1_CMS_CENTER_3 3
+#define STM_TIM234_CR1_CMS_MASK 3
+#define STM_TIM234_CR1_DIR 4
+#define STM_TIM234_CR1_DIR_UP 0
+#define STM_TIM234_CR1_DIR_DOWN 1
+#define STM_TIM234_CR1_OPM 3
+#define STM_TIM234_CR1_URS 2
+#define STM_TIM234_CR1_UDIS 1
+#define STM_TIM234_CR1_CEN 0
+
+#define STM_TIM234_CR2_TI1S 7
+#define STM_TIM234_CR2_MMS 4
+#define STM_TIM234_CR2_MMS_RESET 0
+#define STM_TIM234_CR2_MMS_ENABLE 1
+#define STM_TIM234_CR2_MMS_UPDATE 2
+#define STM_TIM234_CR2_MMS_COMPARE_PULSE 3
+#define STM_TIM234_CR2_MMS_COMPARE_OC1REF 4
+#define STM_TIM234_CR2_MMS_COMPARE_OC2REF 5
+#define STM_TIM234_CR2_MMS_COMPARE_OC3REF 6
+#define STM_TIM234_CR2_MMS_COMPARE_OC4REF 7
+#define STM_TIM234_CR2_MMS_MASK 7
+#define STM_TIM234_CR2_CCDS 3
+
+#define STM_TIM234_SMCR_ETP 15
+#define STM_TIM234_SMCR_ECE 14
+#define STM_TIM234_SMCR_ETPS 12
+#define STM_TIM234_SMCR_ETPS_OFF 0
+#define STM_TIM234_SMCR_ETPS_DIV_2 1
+#define STM_TIM234_SMCR_ETPS_DIV_4 2
+#define STM_TIM234_SMCR_ETPS_DIV_8 3
+#define STM_TIM234_SMCR_ETPS_MASK 3
+#define STM_TIM234_SMCR_ETF 8
+#define STM_TIM234_SMCR_ETF_NONE 0
+#define STM_TIM234_SMCR_ETF_INT_N_2 1
+#define STM_TIM234_SMCR_ETF_INT_N_4 2
+#define STM_TIM234_SMCR_ETF_INT_N_8 3
+#define STM_TIM234_SMCR_ETF_DTS_2_N_6 4
+#define STM_TIM234_SMCR_ETF_DTS_2_N_8 5
+#define STM_TIM234_SMCR_ETF_DTS_4_N_6 6
+#define STM_TIM234_SMCR_ETF_DTS_4_N_8 7
+#define STM_TIM234_SMCR_ETF_DTS_8_N_6 8
+#define STM_TIM234_SMCR_ETF_DTS_8_N_8 9
+#define STM_TIM234_SMCR_ETF_DTS_16_N_5 10
+#define STM_TIM234_SMCR_ETF_DTS_16_N_6 11
+#define STM_TIM234_SMCR_ETF_DTS_16_N_8 12
+#define STM_TIM234_SMCR_ETF_DTS_32_N_5 13
+#define STM_TIM234_SMCR_ETF_DTS_32_N_6 14
+#define STM_TIM234_SMCR_ETF_DTS_32_N_8 15
+#define STM_TIM234_SMCR_ETF_MASK 15
+#define STM_TIM234_SMCR_MSM 7
+#define STM_TIM234_SMCR_TS 4
+#define STM_TIM234_SMCR_TS_ITR0 0
+#define STM_TIM234_SMCR_TS_ITR1 1
+#define STM_TIM234_SMCR_TS_ITR2 2
+#define STM_TIM234_SMCR_TS_ITR3 3
+#define STM_TIM234_SMCR_TS_TI1F_ED 4
+#define STM_TIM234_SMCR_TS_TI1FP1 5
+#define STM_TIM234_SMCR_TS_TI2FP2 6
+#define STM_TIM234_SMCR_TS_ETRF 7
+#define STM_TIM234_SMCR_TS_MASK 7
+#define STM_TIM234_SMCR_OCCS 3
+#define STM_TIM234_SMCR_SMS 0
+#define STM_TIM234_SMCR_SMS_DISABLE 0
+#define STM_TIM234_SMCR_SMS_ENCODER_MODE_1 1
+#define STM_TIM234_SMCR_SMS_ENCODER_MODE_2 2
+#define STM_TIM234_SMCR_SMS_ENCODER_MODE_3 3
+#define STM_TIM234_SMCR_SMS_RESET_MODE 4
+#define STM_TIM234_SMCR_SMS_GATED_MODE 5
+#define STM_TIM234_SMCR_SMS_TRIGGER_MODE 6
+#define STM_TIM234_SMCR_SMS_EXTERNAL_CLOCK 7
+#define STM_TIM234_SMCR_SMS_MASK 7
+
+#define STM_TIM234_SR_CC4OF 12
+#define STM_TIM234_SR_CC3OF 11
+#define STM_TIM234_SR_CC2OF 10
+#define STM_TIM234_SR_CC1OF 9
+#define STM_TIM234_SR_TIF 6
+#define STM_TIM234_SR_CC4IF 4
+#define STM_TIM234_SR_CC3IF 3
+#define STM_TIM234_SR_CC2IF 2
+#define STM_TIM234_SR_CC1IF 1
+#define STM_TIM234_SR_UIF 0
+
+#define STM_TIM234_EGR_TG 6
+#define STM_TIM234_EGR_CC4G 4
+#define STM_TIM234_EGR_CC3G 3
+#define STM_TIM234_EGR_CC2G 2
+#define STM_TIM234_EGR_CC1G 1
+#define STM_TIM234_EGR_UG 0
+
+#define STM_TIM234_CCMR1_OC2CE 15
+#define STM_TIM234_CCMR1_OC2M 12
+#define STM_TIM234_CCMR1_OC2M_FROZEN 0
+#define STM_TIM234_CCMR1_OC2M_SET_HIGH_ON_MATCH 1
+#define STM_TIM234_CCMR1_OC2M_SET_LOW_ON_MATCH 2
+#define STM_TIM234_CCMR1_OC2M_TOGGLE 3
+#define STM_TIM234_CCMR1_OC2M_FORCE_LOW 4
+#define STM_TIM234_CCMR1_OC2M_FORCE_HIGH 5
+#define STM_TIM234_CCMR1_OC2M_PWM_MODE_1 6
+#define STM_TIM234_CCMR1_OC2M_PWM_MODE_2 7
+#define STM_TIM234_CCMR1_OC2M_MASK 7
+#define STM_TIM234_CCMR1_OC2PE 11
+#define STM_TIM234_CCMR1_OC2FE 10
+#define STM_TIM234_CCMR1_CC2S 8
+#define STM_TIM234_CCMR1_CC2S_OUTPUT 0
+#define STM_TIM234_CCMR1_CC2S_INPUT_TI2 1
+#define STM_TIM234_CCMR1_CC2S_INPUT_TI1 2
+#define STM_TIM234_CCMR1_CC2S_INPUT_TRC 3
+#define STM_TIM234_CCMR1_CC2S_MASK 3
+
+#define STM_TIM234_CCMR1_OC1CE 7
+#define STM_TIM234_CCMR1_OC1M 4
+#define STM_TIM234_CCMR1_OC1M_FROZEN 0
+#define STM_TIM234_CCMR1_OC1M_SET_HIGH_ON_MATCH 1
+#define STM_TIM234_CCMR1_OC1M_SET_LOW_ON_MATCH 2
+#define STM_TIM234_CCMR1_OC1M_TOGGLE 3
+#define STM_TIM234_CCMR1_OC1M_FORCE_LOW 4
+#define STM_TIM234_CCMR1_OC1M_FORCE_HIGH 5
+#define STM_TIM234_CCMR1_OC1M_PWM_MODE_1 6
+#define STM_TIM234_CCMR1_OC1M_PWM_MODE_2 7
+#define STM_TIM234_CCMR1_OC1M_MASK 7
+#define STM_TIM234_CCMR1_OC1PE 11
+#define STM_TIM234_CCMR1_OC1FE 2
+#define STM_TIM234_CCMR1_CC1S 0
+#define STM_TIM234_CCMR1_CC1S_OUTPUT 0
+#define STM_TIM234_CCMR1_CC1S_INPUT_TI1 1
+#define STM_TIM234_CCMR1_CC1S_INPUT_TI2 2
+#define STM_TIM234_CCMR1_CC1S_INPUT_TRC 3
+#define STM_TIM234_CCMR1_CC1S_MASK 3
+
+#define STM_TIM234_CCMR2_OC4CE 15
+#define STM_TIM234_CCMR2_OC4M 12
+#define STM_TIM234_CCMR2_OC4M_FROZEN 0
+#define STM_TIM234_CCMR2_OC4M_SET_HIGH_ON_MATCH 1
+#define STM_TIM234_CCMR2_OC4M_SET_LOW_ON_MATCH 2
+#define STM_TIM234_CCMR2_OC4M_TOGGLE 3
+#define STM_TIM234_CCMR2_OC4M_FORCE_LOW 4
+#define STM_TIM234_CCMR2_OC4M_FORCE_HIGH 5
+#define STM_TIM234_CCMR2_OC4M_PWM_MODE_1 6
+#define STM_TIM234_CCMR2_OC4M_PWM_MODE_2 7
+#define STM_TIM234_CCMR2_OC4M_MASK 7
+#define STM_TIM234_CCMR2_OC4PE 11
+#define STM_TIM234_CCMR2_OC4FE 10
+#define STM_TIM234_CCMR2_CC4S 8
+#define STM_TIM234_CCMR2_CC4S_OUTPUT 0
+#define STM_TIM234_CCMR2_CC4S_INPUT_TI4 1
+#define STM_TIM234_CCMR2_CC4S_INPUT_TI3 2
+#define STM_TIM234_CCMR2_CC4S_INPUT_TRC 3
+#define STM_TIM234_CCMR2_CC4S_MASK 3
+
+#define STM_TIM234_CCMR2_OC3CE 7
+#define STM_TIM234_CCMR2_OC3M 4
+#define STM_TIM234_CCMR2_OC3M_FROZEN 0
+#define STM_TIM234_CCMR2_OC3M_SET_HIGH_ON_MATCH 1
+#define STM_TIM234_CCMR2_OC3M_SET_LOW_ON_MATCH 2
+#define STM_TIM234_CCMR2_OC3M_TOGGLE 3
+#define STM_TIM234_CCMR2_OC3M_FORCE_LOW 4
+#define STM_TIM234_CCMR2_OC3M_FORCE_HIGH 5
+#define STM_TIM234_CCMR2_OC3M_PWM_MODE_1 6
+#define STM_TIM234_CCMR2_OC3M_PWM_MODE_2 7
+#define STM_TIM234_CCMR2_OC3M_MASK 7
+#define STM_TIM234_CCMR2_OC3PE 11
+#define STM_TIM234_CCMR2_OC3FE 2
+#define STM_TIM234_CCMR2_CC3S 0
+#define STM_TIM234_CCMR2_CC3S_OUTPUT 0
+#define STM_TIM234_CCMR2_CC3S_INPUT_TI3 1
+#define STM_TIM234_CCMR2_CC3S_INPUT_TI4 2
+#define STM_TIM234_CCMR2_CC3S_INPUT_TRC 3
+#define STM_TIM234_CCMR2_CC3S_MASK 3
+
+#define STM_TIM234_CCER_CC4NP 15
+#define STM_TIM234_CCER_CC4P 13
+#define STM_TIM234_CCER_CC4E 12
+#define STM_TIM234_CCER_CC3NP 11
+#define STM_TIM234_CCER_CC3P 9
+#define STM_TIM234_CCER_CC3E 8
+#define STM_TIM234_CCER_CC2NP 7
+#define STM_TIM234_CCER_CC2P 5
+#define STM_TIM234_CCER_CC2E 4
+#define STM_TIM234_CCER_CC1NP 3
+#define STM_TIM234_CCER_CC1P 1
+#define STM_TIM234_CCER_CC1E 0
+
+struct stm_usb {
+ struct {
+ vuint16_t r;
+ uint16_t _;
+ } epr[8];
+ uint8_t reserved_20[0x40 - 0x20];
+ vuint16_t cntr;
+ uint16_t reserved_42;
+ vuint16_t istr;
+ uint16_t reserved_46;
+ vuint16_t fnr;
+ uint16_t reserved_4a;
+ vuint16_t daddr;
+ uint16_t reserved_4e;
+ vuint16_t btable;
+ uint16_t reserved_52;
+ vuint16_t lpmcsr;
+ uint16_t reserved_56;
+ vuint16_t bcdr;
+ uint16_t reserved_5a;
+};
+
+extern struct stm_usb stm_usb;
+
+#define STM_USB_EPR_CTR_RX 15
+#define STM_USB_EPR_CTR_RX_WRITE_INVARIANT 1
+#define STM_USB_EPR_DTOG_RX 14
+#define STM_USB_EPR_DTOG_RX_WRITE_INVARIANT 0
+#define STM_USB_EPR_STAT_RX 12
+#define STM_USB_EPR_STAT_RX_DISABLED 0
+#define STM_USB_EPR_STAT_RX_STALL 1
+#define STM_USB_EPR_STAT_RX_NAK 2
+#define STM_USB_EPR_STAT_RX_VALID 3
+#define STM_USB_EPR_STAT_RX_MASK 3
+#define STM_USB_EPR_STAT_RX_WRITE_INVARIANT 0
+#define STM_USB_EPR_SETUP 11
+#define STM_USB_EPR_EP_TYPE 9
+#define STM_USB_EPR_EP_TYPE_BULK 0
+#define STM_USB_EPR_EP_TYPE_CONTROL 1
+#define STM_USB_EPR_EP_TYPE_ISO 2
+#define STM_USB_EPR_EP_TYPE_INTERRUPT 3
+#define STM_USB_EPR_EP_TYPE_MASK 3
+#define STM_USB_EPR_EP_KIND 8
+#define STM_USB_EPR_EP_KIND_DBL_BUF 1 /* Bulk */
+#define STM_USB_EPR_EP_KIND_STATUS_OUT 1 /* Control */
+#define STM_USB_EPR_CTR_TX 7
+#define STM_USB_CTR_TX_WRITE_INVARIANT 1
+#define STM_USB_EPR_DTOG_TX 6
+#define STM_USB_EPR_DTOG_TX_WRITE_INVARIANT 0
+#define STM_USB_EPR_STAT_TX 4
+#define STM_USB_EPR_STAT_TX_DISABLED 0
+#define STM_USB_EPR_STAT_TX_STALL 1
+#define STM_USB_EPR_STAT_TX_NAK 2
+#define STM_USB_EPR_STAT_TX_VALID 3
+#define STM_USB_EPR_STAT_TX_WRITE_INVARIANT 0
+#define STM_USB_EPR_STAT_TX_MASK 3
+#define STM_USB_EPR_EA 0
+#define STM_USB_EPR_EA_MASK 0xf
+
+#define STM_USB_CNTR_CTRM 15
+#define STM_USB_CNTR_PMAOVRM 14
+#define STM_USB_CNTR_ERRM 13
+#define STM_USB_CNTR_WKUPM 12
+#define STM_USB_CNTR_SUSPM 11
+#define STM_USB_CNTR_RESETM 10
+#define STM_USB_CNTR_SOFM 9
+#define STM_USB_CNTR_ESOFM 8
+#define STM_USB_CNTR_RESUME 4
+#define STM_USB_CNTR_FSUSP 3
+#define STM_USB_CNTR_LP_MODE 2
+#define STM_USB_CNTR_PDWN 1
+#define STM_USB_CNTR_FRES 0
+
+#define STM_USB_ISTR_CTR 15
+#define STM_USB_ISTR_PMAOVR 14
+#define STM_USB_ISTR_ERR 13
+#define STM_USB_ISTR_WKUP 12
+#define STM_USB_ISTR_SUSP 11
+#define STM_USB_ISTR_RESET 10
+#define STM_USB_ISTR_SOF 9
+#define STM_USB_ISTR_ESOF 8
+#define STM_USB_L1REQ 7
+#define STM_USB_ISTR_DIR 4
+#define STM_USB_ISTR_EP_ID 0
+#define STM_USB_ISTR_EP_ID_MASK 0xf
+
+#define STM_USB_FNR_RXDP 15
+#define STM_USB_FNR_RXDM 14
+#define STM_USB_FNR_LCK 13
+#define STM_USB_FNR_LSOF 11
+#define STM_USB_FNR_LSOF_MASK 0x3
+#define STM_USB_FNR_FN 0
+#define STM_USB_FNR_FN_MASK 0x7ff
+
+#define STM_USB_DADDR_EF 7
+#define STM_USB_DADDR_ADD 0
+#define STM_USB_DADDR_ADD_MASK 0x7f
+
+#define STM_USB_BCDR_DPPU 15
+#define STM_USB_BCDR_PS2DET 7
+#define STM_USB_BCDR_SDET 6
+#define STM_USB_BCDR_PDET 5
+#define STM_USB_BCDR_DCDET 4
+#define STM_USB_BCDR_SDEN 3
+#define STM_USB_BCDR_PDEN 2
+#define STM_USB_BCDR_DCDEN 1
+#define STM_USB_BCDR_BCDEN 0
+
+union stm_usb_bdt {
+ struct {
+ vuint16_t addr_tx;
+ vuint16_t count_tx;
+ vuint16_t addr_rx;
+ vuint16_t count_rx;
+ } single;
+ struct {
+ vuint16_t addr;
+ vuint16_t count;
+ } double_tx[2];
+ struct {
+ vuint16_t addr;
+ vuint16_t count;
+ } double_rx[2];
+};
+
+#define STM_USB_BDT_COUNT_RX_BL_SIZE 15
+#define STM_USB_BDT_COUNT_RX_NUM_BLOCK 10
+#define STM_USB_BDT_COUNT_RX_NUM_BLOCK_MASK 0x1f
+#define STM_USB_BDT_COUNT_RX_COUNT_RX 0
+#define STM_USB_BDT_COUNT_RX_COUNT_RX_MASK 0x1ff
+
+#define STM_USB_BDT_SIZE 8
+
+extern uint8_t stm_usb_sram[];
+
+struct stm_exti {
+ vuint32_t imr;
+ vuint32_t emr;
+ vuint32_t rtsr;
+ vuint32_t ftsr;
+
+ vuint32_t swier;
+ vuint32_t pr;
+};
+
+extern struct stm_exti stm_exti;
+
+#endif /* _STM32F0_H_ */
diff --git a/src/telebt-v3.0/Makefile b/src/telebt-v3.0/Makefile
new file mode 100644
index 00000000..a7ef4d64
--- /dev/null
+++ b/src/telebt-v3.0/Makefile
@@ -0,0 +1,94 @@
+#
+# AltOS build
+#
+#
+
+include ../stm/Makefile.defs
+
+INC = \
+ ao.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_boot.h \
+ ao_pins.h \
+ ao_product.h \
+ ao_cc1200_CC1200.h \
+ ao_profile.h \
+ ao_task.h \
+ stm32l.h \
+ Makefile
+
+#PROFILE=ao_profile.c
+#PROFILE_DEF=-DAO_PROFILE=1
+
+#SAMPLE_PROFILE=ao_sample_profile.c \
+# ao_sample_profile_timer.c
+#SAMPLE_PROFILE_DEF=-DHAS_SAMPLE_PROFILE=1
+
+#STACK_GUARD=ao_mpu_stm.c
+#STACK_GUARD_DEF=-DHAS_STACK_GUARD=1
+
+ALTOS_SRC = \
+ ao_boot_chain.c \
+ ao_interrupt.c \
+ ao_product.c \
+ ao_romconfig.c \
+ ao_cmd.c \
+ ao_config.c \
+ ao_data.c \
+ ao_task.c \
+ ao_led.c \
+ ao_stdio.c \
+ ao_panic.c \
+ ao_timer.c \
+ ao_mutex.c \
+ ao_serial_stm.c \
+ ao_freq.c \
+ ao_dma_stm.c \
+ ao_spi_stm.c \
+ ao_cc1200.c \
+ ao_adc_stm.c \
+ ao_btm.c \
+ ao_usb_stm.c \
+ ao_exti_stm.c \
+ ao_eeprom_stm.c \
+ ao_convert_volt.c \
+ ao_packet_master.c \
+ ao_packet.c \
+ ao_monitor.c \
+ $(PROFILE) \
+ $(SAMPLE_PROFILE) \
+ $(STACK_GUARD)
+
+PRODUCT=TeleBT-v3.0
+PRODUCT_DEF=-DTELEBT_V_3_0
+IDPRODUCT=0x000e
+
+CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) $(SAMPLE_PROFILE_DEF) $(STACK_GUARD_DEF) -Os -g
+
+PROGNAME=telebt-v3.0
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) ao_telebt.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ) altos.ld
+ $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+$(OBJ): $(INC)
+
+ao_product.h: ao-make-product.5c ../Version
+ $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean: clean
+
+clean:
+ rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx
+ rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/telebt-v3.0/ao_pins.h b/src/telebt-v3.0/ao_pins.h
new file mode 100644
index 00000000..838f0dfc
--- /dev/null
+++ b/src/telebt-v3.0/ao_pins.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * 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_PINS_H_
+#define _AO_PINS_H_
+
+#define HAS_TASK_QUEUE 1
+
+/* 8MHz High speed external crystal */
+#define AO_HSE 8000000
+
+/* PLLVCO = 96MHz (so that USB will work) */
+#define AO_PLLMUL 12
+#define AO_RCC_CFGR_PLLMUL (STM_RCC_CFGR_PLLMUL_12)
+
+/* SYSCLK = 32MHz (no need to go faster than CPU) */
+#define AO_PLLDIV 3
+#define AO_RCC_CFGR_PLLDIV (STM_RCC_CFGR_PLLDIV_3)
+
+/* HCLK = 32MHz (CPU clock) */
+#define AO_AHB_PRESCALER 1
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+
+/* Run APB1 at 16MHz (HCLK/2) */
+#define AO_APB1_PRESCALER 2
+#define AO_RCC_CFGR_PPRE1_DIV STM_RCC_CFGR_PPRE2_DIV_2
+
+/* Run APB2 at 16MHz (HCLK/2) */
+#define AO_APB2_PRESCALER 2
+#define AO_RCC_CFGR_PPRE2_DIV STM_RCC_CFGR_PPRE2_DIV_2
+
+#define HAS_SERIAL_1 0
+#define USE_SERIAL_1_STDIN 0
+#define SERIAL_1_PB6_PB7 0
+#define SERIAL_1_PA9_PA10 1
+
+#define HAS_SERIAL_2 1
+#define USE_SERIAL_2_STDIN 1
+#define DELAY_SERIAL_2_STDIN 1
+#define USE_SERIAL_2_FLOW 1
+#define SERIAL_2_PA2_PA3 1
+#define SERIAL_2_PD5_PD6 0
+
+#define HAS_SERIAL_3 0
+#define USE_SERIAL_3_STDIN 0
+#define SERIAL_3_PB10_PB11 1
+#define SERIAL_3_PC10_PC11 0
+#define SERIAL_3_PD8_PD9 0
+
+#define AO_CONFIG_MAX_SIZE 1024
+
+#define HAS_EEPROM 1
+#define USE_INTERNAL_FLASH 0
+#define USE_EEPROM_CONFIG 1
+#define USE_STORAGE_CONFIG 0
+#define HAS_USB 1
+#define HAS_BEEP 0
+#define HAS_BATTERY_REPORT 0
+#define HAS_RADIO 1
+#define HAS_TELEMETRY 0
+#define HAS_APRS 0
+#define HAS_ACCEL 0
+
+#define HAS_SPI_1 1
+#define SPI_1_PA5_PA6_PA7 1 /* CC1200 */
+#define SPI_1_PB3_PB4_PB5 0
+#define SPI_1_PE13_PE14_PE15 0
+#define SPI_1_OSPEEDR STM_OSPEEDR_10MHz
+
+#define HAS_SPI_2 0
+#define SPI_2_PB13_PB14_PB15 0
+#define SPI_2_PD1_PD3_PD4 0
+#define SPI_2_OSPEEDR STM_OSPEEDR_10MHz
+
+#define HAS_I2C_1 0
+#define I2C_1_PB8_PB9 0
+
+#define HAS_I2C_2 0
+#define I2C_2_PB10_PB11 0
+
+#define PACKET_HAS_SLAVE 0
+#define PACKET_HAS_MASTER 1
+
+#define LOW_LEVEL_DEBUG 0
+
+#define LED_PORT_0_ENABLE STM_RCC_AHBENR_GPIOAEN
+#define LED_PORT_1_ENABLE STM_RCC_AHBENR_GPIOCEN
+#define LED_PORT_0 (&stm_gpioa)
+#define LED_PORT_1 (&stm_gpioc)
+#define LED_PORT_0_SHIFT 0
+#define LED_PORT_1_SHIFT 0
+#define LED_PIN_RED (4 + LED_PORT_0_SHIFT)
+#define LED_PIN_BLUE (15 + LED_PORT_1_SHIFT)
+#define AO_LED_RED (1 << LED_PIN_RED)
+#define AO_LED_BLUE (1 << LED_PIN_BLUE)
+#define LED_PORT_0_MASK (AO_LED_RED)
+#define LED_PORT_1_MASK (AO_LED_BLUE)
+#define AO_BT_LED AO_LED_BLUE
+
+#define LEDS_AVAILABLE (AO_LED_RED | AO_LED_BLUE)
+
+#define HAS_GPS 0
+#define HAS_FLIGHT 0
+#define HAS_ADC 1
+#define HAS_ADC_TEMP 0
+#define HAS_LOG 0
+
+/*
+ * ADC
+ */
+#define AO_DATA_RING 32
+#define AO_ADC_NUM_SENSE 2
+
+struct ao_adc {
+ int16_t v_batt;
+};
+
+#define AO_ADC_DUMP(p) \
+ printf("tick: %5u %5d batt: %5d\n", \
+ (p)->tick, \
+ (p)->adc.v_batt);
+
+#define AO_ADC_V_BATT 8
+#define AO_ADC_V_BATT_PORT (&stm_gpiob)
+#define AO_ADC_V_BATT_PIN 0
+
+#define AO_ADC_RCC_AHBENR ((1 << STM_RCC_AHBENR_GPIOEEN))
+
+#define AO_NUM_ADC_PIN 1
+
+#define AO_ADC_PIN0_PORT AO_ADC_V_BATT_PORT
+#define AO_ADC_PIN0_PIN AO_ADC_V_BATT_PIN
+
+#define AO_NUM_ADC (AO_NUM_ADC_PIN)
+
+#define AO_ADC_SQ1 AO_ADC_V_BATT
+
+/*
+ * Voltage divider on ADC battery sampler
+ */
+#define AO_BATTERY_DIV_PLUS 51 /* 5.6k */
+#define AO_BATTERY_DIV_MINUS 100 /* 10k */
+
+/*
+ * ADC reference in decivolts
+ */
+#define AO_ADC_REFERENCE_DV 33
+
+/*
+ * BTM
+ */
+#define HAS_BTM 1
+
+#define ao_serial_btm_getchar ao_serial2_getchar
+#define ao_serial_btm_putchar ao_serial2_putchar
+#define _ao_serial_btm_pollchar _ao_serial2_pollchar
+#define _ao_serial_btm_sleep _ao_serial2_sleep
+#define ao_serial_btm_set_speed ao_serial2_set_speed
+#define ao_serial_btm_drain ao_serial2_drain
+#define ao_serial_btm_rx_fifo (ao_stm_usart2.rx_fifo)
+
+#define AO_BTM_INT_PORT (&stm_gpioa)
+#define AO_BTM_INT_PIN 15
+#define AO_BTM_RESET_PORT (&stm_gpiob)
+#define AO_BTM_RESET_PIN 3
+
+/*
+ * Radio (cc1200)
+ */
+
+/* gets pretty close to 434.550 */
+
+#define AO_RADIO_CAL_DEFAULT 5695485
+
+#define AO_FEC_DEBUG 0
+#define AO_CC1200_SPI_CS_PORT (&stm_gpiob)
+#define AO_CC1200_SPI_CS_PIN 10
+#define AO_CC1200_SPI_BUS AO_SPI_1_PA5_PA6_PA7
+#define AO_CC1200_SPI stm_spi1
+
+#define AO_CC1200_INT_PORT (&stm_gpiob)
+#define AO_CC1200_INT_PIN (11)
+
+#define AO_CC1200_INT_GPIO 2
+#define AO_CC1200_INT_GPIO_IOCFG CC1200_IOCFG2
+
+#define HAS_BOOT_RADIO 0
+
+/* Monitor bits */
+#define HAS_MONITOR 1
+#define LEGACY_MONITOR 0
+#define AO_MONITOR_LED AO_LED_RED
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/telebt-v3.0/ao_telebt.c b/src/telebt-v3.0/ao_telebt.c
new file mode 100644
index 00000000..44ee4f3d
--- /dev/null
+++ b/src/telebt-v3.0/ao_telebt.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * 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_exti.h>
+#include <ao_packet.h>
+#include <ao_eeprom.h>
+#include <ao_profile.h>
+#include <ao_btm.h>
+#if HAS_SAMPLE_PROFILE
+#include <ao_sample_profile.h>
+#endif
+
+int
+main(void)
+{
+ ao_clock_init();
+
+ ao_task_init();
+ ao_serial_init();
+ ao_led_init(LEDS_AVAILABLE);
+ ao_led_on(AO_LED_RED);
+ ao_timer_init();
+
+ ao_spi_init();
+ ao_dma_init();
+ ao_exti_init();
+
+ ao_adc_init();
+ ao_btm_init();
+ ao_cmd_init();
+
+ ao_eeprom_init();
+
+ ao_usb_init();
+ ao_radio_init();
+ ao_packet_master_init();
+ ao_monitor_init();
+
+ ao_config_init();
+
+ ao_led_off(AO_LED_RED);
+
+ ao_start_scheduler();
+ return 0;
+}
diff --git a/src/telebt-v3.0/flash-loader/Makefile b/src/telebt-v3.0/flash-loader/Makefile
new file mode 100644
index 00000000..a1c5168f
--- /dev/null
+++ b/src/telebt-v3.0/flash-loader/Makefile
@@ -0,0 +1,8 @@
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=telebt-v3.0
+include $(TOPDIR)/stm/Makefile-flash.defs
diff --git a/src/telebt-v3.0/flash-loader/ao_pins.h b/src/telebt-v3.0/flash-loader/ao_pins.h
new file mode 100644
index 00000000..8711548d
--- /dev/null
+++ b/src/telebt-v3.0/flash-loader/ao_pins.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * 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_PINS_H_
+#define _AO_PINS_H_
+
+/* External crystal at 8MHz */
+#define AO_HSE 8000000
+
+#include <ao_flash_stm_pins.h>
+
+/* Blue LED */
+
+#define AO_BOOT_PIN 1
+#define AO_BOOT_APPLICATION_GPIO stm_gpioc
+#define AO_BOOT_APPLICATION_PIN 15
+#define AO_BOOT_APPLICATION_VALUE 0
+#define AO_BOOT_APPLICATION_MODE AO_EXTI_MODE_PULL_DOWN
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/teledongle-v1.8/.gitignore b/src/teledongle-v1.8/.gitignore
new file mode 100644
index 00000000..9a30cbb6
--- /dev/null
+++ b/src/teledongle-v1.8/.gitignore
@@ -0,0 +1,2 @@
+ao_product.h
+teledongle-*.elf
diff --git a/src/teledongle-v1.8/Makefile b/src/teledongle-v1.8/Makefile
new file mode 100644
index 00000000..6c05ce9f
--- /dev/null
+++ b/src/teledongle-v1.8/Makefile
@@ -0,0 +1,88 @@
+#
+# AltOS build
+#
+#
+
+include ../stm/Makefile.defs
+
+INC = \
+ ao.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_boot.h \
+ ao_pins.h \
+ ao_product.h \
+ ao_task.h \
+ ao_whiten.h \
+ stm32l.h \
+ ao_cc1200.h \
+ ao_cc1200_CC1200.h \
+ Makefile
+
+#PROFILE=ao_profile.c
+#PROFILE_DEF=-DAO_PROFILE=1
+
+#SAMPLE_PROFILE=ao_sample_profile.c \
+# ao_sample_profile_timer.c
+#SAMPLE_PROFILE_DEF=-DHAS_SAMPLE_PROFILE=1
+
+#STACK_GUARD=ao_mpu_stm.c
+#STACK_GUARD_DEF=-DHAS_STACK_GUARD=1
+
+ALTOS_SRC = \
+ ao_boot_chain.c \
+ ao_interrupt.c \
+ ao_product.c \
+ ao_romconfig.c \
+ ao_cc1200.c \
+ ao_cmd.c \
+ ao_config.c \
+ ao_task.c \
+ ao_led.c \
+ ao_stdio.c \
+ ao_panic.c \
+ ao_timer.c \
+ ao_mutex.c \
+ ao_freq.c \
+ ao_dma_stm.c \
+ ao_spi_stm.c \
+ ao_usb_stm.c \
+ ao_exti_stm.c \
+ ao_send_packet.c \
+ ao_eeprom_stm.c \
+ ao_monitor.c \
+ ao_packet_master.c \
+ ao_packet.c
+
+PRODUCT=TeleDongle-v1.8
+PRODUCT_DEF=-DTELEDONGLE
+IDPRODUCT=0x000c
+
+CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) $(SAMPLE_PROFILE_DEF) $(STACK_GUARD_DEF) -Os -g
+
+PROGNAME=teledongle-v1.8
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) ao_teledongle.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ) altos.ld
+ $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+$(OBJ): $(INC)
+
+ao_product.h: ao-make-product.5c ../Version
+ $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean: clean
+
+clean:
+ rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx
+ rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/teledongle-v1.8/ao_pins.h b/src/teledongle-v1.8/ao_pins.h
new file mode 100644
index 00000000..cff3e5e8
--- /dev/null
+++ b/src/teledongle-v1.8/ao_pins.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * 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.
+ */
+
+/* Using TeleMetrum v1.9 board */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define HAS_TASK_QUEUE 1
+
+/* 8MHz High speed external crystal */
+#define AO_HSE 8000000
+
+/* PLLVCO = 96MHz (so that USB will work) */
+#define AO_PLLMUL 12
+#define AO_RCC_CFGR_PLLMUL (STM_RCC_CFGR_PLLMUL_12)
+
+/* SYSCLK = 32MHz (no need to go faster than CPU) */
+#define AO_PLLDIV 3
+#define AO_RCC_CFGR_PLLDIV (STM_RCC_CFGR_PLLDIV_3)
+
+/* HCLK = 32MHz (CPU clock) */
+#define AO_AHB_PRESCALER 1
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+
+/* Run APB1 at 16MHz (HCLK/2) */
+#define AO_APB1_PRESCALER 2
+#define AO_RCC_CFGR_PPRE1_DIV STM_RCC_CFGR_PPRE2_DIV_2
+
+/* Run APB2 at 16MHz (HCLK/2) */
+#define AO_APB2_PRESCALER 2
+#define AO_RCC_CFGR_PPRE2_DIV STM_RCC_CFGR_PPRE2_DIV_2
+
+#define HAS_SERIAL_1 0
+#define USE_SERIAL_1_STDIN 0
+#define SERIAL_1_PB6_PB7 0
+#define SERIAL_1_PA9_PA10 0
+
+#define HAS_SERIAL_2 0
+#define USE_SERIAL_2_STDIN 0
+#define SERIAL_2_PA2_PA3 0
+#define SERIAL_2_PD5_PD6 0
+
+#define HAS_SERIAL_3 0
+#define USE_SERIAL_3_STDIN 0
+#define SERIAL_3_PB10_PB11 0
+#define SERIAL_3_PC10_PC11 0
+#define SERIAL_3_PD8_PD9 0
+
+#define HAS_EEPROM 1
+#define USE_INTERNAL_FLASH 1
+#define USE_STORAGE_CONFIG 0
+#define USE_EEPROM_CONFIG 1
+#define HAS_USB 1
+#define HAS_BEEP 0
+#define HAS_RADIO 1
+#define HAS_TELEMETRY 0
+#define HAS_RSSI 0
+
+#define HAS_SPI_1 0
+#define SPI_1_PA5_PA6_PA7 0 /* Barometer */
+#define SPI_1_PB3_PB4_PB5 0
+#define SPI_1_PE13_PE14_PE15 0 /* Accelerometer */
+
+#define HAS_SPI_2 1
+#define SPI_2_PB13_PB14_PB15 1 /* Radio */
+#define SPI_2_PD1_PD3_PD4 0
+#define SPI_2_OSPEEDR STM_OSPEEDR_10MHz
+
+#define SPI_2_PORT (&stm_gpiob)
+#define SPI_2_SCK_PIN 13
+#define SPI_2_MISO_PIN 14
+#define SPI_2_MOSI_PIN 15
+
+#define PACKET_HAS_SLAVE 0
+#define PACKET_HAS_MASTER 1
+
+#define LOW_LEVEL_DEBUG 0
+
+#define LED_PORT_0_ENABLE STM_RCC_AHBENR_GPIOCEN
+#define LED_PORT_0 (&stm_gpioc)
+#define LED_PORT_0_MASK (0xffff)
+#define LED_PORT_0_SHIFT 0
+#define LED_PIN_RED 8
+#define LED_PIN_GREEN 9
+#define AO_LED_RED (1 << LED_PIN_RED)
+#define AO_LED_GREEN (1 << LED_PIN_GREEN)
+
+#define LEDS_AVAILABLE (AO_LED_RED | AO_LED_GREEN)
+
+#define HAS_GPS 0
+#define HAS_FLIGHT 0
+#define HAS_ADC 0
+#define HAS_LOG 0
+
+/*
+ * Telemetry monitoring
+ */
+#define HAS_MONITOR 1
+#define LEGACY_MONITOR 0
+#define HAS_MONITOR_PUT 1
+#define AO_MONITOR_LED AO_LED_GREEN
+#define AO_MONITOR_BAD AO_LED_RED
+
+/*
+ * Radio (cc1200)
+ */
+
+/* gets pretty close to 434.550 */
+
+#define AO_RADIO_CAL_DEFAULT 5695733
+
+#define AO_FEC_DEBUG 0
+#define AO_CC1200_SPI_CS_PORT (&stm_gpioc)
+#define AO_CC1200_SPI_CS_PIN 5
+#define AO_CC1200_SPI_BUS AO_SPI_2_PB13_PB14_PB15
+#define AO_CC1200_SPI stm_spi2
+
+#define AO_CC1200_INT_PORT (&stm_gpioe)
+#define AO_CC1200_INT_PIN 1
+
+#define AO_CC1200_INT_GPIO 2
+#define AO_CC1200_INT_GPIO_IOCFG CC1200_IOCFG2
+
+/*
+ * Profiling Viterbi decoding
+ */
+
+#ifndef AO_PROFILE
+#define AO_PROFILE 0
+#endif
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/teledongle-v1.8/ao_teledongle.c b/src/teledongle-v1.8/ao_teledongle.c
new file mode 100644
index 00000000..5ce6b15b
--- /dev/null
+++ b/src/teledongle-v1.8/ao_teledongle.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * 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_exti.h>
+#include <ao_packet.h>
+#include <ao_send_packet.h>
+
+int
+main(void)
+{
+ ao_clock_init();
+
+#if HAS_STACK_GUARD
+ ao_mpu_init();
+#endif
+
+ ao_task_init();
+ ao_led_init(LEDS_AVAILABLE);
+ ao_led_on(AO_LED_RED);
+ ao_timer_init();
+
+ ao_spi_init();
+ ao_dma_init();
+ ao_exti_init();
+
+ ao_cmd_init();
+
+ ao_usb_init();
+ ao_radio_init();
+ ao_monitor_init();
+ ao_packet_master_init();
+ ao_send_packet_init();
+
+ ao_config_init();
+
+ ao_led_off(AO_LED_RED);
+ ao_start_scheduler();
+ return 0;
+}
diff --git a/src/teledongle-v3.0/.gitignore b/src/teledongle-v3.0/.gitignore
new file mode 100644
index 00000000..9a30cbb6
--- /dev/null
+++ b/src/teledongle-v3.0/.gitignore
@@ -0,0 +1,2 @@
+ao_product.h
+teledongle-*.elf
diff --git a/src/teledongle-v3.0/Makefile b/src/teledongle-v3.0/Makefile
new file mode 100644
index 00000000..a656cde5
--- /dev/null
+++ b/src/teledongle-v3.0/Makefile
@@ -0,0 +1,79 @@
+#
+# AltOS build
+#
+#
+
+include ../lpc/Makefile.defs
+
+INC = \
+ ao.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_boot.h \
+ ao_pins.h \
+ ao_product.h \
+ ao_task.h \
+ ao_whiten.h \
+ lpc.h \
+ ao_cc1200.h \
+ ao_cc1200_CC1200.h \
+ Makefile
+
+ALTOS_SRC = \
+ ao_boot_chain.c \
+ ao_interrupt.c \
+ ao_product.c \
+ ao_romconfig.c \
+ ao_cc1200.c \
+ ao_cmd.c \
+ ao_config.c \
+ ao_task.c \
+ ao_led_lpc.c \
+ ao_stdio.c \
+ ao_panic.c \
+ ao_timer_lpc.c \
+ ao_mutex.c \
+ ao_freq.c \
+ ao_spi_lpc.c \
+ ao_usb_lpc.c \
+ ao_exti_lpc.c \
+ ao_send_packet.c \
+ ao_monitor.c \
+ ao_packet_master.c \
+ ao_packet.c
+
+PRODUCT=TeleDongle-v3.0
+PRODUCT_DEF=-DTELEDONGLE
+IDPRODUCT=0x000c
+
+CFLAGS = $(PRODUCT_DEF) $(LPC_CFLAGS) -Os -g
+
+PROGNAME=teledongle-v3.0
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) ao_teledongle.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ) altos.ld
+ $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+$(OBJ): $(INC)
+
+ao_product.h: ao-make-product.5c ../Version
+ $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+load: $(PROG)
+ lpc-load $(PROG)
+
+distclean: clean
+
+clean:
+ rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx
+ rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/teledongle-v3.0/ao_pins.h b/src/teledongle-v3.0/ao_pins.h
new file mode 100644
index 00000000..52a86fcb
--- /dev/null
+++ b/src/teledongle-v3.0/ao_pins.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * 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.
+ */
+
+/* Using TeleDongle v3.0 board */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define AO_STACK_SIZE 320
+
+#define HAS_TASK_QUEUE 1
+
+#define IS_FLASH_LOADER 0
+
+/* Crystal on the board */
+#define AO_LPC_CLKIN 12000000
+
+/* Main clock frequency. 48MHz for USB so we don't use the USB PLL */
+#define AO_LPC_CLKOUT 48000000
+
+/* System clock frequency */
+#define AO_LPC_SYSCLK 24000000
+
+#define HAS_EEPROM 0
+#define USE_INTERNAL_FLASH 0
+#define USE_STORAGE_CONFIG 0
+#define USE_EEPROM_CONFIG 0
+
+#define HAS_USB 1
+#define HAS_USB_CONNECT 0
+#define HAS_USB_VBUS 0
+#define HAS_USB_PULLUP 1
+#define AO_USB_PULLUP_PORT 0
+#define AO_USB_PULLUP_PIN 20
+
+#define HAS_BEEP 0
+#define HAS_RADIO 1
+#define HAS_TELEMETRY 0
+#define HAS_RSSI 0
+
+#define HAS_SPI_0 1
+#define SPI_SCK0_P0_6 1
+
+#define PACKET_HAS_SLAVE 0
+#define PACKET_HAS_MASTER 1
+
+#define LOW_LEVEL_DEBUG 0
+
+#define LED_PORT 0
+#define LED_PIN_RED 14
+#define LED_PIN_GREEN 7
+#define AO_LED_RED (1 << LED_PIN_RED)
+#define AO_LED_GREEN (1 << LED_PIN_GREEN)
+
+#define LEDS_AVAILABLE (AO_LED_RED | AO_LED_GREEN)
+
+#define HAS_GPS 0
+#define HAS_FLIGHT 0
+#define HAS_ADC 0
+#define HAS_LOG 0
+
+/*
+ * Telemetry monitoring
+ */
+#define HAS_MONITOR 1
+#define LEGACY_MONITOR 0
+#define HAS_MONITOR_PUT 1
+#define AO_MONITOR_LED AO_LED_GREEN
+#define AO_MONITOR_BAD AO_LED_RED
+
+/*
+ * Radio (cc1200)
+ */
+
+/* gets pretty close to 434.550 */
+
+#define AO_RADIO_CAL_DEFAULT 5695733
+
+#define AO_FEC_DEBUG 0
+#define AO_CC1200_SPI_CS_PORT 0
+#define AO_CC1200_SPI_CS_PIN 3
+#define AO_CC1200_SPI_BUS 0
+#define AO_CC1200_SPI 0
+
+#define AO_CC1200_INT_PORT 0
+#define AO_CC1200_INT_PIN 2
+
+#define AO_CC1200_INT_GPIO 2
+#define AO_CC1200_INT_GPIO_IOCFG CC1200_IOCFG2
+
+/*
+ * Profiling Viterbi decoding
+ */
+
+#ifndef AO_PROFILE
+#define AO_PROFILE 0
+#endif
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/teledongle-v3.0/ao_teledongle.c b/src/teledongle-v3.0/ao_teledongle.c
new file mode 100644
index 00000000..02b93efe
--- /dev/null
+++ b/src/teledongle-v3.0/ao_teledongle.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * 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_exti.h>
+#include <ao_packet.h>
+#include <ao_send_packet.h>
+
+int
+main(void)
+{
+ ao_clock_init();
+
+#if HAS_STACK_GUARD
+ ao_mpu_init();
+#endif
+
+ ao_task_init();
+ ao_led_init(LEDS_AVAILABLE);
+ ao_led_on(AO_LED_RED);
+ ao_timer_init();
+
+ ao_spi_init();
+ ao_exti_init();
+
+ ao_cmd_init();
+
+ ao_usb_init();
+ ao_radio_init();
+ ao_monitor_init();
+ ao_packet_master_init();
+ ao_send_packet_init();
+
+ ao_config_init();
+
+ ao_led_off(AO_LED_RED);
+ ao_start_scheduler();
+ return 0;
+}
diff --git a/src/teledongle-v3.0/flash-loader/Makefile b/src/teledongle-v3.0/flash-loader/Makefile
new file mode 100644
index 00000000..b8325114
--- /dev/null
+++ b/src/teledongle-v3.0/flash-loader/Makefile
@@ -0,0 +1,7 @@
+#
+# AltOS flash loader build
+#
+
+TOPDIR=../..
+HARDWARE=teledongle-v3.0
+include $(TOPDIR)/lpc/Makefile-flash.defs
diff --git a/src/teledongle-v3.0/flash-loader/ao_pins.h b/src/teledongle-v3.0/flash-loader/ao_pins.h
new file mode 100644
index 00000000..aee5be27
--- /dev/null
+++ b/src/teledongle-v3.0/flash-loader/ao_pins.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * 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_PINS_H_
+#define _AO_PINS_H_
+
+#include <ao_flash_lpc_pins.h>
+
+/* Debug port TXD (pin 6) */
+
+#define AO_BOOT_PIN 1
+#define AO_BOOT_APPLICATION_GPIO 0
+#define AO_BOOT_APPLICATION_PIN 19
+#define AO_BOOT_APPLICATION_VALUE 1
+#define AO_BOOT_APPLICATION_MODE AO_EXTI_MODE_PULL_UP
+
+#define HAS_USB_PULLUP 1
+#define AO_USB_PULLUP_PORT 0
+#define AO_USB_PULLUP_PIN 20
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/test/.gitignore b/src/test/.gitignore
index b8b2513e..9237780b 100644
--- a/src/test/.gitignore
+++ b/src/test/.gitignore
@@ -15,5 +15,6 @@ ao_fat_test
ao_fec_test
ao_flight_test_mm
ao_flight_test_noisy_accel
+ao_flight_test_metrum
ao_micropeak_test
ao_aes_test
diff --git a/src/test/ao_flight_test.c b/src/test/ao_flight_test.c
index 8b737ca1..fbbc4bd9 100644
--- a/src/test/ao_flight_test.c
+++ b/src/test/ao_flight_test.c
@@ -600,7 +600,7 @@ ao_insert(void)
#if 1
printf("%7.2f height %8.2f accel %8.3f "
-#if TELEMEGA && 0
+#if TELEMEGA && 1
"angle %5d "
"accel_x %8.3f accel_y %8.3f accel_z %8.3f gyro_x %8.3f gyro_y %8.3f gyro_z %8.3f mag_x %8d mag_y %8d, mag_z %8d mag_angle %4d "
#endif
@@ -608,7 +608,7 @@ ao_insert(void)
time,
height,
accel,
-#if TELEMEGA && 0
+#if TELEMEGA && 1
ao_sample_orient,
ao_mpu6000_accel(ao_data_static.mpu6000.accel_x),
@@ -764,6 +764,8 @@ ao_sleep(void *wchan)
ao_data_static.hmc5883.z = int16(bytes, 24);
#if HAS_MMA655X
ao_data_static.mma655x = int16(bytes, 26);
+ if (ao_config.pad_orientation != AO_PAD_ORIENTATION_ANTENNA_UP)
+ ao_data_static.mma655x = ao_data_accel_invert(ao_data_static.mma655x);
#endif
ao_records_read++;
ao_insert();
@@ -900,6 +902,9 @@ ao_sleep(void *wchan)
} else if (nword >= 3 && strcmp(words[0], "Apogee") == 0 &&
strcmp(words[1], "lockout:") == 0) {
ao_config.apogee_lockout = atoi(words[2]);
+ } else if (nword >= 3 && strcmp(words[0], "Pad") == 0 &&
+ strcmp(words[1], "orientation:") == 0) {
+ ao_config.pad_orientation = atoi(words[2]);
} else if (nword >= 36 && strcmp(words[0], "CALL") == 0) {
tick = atoi(words[10]);
if (!ao_flight_started) {
diff --git a/src/test/ao_gps_test_ublox.c b/src/test/ao_gps_test_ublox.c
index 5ea205d6..83efbb4f 100644
--- a/src/test/ao_gps_test_ublox.c
+++ b/src/test/ao_gps_test_ublox.c
@@ -59,6 +59,7 @@ struct ao_telemetry_location {
typedef int32_t gps_alt_t;
#define AO_TELEMETRY_LOCATION_ALTITUDE(l) (((gps_alt_t) (l)->altitude_high << 16) | ((l)->altitude_low))
+#define AO_GPS_ORIG_ALTITUDE(l) AO_TELEMETRY_LOCATION_ALTITUDE(l)
#define AO_TELEMETRY_LOCATION_SET_ALTITUDE(l,a) (((l)->altitude_high = (a) >> 16), \
((l)->altitude_low = (a)))
diff --git a/src/microwater/.gitignore b/src/usbtrng-v2.0/.gitignore
index 0573d989..93b38d11 100644
--- a/src/microwater/.gitignore
+++ b/src/usbtrng-v2.0/.gitignore
@@ -1,2 +1,2 @@
ao_product.h
-microwater-*
+usbtrng-*
diff --git a/src/usbtrng-v2.0/Makefile b/src/usbtrng-v2.0/Makefile
new file mode 100644
index 00000000..abbdbbcc
--- /dev/null
+++ b/src/usbtrng-v2.0/Makefile
@@ -0,0 +1,68 @@
+#
+# AltOS build
+#
+#
+
+include ../stmf0/Makefile.defs
+
+INC = \
+ ao.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_pins.h \
+ ao_product.h \
+ ao_task.h \
+ stm32f0.h
+
+#
+# Common AltOS sources
+#
+ALTOS_SRC = \
+ ao_interrupt.c \
+ ao_timer.c \
+ ao_panic.c \
+ ao_mutex.c \
+ ao_dma_stm.c \
+ ao_adc_fast.c \
+ ao_crc_stm.c \
+ ao_stdio.c \
+ ao_led.c \
+ ao_romconfig.c \
+ ao_boot_chain.c \
+ ao_cmd.c \
+ ao_usb_stm.c \
+ ao_task.c \
+ ao_product.c
+
+PRODUCT=usbtrng-v2.0
+PRODUCT_DEF=-DUSBTRNG_V_2_0
+IDPRODUCT=0x0028
+
+CFLAGS = $(PRODUCT_DEF) $(STMF0_CFLAGS) -g -Os
+
+PROGNAME=usbtrng-v2.0
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) ao_usbtrng.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ) altos.ld
+ $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+ao_product.h: ao-make-product.5c ../Version
+ $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+$(OBJ): $(INC)
+
+distclean: clean
+
+clean:
+ rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx
+ rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/usbtrng-v2.0/ao_pins.h b/src/usbtrng-v2.0/ao_pins.h
new file mode 100644
index 00000000..23759444
--- /dev/null
+++ b/src/usbtrng-v2.0/ao_pins.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * 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_PINS_H_
+#define _AO_PINS_H_
+
+#define LED_PORT_ENABLE STM_RCC_AHBENR_IOPAEN
+#define LED_PORT (&stm_gpioa)
+#define LED_PIN_RED 2
+#define LED_PIN_GREEN 3
+#define AO_LED_RED (1 << LED_PIN_RED)
+#define AO_LED_GREEN (1 << LED_PIN_GREEN)
+
+#define LEDS_AVAILABLE (AO_LED_RED | AO_LED_GREEN)
+
+#define HAS_BEEP 0
+
+/* 48MHz clock based on USB */
+#define AO_HSI48 1
+
+/* HCLK = 48MHz */
+#define AO_AHB_PRESCALER 1
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+
+/* APB = 48MHz */
+#define AO_APB_PRESCALER 1
+#define AO_RCC_CFGR_PPRE_DIV STM_RCC_CFGR_PPRE_DIV_1
+
+#define HAS_USB 1
+#define AO_USB_DIRECTIO 1
+#define AO_PA11_PA12_RMP 0
+
+#define IS_FLASH_LOADER 0
+
+/* ADC */
+
+#define AO_ADC_PIN0_PORT (&stm_gpioa)
+#define AO_ADC_PIN0_PIN 6
+#define AO_ADC_PIN0_CH 6
+
+#define AO_ADC_RCC_AHBENR ((1 << STM_RCC_AHBENR_IOPAEN))
+
+#define AO_NUM_ADC 1
+
+/* CRC */
+#define AO_CRC_WIDTH 32
+#define AO_CRC_INIT 0xffffffff
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/usbtrng-v2.0/ao_usbtrng.c b/src/usbtrng-v2.0/ao_usbtrng.c
new file mode 100644
index 00000000..e1f43cdd
--- /dev/null
+++ b/src/usbtrng-v2.0/ao_usbtrng.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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_adc_fast.h>
+#include <ao_crc.h>
+
+static void
+ao_trng_fetch(void)
+{
+ static uint16_t *buffer[2];
+ uint32_t kbytes = 1;
+ uint32_t count;
+ int usb_buf_id;
+ int i;
+ uint16_t *buf;
+ uint32_t *rnd;
+
+ if (!buffer[0]) {
+ buffer[0] = ao_usb_alloc();
+ buffer[1] = ao_usb_alloc();
+ if (!buffer[0])
+ return;
+ }
+
+ ao_cmd_decimal();
+ if (ao_cmd_status == ao_cmd_success)
+ kbytes = ao_cmd_lex_u32;
+ else
+ ao_cmd_status = ao_cmd_success;
+ usb_buf_id = 0;
+ count = kbytes * (1024/AO_USB_IN_SIZE);
+
+ ao_crc_reset();
+
+ ao_led_on(AO_LED_GREEN);
+ while (count--) {
+ buf = buffer[usb_buf_id];
+// printf ("before get: head %3d tail %3d running %d\n", ao_adc_ring_head, ao_adc_ring_tail, ao_adc_running); flush();
+ rnd = (uint32_t *) ao_adc_get(AO_USB_IN_SIZE); /* one 16-bit value per output byte */
+// printf ("after get: head %3d tail %3d running %d\n", ao_adc_ring_head, ao_adc_ring_tail, ao_adc_running); flush();
+ for (i = 0; i < 32; i++)
+ *buf++ = ao_crc_in_32_out_16(*rnd++);
+ ao_adc_ack(AO_USB_IN_SIZE);
+// printf ("after ack: head %3d tail %3d running %d\n", ao_adc_ring_head, ao_adc_ring_tail, ao_adc_running); flush();
+ ao_led_toggle(AO_LED_GREEN|AO_LED_RED);
+ ao_usb_write(buffer[usb_buf_id], AO_USB_IN_SIZE);
+ ao_led_toggle(AO_LED_GREEN|AO_LED_RED);
+ usb_buf_id = 1-usb_buf_id;
+ }
+ ao_led_off(AO_LED_GREEN|AO_LED_RED);
+ flush();
+}
+
+static const struct ao_cmds usbtrng_cmds[] = {
+ { ao_trng_fetch, "f <kbytes>\0Fetch a block of numbers" },
+ { 0, NULL },
+};
+
+void main(void)
+{
+ ao_led_init(LEDS_AVAILABLE);
+ ao_led_on(AO_LED_RED);
+ ao_clock_init();
+ ao_task_init();
+ ao_timer_init();
+ ao_dma_init();
+ ao_adc_init();
+ ao_crc_init();
+
+ ao_cmd_init();
+
+ ao_usb_init();
+
+ ao_cmd_register(usbtrng_cmds);
+ ao_led_off(AO_LED_RED);
+
+ ao_start_scheduler();
+}
diff --git a/src/usbtrng-v2.0/flash-loader/.gitignore b/src/usbtrng-v2.0/flash-loader/.gitignore
new file mode 100644
index 00000000..32dbbbcf
--- /dev/null
+++ b/src/usbtrng-v2.0/flash-loader/.gitignore
@@ -0,0 +1,2 @@
+ao_product.h
+usbtrng*
diff --git a/src/usbtrng-v2.0/flash-loader/Makefile b/src/usbtrng-v2.0/flash-loader/Makefile
new file mode 100644
index 00000000..a2bf199f
--- /dev/null
+++ b/src/usbtrng-v2.0/flash-loader/Makefile
@@ -0,0 +1,8 @@
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=usbtrng-v2.0
+include $(TOPDIR)/stmf0/Makefile-flash.defs
diff --git a/src/usbtrng-v2.0/flash-loader/ao_pins.h b/src/usbtrng-v2.0/flash-loader/ao_pins.h
new file mode 100644
index 00000000..9fa8b715
--- /dev/null
+++ b/src/usbtrng-v2.0/flash-loader/ao_pins.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * 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_PINS_H_
+#define _AO_PINS_H_
+
+#include <ao_flash_stm_pins.h>
+
+/* Red LED */
+
+#define AO_BOOT_PIN 1
+#define AO_BOOT_APPLICATION_GPIO stm_gpioa
+#define AO_BOOT_APPLICATION_PIN 2
+#define AO_BOOT_APPLICATION_VALUE 0
+#define AO_BOOT_APPLICATION_MODE AO_EXTI_MODE_PULL_DOWN
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/usbtrng/ao_pins.h b/src/usbtrng/ao_pins.h
index b1fa6eb9..0f2b1ea6 100644
--- a/src/usbtrng/ao_pins.h
+++ b/src/usbtrng/ao_pins.h
@@ -71,3 +71,5 @@
*/
#define HAS_SCK1 0
#define HAS_MOSI1 0
+
+#define AO_ADC_6 1
diff --git a/src/usbtrng/ao_usbtrng.c b/src/usbtrng/ao_usbtrng.c
index 6b4d20fe..66e12337 100644
--- a/src/usbtrng/ao_usbtrng.c
+++ b/src/usbtrng/ao_usbtrng.c
@@ -18,7 +18,53 @@
#include <ao.h>
#define AO_TRNG_SPI_BUS 1
-#define AO_TRNG_SPI_SPEED AO_SPI_SPEED_250kHz
+
+static uint32_t spi_speed = AO_SPI_SPEED_4MHz;
+
+#define AO_TRNG_SPI_BUF 1024
+
+#if 0
+
+static uint8_t *spi_buf;
+static uint8_t *spi_head, *spi_tail;
+static uint8_t spi_wakeup;
+
+static void
+ao_trng_run(void)
+{
+ int this_time;
+ uint8_t *end;
+
+ if (!spi_buf)
+ spi_buf = ao_usb_alloc(AO_TRNG_SPI_BUF);
+ flush();
+ ao_spi_get(AO_TRNG_SPI_BUS, spi_speed);
+ spi_tail = spi_buf;
+ spi_head = spi_buf;
+ ao_spi_recv_ring_start(spi_buf, AO_TRNG_SPI_BUF, &spi_head, &spi_tail, &spi_wakeup, AO_TRNG_SPI_BUS);
+ while (!ao_usb_out_avail) {
+ ao_arch_block_interrupts();
+ while (spi_head == spi_tail) {
+ spi_wakeup = 0;
+ ao_sleep(&spi_wakeup);
+ }
+ ao_arch_release_interrupts();
+ if (spi_tail > spi_head)
+ end = spi_buf + AO_TRNG_SPI_BUF;
+ else
+ end = spi_head;
+ this_time = end - spi_tail;
+ if (this_time > 64)
+ this_time = 64;
+ ao_usb_write(spi_tail, this_time);
+ spi_tail += this_time;
+ if (spi_tail == spi_buf + AO_TRNG_SPI_BUF)
+ spi_tail = spi_buf;
+ }
+ ao_spi_put(AO_TRNG_SPI_BUS);
+ getchar();
+}
+
static void
ao_trng_test(void)
@@ -26,16 +72,153 @@ ao_trng_test(void)
static uint8_t random[32];
uint8_t i;
- ao_spi_get(AO_TRNG_SPI_BUS, AO_TRNG_SPI_SPEED);
+ ao_spi_get(AO_TRNG_SPI_BUS, spi_speed);
ao_spi_recv(random, sizeof (random), AO_TRNG_SPI_BUS);
ao_spi_put(AO_TRNG_SPI_BUS);
for (i = 0; i < sizeof (random); i++)
printf (" %02x", random[i]);
printf ("\n");
}
+#endif
+
+#define ADC_RING_SIZE 512
+
+static uint8_t *adc_ring;
+static uint16_t adc_head, adc_tail;
+static uint16_t adc_wake;
+
+void lpc_adc_isr(void)
+{
+ uint16_t avail;
+ uint16_t this, next;
+
+ this = adc_head;
+ next = (this + 1) & (ADC_RING_SIZE - 1);
+ if (next == adc_tail) {
+ lpc_adc.inten = 0;
+ return;
+ }
+ adc_ring[this] = lpc_adc.dr[6] >> 8;
+ adc_head = next;
+
+ /* If there are enough entries, wake up any waiters
+ */
+ avail = (next - adc_tail) & (ADC_RING_SIZE - 1);
+ if (avail >= adc_wake) {
+ adc_wake = 0;
+ ao_wakeup(&adc_wake);
+ }
+}
+
+#define AO_ADC_CLKDIV (AO_LPC_SYSCLK / 450000)
+
+static void
+ao_trng_adc_init(void)
+{
+ adc_ring = ao_usb_alloc(ADC_RING_SIZE);
+
+ lpc_scb.sysahbclkctrl |= (1 << LPC_SCB_SYSAHBCLKCTRL_ADC);
+ lpc_scb.pdruncfg &= ~(1 << LPC_SCB_PDRUNCFG_ADC_PD);
+
+ /* Enable interrupt when AO_ADC_6 is complete */
+ lpc_adc.inten = 0;
+
+ lpc_nvic_set_enable(LPC_ISR_ADC_POS);
+ lpc_nvic_set_priority(LPC_ISR_ADC_POS, AO_LPC_NVIC_CLOCK_PRIORITY);
+
+#if AO_ADC_0
+ ao_enable_analog(0, 11, 0);
+#endif
+#if AO_ADC_1
+ ao_enable_analog(0, 12, 1);
+#endif
+#if AO_ADC_2
+ ao_enable_analog(0, 13, 2);
+#endif
+#if AO_ADC_3
+ ao_enable_analog(0, 14, 3);
+#endif
+#if AO_ADC_4
+ ao_enable_analog(0, 15, 4);
+#endif
+#if AO_ADC_5
+ ao_enable_analog(0, 16, 5);
+#endif
+#if AO_ADC_6
+ ao_enable_analog(0, 22, 6);
+#endif
+#if AO_ADC_7
+ ao_enable_analog(0, 23, 7);
+#endif
+
+ lpc_adc.cr = ((1 << (LPC_ADC_CR_SEL + 6)) |
+ (AO_ADC_CLKDIV << LPC_ADC_CR_CLKDIV) |
+ (1 << LPC_ADC_CR_BURST) |
+ (LPC_ADC_CR_CLKS_9 << LPC_ADC_CR_CLKS));
+}
+
+static void
+ao_trng_adc_dump(void)
+{
+ int i;
+
+ while (((adc_head - adc_tail) & (ADC_RING_SIZE - 1)) < 16) {
+ lpc_adc.inten = (1 << (LPC_ADC_INTEN_ADINTEN + 6));
+ adc_wake = 16;
+ ao_sleep(&adc_wake);
+ }
+ printf("adc_head %d tail %d\n", adc_head, adc_tail);
+
+ for (i = 0; i < 16; i++) {
+ printf(" %4d", adc_ring[adc_tail]);
+ adc_tail = (adc_tail + 1) & (ADC_RING_SIZE - 1);
+ }
+ printf("\n");
+ lpc_adc.inten = 0;
+}
+
+static void
+ao_trng_run(void)
+{
+ uint16_t this_time;
+ flush();
+
+ while (!ao_usb_out_avail) {
+ ao_arch_block_interrupts();
+ while (((adc_head - adc_tail) & (ADC_RING_SIZE - 1)) < 64) {
+ lpc_adc.inten = (1 << (LPC_ADC_INTEN_ADINTEN + 6));
+ adc_wake = 64;
+ ao_sleep(&adc_wake);
+ }
+ ao_arch_release_interrupts();
+
+ this_time = ADC_RING_SIZE - adc_tail;
+ if (this_time > 64)
+ this_time = 64;
+ ao_usb_write(&adc_ring[adc_tail], this_time);
+ adc_tail = (adc_tail + this_time) & (ADC_RING_SIZE - 1);
+ }
+ lpc_adc.inten = 0;
+}
+
+static void
+ao_trng_speed(void)
+{
+ ao_cmd_decimal();
+
+ if (ao_cmd_lex_u32 == 0 || ao_cmd_status != ao_cmd_success) {
+ ao_cmd_status = ao_cmd_success;
+ printf ("Current spi speed %d\n", spi_speed);
+ } else {
+ spi_speed = ao_cmd_lex_u32;
+ }
+}
static const struct ao_cmds ao_trng_cmds[] = {
- { ao_trng_test, "R\0Dump some random numbers" },
+// { ao_trng_test, "R\0Dump some random numbers" },
+ { ao_trng_run, "s\0Send random bits until char" },
+ { ao_trng_speed, "S <speed>\0Set SPI speed (48MHz/speed)" },
+ { ao_trng_adc_dump, "a\0Dump ADC data" },
{ 0, NULL }
};
@@ -46,15 +229,15 @@ main(void)
ao_task_init();
ao_timer_init();
- ao_spi_init();
+// ao_spi_init();
ao_usb_init();
+ ao_trng_adc_init();
+
ao_serial_init();
ao_led_init(LEDS_AVAILABLE);
- ao_led_on(AO_LED_GREEN);
-
ao_cmd_init();
ao_cmd_register(ao_trng_cmds);