Julien

1 2 3 24


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 ?



                   (QR-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.


Version débutant

// 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

Dernière mise à jour le 12/01/2021

Présentation


Dans cette rubrique nous allons voir comment programmer la cam OpenMV en maitre afin que celle-ci communique avec le PIC18F4550 (esclave) sur le bus I2C. J’ai été surpris!! En effet il y a un décalage de bits à réaliser vers la droite  au niveau de l’adressage (1er octet), soit pour la cam OpenMV soit pour le PIC18F4550 mais pas les 2 à la fois!!. D’ailleurs j’en profite pour vous dire que ce décalage est aussi présent niveau du 1er octet entre le PIC et un Arduino UNO, il doit aussi être réaliser et pour démonstration vous pouvez vous rendre sur ce lien Communication I2C – Mode Ecriture – Arduino(Maitre) -> PIC 18F4550 (Esclave)
Nous verrons comment voir un tel décalage, et pour cela, nous allons nous servir de la cam OpenMV M7 qui utilise un processeur STM32F765VI ARM Cortex a 216 MHz, mais la version plus récente qui est la cam OpenMV H7 quant à elle utilise un processeur ARM Cortex M7 fonctionnant a 400 MHz. La différence vous l’aurez compris plus rapide pour le traitement d’image par exemple. Mais en ce qui concerne la liaison I2C le principe reste le même il ne faut pas oublier de décaler ce fameux bit vers la droite pour communiquer entre  microcontrôleur camOpenMV (ou Arduino UNO).

Compatibilité python OpenMV vers PIC18F4550 – tests EasyPicV7 – recherche adresse


Décalage d’adresse pour compatibilité ?
oui! effectivement, il faut réaliser un décalage car sinon rien ne fonctionne pas!.

Un jour je me suis posé la question si il était possible de brancher directement mon PIC18F4550 via les broches RB0(SDA) et RB1(SCL) sur les broches de la cam OpenMV qui sont P4(SCL) et P5(SDA) – ou P7(SCL) et P8(SDA) avec des résitances de tirages à 1K ohm. Pour la cam OpenMV, j’ai choisi P4 et P5 pour la création du BUS 2, et apres quelque ligne de code j’ai exécuté le script Python et là c’est le drame !! rien ne fonctionnais et aucune réception niveau PIC. Hum ?! Pourquoi ?


Pour en avoir le cœur net, j’ai donc envoyé directement l’adresse cAdrr = 0x02 sur afin que le PORTD éclaire la led RD1, et à ce moment je me suis rendu compte que c’était RD2 d’allumé et non RD1 – 1bit de plus sur la droite donc décalage à gauche !.


En réfléchissant je me suis rendu compte qu’il y a décalage vers la gauche à cause de ce bit suplémentaire (bit de poids faible)!! bizarre ?!
J’ai donc décalé l’adresse cAdrr = 0x02 >> 1 (côté cam OpenMV) vers la droite pour retirer ce bit supplémentaire afin que ce ne ça soit pas la led RD2 mais bien RD1. Après plusieurs test que nous allons voir un peu plus bas tout fonctionne à merveille et la cam OpenMV est désormais compatible avec le PIC18F4550!!


D’une manière général, si par exemple:

– cAdrr = 0x02 >> 1 (côté cam OpenMV) alors cAdrr = 0x02 (côté PIC) pour compatibilité !

– cAdrr = 0x02 (côté cam OpenMV) alors cAdrr = 0x04 (côté PIC) pour compatibilité !

Toujours réaliser le décalage de 1 (>> 1 ) côté cam OpenMV ou Arduino UNO !

Cam OpenMV (Maître) – PIC18F4550 (Esclave)


MAÎTRE – Programmation Python – Cam OpenMV STM32F765VI ARM

# Maître – OpenMV I2C – 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é !

while(True):

    # 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):
        i2c.send('A', addr = cAdrr) # Ecriture caractère 'A' led RD0 et RD6 allumées
        utime.sleep_ms(500)

        i2c.send('B', addr = cAdrr) # Ecriture caractère 'B' led RD1 et RD6 allumées
        utime.sleep_ms(500)

        i2c.send(0x01, addr = cAdrr) # Ecriture hexa '0x01' led RD0 allumé
        utime.sleep_ms(500)

        i2c.send(0xF0, addr = cAdrr) # Ecriture hexa '0xF0' led RD4 à RD7 allumées
        utime.sleep_ms(500)


Fonctionnement programmation Python

Dans le programme python ci-dessus, j’ai fixé une adresse nommée “cAdrr” de valeur 0x02 en hex (soit 2 en decimal ou bien 00000010b en binaire) mais comme vous pouvez le constater j’ai réalisé via l’opérateur ‘>>’ un décalage de 1 bit vers la gauche je rajoute en définitive 1 bit vers la droite pour que l’adresse se décale entièrement vers la gauche et je me retrouve non plus à 0x02 comme adresse mais à 0x04!! (soit 4 en décimal ou bien 00000100b en binaire), c’est la ou réside la subtilité afin de pouvoir communiquer avec le PIC18F4550 qui lui pour le coup est configuré en esclave avec pour adresse cAdrr = 0x02 qu’il ne faudra pas changer!.
Pour la suite, une fois que l’adresse a été vérifiée et validé, la cam ne fais que d’envoyer des données au PIC18F4550 qui lui les affichera sur son PORTD.


Lettre A donne un code binaire 01000001b soit 41h (h = hexa) soit led RD0 et RD6 allumées sur platine
Lettre B donne un code binaire 01000010b soit 42h (h = hexa) soit led RD1 et RD6 allumées sur platine
0x01 donne un code binaire 00000001b soit led RD0 allumé sur platine
0xF0 donne un code binaire 11110000b soit led RD4 à RD7 allumées sur platine

Bien sûr rien ne vous empêche de faire l’inverse c’est à dire de mettre pour le PIC cAdrr = 0x04 et garder pour la cam OpenMV cAdrr = 0x02 car le décalage a été réalisé cette fois-ci côté PIC !!.

ESCLAVE – Programmation MikroC – PIC18F4550

// Esclave – PIC18F455 I2C – 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é !

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)
    {
      // Lecture -> Esclave vers Maître
      SSPBUF = 0xF0;   // Données sont ajoutée dans le buffer pour la lecture
    }
    else if (SSPSTAT.R_W == 0)
    {
      // Ecriture -> Maître vers Esclave
      LATD = SSPBUF;   // Affichage des données envoyée par le maitre pour 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

  TRISB = 0x03;      // config les broches du PORTB - RB0 et RB1 comme entrée (SDA/SCL)
  PORTB = 0x00;
  LATB = 0x00;

  TRISD = 0x00;      // Configuration PORTD en sortie pour la lecture
  PORTD = 0x00;
  LATD = 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'interrutpion à 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

  Delay_ms(100);     // On attend un peu la fin de l'initialisation
}

