/** * 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) {} }