Zde můžete vidět rozdíly mezi vybranou verzí a aktuální verzí dané stránky.
Obě strany předchozí revize Předchozí verze Následující verze | Předchozí verze | ||
2014:spi-oled-msp [2015/01/10 17:52] Milan Abrman |
2014:spi-oled-msp [2015/01/10 19:48] (aktuální) Milan Abrman |
||
---|---|---|---|
Řádek 48: | Řádek 48: | ||
Pokud je v menu zobrazen požadovaný parametr který má být změněn je možné třetím tlačítkem s názvem submenu prohlížet všechny podnabídky, které daný parametr umožňuje. Neustálým stiskáváním tohoto tlačítka je projížděno dokola kruhové menu, které toto tlačítko u každé nabídky vyvolává. Pokud je nalezen požadovaný parametr, který má být změněn stiskne se jednoduše poslední nastavovací tlačítko, které má název SET. Tímto stiskem dojde okamžitě k odeslání dat po SPI lince do koncového SPI zařízení a jsou nastaveny příslušné bity jeho řídícího registru, které odpovídají nastavenému parametru. Zároveň s tímto odesláním dat dojde také k zapamatování této volby. Na OLED displeji při zpětném prochází v hlavním menu, již bude na druhém řádku displeje u této volby indikován i zvolený parametr. Tím je zřetelně indikováno, jak bylo koncové zařízení nastaveno. | Pokud je v menu zobrazen požadovaný parametr který má být změněn je možné třetím tlačítkem s názvem submenu prohlížet všechny podnabídky, které daný parametr umožňuje. Neustálým stiskáváním tohoto tlačítka je projížděno dokola kruhové menu, které toto tlačítko u každé nabídky vyvolává. Pokud je nalezen požadovaný parametr, který má být změněn stiskne se jednoduše poslední nastavovací tlačítko, které má název SET. Tímto stiskem dojde okamžitě k odeslání dat po SPI lince do koncového SPI zařízení a jsou nastaveny příslušné bity jeho řídícího registru, které odpovídají nastavenému parametru. Zároveň s tímto odesláním dat dojde také k zapamatování této volby. Na OLED displeji při zpětném prochází v hlavním menu, již bude na druhém řádku displeje u této volby indikován i zvolený parametr. Tím je zřetelně indikováno, jak bylo koncové zařízení nastaveno. | ||
- | ==== Program pro řízení ==== | + | ==== Vybrané statě řídícího algoritmu ==== |
- | == Odeslání 16b proměnné == | + | == Inicializace SPI komunikace == |
<code cpp> | <code cpp> | ||
- | void send_SPI(unsigned int c) | + | void SPI_ini(void) |
{ | { | ||
- | //funkce odešle po SPI lince 16bitovou proměnnou | + | // začátek inicializace SPI |
+ | UCA0CTL1 |= UCSWRST; //nastavím | ||
+ | UCA0CTL0 = UCMST + UCSYNC + UCCKPL + UCMSB; // master mode, synchronní mód, polarita hodin,MSB | ||
+ | UCA0CTL1 = UCSSEL_2; | ||
+ | UCA0BR0 = 0x00; //předdělička nižší bity | ||
+ | UCA0BR1 = 0x00; //předdělička vyšší bity | ||
+ | UCA0MCTL = 0x00; //modulace, vždy zapsat 0 (bez modulace) | ||
+ | UCA0CTL1 &=~ UCSWRST; //vynuluji | ||
+ | //konec inicializace SPI | ||
- | unsigned int a; | + | } |
- | a=c>>8; //do a je uložena horní část 16bitové proměnné | + | </code> |
+ | |||
+ | == Odeslání dat po SPI lince do koncového SPI zařízení == | ||
+ | <code cpp> | ||
+ | void DAC_send(unsigned int c) | ||
+ | { | ||
+ | unsigned int a; | ||
+ | a=c>>8; // shiftování doprava o 8 → uložena horní polovina 16 bit proměnné | ||
- | while(!(UCA0IFG&UCTXIFG)); //čekej dokud je buffer prázdný | + | while(!(UCA0IFG & UCTXIFG)); // čekej dokud není prázdný zásobník |
- | UCA0TXBUF=(unsigned char)a; //pošli horní část proměnné | + | UCA0TXBUF=(unsigned char)a; // vloží proměnnou a do zásobníku |
- | while(!(UCA0IFG&UCTXIFG)); //čekej dokud je buffer prázdný | + | while(!(UCA0IFG & UCTXIFG)); // čekej dokud není prázdný zásobník |
- | UCA0TXBUF=(unsigned char)c; //pošli spodní část proměnné | + | UCA0TXBUF=(unsigned char)c; // vloží proměnnou c do zásobníku (horních 8 bitů) |
- | while(!(UCA0IFG&UCTXIFG)); //čekej dokud je buffer prázdný | + | while(!(UCA0IFG & UCTXIFG)); // čekej dokud není prázdný zásobník |
- | P1OUT |= BIT6; | + | P8OUT |= BIT1; // vytvořen krátký pulz (obě jsou součástí enable pro DAC) |
- | P1OUT &=~BIT6; | + | P8OUT &=~BIT1; |
- | return; | + | return; |
} | } | ||
</code> | </code> | ||
- | == Funkce pro odeslání příkazu do LCD== | + | == Funkce pro inicializaci OLED displeje== |
<code cpp> | <code cpp> | ||
- | void LCD_CMD(unsigned char command) | + | // funkce SPI_send je vytvořena naprosto stejně jako DAC_send,pouze má na jiném pinu MCU vyveden chip select |
+ | void LCD_ini(void) | ||
{ | { | ||
- | P8OUT &=~ BIT1; | + | cekej; // čekací smyčka, zpomalení komunikace pro řadič |
- | SPIDAT |= A0; //A0 do 1 | + | INIDATA = DB5 | DB4 | DB3; // nastavení příslušných inicializačních bitů |
- | SPIDAT=SPIDAT+command; | + | SPI_send (INIDATA); // pošle data po SPI |
- | SPIDAT &=~ WR; //WR vynuluji | + | cekej; |
- | send_SPI(SPIDAT); | + | INIDATA = EN | DB5 | DB4 | DB3; // inicializační bity se signálem enable pro displej |
+ | SPI_send (INIDATA); | ||
+ | cekej; | ||
+ | INIDATA = DB5 | DB4 | DB3; | ||
+ | SPI_send (INIDATA); | ||
+ | cekej; | ||
- | SPIDAT |= WR; //WR, A0 přepnu do 1 | + | INIDATA = DB1; |
- | send_SPI(SPIDAT); | + | SPI_send (INIDATA); |
+ | cekej; | ||
+ | INIDATA = EN | DB1; | ||
+ | SPI_send (INIDATA); | ||
+ | cekej; | ||
+ | INIDATA = DB1; | ||
+ | SPI_send (INIDATA); | ||
+ | cekej; | ||
+ | INIDATA = DB3 | DB2; | ||
+ | SPI_send(INIDATA); | ||
+ | cekej; | ||
+ | INIDATA = EN | DB3 | DB2; | ||
+ | SPI_send (INIDATA); | ||
+ | cekej; | ||
+ | INIDATA = DB3 | DB2; | ||
+ | SPI_send(INIDATA); | ||
+ | cekej; | ||
- | SPIDAT=SPIDAT&0xFF00; | + | INIDATA = DB0; |
- | return; | + | SPI_send(INIDATA); |
+ | cekej; | ||
+ | INIDATA = EN | DB0; | ||
+ | SPI_send (INIDATA); | ||
+ | cekej; | ||
+ | INIDATA = DB0; | ||
+ | SPI_send(INIDATA); | ||
+ | cekej; | ||
+ | |||
+ | INIDATA = DB2 | DB1; | ||
+ | SPI_send(INIDATA); | ||
+ | cekej; | ||
+ | INIDATA = EN | DB2 | DB1; | ||
+ | SPI_send (INIDATA); | ||
+ | cekej; | ||
+ | INIDATA = DB2 | DB1; | ||
+ | SPI_send(INIDATA); | ||
+ | cekej; | ||
} | } | ||
</code> | </code> | ||
- | == Funkce pro odeslání dat do LCD== | + | == Funkce pro instrukcí do LCD== |
<code cpp> | <code cpp> | ||
- | void LCD_DATA(unsigned char data) | + | void LCD_instr (unsigned int instrukce) |
{ | { | ||
- | + | LCDINSTR = instrukce; | |
- | SPIDAT=SPIDAT+data; | + | SPI_send (LCDINSTR); |
- | SPIDAT &=~ A0; //A0 vynuluji | + | cekej; |
- | SPIDAT &=~ WR; //WR vynuluji | + | LCDINSTR = EN | instrukce; |
- | send_SPI(SPIDAT); | + | SPI_send (LCDINSTR); |
- | SPIDAT |= WR; //WR, A0 přepnu do 1 | + | cekej; |
- | send_SPI(SPIDAT); | + | LCDINSTR = instrukce; |
- | + | SPI_send (LCDINSTR); | |
- | SPIDAT=SPIDAT&0xFF00; | + | cekej; |
- | return; | + | |
} | } | ||
</code> | </code> | ||
- | == Umístění kurzoru na pozici == | + | == Výpis znaku na displej == |
<code cpp> | <code cpp> | ||
- | void cursor(unsigned int pozice) | + | void LCD_znak (unsigned int znak) |
{ | { | ||
- | LCD_CMD(0x46); // cursor command | + | LCDZNAK = RS | znak; |
- | LCD_DATA((unsigned char)pozice); // lower | + | SPI_send (LCDZNAK); |
- | pozice=pozice>>8; | + | cekej; |
- | LCD_DATA((unsigned char)pozice); // higher | + | LCDZNAK = EN | RS | znak; |
+ | SPI_send (LCDZNAK); | ||
+ | cekej; | ||
+ | LCDZNAK = RS | znak; | ||
+ | SPI_send (LCDZNAK); | ||
+ | cekej; | ||
} | } | ||
</code> | </code> | ||
- | == Vynulování RAM displeje == | + | == Funkce pro výpis řetězce na displeji == |
<code cpp> | <code cpp> | ||
- | void graphics_clear(void) | + | void LCD_text(char radek, char pozice,char prepis, char text [20]) |
{ | { | ||
+ | if (radek==1) | ||
+ | { | ||
+ | LCD_instr(DB7 |0x00+pozice-1); //nastavení výpisu od požadovaného segmentu 1.řádku | ||
+ | } | ||
- | //funkce vyčistí grafickou a textovou část | + | else if (radek==2) |
- | //všechny pozice RAM jsou vyplněny nulami | + | { |
- | //bez vynulování zobrazuje LCD nahodilé symboly a grafiku | + | LCD_instr(DB7 |0x40+pozice-1); //nastavení výpisu od požadovaného segmentu 2.řádku |
+ | } | ||
- | unsigned int m=0; //proměnné pro cykly | + | char buffer[20]; |
- | unsigned int n=0; //proměnné pro cykly | + | int i=0; |
- | LCD_CMD(0x4F); | + | sprintf(buffer,text); // výpis řetězce na OLED |
- | for(m=0;m<27;m++) | + | |
- | { | + | |
+ | while (buffer[i]!=0) | ||
+ | { | ||
+ | LCD_znak(buffer[i]); | ||
+ | i++; | ||
+ | } | ||
+ | if(prepis==true) // povolení přepisu zbývajících segmentů | ||
+ | { | ||
+ | for (i;i>0;i--) | ||
+ | { | ||
+ | LCD_znak(' '); | ||
+ | } | ||
+ | } | ||
+ | else if (prepis==false) | ||
+ | { | ||
- | for(n=0;n<40;n++) | + | } |
- | { | + | } |
- | cursor(9600+m*360+n); //výběr jednotlivých sektorů paměti LCD | + | </code> |
+ | == Nastavení a povolení tlačítek == | ||
+ | <code cpp> | ||
+ | void tlacitka_ini (void) | ||
+ | { | ||
+ | // inicializace tlačítek | ||
+ | P1REN |= BIT2; // povolím pullup rezistor na portu 1 a pinu 2 | ||
+ | P1OUT |= BIT2; // připojím pullup rezistor na 2 | ||
- | LCD_CMD(0x42); //nastav zápis | + | P1REN |= BIT3; |
- | //sektor LCD vyplněn nulami | + | P1OUT |= BIT3; |
- | LCD_DATA(0x00); | + | |
- | LCD_DATA(0x00); | + | |
- | LCD_DATA(0x00); | + | |
- | LCD_DATA(0x00); | + | |
- | LCD_DATA(0x00); | + | |
- | LCD_DATA(0x00); | + | |
- | LCD_DATA(0x00); | + | |
- | LCD_DATA(0x00); | + | |
- | LCD_DATA(0x00); | + | |
- | } | + | P1REN |= BIT4; |
- | } | + | P1OUT |= BIT4; |
+ | |||
+ | P1REN |= BIT5; | ||
+ | P1OUT |= BIT5; | ||
+ | // konec inicializace tlačítek | ||
} | } | ||
</code> | </code> | ||
- | == Zápis pixelu na pozici == | + | == Přerušení od časovače, ošetření zákmitů tlačítek a přiřazení funkce tlačítům== |
<code cpp> | <code cpp> | ||
- | void write_pixel(unsigned int x,unsigned int y) | + | // Timer A0 interrupt service routine → odstranění zákmitů tlačitek |
+ | #pragma vector = TIMER0_A0_VECTOR | ||
+ | __interrupt void Timer_A0 (void) | ||
{ | { | ||
- | static int pozicnik=0; | + | if(menu<9) // nastavení pro tlačítko menu UP |
- | static int mem; | + | |
- | static int pole[20]={0}; | + | |
- | unsigned char sektor; | + | |
- | unsigned char subbit; | + | |
- | subbit=x%8; | + | |
- | sektor=(x-subbit)/8; | + | |
- | + | ||
- | if(pozicnik!=19) | + | |
{ | { | ||
- | cursor(pole[pozicnik+1]);//předchozí pozici vyčistit | + | static int old_state; |
- | LCD_CMD(0x42); | + | int new_state= P1IN & BIT2; |
- | LCD_DATA(0x00); | + | if(new_state < old_state) |
+ | { | ||
+ | btn1_pressed = true; | ||
+ | menu++; | ||
+ | } | ||
+ | old_state = new_state; | ||
} | } | ||
- | else | + | if(menu>1) // nastavevní pro tlačítko menu DOWN |
{ | { | ||
- | cursor(pole[0]);//předchozí pozici vyčistit | + | static int old_state2; |
- | LCD_CMD(0x42); | + | int new_state2= P1IN & BIT3; |
- | LCD_DATA(0x00); | + | if(new_state2 < old_state2) |
+ | { | ||
+ | btn2_pressed = true; | ||
+ | menu--; | ||
+ | } | ||
+ | old_state2 = new_state2; | ||
} | } | ||
- | mem=9600+y*40+sektor; | + | static int old_state3; // nastavení pro tlačítko submenu |
- | pole[pozicnik]=mem; | + | int new_state3= P1IN & BIT4; |
- | pozicnik++; | + | if(new_state3 < old_state3) |
- | + | ||
- | if(pozicnik==20) | + | |
{ | { | ||
- | pozicnik=0; | + | btn3_pressed = true; |
+ | submenu++; | ||
} | } | ||
+ | old_state3 = new_state3; | ||
+ | if(submenu-1 == pocet_submenu) | ||
+ | { | ||
+ | submenu=1; | ||
+ | } | ||
- | cursor(mem); | + | if (send_param!=false) // nastavení pro tlačítko SET |
- | LCD_CMD(0x42); | + | { |
- | LCD_DATA(0b10000000>>subbit); | + | static int old_state4; |
+ | int new_state4= P1IN & BIT5; | ||
+ | if(new_state4 < old_state4) | ||
+ | { | ||
+ | btn4_pressed = true; | ||
+ | } | ||
+ | old_state4 = new_state4; | ||
+ | } | ||
} | } | ||
</code> | </code> | ||
- | ==Inicializační sekvence== | ||
- | <code cpp> | ||
- | SPIDAT|= A0|WR|RD; | ||
- | send_SPI(SPIDAT); | ||
- | _delay_cycles(1000); | ||
- | SPIDAT|= RESET; | ||
- | send_SPI(SPIDAT); | ||
- | _delay_cycles(1000); | ||
- | _delay_cycles(1000); | ||
- | |||
- | // inicializační sekvence pro LCD 320x240 | ||
- | // pro jiný rozměr je nutno přepočítat | ||
- | |||
- | LCD_CMD(0x40); //system set | ||
- | LCD_DATA(0x30); | ||
- | LCD_DATA(0x07); | ||
- | LCD_DATA(0x07); | ||
- | LCD_DATA(0x27); | ||
- | LCD_DATA(0x2F); | ||
- | LCD_DATA(0xEF); | ||
- | LCD_DATA(0x28); | ||
- | LCD_DATA(0x00); | ||
- | |||
- | LCD_CMD(0x44); //scroll | ||
- | LCD_DATA(0x0); | ||
- | LCD_DATA(0x0); | ||
- | LCD_DATA(0xF0); | ||
- | LCD_DATA(0x80); | ||
- | LCD_DATA(0x25); | ||
- | LCD_DATA(0xF0); | ||
- | LCD_DATA(0x00); | ||
- | LCD_DATA(0x4B); | ||
- | LCD_DATA(0x0); | ||
- | LCD_DATA(0x0); | ||
- | |||
- | LCD_CMD(0x5A); //HDOT SCR | ||
- | LCD_DATA(0x0); | ||
- | LCD_CMD(0x5B); //ovlay | ||
- | LCD_DATA(0x01); | ||
- | LCD_CMD(0x58); //DISP ON OFF | ||
- | LCD_DATA(0x56); | ||
- | LCD_CMD(0x46); //CSRW | ||
- | LCD_DATA(0x00); | ||
- | LCD_DATA(0x00); | ||
- | LCD_CMD(0x5D); //CSR FORM | ||
- | LCD_DATA(0x04); | ||
- | LCD_DATA(0x86); | ||
- | LCD_CMD(0x59); //display ON | ||
- | </code> | ||
- | |||
==Test grafické části řízení - jezdící had== | ==Test grafické části řízení - jezdící had== | ||
<code cpp> | <code cpp> | ||
- | unsigned int px=0; | + | // 1. menu |
- | unsigned int py=0; | + | if((btn1_pressed || btn2_pressed) && menu==1) |
- | //proměnné se inkrementují tak dlouho dokud "nenarazí na kraj LCD" | + | { |
- | //což je zjištěno pomocí podmínek if(py==239), následně se vynuluje příznak | + | LCD_instr(DB0); // kompletní vymazání displeje |
- | //a proměnná se dekrementuje, poloha aktuálního bodu je zapsána pomocí write_pixel(px,py); | + | LCD_text(1,1,true,"Input"); |
+ | btn1_pressed=false; | ||
+ | btn2_pressed=false; | ||
+ | send_param=false; // deaktivace SET tlačítka | ||
+ | pocet_submenu=2; | ||
+ | submenu=0; | ||
+ | if(nastaveni1==1) // zapamatování nastavení (pokud vybrán Optical) | ||
+ | { | ||
+ | LCD_text(2,1,true,"Optical"); | ||
+ | submenu=1; | ||
+ | } | ||
+ | else if (nastaveni1==2) // zapamatování nastavení (pokud vybrán Coaxial) | ||
+ | { | ||
+ | LCD_text(2,1,true,"Coaxial"); | ||
+ | submenu=2; | ||
+ | } | ||
+ | } | ||
+ | else if(menu==1 && submenu==1) // výběr příslušného submenu 3. tlačítkem | ||
+ | { | ||
+ | send_param=true; // umožnění potvrzení tlačítkem SET | ||
+ | LCD_text(2,1,true,"Optical"); | ||
+ | if(btn4_pressed == true) // jestliže je stisknuto 4. tlačítko (SET) | ||
+ | { | ||
+ | SPDIFDAT |= BIT0; // nastavení nultého bitu na SPI zařízení do 1 | ||
+ | SPDIF_send(SPDIFDAT); | ||
+ | btn4_pressed=false; | ||
+ | nastaveni1=1; | ||
+ | } | ||
+ | } | ||
- | while(1) | + | else if(menu==1 && submenu==2) |
- | { | + | { |
- | if(xpriznak==0) | + | send_param=true; // umožnění potvrzení tlačítkem SET |
- | px++; | + | LCD_text(2,1,true,"Coaxial"); |
- | if(ypriznak==0) | + | if(btn4_pressed == true) // jestliže je stisknuto 4. tlačítko (SET) |
- | py++; | + | { |
+ | SPDIFDAT &=~ BIT0; // nastavení nultého bitu na SPI zařízení do 0 | ||
+ | SPDIF_send(SPDIFDAT); | ||
+ | btn4_pressed=false; | ||
+ | nastaveni1=2; | ||
+ | } | ||
- | if(xpriznak!=0) | + | } |
- | px--; | + | // konec 1.menu |
- | if(ypriznak!=0) | + | |
- | py--; | + | |
- | if(px==319) | + | // pro ostatní nabídky menu je algoritmus totožný pouze se změnami příslušných proměnných |
- | xpriznak=1; | + | </code> |
- | if(py==239) | + | |
- | ypriznak=1; | + | |
- | if(px==0) | + | Výsledné zařízení je zobrazeno na následujícím obrázku. V horní části obrázku se nachází vývojový kit a z něj jsou vedeny vodiče pro tlačítka a také vodiče do dvou převodníků. Prvním převodníkem je převodník sloužící jako logická sonda (zelené LED), která indikuje nastavené bity řídícího registru koncového SPI zařízení. Druhým převodníkem (červené LED) je převodník sloužící pro převod sériové linky pro paralelní 8 bitovou komunikaci OLED displeje. |
- | xpriznak=0; | + | |
- | if(py==0) | + | |
- | ypriznak=0; | + | |
- | + | ||
- | write_pixel(px,py); //zápis polohy bodu | + | |
- | + | ||
- | for(i=0;i<4000;i++) //jednoduchý delay | + | |
- | {} | + | |
- | + | ||
- | } | + | |
- | + | ||
- | return 0; | + | |
- | } | + | |
- | </code> | + | |
+ | {{ :2014:spi-oled-msp:img_20141111_214344.jpg?nolink |}} | ||
==== Závěr ==== | ==== Závěr ==== | ||
- | Funkční řízení LCD po lince SPI je možno vidět na tomto odkazu https://www.youtube.com/watch?v=FCzxzdG-qaQ . Na uvedeném testu je možno vidět postupné několikanásobné vypsání textu Hello world, následně změněna adresa zápisu a vypsáno písmeno C pro zjištění správně textové mřížky LCD. Grafická část zobrazuje jezdícího hada. Během testování se ukázalo, že k řízení by bylo vhodné zvýšit takt procesoru ( použitá vývojová SPI deska využívá takt pouze 4MHz ) případně využít procesor s rychlejším SPI modulem. Použitý LCD výrobce Raystar nedosahuje příliš vysokého kontrastu. V katalogovém listu je uvedena barva černobílá avšak černá tohoto displeje připomíná spíše modrou. Celkový kód je možno vidět zde http://pastebin.com/UqdhU00N | + | Cílem práce bylo oživit komunikaci s periferií po SPI lince. Touto periferií může být chápán například D/A převodník, či přijímač S/PDIF a mnoho dalších obvodů, které umožňují své řízení po SPI a umožňují tak SW řízení své činnosti. Cílem práce bylo také oživit komunikaci s OLED displejem a na tomto displeji vytvořit intuitivní ovládací menu přípravku. Menu mělo být přehledné a jasně informující o právě nastavených parametrech koncového obvodu. posledním důležitým blokem návrhu bylo vytvoření vhodného algoritmu pro snímání stavu tlačítek. Algoritmus musel efektivně potlačit zákmity, které se na tlačítkách vyskytují a stabilizovat tak jejich činnost a tím stabilizovat činnost celého zařízení při jeho ovládání za pomocí těchto tlačítek. S přihlédnutím na tyto požadavky a na dosažené výsledky je možné konstatovat, že zadání bylo splněno. Ovládací menu funguje spolehlivě. Spolehlivě fungují také tlačítka. Bezchybně funguje také zasílání dat po SPI lince do koncového zařízení,což bylo hlavním úkolem této práce. |