void main()
{
  init(); // Initialisation du PIC18F4550

  while (1)
  {
    // On ne fait rien ...
  }
}


Fonctionnement programmation MikroC

Rien de particulier à dire, le PIC18F4550 est configuré en mode esclave pour plus de détails vous rendre sur ce lien PIC18F4550 – mode esclave (slave)


Conclusion

Ces deux programmation (Python et MikroC) permet uniquement via la cam OpenMV programmer en tant que maître d’envoyer des données sur l’esclave qui est le PIC18F4550 à l’adresse correspondante. Une fois les données reçues, le PIC18F4550 va gérer ces données par l’intermédiaire des interruptions afin de renvoyer les valeurs lues qui émane du maître et de les afficher sur son PORTD (allumées les différentes led du PORTD). Le but recherché dans ce chapitre était uniquement d’arriver à faire dialoguer la cam et le PIC18F4550 ensemble.

Test sur platine EasyPIC V7


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. On remarque bien toutes les 500ms un changement d’état ce qui permet d’afficher les leds de la platine EasyPIC V7.

Le cablâge 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 de 1K ohm de la platine EasyPicV7.

    
  (‘A’ donne 01000001b)          (‘B’ donne 01000010b)

      
 (0x01 donne 00000001b)        (0xF0 donne 11110000b)

Historiques


12/01/2021
– Ajout commentaire sur cAdrr = 0x02 (cam OpenMV) et cAdrr = 0x04 (PIC18F4550)

10/01/2021
-1er mise à disposition

Dernière mise à jour le 09/01/2021

Présentation


Comment utiliser un microcontrôleur en mode esclave (slave en anglais) en utilisant uniquement les interruptions? comment utiliser le “Clock stretching ” ou en français ce qui se traduit par “étirement de l’horloge”? comment les données arrivent dans le buffer et comment le microcontrôleur peut renvoyer des données en mode lecture lorsque le 8ème bit envoyé par le maitre est à l’état haut (“1” logique) – c’est ce que vous voulez connaître? et bien vous vous trouvez au bon endroit.

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.

