Adding an example to drive a small oled display with spi

This commit is contained in:
dwelch67
2014-11-08 01:14:30 -05:00
parent 3451556310
commit abbc87202f
6 changed files with 3459 additions and 0 deletions

32
spi02/Makefile Normal file
View File

@@ -0,0 +1,32 @@
ARMGNU ?= arm-none-eabi
COPS = -mthumb -Wall -O2 -nostdlib -nostartfiles -ffreestanding
all : spi02.hex spi02.bin
clean :
rm -f *.o
rm -f *.bin
rm -f *.hex
rm -f *.elf
rm -f *.list
rm -f *.img
novectors.o : novectors.s
$(ARMGNU)-as novectors.s -o novectors.o
spi02.o : spi02.c fontdata.h
$(ARMGNU)-gcc $(COPS) -c spi02.c -o spi02.o
spi02.elf : memmap novectors.o spi02.o
$(ARMGNU)-ld novectors.o spi02.o -T memmap -o spi02.elf
$(ARMGNU)-objdump -D spi02.elf > spi02.list
spi02.bin : spi02.elf
$(ARMGNU)-objcopy spi02.elf -O binary spi02.bin
spi02.hex : spi02.elf
$(ARMGNU)-objcopy spi02.elf -O ihex spi02.hex

190
spi02/README Normal file
View File

