Les écrans OLED (diodes électroluminescentes organiques) sont devenus l'un des périphériques d'affichage les plus populaires dans le développement embarqué grâce à leur auto-éclairage, fort contraste, faible consommation et large angle de vision. Cet article se concentre sur le modèle classique d'écran OLED de 0,96 pouces avec interface I2C/SPI (puise à la pucee SSD1306) pour expliquer le processus complet de développement de pilote, du principe matériel à l'extension des fonctionnalités. Les exemples de code sont adaptés pour les microcontrôleurs STM32 et peuvent être directement移植és dans l'environnement Keil MDK.
I. Compréhension des bases des écrans OLED
1.1 Spécifications principales (écran OLED de 0,96 pouces)
- Puce pilote : SSD1306 (solution standard de l'industrie) ;
- Résolution : 128×64 (128 colonnes, 64 lignes) ;
- Type d'interface : I2C (2 fils) ou SPI (4/6 fils), l'interface I2C étant plus simple pour les débutants ;
- Tension d'alimentation : 3,3V (certains modèles compatibles 5V, attention au niveau logique) ;
- Couleur d'affichage : monochrome (bleu/blanc) ou bicolore (bleu-jaune/blanc-bleu), nous prendrons l'exemple d'un écran bleu avec texte blanc.
1.2 Principe de fonctionnement du SSD1306
Le SSD1306 est une puce conçue spécifiquement pour les écrans OLED. Son rôle principal est de recevoir les commandes et données du microcontrôleur pour contrôler l'allumage/extinction des pixels :
- Mode commande : reception des instructions de contrôle (initialisation, positionnement du curseur, activation de l'affichage) ;
- Mode données : reception des données d'affichage des pixels, la résolution 128×64 correspondant à 1024 octets de mémoire vidéo (chaque octet contrôle 8 pixels) ;
- Mappage mémoire : les colonnes de l'écran correspondent à l'axe X (0127) de la mémoire, les lignes sont groupées par 8 pour correspondre à l'axe Y (07, chaque groupe représentant 8 lignes).
II. Préparation matérielle et câblage
2.1 Matériel requis
- Contrôleur principal : carte minimale STM32F103C8T6 (noyau Cortex-M3, adapté aux débutants) ;
- Écran OLED : modèle de 0,96 pouces avec interface I2C SSD1306 ;
- Accessoires : câbles dupont, alimentation 3,3V (ou alimentation directe par le microcontrôleur), module USB-série pour le débogage.
2.2 Câblage de l'interface I2C (configuration minimale)
L'interface I2C de l'écran OLED de 0,96 pouces n'utilise que 4 broches principales, comme indiqué ci-desous (exemple pour STM32) :
| Broche OLED | Fonction | Broche STM32 (exemple) | Remarques |
|---|---|---|---|
| VCC | Alimentation | 3,3V | Ne pas connecter en 5V pour éviter d'endommager l'écran |
| GND | Masse | GND | Une masse commune assure la stabilité des niveaux logiques |
| SCL | Horloge I2C | PB6 | Peut être assignée à d'autres GPIO |
| SDA | Données I2C | PB7 | Peut être assignée à d'autres GPIO |
Note : Les écrans OLED avec interface SPI nécessitent des connexions supplémentaires pour les broches DC (sélecteur données/commandes), RES (reset) et CS (sélection de puce), rendant le câblage plus complexe. Pour les débutants, privilégiez la version I2C.
III. Configuration de l'environnement de développement
3.1 Environnement de base
- Outil de développement : Keil MDK-ARM V5.x ;
- Bibliothèque de firmware : bibliothèque standard STM32F10x (ou bibliothèque HAL, nous utiliserons la bibliothèque standard) ;
- Essentiel : implémentation d'un pilote de bas niveau pour I2C (I2C logiciel, sans dépendre du matériel I2C pour une meilleure compatibilité).
3.2 Implémentation du pilote de bas niveau I2C logiciel
Les débutants n'ont pas besoin de comprendre en détail le protocole I2C, ils peuvent directement réutiliser le code I2C logiciel suivant, en modifiant simplement les définitions de broches :
#include "stm32f10x.h"
#include "delais.h" // Fonction de délai à implémenter (niveau us/ms)
// ************************* Définition des broches *************************
#define BROCHE_SCL GPIO_Pin_6
#define PORT_SCL GPIOB
#define BROCHE_SDA GPIO_Pin_7
#define PORT_SDA GPIOB
// Macros de contrôle des niveaux logiques
#define SCL_H() GPIO_SetBits(PORT_SCL, BROCHE_SCL)
#define SCL_L() GPIO_ResetBits(PORT_SCL, BROCHE_SCL)
#define SDA_H() GPIO_SetBits(PORT_SDA, BROCHE_SDA)
#define SDA_L() GPIO_ResetBits(PORT_SDA, BROCHE_SDA)
/**
* @brief Initialisation des broches I2C pour l'écran OLED
* @param Aucun
* @retval Aucun
*/
void Init_I2C_OLED(void)
{
GPIO_InitTypeDef structureGPIO;
// Activation de l'horloge GPIOB
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// Configuration des broches SCL/SDA en sortie push-pull
structureGPIO.GPIO_Pin = BROCHE_SCL | BROCHE_SDA;
structureGPIO.GPIO_Mode = GPIO_Mode_Out_PP;
structureGPIO.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(PORT_SCL, &structureGPIO);
// Niveaux logiques initiaux à haut
SCL_H();
SDA_H();
}
/**
* @brief Signal de départ I2C logiciel
* @param Aucun
* @retval Aucun
*/
void Signal_Debut_I2C(void)
{
SDA_H();
SCL_H();
delais_us(2);
SDA_L(); // SDA passe à bas niveau pendant que SCL est haut
delais_us(2);
SCL_L();
}
/**
* @brief Signal d'arrêt I2C logiciel
* @param Aucun
* @retval Aucun
*/
void Signal_Arret_I2C(void)
{
SDA_L();
SCL_H();
delais_us(2);
SDA_H(); // SDA repasse à haut pendant que SCL est haut
delais_us(2);
SCL_L();
}
/**
* @brief Envoi d'un octet via I2C logiciel
* @param donnee : octet à envoyer
* @retval Aucun
*/
void Envoyer_Octet_I2C(uint8_t donnee)
{
uint8_t i;
for(i=0; i<8; i++)
{
SCL_L();
delais_us(2);
// Envoi bit par bit (bit de poids fort en premier)
if(donnee & 0x80) SDA_H();
else SDA_L();
donnee <<= 1;
delais_us(2);
SCL_H();
delais_us(2);
}
// Attente de l'accusé de réception du esclave (simplifié pour les débutants)
SCL_L();
delais_us(2);
SDA_H();
delais_us(2);
SCL_H();
delais_us(2);
SCL_L();
}
/**
* @brief Envoi d'une commande à l'écran OLED
* @param commande : octet de commande
* @retval Aucun
*/
void Envoyer_Commande_OLED(uint8_t commande)
{
Signal_Debut_I2C();
Envoyer_Octet_I2C(0x78); // Adresse I2C du périphérique (0x3C << 1, adresse par défaut du SSD1306)
Envoyer_Octet_I2C(0x00); // Mode commande (0x00)
Envoyer_Octet_I2C(commande);
Signal_Arret_I2C();
}
/**
* @brief Envoi de données à l'écran OLED
* @param donnee : octet de données d'affichage
* @retval Aucun
*/
void Envoyer_Donnee_OLED(uint8_t donnee)
{
Signal_Debut_I2C();
Envoyer_Octet_I2C(0x78); // Adresse I2C
Envoyer_Octet_I2C(0x40); // Mode données (0x40)
Envoyer_Octet_I2C(donnee);
Signal_Arret_I2C();
}
Points clés :
- L'I2C logiciel n'a pas besoin de configurer le matériel I2C du microcontrôleur, il simule simplement le timing via GPIO, ce qui est plus simple pour les débutants ;
- La fonction
delais_us()doit être implémentée (basée sur SysTick ou un timer), elle est cruciale pour le timing I2C ; - L'adresse I2C par défaut du SSD1306 est 0x3C (certains écrans utilisent 0x78, à ajuster selon le matériel).
IV. Initialisation de l'écran OLED et fonctions d'affichage de base
4.1 Initialisation de l'écran OLED
Le SSD1306 nécessite l'envoi d'une série de commandes d'initialisation pour configurer ses paramètres d'affichage. Voici la fonction d'initialisation générique :
/**
* @brief Initialisation de l'écran OLED
* @param Aucun
* @retval Aucun
*/
void Initialiser_OLED(void)
{
delais_ms(100); // Délai après mise sous tension pour stabilisation de la puce
Init_I2C_OLED(); // Initialisation des broches I2C
// Commandes d'initialisation du SSD1306
Envoyer_Commande_OLED(0xAE); // Désactiver l'affichage
Envoyer_Commande_OLED(0x00); // Définir l'adresse de départ de colonne (4 bits bas)
Envoyer_Commande_OLED(0x10); // Définir l'adresse de départ de colonne (4 bits haut)
Envoyer_Commande_OLED(0x40); // Définir la ligne de départ d'affichage
Envoyer_Commande_OLED(0xB0); // Définir l'adresse de page (axe Y)
Envoyer_Commande_OLED(0x81); // Régler le contraste
Envoyer_Commande_OLED(0xFF); // Valeur de contraste (0~255, plus grand = plus lumineux)
Envoyer_Commande_OLED(0xA1); // Remappage des segments (0xA0 = inversion gauche/droite, 0xA1 = normal)
Envoyer_Commande_OLED(0xA6); // Affichage normal (0xA6 = normal, 0xA7 = inversé)
Envoyer_Commande_OLED(0xA8); // Régler le taux de multiplexage
Envoyer_Commande_OLED(0x3F); // Affichage en 64 lignes
Envoyer_Commande_OLED(0xC8); // Direction de balayage (0xC0 = inversion haut/bas, 0xC8 = normal)
Envoyer_Commande_OLED(0xD3); // Définir le décalage d'affichage
Envoyer_Commande_OLED(0x00); // Décalage de 0
Envoyer_Commande_OLED(0xD5); // Régler la division d'horloge
Envoyer_Commande_OLED(0x80); // Facteur de division
Envoyer_Commande_OLED(0xD9); // Régler la période de précharge
Envoyer_Commande_OLED(0xF1);
Envoyer_Commande_OLED(0xDA); // Configuration des broches COM
Envoyer_Commande_OLED(0x12);
Envoyer_Commande_OLED(0xDB); // Régler la tension VCOMH
Envoyer_Commande_OLED(0x40);
Envoyer_Commande_OLED(0x8D); // Activer la pompe de charge
Envoyer_Commande_OLED(0x14);
Envoyer_Commande_OLED(0xAF); // Activer l'affichage
}
/**
* @brief Définir la position d'affichage sur l'écran OLED (X: colonnes 0~127, Y: pages 0~7)
* @param x : coordonnée de colonne
* @param y : coordonnée de page (chaque page = 8 lignes)
* @retval Aucun
*/
void Definir_Position(uint8_t x, uint8_t y)
{
Envoyer_Commande_OLED(0xB0 + y); // Définir l'adresse de page
Envoyer_Commande_OLED(((x & 0xF0) >> 4) | 0x10); // Définir l'adresse de colonne (4 bits haut)
Envoyer_Commande_OLED(x & 0x0F); // Définir l'adresse de colonne (4 bits bas)
}
/**
* @brief Fonction d'effacement de l'écran
* @param Aucun
* @retval Aucun
*/
void Effacer_Ecran(void)
{
uint8_t x, y;
for(y=0; y<8; y++)
{
Definir_Position(0, y);
for(x=0; x<128; x++)
{
Envoyer_Donnee_OLED(0x00); // Écriture de 0 pour éteindre tous les pixels
}
}
}
4.2 Fonctions d'affichage de caractères/chiffres
Pour afficher des caractères, il faut d'abord préparer les données de matrice de points (nous utiliserons une matrice ASCII 8×16 dans cet exemple). Voici les fonctions d'affichage principales :
// Tableau de matrice ASCII 8×16 (partiel, tableau complet téléchargeable en ligne)
const unsigned char MATRICE_ASCII[] = {
0x00,0x00,0x7C,0x12,0x11,0x12,0x7C,0x00,0x00,0x00,0x7C,0x12,0x11,0x12,0x7C,0x00, // 0
0x00,0x00,0x00,0x70,0x08,0x08,0x08,0x70,0x00,0x00,0x00,0x70,0x08,0x08,0x08,0x70, // 1
// Plus de caractères omis, tableau ASCII complet nécessaire
};
/**
* @brief Afficher un caractère unique (matrice 8×16)
* @param x : colonne de départ (0~127)
* @param y : page de départ (0~7, chaque caractère occupe 2 pages)
* @param c : caractère à afficher (code ASCII)
* @retval Aucun
*/
void Afficher_Caractere(uint8_t x, uint8_t y, uint8_t c)
{
uint8_t i;
c -= '0'; // Basé sur le chiffre 0 (pour les lettres, ajuster le décalage)
Definir_Position(x, y);
// Afficher la moitié supérieure (page 1)
for(i=0; i<8; i++)
{
Envoyer_Donnee_OLED(MATRICE_ASCII[c*16 + i]);
}
Definir_Position(x, y+1);
// Afficher la moitié inférieure (page 2)
for(i=8; i<16; i++)
{
Envoyer_Donnee_OLED(MATRICE_ASCII[c*16 + i]);
}
}
/**
* @brief Afficher une chaîne de chiffres
* @param x : colonne de départ
* @param y : page de départ
* @param nombre : nombre à afficher (0~99999999)
* @param longueur : nombre de chiffres à afficher
* @retval Aucun
*/
void Afficher_Nombre(uint8_t x, uint8_t y, uint32_t nombre, uint8_t longueur)
{
uint8_t i;
for(i=0; i<longueur; i++)
{
Afficher_Caractere(x + 8*i, y, (nombre / (uint32_t)puissance(10, longueur-i-1)) % 10 + '0');
}
}
/**
* @brief Afficher une chaîne de caractères
* @param x : colonne de départ
* @param y : page de départ
* @param chaine : pointeur vers la chaîne de caractères
* @retval Aucun
*/
void Afficher_Chaine(uint8_t x, uint8_t y, uint8_t *chaine)
{
while(*chaine != '\0')
{
Afficher_Caractere(x, y, *chaine);
x += 8;
if(x > 120) // Dépassement de la largeur de l'écran, passage à la ligne suivante
{
x = 0;
y += 2;
}
chaine++;
}
}
V. Exemple pratique : affichage sur l'écran OLED
5.1 Code de test complet
Intégrons les fonctions précédentes pour implémenter des fonctionnalités d'affichage de base :
#include "stm32f10x.h"
#include "oled.h" // Encapsuler les fonctions de pilote OLED dans oled.h/oled.c
#include "delais.h"
int main(void)
{
uint32_t compteur = 0;
// Initialisation du système
SystemInit(); // Configuration de la fréquence principale STM32 (72MHz)
delais_init(); // Initialisation de la fonction de délai
Initialiser_OLED(); // Initialisation de l'écran OLED
Effacer_Ecran(); // Effacer l'écran
while(1)
{
// Affichage d'une chaîne de caractères fixe
Afficher_Chaine(0, 0, "Demonstration OLED");
Afficher_Chaine(0, 2, "Compteur:");
// Affichage d'un nombre incrémenté
Afficher_Nombre(60, 2, compteur, 5);
compteur++;
if(compteur > 99999) compteur = 0;
delais_ms(100); // Rafraîchissement toutes les 100ms
}
}
5.2 Vérification du résultat
- Compiler le code et le télécharger dans le microcontrôleur STM32 ;
- Après mise sous tension, l'écran OLED s'efface d'abord, puis affiche :
- Première ligne : Demonstration OLED ;
- Troisième ligne : Compteur: 00000 (le nombre incrmente toutes les secondes) ;
- Si l'écran n'affiche rien, vérifier le câblage (notamment que VCC est bien à 3,3V), l'adresse I2C et les commandes d'initialisation.
VI. Problèmes courants et solutions
6.1 Pas d'affichage
- Cause 1 : tension d'alimentation incorrecte (5V endommage l'écran ou tension insuffisante) ; Solution : connecter impérativement en 3,3V, vérifier la tension au niveau de la broche VCC de l'écran OLED ;
- Cause 2 : adresse I2C incorrecte ; Solution : changer
Envoyer_Octet_I2C(0x78)en0x38(0x1C<<1) pour tester ; - Cause 3 : imprécision de la fonction de délai ; Solution : optimiser la fonction
delais_us()pour s'assurer que le timing I2C correspond aux exigences du SSD1306.
6.2 Affichage corrompu/décalé
- Cause 1 : incompatibilité entre la matrice de points et la fonction d'affichage ; Solution : s'assurer que la matrice est bien en 8×16 et que le bit de poids fort est envoyé en premier ;
- Cause 2 : définition incorrecte des coordonnées ; Solution : vérifier la fonction
Definir_Position(), les adresses de page doivent être comprises entre 0 et 7, les adresses de colonne entre 0 et 127.
6.3 Scintillement de l'écran
- Cause : fréquence d'effacement/rafraîchissement trop élevée ; Solution : réduire les opérations d'effacement inutiles, ne rafraîchir que les zones modifiées.
VII. Suggestions d'extension des fonctionnalités
- Affichage de caractères chinois : ajouter une matrice de points de 16×16 pour les caractères chinois, modifier la fonction d'affichage pour adapter la largeur de 16 colonnes ;
- Affichage graphique : implémenter l'affichage de points, lignes, cercles et images bitmap en écrivant dans la mémoire vidéo ;
- Défilement d'affichage : utiliser les commandes de défilement du SSD1306 (0x26~0x29) pour un défilement de texte ;
- Mode basse consommation : désactiver l'affichage via
Envoyer_Commande_OLED(0xAE)pour réduire la consommation.