Réglages du PIC18F4550


Avant de se lancer dans la programmation du PIC18F4550, il faut avant toutes choses commencer à savoir ce que l’on désire réaliser en I2C pour que notre PIC puisse jouer le rôle d’esclave. Le PIC18F4550 comporte sur ces broches RB0(SDA) et RB1(SCL) la possibilité de réaliser une liaison I2C, et c’est donc ces 2 broches que nous allons utiliser afin de pouvoir communiquer avec le monde exterieur. En consultant Dans le datasheet (ou doc technique) du PIC18F4550, il faut se rendre au chapitre nommé “Master Synchronoux Serial Port” (MSSP) ou on y trouve 3 autres rubriques qui sont:
– Controle Registres
– SPI Mode
– I2C Mode (Ah? interressant! , c’est donc ce mode que nous là que nous allons utiliser).
Le module MSSP en mode I2C peut être utilisé soit en mode 7 bits soit en mode 10 bits. Pour ma part je vais utiliser en mode 7 bits que nous utilisons en grande majorité.
En ce qui concerne les broches RB0 pour l’horloge série (SCL) et RB1 pour les données série (SDA), nous devons configurer ces broches comme des entrées en définissant les bits TRISB (là encore ceci est très bien spécifié au niveau du datasheet. Vous verrez un peu plus bas au niveau de la programmation que chaque lignes sont détaillées pour explication.
Ce qui va nous intéresser particulièrement ce sont les interruptions ou nous passerons du temps pour bien comprendre la mécanique interne alors commençons sans plus tarder!

(Schéma qui va nous servir pour la réalisation des transmissions de données)

Interruptions

Les données qui émanes du maitre arrives dans un ordres bien précis qui sont en 1er un octet correspondant à l’adressage qui pour moisera 0xA8 (j’ouvre une parenthèse mais si vous utiliser plusieurs PIC sur la même liaison I2C il faut que le maitre distribues les données au microcontrôleurs correspondant – prenez exemple sur un facteur lorsqu’il distribue le courrier il faut une adresse précise pour chaque personne afin que le facteur puisse retrouver à qui il doit déposer le courrier dans la boite au lettres  et bien c’est la même chose pour l’adresse du PIC,  et c’est ce 1er octet qui le détermine je ferme la parenthèse et reprenons). En ce qui concerne le 2ème octet, je vais l’utiliser à ce que l’esclave puisse envoyer l’information au maitre ou bien à ce qu’il puisse afficher l’information sur le PORTD (des leds seront ou pas allumées en fonction de chaque bits afin de voir ce qui a été envoyé par le maitre). Vous savez quoi ??! on fera les 2 un envoi pour le maitre et un affichage au niveau des leds sur le PORTD.


Comment ça fonctionne niveau interruption? c’est là que nous allons voir ça de plus prêt.  Le fonctionnement est le suivant, lorsque le maitre envoie un 1er octet (8bits) qui correspond à l’adressage, le bit de poids faible en l’occurrence le bit 0 permet de savoir si nous somme en lecture ou en écriture. “1” pour la lecture et “0” pour l’écriture tout simplement. Ensuite les octets du bit 1 au bit 7 quant à eux sont l’adresse (oui car le 1er octet envoyé bit0 à bit7 est l’adresse il ne faut surtout pas l’oublier !). On peut représenter l’envoi des données comme ceci:


Diagramme en bloc

Les bits vont arrivés de la part du maitre 1 à 1 sur la broche RB1 (SDA) qui vont se réfugier tranquillement dans le registre SSPSR. Lorsque SSPSR sera rempli des 8bits, il y aura une comparaison avec le registre SSPADD qui correspond à l’adresse de mon PIC que j’ai déja renseigné par la valeur 0xA8 ce qui donne en binaire “10101000b” si SSPSR est égale à SSPADD alors une interruption survient et aussitôt les 8bits sont déplacé dans le buffer nommé SSPBUF qui va pouvoir être lu pas compliqué non? On vérifie si SSPSR correspond à l’adresse SSPADD et si c’est ok alors on peut lire,  sinon cas contraire rien ne se passe.

Quelle adresse choisir?

Voilà une bonne question. Une adresse ne ce choisi pas au hasard, car il faut respecter le protocole I2C qui énonce que le bit de poids faible (bit0) celui-ci sert à 2 états soit logique “1” pour lecture soit logique “0” pour écriture. En fonction du bit de poids faible bit0, l’esclave réagira ou non avec un bit d’acquittement nommé ACK.


Ainsi l’adresse 0xA8 = 10101000b donne ceci en écriture et l’adresse 0xA9 = 10101001b donne ceci en lecture et vous remarquerez que le bit0 change d’état si nous somme en lecture ou en écriture. vous aimerez utiliser une autre adresse comme par exemple l’adresse 0x02 = 00000010b (écriture) donne 0x03 = 00000011b (lecture). Un dernier pour la route ? 0x78 = 01111000b (
écriture) donne 0x79 = 01111001b (lecture) pas compliqué non ? pour ma part j’utilise toujours l’adresse en tant qu’écriture.

Exemple 1 – Reconnaissance adresse cAdrr = 0x02 de l’esclave…


Seule l’adresse 0x02 est reçue par le PIC18F4550 et non les autres ainsi la broche RD1 est à l’état logique “1” de couleur rouge

// Verification adresse 0x02 – MikroC – Electronique71.com

const cAdrr = 0x02; // correspond à l'adresse de l'esclave

void interrupt() // On contrôle chaque interruptions
{
  if (PIR1.SSPIF == 1) // si SSPSTAT = SSPDR alors SSPIF = 1
  {
    LATD = SSPBUF;     // On affiche l'adresse

    SSPCON1.CKP = 1;   // On relâche l'horloge pour lancer la suite

    PIR1.SSPIF = 0;    // On remet à zéro le drapeau d'interruption et on attend 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

  TRISB = 0x03;      // config les broches du PORTB - RB0 et RB1 comme entréed (SDA/SCL)e
  PORTB = 0x00;
  LATB = 0x00;

  TRISD = 0x00;      // Configuration PORTD en sortie pour la lecture
  PORTD = 0x00;
  LATD = 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'interrutpion à 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

  Delay_ms(100);     // On attend un peu la fin de l'initialisation
}

void main()
{
  init(); // Initialisation du PIC18F4550

  while (1)
  {
    // On ne fait rien ...
  }
}


Debugger I2C – Test Proteus

Pour les tests et le principe de fonctionnement je vous laisse vous rendre sur ce lien d’explication Debugger I2C

Ci-dessous le code a copier pour test sous proteus

// Différentes adresses pour test
{SEQUENCE001=S 0xA8 N P}
{SEQUENCE002=S 0xA9 N P}
{SEQUENCE003=S 0x02 N P}


Conclusion

Le code ci-dessus en MikroC + le code du Debugger I2C de proteus montre uniquement que le PIC18F4550 en mode esclave réagira uniquement sur l’adresse 0x02 et non sur les adresse 0xA8 ou 0xA9. Une fois l’adresse correspondante reçue, il affichera celle-ci sur le PORTD c’est d’ailleurs ce que vous voyer au niveau du schéma electronique la broche RD1 est à l’état logique haut (+5V) car c’est bien l’adresse qui correspond à celle que nous avons renseigné dans le SSPADD.

On va maintenant s’interesser sur le principe de comment envoyer (si le maître demande de lire les données de l’esclave) et afficher des données (si le maître demande d’écrire des données sur l’esclave).

Exemple 2 – Ecriture/lecture adresse cAdrr = 0x02 réponse esclave…


Ecriture


Regardons déjà un 1er exemple sur l’écriture, le maitre va envoyer le 1er octet qui correspond à l’adresse (S 02 A = S 0x02 A = S 00000010b A) et tout de suite après il va enchainer avec le 2ème octets qui correspond aux données que nous allons vouloir écrire (0x05 = 00000101b). Ainsi le PORTD met à l’état logique haut RD0 et RD2.

1er octet validé à mis à l’état logique haut RD1.
2ème octet tout de suite dans la foulée a mis à l’état logique haut RD0 et RD2 (c’est pour cette raison que nous avons pas eu le temps de voir RD1 à l’état logique haut car l’interruption pour le 1er octet a été levée rapidement).


Lecture


Regardons maintenant ce que cela donne en mode lecture. Nous avons cette fois-ci toujours la même adresse sauf que le bit0 est à l’état logique “1” cela donne 0x03 = 00000011b(lecture). Dans cette configuration, c’est l’esclave qui va renvoyer ses données au maître car c’est le maitre qui réclame la lecture – la couleur bleu représente l’esclave et en rose le maître. L’esclave a envoyé comme données 0xF0 = 11110000b, et à la fin de l’envoie des données, le maîtres génère un bit d’acquittement  “Nak”en signalant qu’il à bien reçu ses données.

Ces lignes de code ci-dessous qui peuvent être copiés collés (c’est fait pour d’ailleurs ;-)) vont vous permettre de configurer votre PIC18F4550 en mode esclave. Je vous laisse le soin de modifier la variable ‘cAdrr’ pour modifier l’adresse de votre esclave.

// Ecriture/lecture adresse cAdrr = 0x02 – MikroC – Electronique71.com

const cAdrr = 0x02; // correspond à l'adresse de l'esclave

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)
    {
      // Lecture -> Esclave vers Maître
      SSPBUF = 0xF0;   // Données sont ajoutée dans le buffer pour la lecture
    }
    else if (SSPSTAT.R_W == 0)
    {
      // Ecriture -> Maître vers Esclave
      LATD = SSPBUF;   // Affichage des données envoyée par le maitre pour 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

  TRISB = 0x03;      // config les broches du PORTB - RB0 et RB1 comme entrée (SDA/SCL)
  PORTB = 0x00;
  LATB = 0x00;

  TRISD = 0x00;      // Configuration PORTD en sortie pour la lecture
  PORTD = 0x00;
  LATD = 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'interrutpion à 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

  Delay_ms(100);     // On attend un peu la fin de l'initialisation
}

