Julien
26/03/2023
– [Théories] Mise à jour I2C Isis Esclave PIC18F4550
Dernière mise à jour le 25/10/25
Présentation
Le but de ce montage electronique est de comprendre le fonctionnement I2C du PIC18F4550 et de savoir comment régler les différents registres en I2C.Pour cela nous allons utiulisr la simulation Proteus isis et le debugger rien de plus…
Logiciel du PIC
Programme MikroPascal du PIC 12F675 [ Logiciel non disponible sur ce site. – Interface MIDI 020 – PIC 18F26K22 – 31/07/2016 – ou mettre un fichier compresser avec le programme du PIC en question]
Programme du PIC – I²C Esclave
unsigned int iInc = 0;
unsigned char clearBuffer;
void interrupt()
{
if (PIR1.SSPIF)
{
if (R_NOT_W_bit == 1) // Le maître veut lire
{
clearBuffer = SSPBUF;
if ((iInc == 0) && (BF_bit == 0))
{
SSPBUF = 0x55; // 1er octet
LATA = 0x55;
iInc++;
}
else if ((iInc == 1) && (BF_bit == 0))
{
Delay_ms(2000); // On attend 2 sec pour voir le changement
SSPBUF = 0xF0; // 2e octet
LATD = 0xFF;
iInc = 0; // Réinitialisation pour la prochaine lecture
}
}
CKP_bit = 1; // On relâche l'horloge afin de poursuivre
PIR1.SSPIF = 0; // Reset flag
}
}
// Initialisation du PIC18F4550
void init()
{
TRISA = 0x00; // On configure le PORTA comme sortie
LATA = 0x00;
TRISB = 0x03; // On confirgure les broches du PORT B - RB0 et RB1 comme entrée
LATB = 0x00;
TRISC = 0x00; // On configure le PORTD comme sortie
LATC = 0x00;
TRISD = 0x00; // On configure le PORTD comme sortie
LATD = 0x00;
TRISE = 0x00; // On configure le PORTE comme sortie
LATE = 0x00;
INTCON.GIE = 1; // On active les interruptions globales
INTCON.PEIE = 1; // On active les interruptions périphériques
PIE1.SSPIE = 1; // On active les interuptions du MSSP ( Module de port sortie synchrone principal I2C ou SPI)
PIR1.SSPIF = 0; // On met le drapeau à zéro pour commencer
SSPCON1.SSPEN = 1; // On active les broches RB0 et RB1 en mode SDA et SCL
SSPCON1.SSPM0 = 0;
SSPCON1.SSPM1 = 1; // On active le mode 7 bits (0 ? 7 = 8 bits) avec interruptions sur bit Start et Bit Stop
SSPCON1.SSPM2 = 1;
SSPCON1.SSPM3 = 0;
SSPADD = 0x10; // Configuration de l'adresse de l'esclave du PIC 18F4550
SSPSTAT.SMP = 1; // Speed mmode 100 Khz (standard)
SSPCON2.SEN = 1; // étirement activé (permet de génèrer automatiquement une condition Start)
}
// Programme principal
void main()
{
init();
while (1) // Boucle principale
{
// On ne fait rien
}
}
|
I2C debugger
Côté Proteus il suffit de mettre cette ligne de code ci-dessous
Côté maître, le “S” indique la condition de start, suivi de “0x11”, qui correspond à l’adresse de l’esclave en mode lecture. En effet, 0x11 en binaire est 0b00010001, et le bit de poids faible à 1 indique la lecture.Le “A” représente le bit d’acquittement (ACK), qui signifie que le maître continue la lecture. Il est ensuite suivi de “N”, le NACK, indiquant que nous avons terminé et que nous ne réclamons plus d’octet à l’esclave. Enfin, le “P” correspond à la condition de stop.
{SEQUENCE002=S 0x11 A N P}
|
Historiques
– 25/10/25
Première mise à disposition.
26/03/2023
– [Théories] Mise à jour Modélisation du Mosfet
Dernière mise à jour le 26/03/2023
Présentation
Dans cette article nous allons voir comment modéliser un Mosfet en utilisant Proteus (ISIS), Pour cela je vais utiliser le MOSFET BS170 Canal-N afin de faire ressortir ses courbes de Vgs, Id, Vds. et je ferais de même pour le MOSFET IRF9230 Canal-P (vous remarquerez que les courbes sont à l’envers !) et nous comparerons ensuite ces valeurs avec le datasheet (ou doc technique) Le but est de savoir modéliser un composant avec la fonction de transfert pour pouvoir ressortir les courbes en zone ohmique et en saturation. La simulation sera parfaitement juste plutôt que d’utiliser les doc constructeur ou là des divergence peuvent être présente dans la simulation!
Modélisation du Mosfet BS170 – CANAL-N

Sur ce schéma je fais apparaître, deux sources de tensions nommées “Vds” et “Vgs” sonde de courant “Id”, et pour finir une sonde Vgs. Il s’agit de brancher (par simulation) en direct les 2 générateurs de tensions sur le composant et de relever le courant “Id” et la tens “Vgs”. Pour cela je vais maintenant utiliser le graph nommé “TRANSFERT” situé dans Isis. Au niveaux des tensions appliquées sur Vds et Vgs j’ai mis 1V pour les 2.
Selection de graph transfert

Modélisation
Maintenant il ne reste plus qu’à suivre les étapes ci-dessous en images (clique pour agrandir).
Remarques
Vous remarquerez que la courbe (si vous faites la simulation) la plus en bas commence à partir de 2,5V, il en résulte que le transistor BS170 devient passant à partir de cette tension. Si on regarde la doc technique du constructeur il indique “Gate Threshold voltage” tension de seuil comprise entre 0,8V (Min) et 3V(Max), par simulation la tension de seuil est de 2,5V pour être exact à cette tension Vgs le Mosfet commence à être passant !
Modélisation du Mosfet IRF9230 – CANAL-P

Sur ce schéma je fais apparaître, deux sources de tensions nommées “Vds” et “Vgs” sonde de courant “Id”, et pour finir une sonde Vgs. Il s’agit de brancher (par simulation) en direct les 2 générateurs de tensions sur le composant et de relever le courant “Id” et la tens “Vgs”. Pour cela je vais maintenant utiliser le graph nommé “TRANSFERT” situé dans Isis. Au niveaux des tensions appliquées sur Vds et Vgs j’ai mis 1V pour les 2.
Selection de graph transfert

Modélisation
Maintenant il ne reste plus qu’à suivre les étapes ci-dessous en images (clique pour agrandir).


Remarques
Vous remarquerez que la courbe (si vous faites la simulation) est cette fois-ci inversée !! ce qui est normal puisque il s’agit du MOSFET Canal-P par modélisation et que le courant “Id” ainsi que “Vgs”sont tous inférieur à 0. Si on prend la doc technique du constructeur, la tension de seuil Min est de -2V, en ce qui concerne la modélisation , pour le coup la courbe la plus en haut est de -0,2mV (je dis la plus en haut car en valeur absolue serais la plus petite valeur (Min)).
En ce qui concerne les calculs, vous pouvez très bien interpréter tous ces résultats en valeurs absolues. Le but était de modéliser (faire notre propre documentation technique) via ISIS afin d’avoir des résultats de simulation.
Le condensateur
Le condensateur est un composant électronique élémentaire, constitué de deux armatures conductrices (appelées « électrodes ») en influence totale et séparées par un isolant polarisable (ou « diélectrique »). Sa propriété principale est de pouvoir stocker des charges électriques opposées sur ses armatures. La valeur absolue de ces charges est proportionnelle à la valeur absolue de la tension qui lui est appliquée.
Le condensateur est utilisé principalement pour :
Stabiliser une alimentation électrique (il se décharge lors des chutes de tension et se charge lors des pics de tension) ;
traiter des signaux périodiques (filtrage…) ;
séparer le courant alternatif du courant continu, ce dernier étant bloqué par le condensateur ;
stocker de l’énergie, auquel cas on parle de supercondensateur.
Formule avec i = C(du/dt).
Le condensateur est caractérisé par le coefficient de proportionnalité entre charge et tension appelé capacité électrique et exprimée en farads (F). La relation caractéristique d’un condensateur idéal est :
i = C(du/dt).
où :
i : désigne l’intensité du courant électrique qui passe par le composant, exprimée en ampères (symbole : A) ;
u : la tension aux bornes du composant, exprimée en volts (V) ;
C : la capacité électrique du condensateur, exprimée en farads (F) ;
du/dt : la dérivée de la tension par rapport au temps (V/s).
Les signes sont tels que l’électrode par laquelle entre le courant (dans le sens conventionnel du courant : + vers -) voit son potentiel augmenter.
Formule avec Q = CU.
Il est possible aussi de calculer la quantité de charge accumulée par le condensateur, on définit la capacité par la relation:
Q = CU
où :
Q : est la charge stockée sur sa borne positive (qui s’exprime en coulombs),
C : la capacité électrique du condensateur (farads),
U : la tension aux bornes du composant (volts).
Théorème de Millman
Le théorème de Millman est une forme particulière de la loi des nœuds exprimée en termes de potentiel, il est tout à fait possible d’exprimer le potentiel Va par le théorème de Millam qui est a l’honneur de l’électronicien américain Jacob Millman.
Dans un réseau électrique de branches en parallèle, comprenant chacune un générateur de tension parfait en série avec un élément linéaire, la tension aux bornes des branches est égale à la somme des forces électromotrices respectivement multipliées par l’admittance de la branche, le tout divisé par la somme des admittances. oui est c’est à dire ??!

