Zdroj: http://prochazka.clanweb.eu/index.php?a=programy/atmega16_cecko_hd44780 • Vydáno: 28.10.2015 18:00 • Autor: hacesoft
LCD display s radičem HD44780 a mikrořadič ATMEL16...
Ovládaní dvou řádkový LCD display kompatibilní s HD44780 po 7mi drátech. Pomocí ATMELu. Konkrétně ATMEGA16- 16AU - SMD. Kde display je připojen na portu C. Kód je napsán v Céčku.
Po nějakém čase sem se opět na chvilku dostal k programování...
Potřeboval jsem ovládat dvou řádkový LCD display kompatibilní s HD44780 po 7mi drátech. Je použita platforma ATMEL. Konkrétně ATMEGA16- 16AU - SMD. Kde display je připojen na portu C. V tomto pořadí:
Text code
PORTC.0 -> LCD_RS
PORTC.1 -> LCD_RW
PORTC.2 -> LCD_E
PORTC.3 -> NC
PORTC.4 -> D4
PORTC.5 -> D5
PORTC.6 -> D6
PORTC.7 -> D7
PINy D0 až D3 na LCD nutno uzemnit.
Program je napsán v céčku, ve vývojovém prostředí Atmel Studio 7.0. pod WIN10. Jako programátor je použit: USB programátor atmel
Tento programátor se mě nepodařil rozjet ve vývojovém prostředí Atmel Studio 7.0., tak jsem nainstaloval Atmel Studio 4. V Atmel Studio 7.0. v nástrojové záložce TOOLS položka EXTERNAL TOOLS... nakonfigurujte spouštění AS4 (Atmel Studio 4). Potom samotné programování je otázkou jen třech kliknuti (Spustit AT4, CONNECT TO THE SELECTED AVR PROGRAMMER, PROGRAM) a za cca 7 sekund je naprogramováno.
Jen jedna malá poznámka ke kódu. Jak určitě víte, tak na PORTU C mikrořadiče ATMEGA16 sídlí JTAG interface. Který sice lze vypnout v průběhu programování pojistek a konfigurace, ale jako odkojený PIC mikrořadiči, nelogický postup něco někde nastavovat programátorem, to vše má bejt už v obslužném kódu. Jak jsem záhy zjistil, téměř nikdo nepoužívá SW vypnutí JTAG interface. Nedalo mě to, musel jsem na to přijít. Výsledek snažení je zde:
PIC16 code
//MCUCSR |
= (1 << JTD
); // <-- toto se nesmí použít. Kompilér to zoptimalizuje do nepoužitelné podoby!!!
//MCUCSR |
= (1 << JTD
);
MCUCSR
= 0x80; //vypne JTAG interface. Nutno dodržet že za 4clk postat tento příkaz dvakrát!!!
MCUCSR
= 0x80; // druhé potvrzující vypnutí JTAG interface. Nepoužívat bitoví maskovaní. Natvrdo poslat hodnotu celého registru!!!
/*Bits
7 - JTD
: JTAG Interface Disable
When this bit is zero /
, the JTAG interface is enabled if the JTAGEN Fuse is programmed
.
If this bit is one
, the JTAG interface is disabled
. In order to avoid unintentional disabling
or enabling of the JTAG interface
, a timed sequence must be followed when changing
this bit
: The application software must write this bit to the desired value twice within FOUR
cycles to change its value
.
Z céckového zápisu po kompilaci vypadne toto
:
278: 80 e8 ldi r24
, 0x80 ; 128
27a
: 84 bf out
0x34, r24
; 52
27c
: 84 bf out
0x34, r24
; 52
Což splňuje požadavek 4clk
.
Zapnout optimalizaci -Os! --> This is optimalizace for size
*/
Další drobný problém byl, že mikrokontrolér je taktován na 16MHz a pulzy byli strašně krátké, tak se muselo použít před každou instrukcí:
PIC16 code
LCDport SetLcdEn
;
Čekání 1us. To samé platí i pro nastavení PINu EN do LOG. 0. Také je potřeba čekat 1us. Před provedením jakékoliv operace s LCD (kromě prvních dvou při inicializaci -> tam se vždy čeká 100ms) je proveden test na BF FLAG, zda je předchozí příkaz už proveden. Když jednotlivé kódy porovnáte, zjistíte že je témeř identicky jak pro PIC procesor: čtyřbitová komunikace LCD display s radičem HD44780 a mikrořadiče PIC16Fxx
Main.c
PIC16 code
void Main_Init
(void
)
{
# warning
" >>> LCD HD44780 <<< www.prochazka.zde.cz "
#if DEBUG
#define F_CPU 1000UL // 1Khz clk
# warning
"F_CPU Definuji kmitocet oscilatoru 1 Khz pro --> MODE DEBUG --> <util/delay.h> "
#else
#define F_CPU 16000000UL // 16MHz clk
# warning
"F_CPU Definuji kmitocet oscilatoru 16 MHz pro --> MODE RELEASE --> <util/delay.h> "
#endif
#include <stdio
.h>
#include <stdlib
.h>
#include <avr/io
.h>
#include <util/delay
.h>
#include
"lcd_lib.h"
lcd_init
();
lcd_goto
(0,0);
lcd_string
(" LCD HD44780");
_delay_ms
(1000);
lcd_goto
(0,1);
lcd_string
("prochazka.zde.cz");
_delay_ms
(2000);
ScrollMessage
(0,1," Pokus rotace... ");
}
lcd_lib.c
PIC16 code
#include
"lcd_lib.h"
#include <inttypes
.h>
#include <avr/io
.h>
#include <avr/pgmspace
.h>
#include <avr/sfr_defs
.h>
#if DEBUG
#define F_CPU 1000UL // 1Khz clk
#else
#define F_CPU 16000000UL //
16 MHz clk
#endif
#include <util/delay
.h>
void lcd_busy
(void
){
LCDtris &
= ~
(1<<DB7
); //shození bitů do 0 (a zbytek ponechán stávající stav) --> SET BITs INPUT
LCDport ClearLcdRs
;
LCDport SetLcdRw
;
_delay_us
(1);
LCDport SetLcdEn
;
_delay_us
(1);
while
(bit_is_set
(PINC
, 7)) { //Nektere displaye neposilaji informaci o dokonceni
. Test na BF příznak
.
LCDport ClearLcdEn
;
_delay_us
(1);
LCDport SetLcdEn
;
_delay_us
(1);
LCDport ClearLcdEn
;
_delay_us
(1);
LCDport SetLcdEn
;
}
_delay_us
(1);
LCDport ClearLcdRw
;
LCDtris |
= (1<<DB7
); //nahození bitů do 1 (a zbytek ponechán stávající stav) --> SET BITs OUTPUT
}
//----------------------------------------------------------------
void send_prikaz
(unsigned int bCommand
){
lcd_busy
();
LCDport ClearLcdRw
;
_delay_us
(1);
LCDport SetLcdEn
;
zapis_w
(bCommand
);
LCDport ClearLcdEn
;
}
//----------------------------------------------------------------
void WriteChangeNimble
(unsigned int bCommand
){
//zkopirovat
0 az
3 bity z promenne bCommand na porty LCDport do BITu
4 az
7.
//samozrejme ze tento cely slozity zapis se da nahradid jednou ASM instrukci na prohozeni nimble a nasledne zavolat funkci zapis_w
(), bohuzel jak to napsat v Cecku je me zahadou
...
//coment is ASM for PIC
_delay_us
(1);
if
(bCommand &
(1 <<
0)){ //btfsc bCommand
,0
LCDport |
= (1<<DB4
); //bsf LCDport,4
} else
{ LCDport &
= ~
(1<<DB4
);} //bcf _PORTB,4
if
(bCommand &
(1 <<
1)){ //btfsc bCommand
,1
LCDport |
= (1<<DB5
); //bsf LCDport,5
} else
{ LCDport &
= ~
(1<<DB5
);} //bcf _PORTB,5
if
(bCommand &
(1 <<
2)){ //btfsc bCommand
,2
LCDport |
= (1<<DB6
); //bsf LCDport,6
} else
{ LCDport &
= ~
(1<<DB6
);} //bcf _PORTB,6
if
(bCommand &
(1 <<
3)){ //btfsc bCommand
,3
LCDport |
= (1<<DB7
); //bsf LCDport,7
} else
{ LCDport &
= ~
(1<<DB7
);} //bcf _PORTB,7
}
//----------------------------------------------------------------
void zapis_w
(unsigned int bCommand
){
//zkopirovat
4 az
7 bity z promenne bCommand na porty LCDport
.
//coment is ASM for PIC
_delay_ms
(1);
if
(bCommand &
(1 << DB4
)){ //btfsc bCommand
,4
LCDport |
= (1<<DB4
); //bsf LCDport,4
} else
{ LCDport &
= ~
(1<<DB4
);} //bcf _PORTB,4
if
(bCommand &
(1 << DB5
)){ //btfsc bCommand
,5
LCDport |
= (1<<DB5
); //bsf LCDport,5
} else
{ LCDport &
= ~
(1<<DB5
);} //bcf _PORTB,5
if
(bCommand &
(1 << DB6
)){ //btfsc bCommand
,6
LCDport |
= (1<<DB6
); //bsf LCDport,6
} else
{ LCDport &
= ~
(1<<DB6
);} //bcf _PORTB,6
if
(bCommand &
(1 << DB7
)){ //btfsc bCommand
,7
LCDport |
= (1<<DB7
); //bsf LCDport,7
} else
{ LCDport &
= ~
(1<<DB7
);} //bcf _PORTB,7
}
//----------------------------------------------------------------
void lcd_put_cmd
(unsigned int bCommand
){
//Posle prikaz na Display
.
//asm
("movwf _lcd_data");
send_prikaz
(bCommand
);
LCDport SetLcdEn
;
WriteChangeNimble
(bCommand
); //asm ("swapf _lcd_data , f");
LCDport ClearLcdEn
;
}
//----------------------------------------------------------------
void lcd_put_char
(unsigned char sString
){
//Pošle znak na Display
.
lcd_busy
();
LCDport ClearLcdRw
; //LCD_RW = 0;
LCDport SetLcdRs
; //LCD_RS = 1;
_delay_us
(1);
LCDport SetLcdEn
;
zapis_w
(sString
);
LCDport ClearLcdEn
;
_delay_us
(1);
LCDport SetLcdEn
;
WriteChangeNimble
(sString
); //asm ("swapf _lcd_data , f");
_delay_us
(1);
LCDport ClearLcdEn
;
}
//----------------------------------------------------------------
void lcd_string
(const char * s
){
while
(*s
)
lcd_put_char
(*s++
);
}
//----------------------------------------------------------------
void ScrollMessage
(unsigned char pozice
, unsigned char radek
,const char Message
[])
//Pohybuje textem na zvoleném řádku od zadané pozice
.
{
char TempS
[20];
unsigned int MHead
=0,Done
=0,count
;
while
(Done
==0)
{
for
(count
=0;count<20;count++)
{
TempS
[count
]=Message
[MHead+count
];
if
(Message
[MHead+count+
1]==0) Done
=1;
}
MHead++
;
lcd_goto
(pozice
, radek
);
lcd_string
(TempS
);
_delay_ms
(250);
}
}
//----------------------------------------------------------------
void lcd_scroll
(char Direction
)
//Pohybuje celým displayem
. Toto je funkce LCD
(HW
)
{
if
(Direction
==SCROLL_LEFT
)
{
lcd_put_cmd
(0b00011000
); //Scroll LEFT
}
else
{
lcd_put_cmd
(0b00011100
); //Scroll RIGHT
}
}
//----------------------------------------------------------------
void lcd_goto
(unsigned char pozice
, unsigned char radek
)
{
if
(radek >
1) radek
=1;
lcd_put_cmd
(((radek *
40) +
0x80)+ pozice
);
}
//----------------------------------------------------------------
void lcd_init1
(void
){
_delay_ms
(100);
//***************** Nastaveni
8-bitové komunikace ***************************
send_prikaz
(0x30); //8-bit-interface
_delay_ms
(100);
//*************** Nastaveni
4-bitové komunikace *****************************
send_prikaz
(0x20); //4-bit-interface
_delay_ms
(100);
//***************************************************************************
lcd_put_cmd
(0x28); //4-bit-interface, 2-lines
//lcd_put_cmd
(0b00100000
);
lcd_put_cmd
(0x08); //disp.off, curs.off, no-blink
lcd_put_cmd
(0x01); //lcd smazat
lcd_put_cmd
(0x0c); //disp.on, curs.off
//lcd_put_cmd
(0x0F); //Display ON, Cursor On, Cursor Blinking
lcd_put_cmd
(0x06); //Inkrementace, normalni mod.
}
//----------------------------------------------------------------
void lcd_init
(void
){ //Pro jistotu se provede několikrát inicializace
//MCUCSR |
= (1 << JTD
); // <-- toto se nesmi pouzit, kompiler to zoptimalizuje do nepouzitelne podoby!!!
//MCUCSR |
= (1 << JTD
);
MCUCSR
= 0x80; //vypne JTAG interface, nutno dodrzet ze za 4clk postat tento prikaz dvakrat!!!
MCUCSR
= 0x80; // drude potvrzujici vypnuti JTAG interface, nepouzivet bitove maskovani, natvrdo poslat hodnotu celeho registru!!!
/*Bits
7 – JTD
: JTAG Interface Disable
When this bit is zero
, the JTAG interface is enabled if the JTAGEN Fuse is programmed
.
If this bit is one
, the JTAG interface is disabled
. In order to avoid unintentional disabling
or enabling of the JTAG interface
, a timed sequence must be followed when changing
this bit
: The application software must write this bit to the desired value twice within FOUR
cycles to change its value
.
278: 80 e8 ldi r24
, 0x80 ; 128
27a
: 84 bf out
0x34, r24
; 52
27c
: 84 bf out
0x34, r24
; 52
Zapnout optimalizaci -Os! --> This is optimalizace for size
*/
LCDtris |
= ((1<<DB4
) |
(1<<DB5
) |
(1<<DB6
) |
(1<<DB7
) |
(1<<LcdEn
) |
(1<<LcdRw
) |
(1<<LcdRs
)); //nahození bitů do 1 (a zbytek ponechán stávající stav) --> SET PORT OUTPUT
lcd_init1
();
}
lcd_lib.h
PIC16 code
#ifndef LCD_LIB
#define LCD_LIB
#include <inttypes
.h>
#define DB4
4
#define DB5
5
#define DB6
6
#define DB7
7
#define LcdEn
2
#define LcdRw
1
#define LcdRs
0
#define SET &
=
#define CLEAR |
=
#define INVERT ^
=
#define NULLh
0
#define TRUE
1
#define FALSE
0
#define ClearLcdEn &
= ~
((1<<LcdEn
))
#define ClearLcdRw &
= ~
((1<<LcdRw
))
#define ClearLcdRs &
= ~
((1<<LcdRs
))
#define setb
(port
,pin
) port |
= 1<<pin // definuje nastavení bitu
#define clrb
(port
,pin
) port &
= ~
(1<<pin
) // definuje vynulování bitu
#define negb
(port
,pin
) port ^
= 1<<pin // definuje negování bitu
#define SetLcdEn |
= ((1<<LcdEn
))
#define SetLcdRw |
= ((1<<LcdRw
))
#define SetLcdRs |
= ((1<<LcdRs
))
#define LCDport PORTC
#define LCDtris DDRC
#define BF_FLAG
(PINC &
(1<<
5))
#define Led
(1<<
0)
#define LedSet |
= Led
#define LedClear &
= ~Led
#define LedPort PORTA
#define LedTris DDRA
#define SCROLL_LEFT
0xAA
#define SCROLL_RIGHT
0xCC