Pulling in some of the makefile changes that are in master into this

branch to make merging easier later on.
This commit is contained in:
cmcmanis
2014-02-09 13:39:08 -08:00
committed by Piotr Esden-Tempski
parent 4defd3e1d2
commit ae9c116e30
14 changed files with 1939 additions and 0 deletions

View File

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

View File

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

View File

@@ -0,0 +1,70 @@
/*
* 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/>.
*/
/*
* 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,232 @@
/*
* 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/>.
*/
/*
* Interrupt drive Console code (extracted from the usart-irq example)
*
*/
#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"
#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 = (&reg) + 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 <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);
}
/*
* 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);
}

View File

@@ -0,0 +1,54 @@
/*
* 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/>.
*/
#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

View File

@@ -0,0 +1,177 @@
/*
* 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/>.
*/
#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

View File

@@ -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 <stdint.h>
#include <math.h>
#include <stdlib.h>
#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<y) {
if (f >= 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<y) {
if (f >= 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<y) {
if (f >= 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<x+w; i++) {
gfx_drawFastVLine(i, y, h, color);
}
}
void gfx_fillScreen(uint16_t color) {
gfx_fillRect(0, 0, __gfx_state._width, __gfx_state._height, color);
}
// Draw a rounded rectangle
void gfx_drawRoundRect(int16_t x, int16_t y, int16_t w,
int16_t h, int16_t r, uint16_t color) {
// smarter version
gfx_drawFastHLine(x+r , y , w-2*r, color); // Top
gfx_drawFastHLine(x+r , y+h-1, w-2*r, color); // Bottom
gfx_drawFastVLine(x , y+r , h-2*r, color); // Left
gfx_drawFastVLine(x+w-1, y+r , h-2*r, color); // Right
// draw four corners
gfx_drawCircleHelper(x+r , y+r , r, 1, color);
gfx_drawCircleHelper(x+w-r-1, y+r , r, 2, color);
gfx_drawCircleHelper(x+w-r-1, y+h-r-1, r, 4, color);
gfx_drawCircleHelper(x+r , y+h-r-1, r, 8, color);
}
// Fill a rounded rectangle
void gfx_fillRoundRect(int16_t x, int16_t y, int16_t w,
int16_t h, int16_t r, uint16_t color) {
// smarter version
gfx_fillRect(x+r, y, w-2*r, h, color);
// draw four corners
gfx_fillCircleHelper(x+w-r-1, y+r, r, 1, h-2*r-1, color);
gfx_fillCircleHelper(x+r , y+r, r, 2, h-2*r-1, color);
}
// Draw a triangle
void gfx_drawTriangle(int16_t x0, int16_t y0,
int16_t x1, int16_t y1,
int16_t x2, int16_t y2, uint16_t color) {
gfx_drawLine(x0, y0, x1, y1, color);
gfx_drawLine(x1, y1, x2, y2, color);
gfx_drawLine(x2, y2, x0, y0, color);
}
// Fill a triangle
void gfx_fillTriangle ( int16_t x0, int16_t y0,
int16_t x1, int16_t y1,
int16_t x2, int16_t y2, uint16_t color) {
int16_t a, b, y, last;
// Sort coordinates by Y order (y2 >= 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<h; j++) {
for(i=0; i<w; i++ ) {
if(pgm_read_byte(bitmap + j * byteWidth + i / 8) & (128 >> (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;
}

View File

@@ -0,0 +1,76 @@
/*
* A simple port of the AdaFruit minimal graphics code to my
* demo code.
*/
#ifndef _GFX_H
#define _GFX_H
#include <stdint.h>
#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

View File

@@ -0,0 +1,89 @@
/*
* 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 <stdint.h>
#include <math.h>
#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();
}
}

View File

@@ -0,0 +1,455 @@
/*
* 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/>.
*/
/*
* Initialize the ST Micro TFT Display using the SPI port
*/
#include <stdint.h>
#include <libopencm3/stm32/spi.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/cm3/nvic.h>
#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
}

View File

@@ -0,0 +1,61 @@
/*
* 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/>.
*/
#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

View File

@@ -0,0 +1,134 @@
/*
* 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/>.
*/
/*
* This then is the initialization code extracted from the
* sdram example.
*/
#include <stdint.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/fsmc.h>
#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 */
}

View File

@@ -0,0 +1,32 @@
/*
* 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/>.
*/
#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