Sur le schéma ci-dessus, la tension représentative a été calculée en suivant la formule du théorème de Millman:

Le signe négatif signifie que la tension au point Va est négative par rapport à la masse commune.
Remarque: Il n’est pas nécessaire que les sources de tension soient parfaites, celles-ci peuvent inclure des résistances même de forte valeur.
Calculs des surtensions aux bornes d’un condensateur
Ce que je vous propose, c’est de voir ensemble l’illustration ci-dessous , et de faire des calculs en utilisant le théorème de Millman pour arriver à déterminer des potentiels “volants” sur des points particulier d’un circuit électronique. Je me passerais des formules abracadabrantes surtout au niveau des régimes transitoires ou nous allons pas commencer à faire des calculs sur les équations différentiels du 1er ordre ou bien en passant par les transformées de Laplace. Oui! on peut le faire mais je vous épargne tous ces calculs. Le but est de comprendre comment se comporte un condensateur lorsque celui-ci est soumis à différents potentiels, ou lorsque ces potentiels vont changer subitement. Nous verrons pas à pas et en détails ces calculs afin de voir que le condensateur n’admet aucun changement d’état brusque de tension au niveau de ses bornes et c’est pour cette raison que le condensateur s’oppose à toutes variations brusques de tension. (Les variations de tensions sont limitées via le du/dt).

(Clique pour agrandir)
Etude complète

Avant la mise sous tension (interrupteur ouvert) – T(0) Va=Vb=5V
Avant la mise sous tension, l’interrupteur est ouvert, et le condensateur quant a lui est complétement déchargé la tension entre ses bornes Uc = Va – Vb = 0V. Cette tension étant nulle, on peut donc dire que Va = Vb, et pour aller plus loin, je peut représenter le condensateur comme un fil électrique (voir ci-dessous).
J’espère que vous me suivez toujours ? La tension étant nulle à ses bornes c’est comme si je vous demandais de mesurez la tension aux bornes d’un interrupteur lorsque celui-ci est fermé , le voltmètre affichera 0V (un interrupteur fermé correspond à un fil et donc pas de différence de potentiel lorsque nous mesurons 1 fil électrique).
Ceci étant dit au passage, il n’y a pas de courant qui circule non plus!! Oui, le courant va partir du +5V de l’alimentation pour retourner d’où il vient c’est à dire au pôle négatif de l’alimentation qui est représenté par une masse… Or l’interrupteur est ouvert !!
Si le courant est nul, la différence de potentiel entre la source qui est le +5V et le potentiel Va = Vb est égale à la tension elle même car:
UR1 = R1 * i = 1K * 0 = 0V
Va = 5 – UR1 = 5 – 0 = 5V
Comme Va = Vb alors Vb = 5V
Ainsi nous avons déterminé à T(0) que Va = Vb = 5V les tensions sont “superposées” l’une sur l’autre comme le montre le graphique que j’ai réalisé. tant que je ne ferme pas l’interrupteur la tension restera à 5V jusqu’au moment ou je vais appuyer dessus à l’instant T(1)
Après la mise sous tension (interrupteur fermé) – T(1) Va=Vb=0V

Cette fois-ci je vais appuyer sur l’interrupteur, mais à t(1) le potentiel Va est imposé par la masse soit Va=0V le condensateur n’a pas eu le temps pendant dans cette transition de fermeture d’interrupteur de se charger il en résulte que le condensateur correspond toujours à un fil et la tension Vb = 0V car Va = Vb. Laissons maintenant un peu de temps au condensateur de se charger dans la phase que l’on nomme un régime transitoire.
Après la mise sous tension (Condensateur complétement chargé) – T(2) Va=2,5V et Vb = 7,5V

Je vous vois venir pourquoi Va = 2,5V et Vb = 7,5V ??!
Lorsque le condensateur va être complétement chargé (comme dis précédemment nous étudierons pas le régime transitoire suite aux calculs un peu plus lourd), nous somme façe à une situation ou Va et Vb se retrouve à des potentiels “volant” et dans ce cas nous ne ne connaissons plus la tension Va = ? V, et la tension Vb = ? V.
C’est à ce moment précis que nous allons faire appel au théorème de Millman qui est un outil extrêmement puissant (d’ailleurs mon préféré que Norton ou Thévenin) pour résoudre ce genre de situation afin de déterminer des potentiels précisemment.
Les seules tensions connues dans le circuit sont la tension d’alimentation 5V, ainsi que la tension aux bornes du condensateur Uc = 5V (oui le condensateur à été chargé à 100%) or quand est-il pour Va et Vb ? c’est ce que nous allons voir de suite:

