Cours:TDs 2103
Nous allons mettre dans cette série de TDs un ensemble de travaux réalisés en groupes de TD mais en salle informatique.
Sommaire
- 1 Réalisation du dé avec le shield de l'IUT
- 2 Initiation au timer 0
- 3 Timer 0 en interruption
- 4 Reprises des questions non faites par les étudiants
- 5 Utilisation du shield LCD keypad
Réalisation du dé avec le shield de l'IUT
Nous désirons réaliser le dé avec le shield que l'on utilise régulièrement à l'IUT. Les leds étant alignées, elles ne présentent pas une organisation facilement exploitable pour un affichage de type "dé à jouer". Nous décidons donc d'afficher directement le chiffre sur un afficheur sept segments.
Npus allons utiliser aussi (comme dans l'exercice du TD) un tableau précalculé de valeurs. Comme l'afficheur utilise partiellement deux PORTs (PORTD et PORTB) nous décidons d'utiliser la configuration du tableau ci-dessous.
PORT | PD7 | PD6 | PB5 | PB4 | PB3 | PB2 | PB1 | PB0 |
---|---|---|---|---|---|---|---|---|
segment | d | c | a | b | dp | f | g | e |
L'ordre des segments peut sembler étrange mais il est lié au fait que l'on veut une affectation simple des deux PORTs pour réaliser l'affichage. Mais il complique un peu le précalcul des valeurs du tableau.
Exercice 1 : calculer les valeurs pour afficher 0, 1, 2, 3, 4 , 5 et 6.
Affichage du tableau
Exercice 2 : après ce calcul on fera un programme de test qui affiche toutes les secondes toutes les valeurs possible du dé. On prendra bien soin de gérer la sélection de l'afficheur (de droite avec PD4 à 1).
Utilisation du pseudo aléatoire
On rappelle qu'en TD on vous propoe le code suivant :
unsigned char pseudoAleat(int Lim) {
unsigned char Result;
static unsigned int Y=1;
Y = (Y * 32719 + 3) % 32749;
Result = ((Y % Lim)+1);//+1 : eviter 0
return Result;
}
Exercice 3 : Réaliser un programme qui fait défiler les valeurs de cette fonction pseudo aléatoire.
Gestion du bouton de lancement
On utilisera le bouton B qui est en pullup sur PC0. On rappelle que le PORTC en lecture est disponible avec PINC !!!
Exercice 4 : après avoir mis au point une boucle d'attente d'un appui sur le bouton B, réaliser le programme complet qui attend un appui du bouton B et lance le générateur pseudo-aléatoire pour afficher le résultat.
Correction à retirer plus tard :
#include <util/delay.h>
unsigned char pseudoAleat(int Lim) {
unsigned char Result;
static unsigned int Y=1;
Y = (Y * 32719 + 3) % 32749;
Result = ((Y % Lim)+1);//+1 : eviter 0
return Result;
}
int main(){
unsigned char tableau[7]={0xF5,0x50,0xB3,0xF2,0x56,0xE6,0xE7};
unsigned char i;
// setup()
DDRD = 0xD0; // PD7, PD6 , PD4 en sortie
DDRB = 0x3F; // PB0 -> PB5 en sortie
DDRC = 0x00; // PC0 en entree
PORTD |= 0x10; // selection afficheur droit
// loop()
while(1) {
while ((PINC & 0x01)==0x01)
_delay_ms(300);
i= pseudoAleat(6);
PORTD = tableau[i] & 0xD0;
PORTD |= 0x10; // selection afficheur droit
PORTB = tableau[i] & 0x3F;
// _delay_ms(1000);
}// while(1)
return 0;
}
Gestion de deux dés
C'est le bit PD4 du PORTD qui est responsable de la sélection d'un afficheur :
- PD4 = 1 c'est l'afficheur de droite qui est en action
- PD4 = 0 c'est l'afficheur de gauche qui est en action
Si vous voulez afficher deux valeurs, il vous faut les écrire en synchronisme avec la commutation des afficheurs. Cette commutation doit etre réalisée à plus de 25 Hz pour un fonctionnement correct (sans scintillation pour les yeux).
Exercice 5 : Réaliser un programme qui affiche sur deux afficheurs deux résultats de la fonction pseudo-aléatoire. Il faut afficher pendant un certain temps pour que l’œil perçoive !
Exercice 6 : Réaliser un programme complet qui attend que l'on appui sur le bouton B et qui alors utilise deux fois la fonction pseudo-aléatoire pour trouver la valeur des deux dé et qui affiche alors les deux valeurs sur les deux afficheurs.
Initiation au timer 0
Notre objectif est de reprendre un exercice de TD et de le réaliser de manière plus complète en salle d'informatique.
Étant donné que l'exercice présenté plus loin utilise la liaison série que nous n'avons pas appris à utiliser directement en C, vous utiliserez le langage Arduino pour réaliser l'exercice correspondant. La manipulation de PORTs se fera par contre directement en C.
Rappel de la documentation du timer 0
La documentation officielle du timer 0 fait 10 pages. Nous utilisons quant à nous une série de dessins résumant le fonctionnement de certaines parties.
Voici un premier dessin :
Rappel de la documentation du shield utilisé
Puisque nous allons utiliser les LEDs pour afficher une valeur binaire sur 8 bits, voici la documentation correspondante :
Numéro | f5 | f4 | f3 | f2 | f1 | f0 | p1 | p0 |
---|---|---|---|---|---|---|---|---|
Couleur | r | o | v | r | o | v | v | r |
Arduino Pin | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 |
Port Arduino UNO | PB5 | PB4 | PB3 | PB2 | PB1 | PB0 | PD7 | PD6 |
Port Arduino LEONARDO | PC7 | PD6 | PB7 | PB6 | PB5 | PB4 | PE6 | PD7 |
Seule la ligne correspondant à l'Arduino UNO nous intéresse dans la suite.
Exercice
Question 1
Le site : convert base vous propose un algorithme de division par 10 que voici :
unsigned int A;
unsigned int Q; /* the quotient */
Q = ((A >> 1) + A) >> 1; /* Q = A*0.11 */
Q = ((Q >> 4) + Q) ; /* Q = A*0.110011 */
Q = ((Q >> 8) + Q) >> 3; /* Q = A*0.00011001100110011 */
/* either Q = A/10 or Q+1 = A/10 for all A < 534,890 */
Sans chercher à comprendre l'algorithme de division, on vous demande de le transformer en une fonction de prototype :
unsigned int div10(unsigned int A);
Indication
unsigned int div10(unsigned int A){
// a compléter ICI : ceci a été réalisé en TD en salle !!!
}
Ne pas chercher à réaliser un test pour le moment mais réaliser une compilation pour retirer les fautes de syntaxe.
Question 2
Les LEDs du shield maison sont couplées à un arduino UNO. Écrire un sous-programme capable d'afficher un nombre sur 8 bits sur les LEDs. Réaliser un programme de test à l'aide d'un compteur par exemple et/ou mieux qui utilise la division par 10 et sort sur les LEDs.
Indication
Comme l'indique le commentaire ci-dessous, le sous-programme que vous réaliserez ne devra en aucun cas changer d'autres bits que ceux que l'on utilise pour l'affichage des LEDs !!!
//************************************
// Ne modifie que les bits concernés
// pour les deux PORTs concernés
//************************************
void afficheLeds(unsigned char ch){
// A compléter ici. On n'utilisera que des décalages
//et des masques pour réaliser cette fonction
}
Question 3
Écrire un programme complet qui mesure le temps d'exécution du sous programme de division par 10 avec le timer 0, puis modifier le programme pour qu'il puisse comparer avec une division par 10 normale. On pourra utiliser un front montant sur le bouton A pour choisir le type de division réalisé.
Indications
Nous trouvons sur la page de documentation le tableau suivant :
Un programme gérant la détection de front ressemblera à : char etatPresent=0,etatPasse=0;
unsigned char etatSortie=0;
void setup()
{
..... // configuration des e/s
}
void loop()
{
etatPasse=etatPresent; // mémorise l'état précédent (le présent devient le passé)
etatPresent=digitalRead(??); // lecture de la valeur actuelle
if ( ( etatPresent == ?? ) && ( etatPasse == ?? ) ) // si appui alors ....
{
.....
}
}
|
Question 4
Modifier le programme de la question 3 pour qu'au lieu d'afficher sur des LEDs, l'affichage se fasse par la liaison série. On prendra soin d'afficher en même temps le type d'algorithme utilisé et le temps de la durée. On pourra améliorer l'affichage en affichant la durée en ms. Ceci sera fait dans un premier temps en utilisant directement le timer 0, puis dans un deuxième temps en utilisant la primitive Millis.
Indication
- Vous ne pouvez pas utiliser la primitive delay pour ne pas saturer la liaison série car toute manipulation du timer 0 l'emêchera de fonctionner normalement.
- Serial est la documentation sur la liaison série
- Millis donne les millisecondes écoulées depuis le début du programme
La primitive Millis utilise le timer 0 de manière transparente. Mais vous ne pourrez pas l'utiliser si vous utilisez déjà le timer 0.
Question 5 : le mode de scrutation du flag
Nous devons savoir à ce niveau, que tout débordement du timer0 (passage de 0xFF à 0x00) entraîne le positionnement du flag TOV0, bit b0 du registre TIFR. Vous pouvez donc utiliser ce flag pour déterminer si vous avez eu débordement du timer0, ou, en d’autres termes, si le temps programmé est écoulé. Cette méthode à l’inconvénient de vous faire perdre du temps inutilement dans une boucle d'attente.
Petit exemple :
while ((TIFR & 0x01) == 0); //attente passive
Réaliser un chenillard sur les 4 LEDs en utilisant le sous programme de la question 2 et le timer 0 (à régler correctement pour un chenillard visible).
Indication 1
On donne le programme suivant concernant le timer 0 :
int main(void){
// initialisation du timer division par 8
TCCR0 |= 0x02; // prescaler 8 , entrée sur quartz
TCNT0 = 0x00; // tmr0 : début du comptage dans 2 cycles
TIFR |= 0x01; // clr TOV0 with 1
// bit RB0 du PORTB en sortie
DDRB |= 0x01; //RB0 as output
while(1) {
TIFR |= 0x01; // clr TOV0 with 1 : obligatoire !!!
while ((TIFR & (0x01<<TOV0)) == 0);
// ce qui est fait ici est fait tous les 256 comptages de TCNT0
PORTB ^= 0x01; // on bascule avec ou exclusif
// TIFR &= ~(1 << TOV0); // reset the overflow flag
}
return 0;
}
Pouvez-vous donner la fréquence d'oscillation du bit b0 du PORTB avec quelques explications ? Modifiez-le pour le transformer avec un setup() et un loop() et une fréquence visible à l’œil si votre quartz est à 16 MHz et que votre oeil ne peut distinguer que les fréquences inférieures à 25 Hz (2 à 5 Hz serait très bien pour ce chenillard).
Indication 2
- Le registre TIMSK de l'ATMega8 est à remplacer par le registre TIMSK0 pour l'ATMega328.
- Le registre TCCR0 de l'ATMega8 est à remplacer par le registre TCCR0B pour l'ATMega328.
- Le registre TIFR de l'ATMega8 est à remplacer par le registre TIFR0 pour l'ATMega328.
Timer 0 en interruption
Rappel sur la documentation de l'interruption du timer 0
La mise à zéro du bit TOV0 semble être complètement automatique dans les ATMega dès qu'il y a une interruption.
Pour comprendre cette figure, il suffit de se rappeler qu'un front montant dans l'ellipse rouge réalisera cette-interruption. En C cette interruption est désignée par "TIMER0_OVF_vect". Si vous avez compris que ce n'est pas le logiciel qui positionnera le bit TOV0 mais le matériel, alors vous déduisez que pour réaliser une interruption il suffit de
- mettre à 1 le bit TOIE0 du registre TIMSK pour l'ATMega8. Pour l'ATMega328 ce registre s'appelle TIMSK0.
- mettre à 1 le bit I du registre SREG. Ceci se réalise par l'instruction "sei();" en C et "interrupts();" avec l'Arduino.
Réalisation d'un afficheur sept segments sur deux digits
Nous allons réaliser à l'aide d'une interruption la commutation des deux digits d'un affichage sept segments.
Utilisation du travail déjà fait au précédent TD
Pour ne pas réinventer la roue, nous allons utiliser le sous programme que l'on a mis au point pour les LEDs :
//************************************
// Ne modifie que les bits concernés
// pour les deux PORTs concernés
// Version pour UNO
//************************************
void afficheLeds(unsigned char ch){
unsigned char ch_partie;
ch_partie = (ch << 6) & 0xC0;
PORTD &= ~0xC0; // effacement de PD7 et PD6
PORTD |= ch_partie; // seuls les 1 seront écrits
ch_partie = (ch >> 2) & 0x3F;
PORTB &= ~0x3F; // effacement PB5... PB0
PORTB |= ch_partie; // seuls les 1 seront écrits
}
Nous allons garder ce sous-programme ce qui aura des conséquences sur l'ordre des segments dans un octet.
Connexion du shield utilisé aux sept segments
Les 2 afficheurs ne peuvent pas être utilisés simultanément. L'état de la sortie mux (arduino port 4 ou PD4) permet de sélectionner l'un ou l'autre. En allumant successivement l'un puis l'autre rapidement, on a l'illusion qu'ils sont tous 2 allumés. Les segments des afficheurs sont câblés de façon analogue comme décrit ci dessous :
Segment | pt | g | f | e | d | c | b | a |
---|---|---|---|---|---|---|---|---|
Arduino Pin | 11 | 9 | 10 | 8 | 7 | 6 | 12 | 13 |
Port UNO | PB3 | PB1 | PB2 | PB0 | PD7 | PD6 | PB4 | PB5 |
Voici sous forme schématique la documentation correspondante :
Exercice
Question 1
Réaliser un tableau permettant un transcodage suivant le principe :
index 0 | affichage du 0 |
---|---|
index 1 | affichage du 1 |
index n | affichage du n |
index 15 | affichage du F |
Ce tableau sera du type : unsigned char tab[]={0xD7,...};
Essayer de comprendre la première valeur donnée (0xD7) avant de continuer : pourquoi 0xD7 fourni à afficheLeds affiche un zéro ?
Indication
et
sachant que c'est le deuxième qui est utilisé par "afficheLeds" |
Question 2
Utiliser le tableau de la question 1 pour réaliser un affichage d'un compteur hexadécimal sur un seul digit comme ceci :
Question 3
Mettre en place une interruption déclenchée environ une soixantaine de fois par seconde chargée d'afficher soit le poids faible soit le poids fort d'une variable globale (de type unsigned char donc sur 8 bits).
Faire un programme de test qui compte toutes les secondes sur 8 bits et affiche sans arrêt le résultat.
Indications
//*******************************
// UNO + shield OK
//*******************************
#include <avr/io.h>
#include <avr/interrupt.h>
#undef F_CPU
#define F_CPU 16000000UL
#include <util/delay.h>
void afficheLeds(unsigned char ch);
volatile unsigned char toPrint;
volatile unsigned char tab[]={0xD7,...,0x9C};
//************************************
// Interruption Timer 0 Overflow
//************************************
ISR(TIMER0_OVF_vect) {
//***********************
// A COMPLETER ICI
//***********************
}
int main() {
//***********************
// A COMPLETER ICI setup
//***********************
while(1) {
//***********************
// A COMPLETER ICI loop
//***********************
_delay_ms(1000); // attente d'une seconde
}
return 0;
}
//************************************
// Ne modifie que les bits concernés
// pour les deux PORTs concernés
// Version pour UNO
//************************************
void afficheLeds(unsigned char ch){
unsigned char ch_partie;
ch_partie = (ch << 6) & 0xC0;
PORTD &= ~0xC0; // effacement de PD7 et PD6
PORTD |= ch_partie; // seuls les 1 seront écrits
ch_partie = (ch >> 2) & 0x3F;
PORTB &= ~0x3F; // effacement PB5... PB0
PORTB |= ch_partie; // seuls les 1 seront écrits
}
|
#!/bin/bash
########### Pour UNO
avr-gcc -g -mmcu=atmega328p -Wall -Os -c timer0Inter.c
avr-gcc -g -mmcu=atmega328p -o timer0Inter.elf -Wl,-Map,timer0Inter.map timer0Inter.o
#avr-objdump -h -S hello.elf > hello.lss
#avr-objcopy -O binary -R .eeprom hello.elf hello.bin
avr-objcopy -R .eeprom -O ihex timer0Inter.elf timer0Inter.hex
/usr/share/arduino/hardware/tools/avrdude -C/usr/share/arduino/hardware/tools/avrdude.conf -v -v -v -v -patmega328p -carduino -P/dev/ttyACM0 -b115200 -D -V -Uflash:w:timer0Inter.hex:i
pour les cartes UNO. Copier et coller ce script dans un éditeur de texte et sauvez-le en "uno.sh" par exemple. Donnez lui les droit d'exécution en tappant chmod +x uno.sh La compilation se fera avec "./uno.sh" |
Question 4
Reprendre la question 3 et la modifier pour réalise un compteur décimal sur deux digits.
Question 5
On revient à l'environnement Arduino puisqu'on abandonne l'interruption du TIMER0.
Objectif : utiliser ce qui a été fait avec le dé dans le TD 3 (exercice 3) pour réaliser un affichage de deux dés sur les deux afficheurs.
1°) Vous pourrez commencer par vérifier le caractère pseudo-aléatoire du sous-programme fourni :
unsigned char pseudoAleat(int Lim) {
unsigned char Result;
static unsigned int Y=1;
Y = (Y * 32719 + 3) % 32749;
Result = ((Y % Lim)+1); //+1 : eviter 0
return Result;
}
en l'utilisant en mode Arduino avec la liaison série pour voir la suite de valeur de cette fonction avec un appel "serial.println(peseudoAleat(6));"
2°) Utiliser la détection d'un appui sur le bouton A pour lancer les deux dés et afficher les deux résultats.
Indications
Nous trouvons sur la page de documentation le tableau suivant :
Bouton | Position | Arduino Pin | Port | Interruption | Niveau logique de l'entrée arduino si bouton appuyé |
---|---|---|---|---|---|
A | Bas Gauche | 2 | PD2 | int0 | 1 |
D | Haut Gauche | 3 | PD3 | int1 | 1 |
B | Bas Droite | A0 | PC0 | 0 | |
C | Haut Droite | A1 | PC1 | 0 |
Un programme gérant la détection de front ressemblera à :
char etatPresent=0,etatPasse=0;
unsigned char etatSortie=0;
void setup()
{
..... // configuration des e/s
}
void loop()
{
etatPasse=etatPresent; // mémorise l'état précédent (le présent devient le passé)
etatPresent=digitalRead(??); // lecture de la valeur actuelle
if ( ( etatPresent == ?? ) && ( etatPasse == ?? ) ) // si appui alors ....
{
.....
}
}
3°) Pour augmenter le caractère aléatoire, on désire introduire le temps.
Reprises des questions non faites par les étudiants
Exercice 1
On vous donne la correction de la question 3 du TD sur le timer 0 sur la carte Arduino UNO :
void setup() {
// les bits correspondants en sortie pour les LEDs
DDRB |= 0x3F;
DDRD |= 0xC0;
// le bouton A en entrée
pinMode(2,INPUT);
// initialisation du timer avec prescaler 256 ,
TCCR0B |= _BV(CS02);
TCCR0B &= ~(_BV(CS01) | _BV(CS00));
}
//************************************
// Ne modifie que les bits concernés
// pour les deux PORTs concernés
// Version pour UNO
//************************************
void afficheLeds(unsigned char ch){
unsigned char ch_partie;
ch_partie = (ch << 6) & 0xC0;
PORTD &= ~0xC0; // effacement de PD7 et PD6
PORTD |= ch_partie; // seuls les 1 seront écrits
ch_partie = (ch >> 2) & 0x3F;
PORTB &= ~0x3F; // effacement PB5... PB0
PORTB |= ch_partie; // seuls les 1 seront écrits
}
unsigned int div10(unsigned int A){
unsigned int Q; /* the quotient */
Q = ((A >> 1) + A) >> 1; /* Q = A*0.11 */
Q = ((Q >> 4) + Q) ; /* Q = A*0.110011 */
Q = ((Q >> 8) + Q) >> 3; /* Q = A*0.00011001100110011 */
/* either Q = A/10 or Q+1 = A/10 for all A < 534,890 */
return Q; // ne pas oublier le return pour une fonction
}
void loop() {
static char etatPresent=0,etatPasse=0,algo=0;
unsigned char temps,i;
unsigned int res;
etatPasse=etatPresent; // mémorise l'état précédent (le présent devient le passé)
etatPresent=digitalRead(2); // lecture de la valeur actuelle
if ( ( etatPresent == LOW ) && ( etatPasse == HIGH ) )
algo++; // sera utilisé pour son poids faible
TCNT0 = 0x00; // algorithme de calcul ici plusieurs fois
for(i=0;i<55;i++) {
if (algo & 0x01)
res = div10(i+15558);
else
res = (i+15558)/10;
PORTC = res; // autrement simplifié
} // fin de la mesure : lecture du timer
temps=TCNT0;
afficheLeds(temps);
delay(100);
}
1°) Montrer le fonctionnement normal de ce programme. Sur quel bouton faut-il appuyer pour un fonctionnement qui montre la différence de fonctionnement entre les deux algorithmes. Faites constater.
2°) La programmation du timer 0 compromet le fonctionnement de plusieurs primitives Arduino. Vous allez donc retirer dans ce programme tout ce qui concerne la programmation du timer 0 et le remplacer par la primitive Millis. Le temps sera maintenant mesurer en millisecondes et on continuera d'afficher le résultat sur nos LEDs. Faites constater.
ATTENTION : si vous voulez voir quelque chose avec la nouvelle unité il faut augmenter le nombre de boucle d'utilisation de l'algorithme de 55 à 1024 !!!!
3°) Modifier le programme de la question 2°) pour qu'au lieu d'afficher sur des LEDs, l'affichage se fasse par la liaison série. On prendra soin d'afficher en même temps le type d'algorithme utilisé et le temps de la durée. Pour ne pas saturer la liaison série, on utilisera un delay d'au moins 1000 !!! On en profitera aussi pour augmenter encore le nombre de boucles à 1100 : attention il faut changer de type pour i ! Faites constater.
Exercice 2
On désire reprendre la question 5 sur le timer 0 en interruption (qui n'utilise pas l'interruption du timer en fait). L'objectif est d'utiliser l'environnement Arduino ( avec "setup()" et "loop()" de donc sans "main()" ) pour réaliser un lancer de deux dés.
1°) Vous pourrez commencer par vérifier le caractère pseudo-aléatoire du sous-programme fourni :
unsigned char pseudoAleat(int Lim) {
unsigned char Result;
static unsigned int Y=1;
Y = (Y * 32719 + 3) % 32749;
Result = ((Y % Lim)+1); //+1 : eviter 0
return Result;
}
en l'utilisant en mode Arduino avec la liaison série pour voir la suite de valeurs de cette fonction avec un appel "serial.println(pseudoAleat(6));". Faites constater. Encore une fois un delay(1000) ne sera pas de trop pour ne pas saturer la liaison série.
2°) Utiliser la détection d'un appui sur le bouton A pour lancer les deux dés et afficher les deux résultats (encore dans la liaison série). Maintenant, plus besoin du delay(1000) !!! Faites constater.
3°) Pour augmenter le caractère aléatoire, on fait tourner sans arrêt "pseudoAleat(6)" jusqu'à un front montant sur le bouton A. C'est ce résutat qui sera la valeur du premier dé... et pseudoAleat tourne de nouveau sans arrêt jusqu'à un front descendant sur A. C'est ce deuxième résultat qui sera le résultat du deuxième dé. Faites constater.
4°) On garde le programme de la question 3°) sauf que maintenant on doit afficher sur les deux afficheurs 7 segments. Faites constater.
On rappelle que le tableau qui permet d'utiliser afficheLeds() sur un afficheur 7 segments est :
volatile unsigned char tab[]={0xD7,0x41,0xCE,0xCB,0x59,0x9B,0x9F,0xC1,0xDF,0xDB,0xDD,0x1F,0x96,0x4F,0x9E,0x9C};
Utilisation du shield LCD keypad
On vous donne un exemple de programme :
// lcd.c
//
// Copyright 2014 Michel Doussot <michel@mustafar>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301, USA.
#include <avr/io.h>
#define RS 0x01
#define E 0x02
//#define RW 0x08
#define DATAS 0xF0
// unite approximative 2us
void delai(unsigned long int delai) {
volatile long int i=0;
for(i=0;i<delai;i+=1);
}
// portB :
// b7 b6 b5 b4 b3 b2 b1 b0
// D3 D2 D1 D0 RW RS E CE0
// Arduino UNO
//PORTD : b7 b6 b5 b4
// D3 D2 D1 D0
// PORTB :b1 b0
// E RS
void rs_haut(void) {
PORTB = PORTB | RS;
}
void rs_bas(void) {
PORTB = PORTB & ~RS;
}
void e_haut(void) {
PORTB = PORTB | E;
delai(8);
}
void e_bas(void) {
PORTB = PORTB & ~E;
delai(8);
}
void e_puls(void) {
e_haut();
e_bas();
}
void ecris_4(unsigned char valeur) {
unsigned char v;
v = (valeur << 4) & DATAS;
PORTD = PORTD & ~DATAS ;
PORTD = PORTD | v ;
e_puls();
}
void ecris_8(unsigned char valeur) {
unsigned char v;
v = valeur & DATAS;
PORTD = PORTD & ~DATAS ;
PORTD = PORTD | v ;
e_puls();
v = (valeur << 4) & DATAS;
PORTD = PORTD & ~DATAS ;
PORTD = PORTD | v ;
e_puls();
}
void setup() {
PORTB = 0;
delai(6000);
ecris_4(0x03);
delai(1600);
ecris_4(0x03);
delai(800);
ecris_4(0x03);
delai(800);
ecris_4(0x02);
delai(40);
ecris_4(0x02);
ecris_4(0x08);
delai(40);
ecris_4(0x00);
ecris_4(0x06);
delai(40);
ecris_4(0x00);
ecris_4(0x0C);
delai(40);
ecris_4(0x00);
ecris_4(0x01);
delai(800);
}
void writecar(char car) {
rs_haut();
ecris_8((unsigned char)car);
}
void writestr(char *chaine) {
rs_haut();
while (*chaine) {
ecris_8((unsigned char)*chaine++);
}
}
void command (uint8_t value) {
rs_bas();
ecris_8(value);
}
#define LCD_CLEARDISPLAY 0x01
void clearScreen() {
command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero
delai(1000); // this command takes a long time!
}
#define LCD_SETDDRAMADDR 0x80
void setCursor(uint8_t col, uint8_t row)
{
col = col & 0x0F; // %16
row = row & 0x01; // %2
command(LCD_SETDDRAMADDR | (col + 0x40*row));
}
int main(void) {
unsigned char tmp;
DDRD = 0xF0;
DDRB = 0x03;
setup();
clearScreen();
tmp = 0;
writestr("Hello Microsoft ");
setCursor(2,1);
writestr("Hello Linux");
while (1) {
//PORTA = tmp;
delai(100000);
tmp += 1;
}
return 0;
}
Réalisation d'un affichage décimal
On désire réaliser un affichage correct des chiffres.
Utilisation du convertisseur analogique/numérique
Mettre en œuvre et afficher sur LCD