diff --git a/Makefile b/Makefile index 45d3945..18a4669 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ TARGET ?= host -TARGET := $(filter host stm32f0,${TARGET}) +TARGET := $(filter host stm32f0 stm32f1,${TARGET}) ifeq (${TARGET},) $(error No support available for the target) diff --git a/scripts/Makefile.stm32f1 b/scripts/Makefile.stm32f1 new file mode 100644 index 0000000..d751106 --- /dev/null +++ b/scripts/Makefile.stm32f1 @@ -0,0 +1,52 @@ +PREFIX ?= arm-none-eabi- + +LDSCRIPT = support/stm32vl-discovery.ld + +CFLAGS += -Os +LDFLAGS += -Os + +CFLAGS += -fno-common --function-sections -fdata-sections +LDFLAGS += -static -nostartfiles +LDFLAGS += -Wl,--gc-sections +LDFLAGS += -T ${LDSCRIPT} +LIBS += -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group + +# Set up libopencm3. +OPENCM3_DIR ?= libopencm3 +OPENCM3_LIBNAME = opencm3_stm32f1 +CFLAGS += -I$(OPENCM3_DIR)/include +CFLAGS += -DSTM32F1 -msoft-float -mthumb -mcpu=cortex-m3 -mfix-cortex-m3-ldrd +LDFLAGS += -L$(OPENCM3_DIR)/lib +LIBS += -l$(OPENCM3_LIBNAME) + +STLINK_PORT ?= :4242 + +obj-y += source/core/spi.o +obj-y += source/drivers/spi_flash.o + +$(OPENCM3_DIR)/lib/lib$(OPENCM3_LIBNAME).a: + @if [ ! -e libopencm3/.git ]; then \ + git submodule init; \ + git submodule update; \ + fi + @$(MAKE) -C $(OPENCM3_DIR) + +$(BUILD)/tbm: $(OPENCM3_DIR)/lib/lib$(OPENCM3_LIBNAME).a + +$(BUILD)/tbm.bin: $(BUILD)/tbm + $(PREFIX)objcopy -Obinary $< $@ + +$(BUILD)/tbm.hex: $(BUILD)/tbm + $(PREFIX)objcopy -Oihex $< $@ + +run: $(BUILD)/tbm + @echo "GDB $<" + @$(GDB) --batch \ + -ex 'target extended-remote $(STLINK_PORT)' \ + -x flash.scr \ + $< + +flash: $(BUILD)/tbm.bin + stm32flash -g 0x8000000 -w $< $(STM32_PORT) + +.PHONY: run diff --git a/source/platform/stm32f1/Makefile b/source/platform/stm32f1/Makefile new file mode 100644 index 0000000..4feecbe --- /dev/null +++ b/source/platform/stm32f1/Makefile @@ -0,0 +1,5 @@ +obj-y += source/platform/stm32f1/gpio.o +obj-y += source/platform/stm32f1/rcc.o +obj-y += source/platform/stm32f1/rtc.o +obj-y += source/platform/stm32f1/spi.o +obj-y += source/platform/stm32f1/usart.o diff --git a/source/platform/stm32f1/gpio.c b/source/platform/stm32f1/gpio.c new file mode 100644 index 0000000..1818481 --- /dev/null +++ b/source/platform/stm32f1/gpio.c @@ -0,0 +1,41 @@ +#include + +#include + +int gpio_init(void) +{ + /* Set up GPIOs for SPI 1 */ + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, GPIO_SPI1_NSS); + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_SPI1_SCK | + GPIO_SPI1_MOSI); + gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, + GPIO_SPI1_MISO); + /*gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO4); + gpio_set(GPIOA, GPIO4); + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO5 | GPIO6 | GPIO7); + gpio_set_af(GPIOA, GPIO_AF0, GPIO5 | GPIO6 | GPIO7);*/ + + /* Set up GPIOs for user console (USART 1) */ + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART1_TX); + gpio_set_mode(GPIOA, GPIO_MODE_INPUT, + GPIO_CNF_INPUT_FLOAT, GPIO_USART1_RX); + /*gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO9 | GPIO10); + gpio_set_af(GPIOA, GPIO_AF1, GPIO9 | GPIO10);*/ + + /* Set up GPIOs for admin console (USART 2) */ + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART2_TX); + gpio_set_mode(GPIOA, GPIO_MODE_INPUT, + GPIO_CNF_INPUT_FLOAT, GPIO_USART2_RX); + /*gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO2 | GPIO3); + gpio_set_af(GPIOA, GPIO_AF1, GPIO2 | GPIO3);*/ + + return 0; +} + +void gpio_cleanup(void) +{ +} diff --git a/source/platform/stm32f1/rcc.c b/source/platform/stm32f1/rcc.c new file mode 100644 index 0000000..e18f9f6 --- /dev/null +++ b/source/platform/stm32f1/rcc.c @@ -0,0 +1,19 @@ +#include + +#include + +int rcc_init(void) +{ + rcc_periph_clock_enable(RCC_AFIO); + rcc_periph_clock_enable(RCC_GPIOA); + rcc_periph_clock_enable(RCC_GPIOB); + rcc_periph_clock_enable(RCC_SPI1); + rcc_periph_clock_enable(RCC_USART1); + rcc_periph_clock_enable(RCC_USART2); + + return 0; +} + +void rcc_cleanup(void) +{ +} diff --git a/source/platform/stm32f1/rtc.c b/source/platform/stm32f1/rtc.c new file mode 100644 index 0000000..39092e8 --- /dev/null +++ b/source/platform/stm32f1/rtc.c @@ -0,0 +1,42 @@ +#include +#include +#include + +#include +#include + +int rtc_get_time(struct tm *time) +{ + time_t unix_time; + + if (!time) + return -1; + + unix_time = rtc_get_counter_val(); + + if (!gmtime_r(&unix_time, time)) + return -1; + + return 0; +} + +static int rtc_set_time(struct tm *time) +{ + time_t unix_time; + + if (!time) + return -1; + + unix_time = mktime(time); + rtc_set_counter_val((uint32_t)unix_time); + + return 0; +} + +int rtc_init(struct tm *time) +{ + rtc_auto_awake(RCC_LSI, 0x7fff); + rtc_set_time(time); + + return 0; +} diff --git a/source/platform/stm32f1/spi.c b/source/platform/stm32f1/spi.c new file mode 100644 index 0000000..964bd82 --- /dev/null +++ b/source/platform/stm32f1/spi.c @@ -0,0 +1,78 @@ +#define _GNU_SOURCE +#include +#include + +#include +#include +#include + +#include + +static int stm32f1_spi_tx_rx(struct spi_dev *dev, void *rx_buf, size_t rx_len, + const void *tx_buf, size_t tx_len); + +static struct spi_ops stm32f1_spi_ops = { + .tx_rx = stm32f1_spi_tx_rx, +}; + +static void stm32f1_spi_init(void) +{ + spi_reset(SPI1); + spi_init_master(SPI1, + SPI_CR1_BAUDRATE_FPCLK_DIV_64, + SPI_CR1_CPOL_CLK_TO_1_WHEN_IDLE, + SPI_CR1_CPHA_CLK_TRANSITION_2, + SPI_CR1_DFF_8BIT, + SPI_CR1_MSBFIRST); + spi_enable_software_slave_management(SPI1); + spi_set_nss_high(SPI1); + spi_enable(SPI1); + + gpio_set(GPIOA, GPIO_SPI1_NSS); +} + +struct spi_dev *spi_probe(void) +{ + struct spi_dev *dev; + + if (!(dev = malloc(sizeof *dev))) + return NULL; + + dev->ops = &stm32f1_spi_ops; + dev->dev_id = SPI1; + + stm32f1_spi_init(); + + return dev; +} + +void spi_release(struct spi_dev *dev) +{ + free(dev); +} + +static int stm32f1_spi_tx_rx(struct spi_dev *dev, void *rx_buf, size_t rx_len, + const void *tx_buf, size_t tx_len) +{ + char *rx = rx_buf; + const char *tx = tx_buf; + size_t i; + + gpio_clear(GPIOA, GPIO_SPI1_NSS); + + for (i = 0; i < tx_len; ++i) { + spi_send(dev->dev_id, *tx++); + (void)spi_read(dev->dev_id); + } + + if (rx_buf) { + for (i = 0; i < rx_len; ++i) { + spi_send(dev->dev_id, 0); + *rx++ = spi_read(dev->dev_id); + } + } + + gpio_set(GPIOA, GPIO_SPI1_NSS); + + return 0; +} diff --git a/source/platform/stm32f1/usart.c b/source/platform/stm32f1/usart.c new file mode 100644 index 0000000..7af8e48 --- /dev/null +++ b/source/platform/stm32f1/usart.c @@ -0,0 +1,321 @@ +#define _GNU_SOURCE +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define RECV_BUF_LEN 128 + +struct usart_console { + struct console console; + char recv_buf[RECV_BUF_LEN]; + volatile size_t cur; + volatile size_t next; + unsigned irq_no; + uint32_t dev; +}; + +static struct usart_console usart[2]; + +static void usart_isr(struct usart_console *console) +{ + size_t i; + + if (!console) + return; + + while (usart_get_flag(console->dev, USART_SR_RXNE)) { + console->recv_buf[console->next] = usart_recv(console->dev); + + if (console->recv_buf[console->next] == '\003') + scb_reset_system(); + + i = (console->next + 1) % RECV_BUF_LEN; + + if (i != console->cur) + console->next = i; + } +} + +void usart1_isr(void) +{ + usart_isr(usart + 0); +} + +void usart2_isr(void) +{ + usart_isr(usart + 1); +} + +static char usart_getc(struct usart_console *console, int block) +{ + char c = 0; + + while (block && (console->cur == console->next)); + + if (console->cur != console->next) { + c = console->recv_buf[console->cur]; + console->cur = (console->cur + 1) % RECV_BUF_LEN; + } + + return c; +} + +int console_peek(char *c, struct console *console_, int block) +{ + struct usart_console *console = container_of(console_, + struct usart_console, console); + + while (block && console->cur == console->next); + + if (console->cur != console->next) { + *c = console->recv_buf[console->cur]; + return 0; + } + + return -EAGAIN; +} + +int console_getc(char *c, struct console *console_) +{ + struct usart_console *console = container_of(console_, + struct usart_console, console); + + if (console->cur == console->next) + return -1; + + if (console->cur != console->next) { + *c = console->recv_buf[console->cur]; + console->cur = (console->cur + 1) % RECV_BUF_LEN; + } + + return 0; +} + +int console_getline(struct console *console_, char *buf, size_t n) +{ + struct usart_console *console = container_of(console_, + struct usart_console, console); + char *p = buf + strlen(buf); + char c; + + *p = '\0'; + + while (console_getc(&c, console_) == 0) { + switch (c) { + case '\r': + usart_send_blocking(console->dev, '\r'); + usart_send_blocking(console->dev, '\n'); + return 0; + case 10: + case 127: + if (buf < p) { + usart_send_blocking(console->dev, '\010'); + usart_send_blocking(console->dev, ' '); + usart_send_blocking(console->dev, '\010'); + --p; + } + + break; + default: + *p = c; + usart_send_blocking(console->dev, c); + + if (((size_t)p - (size_t)buf) < n) { + ++p; + } + } + + *p = '\0'; + } + + *p = '\0'; + + return -EAGAIN; +} + +ssize_t console_read(struct console *console_, char *buf, size_t n) +{ + struct usart_console *console = container_of(console_, + struct usart_console, console); + char *p = buf; + char c; + + *buf = '\0'; + + while (console_peek(&c, console_, 1) == 0) { + switch (c) { + case '\004': + if (buf < p) + goto out; + + console_getc(&c, console_); + return 0; + case 10: + case 127: + if (buf < p) { + usart_send_blocking(console->dev, '\010'); + usart_send_blocking(console->dev, ' '); + usart_send_blocking(console->dev, '\010'); + --p; + } + + console_getc(&c, console_); + + break; + case '\r': + if (((size_t)p - (size_t)buf) >= n) + goto out; + + *p++ = '\n'; + usart_send_blocking(console->dev, '\r'); + console_getc(&c, console_); + goto out; + default: + if (((size_t)p - (size_t)buf) >= n) + goto out; + + *p++ = c; + usart_send_blocking(console->dev, c); + console_getc(&c, console_); + break; + } + } + +out: + *p = '\0'; + + return p - buf; +} + +static ssize_t usart_read(void *cookie, char *buf, size_t n) +{ + struct usart_console *console = cookie; + char *p = buf; + char c; + + *buf = '\0'; + + while ((c = usart_getc(console, 1)) != '\r') { + switch (c) { + case 10: + case 127: + if (buf < p) { + usart_send_blocking(console->dev, '\010'); + usart_send_blocking(console->dev, ' '); + usart_send_blocking(console->dev, '\010'); + --p; + } + + break; + default: + *p = c; + usart_send_blocking(console->dev, c); + + if (((size_t)p - (size_t)buf) < n) { + ++p; + } + } + + *p = '\0'; + } + + *p = '\n'; + + if (((size_t)p - (size_t)buf) < n) { + ++p; + } + + *p = '\0'; + + return p - buf; +} + +static ssize_t usart_write(void *cookie, const char *buf, size_t n) +{ + struct usart_console *console = cookie; + size_t i; + + for (i = 0; i < n; ++i) { + if (buf[i] == '\n') + usart_send_blocking(console->dev, '\r'); + + usart_send_blocking(console->dev, buf[i]); + }; + + return i; +} + +static int usart_get_irq_no(unsigned *irq_no, unsigned dev) +{ + if (!irq_no) + return -1; + + switch (dev) { + case USART1: *irq_no = NVIC_USART1_IRQ; break; + case USART2: *irq_no = NVIC_USART2_IRQ; break; + default: return -1; + } + + return 0; +} + +static int usart_init(struct usart_console *console) +{ + if (usart_get_irq_no(&console->irq_no, console->dev) < 0) + return -1; + + usart_set_baudrate(console->dev, 115200); + usart_set_databits(console->dev, 8); + usart_set_parity(console->dev, USART_PARITY_NONE); + usart_set_stopbits(console->dev, USART_STOPBITS_1); + usart_set_mode(console->dev, USART_MODE_TX_RX); + usart_set_flow_control(console->dev, USART_FLOWCONTROL_NONE); + + nvic_enable_irq(console->irq_no); + usart_enable(console->dev); + usart_enable_rx_interrupt(console->dev); + + return 0; +} + +struct console *console_init(unsigned dev_id) +{ + cookie_io_functions_t fops = { + .read = usart_read, + .write = usart_write, + .seek = NULL, + .close = NULL, + }; + struct usart_console *console; + + console = usart + dev_id; + + switch (dev_id) { + case 0: console->dev = USART1; break; + case 1: console->dev = USART2; break; + default: return NULL; + } + + console->cur = 0; + console->next = 0; + + usart_init(console); + + console->console.fp = fopencookie(console, "r+w", fops); + setvbuf(console->console.fp, NULL, _IONBF, 0); + + return &console->console; +} diff --git a/support/libopencm3_stm32f1.ld b/support/libopencm3_stm32f1.ld new file mode 100644 index 0000000..3fc2ccb --- /dev/null +++ b/support/libopencm3_stm32f1.ld @@ -0,0 +1,106 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2009 Uwe Hermann + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/* Generic linker script for STM32 targets using libopencm3. */ + +/* Memory regions must be defined in the ld script which includes this one. */ + +/* Enforce emmition of the vector table. */ +EXTERN (vector_table) + +/* Define the entry point of the output file. */ +ENTRY(reset_handler) + +/* Define sections. */ +SECTIONS +{ + .text : { + *(.vectors) /* Vector table */ + *(.text*) /* Program code */ + . = ALIGN(4); + *(.rodata*) /* Read-only data */ + . = ALIGN(4); + } >rom + + /* C++ Static constructors/destructors, also used for __attribute__ + * ((constructor)) and the likes */ + .preinit_array : { + . = ALIGN(4); + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + } >rom + .init_array : { + . = ALIGN(4); + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; + } >rom + .fini_array : { + . = ALIGN(4); + __fini_array_start = .; + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + __fini_array_end = .; + } >rom + + /* + * Another section used by C++ stuff, appears when using newlib with + * 64bit (long long) printf support + */ + .ARM.extab : { + *(.ARM.extab*) + } >rom + .ARM.exidx : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >rom + + . = ALIGN(4); + _etext = .; + + .data : { + _data = .; + *(.data*) /* Read-write initialized data */ + . = ALIGN(4); + _edata = .; + } >ram AT >rom + _data_loadaddr = LOADADDR(.data); + + .bss : { + *(.bss*) /* Read-write zero initialized data */ + *(COMMON) + . = ALIGN(4); + _ebss = .; + } >ram + + /* + * The .eh_frame section appears to be used for C++ exception handling. + * You may need to fix this if you're using C++. + */ + /DISCARD/ : { *(.eh_frame) } + + . = ALIGN(4); + end = .; +} + +PROVIDE(_stack = ORIGIN(ram) + LENGTH(ram)); + diff --git a/support/stm32vl-discovery.ld b/support/stm32vl-discovery.ld new file mode 100644 index 0000000..40de3d3 --- /dev/null +++ b/support/stm32vl-discovery.ld @@ -0,0 +1,31 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2009 Uwe Hermann + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/* Linker script for ST STM32VLDISCOVERY (STM32F100RB, 128K flash, 8K RAM). */ + +/* Define memory regions. */ +MEMORY +{ + rom (rx) : ORIGIN = 0x08000000, LENGTH = 128K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 8K +} + +/* Include the common ld script. */ +INCLUDE libopencm3_stm32f1.ld +