Nous devons respecter le Théorème de Millman ou nous avons toujours une tension sur une résistance (V/R) ce qui nous donne (j’ai réalisé une capture de mrd calculs c’est beaucoup mieux je trouve comme ça…

Nous avons trouver Va super!! il ne reste plus qu’a en déduire Vb en faisant simple:
Uc = Vb – Va ce qui nous donne Vb = Uc + Va = 5 + 2,5 = 7,5V et voilà notre surtension!!
Modification du schéma

J’ai changé la valeur des résistances avec R1 = 20Kohms et R2=2,2Kohms. Niveau calculs j’obtiens:
Va = E * R2/(R1+R2) = 5 * (2200/22200) = 0.4954V
Vb = Uc + Va = 5 + 0,4954 = 5,4954V
Pour Info : J’ai repris la formule générale détaillée dans ma note de calcul ci-dessus formule en rouge.
Pour le reste je vous laisse réflechir un peu 😉 vous devez trouver Va = E * (R2/(R1+R2)).
Graph

(Clique pour agrandir)
Dernière mise à jour le 15/01/2021
Présentation
Vous connaissez tous les codes à barres ! Cependant les QR-codes et codes Datamatrix restent inconnus du grand public. C’est bien dommage car ces codes offrent un monde de possibilités !.
Les codes à barres sont des codes unidimensionnels (1D) où l’information n’est lue et structurée que dans un sens. Les codes bidimensionnels (2D), tels que les QR-codes et les codes Datamatrix contiennent des données horizontales aussi bien que verticales. Ce sont de petits blocs, des pixels, qui sont structurés de façon à former un carré : un carré blanc correspondant au 0 du code binaire, un carré noir correspondant au 1.
Comparés aux codes 1D, les codes 2D présentent beaucoup d’avantages. Ils peuvent contenir dix fois plus d’informations en prenant dix fois moins de place ! De plus, ils contiennent un code correcteur intégré qui leur permet d’être lus même s’ils sont partiellement dégradés. Troisième avantage non négligeable : ces codes peuvent être lus dans n’importe quelle direction (360°). Ils sont donc très pratiques pour des scann automatisés, sans pour autant perdre leur utilité en lecture manuelle.
Il existe plein de générateurs de QR-Code ou de DataMatrix qui circule sur le web, mais, étant curieux, j’avais envie d’en réaliser mon propre générateur en langage Python.
Pour ce faire, il faudra vous munir de la librairie nommé ‘qrcode’ accessible qrcode sont nom indique déja ce que nous allons faire.
Ensuite il faudra vous munir de ‘pylibdmtx’ afin de créer les DataMatrix qui sont disponible directement via ce lien pylibdmtx.
Ce n’est pas terminé, il faudra aussi installer OpenCV afin d’utiliser la bibliothèque CV2, la aussi disponible via ce lien OpenCV.
Quelle est la différence entre un QR-code et un code Datamatrix ?
Les deux codes 2D les plus utilisés sont le QR-code et le code Datamatrix. la différence :
En les regardant côte-à-côte, vous observez immédiatement une différence de structure. Ces deux codes utilisent des points de reconnaissance différents. Un QR-code contient 4 carrés blanc/noir dans chaque coin dont 1 un peu plus en retrait. Le code Datamatrix contient toujours une rangée de pixels noirs en bas et sur le côté gauche, et une rangée de pixels blancs et noirs alternés en haut et sur le côté droit.
Tout ce qui ne correspond pas à ces points de reconnaissance, c’est le champs de données. Ce champs peut contenir jusqu’à 4296 caractères alphanumériques. Pour un code Datamatrix le nombre maximum est de 2335 caractères.
Le QR-code est surtout utilisé dans des contextes liés aux consommateurs. Il peut être scanné par un smartphone. C’est ainsi que facilement, vous pouvez passer une nouvelle commande.
Bien qu’un QR-code contienne plus d’informations, un code Datamatrix prendra moins de place pour stocker la même quantité d’informations (les points de reconnaissance sont plus petits). De plus ils sont plus sécuritaires puisqu’ils sont plus difficiles à pirater.
C’est la raison pour laquelle le code Datamatrix est spécialement adapté pour ces procédures où la sécurité et le gain d’espace jouent un rôle important, telles que le traçage ou l’identification de petites pièces.
Programmation Python – QR-Code
Nous allons regarder ce programme ci-dessous – pour ma part j’utilise Visual Studio Code comme IDE de Microsoft, mais vous pouvez utiliser un autre IDE avec celui ou vous vous sentez le mieux.
# QR-Code – Python – Electronique71.com
import qrcode # On ajoute qrcode qr = qrcode.QRCode( version=1, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=1, border=1, ) qr.add_data('Ceci est un QR-Code - Electronique71.com') qr.make(fit=True) img = qr.make_image(fill_color="black", back_color="white") img.save("QR_Code.png") |
Fonctionnement programmation Python
Ce code a pour but de créer un QR-Code avec comme donnée: ‘Ceci est un QR-Code – Electronique71.com’.
La fonction ‘qrcode.QRCode’ initialise le QR-Code en tant qu’objet, puis via cette object crée, nous appliquons un ajout de donnée par la fonction ‘add_data’. c’est dans cette fonction que nous allons saisir notre texte. Puis pour finir on fabirque ce QR-Code via la fonction ‘make’.
Il ne reste plus qu’à créer une image de la sauvegarder dans le dossier courant (c’est à dire le dossier dans lequel vous avez placé votre script (*.py)
Programmation OpenMV
Intéressons nous maintenant côté caméra sur la partie code toujours en Python.
# QR-Code – Python – Electronique71.com
import sensor, image, time sensor.reset() sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.VGA) sensor.set_windowing((240, 240)) sensor.skip_frames(time = 2000) sensor.set_auto_gain(False) while(True): img = sensor.snapshot() codes = img.find_qrcodes() for code in codes: img.draw_rectangle(code.rect(), thickness=5, color = 255) print(str(code[4])) if not codes: print("Aucun aperçu !") |
Programmation Python – DataMatrix
Regardons cette fois-ci le code pour un DataMatrix
# DataMAtrix – Python – Electronique71.com
import cv2 from PIL import Image from pylibdmtx.pylibdmtx import encode # On importe pylibdmtx size = 200,200 # Taille du datamatrix text = 'Ceci est un Datamatrix - Electronique71.com' encoded = encode(text.encode('utf8')) # Bien encoder en utf8 img = Image.frombytes('RGB', (encoded.width, encoded.height), encoded.pixels) img.thumbnail(size) img.save('DataMatrix.png') |
Fonctionnement programmation Python
Ce code est différent du 1er mais assez simple à comprendre vous ne trouvez pas ?
Programmation OpenMV
Intéressons nous maintenant côté caméra sur la partie code toujours en Python.
# DataMatrix – Python – Electronique71.com
import sensor, image, time, math sensor.reset() sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QVGA) sensor.skip_frames(time = 2000) sensor.set_auto_gain(False) while(True): img = sensor.snapshot() matrices = img.find_datamatrices() for matrix in matrices: img.draw_rectangle(matrix.rect(), thickness=5, color = (255, 0,0)) print(str(matrix[4])) if not matrices: print("Aucun aperçu !") |
Test avec caméra OpenMV
J’ai effectués quelques tests avec la caméra afin d’afficher le text sur le terminal, j’ai uniquement filmé avec ma cam le QR Code et le DataMAtrix situé au dessus (gros carré) test concluant ! En revanche nous devons être proche, mais bon avec quelques réglages niveau objectif, et le tour est joué .(je me vois mal récupérer un QR-Code à plusieurs mètres !)
Avec QR Code
Trouver l’erreur!! …. Niveau terminal .. Mais j’ai modifié le code c’est mieux pour moi !
Avec DataMatrix
Historiques
15/01/2021
– Première mise à disposition
Dernière mise à jour le 15/01/2021
Présentation
Lire et écrire des données sur un µc en utilisant la cam OpenMV ? c’est tout à fait possible
Dans un premier temps, il faut “caler”, ou dirais-je rendre la communication I2C compatible entre la cam et le PIC pour arriver en sorte à ce que la cam puisse écrire des données sur le PIC qui une fois reçues, ce dernier les déposeras tour à tour dans un tableau (array) (tableau de 8×1) qu’on nommera Buffer[8][1].
Une bonne configuration entre la cam OpenMV et le PIC18F4550 est avant tout l’adressage. Oui sans adresse côté PIC il sera impossible de communiquer avec ce dernier. Pour plus de détails sur comment le configurer en mode esclave, vous pouvez vous rendre dans cette rubrique PIC18F4550 (esclave) – OpenMV (maître) – mode I2C.
Pour faire “simple”, si vous configurez votre cam OpenMV avec pour adresse cAdrr = 0x02, il faudra configurer le PIC avec cAdrr = 0x04 (ajout de 1bit à droite donc décalage vers la gauche pour le PIC18F4550 !! – 0x02 << 1 donne 0x04).
ou
si vous configurez votre cam OpenMV avec cette fois-ci pour adresse cAdrr = 0x02 >> 1 (suppression de 1 bit à droite cette fois-ci réalisé au niveau de la cam, décalage à droite !, ce qui donne 0x01), alors côté PIC 18F4550 laisser cAdrr = 0x02).
C’est cette histoire de décalage qui va permettre de rendre compatible la cam OpenMV avec le PIC (plusieurs tests sont situé un peu plus bas pour voir ensemble le principe c’est beaucoup plus parlant)..
Mode Lecture/Ecriture – Cam OpenMV vers PIC18F4550
Nous allons aborder le code côté cam OpenMV en Python code réalisé pour l’écriture et la lecture.
MAÎTRE – Programmation Python – Cam OpenMV STM32F765VI ARM
# Maître – OpenMV I2C – Lecture/Ecriture – Python – Electronique71.com
from pyb import I2C import utime i2c = I2C(2) # creation du bus I2C i2c.init(I2C.MASTER, baudrate=100000) # initialisation du maître sur 100Khz # 0x02 >> 1 : +1 bit à droite (décalage vers la gauche de l'adresse donne 0x04 pour PIC) cAdrr = 0x02 >> 1 # si cAdrr = 0x02 côté cam OpenMV alors mettre cAdrr = 0x04 côté PIC18F4550 pour compatibilité ! utime.sleep_ms(500)# On attend un peu la fin de l'initialisation du bus I2C. # Vérifie si l'esclave répond à cette adresse: # -> renvoi True si l'esclave répond # -> renvoi False si l'escalve ne répond pas validation = i2c.is_ready(cAdrr) if (validation == True): Buffer = bytearray(8) # Création du Buffer (1 octet = 8bits) print("Nettoyage Buffer !! -> " + str(i2c.recv(Buffer,cAdrr))) while (True): # Ecriture i2c.send(0x00, addr = cAdrr) # bit 0 utime.sleep_ms(500) # -> Temp Min : utime.sleep_us(50) i2c.send(0x01, addr = cAdrr) # bit 1 utime.sleep_ms(500) # -> Temp Min : utime.sleep_us(50) i2c.send(0x02, addr = cAdrr) # bit 2 utime.sleep_ms(500) # -> Temp Min : utime.sleep_us(50) i2c.send(0x03, addr = cAdrr) # bit 3 utime.sleep_ms(500) # -> Temp Min : utime.sleep_us(50) i2c.send(0x04, addr = cAdrr) # bit 4 utime.sleep_ms(500) # -> Temp Min : utime.sleep_us(50) i2c.send(0x05, addr = cAdrr) # bit 5 utime.sleep_ms(500) # -> Temp Min : utime.sleep_us(50) i2c.send(0x06, addr = cAdrr) # bit 6 utime.sleep_ms(500) # -> Temp Min : utime.sleep_us(50) i2c.send(0x07, addr = cAdrr) # bit 7 # Lecture utime.sleep_ms(500) # -> Temp Min : utime.sleep_us(50) print(i2c.recv(Buffer,cAdrr)) break # Supprimer "break" pour boucle en continue ! elif (validation == False): print("PIC 18F4550 non reconnu") |
Fonctionnement
Niveau programmation Python, je vous laisse vous rendre sur ces deux liens pour explications détaillés :
PIC18F4550 (esclave) – OpenMV (maître) – mode lecture – I2C
PIC18F4550 (esclave) – OpenMV (maître) – mode ecriture – I2C
Terminal OpenMV
Ci-dessous, une capture des données situées dans le buffer du PIC18F4550