void main()
{
  init(); // Initialisation du PIC18F4550

  while (1)
  {
    // On ne fait rien ...
  }
}


Debugger I2C – Test Proteus

Pour les tests et le principe de fonctionnement je vous laisse vous rendre sur ce lien d’explication Debugger I2C

Ci-dessous le code a copier pour test sous proteus

// -> Ecriture maître vers esclave
{SEQUENCE001=S 0x02 N 0x05 N P}
// -> Lecture maître de l’esclave
{SEQUENCE002=S 0x03 N P}
// -> 1er octet = 0x04 (mauvaise adresse pour l’ecriture)
{SEQUENCE003=S 0x04 N 0x05 N P}
// -> 1er octet = 0xA9 (mauvaise adresse pour la lecture)
{SEQUENCE004=S 0xA9 N P}


Conclusion

Niveau programmation “un peu” de changement .
– Ecriture : 0x02 = 00000010b donne bien une ecriture et le 2ème octet (0x05) va lui aussi être envoyé pour être écrit.
– Lecture : 0x03 = 00000011b donne bien une lecture, c’est en mode lecture que l’esclave va renvoyer ses données afin que le maître puisse les lires.
En faite que faisons nous exactement? (remonter un peu plus haut pour voir le “Diagramme bloc”).
On envoi sur la broche SDA, le 2ème octet 0x05 = 00000101b. Cet octet va donc aller se réfugier tranquillement dans le registre SSPSR. Une interruption survient et va remplir le registre SSPBUF (rien d’anormal niveau fonctionnement interne), Si vous regardez attentivement le Diagramme bloc vous avez “Read” et “Write” Read pour la lecture sur le BUS interne et Write pour l’ecriture sur le BUS interne c’est en fonction des états de “SSPSTAT.R_W”(ce bit agit seul et c’est lui qui vérifie les différents état du bit0 (bit de poid faible)) soit 1 pour la lecture dans ce cas il faut préparer le SSPBUF pour que celui-ci soit lu par le maître ou “SSPSTAT.R_W” qui va être à l’état 0 pour l’ecriture et dans ce cas nous affichons les données sur le PORTD envoyées par le maître . Je vous laisse lire à tête reposer le programme MikroC afin que vous compreniez bien le fonction en mode esclave.


Ce qu’il faut comprendre en I2C, c’est le bit de poids faible bit0 qui mène la danse. Ensuite l’adressage de PIC ne dois pas se terminer avec un bit0 = 1 mais un bit0 = 0. Ainsi, seul le maître décidera d’attribuer un “1” ou “0” logique pour faire les transmissions soit en lecture soit en écriture.

Test sur platine EasyPicV7


Non pas cette fois-ci…

Historiques


09/01/2021
-1er mise à disposition

1 2 3 24