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.
This commit is contained in:
committed by
Piotr Esden-Tempski
parent
4de8d15303
commit
22f59c4583
@@ -0,0 +1,27 @@
|
||||
##
|
||||
## This file is part of the libopencm3 project.
|
||||
##
|
||||
## Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
|
||||
##
|
||||
## This library is free software: you can redistribute it and/or modify
|
||||
## it under the terms of the GNU Lesser General Public License as published by
|
||||
## the Free Software Foundation, either version 3 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This library is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU Lesser General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU Lesser General Public License
|
||||
## along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
|
||||
OBJS = sdram.o lcd.o clock.o
|
||||
|
||||
BINARY = mandel
|
||||
|
||||
LDSCRIPT = ../stm32f429i-discovery.ld
|
||||
|
||||
include ../../Makefile.include
|
||||
|
||||
@@ -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.
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* This file is part of the libopencm3 project.
|
||||
*
|
||||
* Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
|
||||
* Copyright (C) 2011 Stephen Caudle <scaudle@doceme.com>
|
||||
* Copyright (C) 2012 Daniel Serpell <daniel.serpell@gmail.com>
|
||||
* Copyright (C) 2015 Piotr Esden-Tempski <piotr@esden.net>
|
||||
* Copyright (C) 2015 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 <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <libopencm3/stm32/rcc.h>
|
||||
#include <libopencm3/stm32/gpio.h>
|
||||
#include <libopencm3/stm32/usart.h>
|
||||
#include <libopencm3/cm3/systick.h>
|
||||
#include <libopencm3/cm3/nvic.h>
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* This file is part of the libopencm3 project.
|
||||
*
|
||||
* Copyright (C) 2014-2015 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* clock functions
|
||||
*/
|
||||
|
||||
extern void clock_setup(void);
|
||||
extern void msleep(uint32_t);
|
||||
extern uint32_t mtime(void);
|
||||
|
||||
344
examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/lcd.c
Normal file
344
examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/lcd.c
Normal file
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
* This file is part of the libopencm3 project.
|
||||
*
|
||||
* Copyright (C) 2014-2015 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 <stdio.h>
|
||||
#include <libopencm3/stm32/spi.h>
|
||||
#include <libopencm3/stm32/rcc.h>
|
||||
#include <libopencm3/stm32/gpio.h>
|
||||
#include <libopencm3/cm3/nvic.h>
|
||||
#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();
|
||||
}
|
||||
|
||||
48
examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/lcd.h
Normal file
48
examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/lcd.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* This file is part of the libopencm3 project.
|
||||
*
|
||||
* Copyright (C) 2014-2015 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_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
|
||||
227
examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/mandel.c
Normal file
227
examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/mandel.c
Normal file
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* This file is part of the libopencm3 project.
|
||||
*
|
||||
* Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
|
||||
* Copyright (C) 2011 Stephen Caudle <scaudle@doceme.com>
|
||||
* Copyright (C) 2012 Daniel Serpell <daniel.serpell@gmail.com>
|
||||
* Copyright (C) 2015 Piotr Esden-Tempski <piotr@esden.net>
|
||||
* Copyright (C) 2015 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 <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <libopencm3/stm32/rcc.h>
|
||||
#include <libopencm3/stm32/gpio.h>
|
||||
#include <libopencm3/stm32/usart.h>
|
||||
#include <libopencm3/cm3/systick.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
144
examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/sdram.c
Normal file
144
examples/stm32/f4/stm32f429i-discovery/mandelbrot-lcd/sdram.c
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* This file is part of the libopencm3 project.
|
||||
*
|
||||
* Copyright (C) 2014-2015 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"
|
||||
|
||||
#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 */
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* This file is part of the libopencm3 project.
|
||||
*
|
||||
* Copyright (C) 2014-2015 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);
|
||||
#endif
|
||||
Reference in New Issue
Block a user