From e5585dd07dc5436681c7a0810503625344d69f37 Mon Sep 17 00:00:00 2001 From: cmcmanis Date: Fri, 7 Feb 2014 22:31:27 -0800 Subject: [PATCH] Some examples for the STM32F4-Disco board --- .../stm32/f4/stm32f4-disco/blink/Makefile | 22 ++ examples/stm32/f4/stm32f4-disco/blink/README | 8 + examples/stm32/f4/stm32f4-disco/blink/blink.c | 51 +++ .../stm32/f4/stm32f4-disco/sdram/Makefile | 25 ++ examples/stm32/f4/stm32f4-disco/sdram/clock.c | 70 ++++ examples/stm32/f4/stm32f4-disco/sdram/clock.h | 15 + .../stm32/f4/stm32f4-disco/sdram/console.c | 234 ++++++++++++ .../stm32/f4/stm32f4-disco/sdram/console.h | 13 + examples/stm32/f4/stm32f4-disco/sdram/sdram.c | 338 +++++++++++++++++ .../stm32/f4/stm32f4-disco/stm32f4-disco.ld | 32 ++ .../f4/stm32f4-disco/systick-blink/Makefile | 22 ++ .../f4/stm32f4-disco/systick-blink/README | 7 + .../systick-blink/systick-blink.c | 93 +++++ .../stm32/f4/stm32f4-disco/usart-irq/Makefile | 28 ++ .../stm32/f4/stm32f4-disco/usart-irq/README | 27 ++ .../stm32/f4/stm32f4-disco/usart-irq/clock.c | 70 ++++ .../stm32/f4/stm32f4-disco/usart-irq/clock.h | 16 + .../f4/stm32f4-disco/usart-irq/usart-irq.c | 341 ++++++++++++++++++ .../stm32/f4/stm32f4-disco/usart/Makefile | 25 ++ examples/stm32/f4/stm32f4-disco/usart/README | 9 + examples/stm32/f4/stm32f4-disco/usart/clock.c | 71 ++++ examples/stm32/f4/stm32f4-disco/usart/clock.h | 15 + examples/stm32/f4/stm32f4-disco/usart/usart.c | 184 ++++++++++ 23 files changed, 1716 insertions(+) create mode 100644 examples/stm32/f4/stm32f4-disco/blink/Makefile create mode 100644 examples/stm32/f4/stm32f4-disco/blink/README create mode 100644 examples/stm32/f4/stm32f4-disco/blink/blink.c create mode 100644 examples/stm32/f4/stm32f4-disco/sdram/Makefile create mode 100644 examples/stm32/f4/stm32f4-disco/sdram/clock.c create mode 100644 examples/stm32/f4/stm32f4-disco/sdram/clock.h create mode 100644 examples/stm32/f4/stm32f4-disco/sdram/console.c create mode 100644 examples/stm32/f4/stm32f4-disco/sdram/console.h create mode 100644 examples/stm32/f4/stm32f4-disco/sdram/sdram.c create mode 100644 examples/stm32/f4/stm32f4-disco/stm32f4-disco.ld create mode 100644 examples/stm32/f4/stm32f4-disco/systick-blink/Makefile create mode 100644 examples/stm32/f4/stm32f4-disco/systick-blink/README create mode 100644 examples/stm32/f4/stm32f4-disco/systick-blink/systick-blink.c create mode 100644 examples/stm32/f4/stm32f4-disco/usart-irq/Makefile create mode 100644 examples/stm32/f4/stm32f4-disco/usart-irq/README create mode 100644 examples/stm32/f4/stm32f4-disco/usart-irq/clock.c create mode 100644 examples/stm32/f4/stm32f4-disco/usart-irq/clock.h create mode 100644 examples/stm32/f4/stm32f4-disco/usart-irq/usart-irq.c create mode 100644 examples/stm32/f4/stm32f4-disco/usart/Makefile create mode 100644 examples/stm32/f4/stm32f4-disco/usart/README create mode 100644 examples/stm32/f4/stm32f4-disco/usart/clock.c create mode 100644 examples/stm32/f4/stm32f4-disco/usart/clock.h create mode 100644 examples/stm32/f4/stm32f4-disco/usart/usart.c diff --git a/examples/stm32/f4/stm32f4-disco/blink/Makefile b/examples/stm32/f4/stm32f4-disco/blink/Makefile new file mode 100644 index 0000000..d41c681 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/blink/Makefile @@ -0,0 +1,22 @@ +# +# This file is part of the libopencm3 project. +# +# 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 . +# + +BINARY = blink + +LDSCRIPT = ../stm32f4-disco.ld + +include ../../Makefile.include diff --git a/examples/stm32/f4/stm32f4-disco/blink/README b/examples/stm32/f4/stm32f4-disco/blink/README new file mode 100644 index 0000000..66543ab --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/blink/README @@ -0,0 +1,8 @@ +README +------ + +The "HelloWorld" of embedded systems. This code is really really simple, +it sets up the system clock at a known frequency (168Mhz) and enables +a GPIO pin as an output, that happens to be connected to an LED. And it +blinks it on and off. + diff --git a/examples/stm32/f4/stm32f4-disco/blink/blink.c b/examples/stm32/f4/stm32f4-disco/blink/blink.c new file mode 100644 index 0000000..d95a7eb --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/blink/blink.c @@ -0,0 +1,51 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014 Chuck McManis (cmcmanis@mcmanis.com) + * + * 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 . + */ + +#include +#include + +#define GREEN_LED GPIO13 +#define RED_LED GPIO13 + +int main(void) +{ + int i; + + /* Use parameters to set the STM32 clock to 168 MHz. */ + rcc_clock_setup_hse_3v3(&hse_8mhz_3v3[CLOCK_3V3_168MHZ]); + + /* Enable GPIOG clock. (this enables the pins to work) */ + rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPGEN); + + /* Set the "mode" of the GPIO pin to output, no pullups or pulldowns */ + gpio_mode_setup(GPIOG, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GREEN_LED ); + + /* Turn on the GREEN led */ + gpio_set(GPIOG, GREEN_LED); + + /* Blink the GREEN LED on the board. */ + while (1) { + /* Toggle LEDs. */ + gpio_toggle(GPIOG, GREEN_LED); + for (i = 0; i < 10000000; i++) /* Wait a bit. */ + __asm__("nop"); + } + + return 0; +} diff --git a/examples/stm32/f4/stm32f4-disco/sdram/Makefile b/examples/stm32/f4/stm32f4-disco/sdram/Makefile new file mode 100644 index 0000000..e9391f6 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/sdram/Makefile @@ -0,0 +1,25 @@ +# +# This file is part of the libopencm3 project. +# +# 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 . +# + +OBJS = console.o clock.o + +BINARY = sdram + +LDSCRIPT = ../stm32f4-disco.ld + +include ../../Makefile.include + diff --git a/examples/stm32/f4/stm32f4-disco/sdram/clock.c b/examples/stm32/f4/stm32f4-disco/sdram/clock.c new file mode 100644 index 0000000..61f0f82 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/sdram/clock.c @@ -0,0 +1,70 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2013 Chuck McManis + * + * 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 . + */ + +/* + * Now this is just the clock setup code from systick-blink as it is the + * transferrable part. + */ + +#include +#include +#include + +/* Common function descriptions */ +#include "clock.h" + +/* milliseconds since boot */ +static volatile uint32_t system_millis; + +/* Called when systick fires */ +void sys_tick_handler(void) { + system_millis++; +} + +/* simple sleep for delay milliseconds */ +void msleep(uint32_t delay) { + uint32_t wake = system_millis + delay; + while (wake > system_millis) ; +} + +/* Getter function for the current time */ +uint32_t mtime(void) { + return system_millis; +} + +/* + * clock_setup(void) + * + * This function sets up both the base board clock rate + * and a 1khz "system tick" count. The SYSTICK counter is + * a standard feature of the Cortex-M series. + */ +void clock_setup(void) +{ + /* Base board frequency, set to 168Mhz */ + rcc_clock_setup_hse_3v3(&hse_8mhz_3v3[CLOCK_3V3_168MHZ]); + + /* clock rate / 168000 to get 1mS interrupt rate */ + systick_set_reload(168000); + systick_set_clocksource(STK_CSR_CLKSOURCE_AHB); + systick_counter_enable(); + + /* this done last */ + systick_interrupt_enable(); +} diff --git a/examples/stm32/f4/stm32f4-disco/sdram/clock.h b/examples/stm32/f4/stm32f4-disco/sdram/clock.h new file mode 100644 index 0000000..01c14ac --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/sdram/clock.h @@ -0,0 +1,15 @@ +/* + * This include file describes the functions exported by clock.c + */ +#ifndef __CLOCK_H +#define __CLOCK_H + +/* + * Definitions for functions being abstracted out + */ +void msleep(uint32_t); +uint32_t mtime(void); +void clock_setup(void); + +#endif /* generic header protector */ + diff --git a/examples/stm32/f4/stm32f4-disco/sdram/console.c b/examples/stm32/f4/stm32f4-disco/sdram/console.c new file mode 100644 index 0000000..c37afd0 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/sdram/console.c @@ -0,0 +1,234 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2013 Chuck McManis + * + * 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 . + */ + +/* + * USART example (alternate console) + * + * This version then adds in interrupts, which is really handy for + * the receive function as it is impossible to predict when someone + * might type a character, further you can create a "character based + * reset" capability if you choose to. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "console.h" + + +/* + * Some definitions of our console "functions" attached to the + * USART. + * + * These define sort of the minimum "library" of functions which + * we can use on a serial port. + */ + + +#define CONSOLE_UART USART2 + + +/* This is a ring buffer to holding characters as they are typed + * it maintains both the place to put the next character received + * from the UART, and the place where the last character was + * read by the program. See the README file for a discussion of + * the failure semantics. + */ +#define RECV_BUF_SIZE 128 // Arbitrary buffer size +char recv_buf[RECV_BUF_SIZE]; +volatile int recv_ndx_nxt; // Next place to store +volatile int recv_ndx_cur; // Next place to read + +/* For interrupt handling we add a new function which is called + * when recieve interrupts happen. The name (usart2_isr) is created + * by the irq.json file in libopencm3 calling this interrupt for + * USART2 'usart2', adding the suffix '_isr', and then weakly binding + * it to the 'do nothing' interrupt function in vec.c. + * + * By defining it in this file the linker will override that weak + * binding and instead bind it here, but you have to get the name + * right or it won't work. And you'll wonder where your interrupts + * are going. + */ +void usart2_isr(void) { + uint32_t reg; + int i; + + do { + reg = USART_SR(CONSOLE_UART); + if (reg & USART_SR_RXNE) { + recv_buf[recv_ndx_nxt] = USART_DR(CONSOLE_UART); +#ifdef RESET_ON_CTRLC + /* Check for "reset" */ + if (recv_buf[recv_ndx_nxt] == '\003') { + /* reset the system */ + volatile uint32_t *ret = (®) + 7; // Return address on stack + + *ret = (uint32_t) &reset_handler; // force system reset + return; // go to new address + } +#endif + /* Check for "overrun" */ + i = (recv_ndx_nxt + 1) % RECV_BUF_SIZE; + if (i != recv_ndx_cur) { + recv_ndx_nxt = i; + } + } + } while ((reg & USART_SR_RXNE) != 0); // can read back-to-back interrupts +} + +/* + * console_putc(char c) + * + * Send the character 'c' to the USART, wait for the USART + * transmit buffer to be empty first. + */ +void console_putc(char c) { + uint32_t reg; + do { + reg = USART_SR(CONSOLE_UART); + } while ((reg & USART_SR_TXE) == 0); + USART_DR(CONSOLE_UART) = (uint16_t) c & 0xff; +} + +/* + * char = console_getc(int wait) + * + * Check the console for a character. If the wait flag is + * non-zero. Continue checking until a character is received + * otherwise return 0 if called and no character was available. + * + * The implementation is a bit different however, now it looks + * in the ring buffer to see if a character has arrived. + */ +char console_getc(int wait) { + char c = 0; + + while ((wait != 0) && (recv_ndx_cur == recv_ndx_nxt)) ; + if (recv_ndx_cur != recv_ndx_nxt) { + c = recv_buf[recv_ndx_cur]; + recv_ndx_cur = (recv_ndx_cur + 1) % RECV_BUF_SIZE; + } + return c; +} + +/* + * void console_puts(char *s) + * + * Send a string to the console, one character at a time, return + * after the last character, as indicated by a NUL character, is + * reached. + */ +void console_puts(char *s) { + while (*s != '\000') { + console_putc(*s); + /* Add in a carraige return, after sending line feed */ + if (*s == '\n') { + console_putc('\r'); + } + s++; + } +} + +/* + * int console_gets(char *s, int len) + * + * Wait for a string to be entered on the console, limited + * support for editing characters (back space and delete) + * end when a character is received. + */ +int console_gets(char *s, int len) { + char *t = s; + char c; + + *t = '\000'; + /* read until a is received */ + while ((c = console_getc(1)) != '\r') { + if ((c == '\010') || (c == '\127')) { + if (t > s) { + /* send ^H ^H to erase previous character */ + console_puts("\010 \010"); + t--; + } + } else { + *t = c; + console_putc(c); + if ((t - s) < len) { + t++; + } + } + /* update end of string with NUL */ + *t = '\000'; + } + return (t - s); +} + +/* + * Set up the GPIO subsystem with an "Alternate Function" + * on some of the pins, in this case connected to a + * USART. + */ +void console_setup(void) { + + /* MUST enable the GPIO clock in ADDITION to the USART clock */ + rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPDEN); + + /* This example uses PD5 and PD6 for Tx and Rx respectively + * but other pins are available for this role on USART2 (our chosen + * USART) as well, such as PA2 and PA3. You can also split them + * so PA2 for Tx, PD6 for Rx but you would have to enable both + * the GPIOA and GPIOD clocks in that case + */ + gpio_mode_setup(GPIOD, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO5 | GPIO6); + + /* Actual Alternate function number (in this case 7) is part + * depenedent, check the data sheet for the right number to + * use. + */ + gpio_set_af(GPIOD, GPIO_AF7, GPIO5 | GPIO6); + + + /* This then enables the clock to the USART2 peripheral which is + * attached inside the chip to the APB2 bus. Different peripherals + * attach to different buses, and even some UARTS are attached to + * APB1 and some to APB2, again the data sheet is useful here. + */ + rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USART2EN); + + /* Set up USART/UART parameters using the libopencm3 helper functions */ + usart_set_baudrate(CONSOLE_UART, 115200); + usart_set_databits(CONSOLE_UART, 8); + usart_set_stopbits(CONSOLE_UART, USART_STOPBITS_1); + usart_set_mode(CONSOLE_UART, USART_MODE_TX_RX); + usart_set_parity(CONSOLE_UART, USART_PARITY_NONE); + usart_set_flow_control(CONSOLE_UART, USART_FLOWCONTROL_NONE); + usart_enable(CONSOLE_UART); + + /* Enable interrupts from the USART */ + nvic_enable_irq(NVIC_USART2_IRQ); + + /* Specifically enable recieve interrupts */ + usart_enable_rx_interrupt(CONSOLE_UART); +} diff --git a/examples/stm32/f4/stm32f4-disco/sdram/console.h b/examples/stm32/f4/stm32f4-disco/sdram/console.h new file mode 100644 index 0000000..4cddac7 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/sdram/console.h @@ -0,0 +1,13 @@ +/* + * Our simple console definitions + */ + +void console_putc(char c); +char console_getc(int wait); +void console_puts(char *s); +int console_gets(char *s, int len); +void console_setup(void); + +/* this is for fun, if you type ^C to this example it will reset */ +#define RESET_ON_CTRLC + diff --git a/examples/stm32/f4/stm32f4-disco/sdram/sdram.c b/examples/stm32/f4/stm32f4-disco/sdram/sdram.c new file mode 100644 index 0000000..5cde357 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/sdram/sdram.c @@ -0,0 +1,338 @@ +/* + * sdram.c - SDRAM controller example + * + * This file is part of the libopencm3 project. + * + * Copyright (C) 2013 Chuck McManis + * + * 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 . + */ + +#include +#include +#include +#include +#include "clock.h" +#include "console.h" + +#define SDRAM_BASE_ADDRESS ((uint8_t *)(0xd0000000)) + +void sdram_init(void); + +#ifndef NULL +#define NULL (void *)(0) +#endif + +/* + * This is just syntactic sugar but it helps, all of these + * GPIO pins get configured in exactly the same way. + */ +static struct { + uint32_t gpio; + uint16_t pins; +} sdram_pins[6] = { + {GPIOB, GPIO5 | GPIO6 }, + {GPIOC, GPIO0 }, + {GPIOD, GPIO0 | GPIO1 | GPIO8 | GPIO9 | GPIO10 | GPIO14 | GPIO15}, + {GPIOE, GPIO0 | GPIO1 | GPIO7 | GPIO8 | GPIO9 | GPIO10 | + GPIO11 | GPIO12 | GPIO13 | GPIO14 | GPIO15 }, + {GPIOF, GPIO0 | GPIO1 | GPIO2 | GPIO3 | GPIO4 | GPIO5 | GPIO11 | + GPIO12 | GPIO13 | GPIO14 | GPIO15 }, + {GPIOG, GPIO0 | GPIO1 | GPIO4 | GPIO5 |GPIO8 | GPIO15} +}; + +static struct sdram_timing timing = { + .trcd = 2, /* RCD Delay */ + .trp = 2, /* RP Delay */ + .twr = 2, /* Write Recovery Time */ + .trc = 7, /* Row Cycle Delay */ + .tras = 4, /* Self Refresh Time */ + .txsr = 7, /* Exit Self Refresh Time */ + .tmrd = 2, /* Load to Active Delay */ +}; + +/* + * Initialize the SD RAM controller. + */ +void +sdram_init(void) { + int i; + uint32_t cr_tmp, tr_tmp; // control, timing registers + + /* + * First all the GPIO pins that end up as SDRAM pins + */ + rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPBEN); + rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPCEN); + rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPDEN); + rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPEEN); + rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPFEN); + rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPGEN); + + for (i = 0; i < 6; i++) { + gpio_mode_setup(sdram_pins[i].gpio, GPIO_MODE_AF, GPIO_PUPD_NONE, + sdram_pins[i].pins); + gpio_set_output_options(sdram_pins[i].gpio, GPIO_OTYPE_PP, + GPIO_OSPEED_50MHZ, sdram_pins[i].pins); + gpio_set_af(sdram_pins[i].gpio, GPIO_AF12, sdram_pins[i].pins); + } + + /* Enable the SDRAM Controller */ + rcc_peripheral_enable_clock(&RCC_AHB3ENR, RCC_AHB3ENR_FMCEN); + + /* Note the STM32F429-DISCO board has the ram attached to bank 2 */ + /* Timing parameters computed for a 168Mhz clock */ + /* These parameters are specific to the SDRAM chip on the board */ + + cr_tmp = FMC_SDCR_RPIPE_1CLK; + cr_tmp |= FMC_SDCR_SDCLK_2HCLK; + cr_tmp |= FMC_SDCR_CAS_3CYC; + cr_tmp |= FMC_SDCR_NB4; + cr_tmp |= FMC_SDCR_MWID_16b; + cr_tmp |= FMC_SDCR_NR_12; + cr_tmp |= FMC_SDCR_NC_8; + + /* We're programming BANK 2, but per the manual some of the parameters + * only work in CR1 and TR1 so we pull those off and put them in the + * right place. + */ + FMC_SDCR1 |= (cr_tmp & FMC_SDCR_DNC_MASK); + FMC_SDCR2 = cr_tmp; + + tr_tmp = sdram_timing(&timing); + FMC_SDTR1 |= (tr_tmp & FMC_SDTR_DNC_MASK); + FMC_SDTR2 = tr_tmp; + + /* Now start up the Controller per the manual + * - Clock config enable + * - PALL state + * - set auto refresh + * - Load the Mode Register + */ + sdram_command(SDRAM_BANK2, SDRAM_CLK_CONF, 1, 0); + msleep(1); // sleep at least 100uS + sdram_command(SDRAM_BANK2, SDRAM_PALL, 1, 0); + sdram_command(SDRAM_BANK2, SDRAM_AUTO_REFRESH, 4, 0); + tr_tmp = SDRAM_MODE_BURST_LENGTH_2 | + SDRAM_MODE_BURST_TYPE_SEQUENTIAL | + SDRAM_MODE_CAS_LATENCY_3 | + SDRAM_MODE_OPERATING_MODE_STANDARD | + SDRAM_MODE_WRITEBURST_MODE_SINGLE; + sdram_command(SDRAM_BANK2, SDRAM_LOAD_MODE, 1, tr_tmp); + + /* + * set the refresh counter to insure we kick off an + * auto refresh often enough to prevent data loss. + */ + FMC_SDRTR = 683; + /* and Poof! a 8 megabytes of ram shows up in the address space */ +} + +/* + * This code are some routines that implement a "classic" + * hex dump style memory dump. So you can look at what is + * in the RAM, alter it (in a couple of automated ways) + */ +void dump_byte(uint8_t b); +void dump_long(uint32_t l); +uint8_t *dump_line(uint8_t *, uint8_t *); +uint8_t *dump_page(uint8_t *, uint8_t *); + + +/* make a nybble into an ascii hex character 0 - 9, A-F */ +#define HEX_CHAR(x) ((((x) + '0') > '9') ? ((x) + '7') : ((x) + '0')) + +/* send an 8 bit byte as two HEX characters to the console */ +void dump_byte(uint8_t b) { + console_putc(HEX_CHAR((b >> 4) & 0xf)); + console_putc(HEX_CHAR(b & 0xf)); +} + +/* send a 32 bit value as 8 hex characters to the console */ +void dump_long(uint32_t l) { + int i = 0; + for (i = 0; i < 8; i++) { + console_putc(HEX_CHAR((l >> (28 - i * 4)) & 0xf)); + } +} + +/* + * dump a 'line' (an address, 16 bytes, and then the + * ASCII representation of those bytes) to the console. + * Takes an address (and possiblye a 'base' parameter + * so that you can offset the address) and sends 16 + * bytes out. Returns the address +16 so you can + * just call it repeatedly and it will send the + * next 16 bytes out. + */ +uint8_t * +dump_line(uint8_t *addr, uint8_t *base) { + uint8_t *line_addr; + uint8_t b; + uint32_t tmp; + int i; + + line_addr = addr; + tmp = (uint32_t)line_addr - (uint32_t) base; + dump_long(tmp); + console_puts(" | "); + for (i = 0; i < 16; i++) { + dump_byte(*(line_addr+i)); + console_putc(' '); + if (i == 7) { + console_puts(" "); + } + } + console_puts("| "); + for (i = 0; i < 16; i++) { + b = *line_addr++; + console_putc(((b > 126) || (b < 32)) ? '.' : (char) b); + } + console_puts("\n"); + return line_addr; +} + + +/* + * dump a 'page' like the function dump_line except this + * does 16 lines for a total of 256 bytes. Back in the + * day when you had a 24 x 80 terminal this fit nicely + * on the screen with some other information. + */ +uint8_t * +dump_page(uint8_t *addr, uint8_t *base) { + int i; + for (i = 0; i < 16; i++) { + addr = dump_line(addr, base); + } + return addr; +} + +/* + * This example initializes the SDRAM controller and dumps + * it out to the console. You can do various things like + * (FI) fill with increment, (F0) fill with 0, (FF) fill + * with FF. NP (next page), PP (prev page), NL (next line), + * (PL) previous line, and (?) for help. + */ +int +main(void) { + int i; + uint8_t *addr; + char c; + + clock_setup(); + console_setup(); + sdram_init(); + + console_puts("SDRAM Example.\n"); + console_puts("Original data:\n"); + addr = (uint8_t *)(0xd0000000); + (void) dump_page(addr, NULL); + addr = SDRAM_BASE_ADDRESS; + for (i = 0; i < 256; i++) { + *(addr + i) = i; + } + console_puts("Modified data (with Fill Increment)\n"); + addr = SDRAM_BASE_ADDRESS; + addr = dump_page( addr, NULL); + while (1) { + console_puts("CMD> "); + switch (c = console_getc(1)) { + case 'f': + case 'F': + console_puts("Fill "); + switch (c = console_getc(1)) { + case 'i': + case 'I': + console_puts("Increment\n"); + for (i = 0; i < 256; i++) { + *(addr+i) = i; + } + dump_page(addr, NULL); + break; + case '0': + console_puts("Zero\n"); + for (i = 0; i < 256; i++) { + *(addr+i) = 0; + } + dump_page(addr, NULL); + break; + case 'f': + case 'F': + console_puts("Ones (0xff)\n"); + for (i = 0; i < 256; i++) { + *(addr+i) = 0xff; + } + dump_page(addr, NULL); + break; + default: + console_puts("Unrecognized Command, press ? for help\n"); + } + break; + case 'n': + case 'N': + console_puts("Next "); + switch (c = console_getc(1)) { + case 'p': + case 'P': + console_puts("Page\n"); + addr += 256; + dump_page(addr, NULL); + break; + case 'l': + case 'L': + console_puts("Line\n"); + addr += 16; + dump_line(addr, NULL); + break; + default: + console_puts("Unrecognized Command, press ? for help\n"); + } + break; + case 'p': + case 'P': + console_puts("Previous "); + switch (c = console_getc(1)) { + case 'p': + case 'P': + console_puts("Page\n"); + addr -= 256; + dump_page(addr, NULL); + break; + case 'l': + case 'L': + console_puts("Line\n"); + addr -= 16; + dump_line(addr, NULL); + break; + default: + console_puts("Unrecognized Command, press ? for help\n"); + } + break; + case '?': + default: + console_puts("Help\n"); + console_puts(" n p - dump next page\n"); + console_puts(" n l - dump next line\n"); + console_puts(" p p - dump previous page\n"); + console_puts(" p l - dump previous line\n"); + console_puts(" f 0 - fill current page with 0\n"); + console_puts(" f i - fill current page with 0 to 255\n"); + console_puts(" f f - fill current page with 0xff\n"); + console_puts(" ? - this message\n"); + break; + } + } +} diff --git a/examples/stm32/f4/stm32f4-disco/stm32f4-disco.ld b/examples/stm32/f4/stm32f4-disco/stm32f4-disco.ld new file mode 100644 index 0000000..fb4862b --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/stm32f4-disco.ld @@ -0,0 +1,32 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2009 Uwe Hermann + * Copyright (C) 2011 Stephen Caudle + * + * 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 STM32F4DISCOVERY "DISCO" (STM32F429, 2024K flash, 192K RAM). */ + +/* Define memory regions. */ +MEMORY +{ + rom (rx) : ORIGIN = 0x08000000, LENGTH = 2048K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 128K +} + +/* Include the common ld script. */ +INCLUDE libopencm3_stm32f4.ld + diff --git a/examples/stm32/f4/stm32f4-disco/systick-blink/Makefile b/examples/stm32/f4/stm32f4-disco/systick-blink/Makefile new file mode 100644 index 0000000..0aba9ea --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/systick-blink/Makefile @@ -0,0 +1,22 @@ +# +# This file is part of the libopencm3 project. +# +# 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 . +# + +BINARY = systick-blink + +LDSCRIPT = ../stm32f4-disco.ld + +include ../../Makefile.include diff --git a/examples/stm32/f4/stm32f4-disco/systick-blink/README b/examples/stm32/f4/stm32f4-disco/systick-blink/README new file mode 100644 index 0000000..3352d95 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/systick-blink/README @@ -0,0 +1,7 @@ +Systick Blink +------------- + +This version of blink is slightly more sophisticated, it shows how you +can initialize the Cortex M SYSTICK register to give a regular interrupt. +It adds a function for doing precise delays. The original blink code +is then rewritten with this in mind to create a 10Hz blinking pattern. diff --git a/examples/stm32/f4/stm32f4-disco/systick-blink/systick-blink.c b/examples/stm32/f4/stm32f4-disco/systick-blink/systick-blink.c new file mode 100644 index 0000000..a16c835 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/systick-blink/systick-blink.c @@ -0,0 +1,93 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2013 Chuck McManis + * + * 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 . + */ + +/* This version derived from fancy blink */ + +#include +#include +#include +#include + +/* + * Definitions for functions being abstracted out + */ +void msleep(uint32_t); +void clock_setup(void); + +/* monotonically increasing number of milliseconds from reset + * overflows every 49 days if you're wondering + */ +volatile uint32_t system_millis; + +/* Called when systick fires */ +void sys_tick_handler(void) { + system_millis++; +} + +/* sleep for delay milliseconds */ +void msleep(uint32_t delay) { + uint32_t wake = system_millis + delay; + while (wake > system_millis) ; +} + + +/* + * clock_setup(void) + * + * This function sets up both the base board clock rate + * and a 1khz "system tick" count. The SYSTICK counter is + * a standard feature of the Cortex-M series. + */ +void clock_setup(void) +{ + /* Base board frequency, set to 168Mhz */ + rcc_clock_setup_hse_3v3(&hse_8mhz_3v3[CLOCK_3V3_168MHZ]); + + /* clock rate / 168000 to get 1mS interrupt rate */ + systick_set_reload(168000); + systick_set_clocksource(STK_CSR_CLKSOURCE_AHB); + systick_counter_enable(); + + /* this done last */ + systick_interrupt_enable(); +} + +int main(void) +{ + /* Set up clock and systick */ + clock_setup(); + + /* Enable GPIOD clock. */ + rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPGEN); + + /* Set GPIO13-14 (in GPIO port G) to 'output push-pull'. */ + gpio_mode_setup(GPIOG, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO13 | GPIO14); + + /* Set LED for alternating effect when toggling. */ + gpio_set(GPIOG, GPIO13); + + /* Blink the LEDs (PD12, PD13, PD14 and PD15) on the board. */ + while (1) { + gpio_toggle(GPIOG, GPIO13 | GPIO14); + /* Now sleep for 100ms which toggles at 10hz rate */ + msleep(100); + } + + return 0; +} diff --git a/examples/stm32/f4/stm32f4-disco/usart-irq/Makefile b/examples/stm32/f4/stm32f4-disco/usart-irq/Makefile new file mode 100644 index 0000000..57ede67 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/usart-irq/Makefile @@ -0,0 +1,28 @@ +# +# This file is part of the libopencm3 project. +# +# 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 . +# + +OBJS = clock.o + +BINARY = usart-irq + +# Example showing how to generate a map file. +LDFLAGS += -Wl,--Map=$(BINARY).map + +LDSCRIPT = ../stm32f4-disco.ld + +include ../../Makefile.include + diff --git a/examples/stm32/f4/stm32f4-disco/usart-irq/README b/examples/stm32/f4/stm32f4-disco/usart-irq/README new file mode 100644 index 0000000..f9a8e66 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/usart-irq/README @@ -0,0 +1,27 @@ +USART IRQ +--------- + +This is a slightly fancier console interface using interrupts. Basically +this example sets up the USART for serial input and output as before +except that the receive side is interrupt driven. That means you program +won't miss a character if it happens to be taking its time printing something +at the time. + +I've demonstrated this by setting it up so that if you type ^C to the +program it causes an interrupt to occur that resets the program back +to the start. This is done in a slightly tricky way to accomodate the +Cortex M architecture. When you are interrupted in the Cortex M, the +proccessor goes into "Handler" mode, saves information on the stack, +and takes the interrupt. Part of this involves saving a special value +on the stack in the LR register. The useful thing is that it means you +can write interrupt service routines as regular C code, the not so useful +thing is that if you don't return from that stack, the processor gets +confused about what state it should be in. So to avoid confusing the +processor, the interrupt routine changes where the code will return, then +does the return. This pops the processor out of Handler mode and back into +Thread mode, at which point the C code can do a longjump. + +In the future, the C library may be able to note that the processor needs +to change state for you, and save the special gymnastics here, but in the +mean time this works and will continue to work in the future. + diff --git a/examples/stm32/f4/stm32f4-disco/usart-irq/clock.c b/examples/stm32/f4/stm32f4-disco/usart-irq/clock.c new file mode 100644 index 0000000..61f0f82 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/usart-irq/clock.c @@ -0,0 +1,70 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2013 Chuck McManis + * + * 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 . + */ + +/* + * Now this is just the clock setup code from systick-blink as it is the + * transferrable part. + */ + +#include +#include +#include + +/* Common function descriptions */ +#include "clock.h" + +/* milliseconds since boot */ +static volatile uint32_t system_millis; + +/* Called when systick fires */ +void sys_tick_handler(void) { + system_millis++; +} + +/* simple sleep for delay milliseconds */ +void msleep(uint32_t delay) { + uint32_t wake = system_millis + delay; + while (wake > system_millis) ; +} + +/* Getter function for the current time */ +uint32_t mtime(void) { + return system_millis; +} + +/* + * clock_setup(void) + * + * This function sets up both the base board clock rate + * and a 1khz "system tick" count. The SYSTICK counter is + * a standard feature of the Cortex-M series. + */ +void clock_setup(void) +{ + /* Base board frequency, set to 168Mhz */ + rcc_clock_setup_hse_3v3(&hse_8mhz_3v3[CLOCK_3V3_168MHZ]); + + /* clock rate / 168000 to get 1mS interrupt rate */ + systick_set_reload(168000); + systick_set_clocksource(STK_CSR_CLKSOURCE_AHB); + systick_counter_enable(); + + /* this done last */ + systick_interrupt_enable(); +} diff --git a/examples/stm32/f4/stm32f4-disco/usart-irq/clock.h b/examples/stm32/f4/stm32f4-disco/usart-irq/clock.h new file mode 100644 index 0000000..c67363a --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/usart-irq/clock.h @@ -0,0 +1,16 @@ +/* vim: set noexpandtab ts=4 : + * + * This include file describes the functions exported by clock.c + */ +#ifndef __CLOCK_H +#define __CLOCK_H + +/* + * Definitions for functions being abstracted out + */ +void msleep(uint32_t); +uint32_t mtime(void); +void clock_setup(void); + +#endif /* generic header protector */ + diff --git a/examples/stm32/f4/stm32f4-disco/usart-irq/usart-irq.c b/examples/stm32/f4/stm32f4-disco/usart-irq/usart-irq.c new file mode 100644 index 0000000..b46c808 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/usart-irq/usart-irq.c @@ -0,0 +1,341 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2013 Chuck McManis + * + * 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 . + */ + +/* + * USART interrupt driven example + * + * This version then adds in interrupts, which is really handy for + * the receive function as it is impossible to predict when someone + * might type a character, further you can create a "character based + * reset" capability if you choose to. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "clock.h" + + +/* + * Some definitions of our console "functions" attached to the + * USART. + * + * These define sort of the minimum "library" of functions which + * we can use on a serial port. If you wish to use a different + * USART there are several things to change: + * - CONSOLE_UART change this + * - Change the peripheral enable clock + * - add usartx_isr for interrupts + * - nvic_enable_interrupt(your choice of USART/UART) + * - GPIO pins for transmit/receive + * (may be on different alternate functions as well) + */ + + +#define CONSOLE_UART USART2 + +void console_putc(char c); +char console_getc(int wait); +void console_puts(char *s); +int console_gets(char *s, int len); + +/* this is for fun, if you type ^C to this example it will reset */ +#define RESET_ON_CTRLC + +#ifdef RESET_ON_CTRLC + +/* Jump buffer for setjmp/longjmp */ +jmp_buf jump_buf; + +static void do_the_nasty(void); +/* + * do_the_nasty + * + * This is a hack to implement the equivalent of a signal interrupt + * in a system without signals or a kernel or scheduler. Basically + * when the console_getc() function reads a '^C' character, it munges + * the return address of the interrupt to here, and then this function + * does a longjump to the last place we did a setjmp. + */ +static void do_the_nasty(void) { + longjmp(jump_buf, 1); + while(1) ; +} +#endif + +/* This is a ring buffer to holding characters as they are typed + * it maintains both the place to put the next character received + * from the UART, and the place where the last character was + * read by the program. See the README file for a discussion of + * the failure semantics. + */ +#define RECV_BUF_SIZE 128 // Arbitrary buffer size +char recv_buf[RECV_BUF_SIZE]; +volatile int recv_ndx_nxt; // Next place to store +volatile int recv_ndx_cur; // Next place to read + +/* For interrupt handling we add a new function which is called + * when recieve interrupts happen. The name (usart2_isr) is created + * by the irq.json file in libopencm3 calling this interrupt for + * USART2 'usart2', adding the suffix '_isr', and then weakly binding + * it to the 'do nothing' interrupt function in vec.c. + * + * By defining it in this file the linker will override that weak + * binding and instead bind it here, but you have to get the name + * right or it won't work. And you'll wonder where your interrupts + * are going. + */ +void usart2_isr(void) { + uint32_t reg; + int i; + + do { + reg = USART_SR(CONSOLE_UART); + if (reg & USART_SR_RXNE) { + recv_buf[recv_ndx_nxt] = USART_DR(CONSOLE_UART); +#ifdef RESET_ON_CTRLC + /* Check for "reset" */ + if (recv_buf[recv_ndx_nxt] == '\003') { + /* reset the system + * volatile definition of return address on the stack + * to insure it gets stored, changed to point to + * the trampoline function (do_the_nasty) which is + * required because we need to return of an interrupt + * to get the internal value of the LR register reset + * and put the processor back into "Thread" mode from + * "Handler" mode. + * + * See the PM0214 Programming Manual for Cortex M, + * pg 42, to see the format of the Cortex M4 stack after + * an interrupt or exception has occurred. + */ + volatile uint32_t *ret = (®) + 7; + + *ret = (uint32_t) &do_the_nasty; + return; + } +#endif + /* Check for "overrun" */ + i = (recv_ndx_nxt + 1) % RECV_BUF_SIZE; + if (i != recv_ndx_cur) { + recv_ndx_nxt = i; + } + } + } while ((reg & USART_SR_RXNE) != 0); // can read back-to-back interrupts +} + +/* + * console_putc(char c) + * + * Send the character 'c' to the USART, wait for the USART + * transmit buffer to be empty first. + */ +void console_putc(char c) { + uint32_t reg; + do { + reg = USART_SR(CONSOLE_UART); + } while ((reg & USART_SR_TXE) == 0); + USART_DR(CONSOLE_UART) = (uint16_t) c & 0xff; +} + +/* + * char = console_getc(int wait) + * + * Check the console for a character. If the wait flag is + * non-zero. Continue checking until a character is received + * otherwise return 0 if called and no character was available. + * + * The implementation is a bit different however, now it looks + * in the ring buffer to see if a character has arrived. + */ +char console_getc(int wait) { + char c = 0; + + while ((wait != 0) && (recv_ndx_cur == recv_ndx_nxt)) ; + if (recv_ndx_cur != recv_ndx_nxt) { + c = recv_buf[recv_ndx_cur]; + recv_ndx_cur = (recv_ndx_cur + 1) % RECV_BUF_SIZE; + } + return c; +} + +/* + * void console_puts(char *s) + * + * Send a string to the console, one character at a time, return + * after the last character, as indicated by a NUL character, is + * reached. + */ +void console_puts(char *s) { + while (*s != '\000') { + console_putc(*s); + /* Add in a carraige return, after sending line feed */ + if (*s == '\n') { + console_putc('\r'); + } + s++; + } +} + +/* + * int console_gets(char *s, int len) + * + * Wait for a string to be entered on the console, limited + * support for editing characters (back space and delete) + * end when a character is received. + */ +int console_gets(char *s, int len) { + char *t = s; + char c; + + *t = '\000'; + /* read until a is received */ + while ((c = console_getc(1)) != '\r') { + if ((c == '\010') || (c == '\127')) { + if (t > s) { + /* send ^H ^H to erase previous character */ + console_puts("\010 \010"); + t--; + } + } else { + *t = c; + console_putc(c); + if ((t - s) < len) { + t++; + } + } + /* update end of string with NUL */ + *t = '\000'; + } + return (t - s); +} + +void countdown(void); + +/* + * countdown + * + * Count down for 20 seconds to 0. + * + * This provides an example function which is constantly + * printing for 20 seconds and not looking for typed characters. + * however with the interrupt driven receieve queue you can type + * ^C while it is counting down and it will be interrupted. + */ +void countdown(void) { + int i = 200; + while (i-- > 0) { + console_puts("Countdown: "); + console_putc( (i / 600) + '0'); + console_putc(':'); + console_putc( ((i % 600) / 100) + '0'); + console_putc( (((i % 600) / 10) % 10) + '0'); + console_putc('.'); + console_putc( ((i % 600) % 10) + '0'); + console_putc('\r'); + msleep(100); + } +} + +/* + * Set up the GPIO subsystem with an "Alternate Function" + * on some of the pins, in this case connected to a + * USART. + */ +int main(void) { + char buf[128]; + int len; + bool pmask; + + clock_setup(); // initialize our clock + + /* MUST enable the GPIO clock in ADDITION to the USART clock */ + rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPDEN); + + /* This example uses PD5 and PD6 for Tx and Rx respectively + * but other pins are available for this role on USART2 (our chosen + * USART) as well, such as PA2 and PA3. You can also split them + * so PA2 for Tx, PD6 for Rx but you would have to enable both + * the GPIOA and GPIOD clocks in that case + */ + gpio_mode_setup(GPIOD, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO5 | GPIO6); + + /* Actual Alternate function number (in this case 7) is part + * depenedent, CHECK THE DATA SHEET for the right number to + * use. + */ + gpio_set_af(GPIOD, GPIO_AF7, GPIO5 | GPIO6); + + + /* This then enables the clock to the USART2 peripheral which is + * attached inside the chip to the APB2 bus. Different peripherals + * attach to different buses, and even some UARTS are attached to + * APB1 and some to APB2, again the data sheet is useful here. + */ + rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USART2EN); + + /* Set up USART/UART parameters using the libopencm3 helper functions */ + usart_set_baudrate(CONSOLE_UART, 115200); + usart_set_databits(CONSOLE_UART, 8); + usart_set_stopbits(CONSOLE_UART, USART_STOPBITS_1); + usart_set_mode(CONSOLE_UART, USART_MODE_TX_RX); + usart_set_parity(CONSOLE_UART, USART_PARITY_NONE); + usart_set_flow_control(CONSOLE_UART, USART_FLOWCONTROL_NONE); + usart_enable(CONSOLE_UART); + + /* Enable interrupts from the USART */ + nvic_enable_irq(NVIC_USART2_IRQ); + + /* Specifically enable recieve interrupts */ + usart_enable_rx_interrupt(CONSOLE_UART); + + /* At this point our console is ready to go so we can create our + * simple application to run on it. + */ + console_puts("\nUART Demonstration Application\n"); +#ifdef RESET_ON_CTRLC + console_puts("Press ^C at any time to reset system.\n"); + pmask = cm_mask_interrupts(0); + cm_mask_interrupts(pmask); + if (setjmp(jump_buf)) { + console_puts("\nInterrupt received! Restarting from the top\n"); + } +#endif + while (1) { + console_puts("Enter a string: "); + len = console_gets(buf, 128); + if (len) { + if (buf[0] == 'c') { + console_puts("\n"); + countdown(); // long running thing (20 seconds) + } + console_puts("\nYou entered : '"); + console_puts(buf); + console_puts("'\n"); + } else { + console_puts("\nNo string entered\n"); + } + } +} diff --git a/examples/stm32/f4/stm32f4-disco/usart/Makefile b/examples/stm32/f4/stm32f4-disco/usart/Makefile new file mode 100644 index 0000000..dd15e7e --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/usart/Makefile @@ -0,0 +1,25 @@ +# +# This file is part of the libopencm3 project. +# +# 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 . +# + +OBJS = clock.o + +BINARY = usart + +LDSCRIPT = ../stm32f4-disco.ld + +include ../../Makefile.include + diff --git a/examples/stm32/f4/stm32f4-disco/usart/README b/examples/stm32/f4/stm32f4-disco/usart/README new file mode 100644 index 0000000..f260127 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/usart/README @@ -0,0 +1,9 @@ +Simple USART Example +-------------------- + +This example sets up a USART port and provides a few simple +character handling functions to that a short interactive program +can be demonstrated. It re-uses the clock setup from systick_blink +and that means you could do times delays but that aspect isn't used. + +After this example we do character handling with interrupts. diff --git a/examples/stm32/f4/stm32f4-disco/usart/clock.c b/examples/stm32/f4/stm32f4-disco/usart/clock.c new file mode 100644 index 0000000..36a1d5b --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/usart/clock.c @@ -0,0 +1,71 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2013 Chuck McManis + * + * 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 . + */ + +/* + * Now this is just the clock setup code from systick-blink as it is the + * transferrable part. + */ + +#include +#include +#include +#include + +/* Common function descriptions */ +#include "clock.h" + +/* milliseconds since boot */ +static volatile uint32_t system_millis; + +/* Called when systick fires */ +void sys_tick_handler(void) { + system_millis++; +} + +/* simple sleep for delay milliseconds */ +void msleep(uint32_t delay) { + uint32_t wake = system_millis + delay; + while (wake > system_millis) ; +} + +/* Getter function for the current time */ +uint32_t mtime(void) { + return system_millis; +} + +/* + * clock_setup(void) + * + * This function sets up both the base board clock rate + * and a 1khz "system tick" count. The SYSTICK counter is + * a standard feature of the Cortex-M series. + */ +void clock_setup(void) +{ + /* Base board frequency, set to 168Mhz */ + rcc_clock_setup_hse_3v3(&hse_8mhz_3v3[CLOCK_3V3_168MHZ]); + + /* clock rate / 168000 to get 1mS interrupt rate */ + systick_set_reload(168000); + systick_set_clocksource(STK_CSR_CLKSOURCE_AHB); + systick_counter_enable(); + + /* this done last */ + systick_interrupt_enable(); +} diff --git a/examples/stm32/f4/stm32f4-disco/usart/clock.h b/examples/stm32/f4/stm32f4-disco/usart/clock.h new file mode 100644 index 0000000..01c14ac --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/usart/clock.h @@ -0,0 +1,15 @@ +/* + * This include file describes the functions exported by clock.c + */ +#ifndef __CLOCK_H +#define __CLOCK_H + +/* + * Definitions for functions being abstracted out + */ +void msleep(uint32_t); +uint32_t mtime(void); +void clock_setup(void); + +#endif /* generic header protector */ + diff --git a/examples/stm32/f4/stm32f4-disco/usart/usart.c b/examples/stm32/f4/stm32f4-disco/usart/usart.c new file mode 100644 index 0000000..03bbfb9 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/usart/usart.c @@ -0,0 +1,184 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2013 Chuck McManis + * + * 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 . + */ + +/* + * USART example (alternate console) + */ + +#include +#include +#include +#include +#include "clock.h" + +/* + * Some definitions of our console "functions" attached to the + * USART. + * + * These define sort of the minimum "library" of functions which + * we can use on a serial port. + */ + +#define CONSOLE_UART USART2 + +void console_putc(char c); +char console_getc(int wait); +void console_puts(char *s); +int console_gets(char *s, int len); + +/* + * console_putc(char c) + * + * Send the character 'c' to the USART, wait for the USART + * transmit buffer to be empty first. + */ +void console_putc(char c) { + uint32_t reg; + do { + reg = USART_SR(CONSOLE_UART); + } while ((reg & USART_SR_TXE) == 0); + USART_DR(CONSOLE_UART) = (uint16_t) c & 0xff; +} + +/* + * char = console_getc(int wait) + * + * Check the console for a character. If the wait flag is + * non-zero. Continue checking until a character is received + * otherwise return 0 if called and no character was available. + */ +char console_getc(int wait) { + uint32_t reg; + do { + reg = USART_SR(CONSOLE_UART); + } while ((wait != 0) && ((reg & USART_SR_RXNE) == 0)); + return (reg & USART_SR_RXNE) ? USART_DR(CONSOLE_UART) : '\000'; +} + +/* + * void console_puts(char *s) + * + * Send a string to the console, one character at a time, return + * after the last character, as indicated by a NUL character, is + * reached. + */ +void console_puts(char *s) { + while (*s != '\000') { + console_putc(*s); + /* Add in a carraige return, after sending line feed */ + if (*s == '\n') { + console_putc('\r'); + } + s++; + } +} + +/* + * int console_gets(char *s, int len) + * + * Wait for a string to be entered on the console, limited + * support for editing characters (back space and delete) + * end when a character is received. + */ +int console_gets(char *s, int len) { + char *t = s; + char c; + + *t = '\000'; + /* read until a is received */ + while ((c = console_getc(1)) != '\r') { + if ((c == '\010') || (c == '\127')) { + if (t > s) { + /* send ^H ^H to erase previous character */ + console_puts("\010 \010"); + t--; + } + } else { + *t = c; + console_putc(c); + if ((t - s) < len) { + t++; + } + } + /* update end of string with NUL */ + *t = '\000'; + } + return (t - s); +} + +/* + * Set up the GPIO subsystem with an "Alternate Function" + * on some of the pins, in this case connected to a + * USART. + */ +int main(void) { + char buf[128]; + int len; + + clock_setup(); // initialize our clock + + /* MUST enable the GPIO clock in ADDITION to the USART clock */ + rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPDEN); + + /* This example uses PD5 and PD6 for Tx and Rx respectively + * but other pins are available for this role on USART2 (our chosen + * USART) as well, such as PA2 and PA3. You can also split them + * so PA2 for Tx, PD6 for Rx but you would have to enable both + * the GPIOA and GPIOD clocks in that case + */ + gpio_mode_setup(GPIOD, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO5 | GPIO6); + + /* Actual Alternate function number (in this case 7) is part + * depenedent, check the data sheet for the right number to + * use. + */ + gpio_set_af(GPIOD, GPIO_AF7, GPIO5 | GPIO6); + + /* This then enables the clock to the USART2 peripheral which is + * attached inside the chip to the APB2 bus. Different peripherals + * attach to different buses, and even some UARTS are attached to + * APB1 and some to APB2, again the data sheet is useful here. + */ + rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USART2EN); + + /* Set up USART/UART parameters using the libopencm3 helper functions */ + usart_set_baudrate(CONSOLE_UART, 115200); + usart_set_databits(CONSOLE_UART, 8); + usart_set_stopbits(CONSOLE_UART, USART_STOPBITS_1); + usart_set_mode(CONSOLE_UART, USART_MODE_TX_RX); + usart_set_parity(CONSOLE_UART, USART_PARITY_NONE); + usart_set_flow_control(CONSOLE_UART, USART_FLOWCONTROL_NONE); + usart_enable(CONSOLE_UART); + + /* At this point our console is ready to go so we can create our + * simple application to run on it. + */ + console_puts("\nUART Demonstration Application\n"); + while (1) { + console_puts("Enter a string: "); + len = console_gets(buf, 128); + if (len) { + console_puts("\nYou entered : '"); + console_puts(buf); + console_puts("'\n"); + } else { + console_puts("\nNo string entered\n"); + } + } +}