@@ -0,0 +1,190 @@
See the top level README for information on where to find the
schematic and programmers reference manual for the ARM processor
on the raspberry pi. Also find information on how to load and run
these programs.
See the top level README for information on how to connect the raspi
uart to your host computer.
This example is using the raspberry pi spi interface to program
a nice little oled display
https://www.sparkfun.com/products/13003
Well, I would prefer something other than blue but I think OLED is
pretty cool stuff so anyway...
Perhaps some soldering is required, in some way you need to hook up
the signals.
Raspberry Pi signals of interest, all on the P1 connector
GPIO7 SPI0_CE1_N P1-26
GPIO8 SPI0_CE0_N P1-24
GPIO9 SPI0_MISO P1-21
GPIO10 SPI0_MOSI P1-19
GPIO11 SPI0_SCLK P1-23
alt function 0 for all of the above
GPIO25 GPIO_GEN6 P1-22
+3V3 P1-1
GND P1-25
Micro OLED Breakout to raspi
GND P1-25
VCC P1-1
D1/SDI (MOSI) P1-19
D0/SCK P1-23
D2/SDO (MISO) no connect
D/C P1-22
RST P1-26
CS P1-24
I soldered a row of pins on both sides of the oled board only to RTFM
later and find out I only needed the ones on the side not covered by
the ribbon cable.
I took the init routine and the defines for the command from the
sparkfun arduino example. The rest is mine. (something in their
init or the way the thing works you need to add 0x20 to the column
number, I didnt research that, it is most likely as with other
panel devices, the controller probably handles a display with more
pixels but this display has however many it has less than that and
is aligned in some way).
I could have done the reset with a gpio pin as a gpio pin rather as
a chip select but in my prior spi example I had used cs1 as a reset
and was hoping to get away with not driving D/C but later figured out
I had to. It works.
Now the datasheet for the display is confusing as to the 3 and 4 wire
spi protocols. Not quite sure how the device knows one mode from
the other. From what I can tell of the raspberry pi spi controller
that I am using you can only send in units of 8 bits at a time so
it worked out. If I had to do 9 bits then it would have been time
to bit bang or come up with some other solution.
I assume that the reference clock for the spi controller is the 250MHz
used by everything else. The datasheet says max of 100ns for the
clock cycle which is 10MHz. 250MHz/25 = 10MHz, we cant divide by
an odd number so they say so I divided by 26. And so far it appears
to work.
I couldnt quite visualize from the sparkfun data of their logo how the
data was laid out, so I did some experiments. You will hopefully see
from the text I display in my example that the data bytes are a
vertical row of 8 bits, so to display text, you slice your text
vertically. I guessed wrong the first try and my characters were
upside down, so swizzled the bytes. The uncommented lines are the
reversed bytes.
//---- 0x65 ----
//[ ]
//[ ]
//[ ##### ]
//[## ## ]
//[####### ]
//[## ]
//[ ##### ]
//[
//{0x1C,0x3E,0x2A,0x2A,0x2A,0x3A,0x18,0x00},
{0x38,0x7C,0x54,0x54,0x54,0x5C,0x18,0x00},
If you turn your head to the left 90 degrees and look at the top
of the letter you see 00111000 which is a 0x38. Next row (with your
head to the left it is a row) 01111100 or 0x7C and so on.
There appear to be 64 pixels wide, with an 8x8 font that is 8 characters
(the perfect amount for displaying a 32 bit hex number!) and 48 tall
so with an 8x8 font that is 6 rows. And that is how you address this
thing they call the rows pages...
You can set your column to some number and start from there but for
all of these I start the column at 0, it appears that as you write data
it auto-increments the column address for you so you set the page (row)
and the column and then just blast bytes to it to paint 8 rows at once
across.
I didnt explain this in the spi01 example, the spi controller on the
raspberry pi is fairly simple. You of course have to talk to the
gpio controller to select the spi alternate function for all of the
spi controller signals you want to use.
When setting the TA bit with a CS number selected it asserts the
chip select, which you do before clocking out any bytes, and then
clear the ta bit to de-assert the chip select. Understand some basic
terminology, assert doesnt always mean high or a 1 or a non-zero positive
voltage. The chip select is asserted low, so off is the 3.3Volt state
and on, asserted, is at 0Volts. both the raspberry pi and display
datasheets show CS low asserted in the pictures and that is what
is going on here. I play a game with chip select 1, which is normally
not asserted as a high output, I set the invert polarity bit in the
control register which tells it that not asserted is low, making it
output a 0 on that pin, then later I undo that and make it high again
causing a reset (which is also asserted low) to the display. Not sure
if I really needed to do a reset and wire that up, maybe I could
have used the CS1 for the D/C instead of the way I did it. Oh well, that
is an exercise for the reader...
So after you set the TA bit to assert chip select. Then for every write
to the fifo, it takes the lower 8 bits and puts those in the fifo. In
this case I mimiced the sparkfun example and only send 8 bits per
chip select. If you look at the display data sheet it shows multiple
bytes per chip select is supported. The one byte per is certainly
easier to code up and use. So that one byte is one write to the fifo
register. This is clocking out pretty slow relative to the speed of
the arm so you have to wait for it to clock out the waveform by checking
bit 16 of the control register. Once done you can zero the TA bit to
raise chip select.
Wrap that with having the D/C bit high or low before you start and
you can send commands or data.
The datasheet command table is not immediately obvious in how
the commands wor either. You will see lots of individual lines for
example that are some 0xA0 + 0-7 numbers. They key to is that there
are some visual separations using a horizontal line, for the command
in question you have to send all the line items for that command, the
first one is unique to the command and the ones that follow the
controller is expecting. So you need to make sure you dont short
the controller some data otherwise it is out of sync and all messed
up. For example the first one set contrast control first you send
a command (D/C is a 0) of 0x81, then you send a command (D/C is a 0)
of the contrast value between 0x00 and 0xFF. And no in that case it
is not a 0xA0 with the value 0-7 in the lower bits it is just trying
to show you there are 8 bits...
I tried messing with the contrast and certainly didnt see 256 settings
it seems to go from nothing to on pretty soon and doesnt change
much after that, perhaps there are other settings that affect the
contrast/brightness ultimately maybe a multiplier somewhere...
So since I chose to write fonts with this you can see the program will
write
HELLO
World!
12345678
ABCDEF00
On the first four rows, then on the last row will count for a while so
you can see the Hello World. Then I made a simple hex value scroller
that runs for a bit and there you go, you can see the last 6 values.
You can certainly do more than write text on this, its just pixels,
draw whatever you want. The controller on the display has features
for scrolling and perhaps other things. I didnt mess with those.

2821
spi02/fontdata.h Normal file

File diff suppressed because it is too large Load Diff

12
spi02/memmap Normal file
View File

@@ -0,0 +1,12 @@
MEMORY
{
ram : ORIGIN = 0x8000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > ram
.bss : { *(.bss*) } > ram
}

