Some examples for the STM32F4-Disco board

This commit is contained in:
cmcmanis
2014-02-07 22:31:27 -08:00
committed by Piotr Esden-Tempski
parent 2583cc54cc
commit e5585dd07d
23 changed files with 1716 additions and 0 deletions

View File

@@ -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 <http://www.gnu.org/licenses/>.
#
BINARY = blink
LDSCRIPT = ../stm32f4-disco.ld
include ../../Makefile.include

View File

@@ -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.

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#include <libopencm3/stm32/f4/rcc.h>
#include <libopencm3/stm32/f4/gpio.h>
#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;
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
#
OBJS = console.o clock.o
BINARY = sdram
LDSCRIPT = ../stm32f4-disco.ld
include ../../Makefile.include

View File

@@ -0,0 +1,70 @@
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.
*/
/*
* Now this is just the clock setup code from systick-blink as it is the
* transferrable part.
*/
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/cm3/systick.h>
/* 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();
}

View File

@@ -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 */

View File

@@ -0,0 +1,234 @@
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.
*/
/*
* 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 <stdint.h>
#include <setjmp.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/iwdg.h>
#include <libopencm3/cm3/scb.h>
#include <libopencm3/cm3/cortex.h>
#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 = (&reg) + 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 <CR> character is received.
*/
int console_gets(char *s, int len) {
char *t = s;
char c;
*t = '\000';
/* read until a <CR> 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);
}

View File

@@ -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

View File

@@ -0,0 +1,338 @@
/*
* sdram.c - SDRAM controller example
*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/fsmc.h>
#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;
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
* Copyright (C) 2011 Stephen Caudle <scaudle@doceme.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 <http://www.gnu.org/licenses/>.
*/
/* 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

View File

@@ -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 <http://www.gnu.org/licenses/>.
#
BINARY = systick-blink
LDSCRIPT = ../stm32f4-disco.ld
include ../../Makefile.include

View File

@@ -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.

View File

@@ -0,0 +1,93 @@
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.
*/
/* This version derived from fancy blink */
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/cm3/systick.h>
/*
* 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;
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
#
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

View File

@@ -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.

View File

@@ -0,0 +1,70 @@
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.
*/
/*
* Now this is just the clock setup code from systick-blink as it is the
* transferrable part.
*/
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/cm3/systick.h>
/* 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();
}

View File

@@ -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 */

View File

@@ -0,0 +1,341 @@
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.
*/
/*
* 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 <stdint.h>
#include <setjmp.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/iwdg.h>
#include <libopencm3/cm3/scb.h>
#include <libopencm3/cm3/cortex.h>
#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 = (&reg) + 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 <CR> character is received.
*/
int console_gets(char *s, int len) {
char *t = s;
char c;
*t = '\000';
/* read until a <CR> 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");
}
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
#
OBJS = clock.o
BINARY = usart
LDSCRIPT = ../stm32f4-disco.ld
include ../../Makefile.include

View File

@@ -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.

View File

@@ -0,0 +1,71 @@
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.
*/
/*
* Now this is just the clock setup code from systick-blink as it is the
* transferrable part.
*/
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/cm3/systick.h>
/* 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();
}

View File

@@ -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 */

View File

@@ -0,0 +1,184 @@
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.
*/
/*
* USART example (alternate console)
*/
#include <stdint.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/usart.h>
#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 <CR> character is received.
*/
int console_gets(char *s, int len) {
char *t = s;
char c;
*t = '\000';
/* read until a <CR> 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");
}
}
}