ESCLAVE – Programmation MikroC – PIC18F4550

// Esclave – PIC18F4550 – Lecture/Ecriture – MikroC – Electronique71.com
const cAdrr = 0x02; // cAdrr = 0x02 correspond à l'adresse de l'esclave // Si cAdrr = 0x04 côté PIC alors mettre cAddr = 0x02 côté cam OpemMV pour compatibilité ! unsigned char iCount_Ecriture; unsigned char iCount_Lecture; unsigned char Data; unsigned char VideBuffer; // Tableau 2 dimensions pour envoyer donner vars cam OpenMV unsigned char Buffer[8][1] = { {0x00}, // bit 0 {0x00}, // bit 1 {0x00}, // bit 2 {0x00}, // bit 3 {0x00}, // bit 4 {0x00}, // bit 5 {0x00}, // bit 6 {0x00}, // bit 7 }; // Tableau 1 dimension pour envoyer donner vars cam OpenMV //unsigned char Buffer[8] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07}; void interrupt() // On contrôle chaque interruptions { if (PIR1.SSPIF == 1) { // SSPSTAT.R_W -> bit0 = 1: Lecture ou bit0 = 0: Ecriture if (SSPSTAT.R_W == 1) { if (BF_bit == 1) { // Lecture -> Esclave vers Maître SSPBUF = Buffer[0][iCount_Lecture]; // Ajout Buffer pour lecture ++iCount_Lecture; } } else if (SSPSTAT.R_W == 0) { // Ecriture -> Maître vers Esclave Data = iCount_Ecriture >> 2; Buffer[0][Data] = SSPBUF; // On ajoute les données dans un tableau LATD = Buffer[0][Data]; // On affiche les données sur le PORTD ++iCount_Ecriture; } } SSPCON1.CKP = 1; // On relache l'horloge pour lancer la suite PIR1.SSPIF = 0; // On remet à zéro le drapeau d'interruption et on attent la prochaine ! } void init() { ADCON1 = 0xFF; // On configure toutes les broches en numérique (0 ou 1) ADCON0.ADON = 0; // On désactive le module de convertisseur analogique numérique CMCON = 0x07; // On désactive le mode comparateur ADCON1.VCFG0 = 0; // Bien penser à desactiver les tensions de références ! ADCON1.VCFG1 = 0; TRISA = 0xFF; // Config Entrée PORTA -> (RA0 à RA7) PORTA = 0x00; LATA = 0x00; TRISB = 0x3F; // Config les broches du PORTB - RB0 à RB5 comme entrée (SDA/SCL) PORTB = 0x00; LATB = 0x00; TRISD = 0x00; // Config PORTD en sortie pour la lecture PORTD = 0x00; LATD = 0x00; TRISE = 0x00; // Config MCLR PORTE = 0x00; LATE = 0x00; INTCON.GIE = 1; // On active les interruptions globales INTCON.PEIE = 1; // On active les interruptions périphériques PIE1.SSPIE = 1; // On active le MSSP ( Module port série synch principal I2C ou SPI) PIR1.SSPIF = 0; // On met le drapeau d'interruption à zéro pour commencer SSPCON1.SSPEN = 1; // On active les broches RB0 et RB1 en mode SDA et SCL // On active le mode 7 bits (0 à 7 = 8 bits) avec interruptions sur bit Start et Bit Stop SSPCON1.SSPM0 = 0; SSPCON1.SSPM1 = 1; SSPCON1.SSPM2 = 1; SSPCON1.SSPM3 = 1; SSPADD = cAdrr; // Configuration de l'adresse de l'esclave du PIC 18F4550 SSPSTAT.SMP = 1; // vitesse de communication 100 Khz (standard) SSPCON2.SEN = 1; // On active l'étirement de l'horloge SSPCON2.SEN = 1; // On active l'étirement de l'horloge iCount_Ecriture = 0; iCount_Lecture = 0; Data = 0; VideBuffer = SSPBUF; // On vide le Buffer correctement ! Delay_ms(100); // On attend un peu la fin de l'initialisation } void main() { init(); // Initialisation du PIC18F4550 while (1) { if ((P_bit == 1) && (Data >= 8)) { iCount_Ecriture = 0; // On remet à "0" le comptage de l'écriture iCount_Lecture = 0; // On remet à "0" le comptage de lecture Data = 0; // et on receommence le cycle ! VideBuffer = SSPBUF; // on vide correctement le Buffer ! } if ((P_bit == 1) && (iCount_Lecture >= 8)) { iCount_Ecriture = 0; // On remet à "0" le comptage de l'écriture iCount_Lecture = 0; // On remet à "0" le comptage de lecture Data = 0; // et on receommence le cycle ! VideBuffer = SSPBUF; // on vide correctement le Buffer ! } // Lecture du tableau et affichage des données sur le PORTD if (PORTA.B0 == 1){ LATD = Buffer[0][0]; } if (PORTA.B1 == 1){ LATD = Buffer[0][1]; } if (PORTA.B2 == 1){ LATD = Buffer[0][2]; } if (PORTA.B3 == 1){ LATD = Buffer[0][3]; } if (PORTB.B2 == 1){ LATD = Buffer[0][4]; } if (PORTB.B3 == 1){ LATD = Buffer[0][5]; } if (PORTB.B4 == 1){ LATD = Buffer[0][6]; } if (PORTB.B5 == 1){ LATD = Buffer[0][7]; } } } |
Fonctionnement
Niveau programmation MikroC, je vous laisse vous rendre sur ces deux liens pour explications détaillés :
PIC18F4550 (esclave) – OpenMV (maître) – mode lecture – I2C
PIC18F4550 (esclave) – OpenMV (maître) – mode ecriture – I2C
Schéma
Changement d’adresse
Comme évoqué dans la présentation, J’ai réalisé différents tests (8 au total) avec d’autres adresses à savoir de bien respecter que le bit0 doit être à un état logique bas “0”. Pour ne pas compliquer, je fais un décalage à gauche uniquement côté du PIC et non de la cam OpenMV (voir ci-dessous les différentes adresses)
| Test | Adresse OpenMV (cAdrr) | Adresse 18F4550 (cAdrr) | Compatibilité |
| 1 | 0x04 (100b) | 0x08 (1000b) | OK |
| 2 | 0x10 (10000b) | 0x20 (100000b) | OK |
| 3 | 0x14 (10100b) | 0x28 (101000b) | OK |
| 4 | 0xF0 (11110000b) | 0xE0 (11100000b) | OK |
| 5 | 0xAA (10101010b) | 0x54 (01010100b) | OK |
| 6 | 0x80 (10000000b) | 0x00 (00000000b) | NOK |
| 7 | 0x40 (01000000b) | 0x80 (10000000b) | OK |
| 8 | 0x6C (01101100b) | 0xD8 (11011000b) | OK |
Comme vous pouvez le constater, il suffit de rajouter un “0” tout à droite, ce qui décale l’octet à gauche et donc supprime le dernier chiffre tout à gauche.
Test sur platine EasyPIC V7
Oui !! L’envoi des données via la cam OpenMV sont récupéré par les interruptions du PIC18F4550 afin qu’il affiches sur le PORTD ces données. l’affichage se réalise sur le PORTD de ma platine EasyPICV7, les leds s’allumes par comptage binaire de 0 à 7.
Le câblage est réalisés sur le PORTC broche RC3(SCL) vers RB1(SCL du PIC18F4550) et RC4(SDA) vers RB0(SDA du PIC18F4550). En effet ce pont entre le PORTC permet de ramener les résistances de 4k7 sur le PORTB où sont reliés les broches SDA et SCL du PIC18F4550 situé sur la platine EasyPicV7. Ne pas oublier de placer les switchs (SW4) RC3 et RC4 en postion “ON” afin de mettre en circulation les résistances PULL-UP
Pas de photo désolé mais vous avez le code et le schéma c’est le principal ;-).
Historiques
15/01/2021
-1er mise à disposition
Dernière mise à jour le 15/01/2021
Présentation
Nous allons voir maintenant comment lire des données sur un PIC 18F4550 (ou autre) afin de renvoyer ces données à la cam OpenMV
Dans un premier temps, il faut “caler”, ou dirais-je rendre la communication I2C compatible entre la cam et le PIC pour arriver en sorte à ce que la cam puisse écrire des données sur le PIC qui une fois reçues, ce dernier les déposeras tour à tour dans un tableau (array) (tableau de 8×1) qu’on nommera Buffer[8][1].
Une bonne configuration entre la cam OpenMV et le PIC18F4550 est avant tout l’adressage. Oui sans adresse côté PIC il sera impossible de communiquer avec ce dernier. Pour plus de détails sur comment configurer le PIC en mode esclave, vous pouvez vous rendre dans cette rubrique PIC18F4550 (esclave) – OpenMV (maître) – mode I2C.
Pour faire “simple”, si vous configurez votre cam OpenMV avec pour adresse cAdrr = 0x02, il faudra configurer le PIC avec cAdrr = 0x04 (ajout de 1bit à droite donc décalage vers la gauche pour le PIC18F4550 !! – 0x02 << 1 donne 0x04).
ou
si vous configurez votre cam OpenMV avec cette fois-ci pour adresse cAdrr = 0x02 >> 1 (suppression de 1 bit à droite cette fois-ci réalisé au niveau de la cam, décalage à droite !, ce qui donne 0x01), alors côté PIC 18F4550 laisser cAdrr = 0x02).
C’est cette histoire de décalage qui va permettre de rendre compatible la cam OpenMV avec le PIC (plusieurs tests sont situé un peu plus bas pour voir ensemble le principe c’est beaucoup plus parlant).
Mode Lecture – Cam OpenMV vers PIC18F4550
Intéressons nous sur ces lignes de code côté OpenMV afin de réaliser la lectures des données situées dans le Buffer du PIC18F4550.
MAÎTRE – Programmation Python – Cam OpenMV STM32F765VI ARM
# Maître – OpenMV I2C – Lecture – Python – Electronique71.com
from pyb import I2C import utime i2c = I2C(2) # creation du bus I2C i2c.init(I2C.MASTER, baudrate=100000) # initialisation du maître sur 100Khz # 0x02 >> 1 : +1 bit à droite (décalage vers la gauche de l'adresse donne 0x04 pour PIC) cAdrr = 0x02 >> 1 # si cAdrr = 0x02 côté cam OpenMV alors mettre cAdrr = 0x04 côté PIC18F4550 pour compatibilité ! utime.sleep_ms(500)# On attend un peu la fin de l'initialisation du bus I2C. Buffer = bytearray(8) # Création du Buffer (1 octet = 8bits) print("Nettoyage Buffer !! -> " + str(i2c.recv(Buffer,cAdrr))) while (True): # Lecture print(i2c.recv(Buffer,cAdrr)) print("Fin de lecture!!") utime.sleep_ms(1000) # petite tempo de 1sec afin de voir l'affichage |
Fonctionnement
Niveau initialisation, on y retrouve l’initialisation du bus I2C en mode “MASTER” (maître) sur 100Khz, puis, niveau adresse je reste toujours sur la même pour ne pas changer mes habitudes avec cAdrr = 0x02 >> 1, et s’enchaine tout de suite derrière une temporisation de 500ms afin de voir l’affichages des données.
Une autre chose à signaler, c’est la déclaration du Buffer sur un tableau de 1 octet (8 bits) c’est dans ce buffer que nous allons récolter toutes ces données du PIC.
Avant de se lancer dans toute la programmation on va déjà faire un nettoyage du Buffer pour commencer une extraction sur des bases saines !!
Dans la boucle “While”, nous affichons les données du PIC18F4550 en permanence via la fonction “recv” qui comporte deux arguments qui sont:
* Le buffer ou nous allons stocker les 8 bits;
* L’adresse qui correspond à l’esclave que nous voulons lire.
Le buffer, ici paramétré à 8 par l’intermédiaire de la variable bytearray(8), va réclamer 8 bit , c’est à dire qu’il va lancer 8 fois la lecture sur le PIC. Si vous mettez 7 alors il va lancer 7 fois etc.. etc.. Ici la programmation est réalisée sur 8 bits, car j’ai programme le PIC18F4550 sur un Buffer de 1 octet (8bits Buffer[8][1]).
Afin de voir ce qu’il se passe niveau terminal côté OpenCV, je vous ai fais une capture d’écran ou la boucle s’exécute toute les secondes.
Terminal OpenMV
Ci-dessous, une capture des données situées dans le buffer du PIC18F4550

