Part Number: TMS320F28069M
/**
* SPI DMA transfer to a SSD1306 OLED display
*
* The example transfers 8 bits of data internally with the Digital LoopBack(DLB)
* with the McBSP module in SPI Master mode with 8 bit DMA support.
*
* The waveforms are observable on the following McBSP pins:
*
* SSD1306 McBSP GPIO Pin Signal
* -------------------------------------------------
* RESET -> GPIO23 -> Pin#49
* D/C -> GPIO44 -> Pin#18
* SCLK -> MCLKX -> GPIO22 -> Pin#08 -> SPICLK
* MOSI -> MDX -> GPIO20 -> Pin#45 -> SPISIMO
*
* By default for the McBSP examples, the McBSP sample rate generator (SRG)
* input clock frequency is LSPCLK 80E6/4.
*/
#include "DSP28x_Project.h"
#define LCDWIDTH 128 /* Cols 0 - 127 */
#define LCDHEIGHT 64 /* Rows 0 - 63 */
#define LAST_PAGE 7 /* Page end address */
#define LCD_PIXELS (LCDWIDTH * LCDHEIGHT)
#define LCD_BYTES (LCD_PIXELS / 8)
#define LAST_COL (LCDWIDTH)
#define LAST_ROW (LCDHEIGHT)
#pragma DATA_SECTION(rdata, "DMARAML5") /* buffer in DMA L5 SRAM */
Uint16 rdata[1024]; /* Received Data */
#pragma DATA_SECTION(sdata, "DMARAML5") /* buffer in DMA L5 SRAM */
Uint16 sdata[128]; /* Send Data */
#define SETLOWCOLUMN 0x00
#define EXTERNALVCC 0x01
#define SWITCHCAPVCC 0x02
#define SETHIGHCOLUMN 0x10
#define MEMORYMODE 0x20
#define COLUMNADDR 0x21
#define PAGEADDR 0x22
#define SCROLL_ENABLE 0x2F
#define SCROLL_DISABLE 0x2E
#define SCROLL_SET_VERT_AREA 0xA3
#define SCROLL_RIGHT_HORIZ 0x26
#define SCROLL_LEFT_HORIZONTAL 0x27
#define SCROLL_VERT_RIGHT_HORIZ 0x29
#define SCROLL_VERT_LEFT_HORIZ 0x2A
#define SETSTARTLINE 0x40
#define SETCONTRAST 0x81
#define CHARGEPUMP 0x8D
#define SEGREMAP 0xA0
#define SETSEGMENTREMAP 0xA1
#define DISPLAYALLON_RESUME 0xA4
#define DISPLAYALLON 0xA5
#define NORMALDISPLAY 0xA6
#define INVERTDISPLAY 0xA7
#define SETMULTIPLEX 0xA8
#define DISPLAYOFF 0xAE
#define DISPLAYON 0xAF
#define COMSCANINC 0xC0
#define COMSCANDEC 0xC8
#define SETDISPLAYOFFSET 0xD3
#define SETDISPLAYCLOCKDIV 0xD5
#define SETPRECHARGE 0xD9
#define SETCOMPINS 0xDA
#define SETVCOMDETECT 0xDB
#pragma DATA_SECTION(init, "DMARAML5"); /* dmatab in DMA L5 SRAM */
Uint16 init[] = {
0xae, /* DISPLAYOFF */
0xd5, /* SETDISPLAYCLOCKDIV to 0x80 */
0x80,
0xa8, /* SETMULTIPLEX */
LCDHEIGHT - 1,
0xd3, /* SETDISPLAYOFFSET to 0x00 */
0x00,
0x40, /* line #0 | SETSTARTLINE */
0x8d, /* CHARGEPUMP setup */
0x14,
0x20, /* MEMORYMODE addressing */
0x00,
0xa0 | 0x1, /* SEGREMAP */
0xc8, /* COMSCANDEC */
0xda, /* SETCOMPINS */
0x12,
0x81, /* SETCONTRAST to 0xef */
0xcf,
0xd9, /* SETPRECHARGE period */
0xf1,
0xdb, /* SETVCOMDETECT level */
0x40,
0xa4, /* DISPLAYALLON_RESUME */
0xa6, /* NORMALDISPLAY */
0x2e, /* SCROLL_DISABLE */
0xaf, /* DISPLAYON */
};
#define BLACK 0
#define WHITE 1
#define INVERSE 2
typedef int int16_t;
typedef int int8_t;
typedef Uint8 uint8_t;
typedef unsigned int uint16_t;
typedef unsigned char uchar;
volatile enum dmaio_state {
IO_COMPLETE = 0,
IO_PENDING = 1,
} io_state;
#define ssd1306_swap(a, b) { int16_t t = a; a = b; b = t; }
#pragma DATA_SECTION(fb, "DMARAML5") /* buffer in DMA L5 SRAM */
Uint16 fb[1024];
/**
* MCBSP_INIT_DELAY determines the amount of CPU cycles in the 2 sample rate
* generator (SRG) cycles required for the Mcbsp initialization routine.
* MCBSP_CLKG_DELAY determines the amount of CPU cycles in the 2 clock
* generator (CLKG) cycles required for the Mcbsp initialization routine.
*/
#define CPU_SPD 80E6
#define MCBSP_SRG_FREQ CPU_SPD / 2 /* SRG input = LSPCLK (SYSCLKOUT/2) */
#define MCBSP_DELAY 2*(CPU_SPD/MCBSP_SRG_FREQ) /* CPU cycles in 2 SRG init delay */
void delay_loop(void)
{
long i;
for (i = 0; i < MCBSP_DELAY; i++) { } /* must be at least 2 SRG cycles */
}
void error(void)
{
__asm(" ESTOP0"); /* STOP on Error */
for (;;);
}
void init_mcbspa_gpio(void)
{
EALLOW;
GpioCtrlRegs.GPAMUX2.bit.GPIO20 = 2; /* GPIO20=MDXA, data tx, MOSI */
GpioCtrlRegs.GPAMUX2.bit.GPIO22 = 2; /* GPIO22=CLKXA, clk tx, SCL */
GpioCtrlRegs.GPAQSEL2.bit.GPIO22 = 3; /* Asynch input GPIO22 (MCLKXA) */
EDIS;
}
void dma_xfer(Uint16 *src, Uint16 len)
{
io_state = IO_PENDING; /* dont allow concurrent transfers */
EALLOW;
DmaRegs.DMACTRL.bit.HARDRESET = 1;
__asm(" NOP"); /* Only 1 NOP needed per design */
/**
* Channel 1, McBSPA Transmit
*/
DmaRegs.CH1.MODE.bit.CHINTE = 0;
DmaRegs.CH1.BURST_SIZE.all = 0; /* 1 word/burst */
DmaRegs.CH1.SRC_BURST_STEP = 0; /* no effect with 1 word/burst */
DmaRegs.CH1.DST_BURST_STEP = 0; /* no effect with 1 word/burst */
DmaRegs.CH1.TRANSFER_SIZE = len; /* Interrupt after len */
DmaRegs.CH1.SRC_TRANSFER_STEP = 1; /* Increment address on every word */
DmaRegs.CH1.DST_TRANSFER_STEP = 0; /* Don't change detination */
DmaRegs.CH1.SRC_ADDR_SHADOW = (Uint32) src; /* Source is buffer */
DmaRegs.CH1.SRC_BEG_ADDR_SHADOW = (Uint32) src; /* Only for wrap fn */
DmaRegs.CH1.DST_ADDR_SHADOW = (Uint32) &McbspaRegs.DXR1.all; /* Dest. is McBSPA DXR */
DmaRegs.CH1.DST_BEG_ADDR_SHADOW = (Uint32) &McbspaRegs.DXR1.all;/* Only for wrap fn */
DmaRegs.CH1.CONTROL.bit.PERINTCLR = 1; /* Clear interrupt event flag */
DmaRegs.CH1.CONTROL.bit.ERRCLR = 1; /* Clear sync error flag */
DmaRegs.CH1.DST_WRAP_SIZE = 0xffff; /* Max len, no destination wrap */
DmaRegs.CH1.SRC_WRAP_SIZE = 0xffff; /* Max len, no source wrap */
DmaRegs.CH1.MODE.bit.CHINTE = 1; /* Enable channel interrupt */
DmaRegs.CH1.MODE.bit.CHINTMODE = 1; /* Interrupt at end of transfer */
DmaRegs.CH1.MODE.bit.PERINTE = 1; /* Enable peripheral interrupt */
DmaRegs.CH1.MODE.bit.PERINTSEL = DMA_MXEVTA; /* McBSPA peripheral, MXSYNCA int */
DmaRegs.CH1.CONTROL.bit.PERINTCLR = 1; /* Clear spurious interrupt flags */
/**
* Channel 2, McBSPA Receive
* We do really need to read, if we don't read the next SPI frame fails
* Save reception buffer size by reading the burst into the same buffer
*/
DmaRegs.CH2.MODE.bit.CHINTE = 0;
DmaRegs.CH2.BURST_SIZE.all = 0; /* 1 word/burst */
DmaRegs.CH2.SRC_BURST_STEP = 0; /* no effect with 1 word/burst */
DmaRegs.CH2.DST_BURST_STEP = 0; /* no effect with 1 word/burst */
DmaRegs.CH2.TRANSFER_SIZE = len; /* Interrupt after len */
DmaRegs.CH2.SRC_TRANSFER_STEP = 0; /* Don't move source address */
DmaRegs.CH2.DST_TRANSFER_STEP = 1; /* Don't move desination address */
DmaRegs.CH2.SRC_ADDR_SHADOW = (Uint32) &McbspaRegs.DRR1.all; /* Source is McBSPA DRR */
DmaRegs.CH2.SRC_BEG_ADDR_SHADOW = (Uint32) &McbspaRegs.DRR1.all;/* Only for wrap fn */
DmaRegs.CH2.DST_ADDR_SHADOW = (Uint32) &rdata[0]; /* Dest. is Buffer */
DmaRegs.CH2.DST_BEG_ADDR_SHADOW = (Uint32) &rdata[0]; /* Only for wrap fn */
DmaRegs.CH2.CONTROL.bit.PERINTCLR = 1; /* Clear interrupt event flag */
DmaRegs.CH2.CONTROL.bit.ERRCLR = 1; /* Clear sync error flag */
DmaRegs.CH2.DST_WRAP_SIZE = 0xFFFF; /* Max len, no destination wrap */
DmaRegs.CH2.SRC_WRAP_SIZE = 0xFFFF; /* Max len, no source wrap */
DmaRegs.CH2.MODE.bit.CHINTE = 1; /* Enable channel interrupt */
DmaRegs.CH2.MODE.bit.CHINTMODE = 1; /* Interrupt at end of transfer */
DmaRegs.CH2.MODE.bit.PERINTE = 1; /* Enable peripheral interrupt */
DmaRegs.CH2.MODE.bit.PERINTSEL = DMA_MREVTA; /* McBSPA peripheral, MRSYNCA int */
DmaRegs.CH2.CONTROL.bit.PERINTCLR = 1; /* Clear spurious interrupt flags */
/* start DMA */
DmaRegs.CH1.CONTROL.bit.RUN = 1; /* Start McBSPA DMA Transmission */
DmaRegs.CH2.CONTROL.bit.RUN = 1; /* Start McBSPA DMA Reception */
EDIS;
/* init SPI peripheral */
McbspaRegs.SPCR2.all = 0x0000; /* Reset FS gen, SRG & Tx */
McbspaRegs.SPCR1.all = 0x0000; /* Reset Rx, Right justify, DLB dis */
McbspaRegs.PCR.all = 0x0F08; /* (CLKXM=CLKRM=FSXM=FSRM=1,FSXP=1) */
McbspaRegs.SPCR1.bit.DLB = 1;
McbspaRegs.MFFINT.all = 0x0; /* Disable all interrupts */
McbspaRegs.SPCR1.bit.CLKSTP = 3; /* clock schema with CLKXP/CLKRP */
McbspaRegs.PCR.bit.CLKXP = 0; /* CPOL=0,CPHA=1,rise edge,no delay */
McbspaRegs.PCR.bit.CLKRP = 0; /* if CLKSTP=2, then CPHA=0 */
McbspaRegs.RCR2.bit.RDATDLY = 01; /* FSX setup time 1 = master mode */
McbspaRegs.XCR2.bit.XDATDLY = 01; /* FSX setup time 1 = master mode */
McbspaRegs.RCR1.bit.RWDLEN1 = 0; /* 5=32-bit word */
McbspaRegs.XCR1.bit.XWDLEN1 = 0; /* 5=32-bit word */
McbspaRegs.SRGR2.all = 0x2000; /* CLKSM=1, FPER=1 CLKG periods */
McbspaRegs.SRGR1.all = 0x000f; /* Frame Width=1, CLKGDV=16; 0x0f */
McbspaRegs.SPCR2.bit.GRST = 1; /* Enable the sample rate generator */
delay_loop(); /* Wait at least 2 SRG clock cycles */
McbspaRegs.SPCR2.bit.XRST = 1; /* Release TX from Reset */
McbspaRegs.SPCR1.bit.RRST = 1; /* Release RX from Reset */
McbspaRegs.SPCR2.bit.FRST = 1; /* Frame Sync Generator reset */
}
void ssd1306_reset(void)
{
GpioDataRegs.GPACLEAR.bit.GPIO23 = 1; /* RESET = 0 */
DELAY_US(10000); /* wait 10ms */
GpioDataRegs.GPASET.bit.GPIO23 = 1; /* RESET = 1 */
DELAY_US(50000); /* wait 50mS */
}
void ssd1306_init(void)
{
GpioDataRegs.GPBCLEAR.bit.GPIO44 = 1; /* D/C default LOW (CMD MODE) */
dma_xfer(init, sizeof(init)); /* SSD1306 Initialition */
}
void setup_lcd_pins(void)
{
EALLOW;
GpioCtrlRegs.GPAPUD.bit.GPIO23 = 1; /* disable pullup for RESET */
GpioCtrlRegs.GPBPUD.bit.GPIO44 = 1; /* disable pullup for D/C */
GpioCtrlRegs.GPAMUX2.bit.GPIO23 = 0; /* setup GPIO::RESET */
GpioCtrlRegs.GPBMUX1.bit.GPIO44 = 0; /* setup GPIO::D/C */
GpioCtrlRegs.GPADIR.bit.GPIO23 = 1; /* setup Output::Reset */
GpioCtrlRegs.GPBDIR.bit.GPIO44 = 1; /* setup Output::D/C */
EDIS;
GpioDataRegs.GPASET.bit.GPIO22 = 1; /* RESET default HIGH (Release) */
}
void lcd_fb_put(void)
{
while (io_state != IO_COMPLETE); /* wait for dma completion */
GpioDataRegs.GPBSET.bit.GPIO44 = 1; /* D/C default HIGH (DATA MODE) */
dma_xfer(fb, sizeof(fb)); /* SSD1306 Graphic data */
}
void fb_clear(void)
{
Uint8 i;
while (io_state != IO_COMPLETE); /* wait for dma completion */
for (i = 0; i < (LCDWIDTH * LCDHEIGHT / 8); i++)
fb[i] = 0x00;
}
/**
* Set window region for CGRAM access
* void lcd_set_window(uint16_t c_start,
* uint16_t p_start,
* uint16_t c_stop,
* uint16_t p_stop)
*
* @uint16_t c_start : COLUMN start address
* @uint16_t p_start : PAGE start address
* @uint16_t c_stop : COLUMN end address
* @uint16_t p_stop : PAGE end address
*/
void lcd_set_window(uint16_t c_start,
uint16_t p_start,
uint16_t c_stop,
uint16_t p_stop)
{
sdata[0] = COLUMNADDR; /* Set Column Address (0x21) */
sdata[1] = c_start; /* COL start; eg 0 */
sdata[2] = c_stop; /* COL end; max 'lcdwidth -1'; 127 */
sdata[3] = PAGEADDR; /* Set Page Address (0x22) */
sdata[4] = p_start; /* PAGE start; 0 */
sdata[5] = p_stop; /* PAGE end; max=7((lcdheight/8)-1) */
while (io_state != IO_COMPLETE); /* wait for dma completion */
GpioDataRegs.GPBCLEAR.bit.GPIO44 = 1; /* CMD mode */
dma_xfer(sdata, 5); /* transfer 6 bytes */
}
/* INT7.1 is DMA Ch1 */
__interrupt void isr_dma_ch1(void)
{
EALLOW;
DmaRegs.CH1.CONTROL.bit.HALT = 1;
PieCtrlRegs.PIEACK.all = PIEACK_GROUP7; /* Interrupt ACK */
EDIS;
return;
}
/* INT7.2 is DMA Ch2 */
__interrupt void isr_dma_ch2(void)
{
EALLOW;
DmaRegs.CH2.CONTROL.bit.HALT = 1;
PieCtrlRegs.PIEACK.all = PIEACK_GROUP7; /* Interrupt ACK */
io_state = IO_COMPLETE;
EDIS;
return;
}
void main(void)
{
InitSysCtrl();
init_mcbspa_gpio();
DINT;
InitPieCtrl();
IER = 0x0000;
IFR = 0x0000;
InitPieVectTable();
EALLOW;
PieVectTable.DINTCH1 = &isr_dma_ch1; /* Tx ISR DMA CH1 INT7.1 */
PieVectTable.DINTCH2 = &isr_dma_ch2; /* Rx ISR DMA CH2 INT7.2 */
EDIS;
setup_lcd_pins();
ssd1306_reset(); /* Reset LCD */
PieCtrlRegs.PIECTRL.bit.ENPIE = 1; /* Enable PIE block */
PieCtrlRegs.PIEIER7.bit.INTx1 = 1; /* Enable PIE Group 7 INT1(DMA CH1) */
PieCtrlRegs.PIEIER7.bit.INTx2 = 1; /* Enable PIE Group 7 INT2(DMA CH2) */
IER = 0x40; /* Enable CPU INT Group 7 */
EINT; /* Enable Global Interrupts */
ssd1306_init(); /* initialize LCD */
fb_clear(); /* clear fb array */
lcd_set_window(0, 0, 127, 7); /* col:0-127, page:0-7 access */
fb[0] = 0xff; /* draw 8 pixels @x=0, y=0-7 */
lcd_fb_put(); /* write to ssd1306 */
while (1) {}
}