This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC2510: CC2510: USART in SPI Master mode: Can not read MISO after DMA channel 1 TX (MOSI) block transfer.

Part Number: CC2510

Dear Siri (or any other EE),

I would greatly appreciate your help with this problem:

After successful DMA transfer from memory to USART0 TX, it is no more possible to use read MOSI response from a Slave device. Although I can see a valid response from Slave on the logic analyzer, U0DBUF reads all zeros. 

Those same routines I use work perfectly fine until a DMA transfer took place. They communicate with the target without any trouble.

What I tried so far:

- Reinitialize USART

- Set DMA target address to 0x0000

- Set DMA trigger to DMA_TRIG_NONE

- Arm and disarm DMA after above settings

Unfortunately nothing helped. Any advice would be greatly appreciated.

Best regards!

Tom

  • Please provide a simple test code so that we can see how you init the SPI and the DMA. That way we can try to reproduce the issue here.

    BR

    Siri

  • Dear Siri,

    many thanks for your swift response. TIL that the problem only manifests when the Slave is power-cycled before an attemt to read its response. Therefore I suspect it to be some HW problem. I will try to give it some more troubleshooting time and will ask for help once again, once I'd pulled the last hair out of my head.

    Best regards,

    Tom

    For the others reference, I dare to post my whole code here, as there is newer too many code examples, once the Processors Wiki has been taken down (Seriously, why?!)

    EDIT:

    Although the root cause still remains unclear, the solution to the problem was to zero all USART registers and then re-initialize the USART as a SPI Master.

    The code below was udated and it works now.

    Many thanks for all the attention and good will to help.

    Tomas

    /*
     Device: SES Imagotag EPD ESL, type G1 2.7 BW NFC
     Main parts:
     CC2510F32
     EM027BS014 EPD display. It is the same as more frequently seen EM027BS012 and EM027BS013
     The display Vcc is switched via P-MOSFET, controlled by a GPIO pin
     There is also a dual N-MOSFET to discharge two caps when the EPD VCC is turned off.
     Apart from that, there is also a  W25X10CLUXIG 1Mb SPI Flash chip connected to USART1.
     */
    
    #include <hal_types.h>
    #include <hal_defs.h>
    #include <hal_cc8051.h>
    #include <ioCCxx10_bitdef.h>
    #include <stdint.h>
    #include <dma.h>
    
    #if (chip == 2510)
    #include <ioCC2510.h>
    #endif
    #if (chip == 1110)
    #include <ioCC1110.h>
    #endif
    #if (chip == 2511)
    #include <ioCC2511.h>
    #endif
    #if (chip == 1111)
    #include <ioCC1111.h>
    #endif
    
    // Define macro for splitting 16 bits in 2 x 8 bits:
    #define HIBYTE(a) (BYTE)((WORD)(a) >> 8)
    #define LOBYTE(a) (BYTE)(WORD)(a)
    #define SET_WORD(regH, regL, word) \
    do                             \
    {                              \
    (regH) = HIBYTE(word);     \
    (regL) = LOBYTE(word);     \
    } while (0)
    
    // 60, 15 will give a baud rate of approx. 1.002930 Mbps for 26 MHz clock
    #define SPI_BAUD_M 60
    #define SPI_BAUD_E 17
    
    // Define size of allocated UART RX/TX buffer (just an example)
    #define UART_BUFFER_SIZE 50
    
    // Test definitions
    //#define UART_TST_MODE_RX
    #define UART_TST_MODE_TX
    
    // Baudrate = 57.6 kbps (U0BAUD.BAUD_M = 34, U0GCR.BAUD_E = 11)
    // 2400: 131,6
    // 4800: 131,7
    // 9600: 131,8
    // 14400: 34,9
    // 19200: 131,9
    // 28800: 34,10
    // 38400: 131,10
    // 57600: 34,11
    // 76800: 131,11
    // 115200 34,12
    // 230400 34 13
    #define UART_BAUD_M 34
    #define UART_BAUD_E 12
    
    //.............................................................................
    //                         GLOBAL CONSTANTS  for SPI flash
    //............................................................................
    #define READ 0x03   // SPI flash read addr msb, lsb
    #define WRITE 0x02  // WRITE      instruction code
    #define WRDI 0x04   // WR DISBALE instruction code
    #define WREN 0x06   // WR ENABLE  instruction code
    #define RDSR 0x05   // RD STATUS  instruction code
    #define WRSR 0x01   // WR STATUS  instruction code
    #define NOPROT 00x0 // NO WRITE PROTECTION
    #define INFO 0x90   // SPI flash info
    
    // Define size of buffer and number of bytes to send
    #define SPI_BUFFER_SIZE 111
    
    #define LEDpin P1_1
    #define EPDpowerPin P0_0
    #define EPDresetPin P2_0
    #define EPDbusyPin P1_3
    #define EPDdischargePin P1_2
    #define EPDcsPin P0_1
    
    #define EPD_width 264
    #define EPD_heigth 176
    #define EPD_bytes_per_scan EPD_heigth / 4
    #define EPD_stage_time 100
    #define EPD_line_buffer_size 2 * EPD_width / 8 + EPD_bytes_per_scan + 2
    #define FB_Xsize EPD_width / 8 // each frame buffer pixel is 2x2 EPD image pixels
    #define FB_Ysize EPD_heigth / 2
    
    static volatile uint32_t milliseconds = 0;
    static DMA_DESC uartDmaTxDescr;
    static uint8_t DMArunning = 0;
    static uint8_t framebuffer[FB_Ysize][FB_Xsize];
    static uint8_t __xdata SPIbuffer[SPI_BUFFER_SIZE];
    static uint8_t __xdata EPD_line_buffer[EPD_line_buffer_size];
    // static uint8_t __xdata UARTbuffer[UART_BUFFER_SIZE];
    
    /*************************************************************************
     Parameters: uart: 0 or 1 - which UART is to be initialized
     alt: 1 or 2 - which pinout to use
    
     With non-inverted clock polarity (the clock is at logic low when slave select transitions to logic low):
     Mode 0: data is sampled on the rising edge of the clock pulse and shifted out on the falling edge       .CPOL=0  .CPHA=0
     Mode 1: data is sampled on the falling edge of the clock pulse and shifted out on the rising edge       .CPOL=0  .CPHA=1
     With inverted clock polarity (the clock is at logic high when slave select transitions to logic low):
     Mode 2: data is sampled on the falling edge of the clock pulse and shifted out on the rising edge       .CPOL=1  .CPHA=0
     Mode 3: data is sampled on the rising edge of the clock pulse and shifted out on the falling edge       .CPOL=1  .CPHA=1
     USART0 ALT1:    USART0 ALT2:    USART1 ALT1:    USART1 ALT2:
     P0_5:   CLK                             MISO
     P0_4:   SS(!)                           MOSI
     P0_3:   MOSI                            CLK
     P0_2:   MISO                            SS
     P1_7                                                    MISO
     P1_6                                                    MOSI
     P1_5:                   MOSI                            CLK
     P1_4:                   MISO                            SS
     P1_3:                   CLK
     P1_2:                   SS
     */
    
    /**********************************************************************************
     Pro pouziti s EPD:
    
     EPD is connected to USART0 ALT1
     SPI FLASH to USART1 ALT2
    
     CC2510F32 pinout/connections:
     pin1 = P1_2 = Vgh & Vdh Cap discharge (via dual N-MOSFET)
     pin2 = Vcc CC2510
     pin3 = P1_1 = nc (could drive LED) => White LED via 560R
     pin4 = P1_0 = FLASH Vcc
     pin5 = P0_0 = EPD Vcc (via P-MOSFET)
     pin6 = P0_1 = EPD /CS
     pin7 = P0_2 = EPD MISO
     pin8 = P0_3 = EPD MOSI
     pin9 = P0_4 = test pad	-> would be software UART
     pin10 = Vcc CC2510
     pin11 = P0_5 = EPD SCLK
     pin12 = P0_6 = nc
     pin13 = P0_7 = nc (maybe EPD BORDER later?)
     pin14 = P2_0 = EPD /RST
     pin15 = P2_1 = DEBUG DATA
     pin16 = P2_2 = DEBUG CLK
     ...
     pin32 = P1_7 = FLASH MISO
     pin33 = P1_6 = FLASH MOSI
     pin34 = P1_5 = FLASH CLK
     pin35 = P1_4 = FLASH /CS
     pin36 = P1_3 = Display BUSY
     ************************************************************************************/
    
    static inline void delay_us(uint16_t us) // In fact it is more like 1.5 us @26MHz or 3,2us @13MHz, but meh.
    {
    	while (us--)
    		asm("NOP");
    }
    
    /***********************************************************************************
     * @fn          uart0StartTxDmaChan
     *
     * @brief       Function which sets up a DMA channel for UART0 TX.
     *
     * @param       DMA_DESC *uartDmaTxDescr - pointer to DMA descriptor for UART TX
     *              uint8 uartDmaTxChan - DMA channel number for UART TX
     *              uint8* uartTxBuf - pointer to allocated UART TX buffer
     *              uint16 uartTxBufSize - size of allocated UART TX buffer
     *
     * @return      0
     */
    void SPI0DMAtransfer(uint8_t *uartTxBuf, uint8_t uartTxBufSize) {
    
    	DMArunning = 1;
    	P0_1 = 0; // Set CS -> low
    
    // Set source/destination pointer (UART TX buffer address) for UART TX DMA channel,
    // and total number of DMA word transfer (according to UART TX buffer size).
    	uartDmaTxDescr.SRCADDRH = (uint16) (uartTxBuf + 1) >> 8;
    	uartDmaTxDescr.SRCADDRL = (uint16) (uartTxBuf + 1);
    	uartDmaTxDescr.DESTADDRH = ((uint16) (&X_U0DBUF) >> 8) & 0x00FF;
    	uartDmaTxDescr.DESTADDRL = (uint16) (&X_U0DBUF) & 0x00FF;
    	uartDmaTxDescr.LENH = ((uartTxBufSize - 1) >> 8) & 0xFF;
    	uartDmaTxDescr.LENL = (uartTxBufSize - 1) & 0xFF;
    	uartDmaTxDescr.VLEN = DMA_VLEN_FIXED; // Use fixed length DMA transfer count
    
    // Perform 1-byte transfers
    	uartDmaTxDescr.WORDSIZE = DMA_WORDSIZE_BYTE;
    
    // Transfer a single word after each DMA trigger
    	uartDmaTxDescr.TMODE = DMA_TMODE_SINGLE;
    
    // DMA word trigger = USARTx TX complete
    	uartDmaTxDescr.TRIG = DMA_TRIG_UTX0;
    
    	uartDmaTxDescr.SRCINC = DMA_SRCINC_1;        // Increment source pointer by 1 word
    	// address after each transfer.
    	uartDmaTxDescr.DESTINC = DMA_DESTINC_0;      // Do not increment destination pointer:
    	// points to USART UxDBUF register.
    	uartDmaTxDescr.IRQMASK = DMA_IRQMASK_ENABLE; // Enable DMA interrupt to the CPU
    	uartDmaTxDescr.M8 = DMA_M8_USE_8_BITS;       // Use all 8 bits for transfer count
    	uartDmaTxDescr.PRIORITY = DMA_PRI_LOW;       // DMA memory access has low priority
    
    // Link DMA descriptor with its corresponding DMA configuration register.
    	DMA1CFGH = (uint8) ((uint16) &uartDmaTxDescr >> 8);
    	DMA1CFGL = (uint8) ((uint16) &uartDmaTxDescr & 0x00FF);
    
    // Arm the relevant DMA channel for SPI TX, and apply 45 NOP's
    // to allow the DMA configuration to load
    	DMAARM = ((1 << 0x01 /* DMA channel 1 */) & (BIT4 | BIT3 | BIT2 | BIT1 | BIT0));
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    	asm("NOP");
    
    // Enable the DMA interrupt (IEN1.DMAIE = IEN0.EA = 1),
    // and clear potential pending DMA interrupt requests (IRCON.DMAIF = 0).
    	EA = 1;
    	DMAIE = 1;
    	DMAIF = 0;
    
    // Send the very first UART byte to trigger a UART TX session:
    	U0DBUF = uartTxBuf[0];
    
    // At this point the UART peripheral generates a DMA trigger each time it has
    // transmitted a byte, leading to a DMA transfer from the allocated source buffer
    // to the UxDBUF register. Once the DMA controller has completed the defined
    // range of transfers, the CPU vectors its execution to the DMA ISR.
    }
    
    /***********************************************************************************
     * @fn          DMA_ISR
     *
     * @brief       DMA Interrupt Service Routine, called when DMA has finished
     *              transferring one packet/buffer between memory and UxDBUF.
     *
     * @param       none
     *
     * @return      0
     */
    #pragma vector = DMA_VECTOR
    __interrupt void DMA_ISR(void) {
    
    // Clear the main DMA interrupt Request Flag (IRCON.DMAIF = 0)
    	DMAIF = 0;
    // Start a new UART TX session on DMA channel 1:
    	if (DMAIRQ & DMAIRQ_DMAIF1) {
    // Clear DMA Channel 1 Interrupt Request Flag (DMAIRQ.DMAIF1 = 0)
    		DMAIRQ &= ~DMAIRQ_DMAIF1;
    
    		/* In this particular software example a new UART/DMA TX session must
    		 * be initialized outside this DMA ISR. However, it would also be possible
    		 * to use this DMA ISR to start a new UART/DMA TX session by simply
    		 * re-arming the DMA channel and sending the first UART TX byte.
    		 */
    	}
    	DMArunning = 0;		// A flag that we can safely continue using the USART
    	while (U0CSR & 0x01)
    		;	// Let us wait until the last byte is shifted out..
    	P0_1 = 1;		// ...before setting /CS -> high
    }
    
    // Timer 3 ISR counting milliseconds
    #pragma vector = T3_VECTOR
    __interrupt void t3_isr(void) {
    
    	T3OVFIF = 0; // Clears the module interrupt flag.
    	milliseconds += 1;
    	T3IF = 0; // Clears the CPU interrupt flag.
    }
    
    // Starts millisecond counter
    void startMillisTimer(void) {
    	/* Clock control. Configures the Timer tick speed setting, resulting in a
    	 * Timer tick frequency of 203.125 kHz.
    	 */
    	CLKCON = (CLKCON & ~CLKCON_TICKSPD) | TICKSPD_DIV_128;
    
    	/* Timer 3 control. Configuration:
    	 * - Prescaler divider value: 1.
    	 * - Interrupts enabled.
    	 * - Modulo mode.
    	 * The Timer is also cleared and started.
    	 */
    	T3CTL = T3CTL_DIV_1 | T3CTL_START | T3CTL_OVFIM | T3CTL_CLR | T3CTL_MODE_MODULO;
    	T3CC0 = 203; // 203.125khz / 203 = almost 1kHz
    
    	/* Enables global interrupts (IEN0.EA = 1) and interrupts from Timer 3
    	 * (IEN1.T3IE = 1).
    	 */
    	EA = 1;
    	T3IE = 1;
    }
    
    // Returns milliseconds elapsed from execution of startMillisTimer()
    uint32_t millis(void) {
    	return milliseconds;
    }
    
    void delay_ms(uint32_t delay) {
    	uint32_t starttime = millis();
    
    	while ((millis() - starttime) < delay)
    		;
    }
    
    // Set system clock source to 26 Mhz XOSC to support maximum transfer speed,
    // ref. [clk]=>[clk_xosc.c]
    void configureClock26M(void) {
    
    	SLEEP &= ~SLEEP_OSC_PD;
    	while (!(SLEEP & SLEEP_XOSC_S))
    		;
    	CLKCON = (CLKCON & ~(CLKCON_CLKSPD | CLKCON_OSC)) | CLKSPD_DIV_1;
    	while (CLKCON & CLKCON_OSC)
    		;
    	SLEEP |= SLEEP_OSC_PD;
    }
    
    // used only in SPIinit() to set SPI mode on USART0
    void setU0SPImode(uint8_t mode, uint8_t msbfirst) {
    
    	switch (mode) {
    	case 0: //SCK normally low, sampling on rising
    //                 bd Exponent    CLK polarity CLK phase    bit order       bd Exponent
    //                                0-idle low   0-dta on 1   0-LSB first
    		U0GCR = (U0GCR & ~(U0GCR_BAUD_E | U0GCR_CPOL | U0GCR_CPHA | U0GCR_ORDER)) | SPI_BAUD_E;
    		break;
    	case 1: //    normally low, sampling on falling
    		U0GCR = (U0GCR & ~(U0GCR_BAUD_E | U0GCR_CPOL | U0GCR_ORDER)) | U0GCR_CPHA | SPI_BAUD_E;
    		break;
    	case 3: //    normally high, sampling on falling
    		U0GCR = (U0GCR & ~(U0GCR_BAUD_E | U0GCR_CPHA | U0GCR_ORDER)) | U0GCR_CPOL | SPI_BAUD_E;
    		break;
    	case 4: //    normally high, sampling on rising
    		U0GCR = (U0GCR & ~(U0GCR_BAUD_E | U0GCR_ORDER)) | U0GCR_CPOL | U0GCR_CPHA | SPI_BAUD_E;
    		break;
    	}
    	if (msbfirst > 0)
    		U0GCR |= U0GCR_ORDER;
    }
    
    // used only in SPIinit() to set SPI mode on USART1
    void setU1SPImode(uint8_t mode, uint8_t msbfirst) {
    
    	switch (mode) {
    	case 0: //SCK normally low, sampling on rising
    //                 bd Exponent    CLK polarity CLK phase    bit order       bd Exponent
    		U1GCR = (U1GCR & ~(U1GCR_BAUD_E | U1GCR_CPOL | U1GCR_CPHA | U1GCR_ORDER)) | SPI_BAUD_E;
    		break;
    	case 1: //    normally low, sampling on falling
    		U1GCR = (U1GCR & ~(U1GCR_BAUD_E | U1GCR_CPOL | U1GCR_ORDER)) | U1GCR_CPHA | SPI_BAUD_E;
    		break;
    	case 3: //    normally high, sampling on falling
    		U1GCR = (U1GCR & ~(U1GCR_BAUD_E | U1GCR_CPHA | U1GCR_ORDER)) | U1GCR_CPOL | SPI_BAUD_E;
    		break;
    	case 4: //    normally high, sampling on rising
    		U1GCR = (U1GCR & ~(U1GCR_BAUD_E | U1GCR_ORDER)) | U1GCR_CPOL | U1GCR_CPHA | SPI_BAUD_E;
    		break;
    	}
    	if (msbfirst > 0)
    		U1GCR |= U1GCR_ORDER;
    }
    
    void SPICSlow(uint8_t usart) // !!! EPD /CS on on the device is connected to P0_1, not P0_4 !!!
    {
    	switch (usart) {
    	case 0:
    		switch (P0SEL & (BIT5 | BIT3 | BIT2)) // Pins 5,3,2 are set to periherial?
    		{
    		case BIT5 | BIT3 | BIT2: // Yes -> assume ALT1 is active -> our /CS pin should be P0_4, but in fact is P0_1
    			P0_1 = 0;
    			break;
    		default: // No -> our CS is P1_2 is GPIO (ALT2)
    			P1_2 = 0;
    			break;
    		}
    		break;
    	case 1:
    		switch (P0SEL & (BIT5 | BIT4 | BIT3)) // Pins 5,4,3 are set to periherial?
    		{
    		case BIT5 | BIT4 | BIT3: // Yes -> assume ALT1 is active -> our /CS pin is P0_2
    			P0_2 = 0;
    			break;
    		default:
    			P1_4 = 0;
    			break;
    		}
    	}
    }
    
    void SPICShigh(uint8_t usart) // !!! EPD /CS on on the device is connected to P0_1, not P0_4 !!!
    {
    	switch (usart) {
    	case 0:
    		switch (P0SEL & (BIT5 | BIT3 | BIT2)) // Pins 5,3,2 are set to periherial?
    		{
    		case BIT5 | BIT3 | BIT2: // Yes -> assume ALT1 is active -> our /CS pin should be P0_4, but in fact is P0_1
    			P0_1 = 1;
    			break;
    		default: // No -> our CS is P1_2 is GPIO (ALT2)
    			P1_2 = 1;
    			break;
    		}
    		break;
    	case 1:
    		switch (P0SEL & (BIT5 | BIT4 | BIT3)) // Pins 5,4,3 are set to periherial?
    		{
    		case BIT5 | BIT4 | BIT3: // Yes -> assume ALT1 is active -> our /CS pin is P0_2
    			P0_2 = 1;
    			break;
    		default:
    			P1_4 = 1;
    			break;
    		}
    	}
    }
    
    // Init SPI (LSB first) on USART 0 or 1, with pins ALT1 or ALT2, and SPI mode 0-4
    uint8_t SPIinit(uint8_t usart, uint8_t alt, uint8_t mode, uint8_t msbfirst) {
    	uint8_t error = 0x00;
    
    	configureClock26M();
    	switch (usart) {
    	case 0:
    		switch (alt) {
    		case 1:
    // For some reason, /CS pin of diplay is not P0_4, but P0_1!!!
    			PERCFG = (PERCFG & ~PERCFG_U0CFG) | PERCFG_U1CFG; // Set output pins: UART0=ALT1, UART1=ALT2
    			P2DIR = (P2DIR & ~P2DIR_PRIP0) | P2DIR_PRIP0_0;   // Give priority to USART0 over USART1 for P0 pins
    			P0SEL = (P0SEL & ~BIT1) | BIT5 | BIT3 | BIT2;     // Clear - GPIO; Set - periph. P0_4 is sw controlled GPIO SS
    			P0_1 = 1;                                         // /CS deasert
    			P0DIR |= BIT1;                                    // /CS Set - OUTPUT
    			U0CSR &= ~(U0CSR_MODE | U0CSR_SLAVE);             // Set USART0 to SPI mode and Master mode
    			U0BAUD = SPI_BAUD_M;
    			setU0SPImode(mode, msbfirst);
    			break;
    		case 2:
    			PERCFG = (PERCFG & ~PERCFG_U1CFG) | PERCFG_U0CFG; // Set output pins: UART1=ALT1, UART0=ALT2
    			P2SEL = P2SEL & ~(P2SEL_PRI3P1 | P2SEL_PRI0P1);   // Give priority to USART0 over USART1 for P1 pins
    			P1SEL = (P1SEL & ~BIT2) | BIT5 | BIT4 | BIT3;     // Clear - GPIO; Set - periph. P1_2 is sw controlled GPIO SS
    			P1_2 = 1;                                         // /CS deasert
    			P1DIR |= BIT2;                                    // /CS Set - OUTPUT
    			U0CSR &= ~(U0CSR_MODE | U0CSR_SLAVE);             // Set USART0 to SPI mode and Master mode
    			U0BAUD = SPI_BAUD_M;
    			setU0SPImode(mode, msbfirst);
    			break;
    		default:
    			error |= 1;
    		}
    		break;
    	case 1:
    		switch (alt) {
    		case 1:
    			PERCFG = (PERCFG & ~PERCFG_U1CFG) | PERCFG_U0CFG; // Set output pins: UART1=ALT1, UART0=ALT2
    			P2DIR = (P2DIR & ~P2DIR_PRIP0) | P2DIR_PRIP0_1;   // Give priority to USART1 over USART0 for P0 pins
    			P0SEL = (P0SEL & ~BIT2) | BIT5 | BIT4 | BIT3;     // Clear - GPIO; Set - periph. P0_2 is sw controlled GPIO SS
    			P0_2 = 1;                                         // /CS deasert
    			P0DIR |= BIT2;                                    // /CS Set - OUTPUT
    			U1CSR &= ~(U1CSR_MODE | U1CSR_SLAVE);             // Set USART0 to SPI mode and Master mode
    			U1BAUD = SPI_BAUD_M;
    			setU1SPImode(mode, msbfirst);
    			break;
    		case 2:
    			PERCFG = (PERCFG & ~PERCFG_U0CFG) | PERCFG_U1CFG; // Set output pins: UART1=ALT2, UART0=ALT1
    			P2SEL = (P2SEL & ~P2SEL_PRI2P1) | P2SEL_PRI3P1;   // Give priority to USART1 over USART0 for P1 pins
    			P1SEL = (P1SEL & ~BIT4) | BIT7 | BIT6 | BIT5;     // Clear - GPIO; Set - periph. P1_4 is sw controlled GPIO SS
    			P1_4 = 1;                                         // /CS deasert
    			P1DIR |= BIT4;                                    // /CS Set - OUTPUT
    			U1CSR &= ~(U1CSR_MODE | U1CSR_SLAVE);             // Set USART0 to SPI mode and Master mode
    			U1BAUD = SPI_BAUD_M;
    			setU1SPImode(mode, msbfirst);
    			break;
    		default:
    			error |= 2;
    		}
    	}
    	return error;
    }
    
    // USART 0/1, *buffer, bytes, CS after transfer 0/1
    uint8_t SPItransfer(uint8_t usart, uint8_t *buffer, uint8_t bytes, uint8_t CSafterTransfer) {
    	uint8_t error = 0;
    
    	if (SPI_BUFFER_SIZE < bytes)
    		error = 1;
    	else if ((usart == 0) && !(PERCFG & PERCFG_U0CFG)) //USART0 ALT1
    			{
    		P0_1 = 0; //  /CS ->low  For some rason, /CS for display is not on P0_4 but on P0_1!!!
    //    delay_us(5);
    		for (uint8_t i = 0; i < bytes; i++) {
    			U0DBUF = buffer[i];	// Write char form buffer to USART0 buffer (transmit data)
    			while (!(U0CSR & U0CSR_TX_BYTE))
    				asm("NOP");
    // Check if byte is transmitted (and a byte is recieved)
    			U0CSR &= ~U0CSR_TX_BYTE;	// Clear transmit byte status
    			buffer[i] = U0DBUF;		// Write received byte to buffer
    /*
     This works perfectly fine before a DMA transfer.
     Unfortunately, if DMA transfer took place anytime before using this function
     and the Slawe was powered off and on again, the U0DBUF always reads 0x00, although
     the LA displays nicely the Slave device response. Why?! I'm completely clueless...
     EDIT:
     The solution was to zero all USART registers and then re-initialize the SPI.
     */
    		}
    		if (CSafterTransfer)
    			P0_1 = 1;			// Set /CS -> high
    	} else if ((usart == 0) && (PERCFG & PERCFG_U0CFG)) //USART0 ALT2
    			{
    		P1_2 = 0; // CS ->low
    //    delay_us(5);
    		for (uint8_t i = 0; i < bytes; i++) {
    			U0DBUF = buffer[i];
    			while (!(U0CSR & U0CSR_TX_BYTE))
    				asm("NOP");
    			U0CSR &= ~U0CSR_TX_BYTE;
    			buffer[i] = U0DBUF;
    		}
    		if (CSafterTransfer)
    			P1_2 = 1; // Set CS -> high
    	} else if ((usart == 1) && !(PERCFG & PERCFG_U1CFG)) //USART1 ALT1
    			{
    		P0_2 = 0; // CS ->low
    //    delay_us(5);
    		for (uint8_t i = 0; i < bytes; i++) {
    			U1DBUF = buffer[i];
    			while (!(U1CSR & U1CSR_TX_BYTE))
    				asm("NOP");
    			U1CSR &= ~U1CSR_TX_BYTE;
    			buffer[i] = U1DBUF;
    		}
    		if (CSafterTransfer)
    			P0_2 = 1; // Set CS -> high
    	} else if ((usart == 1) && (PERCFG & PERCFG_U1CFG)) //USART1 ALT2
    			{
    		P1_4 = 0; // CS ->low
    //    delay_us(5);
    		for (uint8_t i = 0; i < bytes; i++) {
    			U1DBUF = buffer[i];
    			while (!(U1CSR & U1CSR_TX_BYTE))
    				asm("NOP");
    			U1CSR &= ~U1CSR_TX_BYTE;
    			buffer[i] = U1DBUF;
    		}
    		if (CSafterTransfer)
    			P1_4 = 1; // Set CS -> high
    	} else
    		error |= 2;
    	return error; // error
    }
    
    void UART0Init(void) // baudrate - see defines, ALT1
    {
    // Set system clock source to 26 Mhz XSOSC to support maximum transfer speed,
    // ref. [clk]=>[clk_xosc.c]
    	SLEEP &= ~SLEEP_OSC_PD;
    	while (!(SLEEP & SLEEP_XOSC_S))
    		;
    	CLKCON = (CLKCON & ~(CLKCON_CLKSPD | CLKCON_OSC)) | CLKSPD_DIV_1;
    	while (CLKCON & CLKCON_OSC)
    		;
    	SLEEP |= SLEEP_OSC_PD;
    // Initialise bitrate
    	U0BAUD = UART_BAUD_M;
    	U0GCR = (U0GCR & ~U0GCR_BAUD_E) | UART_BAUD_E;
    
    // Initialise UART protocol (start/stop bit, data bits, parity, etc.):
    
    // USART mode = UART (U0CSR.MODE = 1)
    	U0CSR |= U0CSR_MODE;
    // Start bit level = low => Idle level = high  (U0UCR.START = 0)
    	U0UCR &= ~U0UCR_START;
    // Stop bit level = high (U0UCR.STOP = 1)
    	U0UCR |= U0UCR_STOP;
    // Number of stop bits = 1 (U0UCR.SPB = 0)
    	U0UCR &= ~U0UCR_SPB;
    // Parity = disabled (U0UCR.PARITY = 0)
    	U0UCR &= ~U0UCR_PARITY;
    // 9-bit data enable = 8 bits transfer (U0UCR.BIT9 = 0)
    	U0UCR &= ~U0UCR_BIT9;
    // Level of bit 9 = 0 (U0UCR.D9 = 0), used when U0UCR.BIT9 = 1
    // Level of bit 9 = 1 (U0UCR.D9 = 1), used when U0UCR.BIT9 = 1
    // Parity = Even (U0UCR.D9 = 0), used when U0UCR.PARITY = 1
    // Parity = Odd (U0UCR.D9 = 1), used when U0UCR.PARITY = 1
    	U0UCR &= ~U0UCR_D9;
    // Flow control = disabled (U0UCR.FLOW = 0)
    	U0UCR &= ~U0UCR_FLOW;
    // Bit order = LSB first (U0GCR.ORDER = 0)
    	U0GCR &= ~U0GCR_ORDER;
    	/***************************************************************************
    	 * Setup I/O ports
    	 *
    	 * Port and pins used by USART0 operating in UART-mode are
    	 * RX     : P0_2
    	 * TX     : P0_3
    	 * CT/CTS : P0_4
    	 * RT/RTS : P0_5
    	 *
    	 * These pins can be set to function as peripheral I/O to be be used by UART0.
    	 * The TX pin on the transmitter must be connected to the RX pin on the receiver.
    	 * If enabling hardware flow control (U0UCR.FLOW = 1) the CT/CTS (Clear-To-Send)
    	 * on the transmitter must be connected to the RS/RTS (Ready-To-Send) pin on the
    	 * receiver.
    	 */
    // Configure USART0 for Alternative 1 => Port P0 (PERCFG.U0CFG = 0)
    // To avoid potential I/O conflict with USART1:
    // configure USART1 for Alternative 2 => Port P1 (PERCFG.U1CFG = 1)
    	PERCFG = (PERCFG & ~PERCFG_U0CFG) | PERCFG_U1CFG;
    // Configure relevant Port P0 pins for peripheral function:
    // P0SEL.SELP0_2/3/4/5 = 1 => RX = P0_2, TX = P0_3, CT = P0_4, RT = P0_5
    // Configure them AFTER configuring the USART in order to avoid glitch on TX
    	P0SEL |= BIT5 | BIT4 | BIT3 | BIT2;
    }
    
    void UART0Send(uint8_t *uartTxBuf, uint8_t uartTxBufLength) {
    // Clear any pending TX interrupt request (set U0CSR.TX_BYTE = 0)
    	U0CSR &= ~U0CSR_TX_BYTE;
    // Loop: send each UART0 sample on the UART0 TX line
    	for (uint8_t uartTxIndex = 0; uartTxIndex < uartTxBufLength; uartTxIndex++) {
    		U0DBUF = uartTxBuf[uartTxIndex];
    		while (!(U0CSR & U0CSR_TX_BYTE))
    			;
    		U0CSR &= ~U0CSR_TX_BYTE;
    	}
    }
    
    void UART0Receive(uint8_t *uartRxBuf, uint8_t uartRxBufLength) {
    // Enable UART0 RX (U0CSR.RE = 1)
    	U0CSR |= U0CSR_RE;
    // Clear any pending RX interrupt request (set U0CSR.RX_BYTE = 0)
    	U0CSR &= ~U0CSR_RX_BYTE;
    // Loop: receive each UART0 sample from the UART0 RX line
    	for (uint8_t uartRxIndex = 0; uartRxIndex < uartRxBufLength; uartRxIndex++) {
    // Wait until data received (U0CSR.RX_BYTE = 1)
    		while (!(U0CSR & U0CSR_RX_BYTE))
    			;
    // Read UART0 RX buffer
    		uartRxBuf[uartRxIndex] = U0DBUF;
    	}
    }
    
    // register_index The Register Index as SPI Data to COG
    // register_data The Register Data for sending command data to COG
    // return the SPI read value
    uint8_t EPDreadReg(uint8_t register_index) {
    	uint8_t result, buf[5];
    
    #ifndef SIMULATOR
    	while (DMArunning)
    		; //wait if DMA transfer is running
    	buf[0] = 0x70;
    	buf[1] = register_index;
    	SPICShigh(0);
    	delay_us(10);
    	SPItransfer(0, buf, 2, 1); // header of Register Index
    	delay_us(10);
    	buf[0] = 0x73;
    	buf[1] = 0x00;
    	SPItransfer(0, buf, 2, 1); // Read the register
    	result = buf[1];
    #endif
    	return result;
    }
    
    void EPDwriteReg(uint8_t register_index, uint8_t Data) {
    	uint8_t buf[5];
    
    #ifndef SIMULATOR
    	while (DMArunning)
    		; //wait if DMA transfer is running
    	buf[0] = 0x70;
    	buf[1] = register_index;
    	EPDcsPin = 1;
    	SPItransfer(0, buf, 2, 1); // header of Register Index
    	buf[0] = 0x72;
    	buf[1] = Data;
    	SPItransfer(0, buf, 2, 1); // Read the register
    #endif
    }
    
    //SPI command if register data is larger than two bytes:
    //register_index The Register Index as SPI command to COG
    //register_data The Register Data for sending command data to COG
    void EPDsendArray(uint8_t register_index, uint8_t *register_data, uint8_t length) {
    	uint8_t buf[5];
    
    #ifndef SIMULATOR
    	while (DMArunning)
    		; //wait if DMA transfer is running
    	buf[0] = 0x70;
    	buf[1] = register_index;
    	EPDcsPin = 1;
    	SPItransfer(0, buf, 2, 1); // header of Register Index
    	buf[0] = 0x72;
    	SPItransfer(0, buf, 1, 0);
    	SPItransfer(0, register_data, length, 1); // Write the data
    #endif
    }
    
    //SPI command if register data is larger than two bytes - DMA transfer:
    //register_index The Register Index as SPI command to COG
    //register_data The Register Data for sending command data to COG
    void EPDsendArrayDMA(uint8_t register_index, uint8_t *register_data, uint8_t length) {
    	uint8_t buf[5];
    
    #ifndef SIMULATOR
    	while (DMArunning)
    		; //wait if DMA transfer is running
    	buf[0] = 0x70;
    	buf[1] = register_index;
    	EPDcsPin = 1;
    	SPItransfer(0, buf, 2, 1); // header of Register Index
    	buf[0] = 0x72;
    	SPItransfer(0, buf, 1, 0);
    	SPI0DMAtransfer(register_data, length); // Write the data
    #endif
    }
    
    // Get COG ID
    uint8_t EPDgetId(void) {
    	static uint8_t buf[2];
    
    #ifndef SIMULATOR
    	while (DMArunning)
    		; //wait if DMA transfer is running
    	buf[0] = 0x71;
    	buf[1] = 0x00;
    	SPICShigh(0);
    	delay_us(10);
    	SPItransfer(0, buf, 2, 1); // header of Register Index
    #endif
    	return buf[1];
    }
    
    enum EPD_stage { /* Image pixel -> Display pixel */
    	COMPENSATE, /* B -> W, W -> B (Current Image) */
    	WHITE, /* B -> N, W -> W (Current Image) */
    	INVERSE, /* B -> N, W -> B (New Image) */
    	NORMAL /* B -> B, W -> W (New Image) */
    };
    
    void EPDinitGPIO(void) {
    // Discharge output - P1_2
    	P1SEL = (P1SEL & ~BIT2); // Clear - GPIO; Set - periph. P1_2 is Discharge
    	P1_2 = 0;                // Discharge dual N-MOSFET GS=0V - No discharge
    	P1DIR |= BIT2;           // Discharge pin is OUTPUT
    
    // /Vcc output - P0_0
    	P0SEL = (P0SEL & ~BIT0); // Clear - GPIO; Set - periph. P0_0 is /Vcc Disp
    	P0_0 = 1;                // Power P-MOSFET GS=3V - No power
    	P0DIR |= BIT0;           // Power pin is OUTPUT
    
    // /RST output - P2_0
    	P2SEL &= ~BIT0;
    	P2_0 = 0;      // keep it in RESET
    	P2DIR |= BIT0; // Reset is OUTPUT
    
    // BUSY is input - P1_3
    	P1SEL &= ~BIT3; // Clear - GPIO; Set - periph. P1_3 is BUSY form EPD
    	P1INP |= BIT3;  // Set = No PU/PD resistor
    	P1_3 = 0;
    	P1DIR &= ~BIT3;
    }
    
    void EPDbusyWait() {
    	while (EPDbusyPin)
    		;
    }
    
    /* pixels on display are numbered from 1 so even is actually bits 1,3,5,... */
    static void EPD_even_pixels(uint8_t **pp, const uint8_t *data, uint8_t fixed_value, const uint8_t *mask, enum EPD_stage stage) {
    
    	if (data)
    		for (uint8_t b = 0; b < (EPD_width / 8); b++) {
    			uint8_t pixels = data[b] & 0xaa;
    			uint8_t pixel_mask = 0xff;
    			uint8_t p1, p2, p3, p4;
    
    			if (mask) {
    				pixel_mask = (mask[b] ^ pixels) & 0xaa;
    				pixel_mask |= pixel_mask >> 1;
    			}
    
    			switch (stage) {
    			case COMPENSATE: /* B -> W, W -> B (Current) */
    				pixels = 0xaa | ((pixels ^ 0xaa) >> 1);
    				break;
    			case WHITE: /* B -> N, W -> W (Current) */
    				pixels = 0x55 + ((pixels ^ 0xaa) >> 1);
    				break;
    			case INVERSE: /* B -> N, W -> B (New) */
    				pixels = 0x55 | (pixels ^ 0xaa);
    				break;
    			case NORMAL: /* B -> B, W -> W (New) */
    				pixels = 0xaa | (pixels >> 1);
    				break;
    			}
    
    			pixels = (pixels & pixel_mask) | (~pixel_mask & 0x55);
    			p1 = (pixels >> 6) & 0x03;
    			p2 = (pixels >> 4) & 0x03;
    			p3 = (pixels >> 2) & 0x03;
    			p4 = (pixels >> 0) & 0x03;
    			pixels = (p1 << 0) | (p2 << 2) | (p3 << 4) | (p4 << 6);
    			*(*pp)++ = pixels;
    		}
    	else
    		for (uint8_t b = 0; b < (EPD_width / 8); b++)
    			*(*pp)++ = fixed_value;
    }
    
    /* pixels on display are numbered from 1 so odd is actually bits 0,2,4,... */
    static void EPD_odd_pixels(uint8_t **pp, const uint8_t *data, uint8_t fixed_value, const uint8_t *mask, enum EPD_stage stage) {
    
    	if (data)
    		for (uint8_t b = EPD_width / 8; b > 0; b--) {
    			uint8_t pixels = data[b - 1] & 0x55;
    			uint8_t pixel_mask = 0xff;
    
    			if (mask) {
    				pixel_mask = (mask[b - 1] ^ pixels) & 0x55;
    				pixel_mask |= pixel_mask << 1;
    			}
    
    			switch (stage) {
    			case COMPENSATE: /* B -> W, W -> B (Current) */
    				pixels = 0xaa | (pixels ^ 0x55);
    				break;
    			case WHITE: /* B -> N, W -> W (Current) */
    				pixels = 0x55 + (pixels ^ 0x55);
    				break;
    			case INVERSE: /* B -> N, W -> B (New) */
    				pixels = 0x55 | ((pixels ^ 0x55) << 1);
    				break;
    			case NORMAL: /* B -> B, W -> W (New) */
    				pixels = 0xaa | pixels;
    				break;
    			}
    			pixels = (pixels & pixel_mask) | (~pixel_mask & 0x55);
    			*(*pp)++ = pixels;
    		}
    	else
    		for (uint8_t b = EPD_width / 8; b > 0; b--)
    			*(*pp)++ = fixed_value;
    }
    
    /* interleave bits: (byte)76543210 -> (16 bit).7.6.5.4.3.2.1 */
    static inline uint16_t EPD_interleave_bits(uint16_t value) {
    	value = (value | (value << 4)) & 0x0f0f;
    	value = (value | (value << 2)) & 0x3333;
    	value = (value | (value << 1)) & 0x5555;
    
    	return value;
    }
    
    /* output one line of scan and data bytes to the display */
    static void EPD_one_line(uint8_t line, const uint8_t *data, uint8_t fixed_value, const uint8_t *mask, enum EPD_stage stage) {
    	uint8_t *p = EPD_line_buffer;
    
    	/* data bytes */
    	EPD_odd_pixels(&p, data, fixed_value, mask, stage);
    
    	/* scan line */
    	for (uint8_t b = EPD_bytes_per_scan; b > 0; b--) {
    		if (line / 4 == b - 1)
    			*p++ = 0x03 << (2 * (line & 0x03));
    		else
    			*p++ = 0x00;
    	}
    
    	/* data bytes */
    	EPD_even_pixels(&p, data, fixed_value, mask, stage);
    	switch (line) {
    	case 0: {
    //      first line in a frame
    //	EPDsendArray(0x0a, EPD_line_buffer, p - EPD_line_buffer);
    		EPDsendArrayDMA(0x0a, EPD_line_buffer, p - EPD_line_buffer);
    		break;
    	}
    	case EPD_heigth - 1: {
    //		last line in a frame
    		/* Output data to panel */
    		EPDwriteReg(0x02, 0x07);
    //		EPDsendArray(0x0a, EPD_line_buffer, p - EPD_line_buffer);
    		EPDsendArrayDMA(0x0a, EPD_line_buffer, p - EPD_line_buffer);
    		/* Output data to panel */
    		EPDwriteReg(0x02, 0x07);
    		break;
    	}
    	default: {
    
    // The output latch is written before the next line so as to free CPU while DMA works,
    // else the EPDwiteReg() will have to block until DMA to SPI transfer is done.
    		/* Output data to panel */
    		EPDwriteReg(0x02, 0x07);
    //		EPDsendArray(0x0a, EPD_line_buffer, p - EPD_line_buffer);
    		EPDsendArrayDMA(0x0a, EPD_line_buffer, p - EPD_line_buffer);
    
    	}
    	}
    }
    
    static void EPD_frame_fixed(uint8_t fixed_value, enum EPD_stage stage) {
    	for (uint8_t line = 0; line < EPD_heigth; line++)
    		EPD_one_line(line, NULL, fixed_value, NULL, stage);
    }
    
    static void EPD_frame_data(const uint8_t *image, const uint8_t *mask, enum EPD_stage stage) {
    	uint8_t line;
    
    	if (!mask) {
    		for (line = 0; line < EPD_heigth; line++) {
    			EPD_one_line(line, &image[line * (EPD_width / 8)], 0, NULL, stage);
    		}
    	} else {
    		for (line = 0; line < EPD_heigth; line++) {
    			uint16_t n = line * EPD_width / 8;
    
    			EPD_one_line(line, &image[n], 0, &mask[n], stage);
    		}
    	}
    }
    
    static void EPD_frame_FB(const uint8_t *image, const uint8_t *mask, enum EPD_stage stage) {
    	uint8_t line, linedata[FB_Xsize * 2], workingbyte;
    	register uint16_t workingword;
    
    	if (!mask) {
    		for (line = 0; line < EPD_heigth; line++) {
    
    // Populate line buffer according to FrameBuffer data
    			for (uint8_t i = 0; i < FB_Xsize; i++) {
    // Fill both bytes with default white 0 (not 0b10)! pixels. The final 0b00 to 0b01 transformation happens at EPD_one_line()
    				workingword = 0x0000;
    				workingbyte = image[(line >> 1) * FB_Xsize + i];
    
    // Set 0b11 at the position in to-be orred variable where black point should appear, provided that there is 1
    // at the current bit position in the working byte of framebuffer.
    
    // interleave bits: (byte)76543210 -> (16 bit).7.6.5.4.3.2.1
    				workingword = EPD_interleave_bits(workingbyte);
    // and duplicate 0b1 into neighboring bit so that we always get two consecutive black pixels in a row.
    				workingword |= workingword << 1;
    				/* It is so much faster with interleave bits than via this loop!
    				 for (uint8_t j = 0; j < 8; j++) {
    				 if (workingbyte & 1)	// if the last bit is set
    				 workingword |= 0x03<<(j<<1);
    				 workingbyte >>= 1;
    				 }
    				 */
    				linedata[i << 1] = (workingword >> 8);
    				linedata[(i << 1) + 1] = workingword;
    			}
    
    			EPD_one_line(line, linedata, 0, NULL, stage);
    		}
    	} else {
    		for (line = 0; line < EPD_heigth; line++) {
    			uint16_t n = line * EPD_width / 8;
    
    			EPD_one_line(line, &image[n], 0, &mask[n], stage);
    		}
    	}
    }
    
    static void EPD_frame_fixed_repeat(uint8_t fixed_value, enum EPD_stage stage) {
    	uint32_t starttime = millis();
    	uint32_t endtime = starttime + 400;
    
    	do
    		EPD_frame_fixed(fixed_value, stage);
    	while (millis() < endtime);
    }
    
    static void EPD_frame_data_repeat(const uint8_t *image, const uint8_t *mask, enum EPD_stage stage) {
    	uint32_t starttime = millis();
    	uint32_t endtime = starttime + EPD_stage_time;
    
    	do
    		EPD_frame_data(image, mask, stage);
    	while (millis() < endtime);
    }
    
    static void EPD_frame_FB_repeat(const uint8_t *image, const uint8_t *mask, enum EPD_stage stage) {
    	uint32_t starttime = millis();
    	uint32_t endtime = starttime + EPD_stage_time;
    
    	if ((stage == INVERSE) || (stage == COMPENSATE) || (stage == WHITE))
    		endtime = starttime;  // INVERSE only once
    	if ((stage == NORMAL))
    		endtime = endtime + 700;
    	do
    		EPD_frame_FB(image, mask, stage);
    	while (millis() < endtime);
    }
    
    uint8_t EPDstart(void) {
    	uint8_t i, EPDchannelSelect[8] = { 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFE, 0x00, 0x00 };
    
    // Without this, the MOSI will not be clocked-in into U0DBUF after a DMA transfer if the MOSI
    // was povered off and on again.
    	U0CSR = 0;
    	U0UCR = 0;
    	U0GCR = 0;
    	U0BAUD = 0;
    //
    	SPIinit(0, 1, 0, 1);     // USART 0 - EPD, 1 - Flash; USART 0/1, ALT 1/2, SPIMODE 0-4, MSBFisrst 1/0
    	EPDinitGPIO();
    	EPDdischargePin = 0;
    	EPDpowerPin = 0; //turn power ON
    	EPDcsPin = 1;
    	EPDresetPin = 1;
    	delay_ms(3);
    	EPDresetPin = 0;
    	delay_ms(3);
    	EPDresetPin = 1;
    	delay_ms(3);
    	EPDbusyWait();
    //Check COG ID
    	if ((EPDgetId() & 0x1F) != 0x12)
    		return 1; // error bad COG version
    	else {
    		EPDwriteReg(0x02, 0x40); //Disable Output Enable
    //Check Breakage
    		if ((EPDreadReg(0x0F) & 0x80) != 0x80)
    			return 2; // error breakage
    		else {
    			EPDwriteReg(0x0B, 0x02);                 //Power Saving Mode
    			EPDsendArray(0x01, EPDchannelSelect, 8); //Channel Select
    			EPDwriteReg(0x07, 0xD1);                 //High Power Mode Osc Setting
    			EPDwriteReg(0x08, 0x02);                 //Power Setting
    			EPDwriteReg(0x09, 0xC2);                 //Set Vcom level
    			EPDwriteReg(0x04, 0x03);                 //Power Setting
    			EPDwriteReg(0x03, 0x01);                 //Driver latch on
    			EPDwriteReg(0x03, 0x00);                 //Driver latch off
    			delay_ms(5);                             //5ms
    			i = 0;                                   //Chargepump Start
    			do {
    				EPDwriteReg(0x05, 0x01); //Start chargepump positive V VGH & VDH on
    				delay_ms(150);           // delay 150 ms
    				EPDwriteReg(0x05, 0x03); //Start chargepump neg voltage VGL & VDL on
    				delay_ms(90);            //90 ms
    				EPDwriteReg(0x05, 0x0F); //Set chargepump Vcom_Driver to ON
    				delay_ms(40);
    				if ((EPDreadReg(0x0F) & 0x40) == 0x40) //Check DC/DC
    						{
    					EPDwriteReg(0x02, 0x06); //Output enable to disable
    					break;
    				}
    			} while ((i++) != 4);
    			if (i >= 4)
    				return 3; // error charge pump
    			else
    				return 0;
    		}
    	}
    }
    
    void EPDstop(void) {
    	EPD_frame_fixed(0x00, COMPENSATE);                //Nothing frame
    	EPD_one_line(0x00, NULL, 0x00, NULL, COMPENSATE); // Dummy line
    	delay_ms(100);
    	EPDwriteReg(0x0B, 0x00); // ???
    	EPDwriteReg(0x03, 0x01); // Latch reset turn on
    	EPDwriteReg(0x05, 0x03); // Power off chargepump Vcom off
    	EPDwriteReg(0x05, 0x01); // Power off chargepump negative voltage VGL & VDL off
    	delay_ms(300);           // 150ms
    	EPDwriteReg(0x04, 0x80); // Discharge internal
    	EPDwriteReg(0x05, 0x00); // Power off chargepump positive voltage VGH & VDH off
    	EPDwriteReg(0x07, 0x01); // Turn off osc
    	delay_ms(50);
    	SPItransfer(0, (uint8_t*) 0, 1, 1); // set MOSI to low
    	EPDpowerPin = 1;                    //turn power OFF
    	delay_ms(10);
    	EPDcsPin = 0;
    	EPDresetPin = 0;
    	EPDdischargePin = 1; // Discharge output - P1_2
    	delay_ms(150);       //150ms
    	EPDdischargePin = 0; //discharge caps
    }
    
    void FBclean1(void) {
    	for (uint8_t y = 0; y < FB_Ysize; y++)
    		for (uint8_t x = 0; x < FB_Xsize; x++)
    			framebuffer[y][x] = (y % 2) ? 0x55 : 0xAA;
    }
    
    void FBclean2(void) {
    	for (uint8_t y = 0; y < FB_Ysize; y++)
    		for (uint8_t x = 0; x < FB_Xsize; x++)
    			framebuffer[y][x] = (((y >> 1) % 2) || ((y >> 2) % 4)) ? 0xF0 : 0x0F;
    }
    
    void main(void) {
    
    #ifndef SIMULATOR
    	configureClock26M();
    	startMillisTimer();
    
    	P1SEL = (P1SEL & ~BIT1); // Clear - GPIO; Set - periph. P1_1 is LED
    	LEDpin = 0;              // LED power off
    	P1DIR |= BIT1;           // LED - OUTPUT
    	LEDpin = 1;              // LED power on
    	P1SEL = (P1SEL & ~BIT1); // Clear - GPIO; Set - periph. P1_0 is FLASH Vcc
    	P1_0 = 0;                // FLASH Vcc power off
    	P1DIR |= BIT0;           // FLASH Vcc Set - OUTPUT
    	P1_0 = 1;                // FLASH Vcc power on
    
    	SPIinit(0, 1, 0, 1);     // USART 0 - EPD, 1 - Flash
    	EPDstart();
    #endif
    // FOr fixed frames: 0xAA - all white 0xFF - all black 0x00 - no change
    	FBclean1();
    	EPD_frame_fixed(0xFF, NORMAL);
    	LEDpin ^= 1;
    
    	EPD_frame_FB_repeat(framebuffer[0], NULL, COMPENSATE);
    	LEDpin ^= 1;
    	EPD_frame_FB_repeat(framebuffer[0], NULL, WHITE);
    	LEDpin ^= 1;
    	EPD_frame_fixed(0xAA, NORMAL);
    	LEDpin ^= 1;
    	FBclean2();
    //	EPD_frame_FB_repeat(framebuffer[0], NULL, INVERSE);
    	LEDpin ^= 1;
    	EPD_frame_FB_repeat(framebuffer[0], NULL, NORMAL);
    	LEDpin ^= 1;
    	EPDstop();
    	delay_ms(5000);
    
    	EPDstart();
    	FBclean2();
    	EPD_frame_fixed(0xFF, NORMAL);
    	LEDpin ^= 1;
    	EPD_frame_FB_repeat(framebuffer[0], NULL, COMPENSATE);
    	LEDpin ^= 1;
    	EPD_frame_FB_repeat(framebuffer[0], NULL, WHITE);
    	LEDpin ^= 1;
    
    	EPD_frame_fixed(0xAA, NORMAL);
    	LEDpin ^= 1;
    	FBclean2();
    	LEDpin ^= 1;
    	FBclean1();
    //	EPD_frame_FB_repeat(framebuffer[0], NULL, INVERSE);
    	LEDpin ^= 1;
    	EPD_frame_FB_repeat(framebuffer[0], NULL, NORMAL);
    	LEDpin ^= 1;
    
    //        EPD_frame_fixed_repeat(0xAA, NORMAL);
    //        LEDpin ^= 1;
    	/*
    	 // 0xaa - white 0xFF - black
    	 EPD_frame_fixed_repeat(0xaa, COMPENSATE);
    	 LEDpin ^= 1;
    	 EPD_frame_fixed_repeat(0xff, WHITE);
    	 LEDpin ^= 1;
    	 EPD_frame_fixed_repeat(0xaa, INVERSE);
    	 LEDpin ^= 1;
    	 EPD_frame_fixed_repeat(0xaa, NORMAL);
    	 LEDpin ^= 1;
    	 */
    
    #ifndef SIMULATOR
    	EPDstop();
    
    	/* The bellow code is just a stump, reading the SPI flash content into a buffer
    	 for no reason right now, as the saving/reading routines are not yet implemented
    	 */
    	SPIinit(1, 2, 0, 1);     //USART 0/1, ALT 1/2, SPIMODE 0-4, MSBFisrst 1/0
    	for (uint8_t i = 0; i < SPI_BUFFER_SIZE; i++)
    		SPIbuffer[i] = 0x00;
    	SPIbuffer[0] = 0x03;
    	SPIbuffer[1] = 0x00; //MSB first
    	SPIbuffer[2] = 0x00;
    	SPIbuffer[3] = 0x00;
    //	SPIbuffer[4] = 0x00;
    //	SPIbuffer[5] = 0x00;
    //	SPIbuffer[6] = 0x00;
    	for (uint16_t j = 0; j <= 16384; j++) {
    		SPItransfer(1, &SPIbuffer[0], 8, 0);
    		/*
    		 UART0Init();
    		 if (j == 0) UART0Send(&SPIbuffer[4],4); else UART0Send(SPIbuffer,8);
    		 */
    		for (uint8_t i = 0; i < 8; i++)
    			SPIbuffer[i] = 0x00;
    	}
    	SPICShigh(1); // put /CS high again
    	delay_us(10);
    	SPICSlow(1); // /CS low (else the IC will be powered through it)
    	P1_0 = 0;    //FLASH Power off
    	LEDpin = 0;
    #endif
    	while (1)
    		asm("NOP");
    }
    

  • Hi Tom

    Thank you so much for sharing your solution.

    BR

    Siri