summaryrefslogtreecommitdiff
path: root/src/stmf0
diff options
context:
space:
mode:
Diffstat (limited to 'src/stmf0')
-rw-r--r--src/stmf0/Makefile-stmf0.defs7
-rw-r--r--src/stmf0/altos-raw.ld85
-rw-r--r--src/stmf0/altos.ld8
-rw-r--r--src/stmf0/ao_adc_stm.c340
-rw-r--r--src/stmf0/ao_arch.h7
-rw-r--r--src/stmf0/ao_arch_funcs.h13
-rw-r--r--src/stmf0/ao_beep_stm.c389
-rw-r--r--src/stmf0/ao_crc.h3
-rw-r--r--src/stmf0/ao_flash_stm.c13
-rw-r--r--src/stmf0/ao_interrupt.c2
-rw-r--r--src/stmf0/ao_serial_stm.c500
-rw-r--r--src/stmf0/ao_spi_stm.c6
-rw-r--r--src/stmf0/ao_spi_stm_slave.c339
-rw-r--r--src/stmf0/stm32f0.h159
14 files changed, 1842 insertions, 29 deletions
diff --git a/src/stmf0/Makefile-stmf0.defs b/src/stmf0/Makefile-stmf0.defs
index f3296b69..f2c53499 100644
--- a/src/stmf0/Makefile-stmf0.defs
+++ b/src/stmf0/Makefile-stmf0.defs
@@ -4,7 +4,7 @@ endif
include $(TOPDIR)/Makedefs
-vpath % $(TOPDIR)/stmf0:$(TOPDIR)/product:$(TOPDIR)/drivers:$(TOPDIR)/kernel:$(TOPDIR)/util:$(TOPDIR)/kalman:$(TOPDIR/aes):$(TOPDIR):$(TOPDIR)/math
+vpath % $(TOPDIR)/stmf0:$(TOPDIR)/product:$(TOPDIR)/drivers:$(TOPDIR)/kernel:$(TOPDIR)/util:$(TOPDIR)/kalman:$(TOPDIR)/aes:$(TOPDIR):$(TOPDIR)/math:$(TOPDIR)/lisp
vpath make-altitude $(TOPDIR)/util
vpath make-kalman $(TOPDIR)/util
vpath kalman.5c $(TOPDIR)/kalman
@@ -27,7 +27,10 @@ CC=$(ARM_CC)
WARN_FLAGS=-Wall -Wextra -Werror -Wcast-align
-AO_CFLAGS=-I. -I$(TOPDIR)/stmf0 -I$(TOPDIR)/kernel -I$(TOPDIR)/drivers -I$(TOPDIR)/product -I$(TOPDIR) -I$(TOPDIR)/math $(PDCLIB_INCLUDES)
+AO_CFLAGS=-I. -I$(TOPDIR)/stmf0 -I$(TOPDIR)/kernel -I$(TOPDIR)/drivers \
+ -I$(TOPDIR)/product -I$(TOPDIR) -I$(TOPDIR)/math -I$(TOPDIR)/lisp \
+ $(PDCLIB_INCLUDES)
+
STMF0_CFLAGS=-std=gnu99 -mlittle-endian -mcpu=cortex-m0 -mthumb\
-ffreestanding -nostdlib $(AO_CFLAGS) $(WARN_FLAGS)
diff --git a/src/stmf0/altos-raw.ld b/src/stmf0/altos-raw.ld
new file mode 100644
index 00000000..eb285e07
--- /dev/null
+++ b/src/stmf0/altos-raw.ld
@@ -0,0 +1,85 @@
+/*
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 = 0x08000000, LENGTH = 32K
+ 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 : {
+ __text_start__ = .;
+ *(.interrupt) /* Interrupt vectors */
+ } > rom
+
+ .text ORIGIN(rom) + 0x100 : {
+
+ /* 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__ = .;
+
+ /* 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/altos.ld b/src/stmf0/altos.ld
index 8f8933c6..74fdf3ea 100644
--- a/src/stmf0/altos.ld
+++ b/src/stmf0/altos.ld
@@ -55,10 +55,16 @@ SECTIONS {
ao_product.o(.romconfig*)
*(.text*) /* Executable code */
+ } > rom
+
+ .ARM.exidx : {
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
- *(.rodata*) /* Constants */
+ } > rom
+ .rodata : {
+ *(.rodata*) /* Constants */
} > rom
+
__text_end__ = .;
/* Boot data which must live at the start of ram so that
diff --git a/src/stmf0/ao_adc_stm.c b/src/stmf0/ao_adc_stm.c
new file mode 100644
index 00000000..2b23dc50
--- /dev/null
+++ b/src/stmf0/ao_adc_stm.c
@@ -0,0 +1,340 @@
+/*
+ * 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_data.h>
+
+#define AO_ADC_DEBUG 0
+
+static uint8_t ao_adc_ready;
+
+/*
+ * Callback from DMA ISR
+ *
+ * Mark time in ring, shut down DMA engine
+ */
+static void ao_adc_done(int index)
+{
+ (void) index;
+ /* Clear ISR bits */
+ stm_adc.isr = ((1 << STM_ADC_ISR_AWD) |
+ (1 << STM_ADC_ISR_OVR) |
+ (1 << STM_ADC_ISR_EOSEQ) |
+ (1 << STM_ADC_ISR_EOC));
+
+ AO_DATA_PRESENT(AO_DATA_ADC);
+ ao_dma_done_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1));
+ if (ao_data_present == AO_DATA_ALL) {
+#if HAS_MS5607
+ ao_data_ring[ao_data_head].ms5607_raw = ao_ms5607_current;
+#endif
+#if HAS_MMA655X
+ ao_data_ring[ao_data_head].mma655x = ao_mma655x_current;
+#endif
+#if HAS_HMC5883
+ ao_data_ring[ao_data_head].hmc5883 = ao_hmc5883_current;
+#endif
+#if HAS_MPU6000
+ ao_data_ring[ao_data_head].mpu6000 = ao_mpu6000_current;
+#endif
+ ao_data_ring[ao_data_head].tick = ao_tick_count;
+ ao_data_head = ao_data_ring_next(ao_data_head);
+ ao_wakeup((void *) &ao_data_head);
+ }
+ ao_adc_ready = 1;
+}
+
+/*
+ * Start the ADC sequence using the DMA engine
+ */
+void
+ao_adc_poll(void)
+{
+ if (!ao_adc_ready)
+ return;
+ ao_adc_ready = 0;
+ stm_adc.isr = 0;
+ ao_dma_set_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1),
+ &stm_adc.dr,
+ (void *) (&ao_data_ring[ao_data_head].adc),
+ AO_NUM_ADC,
+ (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) |
+ (1 << STM_DMA_CCR_TCIE));
+ ao_dma_set_isr(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1), ao_adc_done);
+ ao_dma_start(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1));
+
+ stm_adc.cr |= (1 << STM_ADC_CR_ADSTART);
+}
+
+static void
+ao_adc_dump(void)
+{
+ struct ao_data packet;
+
+ ao_data_get(&packet);
+ AO_ADC_DUMP(&packet);
+}
+
+#if AO_ADC_DEBUG
+static void
+ao_adc_one(void)
+{
+ int ch;
+ uint16_t value;
+
+ ao_cmd_decimal();
+ if (ao_cmd_status != ao_cmd_success)
+ return;
+ ch = ao_cmd_lex_i;
+ if (ch < 0 || AO_NUM_ADC <= ch) {
+ ao_cmd_status = ao_cmd_syntax_error;
+ return;
+ }
+
+ ao_timer_set_adc_interval(0);
+ ao_delay(1);
+
+ printf("At top, data %u isr %04x cr %04x\n", stm_adc.dr, stm_adc.isr, stm_adc.cr);
+
+ if (stm_adc.cr & (1 << STM_ADC_CR_ADEN)) {
+ printf("Disabling\n"); flush();
+ stm_adc.cr |= (1 << STM_ADC_CR_ADDIS);
+ while (stm_adc.cr & (1 << STM_ADC_CR_ADDIS))
+ ;
+ printf("Disabled\n"); flush();
+ }
+
+ /* Turn off everything */
+ stm_adc.cr &= ~((1 << STM_ADC_CR_ADCAL) |
+ (1 << STM_ADC_CR_ADSTP) |
+ (1 << STM_ADC_CR_ADSTART) |
+ (1 << STM_ADC_CR_ADEN));
+
+ printf("After disable, ADC status %04x\n", stm_adc.cr);
+
+ /* Configure */
+ stm_adc.cfgr1 = ((0 << STM_ADC_CFGR1_AWDCH) | /* analog watchdog channel 0 */
+ (0 << STM_ADC_CFGR1_AWDEN) | /* Disable analog watchdog */
+ (0 << STM_ADC_CFGR1_AWDSGL) | /* analog watchdog on all channels */
+ (0 << STM_ADC_CFGR1_DISCEN) | /* Not discontinuous mode. All channels converted with one trigger */
+ (0 << STM_ADC_CFGR1_AUTOOFF) | /* Leave ADC running */
+ (1 << STM_ADC_CFGR1_WAIT) | /* Wait for data to be read before next conversion */
+ (0 << STM_ADC_CFGR1_CONT) | /* only one set of conversions per trigger */
+ (1 << STM_ADC_CFGR1_OVRMOD) | /* overwrite on overrun */
+ (STM_ADC_CFGR1_EXTEN_DISABLE << STM_ADC_CFGR1_EXTEN) | /* SW trigger */
+ (0 << STM_ADC_CFGR1_ALIGN) | /* Align to LSB */
+ (STM_ADC_CFGR1_RES_12 << STM_ADC_CFGR1_RES) | /* 12 bit resolution */
+ (STM_ADC_CFGR1_SCANDIR_UP << STM_ADC_CFGR1_SCANDIR) | /* scan 0 .. n */
+ (STM_ADC_CFGR1_DMACFG_ONESHOT << STM_ADC_CFGR1_DMACFG) | /* one set of conversions then stop */
+ (0 << STM_ADC_CFGR1_DMAEN)); /* disable DMA */
+
+ stm_adc.chselr = (1 << ch);
+
+ /* Longest sample time */
+ stm_adc.smpr = STM_ADC_SMPR_SMP_41_5 << STM_ADC_SMPR_SMP;
+
+ printf("Before enable, ADC status %04x\n", stm_adc.cr); flush();
+ /* Enable */
+ stm_adc.cr |= (1 << STM_ADC_CR_ADEN);
+ while ((stm_adc.isr & (1 << STM_ADC_ISR_ADRDY)) == 0)
+ ;
+
+ /* Start */
+ stm_adc.cr |= (1 << STM_ADC_CR_ADSTART);
+
+ /* Wait for conversion complete */
+ while (!(stm_adc.isr & (1 << STM_ADC_ISR_EOC)))
+ ;
+
+ value = stm_adc.dr;
+ printf ("value %u, cr is %04x isr is %04x\n",
+ value, stm_adc.cr, stm_adc.isr);
+
+
+ /* Clear ISR bits */
+ stm_adc.isr = ((1 << STM_ADC_ISR_AWD) |
+ (1 << STM_ADC_ISR_OVR) |
+ (1 << STM_ADC_ISR_EOSEQ) |
+ (1 << STM_ADC_ISR_EOC));
+}
+#endif
+
+__code struct ao_cmds ao_adc_cmds[] = {
+ { ao_adc_dump, "a\0Display current ADC values" },
+#if AO_ADC_DEBUG
+ { ao_adc_one, "A ch\0Display one ADC channel" },
+#endif
+ { 0, NULL },
+};
+
+void
+ao_adc_init(void)
+{
+ uint32_t chselr;
+
+ /* 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);
+ stm_pupdr_set(AO_ADC_PIN0_PORT, AO_ADC_PIN0_PIN, STM_PUPDR_NONE);
+#endif
+#ifdef AO_ADC_PIN1_PORT
+ stm_moder_set(AO_ADC_PIN1_PORT, AO_ADC_PIN1_PIN, STM_MODER_ANALOG);
+ stm_pupdr_set(AO_ADC_PIN1_PORT, AO_ADC_PIN1_PIN, STM_PUPDR_NONE);
+#endif
+#ifdef AO_ADC_PIN2_PORT
+ stm_moder_set(AO_ADC_PIN2_PORT, AO_ADC_PIN2_PIN, STM_MODER_ANALOG);
+ stm_pupdr_set(AO_ADC_PIN2_PORT, AO_ADC_PIN2_PIN, STM_PUPDR_NONE);
+#endif
+#ifdef AO_ADC_PIN3_PORT
+ stm_moder_set(AO_ADC_PIN3_PORT, AO_ADC_PIN3_PIN, STM_MODER_ANALOG);
+ stm_pupdr_set(AO_ADC_PIN3_PORT, AO_ADC_PIN3_PIN, STM_PUPDR_NONE);
+#endif
+#ifdef AO_ADC_PIN4_PORT
+ stm_moder_set(AO_ADC_PIN4_PORT, AO_ADC_PIN4_PIN, STM_MODER_ANALOG);
+ stm_pupdr_set(AO_ADC_PIN4_PORT, AO_ADC_PIN4_PIN, STM_PUPDR_NONE);
+#endif
+#ifdef AO_ADC_PIN5_PORT
+ stm_moder_set(AO_ADC_PIN5_PORT, AO_ADC_PIN5_PIN, STM_MODER_ANALOG);
+ stm_pupdr_set(AO_ADC_PIN5_PORT, AO_ADC_PIN5_PIN, STM_PUPDR_NONE);
+#endif
+#ifdef AO_ADC_PIN6_PORT
+ stm_moder_set(AO_ADC_PIN6_PORT, AO_ADC_PIN6_PIN, STM_MODER_ANALOG);
+ stm_pupdr_set(AO_ADC_PIN6_PORT, AO_ADC_PIN6_PIN, STM_PUPDR_NONE);
+#endif
+#ifdef AO_ADC_PIN7_PORT
+ stm_moder_set(AO_ADC_PIN7_PORT, AO_ADC_PIN7_PIN, STM_MODER_ANALOG);
+ stm_pupdr_set(AO_ADC_PIN7_PORT, AO_ADC_PIN7_PIN, STM_PUPDR_NONE);
+#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
+
+ /* Wait for ADC to be idle */
+ while (stm_adc.cr & ((1 << STM_ADC_CR_ADCAL) |
+ (1 << STM_ADC_CR_ADDIS)))
+ ;
+
+ /* Disable */
+ if (stm_adc.cr & (1 << STM_ADC_CR_ADEN)) {
+ stm_adc.cr |= (1 << STM_ADC_CR_ADDIS);
+ while (stm_adc.cr & (1 << STM_ADC_CR_ADDIS))
+ ;
+ }
+
+ /* Turn off everything */
+ stm_adc.cr &= ~((1 << STM_ADC_CR_ADCAL) |
+ (1 << STM_ADC_CR_ADSTP) |
+ (1 << STM_ADC_CR_ADSTART) |
+ (1 << STM_ADC_CR_ADEN));
+
+ /* Configure */
+ stm_adc.cfgr1 = ((0 << STM_ADC_CFGR1_AWDCH) | /* analog watchdog channel 0 */
+ (0 << STM_ADC_CFGR1_AWDEN) | /* Disable analog watchdog */
+ (0 << STM_ADC_CFGR1_AWDSGL) | /* analog watchdog on all channels */
+ (0 << STM_ADC_CFGR1_DISCEN) | /* Not discontinuous mode. All channels converted with one trigger */
+ (0 << STM_ADC_CFGR1_AUTOOFF) | /* Leave ADC running */
+ (1 << STM_ADC_CFGR1_WAIT) | /* Wait for data to be read before next conversion */
+ (0 << STM_ADC_CFGR1_CONT) | /* only one set of conversions per trigger */
+ (1 << STM_ADC_CFGR1_OVRMOD) | /* overwrite on overrun */
+ (STM_ADC_CFGR1_EXTEN_DISABLE << STM_ADC_CFGR1_EXTEN) | /* SW trigger */
+ (0 << STM_ADC_CFGR1_ALIGN) | /* Align to LSB */
+ (STM_ADC_CFGR1_RES_12 << STM_ADC_CFGR1_RES) | /* 12 bit resolution */
+ (STM_ADC_CFGR1_SCANDIR_UP << STM_ADC_CFGR1_SCANDIR) | /* scan 0 .. n */
+ (STM_ADC_CFGR1_DMACFG_ONESHOT << STM_ADC_CFGR1_DMACFG) | /* one set of conversions then stop */
+ (1 << STM_ADC_CFGR1_DMAEN)); /* enable DMA */
+
+ /* 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_71_5 << STM_ADC_SMPR_SMP;
+
+ stm_adc.chselr = chselr;
+
+ stm_adc.ccr = ((0 << STM_ADC_CCR_VBATEN) |
+ (0 << STM_ADC_CCR_TSEN) |
+ (0 << STM_ADC_CCR_VREFEN));
+
+ /* Calibrate */
+ stm_adc.cr |= (1 << STM_ADC_CR_ADCAL);
+ while ((stm_adc.cr & (1 << STM_ADC_CR_ADCAL)) != 0)
+ ;
+
+ /* Enable */
+ stm_adc.cr |= (1 << STM_ADC_CR_ADEN);
+ while ((stm_adc.isr & (1 << STM_ADC_ISR_ADRDY)) == 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_cmd_register(&ao_adc_cmds[0]);
+
+ ao_adc_ready = 1;
+}
diff --git a/src/stmf0/ao_arch.h b/src/stmf0/ao_arch.h
index a36482b6..c5f451f5 100644
--- a/src/stmf0/ao_arch.h
+++ b/src/stmf0/ao_arch.h
@@ -144,10 +144,15 @@ ao_adc_init();
/* ADC maximum reported value */
#define AO_ADC_MAX 4095
+#ifndef HAS_BOOT_LOADER
+#define HAS_BOOT_LOADER 1
+#endif
+
+#if HAS_BOOT_LOADER
#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
#endif /* _AO_ARCH_H_ */
diff --git a/src/stmf0/ao_arch_funcs.h b/src/stmf0/ao_arch_funcs.h
index 0cb0e43d..c38ce41a 100644
--- a/src/stmf0/ao_arch_funcs.h
+++ b/src/stmf0/ao_arch_funcs.h
@@ -314,7 +314,18 @@ struct ao_stm_usart {
struct ao_fifo rx_fifo;
struct ao_fifo tx_fifo;
struct stm_usart *reg;
- uint8_t tx_started;
+ uint8_t tx_running;
+ uint8_t draining;
+#if HAS_SERIAL_SW_FLOW
+ /* RTS - 0 if we have FIFO space, 1 if not
+ * CTS - 0 if we can send, 0 if not
+ */
+ struct stm_gpio *gpio_rts;
+ struct stm_gpio *gpio_cts;
+ uint8_t pin_rts;
+ uint8_t pin_cts;
+ uint8_t rts;
+#endif
};
#if HAS_SERIAL_1
diff --git a/src/stmf0/ao_beep_stm.c b/src/stmf0/ao_beep_stm.c
new file mode 100644
index 00000000..610f4a31
--- /dev/null
+++ b/src/stmf0/ao_beep_stm.c
@@ -0,0 +1,389 @@
+/*
+ * 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"
+
+#ifndef BEEPER_CHANNEL
+#error BEEPER_CHANNEL undefined
+#endif
+
+#ifndef BEEPER_TIMER
+#define BEEPER_TIMER 1
+#endif
+
+#if BEEPER_TIMER == 1
+#define timer stm_tim1
+#define STM_RCC_TIMER STM_RCC_APB2ENR_TIM1EN
+#define stm_rcc_enr stm_rcc.apb2enr
+#endif
+
+#if BEEPER_TIMER == 2
+#define timer stm_tim2
+#define STM_RCC_TIMER STM_RCC_APB1ENR_TIM2EN
+#define stm_rcc_enr stm_rcc.apb1enr
+#endif
+
+#if BEEPER_TIMER == 3
+#define timer stm_tim3
+#define STM_RCC_TIMER STM_RCC_APB1ENR_TIM3EN
+#define stm_rcc_enr stm_rcc.apb1enr
+#endif
+
+#ifndef timer
+#error BEEPER_TIMER invalid
+#endif
+
+static inline void
+disable(void)
+{
+ timer.cr1 = 0;
+#if BEEPER_TIMER == 1
+ timer.bdtr = 0;
+#endif
+ stm_rcc_enr &= ~(1 << STM_RCC_TIMER);
+
+ /* Disconnect the timer from the pin */
+ stm_afr_set(BEEPER_PORT, BEEPER_PIN, STM_AFR_NONE);
+}
+
+void
+ao_beep(uint8_t beep)
+{
+ if (beep == 0) {
+ disable();
+ } else {
+ stm_rcc_enr |= (1 << STM_RCC_TIMER);
+
+#if BEEPER_TIMER == 1
+ /* Master output enable */
+ stm_tim1.bdtr = (1 << STM_TIM1_BDTR_MOE);
+
+ stm_tim1.cr2 = ((0 << STM_TIM1_CR2_TI1S) |
+ (STM_TIM1_CR2_MMS_RESET << STM_TIM1_CR2_MMS) |
+ (0 << STM_TIM1_CR2_CCDS));
+
+ /* Set prescaler to match cc1111 clocks
+ */
+ stm_tim1.psc = AO_TIM_CLK / 750000;
+
+ /* 1. Select the counter clock (internal, external, prescaler).
+ *
+ * Setting SMCR to zero means use the internal clock
+ */
+
+ stm_tim1.smcr = 0;
+
+ /* 2. Write the desired data in the TIMx_ARR and TIMx_CCRx registers. */
+ stm_tim1.arr = beep;
+ stm_tim1.ccr1 = beep;
+
+ /* 3. Set the CCxIE and/or CCxDE bits if an interrupt and/or a
+ * DMA request is to be generated.
+ */
+ /* don't want this */
+
+ /* 4. Select the output mode. For example, you must write
+ * OCxM=011, OCxPE=0, CCxP=0 and CCxE=1 to toggle OCx output
+ * pin when CNT matches CCRx, CCRx preload is not used, OCx
+ * is enabled and active high.
+ */
+
+#if BEEPER_CHANNEL == 1
+ stm_tim1.ccmr1 = ((0 << STM_TIM1_CCMR1_OC2CE) |
+ (STM_TIM1_CCMR_OCM_FROZEN << STM_TIM1_CCMR1_OC2M) |
+ (0 << STM_TIM1_CCMR1_OC2PE) |
+ (0 << STM_TIM1_CCMR1_OC2FE) |
+ (STM_TIM1_CCMR_CCS_OUTPUT << STM_TIM1_CCMR1_CC2S) |
+
+ (0 << STM_TIM1_CCMR1_OC1CE) |
+ (STM_TIM1_CCMR_OCM_TOGGLE << STM_TIM1_CCMR1_OC1M) |
+ (0 << STM_TIM1_CCMR1_OC1PE) |
+ (0 << STM_TIM1_CCMR1_OC1FE) |
+ (STM_TIM1_CCMR_CCS_OUTPUT << STM_TIM1_CCMR1_CC1S));
+
+ stm_tim1.ccer = ((0 << STM_TIM1_CCER_CC4P) |
+ (0 << STM_TIM1_CCER_CC4E) |
+ (0 << STM_TIM1_CCER_CC3NP) |
+ (0 << STM_TIM1_CCER_CC3NE) |
+ (0 << STM_TIM1_CCER_CC3P) |
+ (0 << STM_TIM1_CCER_CC3E) |
+ (0 << STM_TIM1_CCER_CC2NP) |
+ (0 << STM_TIM1_CCER_CC2NE) |
+ (0 << STM_TIM1_CCER_CC2P) |
+ (0 << STM_TIM1_CCER_CC2E) |
+ (0 << STM_TIM1_CCER_CC1NE) |
+ (0 << STM_TIM1_CCER_CC1P) |
+ (1 << STM_TIM1_CCER_CC1E));
+#endif
+#if BEEPER_CHANNEL == 2
+ stm_tim1.ccmr1 = ((0 << STM_TIM1_CCMR1_OC2CE) |
+ (STM_TIM1_CCMR_OCM_TOGGLE << STM_TIM1_CCMR1_OC2M) |
+ (0 << STM_TIM1_CCMR1_OC2PE) |
+ (0 << STM_TIM1_CCMR1_OC2FE) |
+ (STM_TIM1_CCMR_CCS_OUTPUT << STM_TIM1_CCMR1_CC2S) |
+
+ (0 << STM_TIM1_CCMR1_OC1CE) |
+ (STM_TIM1_CCMR_OCM_FROZEN << STM_TIM1_CCMR1_OC1M) |
+ (0 << STM_TIM1_CCMR1_OC1PE) |
+ (0 << STM_TIM1_CCMR1_OC1FE) |
+ (STM_TIM1_CCMR_CCS_OUTPUT << STM_TIM1_CCMR1_CC1S));
+
+ stm_tim1.ccer = ((0 << STM_TIM1_CCER_CC4P) |
+ (0 << STM_TIM1_CCER_CC4E) |
+ (0 << STM_TIM1_CCER_CC3NP) |
+ (0 << STM_TIM1_CCER_CC3NE) |
+ (0 << STM_TIM1_CCER_CC3P) |
+ (0 << STM_TIM1_CCER_CC3E) |
+ (0 << STM_TIM1_CCER_CC2NP) |
+ (0 << STM_TIM1_CCER_CC2NE) |
+ (0 << STM_TIM1_CCER_CC2P) |
+ (1 << STM_TIM1_CCER_CC2E) |
+ (0 << STM_TIM1_CCER_CC1NE) |
+ (0 << STM_TIM1_CCER_CC1P) |
+ (0 << STM_TIM1_CCER_CC1E));
+#endif
+#if BEEPER_CHANNEL == 3
+ stm_tim1.ccmr2 = ((0 << STM_TIM1_CCMR2_OC4CE) |
+ (STM_TIM1_CCMR_OCM_FROZEN << STM_TIM1_CCMR2_OC4M) |
+ (0 << STM_TIM1_CCMR2_OC4PE) |
+ (0 << STM_TIM1_CCMR2_OC4FE) |
+ (STM_TIM1_CCMR_CCS_OUTPUT << STM_TIM1_CCMR2_CC4S) |
+
+ (0 << STM_TIM1_CCMR2_OC3CE) |
+ (STM_TIM1_CCMR_OCM_TOGGLE << STM_TIM1_CCMR2_OC3M) |
+ (0 << STM_TIM1_CCMR2_OC3PE) |
+ (0 << STM_TIM1_CCMR2_OC3FE) |
+ (STM_TIM1_CCMR_CCS_OUTPUT << STM_TIM1_CCMR2_CC3S));
+
+ stm_tim1.ccer = ((0 << STM_TIM1_CCER_CC4P) |
+ (0 << STM_TIM1_CCER_CC4E) |
+ (0 << STM_TIM1_CCER_CC3NP) |
+ (0 << STM_TIM1_CCER_CC3NE) |
+ (0 << STM_TIM1_CCER_CC3P) |
+ (1 << STM_TIM1_CCER_CC3E) |
+ (0 << STM_TIM1_CCER_CC2NP) |
+ (0 << STM_TIM1_CCER_CC2NE) |
+ (0 << STM_TIM1_CCER_CC2P) |
+ (0 << STM_TIM1_CCER_CC2E) |
+ (0 << STM_TIM1_CCER_CC1NE) |
+ (0 << STM_TIM1_CCER_CC1P) |
+ (0 << STM_TIM1_CCER_CC1E));
+#endif
+#if BEEPER_CHANNEL == 4
+ stm_tim1.ccmr2 = ((0 << STM_TIM1_CCMR2_OC4CE) |
+ (STM_TIM1_CCMR2_OC4M_TOGGLE << STM_TIM1_CCMR2_OC4M) |
+ (0 << STM_TIM1_CCMR2_OC4PE) |
+ (0 << STM_TIM1_CCMR2_OC4FE) |
+ (STM_TIM1_CCMR2_CC4S_OUTPUT << STM_TIM1_CCMR2_CC4S) |
+
+ (0 << STM_TIM1_CCMR2_OC3CE) |
+ (STM_TIM1_CCMR2_OC3M_FROZEN << STM_TIM1_CCMR2_OC3M) |
+ (0 << STM_TIM1_CCMR2_OC3PE) |
+ (0 << STM_TIM1_CCMR2_OC3FE) |
+ (STM_TIM1_CCMR2_CC3S_OUTPUT << STM_TIM1_CCMR2_CC3S));
+
+ stm_tim1.ccer = ((0 << STM_TIM1_CCER_CC4NP) |
+ (0 << STM_TIM1_CCER_CC4P) |
+ (1 << STM_TIM1_CCER_CC4E) |
+ (0 << STM_TIM1_CCER_CC3NP) |
+ (0 << STM_TIM1_CCER_CC3P) |
+ (0 << STM_TIM1_CCER_CC3E) |
+ (0 << STM_TIM1_CCER_CC2NP) |
+ (0 << STM_TIM1_CCER_CC2P) |
+ (0 << STM_TIM1_CCER_CC2E) |
+ (0 << STM_TIM1_CCER_CC1NP) |
+ (0 << STM_TIM1_CCER_CC1P) |
+ (0 << STM_TIM1_CCER_CC1E));
+#endif
+ /* 5. Enable the counter by setting the CEN bit in the TIMx_CR1 register. */
+
+ stm_tim1.cr1 = ((STM_TIM1_CR1_CKD_1 << STM_TIM1_CR1_CKD) |
+ (0 << STM_TIM1_CR1_ARPE) |
+ (STM_TIM1_CR1_CMS_EDGE << STM_TIM1_CR1_CMS) |
+ (0 << STM_TIM1_CR1_DIR) |
+ (0 << STM_TIM1_CR1_OPM) |
+ (0 << STM_TIM1_CR1_URS) |
+ (0 << STM_TIM1_CR1_UDIS) |
+ (1 << STM_TIM1_CR1_CEN));
+
+ /* Update the values */
+ stm_tim1.egr = (1 << STM_TIM1_EGR_UG);
+#endif
+#if BEEPER_TIMER == 2 || BEEPER_TIMER == 3
+
+ timer.cr2 = ((0 << STM_TIM23_CR2_TI1S) |
+ (STM_TIM23_CR2_MMS_RESET << STM_TIM23_CR2_MMS) |
+ (0 << STM_TIM23_CR2_CCDS));
+
+ /* Set prescaler to match cc1111 clocks
+ */
+ timer.psc = AO_TIM_CLK / 750000;
+
+ /* 1. Select the counter clock (internal, external, prescaler).
+ *
+ * Setting SMCR to zero means use the internal clock
+ */
+
+ timer.smcr = 0;
+
+ /* 2. Write the desired data in the TIMx_ARR and TIMx_CCRx registers. */
+ timer.arr = beep;
+ timer.ccr1 = beep;
+
+ /* 3. Set the CCxIE and/or CCxDE bits if an interrupt and/or a
+ * DMA request is to be generated.
+ */
+ /* don't want this */
+
+ /* 4. Select the output mode. For example, you must write
+ * OCxM=011, OCxPE=0, CCxP=0 and CCxE=1 to toggle OCx output
+ * pin when CNT matches CCRx, CCRx preload is not used, OCx
+ * is enabled and active high.
+ */
+
+#if BEEPER_CHANNEL == 1
+ timer.ccmr1 = ((0 << STM_TIM23_CCMR1_OC2CE) |
+ (STM_TIM23_CCMR1_OC2M_FROZEN << STM_TIM23_CCMR1_OC2M) |
+ (0 << STM_TIM23_CCMR1_OC2PE) |
+ (0 << STM_TIM23_CCMR1_OC2FE) |
+ (STM_TIM23_CCMR1_CC2S_OUTPUT << STM_TIM23_CCMR1_CC2S) |
+
+ (0 << STM_TIM23_CCMR1_OC1CE) |
+ (STM_TIM23_CCMR1_OC1M_TOGGLE << STM_TIM23_CCMR1_OC1M) |
+ (0 << STM_TIM23_CCMR1_OC1PE) |
+ (0 << STM_TIM23_CCMR1_OC1FE) |
+ (STM_TIM23_CCMR1_CC1S_OUTPUT << STM_TIM23_CCMR1_CC1S));
+
+ timer.ccer = ((0 << STM_TIM23_CCER_CC4P) |
+ (0 << STM_TIM23_CCER_CC4E) |
+ (0 << STM_TIM23_CCER_CC3NP) |
+ (0 << STM_TIM23_CCER_CC3P) |
+ (0 << STM_TIM23_CCER_CC3E) |
+ (0 << STM_TIM23_CCER_CC2NP) |
+ (0 << STM_TIM23_CCER_CC2P) |
+ (0 << STM_TIM23_CCER_CC2E) |
+ (0 << STM_TIM23_CCER_CC1P) |
+ (1 << STM_TIM23_CCER_CC1E));
+#endif
+#if BEEPER_CHANNEL == 2
+ timer.ccmr1 = ((0 << STM_TIM23_CCMR1_OC2CE) |
+ (STM_TIM23_CCMR1_OC2M_TOGGLE << STM_TIM23_CCMR1_OC2M) |
+ (0 << STM_TIM23_CCMR1_OC2PE) |
+ (0 << STM_TIM23_CCMR1_OC2FE) |
+ (STM_TIM23_CCMR1_CC2S_OUTPUT << STM_TIM23_CCMR1_CC2S) |
+
+ (0 << STM_TIM23_CCMR1_OC1CE) |
+ (STM_TIM23_CCMR1_OC1M_FROZEN << STM_TIM23_CCMR1_OC1M) |
+ (0 << STM_TIM23_CCMR1_OC1PE) |
+ (0 << STM_TIM23_CCMR1_OC1FE) |
+ (STM_TIM23_CCMR1_CC1S_OUTPUT << STM_TIM23_CCMR1_CC1S));
+
+ timer.ccer = ((0 << STM_TIM23_CCER_CC4P) |
+ (0 << STM_TIM23_CCER_CC4E) |
+ (0 << STM_TIM23_CCER_CC3NP) |
+ (0 << STM_TIM23_CCER_CC3P) |
+ (0 << STM_TIM23_CCER_CC3E) |
+ (0 << STM_TIM23_CCER_CC2NP) |
+ (0 << STM_TIM23_CCER_CC2P) |
+ (1 << STM_TIM23_CCER_CC2E) |
+ (0 << STM_TIM23_CCER_CC1P) |
+ (0 << STM_TIM23_CCER_CC1E));
+#endif
+#if BEEPER_CHANNEL == 3
+ timer.ccmr2 = ((0 << STM_TIM23_CCMR2_OC4CE) |
+ (STM_TIM23_CCMR2_OC4M_FROZEN << STM_TIM23_CCMR2_OC4M) |
+ (0 << STM_TIM23_CCMR2_OC4PE) |
+ (0 << STM_TIM23_CCMR2_OC4FE) |
+ (STM_TIM23_CCMR2_CC4S_OUTPUT << STM_TIM23_CCMR2_CC4S) |
+
+ (0 << STM_TIM23_CCMR2_OC3CE) |
+ (STM_TIM23_CCMR2_OC3M_TOGGLE << STM_TIM23_CCMR2_OC3M) |
+ (0 << STM_TIM23_CCMR2_OC3PE) |
+ (0 << STM_TIM23_CCMR2_OC3FE) |
+ (STM_TIM23_CCMR2_CC3S_OUTPUT << STM_TIM23_CCMR2_CC3S));
+
+ timer.ccer = ((0 << STM_TIM23_CCER_CC4P) |
+ (0 << STM_TIM23_CCER_CC4E) |
+ (0 << STM_TIM23_CCER_CC3NP) |
+ (0 << STM_TIM23_CCER_CC3P) |
+ (1 << STM_TIM23_CCER_CC3E) |
+ (0 << STM_TIM23_CCER_CC2NP) |
+ (0 << STM_TIM23_CCER_CC2P) |
+ (0 << STM_TIM23_CCER_CC2E) |
+ (0 << STM_TIM23_CCER_CC1P) |
+ (0 << STM_TIM23_CCER_CC1E));
+#endif
+#if BEEPER_CHANNEL == 4
+ timer.ccmr2 = ((0 << STM_TIM23_CCMR2_OC4CE) |
+ (STM_TIM23_CCMR2_OC4M_TOGGLE << STM_TIM23_CCMR2_OC4M) |
+ (0 << STM_TIM23_CCMR2_OC4PE) |
+ (0 << STM_TIM23_CCMR2_OC4FE) |
+ (STM_TIM23_CCMR2_CC4S_OUTPUT << STM_TIM23_CCMR2_CC4S) |
+
+ (0 << STM_TIM23_CCMR2_OC3CE) |
+ (STM_TIM23_CCMR2_OC3M_FROZEN << STM_TIM23_CCMR2_OC3M) |
+ (0 << STM_TIM23_CCMR2_OC3PE) |
+ (0 << STM_TIM23_CCMR2_OC3FE) |
+ (STM_TIM23_CCMR2_CC3S_OUTPUT << STM_TIM23_CCMR2_CC3S));
+
+ timer.ccer = ((0 << STM_TIM23_CCER_CC4P) |
+ (1 << STM_TIM23_CCER_CC4E) |
+ (0 << STM_TIM23_CCER_CC3NP) |
+ (0 << STM_TIM23_CCER_CC3P) |
+ (0 << STM_TIM23_CCER_CC3E) |
+ (0 << STM_TIM23_CCER_CC2NP) |
+ (0 << STM_TIM23_CCER_CC2P) |
+ (0 << STM_TIM23_CCER_CC2E) |
+ (0 << STM_TIM23_CCER_CC1P) |
+ (0 << STM_TIM23_CCER_CC1E));
+#endif
+ /* 5. Enable the counter by setting the CEN bit in the TIMx_CR1 register. */
+
+ timer.cr1 = ((STM_TIM23_CR1_CKD_1 << STM_TIM23_CR1_CKD) |
+ (0 << STM_TIM23_CR1_ARPE) |
+ (STM_TIM23_CR1_CMS_EDGE << STM_TIM23_CR1_CMS) |
+ (0 << STM_TIM23_CR1_DIR) |
+ (0 << STM_TIM23_CR1_OPM) |
+ (0 << STM_TIM23_CR1_URS) |
+ (0 << STM_TIM23_CR1_UDIS) |
+ (1 << STM_TIM23_CR1_CEN));
+
+ /* Update the values */
+ timer.egr = (1 << STM_TIM23_EGR_UG);
+
+ /* Hook the timer up to the beeper pin */
+ stm_afr_set(BEEPER_PORT, BEEPER_PIN, STM_AFR_AF2);
+#endif
+ }
+}
+
+void
+ao_beep_for(uint8_t beep, uint16_t ticks) __reentrant
+{
+ ao_beep(beep);
+ ao_delay(ticks);
+ ao_beep(0);
+}
+
+void
+ao_beep_init(void)
+{
+ ao_enable_output(BEEPER_PORT, BEEPER_PIN, BEEPER, 0);
+
+ /* Leave the timer off until requested */
+ stm_rcc_enr &= ~(1 << STM_RCC_TIMER);
+}
diff --git a/src/stmf0/ao_crc.h b/src/stmf0/ao_crc.h
index 7acc6f9c..b6d91023 100644
--- a/src/stmf0/ao_crc.h
+++ b/src/stmf0/ao_crc.h
@@ -35,7 +35,8 @@
static inline uint16_t
ao_crc_in_32_out_16(uint32_t v) {
stm_crc.dr.u32 = v;
- return stm_crc.dr.u16;
+ v = stm_crc.dr.u32;
+ return v ^ (v >> 16);
}
static inline uint16_t
diff --git a/src/stmf0/ao_flash_stm.c b/src/stmf0/ao_flash_stm.c
index 2aeff388..2d57eea7 100644
--- a/src/stmf0/ao_flash_stm.c
+++ b/src/stmf0/ao_flash_stm.c
@@ -19,6 +19,12 @@
#include <ao.h>
#include <ao_flash.h>
+/* Note that the HSI clock must be running for this code to work.
+ * Also, special care must be taken with the linker to ensure that the
+ * functions marked 'ramtext' land in ram and not rom. An example of that
+ * can be found in altos-loader.ld
+ */
+
static uint8_t
ao_flash_is_locked(void)
{
@@ -44,12 +50,7 @@ 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))
- ;
-}
+#define ao_flash_wait_bsy() do { while (stm_flash.sr & (1 << STM_FLASH_SR_BSY)); } while (0)
static void __attribute__ ((section(".ramtext"),noinline))
_ao_flash_erase_page(uint32_t *page)
diff --git a/src/stmf0/ao_interrupt.c b/src/stmf0/ao_interrupt.c
index 79412483..fcd330f1 100644
--- a/src/stmf0/ao_interrupt.c
+++ b/src/stmf0/ao_interrupt.c
@@ -26,9 +26,11 @@
#define IS_FLASH_LOADER 0
#endif
+#ifndef RELOCATE_INTERRUPT
#if !IS_FLASH_LOADER
#define RELOCATE_INTERRUPT 1
#endif
+#endif
extern void main(void);
extern char __stack__;
diff --git a/src/stmf0/ao_serial_stm.c b/src/stmf0/ao_serial_stm.c
new file mode 100644
index 00000000..30b0dbd2
--- /dev/null
+++ b/src/stmf0/ao_serial_stm.c
@@ -0,0 +1,500 @@
+/*
+ * Copyright © 2016 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, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+#include <ao.h>
+#include <ao_exti.h>
+
+void
+ao_debug_out(char c)
+{
+ if (c == '\n')
+ ao_debug_out('\r');
+ while (!(stm_usart1.isr & (1 << STM_USART_ISR_TXE)));
+ stm_usart1.tdr = c;
+}
+
+static int
+_ao_usart_tx_start(struct ao_stm_usart *usart)
+{
+ if (!ao_fifo_empty(usart->tx_fifo)) {
+#if HAS_SERIAL_SW_FLOW
+ if (usart->gpio_cts && ao_gpio_get(usart->gpio_cts, usart->pin_cts, foo) == 1) {
+ ao_exti_enable(usart->gpio_cts, usart->pin_cts);
+ return 0;
+ }
+#endif
+ if (usart->reg->isr & (1 << STM_USART_ISR_TXE))
+ {
+ usart->tx_running = 1;
+ usart->reg->cr1 |= (1 << STM_USART_CR1_TXEIE) | (1 << STM_USART_CR1_TCIE);
+ ao_fifo_remove(usart->tx_fifo, usart->reg->tdr);
+ ao_wakeup(&usart->tx_fifo);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#if HAS_SERIAL_SW_FLOW
+static void
+_ao_usart_cts(struct ao_stm_usart *usart)
+{
+ if (_ao_usart_tx_start(usart))
+ ao_exti_disable(usart->gpio_cts, usart->pin_cts);
+}
+#endif
+
+static void
+_ao_usart_rx(struct ao_stm_usart *usart, int stdin)
+{
+ if (usart->reg->isr & (1 << STM_USART_ISR_RXNE)) {
+ usart->reg->icr = (1 << STM_USART_ICR_ORECF);
+ if (!ao_fifo_full(usart->rx_fifo)) {
+ ao_fifo_insert(usart->rx_fifo, usart->reg->rdr);
+ ao_wakeup(&usart->rx_fifo);
+ if (stdin)
+ ao_wakeup(&ao_stdin_ready);
+#if HAS_SERIAL_SW_FLOW
+ /* If the fifo is nearly full, turn off RTS and wait
+ * for it to drain a bunch
+ */
+ if (usart->gpio_rts && ao_fifo_mostly(usart->rx_fifo)) {
+ ao_gpio_set(usart->gpio_rts, usart->pin_rts, usart->pin_rts, 1);
+ usart->rts = 0;
+ }
+#endif
+ } else {
+ usart->reg->cr1 &= ~(1 << STM_USART_CR1_RXNEIE);
+ }
+ }
+}
+
+static void
+ao_usart_isr(struct ao_stm_usart *usart, int stdin)
+{
+ _ao_usart_rx(usart, stdin);
+
+ if (!_ao_usart_tx_start(usart))
+ usart->reg->cr1 &= ~(1<< STM_USART_CR1_TXEIE);
+
+ if (usart->reg->isr & (1 << STM_USART_ISR_TC)) {
+ usart->tx_running = 0;
+ usart->reg->cr1 &= ~(1 << STM_USART_CR1_TCIE);
+ if (usart->draining) {
+ usart->draining = 0;
+ ao_wakeup(&usart->tx_fifo);
+ }
+ }
+}
+
+static int
+_ao_usart_pollchar(struct ao_stm_usart *usart)
+{
+ int c;
+
+ if (ao_fifo_empty(usart->rx_fifo))
+ c = AO_READ_AGAIN;
+ else {
+ uint8_t u;
+ ao_fifo_remove(usart->rx_fifo,u);
+ if ((usart->reg->cr1 & (1 << STM_USART_CR1_RXNEIE)) == 0) {
+ if (ao_fifo_barely(usart->rx_fifo))
+ usart->reg->cr1 |= (1 << STM_USART_CR1_RXNEIE);
+ }
+#if HAS_SERIAL_SW_FLOW
+ /* If we've cleared RTS, check if there's space now and turn it back on */
+ if (usart->gpio_rts && usart->rts == 0 && ao_fifo_barely(usart->rx_fifo)) {
+ ao_gpio_set(usart->gpio_rts, usart->pin_rts, foo, 0);
+ usart->rts = 1;
+ }
+#endif
+ c = u;
+ }
+ return c;
+}
+
+static char
+ao_usart_getchar(struct ao_stm_usart *usart)
+{
+ int c;
+ ao_arch_block_interrupts();
+ while ((c = _ao_usart_pollchar(usart)) == AO_READ_AGAIN)
+ ao_sleep(&usart->rx_fifo);
+ ao_arch_release_interrupts();
+ return (char) c;
+}
+
+static inline uint8_t
+_ao_usart_sleep_for(struct ao_stm_usart *usart, uint16_t timeout)
+{
+ return ao_sleep_for(&usart->rx_fifo, timeout);
+}
+
+static void
+ao_usart_putchar(struct ao_stm_usart *usart, char c)
+{
+ ao_arch_block_interrupts();
+ while (ao_fifo_full(usart->tx_fifo))
+ ao_sleep(&usart->tx_fifo);
+ ao_fifo_insert(usart->tx_fifo, c);
+ _ao_usart_tx_start(usart);
+ ao_arch_release_interrupts();
+}
+
+static void
+ao_usart_drain(struct ao_stm_usart *usart)
+{
+ ao_arch_block_interrupts();
+ while (!ao_fifo_empty(usart->tx_fifo) || usart->tx_running) {
+ usart->draining = 1;
+ ao_sleep(&usart->tx_fifo);
+ }
+ ao_arch_release_interrupts();
+}
+
+static const struct {
+ uint32_t brr;
+} ao_usart_speeds[] = {
+ [AO_SERIAL_SPEED_4800] = {
+ AO_PCLK / 4800
+ },
+ [AO_SERIAL_SPEED_9600] = {
+ AO_PCLK / 9600
+ },
+ [AO_SERIAL_SPEED_19200] = {
+ AO_PCLK / 19200
+ },
+ [AO_SERIAL_SPEED_57600] = {
+ AO_PCLK / 57600
+ },
+ [AO_SERIAL_SPEED_115200] = {
+ AO_PCLK / 115200
+ },
+};
+
+static void
+ao_usart_set_speed(struct ao_stm_usart *usart, uint8_t speed)
+{
+ if (speed > AO_SERIAL_SPEED_115200)
+ return;
+ usart->reg->brr = ao_usart_speeds[speed].brr;
+}
+
+static void
+ao_usart_init(struct ao_stm_usart *usart)
+{
+ usart->reg->cr1 = ((0 << STM_USART_CR1_M1) |
+ (0 << STM_USART_CR1_EOBIE) |
+ (0 << STM_USART_CR1_RTOIE) |
+ (0 << STM_USART_CR1_DEAT) |
+ (0 << STM_USART_CR1_DEDT) |
+ (0 << STM_USART_CR1_OVER8) |
+ (0 << STM_USART_CR1_CMIE) |
+ (0 << STM_USART_CR1_MME) |
+ (0 << STM_USART_CR1_M0) |
+ (0 << STM_USART_CR1_WAKE) |
+ (0 << STM_USART_CR1_PCE) |
+ (0 << STM_USART_CR1_PS) |
+ (0 << STM_USART_CR1_PEIE) |
+ (0 << STM_USART_CR1_TXEIE) |
+ (0 << STM_USART_CR1_TCIE) |
+ (1 << STM_USART_CR1_RXNEIE) |
+ (0 << STM_USART_CR1_IDLEIE) |
+ (1 << STM_USART_CR1_TE) |
+ (1 << STM_USART_CR1_RE) |
+ (0 << STM_USART_CR1_UESM) |
+ (0 << STM_USART_CR1_UE));
+
+ usart->reg->cr2 = ((0 << STM_USART_CR2_ADD) |
+ (0 << STM_USART_CR2_RTOEN) |
+ (0 << STM_USART_CR2_ABRMOD) |
+ (0 << STM_USART_CR2_ABREN) |
+ (0 << STM_USART_CR2_MSBFIRST) |
+ (0 << STM_USART_CR2_DATAINV) |
+ (0 << STM_USART_CR2_TXINV) |
+ (0 << STM_USART_CR2_RXINV) |
+ (0 << STM_USART_CR2_SWAP) |
+ (0 << STM_USART_CR2_LINEN) |
+ (0 << STM_USART_CR2_STOP) |
+ (0 << STM_USART_CR2_CLKEN) |
+ (0 << STM_USART_CR2_CPOL) |
+ (0 << STM_USART_CR2_CHPA) |
+ (0 << STM_USART_CR2_LBCL) |
+ (0 << STM_USART_CR2_LBDIE) |
+ (0 << STM_USART_CR2_LBDL) |
+ (0 << STM_USART_CR2_ADDM7));
+
+ usart->reg->cr3 = ((0 << STM_USART_CR3_WUFIE) |
+ (0 << STM_USART_CR3_WUS) |
+ (0 << STM_USART_CR3_SCARCNT) |
+ (0 << STM_USART_CR3_DEP) |
+ (0 << STM_USART_CR3_DEM) |
+ (0 << STM_USART_CR3_DDRE) |
+ (0 << STM_USART_CR3_OVRDIS) |
+ (0 << STM_USART_CR3_ONEBIT) |
+ (0 << STM_USART_CR3_CTIIE) |
+ (0 << STM_USART_CR3_CTSE) |
+ (0 << STM_USART_CR3_RTSE) |
+ (0 << STM_USART_CR3_DMAT) |
+ (0 << STM_USART_CR3_DMAR) |
+ (0 << STM_USART_CR3_SCEN) |
+ (0 << STM_USART_CR3_NACK) |
+ (0 << STM_USART_CR3_HDSEL) |
+ (0 << STM_USART_CR3_IRLP) |
+ (0 << STM_USART_CR3_IREN) |
+ (0 << STM_USART_CR3_EIE));
+
+
+ /* Pick a 9600 baud rate */
+ ao_usart_set_speed(usart, AO_SERIAL_SPEED_9600);
+
+ /* Enable the usart */
+ usart->reg->cr1 |= (1 << STM_USART_CR1_UE);
+
+}
+
+#if HAS_SERIAL_HW_FLOW
+static void
+ao_usart_set_flow(struct ao_stm_usart *usart)
+{
+ usart->reg->cr3 |= ((1 << STM_USART_CR3_CTSE) |
+ (1 << STM_USART_CR3_RTSE));
+}
+#endif
+
+#if HAS_SERIAL_1
+
+struct ao_stm_usart ao_stm_usart1;
+
+void stm_usart1_isr(void) { ao_usart_isr(&ao_stm_usart1, USE_SERIAL_1_STDIN); }
+
+char
+ao_serial1_getchar(void)
+{
+ return ao_usart_getchar(&ao_stm_usart1);
+}
+
+void
+ao_serial1_putchar(char c)
+{
+ ao_usart_putchar(&ao_stm_usart1, c);
+}
+
+int
+_ao_serial1_pollchar(void)
+{
+ return _ao_usart_pollchar(&ao_stm_usart1);
+}
+
+uint8_t
+_ao_serial1_sleep_for(uint16_t timeout)
+{
+ return _ao_usart_sleep_for(&ao_stm_usart1, timeout);
+}
+
+void
+ao_serial1_drain(void)
+{
+ ao_usart_drain(&ao_stm_usart1);
+}
+
+void
+ao_serial1_set_speed(uint8_t speed)
+{
+ ao_usart_drain(&ao_stm_usart1);
+ ao_usart_set_speed(&ao_stm_usart1, speed);
+}
+#endif /* HAS_SERIAL_1 */
+
+#if HAS_SERIAL_2
+
+struct ao_stm_usart ao_stm_usart2;
+
+void stm_usart2_isr(void) { ao_usart_isr(&ao_stm_usart2, USE_SERIAL_2_STDIN); }
+
+char
+ao_serial2_getchar(void)
+{
+ return ao_usart_getchar(&ao_stm_usart2);
+}
+
+void
+ao_serial2_putchar(char c)
+{
+ ao_usart_putchar(&ao_stm_usart2, c);
+}
+
+int
+_ao_serial2_pollchar(void)
+{
+ return _ao_usart_pollchar(&ao_stm_usart2);
+}
+
+uint8_t
+_ao_serial2_sleep_for(uint16_t timeout)
+{
+ return _ao_usart_sleep_for(&ao_stm_usart2, timeout);
+}
+
+void
+ao_serial2_drain(void)
+{
+ ao_usart_drain(&ao_stm_usart2);
+}
+
+void
+ao_serial2_set_speed(uint8_t speed)
+{
+ ao_usart_drain(&ao_stm_usart2);
+ ao_usart_set_speed(&ao_stm_usart2, speed);
+}
+
+#if HAS_SERIAL_SW_FLOW
+void
+ao_serial2_cts(void)
+{
+ _ao_usart_cts(&ao_stm_usart2);
+}
+#endif
+
+#endif /* HAS_SERIAL_2 */
+
+#if HAS_SERIAL_SW_FLOW
+static void
+ao_serial_set_sw_rts_cts(struct ao_stm_usart *usart,
+ void (*isr)(void),
+ struct stm_gpio *port_rts,
+ int pin_rts,
+ struct stm_gpio *port_cts,
+ int pin_cts)
+{
+ /* Pull RTS low to note that there's space in the FIFO
+ */
+ ao_enable_output(port_rts, pin_rts, foo, 0);
+ usart->gpio_rts = port_rts;
+ usart->pin_rts = pin_rts;
+ usart->rts = 1;
+
+ ao_exti_setup(port_cts, pin_cts, AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_MED, isr);
+ usart->gpio_cts = port_cts;
+ usart->pin_cts = pin_cts;
+}
+#endif
+
+void
+ao_serial_init(void)
+{
+#if HAS_SERIAL_1
+ /*
+ * TX RX
+ * PA9 PA10
+ * PB6 PB7
+ */
+
+#if SERIAL_1_PA9_PA10
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_IOPAEN);
+
+ stm_afr_set(&stm_gpioa, 9, STM_AFR_AF1);
+ stm_afr_set(&stm_gpioa, 10, STM_AFR_AF1);
+#else
+#if SERIAL_1_PB6_PB7
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_IOPBEN);
+
+ stm_afr_set(&stm_gpiob, 6, STM_AFR_AF0);
+ stm_afr_set(&stm_gpiob, 7, STM_AFR_AF0);
+#else
+#error "No SERIAL_1 port configuration specified"
+#endif
+#endif
+ /* Enable USART */
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_USART1EN);
+
+ ao_stm_usart1.reg = &stm_usart1;
+ ao_usart_init(&ao_stm_usart1);
+
+ stm_nvic_set_enable(STM_ISR_USART1_POS);
+ stm_nvic_set_priority(STM_ISR_USART1_POS, 4);
+#if USE_SERIAL_1_STDIN && !DELAY_SERIAL_1_STDIN
+ ao_add_stdio(_ao_serial1_pollchar,
+ ao_serial1_putchar,
+ NULL);
+#endif
+#endif
+
+#if HAS_SERIAL_2
+ /*
+ * TX RX
+ * PA2 PA3
+ * PA14 PA15
+ */
+
+# if SERIAL_2_PA2_PA3
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_IOPAEN);
+
+ stm_afr_set(&stm_gpioa, 2, STM_AFR_AF1);
+ stm_afr_set(&stm_gpioa, 3, STM_AFR_AF1);
+# if USE_SERIAL_2_FLOW
+# if USE_SERIAL_2_SW_FLOW
+ ao_serial_set_sw_rts_cts(&ao_stm_usart2,
+ ao_serial2_cts,
+ SERIAL_2_PORT_RTS,
+ SERIAL_2_PIN_RTS,
+ SERIAL_2_PORT_CTS,
+ SERIAL_2_PIN_CTS);
+# else
+ stm_afr_set(&stm_gpioa, 0, STM_AFR_AF1);
+ stm_afr_set(&stm_gpioa, 1, STM_AFR_AF1);
+# endif
+# endif
+# else
+# if SERIAL_2_PA14_PA15
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_IOPAEN);
+
+ stm_afr_set(&stm_gpioa, 14, STM_AFR_AF1);
+ stm_afr_set(&stm_gpioa, 15, STM_AFR_AF1);
+# if USE_SERIAL_2_FLOW
+# error "Don't know how to set flowcontrol for serial 2 on PA14"
+# endif
+# else
+# if SERIAL_2_PA2_PA15
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_IOPAEN);
+
+ stm_afr_set(&stm_gpioa, 2, STM_AFR_AF1);
+ stm_afr_set(&stm_gpioa, 15, STM_AFR_AF1);
+# if USE_SERIAL_2_FLOW
+# error "Don't know how to set flowcontrol for serial 2 on PA2_PA15"
+# 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 && !USE_SERIAL_2_SW_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 && !DELAY_SERIAL_2_STDIN
+ ao_add_stdio(_ao_serial2_pollchar,
+ ao_serial2_putchar,
+ NULL);
+# endif
+#endif
+}
diff --git a/src/stmf0/ao_spi_stm.c b/src/stmf0/ao_spi_stm.c
index 0448ad8c..5e76d6c3 100644
--- a/src/stmf0/ao_spi_stm.c
+++ b/src/stmf0/ao_spi_stm.c
@@ -536,12 +536,18 @@ void
ao_spi_init(void)
{
#if HAS_SPI_1
+#ifndef SPI_1_PA5_PA6_PA7
+#error SPI_1_PA5_PA6_PA7 undefined
+#endif
# if SPI_1_PA5_PA6_PA7
stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_IOPAEN);
stm_ospeedr_set(&stm_gpioa, 5, SPI_1_OSPEEDR);
stm_ospeedr_set(&stm_gpioa, 6, SPI_1_OSPEEDR);
stm_ospeedr_set(&stm_gpioa, 7, SPI_1_OSPEEDR);
# endif
+# ifndef SPI_1_PB3_PB4_PB5
+# error SPI_1_PB3_PB4_PB5 undefined
+# endif
# if SPI_1_PB3_PB4_PB5
stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_IOPBEN);
stm_ospeedr_set(&stm_gpiob, 3, SPI_1_OSPEEDR);
diff --git a/src/stmf0/ao_spi_stm_slave.c b/src/stmf0/ao_spi_stm_slave.c
new file mode 100644
index 00000000..962ff2c6
--- /dev/null
+++ b/src/stmf0/ao_spi_stm_slave.c
@@ -0,0 +1,339 @@
+/*
+ * 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_spi_stm_slave_info {
+ uint8_t miso_dma_index;
+ uint8_t mosi_dma_index;
+ struct stm_spi *stm_spi;
+};
+
+static uint8_t ao_spi_slave_mutex[STM_NUM_SPI];
+static uint8_t ao_spi_slave_index[STM_NUM_SPI];
+
+static const struct ao_spi_stm_slave_info ao_spi_stm_slave_info[STM_NUM_SPI] = {
+ {
+ .miso_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_RX),
+ .mosi_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_TX),
+ &stm_spi1
+ },
+ {
+ .miso_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI2_RX),
+ .mosi_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI2_TX),
+ &stm_spi2
+ }
+};
+
+static uint8_t spi_dev_null;
+
+void
+ao_spi_slave_send(void *block, uint16_t len)
+{
+ struct stm_spi *stm_spi = ao_spi_stm_slave_info[AO_SPI_INDEX(SPI_SLAVE_INDEX)].stm_spi;
+ uint8_t mosi_dma_index = ao_spi_stm_slave_info[AO_SPI_INDEX(SPI_SLAVE_INDEX)].mosi_dma_index;
+ uint8_t miso_dma_index = ao_spi_stm_slave_info[AO_SPI_INDEX(SPI_SLAVE_INDEX)].miso_dma_index;
+
+ /* Set up the transmit DMA to deliver data */
+ ao_dma_set_transfer(mosi_dma_index,
+ &stm_spi->dr,
+ block,
+ len,
+ (0 << STM_DMA_CCR_MEM2MEM) |
+ (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+ (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+ (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+ (1 << STM_DMA_CCR_MINC) |
+ (0 << STM_DMA_CCR_PINC) |
+ (0 << STM_DMA_CCR_CIRC) |
+ (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
+
+ /* Clear RXNE */
+ (void) stm_spi->dr;
+
+ /* Set up the receive DMA -- when this is done, we know the SPI unit
+ * is idle. Without this, we'd have to poll waiting for the BSY bit to
+ * be cleared
+ */
+ ao_dma_set_transfer(miso_dma_index,
+ &stm_spi->dr,
+ &spi_dev_null,
+ len,
+ (0 << STM_DMA_CCR_MEM2MEM) |
+ (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+ (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+ (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+ (0 << 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));
+ stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+ (0 << STM_SPI_CR2_RXNEIE) |
+ (0 << STM_SPI_CR2_ERRIE) |
+ (0 << STM_SPI_CR2_SSOE) |
+ (1 << STM_SPI_CR2_TXDMAEN) |
+ (1 << STM_SPI_CR2_RXDMAEN));
+ ao_dma_start(miso_dma_index);
+ ao_dma_start(mosi_dma_index);
+ ao_arch_critical(
+ while (!ao_dma_done[miso_dma_index])
+ ao_sleep(&ao_dma_done[miso_dma_index]);
+ );
+ ao_dma_done_transfer(mosi_dma_index);
+ ao_dma_done_transfer(miso_dma_index);
+}
+
+uint8_t
+ao_spi_slave_recv(void *block, uint16_t len)
+{
+ struct stm_spi *stm_spi = ao_spi_stm_slave_info[AO_SPI_INDEX(SPI_SLAVE_INDEX)].stm_spi;
+ uint8_t mosi_dma_index = ao_spi_stm_slave_info[AO_SPI_INDEX(SPI_SLAVE_INDEX)].mosi_dma_index;
+ uint8_t miso_dma_index = ao_spi_stm_slave_info[AO_SPI_INDEX(SPI_SLAVE_INDEX)].miso_dma_index;
+
+ /* Set up transmit DMA to make the SPI hardware actually run */
+ ao_dma_set_transfer(mosi_dma_index,
+ &stm_spi->dr,
+ &spi_dev_null,
+ len,
+ (0 << STM_DMA_CCR_MEM2MEM) |
+ (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+ (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+ (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+ (0 << STM_DMA_CCR_MINC) |
+ (0 << STM_DMA_CCR_PINC) |
+ (0 << STM_DMA_CCR_CIRC) |
+ (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
+
+ /* Clear RXNE */
+ (void) stm_spi->dr;
+
+ /* Set up the receive DMA to capture data */
+ ao_dma_set_transfer(miso_dma_index,
+ &stm_spi->dr,
+ block,
+ len,
+ (0 << STM_DMA_CCR_MEM2MEM) |
+ (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+ (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+ (STM_DMA_CCR_PSIZE_8 << 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));
+
+ stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+ (0 << STM_SPI_CR2_RXNEIE) |
+ (0 << STM_SPI_CR2_ERRIE) |
+ (0 << STM_SPI_CR2_SSOE) |
+ (1 << STM_SPI_CR2_TXDMAEN) |
+ (1 << STM_SPI_CR2_RXDMAEN));
+ ao_dma_start(miso_dma_index);
+ ao_dma_start(mosi_dma_index);
+
+ /* Wait until the SPI unit is done */
+ ao_arch_critical(
+ while (!ao_dma_done[miso_dma_index])
+ ao_sleep(&ao_dma_done[miso_dma_index]);
+ );
+
+ ao_dma_done_transfer(mosi_dma_index);
+ ao_dma_done_transfer(miso_dma_index);
+ return 1;
+}
+
+static void
+ao_spi_slave_disable_index(uint8_t spi_index)
+{
+ /* Disable current config
+ */
+ switch (AO_SPI_INDEX(spi_index)) {
+ case STM_SPI_INDEX(1):
+ switch (spi_index) {
+ case AO_SPI_1_PA5_PA6_PA7:
+ stm_gpio_set(&stm_gpioa, 5, 1);
+ stm_moder_set(&stm_gpioa, 5, STM_MODER_OUTPUT);
+ stm_moder_set(&stm_gpioa, 6, STM_MODER_INPUT);
+ stm_moder_set(&stm_gpioa, 7, STM_MODER_OUTPUT);
+ break;
+ case AO_SPI_1_PB3_PB4_PB5:
+ stm_gpio_set(&stm_gpiob, 3, 1);
+ stm_moder_set(&stm_gpiob, 3, STM_MODER_OUTPUT);
+ stm_moder_set(&stm_gpiob, 4, STM_MODER_INPUT);
+ stm_moder_set(&stm_gpiob, 5, STM_MODER_OUTPUT);
+ break;
+ case AO_SPI_1_PE13_PE14_PE15:
+ stm_gpio_set(&stm_gpioe, 13, 1);
+ stm_moder_set(&stm_gpioe, 13, STM_MODER_OUTPUT);
+ stm_moder_set(&stm_gpioe, 14, STM_MODER_INPUT);
+ stm_moder_set(&stm_gpioe, 15, STM_MODER_OUTPUT);
+ break;
+ }
+ break;
+ case STM_SPI_INDEX(2):
+ switch (spi_index) {
+ case AO_SPI_2_PB13_PB14_PB15:
+ stm_gpio_set(&stm_gpiob, 13, 1);
+ stm_moder_set(&stm_gpiob, 13, STM_MODER_OUTPUT);
+ stm_moder_set(&stm_gpiob, 14, STM_MODER_INPUT);
+ stm_moder_set(&stm_gpiob, 15, STM_MODER_OUTPUT);
+ break;
+ case AO_SPI_2_PD1_PD3_PD4:
+ stm_gpio_set(&stm_gpiod, 1, 1);
+ stm_moder_set(&stm_gpiod, 1, STM_MODER_OUTPUT);
+ stm_moder_set(&stm_gpiod, 3, STM_MODER_INPUT);
+ stm_moder_set(&stm_gpiod, 4, STM_MODER_OUTPUT);
+ break;
+ }
+ break;
+ }
+}
+
+static void
+ao_spi_slave_enable_index(uint8_t spi_index)
+{
+ switch (AO_SPI_INDEX(spi_index)) {
+ case STM_SPI_INDEX(1):
+ switch (spi_index) {
+ case AO_SPI_1_PA5_PA6_PA7:
+ stm_afr_set(&stm_gpioa, 5, STM_AFR_AF5);
+ stm_afr_set(&stm_gpioa, 6, STM_AFR_AF5);
+ stm_afr_set(&stm_gpioa, 7, STM_AFR_AF5);
+ break;
+ case AO_SPI_1_PB3_PB4_PB5:
+ stm_afr_set(&stm_gpiob, 3, STM_AFR_AF5);
+ stm_afr_set(&stm_gpiob, 4, STM_AFR_AF5);
+ stm_afr_set(&stm_gpiob, 5, STM_AFR_AF5);
+ break;
+ case AO_SPI_1_PE13_PE14_PE15:
+ stm_afr_set(&stm_gpioe, 13, STM_AFR_AF5);
+ stm_afr_set(&stm_gpioe, 14, STM_AFR_AF5);
+ stm_afr_set(&stm_gpioe, 15, STM_AFR_AF5);
+ break;
+ }
+ break;
+ case STM_SPI_INDEX(2):
+ switch (spi_index) {
+ case AO_SPI_2_PB13_PB14_PB15:
+ stm_afr_set(&stm_gpiob, 13, STM_AFR_AF5);
+ stm_afr_set(&stm_gpiob, 14, STM_AFR_AF5);
+ stm_afr_set(&stm_gpiob, 15, STM_AFR_AF5);
+ break;
+ case AO_SPI_2_PD1_PD3_PD4:
+ stm_afr_set(&stm_gpiod, 1, STM_AFR_AF5);
+ stm_afr_set(&stm_gpiod, 3, STM_AFR_AF5);
+ stm_afr_set(&stm_gpiod, 4, STM_AFR_AF5);
+ break;
+ }
+ break;
+ }
+}
+
+void
+ao_spi_slave_get(uint8_t spi_index, uint32_t speed)
+{
+ uint8_t id = AO_SPI_INDEX(spi_index);
+ struct stm_spi *stm_spi = ao_spi_stm_slave_info[id].stm_spi;
+
+ ao_mutex_get(&ao_spi_slave_mutex[id]);
+ stm_spi->cr1 = ((0 << STM_SPI_CR1_BIDIMODE) | /* Three wire mode */
+ (0 << STM_SPI_CR1_BIDIOE) |
+ (0 << STM_SPI_CR1_CRCEN) | /* CRC disabled */
+ (0 << STM_SPI_CR1_CRCNEXT) |
+ (0 << STM_SPI_CR1_DFF) |
+ (0 << STM_SPI_CR1_RXONLY) |
+ (1 << STM_SPI_CR1_SSM) | /* Software SS handling */
+ (1 << STM_SPI_CR1_SSI) | /* ... */
+ (0 << STM_SPI_CR1_LSBFIRST) | /* Big endian */
+ (1 << STM_SPI_CR1_SPE) | /* Enable SPI unit */
+ (speed << STM_SPI_CR1_BR) | /* baud rate to pclk/4 */
+ (1 << STM_SPI_CR1_MSTR) |
+ (0 << STM_SPI_CR1_CPOL) | /* Format 0 */
+ (0 << STM_SPI_CR1_CPHA));
+ if (spi_index != ao_spi_slave_index[id]) {
+
+ /* Disable old config
+ */
+ ao_spi_slave_disable_index(ao_spi_slave_index[id]);
+
+ /* Enable new config
+ */
+ ao_spi_slave_enable_index(spi_index);
+
+ /* Remember current config
+ */
+ ao_spi_slave_index[id] = spi_index;
+ }
+}
+
+void
+ao_spi_slave_put(uint8_t spi_index)
+{
+ uint8_t id = AO_SPI_INDEX(spi_index);
+ struct stm_spi *stm_spi = ao_spi_stm_slave_info[id].stm_spi;
+
+ stm_spi->cr1 = 0;
+ ao_mutex_put(&ao_spi_slave_mutex[id]);
+}
+
+static void
+ao_spi_channel_init(uint8_t spi_index)
+{
+ uint8_t id = AO_SPI_INDEX(spi_index);
+ struct stm_spi *stm_spi = ao_spi_stm_slave_info[id].stm_spi;
+
+ ao_spi_slave_disable_index(spi_index);
+
+ stm_spi->cr1 = 0;
+ (void) stm_spi->sr;
+ stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+ (0 << STM_SPI_CR2_RXNEIE) |
+ (0 << STM_SPI_CR2_ERRIE) |
+ (0 << STM_SPI_CR2_SSOE) |
+ (0 << STM_SPI_CR2_TXDMAEN) |
+ (0 << STM_SPI_CR2_RXDMAEN));
+}
+
+void
+ao_spi_slave_init(void)
+{
+#if HAS_SPI_SLAVE_1
+# if SPI_1_PA5_PA6_PA7
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
+# endif
+# if SPI_1_PB3_PB4_PB5
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
+# endif
+# if SPI_1_PE13_PE14_PE15
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOEEN);
+# endif
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SPI1EN);
+ ao_spi_slave_index[0] = AO_SPI_CONFIG_NONE;
+ ao_spi_channel_init(0);
+#endif
+
+#if HAS_SPI_SLAVE_2
+# if SPI_2_PB13_PB14_PB15
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
+# endif
+# if SPI_2_PD1_PD3_PD4
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIODEN);
+# endif
+ stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_SPI2EN);
+ ao_spi_slave_index[1] = AO_SPI_CONFIG_NONE;
+ ao_spi_channel_init(1);
+#endif
+}
diff --git a/src/stmf0/stm32f0.h b/src/stmf0/stm32f0.h
index bafa763a..e53a5dfd 100644
--- a/src/stmf0/stm32f0.h
+++ b/src/stmf0/stm32f0.h
@@ -1812,15 +1812,15 @@ extern struct stm_tim23 stm_tim2, stm_tim3;
#define STM_TIM23_CCMR2_OC4CE 15
#define STM_TIM23_CCMR2_OC4M 12
-#define STM_TIM23_CCMR2_OCM_FROZEN 0
-#define STM_TIM23_CCMR2_OCM_SET_HIGH_ON_MATCH 1
-#define STM_TIM23_CCMR2_OCM_SET_LOW_ON_MATCH 2
-#define STM_TIM23_CCMR2_OCM_TOGGLE 3
-#define STM_TIM23_CCMR2_OCM_FORCE_LOW 4
-#define STM_TIM23_CCMR2_OCM_FORCE_HIGH 5
-#define STM_TIM23_CCMR2_OCM_PWM_MODE_1 6
-#define STM_TIM23_CCMR2_OCM_PWM_MODE_2 7
-#define STM_TIM23_CCMR2_OCM_MASK 7
+#define STM_TIM23_CCMR2_OC4M_FROZEN 0
+#define STM_TIM23_CCMR2_OC4M_SET_HIGH_ON_MATCH 1
+#define STM_TIM23_CCMR2_OC4M_SET_LOW_ON_MATCH 2
+#define STM_TIM23_CCMR2_OC4M_TOGGLE 3
+#define STM_TIM23_CCMR2_OC4M_FORCE_LOW 4
+#define STM_TIM23_CCMR2_OC4M_FORCE_HIGH 5
+#define STM_TIM23_CCMR2_OC4M_PWM_MODE_1 6
+#define STM_TIM23_CCMR2_OC4M_PWM_MODE_2 7
+#define STM_TIM23_CCMR2_OC4M_MASK 7
#define STM_TIM23_CCMR2_OC4PE 11
#define STM_TIM23_CCMR2_OC4FE 10
#define STM_TIM23_CCMR2_CC4S 8
@@ -1832,15 +1832,15 @@ extern struct stm_tim23 stm_tim2, stm_tim3;
#define STM_TIM23_CCMR2_OC3CE 7
#define STM_TIM23_CCMR2_OC3M 4
-#define STM_TIM23_CCMR2_OCM_FROZEN 0
-#define STM_TIM23_CCMR2_OCM_SET_HIGH_ON_MATCH 1
-#define STM_TIM23_CCMR2_OCM_SET_LOW_ON_MATCH 2
-#define STM_TIM23_CCMR2_OCM_TOGGLE 3
-#define STM_TIM23_CCMR2_OCM_FORCE_LOW 4
-#define STM_TIM23_CCMR2_OCM_FORCE_HIGH 5
+#define STM_TIM23_CCMR2_OC3M_FROZEN 0
+#define STM_TIM23_CCMR2_OC3M_SET_HIGH_ON_MATCH 1
+#define STM_TIM23_CCMR2_OC3M_SET_LOW_ON_MATCH 2
+#define STM_TIM23_CCMR2_OC3M_TOGGLE 3
+#define STM_TIM23_CCMR2_OC3M_FORCE_LOW 4
+#define STM_TIM23_CCMR2_OC3M_FORCE_HIGH 5
#define STM_TIM23_CCMR2_OC3M_PWM_MODE_1 6
-#define STM_TIM23_CCMR2_OCM_PWM_MODE_2 7
-#define STM_TIM23_CCMR2_OCM_MASK 7
+#define STM_TIM23_CCMR2_OC3M_PWM_MODE_2 7
+#define STM_TIM23_CCMR2_OC3M_MASK 7
#define STM_TIM23_CCMR2_OC3PE 11
#define STM_TIM23_CCMR2_OC3FE 2
#define STM_TIM23_CCMR2_CC3S 0
@@ -2010,4 +2010,129 @@ struct stm_exti {
extern struct stm_exti stm_exti;
+struct stm_usart {
+ vuint32_t cr1; /* control register 1 */
+ vuint32_t cr2; /* control register 2 */
+ vuint32_t cr3; /* control register 3 */
+ vuint32_t brr; /* baud rate register */
+
+ vuint32_t gtpr; /* guard time and prescaler */
+ vuint32_t rtor; /* receiver timeout register */
+ vuint32_t rqr; /* request register */
+ vuint32_t isr; /* interrupt and status register */
+
+ vuint32_t icr; /* interrupt flag clear register */
+ vuint32_t rdr; /* receive data register */
+ vuint32_t tdr; /* transmit data register */
+};
+
+#define STM_USART_CR1_M1 28
+#define STM_USART_CR1_EOBIE 27
+#define STM_USART_CR1_RTOIE 26
+#define STM_USART_CR1_DEAT 21
+#define STM_USART_CR1_DEDT 16
+#define STM_USART_CR1_OVER8 15
+#define STM_USART_CR1_CMIE 14
+#define STM_USART_CR1_MME 13
+#define STM_USART_CR1_M0 12
+#define STM_USART_CR1_WAKE 11
+#define STM_USART_CR1_PCE 10
+#define STM_USART_CR1_PS 9
+#define STM_USART_CR1_PEIE 8
+#define STM_USART_CR1_TXEIE 7
+#define STM_USART_CR1_TCIE 6
+#define STM_USART_CR1_RXNEIE 5
+#define STM_USART_CR1_IDLEIE 4
+#define STM_USART_CR1_TE 3
+#define STM_USART_CR1_RE 2
+#define STM_USART_CR1_UESM 1
+#define STM_USART_CR1_UE 0
+
+#define STM_USART_CR2_ADD 24
+#define STM_USART_CR2_RTOEN 23
+#define STM_USART_CR2_ABRMOD 21
+#define STM_USART_CR2_ABREN 20
+#define STM_USART_CR2_MSBFIRST 19
+#define STM_USART_CR2_DATAINV 18
+#define STM_USART_CR2_TXINV 17
+#define STM_USART_CR2_RXINV 16
+#define STM_USART_CR2_SWAP 15
+#define STM_USART_CR2_LINEN 14
+#define STM_USART_CR2_STOP 12
+#define STM_USART_CR2_CLKEN 11
+#define STM_USART_CR2_CPOL 10
+#define STM_USART_CR2_CHPA 9
+#define STM_USART_CR2_LBCL 8
+#define STM_USART_CR2_LBDIE 6
+#define STM_USART_CR2_LBDL 5
+#define STM_USART_CR2_ADDM7 4
+
+#define STM_USART_CR3_WUFIE 22
+#define STM_USART_CR3_WUS 20
+#define STM_USART_CR3_SCARCNT 17
+#define STM_USART_CR3_DEP 15
+#define STM_USART_CR3_DEM 14
+#define STM_USART_CR3_DDRE 13
+#define STM_USART_CR3_OVRDIS 12
+#define STM_USART_CR3_ONEBIT 11
+#define STM_USART_CR3_CTIIE 10
+#define STM_USART_CR3_CTSE 9
+#define STM_USART_CR3_RTSE 8
+#define STM_USART_CR3_DMAT 7
+#define STM_USART_CR3_DMAR 6
+#define STM_USART_CR3_SCEN 5
+#define STM_USART_CR3_NACK 4
+#define STM_USART_CR3_HDSEL 3
+#define STM_USART_CR3_IRLP 2
+#define STM_USART_CR3_IREN 1
+#define STM_USART_CR3_EIE 0
+
+#define STM_USART_GTPR_GT 8
+#define STM_USART_GTPR_PSC 0
+
+#define STM_USART_RQR_TXFRQ 4
+#define STM_USART_RQR_RXFRQ 3
+#define STM_USART_RQR_MMRQ 2
+#define STM_USART_RQR_SBKRQ 1
+#define STM_USART_RQR_ABRRQ 0
+
+#define STM_USART_ISR_REACK 22
+#define STM_USART_ISR_TEACK 21
+#define STM_USART_ISR_WUF 20
+#define STM_USART_ISR_RWU 19
+#define STM_USART_ISR_SBKF 18
+#define STM_USART_ISR_CMF 17
+#define STM_USART_ISR_BUSY 16
+#define STM_USART_ISR_ABRF 15
+#define STM_USART_ISR_ABRE 14
+#define STM_USART_ISR_EOBF 12
+#define STM_USART_ISR_RTOF 11
+#define STM_USART_ISR_CTS 10
+#define STM_USART_ISR_CTSIF 9
+#define STM_USART_ISR_LBDF 8
+#define STM_USART_ISR_TXE 7
+#define STM_USART_ISR_TC 6
+#define STM_USART_ISR_RXNE 5
+#define STM_USART_ISR_IDLE 4
+#define STM_USART_ISR_ORE 3
+#define STM_USART_ISR_NF 2
+#define STM_USART_ISR_FE 1
+#define STM_USART_ISR_PE 0
+
+#define STM_USART_ICR_WUCF 20
+#define STM_USART_ICR_CMCF 17
+#define STM_USART_ICR_EOBCF 12
+#define STM_USART_ICR_RTOCF 11
+#define STM_USART_ICR_CTSCF 9
+#define STM_USART_ICR_LBDCF 8
+#define STM_USART_ICR_TCCF 6
+#define STM_USART_ICR_IDLECF 4
+#define STM_USART_ICR_ORECF 3
+#define STM_USART_ICR_NCF 2
+#define STM_USART_ICR_FECF 1
+#define STM_USART_ICR_PECF 0
+
+extern struct stm_usart stm_usart1;
+extern struct stm_usart stm_usart2;
+
#endif /* _STM32F0_H_ */