Produkty Novinky Články Návody Kontakty

SAM9260 a znakový LCD - 3. ovladač, část první

V minulých dílech našeho seriálu jsme připojili LCD displej k procesorovému modulu SAM9260 a stručně jsme si popsali způsob, jak s displejem budeme komunikovat prostřednictvím ovladače pro operační systém Linux. Co nás čeká a nemine je implementace samotného ovladače. Ta bude předmětem dalších dvou dílů.
Implementace ovladače znakového LCD displeje je jednoduchá, LCD displej není složité výstupní zařízení. Připravili jsme pro vás vzorový zdrojový kód ovladače, který je ke stažení na produktových stránkach modulu SAM9260. Zdrojový kód samotný je bohatě komentovaný, nicméně několik jeho částí stojí za podrobnější vysvětlení.
Začneme strukturou lcd1602_t, která obsahuje parametry důležité pro funkci ovladače. Za bližší zmínku stojí 32 bajtů velký bufer buffer, kam si vždy z uživatelského prostoru zkopírujeme znaky, které máme zobrazit na displeji a to včetně tzv. řídících znaků. Dalším důležitým členem je pio_base. Sem si uložíme přidělenou virtuální adresu začátku bloku řídích a datových registrů brány PIOB mikrokontroléru AT91SAM9260. Jak totiž popisujeme v teoretickém úvodu do vývoje ovladačů, přímý přístup na registry periférie mikrokontroléru přes jejich fyzickou adresu (PIOB má 0xFFFF F600) je hrubou chybou. Nejprve je třeba si zajistit výhradní přístup na blok registrů brány PIOB a pak požádat jádro o převedení fyzické adresy na virtuální adresu generovanou jednotkou MMU pro správu paměti.
struct lcd1602_t
{ 	
	// major-minor par
	dev_t			dev_num;
	// ukazatel na strukturu cdev reprezentujici znakove zarizeni
	struct cdev *		cdev_ptr;
	// ukazatel na tridu zarizeni v adresari /sys/class
	struct class *		sys_class_ptr;
	struct device * 	sys_dev_ptr;
	// hlida pocet volani operace open()
	atomic_t		open_cnt;
	// zacatek registru PIOB brany jako I/O pamet
	void __iomem *		pio_base;
	// buffer znaku
	unsigned char		buffer[BUFFER_SIZE]; 
};

Činnost ovladače začíná jeho inicializací. Nemá smysl zde popisovat kroky nutné pro začleněnní ovladače do jádra, to je dobře popsané v našem teoretickém úvodu. Mnohem zajímavější je funkce init_pio(), která zinicializuje port PIOB žádaným způsobem.
static void __init init_pio(void)
{
	// vypneme u PIO pro LCD preruseni
	pio_writel(PIO_IDR, (LCD_D0D7_PIN_MASK | LCD_RS_PIN_MASK | LCD_E_PIN_MASK));
	// vypneme u PIO pro LCD pulup odpory 	
	pio_writel(PIO_PUDR, (LCD_D0D7_PIN_MASK | LCD_RS_PIN_MASK | LCD_E_PIN_MASK));
	// nastavime PIO pro LCD na log. 0
	pio_writel(PIO_CODR, (LCD_D0D7_PIN_MASK | LCD_RS_PIN_MASK | LCD_E_PIN_MASK));
	// prepneme PIO pro LCD na vystupni PIO
	pio_writel(PIO_OER, (LCD_D0D7_PIN_MASK | LCD_RS_PIN_MASK | LCD_E_PIN_MASK));
	// pro D0-D7 povolime zapis dat primo pres PIO_ODSR
	// lze tak zapisovat rovnou cele slovo, nicmene prenesou se jen ty bity,
	// ktere maji v masce 1 v PIO_OWER
	pio_writel(PIO_OWER, LCD_D0D7_PIN_MASK);
	// prepneme PIO pro LCD na GPIO
	pio_writel(PIO_PER, (LCD_D0D7_PIN_MASK | LCD_RS_PIN_MASK | LCD_E_PIN_MASK));
}

Z komentářů v kódu funkce init_pio() je způsob nastavení portu PIOB patrný. Co však stojí za zmínku je předposlední krok, kdy v registru PIO_OWER (Output Write Enable Register) nastavujeme na log. 1 bity odpovídající pinům PB24 - PB31, tj. vývodům na které je připojena datová sběrnice LCD displeje. Proč? Protože pro datovou sběrnici využíváme funkci ”synchronous data output”. O co jde? Mikrontrolér AT91SAM9260 umožňuje nastavovat hodnotu jednotlivých výstupních pinů jeho I/O bran. Log. 1 na daném pinu nastavíme zápisem jedničky do odpovídajícího bitu v registru PIO_SODR (Set Output Data Register), log. 0 naopak zápisem jedničky do bitu v registru PIO_CODR (Clear Output Data Register). To ale znamená, že když budeme chtít na datovou sběrnici displeje, tj. na piny PB24 - PB31, zapsat znak vyjádřený binárně jako 11110000, tak musíme použít dvě operace zápisu. Jedna nastaví v půlce datové sběrnice jedničky, druhá nuly. V našem konkrétním případě to nevadí, ale obecně může být potřeba, aby na datové sběrnici byla nová data vystavena v rámci jedné operace zápisu, prostě zaráz. I toto mikrokontrolér AT91SAM9260 umí. Je totiž možné hodnotu, která má být vystavena na výstupních pinech dané I/O brány, zapsat přímo do registru PIO_ODSR (Output Data Status Register). Tento register je normálně určen pouze pro čtení, protože obsahuje poslední hodnotu zapsanou na výstupní piny, ale pro vybrané piny může fungovat i v režimu zápisu, kdy zapisovaná hodnota je přenášena rovnou na dané piny. Tato funkce se zapíná zápisem jedničky do odpovídajícího bitu v registru PIO_OWER.
Další zajimavou funkcí z fáze incializace ovladače je funkce init_lcd(), která se postará o inicializaci LCD displeje, resp. jeho řadiče. Níže uvádíme její kód.
static void __init init_lcd(void)
{
	// cekaci doba po zapnuti zapajeni
	wait_powerup();
	// nastavit zakladni parametry: 8bit, 2 radky, znaky 5x7 bodu
	write_cmd(LCD_CMD_SETUP);
	// zapnout disple
	write_cmd(LCD_CMD_DISPLAY_ON);
	// smazat displej - mohou tam byt nahodna data
	write_cmd(LCD_CMD_CLEAR);
	// nastavit posun kurzoru (ano) a posun displeje (ne)
	write_cmd(LCD_CMD_SET_INPUT_MODE);
}

Pro zápis dat do displeje se používají funkce write_char() a write_cmd(). První jmenovaná umí zapsat do LCD displeje jeden znak na aktuální pozici kurzoru, druhá slouží pro zápis instrukcí. Obě dělají víceméně totéž - nastaví signál RS do příslušné logické úrovně, zapíší jeden bajt (instrukci/znak) a počkají odpovídající dobu na zpracování dat displejem.
Nakonec je třeba zmínit funkci write_buf(), která se stará o přenesení znaků do LCD displeje a o interpretaci řídích znaků v zobrazovaném textu. Její kód popíšeme v následující části. Před vlastním rozborem jejího kódu však provedeme krátkou odbočku a probereme si různé strategie, jak z uživatelského prostoru nastavit počáteční pozici zobrazovaného textu.

Seznam dílů