Sans plus attendre, regardons maintenant la programmation côté PIC toujours en langage C (MikroC) et niveau réglage horloge cela donne ceci:
ESCLAVE – Programmation MikroC – PIC18F4550

// Esclave – PIC18F4550 – Lecture – MikroC – Electronique71.com
const cAdrr = 0x02; // cAdrr = 0x02 correspond à l'adresse de l'esclave // Si cAdrr = 0x04 côté PIC alors mettre cAddr = 0x02 côté cam OpemMV pour compatibilité ! unsigned char iCount_Lecture; unsigned char Data; unsigned char VideBuffer; // Tableau 2 dimensions pour envoyer donner vars cam OpenMV unsigned char Buffer[8][1] = { {0x00}, // bit 0 {0x01}, // bit 1 {0x02}, // bit 2 {0x03}, // bit 3 {0x04}, // bit 4 {0x05}, // bit 5 {0x06}, // bit 6 {0x07}, // bit 7 }; // Tableau 1 dimension pour envoyer donner vars cam OpenMV //unsigned char Buffer[8] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07}; void interrupt() // On contrôle chaque interruptions { if (PIR1.SSPIF == 1) { // SSPSTAT.R_W -> bit0 = 1: Lecture ou bit0 = 0: Ecriture if (SSPSTAT.R_W == 1) { if (BF_bit == 1) // On attend que le Buffer soit bien remplis avant d'envoyer ! { // Lecture -> Maître (OpenMV) vers Esclave (18F4550) // On ajoute les différents bits du Buffer pour envoyer ver OpenMV SSPBUF = Buffer[0][iCount_Lecture]; ++iCount_Lecture; // On ajoute les données dans un tableau } } } SSPCON1.CKP = 1; // On relache l'horloge pour lancer la suite PIR1.SSPIF = 0; // On remet à zéro le drapeau d'interruption et on attent la prochaine ! } void init() { ADCON1 = 0xFF; // On configure toutes les broches en numérique (0 ou 1) ADCON0.ADON = 0; // On désactive le module de convertisseur analogique numérique CMCON = 0x07; // On désactive le mode comparateur ADCON1.VCFG0 = 0; // Bien penser à desactiver les tensions de références ! ADCON1.VCFG1 = 0; TRISB = 0x03; // Config les broches du PORTB - RB0 et RB1 comme entrée (SDA/SCL) PORTB = 0x00; LATB = 0x00; TRISD = 0x00; // Config PORTD en sortie pour la lecture PORTD = 0x00; LATD = 0x00; TRISE = 0x00; // Config MCLR PORTE = 0x00; LATE = 0x00; INTCON.GIE = 1; // On active les interruptions globales INTCON.PEIE = 1; // On active les interruptions périphériques PIE1.SSPIE = 1; // On active le MSSP ( Module port série synch principal I2C ou SPI) PIR1.SSPIF = 0; // On met le drapeau d'interruption à zéro pour commencer SSPCON1.SSPEN = 1; // On active les broches RB0 et RB1 en mode SDA et SCL // On active le mode 7 bits (0 à 7 = 8 bits) avec interruptions sur bit Start et Bit Stop SSPCON1.SSPM0 = 0; SSPCON1.SSPM1 = 1; SSPCON1.SSPM2 = 1; SSPCON1.SSPM3 = 1; SSPADD = cAdrr; // Configuration de l'adresse de l'esclave du PIC 18F4550 SSPSTAT.SMP = 1; // vitesse de communication 100 Khz (standard) SSPCON2.SEN = 1; // On active l'étirement de l'horloge SSPCON2.SEN = 1; // On active l'étirement de l'horloge iCount_Lecture = 0; VideBuffer = SSPBUF; // On vide le Buffer correctement ! Delay_ms(100); // On attend un peu la fin de l'initialisation } void main() { init(); // Initialisation du PIC18F4550 while (1) { if ((P_bit == 1) && (iCount_Lecture >= 8)) { iCount_Lecture = 0; // On remet à "0" à 8 VideBuffer = SSPBUF; // On vide le buffer correctement !! } } } |
Fonctionnement
Dans cette configuration, le PIC réalise uniquement la récupération des élements du tableau Buffer[8][1] ou plus précisement Buffer[0][iCount_Lecture] ou on incrémente les lignes du Buffer une à une afin de récupérer les données et de les renvoyer au Maître (cam OpenMV).
Le tableau tout du moins le Buffer composé de 8×1 (8 élements qui stock 1 donnée) a été initialisé de 0 à 7 en héxadécimal.
1er envoi = Adresse:
Lorsque la cam OpenMV va faire une demande de lecture, la fonction “recv” vu précédemment va contenir un 1er octet qui correspondra TOUJOURS à l’adresse (cAdrr = 0x02 >> 2),
Dans ce premier octet d’envoi le bit de poids faible (bit0) sera à l’état “1” – il n’y a rien à faire de plus dans la fonction “recv” qui réalise tout le travail, tout du moins la seul chose à faire c’est de rentrer le Buffer dans cette fonction ainsi quel’adresse de l’esclave afin de stocker les données pour les lires sur le terminal.
Lorsque l’adresse est reconnue par le PIC18F4550, une interruption sera déclenchée à condition bien évidemment d’avoir pensé à activer les bits INTCON.GIE = 1 et INTCON.PEIE = 1 dans le registre INTCON du PIC. Le registre SSPSR quant à lui va recevoir le 1er octet qui comporte l’adresse.
Si l’adresse est reconnue par le PIC, le registre SSPSR va transférer l’octet reçu vers le buffer interne du PIC qui se nomme SSPBUF, et c’est à ce moment précis lorsque le SSPBUF est “rempli” (1 octet = 8bits) une interruption survient et met à l’état logique haut “1” le bit SSPIF du registre PIR1.
Nous nous retrouvons maintenant au niveau de la ligne SSPSTAT.R_W. Ce bit R_W (R=read/W=Write), permet de lire le bit de poids faible (bit0) afin de voir si celui-ci est à l’état logique haut “1” qui correspond à une lecture.
(Je tiens à le rappeler, mais ce bit change uniquement d’état sur l’envoi du 1er octet ! et non pas surs les autres octets situés sur le bus I2C)
Dans cette programmation, le code ne fait uniquement de récupérer chaque élements du tableau (Buffer[8][1]), afin de les copier dans le registre SSPBUF. Chaque fois qu’une demande de lecture est envoyées par la cam OpenMV, le PIC ne fait que de transmettre ses données et exécutera un total de 8 tour suite à la réclamation de la cam OpenMV via la fonction “bytearray(8)”.
J’ai crée une variable que j’ai nommée “VideBuffer”. Cette variable (optionnelle) permet de vider le registre SSPBUF mais n’est pas obligatoire. Dans certaine programmation nous devons créer ce genre de variable pour “vider” le Buffer alors j’ai gardé ce principe.
Schéma
Changement d’adresse
Comme évoqué dans la présentation, J’ai réalisé différents tests (8 au total) avec d’autres adresses à savoir de bien respecter que le bit0 doit être à un état logique bas “0”. Pour ne pas compliquer, je fais un décalage à gauche uniquement côté du PIC et non côté cam OpenMV (voir ci-dessous les différentes adresses)
| Test | Adresse OpenMV (cAdrr) | Adresse 18F4550 (cAdrr) | Compatibilité |
| 1 | 0x04 (100b) | 0x08 (1000b) | OK |
| 2 | 0x10 (10000b) | 0x20 (100000b) | OK |
| 3 | 0x14 (10100b) | 0x28 (101000b) | OK |
| 4 | 0xF0 (11110000b) | 0xE0 (11100000b) | OK |
| 5 | 0xAA (10101010b) | 0x54 (01010100b) | OK |
| 6 | 0x80 (10000000b) | 0x00 (00000000b) | NOK |
| 7 | 0x40 (01000000b) | 0x80 (10000000b) | OK |
| 8 | 0x6C (01101100b) | 0xD8 (11011000b) | OK |
Comme vous pouvez le constater, il suffit de rajouter un “0” tout à droite, ce qui décale l’octet à gauche et donc supprime le dernier chiffre tout à gauche.
Test sur platine EasyPIC V7
Le câblage est réalisés sur le PORTC broche RC3(SCL) vers RB1(SCL du PIC18F4550) et RC4(SDA) vers RB0(SDA du PIC18F4550). En effet ce pont entre le PORTC permet de ramener les résistances de 4k7 sur le PORTB où sont reliés les broches SDA et SCL du PIC18F4550 situé sur la platine EasyPicV7. Ne pas oublier de placer les switchs (SW4) RC3 et RC4 en postion “ON” afin de mettre en circulation les résistances PULL-UP
Aucunes photos mise à part celle située en présentation….
Historiques
15/01/2021
-1er mise à disposition
Dernière mise à jour le 15/01/2021
Présentation
Vous avez envie d’écrire des données via la liaison I2C sur un PIC 18F4550 (ou autres) en utilisant la cam OpenMV ? c’est ce que nous allons voir dans cette article, ou il y a aura la programmation de la cam OpenMV en Python et un autre “bout” de code en MikroC pour le PIC.
Dans un premier temps, il faut “caler”, ou dirais-je rendre la communication I2C compatible entre la cam et le PIC pour arriver en sorte à ce que la cam puisse écrire des données sur le PIC qui une fois reçues, ce dernier les déposeras tour à tour dans un tableau (array) (tableau de 8×1) qu’on nommera Buffer[8][1].
Une bonne configuration entre la cam OpenMV et le PIC18F4550 est avant tout l’adressage. Oui sans adresse côté PIC il sera impossible de communiquer avec ce dernier. Pour plus de détails sur comment le configurer en mode esclave, vous pouvez vous rendre dans cette rubrique PIC18F4550 (esclave) – OpenMV (maître) – mode I2C.
Pour faire “simple”, si vous configurez votre cam OpenMV avec pour adresse cAdrr = 0x02, il faudra configurer le PIC avec cAdrr = 0x04 (ajout de 1bit à droite donc décalage vers la gauche pour le PIC18F4550 !! – 0x02 << 1 donne 0x04).
ou
si vous configurez votre cam OpenMV avec cette fois-ci pour adresse cAdrr = 0x02 >> 1 (suppression de 1 bit à droite cette fois-ci réalisé au niveau de la cam, décalage à droite !, ce qui donne 0x01), alors côté PIC 18F4550 laisser cAdrr = 0x02).
C’est cette histoire de décalage qui va permettre de rendre compatible la cam OpenMV avec le PIC (on fera plusieurs exemples un peu plus bas pour voir ensemble le principe c’est beaucoup plus parlant).
Mode Ecriture – Cam OpenMV vers PIC18F4550
Ce que je vous propose c’est de regarder dans un 1er temps le code côté cam en Python, et nous ferons une explication sur le principe de fonctionnement.
MAÎTRE – Programmation Python – Cam OpenMV STM32F765VI ARM
# Maître – OpenMV I2C – Ecriture – Python – Electronique71.com
from pyb import I2C import utime i2c = I2C(2) # creation du bus I2C i2c.init(I2C.MASTER, baudrate=100000) # initialisation du maître sur 100Khz # 0x02 >> 1 : +1 bit à droite (décalage vers la gauche de l'adresse donne 0x04 pour PIC) cAdrr = 0x02 >> 1 # si cAdrr = 0x02 côté cam OpenMV alors mettre cAdrr = 0x04 côté PIC18F4550 pour compatibilité ! utime.sleep_ms(500)# On attend un peu la fin de l'initialisation du bus I2C. while (True): # Ecriture i2c.send(0x00, addr = cAdrr) # bit 0 utime.sleep_ms(500) # -> Temp Min : utime.sleep_us(50) i2c.send(0x01, addr = cAdrr) # bit 1 utime.sleep_ms(500) # -> Temp Min : utime.sleep_us(50) i2c.send(0x02, addr = cAdrr) # bit 2 utime.sleep_ms(500) # -> Temp Min : utime.sleep_us(50) i2c.send(0x03, addr = cAdrr) # bit 3 utime.sleep_ms(500) # -> Temp Min : utime.sleep_us(50) i2c.send(0x04, addr = cAdrr) # bit 4 utime.sleep_ms(500) # -> Temp Min : utime.sleep_us(50) i2c.send(0x05, addr = cAdrr) # bit 5 utime.sleep_ms(500) # -> Temp Min : utime.sleep_us(50) i2c.send(0x06, addr = cAdrr) # bit 6 utime.sleep_ms(500) # -> Temp Min : utime.sleep_us(50) i2c.send(0x07, addr = cAdrr) # bit 7 print("Fin d'écriture !!") break |
Fonctionnement
Ce code en plusieurs lignes permet uniquement d’envoyer des données sur le bus I2C, et c’est la fonction “send” qui va réaliser tout le travail.
afin de voir ce qu’il se passe côté PIC, j’ai ajouter entre chaque envoi une temporisation de 500ms ce qui permettra de voir le changement des états des leds sur la platine EasyPICV7 (rien ne vous empêche de faire ce test sur une platine d’expérimentation sans soudure).
Dans cette fonction “send” (envoyer) il faut stipuler les données à transmettres, ici, je ne fais simplement l’envoi de chiffre de 0 à 7 en hexadécimal. Biensûr il est possible d’envoyer des caractères comme des lettres en prenant soin d’ajouter ces caractères entre guillemet comme ceci : send(‘A’, addr = cAdrr) – ou send(‘J’, addr = cAdrr) – ou voir même des symboles comme send(‘#’, addr = cAdrr)…etc…etc…
Ce code permet uniquement d’écrire sur le PIC18F4550 rien de plus c’est déjà pas mal pour un début.
Penchons nous côté programmation du PIC18F4550 en utilisant le langage C (MikroC), et regardons les réglages de horloge ou je procède ainsi.
ESCLAVE – Programmation MikroC – PIC18F4550

// Esclave – PIC18F4550 – Ecriture – MikroC – Electronique71.com
const cAdrr = 0x02; // cAdrr = 0x02 correspond à l'adresse de l'esclave // Si cAdrr = 0x04 côté PIC alors mettre cAddr = 0x02 côté cam OpemMV pour compatibilité ! unsigned char iCount_Ecriture; unsigned char Data; unsigned char VideBuffer; // Tableau 2 dimensions pour envoyer donner vars cam OpenMV unsigned char Buffer[8][1] = { {0x00}, // bit 0 {0x00}, // bit 1 {0x00}, // bit 2 {0x00}, // bit 3 {0x00}, // bit 4 {0x00}, // bit 5 {0x00}, // bit 6 {0x00}, // bit 7 }; // Tableau 1 dimension pour envoyer donner vars cam OpenMV //unsigned char Buffer[8] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07}; void interrupt() // On contrôle chaque interruptions { if (PIR1.SSPIF == 1) { // SSPSTAT.R_W -> bit0 = 1: Lecture ou bit0 = 0: Ecriture if (SSPSTAT.R_W == 0) { // Ecriture -> Maître (OpenMV) vers Esclave (18F4550) Data = iCount_Ecriture >> 2; Buffer[0][Data] = SSPBUF; // On ajoute les données dans un tableau LATD = Buffer[0][Data]; // On affiche les données sur le PORTD ++iCount_Ecriture; } } SSPCON1.CKP = 1; // On relache l'horloge pour lancer la suite PIR1.SSPIF = 0; // On remet à zéro le drapeau d'interruption et on attent la prochaine ! } void init() { ADCON1 = 0xFF; // On configure toutes les broches en numérique (0 ou 1) ADCON0.ADON = 0; // On désactive le module de convertisseur analogique numérique CMCON = 0x07; // On désactive le mode comparateur ADCON1.VCFG0 = 0; // Bien penser à desactiver les tensions de références ! ADCON1.VCFG1 = 0; TRISA = 0xFF; // Config Entrée PORTA -> (RA0 à RA7) PORTA = 0x00; LATA = 0x00; TRISB = 0x3F; // Config les broches du PORTB - RB0 à RB5 comme entrée (SDA/SCL) PORTB = 0x00; LATB = 0x00; TRISD = 0x00; // Config PORTD en sortie pour la lecture PORTD = 0x00; LATD = 0x00; TRISE = 0x00; // Config MCLR PORTE = 0x00; LATE = 0x00; INTCON.GIE = 1; // On active les interruptions globales INTCON.PEIE = 1; // On active les interruptions périphériques PIE1.SSPIE = 1; // On active le MSSP ( Module port série synch principal I2C ou SPI) PIR1.SSPIF = 0; // On met le drapeau d'interruption à zéro pour commencer SSPCON1.SSPEN = 1; // On active les broches RB0 et RB1 en mode SDA et SCL // On active le mode 7 bits (0 à 7 = 8 bits) avec interruptions sur bit Start et Bit Stop SSPCON1.SSPM0 = 0; SSPCON1.SSPM1 = 1; SSPCON1.SSPM2 = 1; SSPCON1.SSPM3 = 1; SSPADD = cAdrr; // Configuration de l'adresse de l'esclave du PIC 18F4550 SSPSTAT.SMP = 1; // vitesse de communication 100 Khz (standard) SSPCON2.SEN = 1; // On active l'étirement de l'horloge SSPCON2.SEN = 1; // On active l'étirement de l'horloge iCount_Ecriture = 0; Data = 0; VideBuffer = SSPBUF; // On vide le Buffer correctement ! Delay_ms(100); // On attend un peu la fin de l'initialisation } void main() { init(); // Initialisation du PIC18F4550 while (1) { if ((P_bit == 1) && (Data >= 8)) { iCount_Ecriture = 0; // On remet à "0" à 8 Data = 0; // et on receommence le cycle ! } // Lecture du tableau et affichage des données sur le PORTD if (PORTA.B0 == 1){ LATD = Buffer[0][0]; } if (PORTA.B1 == 1){ LATD = Buffer[0][1]; } if (PORTA.B2 == 1){ LATD = Buffer[0][2]; } if (PORTA.B3 == 1){ LATD = Buffer[0][3]; } if (PORTB.B2 == 1){ LATD = Buffer[0][4]; } if (PORTB.B3 == 1){ LATD = Buffer[0][5]; } if (PORTB.B4 == 1){ LATD = Buffer[0][6]; } if (PORTB.B5 == 1){ LATD = Buffer[0][7]; } } } |
Fonctionnement
Tout réside sur la réception côté PIC18F4550 ou nous devons utiliser les interruptions.
1er envoi = Adresse:
Lorsque la cam OpenMV va envoyer des données, le 1er octet correspondra TOUJOURS à l’adresse, ici l’adresse pour communiquer avec le PIC est cAdrr = 0x02.
Dans cette adresse le bit de poids faible (bit0) sera à l’état “0” car nous faisons uniquement une écriture.
Lorsque l’adresse est reconnue par le PIC18F4550, une interruption sera déclenchée à condition bien sûr d’avoir activé soigneusement les bits INTCON.GIE = 1 et INTCON.PEIE = 1 dans le registre INTCON du PIC. Le registre SSPSR quant à lui ne va recevoir uniquement un octet complet à chaque réception des données qui émane de la cam OpenMV.
Si l’adresse est reconnue pas le PIC, le registre SSPSR va transférer cette octet vers le buffer interne du PIC qui se nomme SSPBUF, et c’est à ce moment précis lorsque le SSPBUF est “rempli” (1 octet = 8bits) une interruption survient et met à l’état logique haut “1” le bit SSPIF du registre PIR1.
Nous nous retrouvons maintenant au niveau de la ligne SSPSTAT.R_W. Ce bit R_W (R=read/W=Write), permet de lire le bit de poids faible (bit0) afin de voir si celui-ci est à l’état logique bas “0” qui correspond à une écriture ce qui est notre cas , ou à l’état logique haut “1”, dans ce cas nous somme en écriture. Il est important de signaler que ce bit change uniquement d’état sur l’envoi du 1er octet ! (adresse ou le bit R_W contrôle l’état du bit de poids faible bit0).
nous passons à la ligne précédente ou nous allons faire un décalage de 2 vers la droite ! (on verras un peu plus loin pourquoi mais attention ce sera compliqué niveau explication !).
Pour le reste, nous récupérons les données de la cam OpenMV afin de les placer tour à tour dans un tableau nommé Buffer.
Une fois l’écriture terminée, il suffit de présser les boutons RA0 ou RA1 ou … (voir schéma ci-dessous) pour afficher les valeurs envoyées depuis la cam OpenMV tout simplement.
2ème envoi:
Pour le 2ème envoi, idem encore une fois on fait une lecture de l’adresse et on recommence comme évoqué précédement, c’est bien la ou réside la particularité de la fonction “send” ou je serais dans l’obligation de faire un décalage de 2 vers la droite…
Pourquoi ??? Data = iCount_Ecriture >> 2;
Hum ?!! vous êtes sur de vouloir attaquer ce sujet ? ok très bien.
Chaque fois que nous envoyons via la cam OpenMV un caractère sur la liaison I2C en utilisant la fonction “send”, cela compte 4 tours ?!, j’ai réalisé plusieurs fois des tests et la pour le coup je n’ai pas compris et je ne comprend toujours pas pourquoi….
Ce qui est sûr c’est que niveau affichage voila ce qu’il en résulte si nous faisons des envoies au coup par coup:
Sans décalage:
PORTC affiche : 100b = 4
PORTC affiche : 1000b = 8
PORTC affiche : 1100b = 12
PORTC affiche : 10000b = 16
PORTC affiche : 10100b = 20
PORTC affiche : 11000b = 24
PORTC affiche : 11100b = 28
PORTC affiche : 100000b = 32
PORTC affiche : 100100b = 36
PORTC affiche : 101000b = 40
PORTC affiche : 101100b = 44
PORTC affiche : 110000b = 48
PORTC affiche : 110100b = 52
Avec décalage:
PORTC affiche : 100b = 4 avec décalage 100b >> 2 = 1 = 1
PORTC affiche : 1000b = 8 avec décalage 1000b >> 2 = 10 = 2
PORTC affiche : 1100b = 12 avec décalage 1100b >> 2 = 11 = 3
PORTC affiche : 10000b = 16 avec décalage 10000b >> 2 = 100 = 4
PORTC affiche : 10100b = 20 avec décalage 10100b >> 2 = 101 = 5
PORTC affiche : 11000b = 24 avec décalage 11000b >> 2 = 110 = 6
PORTC affiche : 11100b = 28 avec décalage 11100b >> 2 = 111 = 7
PORTC affiche : 100000b = 32 avec décalage 100000b >> 2 = 1000 = 8
PORTC affiche : 100100b = 36 avec décalage 100100b >> 2 = 1001 = 9
PORTC affiche : 101000b = 40 avec décalage 101000b >> 2 = 1010 = 10
PORTC affiche : 101100b = 44 avec décalage 101100b >> 2 = 1011 = 11
PORTC affiche : 110000b = 48 avec décalage 110000b >> 2 = 1100 = 12
PORTC affiche : 110100b = 52 avec décalage 110100b >> 2 = 1101 = 13
PORTC affiche : 111000b = 56 avec décalage 111000b >> 2 = 1110 = 14
PORTC affiche : 111100b = 60 avec décalage 111100b >> 2 = 1111 = 15
Je vous rassure, je me suis gratté la tête un bon moment pour arriver à trouver une astuce afin de contourner ce problème ce que je peux vous dire, c’est que la fonction “send” côté cam OpenMV va envoyer l’adresse + les données … mettons ça de côté, le principal c’est d’arriver à un code fonctionnel comme celui-ci cadencé niveau du PIC à l’aide d’un quartz de 8Mhz uniquement.
Schéma
Changement d’adresse
Comme évoqué dans la présentation, J’ai réalisé différents tests (8 au total) avec d’autres adresses à savoir de bien respecter que le bit0 soit à un état logique bas “0”. Pour ne pas compliquer, je fais un décalage à gauche uniquement côté PIC et non côté cam OpenMV (voir ci-dessous les différentes adresses)
| Test | Adresse OpenMV (cAdrr) | Adresse 18F4550 (cAdrr) | Compatibilité |
| 1 | 0x04 (100b) | 0x08 (1000b) | OK |
| 2 | 0x10 (10000b) | 0x20 (100000b) | OK |
| 3 | 0x14 (10100b) | 0x28 (101000b) | OK |
| 4 | 0xF0 (11110000b) | 0xE0 (11100000b) | OK |
| 5 | 0xAA (10101010b) | 0x54 (01010100b) | OK |
| 6 | 0x80 (10000000b) | 0x00 (00000000b) | NOK |
| 7 | 0x40 (01000000b) | 0x80 (10000000b) | OK |
| 8 | 0x6C (01101100b) | 0xD8 (11011000b) | OK |
Comme vous pouvez le constater, il suffit de rajouter un “0” tout à droite, ce qui décale l’octet à gauche et donc supprime le dernier chiffre tout à gauche.
Test sur platine EasyPIC V7
Oui !! L’envoi des données via la cam OpenMV sont récupérés par les interruptions du PIC18F4550 afin que celle-ci soient affichées sur le PORTD. Ainsi, les leds s’allumes par comptage binaire de 0 à 7.
Le câblage est réalisés sur le PORTC broche RC3(SCL) vers RB1(SCL du PIC18F4550) et RC4(SDA) vers RB0(SDA du PIC18F4550). En effet ce pont entre le PORTC permet de ramener les résistances de 4k7 sur le PORTB où sont reliés les broches SDA et SCL du PIC18F4550 situé sur la platine EasyPicV7. Ne pas oublier de placer les switchs (SW4) RC3 et RC4 en postion “ON” afin de mettre en circulation les résistances PULL-UP qui sont la pour tirer vers le haut (on appel ceci des résistances de tirage d’ou l’expression PULL-UP tirage vers le haut).
Aucunes photos désolé! mais vous avez le code et le schéma c’est le principal ;-).
Historiques
15/01/2021
-1er mise à disposition
Dernière mise à jour le 10/01/2021
Présentation
Comment utiliser un microcontrôleur en mode maître (master en anglais) sur un bus I2C?
C’est ce que nous allons voir d’une façon très simple.
Soft_I2C Logiciel (Software) ou I2C Matériel (Hardware) ?
Bonne question? mais qu’est ce que cela veux bien dire ?
Pour faire simple:
I2C Logiciel (Software) : Lorsque nous parlons de I2C Logiciel c’est que votre microcontrôleur ne possède pas les broches SDA et SDL alors il faut trouver une parade afin de créer ces 2 broches pour la communication par la méthode logiciel (Software) en programmant quelques lignes de code supplémentaires. (On utilise la routine Soft_I2C de MikroC).
I2C Matériel (Hardware) : Lorsque nous parlons de I2C Matériel, c’est que votre microcontrôleur possède les broches SDA et SDL. (On utilise la routine I2C1 de MikroC)
Voilà la différence entre les deux !! pas compliquer non? avec ou sans broches SDA et SDL il est tout à fait possible de communiquer par la liaison I2C mais je vous conseille si vous devez réaliser un programme un peu plus complexe en utilisant des interruptions, vérifier le Buffer, ou autres de vous garantir que le PIC que vous utilisez soit bien équipé des broches SDA/SDL.
Bon avec tous ces grands discours essayons de regarder ça de plus prêt en commençant par des choses simples on verra ensuite le compliquer.
Exemple 1 – Écriture en boucle
J’indique en exemple qui d’ailleurs sera le seul, sur la façon d’envoyer des données via la liaison I2C lorsque le PIC18F4550 est configuré en maître. Dans cette configuration c’est le pic qui va cadencer le bus I2C (horloge réglée à 100Khz standard). le programme qui va suivre ne fait que d’envoyer des données en boucle “while” et que ces données seront réceptionné via le Debugger I2C de proteus.
// Ecriture en boucle – MikroC – Electronique71.com
void init() { ADCON1 = 0xFF; // On configure toutes les broches en numérique (0 ou 1) ADCON0.ADON = 0; // On désactive le module de convertisseur analogique numérique CMCON = 0x07; // On désactive le mode comparateur TRISB = 0x03; // config les broches du PORTB - RB0 et RB1 comme entréed (SDA/SCL)e PORTB = 0x00; LATB = 0x00; SSPCON1.SSPEN = 1; // On active les broches RB0 et RB1 en mode SDA et SCL // On active le mode 7 bits (0 à 7 = 8 bits) avec interruptions sur bit Start et Bit Stop SSPCON1.SSPM0 = 0; SSPCON1.SSPM1 = 1; SSPCON1.SSPM2 = 1; SSPCON1.SSPM3 = 1; I2C1_Init(100000); // Initialisation à 100Khz Delay_ms(100); // On attend un peu la fin de l'initialisation } void main() { init(); // Initialisation du PIC18F4550 while (1) { I2C1_Start(); // Start pour commencer à envoyer les données I2C1_Wr(0x02); // Ecriture du 1er octet (adresse de l'esclave) I2C1_Stop(); // Fin de l'envoi Delay_1sec(); // On attend 1sec avant l'envoi I2C1_Start(); // Start pour commencer à envoyer les données I2C1_Wr(0xF0); // Ecriture du 2ème octet (données) I2C1_Stop(); // Fin de l'envoi Delay_1sec(); // On attend 1sec avant l'envoi } } |
Ce code est à utiliser (version adulte)
// Ecriture en boucle – MikroC – Electronique71.com
void init() { ADCON1 = 0xFF; // On configure toutes les broches en numérique (0 ou 1) ADCON0.ADON = 0; // On désactive le module de convertisseur analogique numérique CMCON = 0x07; // On désactive le mode comparateur TRISB = 0x03; // config les broches du PORTB - RB0 et RB1 comme entréed (SDA/SCL)e PORTB = 0x00; LATB = 0x00; SSPCON1.SSPEN = 1; // On active les broches RB0 et RB1 en mode SDA et SCL // On active le mode 7 bits (0 à 7 = 8 bits) avec interruptions sur bit Start et Bit Stop SSPCON1.SSPM0 = 0; SSPCON1.SSPM1 = 1; SSPCON1.SSPM2 = 1; SSPCON1.SSPM3 = 1; I2C1_Init(100000); // Initialisation à 100Khz Delay_ms(100); // On attend un peu la fin de l'initialisation } void main() { init(); // Initialisation du PIC18F4550 while (1) { I2C1_Start(); // Start pour commencer à envoyer les données I2C1_Wr(0x02); // Ecriture du 1er octet (adresse de l'esclave) I2C1_Repeated_Start(); // On répète pour envoyer le 2ème octet I2C1_Wr(0xF0); // Ecriture du 2ème octet (données) I2C1_Stop(); // Fin de l'envoi Delay_1sec(); // On attend un peu avant de recommencer } } |
Debugger I2C – Test Proteus
Chaque données envoyées par le PIC, celle-ci sont reçu sur le Debugger I2C. Vous pouvez d’après la capture ci-dessous remarquer que nous retrouvons bien sur le Debugger 0x02 – 0xF0 – 0x02 -… en boucle.

C’était vraiement simple vous ne trouvez pas?
Test sur platine EasyPicV7
Non.
Historiques
10/01/2021
-1er mise à disposition
