49
spi02/novectors.s Normal file
View File

@@ -0,0 +1,49 @@
.globl _start
_start:
ldr sp,stack_start
ldr r0,thumb_start_add
bx r0
stack_start: .word 0x8000
thumb_start_add: .word thumb_start
.word 0
.word 0
.thumb
.thumb_func
thumb_start:
bl notmain
hang: b hang
.thumb_func
.globl PUT32
PUT32:
str r1,[r0]
bx lr
.thumb_func
.globl GET32
GET32:
ldr r0,[r0]
bx lr
.thumb_func
.globl dummy
dummy:
bx lr
;@-------------------------------------------------------------------------
;@
;@ Copyright (c) 2012 David Welch dwelch@dwelch.com
;@
;@ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
;@
;@ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
;@
;@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
;@
;@-------------------------------------------------------------------------

355
spi02/spi02.c Normal file
View File

@@ -0,0 +1,355 @@
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
#include "fontdata.h"
extern void PUT32 ( unsigned int, unsigned int );
extern unsigned int GET32 ( unsigned int );
extern void dummy ( unsigned int );
#define GPFSEL0 0x20200000
#define GPFSEL1 0x20200004
#define GPFSEL2 0x20200008
#define GPSET0 0x2020001C
#define GPCLR0 0x20200028
#define GPPUD 0x20200094
#define GPPUDCLK0 0x20200098
#define AUX_ENABLES 0x20215004
#define AUX_MU_IO_REG 0x20215040
#define AUX_MU_IER_REG 0x20215044
#define AUX_MU_IIR_REG 0x20215048
#define AUX_MU_LCR_REG 0x2021504C
#define AUX_MU_MCR_REG 0x20215050
#define AUX_MU_LSR_REG 0x20215054
#define AUX_MU_MSR_REG 0x20215058
#define AUX_MU_SCRATCH 0x2021505C
#define AUX_MU_CNTL_REG 0x20215060
#define AUX_MU_STAT_REG 0x20215064
#define AUX_MU_BAUD_REG 0x20215068
#define AUX_SPI0_CS 0x20204000
#define AUX_SPI0_FIFO 0x20204004
#define AUX_SPI0_CLK 0x20204008
#define AUX_SPI0_DLEN 0x2020400C
#define AUX_SPI0_LTOH 0x20204010
#define AUX_SPI0_DC 0x20204014
#define SETCONTRAST 0x81
#define DISPLAYALLONRESUME 0xA4
#define DISPLAYALLON 0xA5
#define NORMALDISPLAY 0xA6
#define INVERTDISPLAY 0xA7
#define DISPLAYOFF 0xAE
#define DISPLAYON 0xAF
#define SETDISPLAYOFFSET 0xD3
#define SETCOMPINS 0xDA
#define SETVCOMDESELECT 0xDB
#define SETDISPLAYCLOCKDIV 0xD5
#define SETPRECHARGE 0xD9
#define SETMULTIPLEX 0xA8
#define SETLOWCOLUMN 0x00
#define SETHIGHCOLUMN 0x10
#define SETSTARTLINE 0x40
#define MEMORYMODE 0x20
#define COMSCANINC 0xC0
#define COMSCANDEC 0xC8
#define SEGREMAP 0xA0
#define CHARGEPUMP 0x8D
#define EXTERNALVCC 0x01
#define SWITCHCAPVCC 0x02
unsigned int hex_screen_history[6];
//GPIO14 TXD0 and TXD1
//GPIO15 RXD0 and RXD1
//alt function 5 for uart1
//alt function 0 for uart0
//((250,000,000/115200)/8)-1 = 270
//------------------------------------------------------------------------
void uart_init ( void )
{
unsigned int ra;
ra=GET32(AUX_ENABLES);
ra|=1; //enable mini uart
PUT32(AUX_ENABLES,ra);
PUT32(AUX_MU_IER_REG,0);
PUT32(AUX_MU_CNTL_REG,0);
PUT32(AUX_MU_LCR_REG,3);
PUT32(AUX_MU_MCR_REG,0);
PUT32(AUX_MU_IER_REG,0);
PUT32(AUX_MU_IIR_REG,0xC6);
PUT32(AUX_MU_BAUD_REG,270);
//setup gpio before enabling uart
ra=GET32(GPFSEL1);
ra&=~(7<<12); //gpio14
ra|=2<<12; //alt5
ra&=~(7<<15); //gpio15
ra|=2<<15; //alt5
PUT32(GPFSEL1,ra);
PUT32(GPPUD,0);
for(ra=0;ra<150;ra++) dummy(ra);
PUT32(GPPUDCLK0,(1<<14)|(1<<15));
for(ra=0;ra<150;ra++) dummy(ra);
PUT32(GPPUDCLK0,0);
//enable uart
PUT32(AUX_MU_CNTL_REG,3);
}
//------------------------------------------------------------------------
void uart_putc ( unsigned int c )
{
while(1)
{
if(GET32(AUX_MU_LSR_REG)&0x20) break;
}
PUT32(AUX_MU_IO_REG,c);
}
//------------------------------------------------------------------------
void hexstrings ( unsigned int d )
{
//unsigned int ra;
unsigned int rb;
unsigned int rc;
rb=32;
while(1)
{
rb-=4;
rc=(d>>rb)&0xF;
if(rc>9) rc+=0x37; else rc+=0x30;
uart_putc(rc);
if(rb==0) break;
}
uart_putc(0x20);
}
//------------------------------------------------------------------------
void hexstring ( unsigned int d )
{
hexstrings(d);
uart_putc(0x0D);
uart_putc(0x0A);
}
//GPIO7 SPI0_CE1_N P1-26 (use for reset)
//GPIO8 SPI0_CE0_N P1-24
//GPIO9 SPI0_MISO P1-21
//GPIO10 SPI0_MOSI P1-19
//GPIO11 SPI0_SCLK P1-23
//alt function 0 for all of the above
//P1 1 +3V3
//P1 25 GND
//P1 22 GPIO25 use as D/C
//------------------------------------------------------------------------
void spi_init ( void )
{
unsigned int ra;
ra=GET32(AUX_ENABLES);
ra|=2; //enable spi0
PUT32(AUX_ENABLES,ra);
ra=GET32(GPFSEL0);
ra&=~(7<<27); //gpio9
ra|=4<<27; //alt0
ra&=~(7<<24); //gpio8
ra|=4<<24; //alt0
ra&=~(7<<21); //gpio7
ra|=4<<21; //alt0
PUT32(GPFSEL0,ra);
ra=GET32(GPFSEL1);
ra&=~(7<<0); //gpio10/
ra|=4<<0; //alt0
ra&=~(7<<3); //gpio11/
ra|=4<<3; //alt0
PUT32(GPFSEL1,ra);
ra=GET32(GPFSEL2);
ra&=~(7<<15); //gpio25/
ra|=1<<15; //output
PUT32(GPFSEL2,ra);
PUT32(AUX_SPI0_CS,0x0000030);
// PUT32(AUX_SPI0_CLK,0x0000); //slowest possible, could probably go faster here
PUT32(AUX_SPI0_CLK,26);
}
//------------------------------------------------------------------------
void spi_one_byte ( unsigned int x )
{
PUT32(AUX_SPI0_CS,0x000000B0); //TA=1 cs asserted
while(1)
{
if(GET32(AUX_SPI0_CS)&(1<<18)) break; //TXD
}
PUT32(AUX_SPI0_FIFO,x&0xFF);
while(1) if(GET32(AUX_SPI0_CS)&(1<<16)) break;
//while(1) if(GET32(AUX_SPI0_CS)&(1<<17)) break; //should I wait for this?
PUT32(AUX_SPI0_CS,0x00000000); //cs0 comes back up
}
//------------------------------------------------------------------------
void spi_command ( unsigned int cmd )
{
PUT32(GPCLR0,1<<25); //D/C = 0 for command
spi_one_byte(cmd);
}
//------------------------------------------------------------------------
void spi_data ( unsigned int data )
{
PUT32(GPSET0,1<<25); //D/C = 1 for data
spi_one_byte(data);
}
//------------------------------------------------------------------------
void SetPageStart ( unsigned int x )
{
spi_command(0xB0|(x&0x07));
}
//------------------------------------------------------------------------
void SetColumn ( unsigned int x )
{
x+=0x20;
spi_command(0x10|((x>>4)&0x0F));
spi_command(0x00|((x>>0)&0x0F));
}
//------------------------------------------------------------------------
void show_text(unsigned int col, char *s)
{
unsigned int ra;
SetPageStart(col);
SetColumn(0);
while(*s)
{
for(ra=0;ra<8;ra++) spi_data(fontdata[(unsigned)(*s)][ra]);
s++;
}
}
//------------------------------------------------------------------------
void show_text_hex(unsigned int col, unsigned int x)
{
unsigned int ra;
unsigned int rb;
unsigned int rc;
SetPageStart(col);
SetColumn(0);
for(ra=0;ra<8;ra++)
{
rb=(x>>28)&0xF;
if(rb>9) rb+=0x37; else rb+=0x30;
x<<=4;
for(rc=0;rc<8;rc++) spi_data(fontdata[rb][rc]);
}
}
//------------------------------------------------------------------------
void hex_screen ( unsigned int x )
{
unsigned int ra;
for(ra=0;ra<5;ra++)
{
hex_screen_history[ra]=hex_screen_history[ra+1];
show_text_hex(ra,hex_screen_history[ra]);
}
hex_screen_history[ra]=x;
show_text_hex(ra,hex_screen_history[ra]);
}
//------------------------------------------------------------------------
void ClearScreen ( void )
{
unsigned int ra;
unsigned int rb;
for(ra=0;ra<8;ra++)
{
SetPageStart(ra);
SetColumn(0);
for(rb=0;rb<0x80;rb++) spi_data(0);
}
}
//------------------------------------------------------------------------
int notmain ( void )
{
unsigned int ra;
uart_init();
hexstring(0x12345678);
for(ra=0;ra<10;ra++)
{
hexstring(ra);
}
spi_init();
PUT32(AUX_SPI0_CS,0x00000000); //cs1 high
for(ra=0;ra<0x10000;ra++) dummy(ra);
PUT32(AUX_SPI0_CS,0x00400000); //cs1 low, reset
for(ra=0;ra<0x10000;ra++) dummy(ra);
PUT32(AUX_SPI0_CS,0x00000000); //cs1 comes back up
// Display Init sequence for 64x48 OLED module
spi_command(DISPLAYOFF); // 0xAE
spi_command(SETDISPLAYCLOCKDIV); // 0xD5
spi_command(0x80); // the suggested ratio 0x80
spi_command(SETMULTIPLEX); // 0xA8
spi_command(0x2F);
spi_command(SETDISPLAYOFFSET); // 0xD3
spi_command(0x0); // no offset
spi_command(SETSTARTLINE | 0x0); // line #0
spi_command(CHARGEPUMP); // enable charge pump
spi_command(0x14);
spi_command(NORMALDISPLAY); // 0xA6
spi_command(DISPLAYALLONRESUME); // 0xA4
spi_command(SEGREMAP | 0x1);
spi_command(COMSCANDEC);
spi_command(SETCOMPINS); // 0xDA
spi_command(0x12);
spi_command(SETCONTRAST); // 0x81
spi_command(0x7F);
spi_command(SETPRECHARGE); // 0xd9
spi_command(0xF1);
spi_command(SETVCOMDESELECT); // 0xDB
spi_command(0x40);
ClearScreen();
spi_command(DISPLAYON); //--turn on oled panel
//spi_command(DISPLAYALLON);
show_text(0,"HELLO");
show_text(1,"World!");
show_text_hex(2,0x12345678);
show_text_hex(3,0xABCDEF00);
for(ra=0;ra<=0x10000;ra++) show_text_hex(5,ra);
//ClearScreen();
for(ra=0;ra<6;ra++) hex_screen_history[ra]=0;
for(ra=0;ra<0x1000;ra++) hex_screen(ra);
hexstring(0x12345678);
return(0);
}
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
//
// Copyright (c) 2014 David Welch dwelch@dwelch.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//-------------------------------------------------------------------------