diff --git a/examples/stm32/f4/stm32f4-disco/lcd-serial/Makefile b/examples/stm32/f4/stm32f4-disco/lcd-serial/Makefile new file mode 100644 index 0000000..f9e11c7 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/lcd-serial/Makefile @@ -0,0 +1,10 @@ +OBJS = sdram.o clock.o console.o lcd-spi.o gfx.o + +BINARY = lcd-serial + +# we use sin/cos from the library +LDLIBS += -lm + +LDSCRIPT = ../stm32f4-disco.ld + +include ../../Makefile.include diff --git a/examples/stm32/f4/stm32f4-disco/lcd-serial/README b/examples/stm32/f4/stm32f4-disco/lcd-serial/README new file mode 100644 index 0000000..358ecfd --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/lcd-serial/README @@ -0,0 +1,33 @@ +README lcd-serial +----------------- + +This example sets up the LCD display on the DISCO board +and shows some sample screens. It uses some graphics code +that was inspired by AdaFruit graphics library. Not that +a lot of it is used but once the display was "on" it was +something to use to put something other than straight +lines on it :-). + +It is a bit more complex because there are a lot of things +going on at the same time. First the display is connected +to the CPU via an SPI port. Second, the display is, like +most LCD displays, a fairly complex controller chip in itself +so it generally has a fairly complex initialization sequence. +And finally, once initialized, drawing something other than +straight lines involves a bit of bit fiddling. + +Once it is set up the initialization routine writes a pattern +of lines in the RAM used to hold a frame and puts it on the +LCD. + +Pressing a key will clear the screen and fill it with a box +that has a simple text message in a box and 3 circles along +the bottom. + +Pressing any key again, will bring up a display that says +"PLANETS!" and animates three planets orbiting a star (not +to scale :-) to give you a feel for the "speed" of animation +when you're dumping the entire screen through the SPI port +each time to update the display. The next example uses +the TFT interface of the chip to load the data into the +display. diff --git a/examples/stm32/f4/stm32f4-disco/lcd-serial/clock.c b/examples/stm32/f4/stm32f4-disco/lcd-serial/clock.c new file mode 100644 index 0000000..63150d5 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/lcd-serial/clock.c @@ -0,0 +1,70 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014 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/lcd-serial/clock.h b/examples/stm32/f4/stm32f4-disco/lcd-serial/clock.h new file mode 100644 index 0000000..01c14ac --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/lcd-serial/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/lcd-serial/console.c b/examples/stm32/f4/stm32f4-disco/lcd-serial/console.c new file mode 100644 index 0000000..a78ea99 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/lcd-serial/console.c @@ -0,0 +1,232 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014 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 . + */ + +/* + * Interrupt drive Console code (extracted from the usart-irq example) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "clock.h" +#include "console.h" + + +/* 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) &reset_handler; + 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); +} + +/* + * console_setup(int baudrate) + * + * Set the pins and clocks to create a console that we can + * use for serial messages and getting text from the user. + */ +void console_setup(int baud) { + + /* 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, baud); + 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/lcd-serial/console.h b/examples/stm32/f4/stm32f4-disco/lcd-serial/console.h new file mode 100644 index 0000000..09991f8 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/lcd-serial/console.h @@ -0,0 +1,54 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014 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 . + */ + +#ifndef __CONSOLE_H +#define __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. 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 + +/* + * 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(int baudrate); + +/* this is for fun, if you type ^C to this example it will reset */ +#define RESET_ON_CTRLC + +#endif diff --git a/examples/stm32/f4/stm32f4-disco/lcd-serial/font-7x12.c b/examples/stm32/f4/stm32f4-disco/lcd-serial/font-7x12.c new file mode 100644 index 0000000..b623ad7 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/lcd-serial/font-7x12.c @@ -0,0 +1,177 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014 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 . + */ + +#ifndef MCMFONT_INC +#define MCMFONT_INC + +/* Ascii 7 x 12 font + * + * This is a pretty generic 7 x 12 ASCII font that includes + * lower case descenders (indicated by (data & 0x80) != 0 + * on the first data byte of the character. Each row + * represents 9 "lines" of the font, the bits in the byte + * represent columns. When a glyph descends below the base + * line it is rendered 3 pixels lower (hence the height of + * 12 pixels rather than 9 even though the data is only 9 + * rows tall) + */ + +#define FONT_CHAR_WIDTH 7 +#define FONT_CHAR_HEIGHT 12 + +static const unsigned char mcm_font[] = { + 0x00, 0x00, 0x00, 0x00, 0x31, 0x4A, 0x44, 0x4A, 0x31, // 0 + 0xBC, 0x22, 0x3B, 0x22, 0x22, 0x3b, 0x20, 0x20, 0x40, // 1 + 0x80, 0x61, 0x12, 0x14, 0x18, 0x10, 0x30, 0x30, 0x30, // 2 + 0x30, 0x48, 0x40, 0x40, 0x20, 0x30, 0x48, 0x48, 0x30, // 3 + 0x00, 0x00, 0x18, 0x20, 0x40, 0x78, 0x40, 0x20, 0x30, // 4 + 0x16, 0x0e, 0x10, 0x20, 0x40, 0x40, 0x38, 0x04, 0x18, // 5 + 0x80, 0x2c, 0x52, 0x12, 0x12, 0x12, 0x02, 0x02, 0x02, // 6 + 0x18, 0x24, 0x42, 0x42, 0x3e, 0x42, 0x42, 0x24, 0x18, // 7 + 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x48, 0x30, // 8 + 0x00, 0x00, 0x40, 0x48, 0x50, 0x60, 0x50, 0x4A, 0x44, // 9 + 0x40, 0x20, 0x10, 0x10, 0x10, 0x10, 0x18, 0x24, 0x42, // 10 + 0x80, 0x48, 0x48, 0x48, 0x48, 0x74, 0x40, 0x40, 0x40, // 11 + 0x00, 0x00, 0x00, 0x62, 0x22, 0x24, 0x28, 0x30, 0x20, // 12 + 0x08, 0x1c, 0x20, 0x18, 0x20, 0x40, 0x3c, 0x02, 0x0c, // 13 + 0x00, 0x00, 0x00, 0x18, 0x24, 0x42, 0x42, 0x24, 0x18, // 14 + 0x00, 0x00, 0x00, 0x3f, 0x54, 0x24, 0x24, 0x24, 0x24, // 15 + + 0x98, 0x24, 0x42, 0x42, 0x64, 0x58, 0x40, 0x40, 0x40, // 16 + 0x00, 0x00, 0x00, 0x1f, 0x24, 0x42, 0x42, 0x24, 0x18, // 17 + 0x00, 0x00, 0x00, 0x3f, 0x48, 0x08, 0x08, 0x08, 0x08, // 18 + 0x00, 0x00, 0x00, 0x62, 0x24, 0x24, 0x24, 0x24, 0x18, // 19 + 0x10, 0x10, 0x38, 0x54, 0x54, 0x54, 0x38, 0x10, 0x10, // 20 + 0x00, 0x00, 0x00, 0x00, 0x62, 0x14, 0x08, 0x14, 0x23, // 21 + 0x80, 0x49, 0x2a, 0x2a, 0x2a, 0x1c, 0x08, 0x08, 0x08, // 22 + 0x00, 0x00, 0x00, 0x22, 0x41, 0x49, 0x49, 0x49, 0x36, // 23 + 0x00, 0x1c, 0x22, 0x41, 0x41, 0x41, 0x22, 0x22, 0x63, // 24 + 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x50, 0x30, 0x10, // 25 + 0x00, 0x00, 0x04, 0x02, 0x7f, 0x02, 0x04, 0x00, 0x00, // 26 + 0x00, 0x00, 0x10, 0x20, 0x7f, 0x20, 0x10, 0x00, 0x00, // 27 + 0x08, 0x1c, 0x2a, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // 28 + 0x00, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x08, 0x00, 0x00, // 29 + 0x7f, 0x20, 0x10, 0x08, 0x06, 0x08, 0x10, 0x20, 0x7f, // 30 + 0x00, 0x30, 0x45, 0x06, 0x30, 0x45, 0x06, 0x00, 0x00, // 31 + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 32 + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x08, // 33 + 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 34 + 0x14, 0x14, 0x14, 0x7f, 0x14, 0x7f, 0x14, 0x14, 0x14, // 35 + 0x08, 0x3f, 0x48, 0x48, 0x3e, 0x09, 0x09, 0x7e, 0x08, // 36 + 0x20, 0x51, 0x22, 0x04, 0x08, 0x10, 0x22, 0x45, 0x02, // 37 + 0x38, 0x44, 0x44, 0x28, 0x10, 0x29, 0x46, 0x46, 0x39, // 38 + 0x20, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 39 + 0x08, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x10, 0x08, // 40 + 0x08, 0x04, 0x02, 0x02, 0x02, 0x02, 0x02, 0x04, 0x08, // 41 + 0x80, 0x49, 0x2a, 0x1c, 0x7f, 0x1c, 0x2a, 0x49, 0x80, // 42 + 0x00, 0x80, 0x80, 0x80, 0x7e, 0x80, 0x80, 0x80, 0x00, // 43 + 0x80, 0x00, 0x00, 0x00, 0x00, 0x30, 0x10, 0x10, 0x20, // 44 + 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, // 45 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, // 46 + 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00, // 47 + + 0x3e, 0x41, 0x43, 0x45, 0x49, 0x51, 0x61, 0x41, 0x3e, // 48 + 0x08, 0x18, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, // 49 + 0x3e, 0x41, 0x01, 0x02, 0x0c, 0x10, 0x20, 0x40, 0x7f, // 50 + 0x3e, 0x41, 0x01, 0x01, 0x1e, 0x01, 0x01, 0x41, 0x3e, // 51 + 0x02, 0x06, 0x0a, 0x12, 0x22, 0x7f, 0x02, 0x02, 0x02, // 52 + 0x7f, 0x40, 0x40, 0x40, 0x7e, 0x01, 0x01, 0x41, 0x3e, // 53 + 0x3e, 0x41, 0x40, 0x40, 0x7e, 0x41, 0x41, 0x41, 0x3e, // 54 + 0x7f, 0x41, 0x02, 0x04, 0x08, 0x10, 0x10, 0x10, 0x10, // 55 + 0x3e, 0x41, 0x41, 0x41, 0x3e, 0x41, 0x41, 0x41, 0x3e, // 56 + 0x3f, 0x41, 0x41, 0x41, 0x3f, 0x01, 0x01, 0x41, 0x3e, // 57 + 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, // 58 + 0xa0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x10, 0x10, 0x20, // 59 + 0x04, 0x08, 0x10, 0x20, 0x40, 0x20, 0x10, 0x08, 0x04, // 60 + 0x00, 0x00, 0x00, 0x7f, 0x00, 0x7f, 0x00, 0x00, 0x00, // 61 + 0x10, 0x08, 0x04, 0x02, 0x01, 0x02, 0x04, 0x08, 0x10, // 62 + 0x3e, 0x41, 0x41, 0x02, 0x04, 0x08, 0x08, 0x00, 0x08, // 63 + + 0x3e, 0x41, 0x41, 0x1d, 0x55, 0x5e, 0x40, 0x40, 0x3e, // 64 + 0x1c, 0x22, 0x41, 0x41, 0x7f, 0x41, 0x41, 0x41, 0x41, // 65 + 0x7e, 0x21, 0x21, 0x21, 0x3e, 0x21, 0x21, 0x21, 0x7e, // 66 + 0x1e, 0x21, 0x40, 0x40, 0x40, 0x40, 0x40, 0x21, 0x1e, // 67 + 0x7e, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x7e, // 68 + 0x7f, 0x40, 0x40, 0x40, 0x78, 0x40, 0x40, 0x40, 0x7f, // 69 + 0x7f, 0x40, 0x40, 0x40, 0x78, 0x40, 0x40, 0x40, 0x40, // 70 + 0x1e, 0x21, 0x40, 0x40, 0x40, 0x47, 0x41, 0x21, 0x1e, // 71 + 0x41, 0x41, 0x41, 0x41, 0x7f, 0x41, 0x41, 0x41, 0x41, // 72 + 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, // 73 + 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x44, 0x38, // 74 + 0x41, 0x42, 0x44, 0x48, 0x70, 0x48, 0x44, 0x42, 0x41, // 75 + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7f, // 76 + 0x41, 0x63, 0x55, 0x49, 0x49, 0x41, 0x41, 0x41, 0x41, // 77 + 0x41, 0x61, 0x51, 0x49, 0x45, 0x43, 0x41, 0x41, 0x41, // 78 + 0x1c, 0x22, 0x41, 0x41, 0x41, 0x41, 0x41, 0x22, 0x1c, // 79 + + 0x7e, 0x41, 0x41, 0x41, 0x7e, 0x40, 0x40, 0x40, 0x40, // 80 + 0x1c, 0x22, 0x41, 0x41, 0x41, 0x41, 0x45, 0x22, 0x1d, // 81 + 0x7e, 0x41, 0x41, 0x41, 0x7e, 0x48, 0x44, 0x42, 0x41, // 82 + 0x3e, 0x41, 0x40, 0x40, 0x3e, 0x01, 0x01, 0x41, 0x3e, // 83 + 0x7f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, // 84 + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x3e, // 85 + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x22, 0x14, 0x08, // 86 + 0x41, 0x41, 0x41, 0x49, 0x49, 0x49, 0x55, 0x63, 0x41, // 87 + 0x41, 0x41, 0x22, 0x14, 0x08, 0x14, 0x22, 0x41, 0x41, // 88 + 0x41, 0x41, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, // 89 + 0x7f, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x7f, // 90 + 0x1e, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1e, // 91 + 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00, // 92 + 0x3c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x3c, // 93 + 0x3e, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 94 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, // 95 + + 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 96 + 0x00, 0x00, 0x00, 0x1e, 0x22, 0x42, 0x41, 0x46, 0x3a, // 97 + 0x40, 0x40, 0x40, 0x5c, 0x62, 0x42, 0x42, 0x62, 0x5c, // 98 + 0x00, 0x00, 0x00, 0x3c, 0x42, 0x40, 0x40, 0x42, 0x3c, // 99 + 0x02, 0x02, 0x02, 0x3a, 0x46, 0x42, 0x42, 0x46, 0x3a, // 100 + 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x7e, 0x40, 0x3e, // 101 + 0x0c, 0x12, 0x10, 0x10, 0x7c, 0x10, 0x10, 0x10, 0x10, // 102 + 0xba, 0x46, 0x42, 0x42, 0x46, 0x3a, 0x02, 0x42, 0x3c, // 103 + 0x40, 0x40, 0x40, 0x58, 0x64, 0x42, 0x42, 0x42, 0x42, // 104 + 0x00, 0x08, 0x00, 0x18, 0x08, 0x08, 0x08, 0x08, 0x08, // 105 + 0x82, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x42, 0x3c, // 106 + 0x40, 0x40, 0x40, 0x44, 0x48, 0x70, 0x48, 0x44, 0x42, // 107 + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, // 108 + 0x00, 0x00, 0x00, 0x76, 0x49, 0x49, 0x49, 0x49, 0x49, // 109 + 0x00, 0x00, 0x00, 0x5c, 0x62, 0x42, 0x42, 0x42, 0x42, // 110 + 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x3c, // 111 + + 0xdc, 0x62, 0x42, 0x42, 0x62, 0x5c, 0x40, 0x40, 0x40, // 112 + 0xba, 0x46, 0x42, 0x42, 0x46, 0x3a, 0x02, 0x02, 0x02, // 113 + 0x00, 0x00, 0x00, 0x5c, 0x62, 0x40, 0x40, 0x40, 0x40, // 114 + 0x00, 0x00, 0x00, 0x3c, 0x42, 0x30, 0x0c, 0x42, 0x3c, // 115 + 0x00, 0x10, 0x10, 0x7c, 0x10, 0x01, 0x10, 0x12, 0x0c, // 116 + 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, // 117 + 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x28, 0x10, // 118 + 0x00, 0x00, 0x00, 0x41, 0x49, 0x49, 0x49, 0x49, 0x36, // 119 + 0x00, 0x00, 0x00, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, // 120 + 0xc2, 0x42, 0x42, 0x42, 0x46, 0x3a, 0x02, 0x42, 0x3c, // 121 + 0x00, 0x00, 0x00, 0x7e, 0x04, 0x08, 0x10, 0x20, 0x7e, // 122 + 0x0e, 0x10, 0x10, 0x10, 0x20, 0x10, 0x10, 0x10, 0x0e, // 123 + 0x10, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x10, 0x00, // 124 + 0x38, 0x04, 0x04, 0x04, 0x02, 0x04, 0x04, 0x04, 0x38, // 125 + 0x30, 0x49, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 126 + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f // 127 +}; + +#endif + diff --git a/examples/stm32/f4/stm32f4-disco/lcd-serial/gfx.c b/examples/stm32/f4/stm32f4-disco/lcd-serial/gfx.c new file mode 100644 index 0000000..283287b --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/lcd-serial/gfx.c @@ -0,0 +1,501 @@ +/* +This is the core graphics library for all our displays, providing a common +set of graphics primitives (points, lines, circles, etc.). It needs to be +paired with a hardware-specific library for each display device we carry +(to handle the lower-level functions). + +Adafruit invests time and resources providing this open source code, please +support Adafruit & open-source hardware by purchasing products from Adafruit! + +Copyright (c) 2013 Adafruit Industries. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +* +* Modified the AdaFruit library to be a C library, changed the font and +* generally munged it in a variety of ways, creating a reasonably quick +* and dirty way to put something "interesting" on the LCD display. +* --Chuck McManis (2013, 2014) +* +*/ + +#include +#include +#include +#include "gfx.h" +#include "font-7x12.c" + +#define pgm_read_byte(addr) (*(const unsigned char *)(addr)) + +struct gfx_state __gfx_state; + +void +gfx_drawPixel(int x, int y, uint16_t color) { + if ((x < 0) || (x >= __gfx_state._width) || + (y < 0) || (y >= __gfx_state._height)) { + return; // off screen so don't draw it + } + (__gfx_state.drawpixel)(x, y, color); +} +#define true 1 + +void +gfx_init(void (*pixel_func)(int, int, uint16_t), int width, int height) +{ + __gfx_state._width = width; + __gfx_state._height = height; + __gfx_state.rotation = 0; + __gfx_state.cursor_y = __gfx_state.cursor_x = 0; + __gfx_state.textsize = 1; + __gfx_state.textcolor = 0; + __gfx_state.textbgcolor = 0xFFFF; + __gfx_state.wrap = true; + __gfx_state.drawpixel = pixel_func; +} + +// Draw a circle outline +void gfx_drawCircle(int16_t x0, int16_t y0, int16_t r, + uint16_t color) { + int16_t f = 1 - r; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * r; + int16_t x = 0; + int16_t y = r; + + gfx_drawPixel(x0 , y0+r, color); + gfx_drawPixel(x0 , y0-r, color); + gfx_drawPixel(x0+r, y0 , color); + gfx_drawPixel(x0-r, y0 , color); + + while (x= 0) { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + + gfx_drawPixel(x0 + x, y0 + y, color); + gfx_drawPixel(x0 - x, y0 + y, color); + gfx_drawPixel(x0 + x, y0 - y, color); + gfx_drawPixel(x0 - x, y0 - y, color); + gfx_drawPixel(x0 + y, y0 + x, color); + gfx_drawPixel(x0 - y, y0 + x, color); + gfx_drawPixel(x0 + y, y0 - x, color); + gfx_drawPixel(x0 - y, y0 - x, color); + } +} + +void gfx_drawCircleHelper( int16_t x0, int16_t y0, + int16_t r, uint8_t cornername, uint16_t color) { + int16_t f = 1 - r; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * r; + int16_t x = 0; + int16_t y = r; + + while (x= 0) { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + if (cornername & 0x4) { + gfx_drawPixel(x0 + x, y0 + y, color); + gfx_drawPixel(x0 + y, y0 + x, color); + } + if (cornername & 0x2) { + gfx_drawPixel(x0 + x, y0 - y, color); + gfx_drawPixel(x0 + y, y0 - x, color); + } + if (cornername & 0x8) { + gfx_drawPixel(x0 - y, y0 + x, color); + gfx_drawPixel(x0 - x, y0 + y, color); + } + if (cornername & 0x1) { + gfx_drawPixel(x0 - y, y0 - x, color); + gfx_drawPixel(x0 - x, y0 - y, color); + } + } +} + +void gfx_fillCircle(int16_t x0, int16_t y0, int16_t r, + uint16_t color) { + gfx_drawFastVLine(x0, y0-r, 2*r+1, color); + gfx_fillCircleHelper(x0, y0, r, 3, 0, color); +} + +// Used to do circles and roundrects +void gfx_fillCircleHelper(int16_t x0, int16_t y0, int16_t r, + uint8_t cornername, int16_t delta, uint16_t color) { + + int16_t f = 1 - r; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * r; + int16_t x = 0; + int16_t y = r; + + while (x= 0) { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + + if (cornername & 0x1) { + gfx_drawFastVLine(x0+x, y0-y, 2*y+1+delta, color); + gfx_drawFastVLine(x0+y, y0-x, 2*x+1+delta, color); + } + if (cornername & 0x2) { + gfx_drawFastVLine(x0-x, y0-y, 2*y+1+delta, color); + gfx_drawFastVLine(x0-y, y0-x, 2*x+1+delta, color); + } + } +} + +// Bresenham's algorithm - thx wikpedia +void gfx_drawLine(int16_t x0, int16_t y0, + int16_t x1, int16_t y1, + uint16_t color) { + int16_t steep = abs(y1 - y0) > abs(x1 - x0); + if (steep) { + swap(x0, y0); + swap(x1, y1); + } + + if (x0 > x1) { + swap(x0, x1); + swap(y0, y1); + } + + int16_t dx, dy; + dx = x1 - x0; + dy = abs(y1 - y0); + + int16_t err = dx / 2; + int16_t ystep; + + if (y0 < y1) { + ystep = 1; + } else { + ystep = -1; + } + + for (; x0<=x1; x0++) { + if (steep) { + gfx_drawPixel(y0, x0, color); + } else { + gfx_drawPixel(x0, y0, color); + } + err -= dy; + if (err < 0) { + y0 += ystep; + err += dx; + } + } +} + +// Draw a rectangle +void gfx_drawRect(int16_t x, int16_t y, + int16_t w, int16_t h, + uint16_t color) { + gfx_drawFastHLine(x, y, w, color); + gfx_drawFastHLine(x, y+h-1, w, color); + gfx_drawFastVLine(x, y, h, color); + gfx_drawFastVLine(x+w-1, y, h, color); +} + +void gfx_drawFastVLine(int16_t x, int16_t y, + int16_t h, uint16_t color) { + // Update in subclasses if desired! + gfx_drawLine(x, y, x, y+h-1, color); +} + +void gfx_drawFastHLine(int16_t x, int16_t y, + int16_t w, uint16_t color) { + // Update in subclasses if desired! + gfx_drawLine(x, y, x+w-1, y, color); +} + +void gfx_fillRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color) { + // Update in subclasses if desired! + int16_t i; + for (i=x; i= y1 >= y0) + if (y0 > y1) { + swap(y0, y1); swap(x0, x1); + } + if (y1 > y2) { + swap(y2, y1); swap(x2, x1); + } + if (y0 > y1) { + swap(y0, y1); swap(x0, x1); + } + + if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing + a = b = x0; + if(x1 < a) a = x1; + else if(x1 > b) b = x1; + if(x2 < a) a = x2; + else if(x2 > b) b = x2; + gfx_drawFastHLine(a, y0, b-a+1, color); + return; + } + + int16_t + dx01 = x1 - x0, + dy01 = y1 - y0, + dx02 = x2 - x0, + dy02 = y2 - y0, + dx12 = x2 - x1, + dy12 = y2 - y1, + sa = 0, + sb = 0; + + // For upper part of triangle, find scanline crossings for segments + // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1 + // is included here (and second loop will be skipped, avoiding a /0 + // error there), otherwise scanline y1 is skipped here and handled + // in the second loop...which also avoids a /0 error here if y0=y1 + // (flat-topped triangle). + if(y1 == y2) last = y1; // Include y1 scanline + else last = y1-1; // Skip it + + for(y=y0; y<=last; y++) { + a = x0 + sa / dy01; + b = x0 + sb / dy02; + sa += dx01; + sb += dx02; + /* longhand: + a = x0 + (x1 - x0) * (y - y0) / (y1 - y0); + b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); + */ + if(a > b) swap(a,b); + gfx_drawFastHLine(a, y, b-a+1, color); + } + + // For lower part of triangle, find scanline crossings for segments + // 0-2 and 1-2. This loop is skipped if y1=y2. + sa = dx12 * (y - y1); + sb = dx02 * (y - y0); + for(; y<=y2; y++) { + a = x1 + sa / dy12; + b = x0 + sb / dy02; + sa += dx12; + sb += dx02; + /* longhand: + a = x1 + (x2 - x1) * (y - y1) / (y2 - y1); + b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); + */ + if(a > b) swap(a,b); + gfx_drawFastHLine(a, y, b-a+1, color); + } +} + +void gfx_drawBitmap(int16_t x, int16_t y, + const uint8_t *bitmap, int16_t w, int16_t h, + uint16_t color) { + + int16_t i, j, byteWidth = (w + 7) / 8; + + for(j=0; j> (i & 7))) { + gfx_drawPixel(x+i, y+j, color); + } + } + } +} + +void gfx_write(uint8_t c) { + if (c == '\n') { + __gfx_state.cursor_y += __gfx_state.textsize*12; + __gfx_state.cursor_x = 0; + } else if (c == '\r') { + // skip em + } else { + gfx_drawChar(__gfx_state.cursor_x, __gfx_state.cursor_y, + c, __gfx_state.textcolor, __gfx_state.textbgcolor, + __gfx_state.textsize); + __gfx_state.cursor_x += __gfx_state.textsize*8; + if (__gfx_state.wrap && (__gfx_state.cursor_x > (__gfx_state._width - __gfx_state.textsize*8))) { + __gfx_state.cursor_y += __gfx_state.textsize*12; + __gfx_state.cursor_x = 0; + } + } +} + +void gfx_puts(char *s) { + while (*s) { + gfx_write(*s); + s++; + } +} + +// Draw a character +void gfx_drawChar(int16_t x, int16_t y, unsigned char c, + uint16_t color, uint16_t bg, uint8_t size) { + + int8_t i, j, line; + int8_t descender; + unsigned const char *glyph; + + glyph = &mcm_font[(c & 0x7f) * 9]; + + descender = (*glyph & 0x80) != 0; + + for (i=0; i<12; i++ ) { + line = 0x00; + if ( descender ) { + if (i > 2) { + line = *(glyph + (i - 3)); + } + } else { + if (i < 9) { + line = *(glyph + i); + } + } + line &= 0x7f; + for (j = 0; j<8; j++) { + if (line & 0x80) { + if (size == 1) // default size + gfx_drawPixel(x+j, y+i, color); + else { // big size + gfx_fillRect(x+(j*size), y+(i*size), size, size, color); + } + } else if (bg != color) { + if (size == 1) // default size + gfx_drawPixel(x+j, y+i, bg); + else { // big size + gfx_fillRect(x+j*size, y+i*size, size, size, bg); + } + } + line <<= 1; + } + } +} + +void gfx_setCursor(int16_t x, int16_t y) { + __gfx_state.cursor_x = x; + __gfx_state.cursor_y = y; +} + +void gfx_setTextSize(uint8_t s) { + __gfx_state.textsize = (s > 0) ? s : 1; +} + +void gfx_setTextColor(uint16_t c, uint16_t b) { + __gfx_state.textcolor = c; + __gfx_state.textbgcolor = b; +} + +void gfx_setTextWrap(uint8_t w) { + __gfx_state.wrap = w; +} + +uint8_t gfx_getRotation(void) { + return __gfx_state.rotation; +} + +void gfx_setRotation(uint8_t x) { + __gfx_state.rotation = (x & 3); + switch(__gfx_state.rotation) { + case 0: + case 2: + __gfx_state._width = GFX_WIDTH; + __gfx_state._height = GFX_HEIGHT; + break; + case 1: + case 3: + __gfx_state._width = GFX_HEIGHT; + __gfx_state._height = GFX_WIDTH; + break; + } +} + +// Return the size of the display (per current rotation) +uint16_t gfx_width(void) { + return __gfx_state._width; +} + +uint16_t gfx_height(void) { + return __gfx_state._height; +} + diff --git a/examples/stm32/f4/stm32f4-disco/lcd-serial/gfx.h b/examples/stm32/f4/stm32f4-disco/lcd-serial/gfx.h new file mode 100644 index 0000000..a10b184 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/lcd-serial/gfx.h @@ -0,0 +1,76 @@ +/* + * A simple port of the AdaFruit minimal graphics code to my + * demo code. + */ +#ifndef _GFX_H +#define _GFX_H +#include + +#define swap(a, b) { int16_t t = a; a = b; b = t; } + +void gfx_drawPixel(int x, int y, uint16_t color); +void gfx_drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color); +void gfx_drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); +void gfx_drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); +void gfx_drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); +void gfx_fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); +void gfx_fillScreen(uint16_t color); + +void gfx_drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color); +void gfx_drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, + uint16_t color); +void gfx_fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color); +void gfx_init(void (*draw)(int, int, uint16_t), int, int); + +void gfx_fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, + int16_t delta, uint16_t color); +void gfx_drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, + int16_t x2, int16_t y2, uint16_t color); +void gfx_fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, + int16_t x2, int16_t y2, uint16_t color); +void gfx_drawRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, + int16_t radius, uint16_t color); +void gfx_fillRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, + int16_t radius, uint16_t color); +void gfx_drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, + int16_t w, int16_t h, uint16_t color); +void gfx_drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, + uint16_t bg, uint8_t size); +void gfx_setCursor(int16_t x, int16_t y); +void gfx_setTextColor(uint16_t c, uint16_t bg); +void gfx_setTextSize(uint8_t s); +void gfx_setTextWrap(uint8_t w); +void gfx_setRotation(uint8_t r); +void gfx_puts(char *); +void gfx_write(uint8_t); + +uint16_t gfx_height(void); +uint16_t gfx_width(void); + +uint8_t gfx_getRotation(void); + +#define GFX_WIDTH 320 +#define GFX_HEIGHT 240 + +struct gfx_state { + int16_t _width, _height, cursor_x, cursor_y; + uint16_t textcolor, textbgcolor; + uint8_t textsize, rotation; + uint8_t wrap; + void (*drawpixel)(int, int, uint16_t); +}; + +extern struct gfx_state __gfx_state; + +#define GFX_COLOR_WHITE 0xFFFF +#define GFX_COLOR_BLACK 0x0000 +#define GFX_COLOR_GREY 0xF7DE +#define GFX_COLOR_BLUE 0x001F +#define GFX_COLOR_BLUE2 0x051F +#define GFX_COLOR_RED 0xF800 +#define GFX_COLOR_MAGENTA 0xF81F +#define GFX_COLOR_GREEN 0x07E0 +#define GFX_COLOR_CYAN 0x7FFF +#define GFX_COLOR_YELLOW 0xFFE0 + +#endif // _ADAFRUIT_GFX_H diff --git a/examples/stm32/f4/stm32f4-disco/lcd-serial/lcd-serial.c b/examples/stm32/f4/stm32f4-disco/lcd-serial/lcd-serial.c new file mode 100644 index 0000000..706b3e0 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/lcd-serial/lcd-serial.c @@ -0,0 +1,89 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014 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 "clock.h" +#include "console.h" +#include "sdram.h" +#include "lcd-spi.h" +#include "gfx.h" + +/* Convert degrees to radians */ +#define d2r(d) ((d) * 6.2831853 / 360.0) + +/* + * This is our example, the heavy lifing is actually in lcd-spi.c but + * this drives that code. + */ +int main(void) { + int p1, p2, p3; + + clock_setup(); + console_setup(115200); + sdram_init(); + lcd_spi_init(); + console_puts("LCD Initialized\n"); + console_puts("Should have a checker pattern, press any key to proceed\n"); + (void) console_getc(1); + gfx_init(lcd_draw_pixel, 240, 320); + gfx_fillScreen(LCD_GREY); + gfx_fillRoundRect(10, 10, 220, 220, 5, LCD_WHITE); + gfx_drawRoundRect(10, 10, 220, 220, 5, LCD_RED); + gfx_fillCircle(20, 250, 10, LCD_RED); + gfx_fillCircle(120, 250, 10, LCD_GREEN); + gfx_fillCircle(220, 250, 10, LCD_BLUE); + gfx_setTextSize(2); + gfx_setCursor(15, 25); + gfx_puts("STM32F4-DISCO"); + gfx_setTextSize(1); + gfx_setCursor(15, 49); + gfx_puts("Simple example to put some"); + gfx_setCursor(15, 60); + gfx_puts("stuff on the LCD screen."); + lcd_show_frame(); + console_puts("Now it has a bit of structured graphics.\n"); + console_puts("Press a key for some simple animation.\n"); + (void) console_getc(1); + gfx_setTextColor(LCD_YELLOW, LCD_BLACK); + gfx_setTextSize(3); + p1 = 0; + p2 = 45; + p3 = 90; + while (1) { + gfx_fillScreen(LCD_BLACK); + gfx_setCursor(15, 36); + gfx_puts("PLANETS!"); + gfx_fillCircle( 120, 160, 40, LCD_YELLOW); + gfx_drawCircle( 120, 160, 55, LCD_GREY); + gfx_drawCircle( 120, 160, 75, LCD_GREY); + gfx_drawCircle( 120, 160, 100, LCD_GREY); + + gfx_fillCircle( 120 + (sin(d2r(p1)) * 55), + 160 + (cos(d2r(p1)) * 55), 5, LCD_RED); + gfx_fillCircle( 120 + (sin(d2r(p2)) * 75), + 160 + (cos(d2r(p2)) * 75), 10, LCD_WHITE); + gfx_fillCircle( 120 + (sin(d2r(p3)) * 100), + 160 + (cos(d2r(p3)) * 100), 8, LCD_BLUE); + p1 = (p1 + 3) % 360; + p2 = (p2 + 2) % 360; + p3 = (p3 + 1) % 360; + lcd_show_frame(); + } +} diff --git a/examples/stm32/f4/stm32f4-disco/lcd-serial/lcd-spi.c b/examples/stm32/f4/stm32f4-disco/lcd-serial/lcd-spi.c new file mode 100644 index 0000000..2935e79 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/lcd-serial/lcd-spi.c @@ -0,0 +1,455 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014 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 . + */ + +/* + * Initialize the ST Micro TFT Display using the SPI port + */ +#include +#include +#include +#include +#include +#include "console.h" +#include "clock.h" +#include "sdram.h" +#include "lcd-spi.h" + + +/* forward prototypes for some helper functions */ +static int print_decimal(int v); +static int print_hex(int v); + +/* + * This is an ungainly workaround (aka hack) basically I want to know + * when the SPI port is 'done' sending all of the bits out, and it is + * done when it has clocked enough bits that it would have received a + * byte. Since we're using the SPI port in write_only mode I am not + * collecting the "received" bytes into a buffer, but one could of + * course. I keep track of how many bytes should have been returned + * by decrementing the 'rx_pend' volatile. When it reaches 0 we know + * we are done. + */ + +volatile int rx_pend; +volatile uint16_t spi_rx_buf; + +/* + * This is the ISR we use. Note that the name is based on the name + * in the irq.json file of libopencm3 plus the "_isr" extension. + */ +void +spi5_isr(void) { + spi_rx_buf = SPI_DR(SPI5); + --rx_pend; +} + +/* Simple double buffering, one frame is displayed, the + * other being built. + */ +uint16_t *cur_frame; +uint16_t *display_frame; + + +/* + * Drawing a pixel consists of storing a 16 bit value in the + * memory used to hold the frame. This code computes the address + * of the word to store, and puts in the value we pass to it. + */ +void +lcd_draw_pixel(int x, int y, uint16_t color) { + *(cur_frame + x + y * LCD_WIDTH) = color; +} + +/* + * Fun fact, same SPI port as the MEMS example but different + * I/O pins. Clearly you can't use both the SPI port and the + * MEMS chip at the same time in this example. + * + * For the STM32-DISCO board, SPI pins in use: + * N/C - RESET + * PC2 - CS (could be NSS but won't be) + * PF7 - SCLK (AF5) SPI5 + * PD13 - DATA / CMD* + * PF9 - MOSI (AF5) SPI5 + */ + +/* + * This structure defines the sequence of commands to send + * to the Display in order to initialize it. The AdaFruit + * folks do something similar, it helps when debugging the + * initialization sequence for the display. + */ +struct tft_command { + uint16_t delay; // If you need a delay after + uint8_t cmd; // command to send + uint8_t n_args; // How many arguments it has +}; + + +/* prototype for lcd_command */ +static void lcd_command(uint8_t cmd, int delay, int n_args, + const uint8_t *args); + +/* + * void lcd_command(cmd, delay, args, arg_ptr) + * + * All singing all dancing 'do a command' feature. Basically it + * sends a command, and if args are present it sets 'data' and + * sends those along too. + */ +static void +lcd_command(uint8_t cmd, int delay, int n_args, const uint8_t *args) { + uint32_t timeout; + int i; + + gpio_clear(GPIOC, GPIO2); // Select the LCD + rx_pend++; + spi_send(SPI5, cmd); + /* We need to wait until it is sent, if we turn on the Data + * line too soon, it ends up confusing the display to thinking + * its a data transfer, as it samples the D/CX line on the last + * bit sent. + */ + for (timeout = 0; (timeout < 1000) && (rx_pend); timeout++); + rx_pend = 0; // sometimes, at 10Mhz we miss this + if (n_args) { + gpio_set(GPIOD, GPIO13); // Set the D/CX pin + for (i = 0; i < n_args; i++) { + rx_pend++; + spi_send(SPI5, *(args+i)); + } + /* This wait so that we don't pull CS too soon after + * sending the last byte of data. + */ + for (timeout = 0; (timeout < 1000) && (rx_pend); timeout++); + } + gpio_set(GPIOC, GPIO2); // Turn off chip select + gpio_clear(GPIOD, GPIO13); // always reset D/CX + if (delay) { + msleep(delay); // wait, if called for + } +} + +/* + * This creates a 'script' of commands that can be played + * to the LCD controller to initialize it. + * One array holds the 'argument' bytes, the other + * the commands. + * Keeping them in sync is essential + */ +static const uint8_t cmd_args[] = { + 0x00, 0x1B, + 0x0a, 0xa2, + 0x10, + 0x10, + 0x45, 0x15, + 0x90, +// 0xc8, // original +// 11001000 = MY, MX, BGR + 0x08, + 0xc2, + 0x55, + 0x0a, 0xa7, 0x27, 0x04, + 0x00, 0x00, 0x00, 0xef, + 0x00, 0x00, 0x01, 0x3f, +// 0x01, 0x00, 0x06, // original + 0x01, 0x00, 0x00, // modified to remove RGB mode + 0x01, + 0x0F, 0x29, 0x24, 0x0C, 0x0E, + 0x09, 0x4E, 0x78, 0x3C, 0x09, + 0x13, 0x05, 0x17, 0x11, 0x00, + 0x00, 0x16, 0x1B, 0x04, 0x11, + 0x07, 0x31, 0x33, 0x42, 0x05, + 0x0C, 0x0A, 0x28, 0x2F, 0x0F, +}; + +/* + * These are the commands we're going to send to the + * display to initialize it. We send them all, in sequence + * with occasional delays. Commands that require data bytes + * as arguments, indicate how many bytes to pull out the + * above array to include. + * + * The sequence was pieced together from the ST Micro demo + * code, the data sheet, and other sources on the web. + */ +const struct tft_command initialization[] = { + { 0, 0xb1, 2 }, // 0x00, 0x1B, + { 0, 0xb6, 2 }, // 0x0a, 0xa2, + { 0, 0xc0, 1 }, // 0x10, + { 0, 0xc1, 1 }, // 0x10, + { 0, 0xc5, 2 }, // 0x45, 0x15, + { 0, 0xc7, 1 }, // 0x90, + { 0, 0x36, 1 }, // 0xc8, + { 0, 0xb0, 1 }, // 0xc2, + { 0, 0x3a, 1 }, // 0x55 **added, pixel format 16 bpp + { 0, 0xb6, 4 }, // 0x0a, 0xa7, 0x27, 0x04, + { 0, 0x2A, 4 }, // 0x00, 0x00, 0x00, 0xef, + { 0, 0x2B, 4 }, // 0x00, 0x00, 0x01, 0x3f, + { 0, 0xf6, 3 }, // 0x01, 0x00, 0x06, + { 200, 0x2c, 0 }, + { 0, 0x26, 1}, // 0x01, + { 0, 0xe0, 15 }, // 0x0F, 0x29, 0x24, 0x0C, 0x0E, + // 0x09, 0x4E, 0x78, 0x3C, 0x09, + // 0x13, 0x05, 0x17, 0x11, 0x00, + { 0, 0xe1, 15 }, // 0x00, 0x16, 0x1B, 0x04, 0x11, + // 0x07, 0x31, 0x33, 0x42, 0x05, + // 0x0C, 0x0A, 0x28, 0x2F, 0x0F, + { 200, 0x11, 0 }, + { 0, 0x29, 0 }, + { 0, 0, 0 } // cmd == 0 indicates last command +}; + +/* prototype for initialize_display */ +static void initialize_display(const struct tft_command cmds[]); + +/* + * void initialize_display(struct cmds[]) + * + * This is the function that sends the entire list. It also puts + * the commands it is sending to the console. + */ +static void +initialize_display(const struct tft_command cmds[]) { + int i = 0; + int arg_offset = 0; + int j; + + /* Initially arg offset is zero, so each time we 'consume' + * a few bytes in the args array the offset is moved and + * that changes the pointer we send to the command function. + */ + while (cmds[i].cmd) { + console_puts("CMD: "); + print_hex(cmds[i].cmd); + console_puts(", "); + if (cmds[i].n_args) { + console_puts("ARGS: "); + for (j = 0; j < cmds[i].n_args; j++) { + print_hex(cmd_args[arg_offset+j]); + console_puts(", "); + } + } + console_puts("DELAY: "); + print_decimal(cmds[i].delay); + console_puts("ms\n"); + + lcd_command(cmds[i].cmd, cmds[i].delay, cmds[i].n_args, + &cmd_args[arg_offset]); + arg_offset += cmds[i].n_args; + i++; + } + console_puts("Done.\n"); +} + +/* prototype for test_image */ +static void test_image(void); + +/* + * Interesting questions: + * - How quickly can I write a full frame? + * * Take the bits sent (16 * width * height) + * and divide by the baud rate (10.25Mhz) + * * Tests in main.c show that yes, it taks 74ms. + * + * Create non-random data in the frame buffer. In our case + * a black background and a grid 16 pixels x 16 pixels of + * white lines. No line on the right edge and bottom of screen. + */ +static void +test_image(void) { + int x, y; + uint16_t pixel; + + for (x = 0; x < LCD_WIDTH; x++) { + for (y = 0; y < LCD_HEIGHT; y++) { + pixel = 0; // all black + if ((x % 16) == 0) { + pixel = 0xffff; // all white + } + if ((y % 16) == 0) { + pixel = 0xffff; // all white + } + lcd_draw_pixel(x, y, pixel); + } + } +} + +/* + * void lcd_show_frame(void) + * + * Dump an entire frame to the LCD all at once. In theory you + * could call this with DMA but that is made more difficult by + * the implementation of SPI and the modules interpretation of + * D/CX line. + */ +void lcd_show_frame(void) { + uint16_t *t; + uint8_t size[4]; + + t = display_frame; + display_frame = cur_frame; + cur_frame = t; + /* */ + size[0] = 0; + size[1] = 0; + size[2] = (LCD_WIDTH >> 8) & 0xff; + size[3] = (LCD_WIDTH) & 0xff; + lcd_command(0x2A, 0, 4, (const uint8_t *)&size[0]); + size[0] = 0; + size[1] = 0; + size[2] = (LCD_HEIGHT >> 8) & 0xff; + size[3] = LCD_HEIGHT & 0xff; + lcd_command(0x2B, 0, 4, (const uint8_t *)&size[0]); + lcd_command(0x2C, 0, FRAME_SIZE_BYTES, (const uint8_t *)display_frame); +} + +/* + * void lcd_spi_init(void) + * + * Initialize the SPI port, and the through that port + * initialize the LCD controller. Note that this code + * will expect to be able to draw into the SDRAM on + * the board, so the sdram much be initialized before + * calling this function. + * + * SPI Port and GPIO Defined - for STM32F4-Disco + * + * LCD_CS PC2 + * LCD_SCK PF7 + * LCD_DC PD13 + * LCD_MOSI PF9 + * LCD_SPI SPI5 + * LCD_WIDTH 240 + * LCD_HEIGHT 320 + */ +void +lcd_spi_init(void) { + uint32_t tmp; + + /* + * Set up the GPIO lines for the SPI port and + * control lines on the display. + */ + 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_IOPFEN); + + gpio_mode_setup(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO2); + gpio_mode_setup(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO13); + + gpio_mode_setup(GPIOF, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO7 | GPIO9); + gpio_set_af(GPIOF, GPIO_AF5, GPIO7 | GPIO9); + + cur_frame = (uint16_t *)(SDRAM_BASE_ADDRESS); + display_frame = cur_frame + (LCD_WIDTH * LCD_HEIGHT); + + rx_pend = 0; + /* Implement state management hack */ + nvic_enable_irq(NVIC_SPI5_IRQ); + + rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_SPI5EN); + /* This should configure SPI5 as we need it configured */ + tmp = SPI_SR(LCD_SPI); + SPI_CR2(LCD_SPI) |= (SPI_CR2_SSOE | SPI_CR2_RXNEIE); + + /* device clocks on the rising edge of SCK with MSB first */ + tmp = SPI_CR1_BAUDRATE_FPCLK_DIV_4 | // 10.25Mhz SPI Clock (42M/4) + SPI_CR1_MSTR | // Master Mode + SPI_CR1_BIDIOE | // Write Only + SPI_CR1_SPE; // Enable SPI + + SPI_CR1(LCD_SPI) = tmp; // Do it. + if (SPI_SR(LCD_SPI) & SPI_SR_MODF) { + SPI_CR1(LCD_SPI) = tmp; // Re-writing will reset MODF + console_puts("Initial mode fault.\n"); + } + + /* Set up the display */ + console_puts("Initialize the display.\n"); + initialize_display(initialization); + + /* create a test image */ + console_puts("Generating Test Image\n"); + test_image(); + + /* display it on the LCD */ + console_puts("And ... voila\n"); + lcd_show_frame(); +} + +/* + * int len = print_decimal(int value) + * + * Very simple routine to print an integer as a decimal + * number on the console. + */ +int +print_decimal(int num) { + int ndx = 0; + char buf[10]; + int len = 0; + char is_signed = 0; + + if (num < 0) { + is_signed++; + num = 0 - num; + } + buf[ndx++] = '\000'; + do { + buf[ndx++] = (num % 10) + '0'; + num = num / 10; + } while (num != 0); + ndx--; + if (is_signed != 0) { + console_putc('-'); + len++; + } + while (buf[ndx] != '\000') { + console_putc(buf[ndx--]); + len++; + } + return len; // number of characters printed +} + +/* + * int print_hex(int value) + * + * Very simple routine for printing out hex constants. + */ +static int print_hex(int v) { + int ndx = 0; + char buf[10]; + int len; + + buf[ndx++] = '\000'; + do { + char c = v & 0xf; + buf[ndx++] = (c > 9) ? '7'+ c : '0' + c; + v = (v >> 4) & 0x0fffffff; + } while (v != 0); + ndx--; + console_puts("0x"); + len = 2; + while (buf[ndx] != '\000') { + console_putc(buf[ndx--]); + len++; + } + return len; // number of characters printed +} diff --git a/examples/stm32/f4/stm32f4-disco/lcd-serial/lcd-spi.h b/examples/stm32/f4/stm32f4-disco/lcd-serial/lcd-spi.h new file mode 100644 index 0000000..ac3444c --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/lcd-serial/lcd-spi.h @@ -0,0 +1,61 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014 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 . + */ + +#ifndef __LCD_SPI_H +#define LCD_SPI_H + +/* + * prototypes for the LCD example + * + * This is a very basic API, initialize, a function which will show the + * frame, and a function which will draw a pixel in the framebuffer. + */ + +void lcd_spi_init(void); +void lcd_show_frame(void); +void lcd_draw_pixel(int x, int y, uint16_t color); + +// Color definitions +#define LCD_BLACK 0x0000 +#define LCD_BLUE 0x1F00 +#define LCD_RED 0x00F8 +#define LCD_GREEN 0xE007 +#define LCD_CYAN 0xFF07 +#define LCD_MAGENTA 0x1FF8 +#define LCD_YELLOW 0xE0FF +#define LCD_WHITE 0xFFFF +#define LCD_GREY 0xc339 + +/* + * SPI Port and GPIO Defined - for STM32F4-Disco + */ +// #define LCD_RESET PA3 not used +#define LCD_CS PC2 // CH 1 +#define LCD_SCK PF7 // CH 2 +#define LCD_DC PD13 // CH 4 +#define LCD_MOSI PF9 // CH 3 + +#define LCD_SPI SPI5 + +#define LCD_WIDTH 240 +#define LCD_HEIGHT 320 + +#define FRAME_SIZE (LCD_WIDTH * LCD_HEIGHT) +#define FRAME_SIZE_BYTES (FRAME_SIZE * 2) +#endif diff --git a/examples/stm32/f4/stm32f4-disco/lcd-serial/sdram.c b/examples/stm32/f4/stm32f4-disco/lcd-serial/sdram.c new file mode 100644 index 0000000..4c6e4e7 --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/lcd-serial/sdram.c @@ -0,0 +1,134 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014 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 then is the initialization code extracted from the + * sdram example. + */ +#include +#include +#include +#include +#include "clock.h" +#include "sdram.h" + +/* + * 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 */ +} diff --git a/examples/stm32/f4/stm32f4-disco/lcd-serial/sdram.h b/examples/stm32/f4/stm32f4-disco/lcd-serial/sdram.h new file mode 100644 index 0000000..157f30a --- /dev/null +++ b/examples/stm32/f4/stm32f4-disco/lcd-serial/sdram.h @@ -0,0 +1,32 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014 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 . + */ + +#ifndef __SDRAM_H +#define __SDRAM_H + +#define SDRAM_BASE_ADDRESS ((uint8_t *)(0xd0000000)) + +/* Initialize the SDRAM chip on the board */ +void sdram_init(void); + +#ifndef NULL +#define NULL (void *)(0) +#endif + +#endif