From 22f59c458336779490976fa24ce7902a904b6523 Mon Sep 17 00:00:00 2001 From: Chuck McManis Date: Sun, 15 Feb 2015 17:57:48 -0800 Subject: [PATCH] LCD version of the Mandelbrot example This version is the ASCII one but uses the LCD display that is attached to the board for a more colorful result. This example also zooms into a more "interesting" place in the set so the display stays interesting during the full 100 generations. --- .../mandelbrot-lcd/Makefile | 27 ++ .../mandelbrot-lcd/README.md | 14 + .../mandelbrot-lcd/clock.c | 88 +++++ .../mandelbrot-lcd/clock.h | 27 ++ .../stm32f429i-discovery/mandelbrot-lcd/lcd.c | 344 ++++++++++++++++++ .../stm32f429i-discovery/mandelbrot-lcd/lcd.h | 48 +++ .../mandelbrot-lcd/mandel.c | 227 ++++++++++++ .../mandelbrot-lcd/sdram.c | 144 ++++++++ .../mandelbrot-lcd/sdram.h | 27 ++ 9 files changed, 946 insertions(+) create mode 100644 examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/Makefile create mode 100644 examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/README.md create mode 100644 examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/clock.c create mode 100644 examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/clock.h create mode 100644 examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/lcd.c create mode 100644 examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/lcd.h create mode 100644 examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/mandel.c create mode 100644 examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/sdram.c create mode 100644 examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/sdram.h diff --git a/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/Makefile b/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/Makefile new file mode 100644 index 0000000..61f9826 --- /dev/null +++ b/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/Makefile @@ -0,0 +1,27 @@ +## +## This file is part of the libopencm3 project. +## +## Copyright (C) 2009 Uwe Hermann +## +## This library is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU Lesser General Public License for more details. +## +## You should have received a copy of the GNU Lesser General Public License +## along with this library. If not, see . +## + +OBJS = sdram.o lcd.o clock.o + +BINARY = mandel + +LDSCRIPT = ../stm32f429i-discovery.ld + +include ../../Makefile.include + diff --git a/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/README.md b/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/README.md new file mode 100644 index 0000000..7809d02 --- /dev/null +++ b/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/README.md @@ -0,0 +1,14 @@ +# README + +This example program demonstrates the floating point coprocessor usage on +the ST STM32F429IDISCOVERY eval board. + +The mandlebrot is calculated and displayed on the attached LCD + +## Board connections + +| Port | Function | Description | +| ----- | ------------- | --------------------------------- | +| `PA9` | `(USART1_TX)` | TTL serial output `(115200,8,N,1)` | + +Data can be sent to the serial port for debugging. diff --git a/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/clock.c b/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/clock.c new file mode 100644 index 0000000..48e2fa9 --- /dev/null +++ b/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/clock.c @@ -0,0 +1,88 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2009 Uwe Hermann + * Copyright (C) 2011 Stephen Caudle + * Copyright (C) 2012 Daniel Serpell + * Copyright (C) 2015 Piotr Esden-Tempski + * Copyright (C) 2015 Chuck McManis + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include "clock.h" + +void clock_setup(void) +{ + rcc_clock_setup_hse_3v3(&hse_8mhz_3v3[CLOCK_3V3_168MHZ]); + + /* set up the SysTick function (1mS interrupts) */ + systick_set_clocksource(STK_CSR_CLKSOURCE_AHB); + STK_CVR = 0; + systick_set_reload(rcc_ahb_frequency / 1000); + systick_counter_enable(); + systick_interrupt_enable(); +} + +/* simple millisecond counter */ +static volatile uint32_t system_millis; +static volatile uint32_t delay_timer; + +/* + * Simple systick handler + * + * Increments a 32 bit value once per millesecond + * which rolls over every 49 days. + */ +void sys_tick_handler(void) +{ + system_millis++; + if (delay_timer > 0) { + delay_timer--; + } +} + +/* + * Simple spin loop waiting for time to pass + * + * A couple of things to note: + * First, you can't just compare to + * system_millis because doing so will mean + * you delay forever if you happen to hit a + * time where it is rolling over. + * Second, accuracy is "at best" 1mS as you + * may call this "just before" the systick hits + * with a value of '1' and it would return + * nearly immediately. So if you need really + * precise delays, use one of the timers. + */ +void +msleep(uint32_t delay) +{ + delay_timer = delay; + while (delay_timer); +} + +uint32_t +mtime(void) +{ + return system_millis; +} diff --git a/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/clock.h b/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/clock.h new file mode 100644 index 0000000..8bebe52 --- /dev/null +++ b/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/clock.h @@ -0,0 +1,27 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014-2015 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 . + */ + +/* + * clock functions + */ + +extern void clock_setup(void); +extern void msleep(uint32_t); +extern uint32_t mtime(void); + diff --git a/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/lcd.c b/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/lcd.c new file mode 100644 index 0000000..33472a5 --- /dev/null +++ b/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/lcd.c @@ -0,0 +1,344 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014-2015 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 +#include "clock.h" +#include "sdram.h" +#include "lcd.h" + +/* + * 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 FRAME_SIZE (LCD_WIDTH * LCD_HEIGHT) +#define FRAME_SIZE_BYTES (FRAME_SIZE * 2) + +/* 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) +{ + if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)) { + printf("Draw out of range [%d, %d]\n", x, y); + while (1); + } + *(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) +{ + int i; + + gpio_clear(GPIOC, GPIO2); /* Select the LCD */ + (void) spi_xfer(LCD_SPI, cmd); + if (n_args) { + gpio_set(GPIOD, GPIO13); /* Set the D/CX pin */ + for (i = 0; i < n_args; i++) { + (void) spi_xfer(LCD_SPI, *(args+i)); + } + } + 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; + + /* 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) { + lcd_command(cmds[i].cmd, cmds[i].delay, cmds[i].n_args, + &cmd_args[arg_offset]); + arg_offset += cmds[i].n_args; + i++; + } +} + +/* 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_init(void) +{ + + /* + * Set up the GPIO lines for the SPI port and + * control lines on the display. + */ + rcc_periph_clock_enable(RCC_GPIOC | RCC_GPIOD | RCC_GPIOF); + + 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); + + rcc_periph_clock_enable(RCC_SPI5); + spi_init_master(LCD_SPI, SPI_CR1_BAUDRATE_FPCLK_DIV_4, + SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE, + SPI_CR1_CPHA_CLK_TRANSITION_1, + SPI_CR1_DFF_8BIT, + SPI_CR1_MSBFIRST); + spi_enable_ss_output(LCD_SPI); + spi_enable(LCD_SPI); + + /* Set up the display */ + initialize_display(initialization); + + /* create a test image */ + test_image(); + + /* display it on the LCD */ + lcd_show_frame(); +} + diff --git a/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/lcd.h b/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/lcd.h new file mode 100644 index 0000000..9d1ae33 --- /dev/null +++ b/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/lcd.h @@ -0,0 +1,48 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014-2015 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_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 + +#define LCD_WIDTH 240 +#define LCD_HEIGHT 320 + +#endif diff --git a/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/mandel.c b/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/mandel.c new file mode 100644 index 0000000..5bcc712 --- /dev/null +++ b/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/mandel.c @@ -0,0 +1,227 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2009 Uwe Hermann + * Copyright (C) 2011 Stephen Caudle + * Copyright (C) 2012 Daniel Serpell + * Copyright (C) 2015 Piotr Esden-Tempski + * Copyright (C) 2015 Chuck McManis + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include "clock.h" +#include "sdram.h" +#include "lcd.h" + +/* utility functions */ +void uart_putc(char c); +int _write(int fd, char *ptr, int len); + +void mandel(float, float, float); + +static void gpio_setup(void) +{ + /* Setup GPIO pin GPIO13 on GPIO port G for LED. */ + rcc_periph_clock_enable(RCC_GPIOG); + gpio_mode_setup(GPIOG, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO13); + + /* Setup GPIO A pins for the USART1 function */ + rcc_periph_clock_enable(RCC_GPIOA); + rcc_periph_clock_enable(RCC_USART1); + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO9 | GPIO10); + gpio_set_af(GPIOA, GPIO_AF7, GPIO9 | GPIO10); + + usart_set_baudrate(USART1, 115200); + usart_set_databits(USART1, 8); + usart_set_stopbits(USART1, USART_STOPBITS_1); + usart_set_mode(USART1, USART_MODE_TX_RX); + usart_set_parity(USART1, USART_PARITY_NONE); + usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE); + usart_enable(USART1); +} + +/* Maximum number of iterations for the escape-time calculation */ +#define max_iter 32 +uint16_t lcd_colors[] = { + 0x0, + 0x1f00, + 0x00f8, + 0xe007, + 0xff07, + 0x1ff8, + 0xe0ff, + 0xffff, + 0xc339, + 0x1f00 >> 1, + 0x00f8 >> 1, + 0xe007 >> 1, + 0xff07 >> 1, + 0x1ff8 >> 1, + 0xe0ff >> 1, + 0xffff >> 1, + 0xc339 >> 1, + 0x1f00 << 1, + 0x00f8 << 1, + 0x6007 << 1, + 0x6f07 << 1, + 0x1ff8 << 1, + 0x60ff << 1, + 0x6fff << 1, + 0x4339 << 1, + 0x1f00 ^ 0x6ac9, + 0x00f8 ^ 0x6ac9, + 0xe007 ^ 0x6ac9, + 0xff07 ^ 0x6ac9, + 0x1ff8 ^ 0x6ac9, + 0xe0ff ^ 0x6ac9, + 0xffff ^ 0x6ac9, + 0xc339 ^ 0x6ac9, + 0, + 0, + 0, + 0, + 0 +}; + + +static int iterate(float, float); +/* Main mandelbrot calculation */ +static int iterate(float px, float py) +{ + int it = 0; + float x = 0, y = 0; + while (it < max_iter) { + float nx = x*x; + float ny = y*y; + if ((nx + ny) > 4) { + return it; + } + /* Zn+1 = Zn^2 + P */ + y = 2*x*y + py; + x = nx - ny + px; + it++; + } + return 0; +} + +void mandel(float cx, float cy, float scale) +{ + int x, y; + int change = 0; + for (x = -120; x < 120; x++) { + for (y = -160; y < 160; y++) { + int i = iterate(cx + x*scale, cy + y*scale); + if (i >= max_iter) { + i = max_iter; + } else { + change++; + } + lcd_draw_pixel(x+120, y+160, lcd_colors[i]); + } + } +} + +int main(void) +{ + int gen = 0; + float scale = 0.25f, center_x = -0.5f, center_y = 0.0f; + + + /* Clock setup */ + clock_setup(); + /* USART and GPIO setup */ + gpio_setup(); + /* Enable the SDRAM attached to the board */ + sdram_init(); + /* Enable the LCD attached to the board */ + lcd_init(); + + printf("System initialized.\n"); + + while (1) { + /* Blink the LED (PG13) on the board with each fractal drawn. */ + gpio_toggle(GPIOG, GPIO13); /* LED on/off */ + mandel(center_x, center_y, scale); /* draw mandelbrot */ + lcd_show_frame(); /* show it */ + /* Change scale and center */ + center_x += 0.1815f * scale; + center_y += 0.505f * scale; + scale *= 0.875f; + gen++; + if (gen > 99) { + scale = 0.25f; + center_x = -0.5f; + center_y = 0.0f; + gen = 0; + } + /* + printf("Generation: %d\n", generation); + printf("Cx, Cy = %9.2f, %9.2f, scale = %9.2f\n", + center_x, center_y, scale); + */ + } + + return 0; +} + +/* + * uart_putc + * + * This pushes a character into the transmit buffer for + * the channel and turns on TX interrupts (which will fire + * because initially the register will be empty.) If the + * ISR sends out the last character it turns off the transmit + * interrupt flag, so that it won't keep firing on an empty + * transmit buffer. + */ +void +uart_putc(char c) { + + while ((USART_SR(USART1) & USART_SR_TXE) == 0); + USART_DR(USART1) = c; +} + +/* + * Called by libc stdio functions + */ +int +_write(int fd, char *ptr, int len) +{ + int i = 0; + + /* + * Write "len" of char from "ptr" to file id "fd" + * Return number of char written. + */ + if (fd > 2) { + return -1; /* STDOUT, STDIN, STDERR */ + } + while (*ptr && (i < len)) { + uart_putc(*ptr); + if (*ptr == '\n') { + uart_putc('\r'); + } + i++; + ptr++; + } + return i; +} + diff --git a/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/sdram.c b/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/sdram.c new file mode 100644 index 0000000..9fc4e0b --- /dev/null +++ b/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/sdram.c @@ -0,0 +1,144 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014-2015 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" + +#ifndef NULL +#define NULL (void *)(0) +#endif + +/* + * This is just syntactic sugar but it helps, all of these + * GPIO pins get configured in exactly the same way. + */ +static struct { + uint32_t gpio; + uint16_t pins; +} sdram_pins[6] = { + {GPIOB, GPIO5 | GPIO6 }, + {GPIOC, GPIO0 }, + {GPIOD, GPIO0 | GPIO1 | GPIO8 | GPIO9 | GPIO10 | GPIO14 | GPIO15}, + {GPIOE, GPIO0 | GPIO1 | GPIO7 | GPIO8 | GPIO9 | GPIO10 | + GPIO11 | GPIO12 | GPIO13 | GPIO14 | GPIO15 }, + {GPIOF, GPIO0 | GPIO1 | GPIO2 | GPIO3 | GPIO4 | GPIO5 | GPIO11 | + GPIO12 | GPIO13 | GPIO14 | GPIO15 }, + {GPIOG, GPIO0 | GPIO1 | GPIO4 | GPIO5 | GPIO8 | GPIO15} +}; + +static struct sdram_timing timing = { + .trcd = 2, /* RCD Delay */ + .trp = 2, /* RP Delay */ + .twr = 2, /* Write Recovery Time */ + .trc = 7, /* Row Cycle Delay */ + .tras = 4, /* Self Refresh Time */ + .txsr = 7, /* Exit Self Refresh Time */ + .tmrd = 2, /* Load to Active Delay */ +}; + +/* + * Initialize the SD RAM controller. + */ +void +sdram_init(void) { + int i; + uint32_t cr_tmp, tr_tmp; /* control, timing registers */ + + /* + * First all the GPIO pins that end up as SDRAM pins + */ + rcc_periph_clock_enable(RCC_GPIOB); + rcc_periph_clock_enable(RCC_GPIOC); + rcc_periph_clock_enable(RCC_GPIOD); + rcc_periph_clock_enable(RCC_GPIOE); + rcc_periph_clock_enable(RCC_GPIOF); + rcc_periph_clock_enable(RCC_GPIOG); + + 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_periph_clock_enable(RCC_FSMC); + + /* 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); + /* sleep at least 100uS */ + msleep(1); +/* + for (i = 0; i < 1000; i++) { + __asm("nop"); + } +*/ + 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/stm32f429i-discovery/mandelbrot-lcd/sdram.h b/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/sdram.h new file mode 100644 index 0000000..8b0fec9 --- /dev/null +++ b/examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/sdram.h @@ -0,0 +1,27 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014-2015 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); +#endif