If we can just send 1 signal correctly over the MOSI line!!! Design and implementation details on the way to a valid SPI-LCD interface driver To be tackled today Review -- What is SPI? Review -- What is the SPI “master slave” relationship? How do we configure the SPI interface inside the Blackfin? How do we activate the chip-select line – PF5? Does activating the PF5 line as SPI output control mean we have to change all the SetupPF8to11ASM( ) and other routines? When do we activate the chip-select line, and how long for? What happens when there is not a new value in the SPI transmit buffer – what does the SPI interface do – it can’t do nothing – does it start transmitting zeros (which would turn out all the LEDs we just turned on One function left to be developed • • • InitHardware_SPI( ), NO_DELAY, RUN_ONCE) perhaps contains InitializeSPI (unsigned short int SPI_baudrate); and void StartSPI_ASM(void); 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 2 / 29 Lab 4 looks look – three new tasks InitScheduler( ) AddTask(InitHardware, NO_DELAY, RUN_ONCE) AddTask(Init_SPIandLCD, NO_DELAY, RUN_ONCE) AddTask(MeasureTemperatureTiming, NO_DELAY, 1); AddTask(CalculateTemperature, QUARTER_SECOND, QUARTER_SECOND) AddTask(DisplayTempertureLED, 3/8 seconds, QUARTER_SECOND) AddTask(SPI_Message_HappyXmasLCD, 4.5_SECOND, TWO_SECOND); AddTask(SPI_Message_TemperatureLCD, 5.5_SECOND, TWO_SECOND); StartScheduler( ) Loop • • GotoSleepTillInterrupt Dispatch Tasks • -- DO CODE REVIEW Init LCD Screen ( ) – This is essentially “send a special message” bool SPI_In_USE; // ‘SEMAPHORE’ (lock) short int sendMyMessage; // ‘MESSAGE’ (simple) char SPImessage[80]; // ‘MESSAGE’ body char LCDcommandString[ ] = {0x30, 0x30, 0x22, 0x00 /* end of string character */}; // LOOK AT LAB. 4 PRELAB REFERENCE SHEET #include <stdio.h> void InitLCDSreen (void) { if (SPI_In_Use == true) return; // Must wait till SPI interface is not in use by another task // SPI_In_Use is false // We want to use the SPI interface – block other tasks from using it SPI_In_Use = true; strcpy(SPImessage, LCDcommandString ); sendMyMessage = INSTRUCTION; // DEFINE INSTRUCTION AS 2 SOMEWHERE } -- DO CODE REVIEW 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 4 / 26 SendMessageSPI_LCD( ) { static int whichState = switch8HIGH; static int messageElement = 0; // Kill this task if all the elements in the message have been sent if (numberCharactersToSend == 0) { DeleteTask(sendMessageTaskNumber); SPIinUse = false; return; } -- DO CODE REVIEW 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 5 / 26 State 1 – send character + SW8 high + SW10 high IF data #define SW8_high 0x100 -- DO CODE REVIEW #define SW8_low 0x000 -- DO CODE REVIEW #define SW10_High 0x800 -- DO CODE REVIEW #define SW10_Low 0x000 -- DO CODE REVIEW switch (whichState) { case STATE1: -- DO CODE REVIEW // get the character to send and convert to short int value (SPI using 16-bit values) unsigned short int SPIvalue = SPImessage(messageElement); // Next char to send // set switch 8 high SPIvalue = SPIvalue | SW8_high; -- DO CODE REVIEW // Do we set switch 10 high if (sendMyMessage == IS_INSTRUCTION) -- DO CODE REVIEW SPIvalue = SPIvalue | SW10_high; -- DO CODE REVIEW if (!SPIReady( ) ) return; // The SPI device might not be ready if Blackfin too fast WriteSPI(SPIvalue); nextState = STATE2 break; 3/23/2016 6 / 26 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada Things we are missing All relate to SPI HARDWARE bool SPI_Ready(void) void WriteSPI(unsigned short SPIvalue); void InitSPI(void); We are going to have to read the reference sheet 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 7 / 26 Review -- Master / Slave concept Slave Select (Chip Select) We put a value into the Blackfin SPI_TDBR register – hardware does the rest Blackfin sends out active low chip select signal PF5 Blackfin sends out the “valuebits” on the MOSI signal. Slave accepts signal as SS1 is connected to PF5 Then PF5 goes high – no more information for SLAVE If we get the first step correct – then everything else should happen automatically – provided we have set up the SPI interface correctly 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 8 / 29 Review -- – Using an SPI interface to logic lab LED SPI_TDBR Blackfin Processor SLAVE SELECT PF5 used (PF0 to PF7) MOSI SPI_RXBR SPI CLOCK MISO SLAVE OUTPUT INTERFACE SLAVE INPUT INTERFACE LOAD Slave to LCD CONTROL DATA CJ7 / CJ8 output to LED 3/23/2016 SWITCHES (LOGIC LAB) SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 9 / 29 Blackfin transmits 16 bits with THIS format over the MOSI line DB7, DB6, ………DB1, DB0 E – Enable / Strobe RS 1 0 – When this line goes from high to the low, then the command is send to (latched into) LCD We don’t care at the moment 0 – LCD instruction THIS ISTo NEW STUFF make LCD respond to command 0x4F0 Blackfin must transmit R/W We justThen want “something” 0x5F0 ( E High ) 1 – Read from LCD 0x4F0 ( E low ) valid on MOSI line 0x5F0 ( E high ) 0 – Write to LCD 1 – LCD data 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 10 / 29 Start with something we know how to do – PF5 as Output signal Need to set PF5 as output to make SPI work But if we change Initialize_ProgrammableFlagsASM(); from Lab. 1 too much else MIGHT need changing TEST(GPIO_Setup_TEST) { CHANGING InitializePFIO would mean we would have to change all Tests from Lab. 1 CHANGES NEEDED FOR LAB. 10 (FAR FUTURE) BUT NOT NOW -- CAN WE DO SOMETHING ELSE? } 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 11 / 29 void ChangePFtoOutputASM(int which_pins ) Make sure nothing else changes in FIO_DIR – so read, then OR, then write ONE SOLUTION -- Review for Quiz 3 and Final exam PF5 GPIO changed to Output without changing other things #include <blackfin.h> P1.L = lo (FIO_DIR); P1.H = hi (FIO_DIR); old_value_R1 = W[P1] (Z); // Get old value -- unsigned new_value_R2 = old_value_R1 | which_pins_R0 // INPAR1 in R0 W[P1] = new_value_R2; ssync; // Force Blackfin to do the write (store) NOW not later 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 12 / 29 Blackfin interface details More slave side Blackfin side 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 13 / 29 Concept We write 16-bits (0xFF0A) into SPI_TDBR Hardware transfers this to SHIFT register • • Hardware sends out 1 bit from shift register over MOSI line to SLAVE each clock tick – speeds up to 25 MHz per bit (way faster than LEDs flashing speed) Hardware receives 1 bit over MISO line from the SLAVE and puts into shift register each clock tick – speeds up to 25 MHz per bit Hardware transfers shift register value (from slave) into SPI_RDBR (receive DBR) • 0x F F 0 A For next 16 ticks of SPI clock • SPI_TDBR now empty SPI_RDBR is now FULL This transmission over a serial line (16-bits 1 at a time) is much slower than other internal Blackfin operation • Must be careful not to overwrite last transmission with new transmission by “trying to hard” – writing values too quickly 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 14 / 29 TRY about 0x8000 to start with 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 15 / 29 SPI_registers -- Hardware Chap. 10 WHAT TO WORRY ABOUT WHAT TO IGNORE SPI_BAUD – We want to be able to change this – slow it down to ??? • • Maximum serial clock rate is ¼ of the system clock rate SCK freq = Peripheral clock frequency / 2 * SPI_BAUD -- Many Mbits SPI_FLG (Not SPI_FLAG) – do we need ChangePFtoOutputASM • • FLS5 bit – activates PF5 as slave select line FLG5 bit – could be used by us to control value of PF5 line (Slave select) • • when FLG5 bit is low, PF5 output is low, when FLG5 bit is high, PF5 output is high, However we would rather have the SPI hardware change PF5 output line at the correct time for us. Page 10-12 of the manual says “if CPHA = 0, the SPI hardware sets the output value (PF5) and the FLG5 bit (that developer sets) is ignored” MEANS WE WOULD NOT NEED TO CHANGE InitGPIOFlagsASM( ) SOUNDS GOOD TO ME – JUST “SET PLS5 TO 1” AND THE PROCESSOR DOES THE WORK -- except how do you make CPHA = 0? 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 16 / 29 SPI-registers -- more SPI_STAT – SPI Status register Has some read only bits • RO bits are changed by processor hardware – not us Has some “write 1 to clear” sticky bits which are set when error condition occurs • Remember to to write 1 to clear these bits during SPI Setup SPI_TDBR – transmit data buffer register Value written to this register is transmitted over SPI interface Writing to this register clears the SPI transmit interrupt signal • One of the questions about writing the SPI_ISR function is answered by this information • When we write to the SPI_TDBR register, the interrupt signal is cleared automatically. We don’t have to clear a bit as we would do with the core timer and general purpose timer register interrupts • However we not writing an SPI_ISR – what a shame – all that information going to waste 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 17 / 29 Status register information RO and W1C bits -- SPI_Ready( ); 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 18 / 29 Chapter 10 in reference manual and reference sheet SPI_Ready( ) code We don’t have a “ready to start ” bit – we have a “have finished” bit instead NOTE RO not R0 (Read only) SYSTEM CHANGED 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 19 / 29 All set in InitSPI( ) Set this bit after others set Separate line of code ???? ???? DON’T SENT ZERO LCD “STROBE” IS 1 -> 0 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 20 / 29 SPI_CTL register InitSPI( ) Values needed during setup TIMOD – transfer initiation mode • • 01 – Start transfer with write to SPI_TDBR. Interrupt when SPI_TDBR is empty. Timing issues possible here – get an interrupt after SPI_TDBR is empty the first time PSSE – Slave Select Enable • 0 – Disable – setting this as 1 makes this Blackfin a slave device. There might be circumstances where you want one Blackfin as master, and another as a slave – but this is not one of them. SIZE = 1 (16 bits) LSBF – Last significant bit first • 0 as we want MSBF first as that is the way the LCD interface has been designed MSTR – master • 1 as we want Blackfin to be master, not slave SPE – SPI Enable • 1 – but we might not want to do this during set-up, configure SPI up – then enable SPI as last step WOM – Write open drain master • 0 – Normal – because this was the way the interface was designed EMISO – Enable MISO to allow slave to talk to master • 0 – Not in this part of the lab GM – Get more data • 0 – when SPI_RDBR (receive buffer) is full – discard new incoming data – don’t really care at the moment 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 21 / 29 SPI_CTL register Things we still don’t understand SZ – send zeros (or last word again) when SPI_TDBR is empty • • causes what to happen? Sending zeros sounds bad as that means the LCD will think we are sending it commands CPOL – clock polarity • Means what – and do we care? CPHA – Clock Phase • • When CPHA = 1, slave select controlled by user software When CPHA = 0, slave select controlled by SPI hardware MURPHY’S RULE – any bit whose function is not obvious will be the key of whether you get the interface to work or not 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 22 / 29 VALID slave load on signal transition VALID DATA STORED Figure 10-11 SLAVE DATA IS LOADED ON L-to-H CLOCK TRANSITION Different than Slave is SELECTED when PF5 = 0 INVALID slave load on signal transition INVALID DATA STORED 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 23 / 29 We are now at the stage where we can do some experimenting with the hardware We know that the following pins are “key” to the operation of the SPI interface. MOSI – this will show the data being transmitted from the SPI interface PF5 – chip select line • • When this is pulled low, then the slave will accept any data being transmitted, otherwise data is ignored When this goes – low to high – then the serial data transmitted to the slave is “latched” (converted into a parallel signal that is then sent to LCD as a data or a command request. Place scope probes on these lines 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 24 / 29 Lab 4 quick test InitScheduler( ) AddTask(InitHardware, NO_DELAY, RUN_ONCE) AddTask(Init_SPIandLCD, NO_DELAY, RUN_ONCE) AddTask(AnythingOnMOSILine, 4.5_SECOND, TWO_SECOND); AddTask(MeasureTemperatureTiming, NO_DELAY, 1); AddTask(CalculateTemperature, QUARTER_SECOND, QUARTER_SECOND) AddTask(DisplayTempertureLED, 3/8 seconds, QUARTER_SECOND) AddTask(SPI_Message_HappyXmasLCD, 4.5_SECOND, TWO_SECOND); AddTask(SPI_Message_TemperatureLCD, 5.5_SECOND, TWO_SECOND); StartScheduler( ) Loop • • GotoSleepTillInterrupt Dispatch Tasks AnythingOnMOSILine( ) – This is essentially “send a special message” bool SPI_In_USE; // ‘SEMAPHORE’ (lock) short int sendMyMessage; // ‘MESSAGE’ (simple) char SPImessage[80]; // ‘MESSAGE’ body char SimpleTest[ ] = {0xFF, 0xAA, 0x23, 0x67, EOS /* end of string character */}; #include <stdio.h> void AnythingOnMOSILine (void) { if (SPI_In_Use == true) return; // Must wait till SPI interface is not in use by another task // SPI_In_Use is false // We want to use the SPI interface – block other tasks from using it SPI_In_Use = true; strcpy(SPImessage, SimpleTest); sendMyMessage = DATA; // DEFINE INSTRUCTION AS 2 SOMEWHERE } -- DO CODE REVIEW 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 26 / 26 Concept We write 16-bits (0xFF0A) into SPI_TDBR Hardware transfers this to SHIFT register • 0x F F 0 A For next 16 ticks of SPI clock • • Hardware sends out 1 bit from shift register over MOSI line to SLAVE each clock tick – speeds up to 25 MHz per bit Hardware receives 1 bit over MISO line from the SLAVE and puts into shift register each clock tick – speeds up to 25 MHz per bit Hardware transfers shift register value (from slave) into SPI_RDBR (receive DBR) • SPI_TDBR now empty SPI_RDBR is now FULL This transmission over a serial line (16-bits 1 at a time) is much slower than other internal Blackfin operation • Must be handled via interrupt control 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 27 / 29 Lab. 4 interface SPI from Blackfin master (MOSI, MISO, CLK, PF5 SPI to interface (slave) (MOSI, MISO, CLK, slave select ) LINES TO LOGIC LAB LED LOOK HERE FOR EXPECTED SIGNALS 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 28 / 26 What we should see on scope Sending 0xFF, 0xAA, 0x23, 0x67, EOS Make SPI_BAUD very large (very slow) Connect Lab. 4 interface to logic lab lights (top) See 0x05FF, 0x04FF, 0x05FF Then 0x05AA, 0x04AA, 0x05AA Then 0x0523, 0x0423, 0x0523 Then 0x0567, 0x0467, 0x0567 3/23/2016 IN ACTUAL LAB YOU’LL GET A TEST PROGRAM -RACING LIGHTS SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 29 / 29 Things we are missing All relate to SPI HARDWARE bool SPI_Ready(void) { Check SPI Finished bit in SPI status register } void WriteSPI(unsigned short SPIvalue) { Write the value to SPI transmit buffer register } void InitSPI(void) { Set slave select in SPI flag register Set Baud rate Set all necessary bits in SPI Control register Enable SPI interface } 3/23/2016 30 / 26 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada Final Lab. 3 and Lab. 4 InitScheduler( ) -- Lab 3 looks something like this AddTask(InitHardware, NO_DELAY, RUN_ONCE) AddTask(MeasureTemperatureTiming, NO_DELAY, 1); AddTask(CalculateTemperature, QUARTER_SECOND, QUARTER_SECOND) AddTask(DisplayTempertureLED, 3/8 seconds, QUARTER_SECOND) AddTask(FlashLED6, NO_DELAY, EIGHTH_SECOND StartScheduler( ) Loop • • GotoSleepTillInterrupt Dispatch Tasks 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 31 / 29 Lab 4 looks look – three new tasks InitScheduler( ) AddTask(InitHardware, NO_DELAY, RUN_ONCE) AddTask(Init_SPIandLCD, NO_DELAY, RUN_ONCE) AddTask(MeasureTemperatureTiming, NO_DELAY, 1); AddTask(CalculateTemperature, QUARTER_SECOND, QUARTER_SECOND) AddTask(DisplayTempertureLED, 3/8 seconds, QUARTER_SECOND) AddTask(SPI_Message_HappyXmasLCD, 4.5_SECOND, TWO_SECOND); AddTask(SPI_Message_TemperatureLCD, 5.5_SECOND, TWO_SECOND); StartScheduler( ) Loop • • GotoSleepTillInterrupt Dispatch Tasks • -- DO CODE REVIEW Code reminder – using sprintf( ) Xmas message and temperature I want to put the temperature (as a number) into a string that gets transmitted over SPI – how would I do that using “C”? volatile bool SPI_In_USE; // ‘SEMAPHORE’ (lock) volatile short int sendMyMessage; // ‘MESSAGE’ (simple) volatile char SPImessage[80]; // ‘MESSAGE’ body volatile float temperature; // Calculated as a Lab. 3 Task // Know terms in red for final #include <stdio.h> -- DO CODE REVIEW void SPI_Message_TemperatureLCD(void) { if (SPI_In_Use == true) return; // Must wait till SPI interface is not in use by another task // SPI_In_Use must be false – therefore we can change it and use SPI // We want to use the SPI interface – block other tasks from using it SPI_In_Use = true; sprintf(SPImessage, “Temperature %f4.2 C”, temperature); sendMyMessage = DATA; // DEFINE DATA AS 1 SOMEWHERE } 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 33 / 26 Init LCD Screen ( ) – This is essentially “send a special message” bool SPI_In_USE; // ‘SEMAPHORE’ (lock) short int sendMyMessage; // ‘MESSAGE’ (simple) char SPImessage[80]; // ‘MESSAGE’ body char LCDcommandString[ ] = {0x30, 0x30, 0x22, 0x00 /* end of string character */}; // LOOK AT LAB. 4 PRELAB REFERENCE SHEET #include <stdio.h> void InitLCDSreen (void) { if (SPI_In_Use == true) return; // Must wait till SPI interface is not in use by another task // SPI_In_Use is false // We want to use the SPI interface – block other tasks from using it SPI_In_Use = true; strcpy(SPImessage, LCDcommandString ); sendMyMessage = INSTRUCTION; // DEFINE INSTRUCTION AS 2 SOMEWHERE } -- DO CODE REVIEW 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 34 / 26 We now need to do the message handler task -- DO CODE REVIEW enum {NO_MESSAGE, DATA, INSTRUCTION}; // Be able to explain “enum” for final void SPI_MessageHandler(void) { if ( sendMyMessage == NO_MESSAGE) return; // Message to send – start up the sending task numberCharactersToSend = strlen(SPIMessage); sendMessageTaskNumber = AddTask(SendMessageSPI_LCD, NO_DELAY, AS_FAST_AS_POSSIBLE); // Start with as FAST_AS_POSSIBLE with a slow value -- second / 20; } -- DO CODE REVIEW 3/23/2016 SPI and LCD , Copyright M. Smith, ECE, University of Calgary, Canada 35 / 26