Nouvelles:

Bienvenue au Forum de discussion Électro-Bidouilleur! Inscrivez-vous pour participer aux discussions!: 
https://forum.bidouilleur.ca/index.php?action=signup Les demandes d'inscription sont révisées quotidiennement.

Menu principal

Conversion Analogique-Numérique avec un PIC

Démarré par olibou, Décembre 15, 2019, 09:19:35 PM

« précédent - suivant »

olibou

Bonjour,

Je débute en électronique et j'essaie de comprendre le fonctionnement des microcontrôleurs PIC, mais je bloque depuis plusieurs semaines sur la conversion analogique/numérique. Pouvez-vous me mettre sur la voie?
Je travaille avec un PIC 16F676 (data sheet : http://ww1.microchip.com/downloads/en/DeviceDoc/40039F.pdf) sous MPLAB (en C).
Pour tester la conversion de signaux analogique en numérique, je voudrais simplement allumer deux LED en fonction de la valeur lue par le convertisseur... mais rien ne se passe : apparemment la conversion n'a pas lieu, mais je ne comprend pas pourquoi.
Voici mon code, pouvez-vous m'aider s'il vous plaît?



#include <xc.h>
#define _XTAL_FREQ 4000000


void main(void) {
//Selection canal Analog.  : AN0
ADCON0bits.CHS0=0;
ADCON0bits.CHS1=0;
ADCON0bits.CHS2=0;

// Fréq opération= OSC/16
ADCON1bits.ADCS0=1;
ADCON1bits.ADCS1=0;
ADCON1bits.ADCS2=1;


ADCON0bits.VCFG=0;//Vref = VDD

//Format Resultat Convetion
ADCON0bits.ADFM=1;//justif. droite

//Selection A/D pins
ANSELbits.ANS0=1;//PIN 0 analog.
ANSELbits.ANS1=0;//PIN 1 digital

// selction I/O pins
TRISAbits.TRISA0=1;//PIN 0 entée
TRISAbits.TRISA1=0;//PIN 1 sortie
TRISAbits.TRISA2=0;//PIN 2 sortie


PORTAbits.RA1=0;//LED pin 1 eteinte
PORTAbits.RA2=0;//LED pin 2 eteinte


unsigned int result=0;



while(1){
    ADCON0bits.ADON=1;//initialiser Conv Analog-Num.
    if (ADCON0bits.GO_DONE==0){
        ADCON0bits.GO_DONE=1;//démarrer la conversion
    }
   
    while(ADCON0bits.GO_DONE=1){}// attendre que la conversion soit terminée
    result=ADRESL; //resultat sur 8 bits
       
   
   //Allumer LED 1 ou 2 en fonction du resultat
        if (result>100){
            PORTAbits.RA1=1;
            PORTAbits.RA2=0;
        }
        else if (result<100){
            PORTAbits.RA2=1;
            PORTAbits.RA1=0;
        }
   
}

    return;
}



Curiosus

Bonjour olibou,

Je pratique pas  MPLAB (en C), j'écris tout mes programmes an ASM, si tu veux je peux l'écrire an ASM, et t'expliquer la façon de faire
pour que tu le réécrive en C

A+


olibou

Bonjour Curiosus et merci pour ta réponse.

Allons-y pour l'assembleur!
J'ai l'impression que la plupart des bidouilleurs de PIC programment en assembleur, ce sera peut être l'occasion de m'y mettre!
Merci ;D

Yffig

Bonjour Olibou,
Non, une majorité de bidouilleurs ne codent pas les PICs en ASM.
Si tu utilises un petit 8 bits genre 16F84 très utilisé autrefois , ou même un 12F683 qui possède un ADC 10 bits (sujet évoqué récemment sur le forum), vu le "peu" de mémoire programme dont tu disposes, l'ASM est une obligation. C'est aussi le cas de ton 16F676 (1K de flash...)  L'ASM est aussi incontournable si tu veux réaliser un petit fréquencemètre. L'ASM te permet de maîtriser complètement tes durées d'exécution.
L'ASM t'oblige cependant à  te "farcir" la quasi totalité d'au moins quelques chapitres de  la datasheet mais après tu sais ce que tu fais...en particulier la manière de configurer tous les bits de tous les registres...et y'en a un paquet !
Culturellement l'ASM , même celui des PICs (cf la détestation que Cyrob a vis à  vis de son jeu d'instructions), ça ne me gêne pas, vu que j'en ai pissé des lignes de code ASM en Z80 fin des 70's , début des 80's.
Par contre dès que tu veux réaliser quelques calculs avec un minimum de précision, alors la bibliothèque de calcul en float du C est indispensable et là  tu auras besoin du C et donc d'une mémoire programme plus conséquente, et donc d'un PIC mieux doté en flash. idem si tu veux utiliser les outils I²C, il vaut mieux utiliser les bibliothèques du C.
Pour ma part, j'utilise le Compilateur C de MikroElektronika qui est payant mais tu peux télécharger le complilateur mikroC gratos (limité à  1K de mémoire programme) et regarder la bibliothèque ADC. Il te donnera des exemples en C de configuration des registres que tu pourras convertir en MPLAB pour tes besoins propres.
https://www.mikroe.com/mikroc-pic
Tu peux aussi télécharger les tutos en ASM de BigOnOff (le tuto 16F87X en particulier qui contient les ADC) vu que c'est en français et particulièrement bien détaillé et avec de nombreux exemples
https://www.abcelectronique.com/bigonoff/organisation.php?par=09f52
Bonne journée.
Yffig

olibou

Bonjour Yffig,

Le PIC que j'ai choisi comporte effectivement peu de mémoire flash mais je l'utilise essentiellement à  des fins "pédagogiques" pour tenter de comprendre comment programmer un PIC.
Pour mes "vrai" projets, je tiendrais compte de ton conseil et choisirais un PIC mieux doté en mémoire.
je vais tester MicroC pour voir si j'y trouve une solution à  mon énigme...

Merci pour ta réponse!




Curiosus

#5
Bonjour olibou, et tout le forum,

J'ai fini le programme, je le teste et le mets vers les 1 heure 30  du matin, en France métropolitain, (avec un schéma) chose que tu aurais du faire.

Je vais pas te dire qu'un langage est mieux qu'un autre, c'est à  toi de voir, personnellement je suis pas d'accord de payer une licence pour programmer.

Je me suis constitué une bibliothèque que j'ai écrit en ASM, ce qui me permets de la modifier à  tous moment.

Revenons à  ton programme, J'ai mis 3 led rouge, vert, bleu, avec un potentiomètre, si tu tournes le potentiomètres
elles s’allumeront en fonction de la position de celui-ci..

Si tu vois une autre idée ?

A+


   


olibou

Bonjour Curiosus,

C'est vraiment super merci!
C'est vrai que j'aurais pu poster un schéma... :-[ mais ta proposition correspond tout à  fait à  ce que je voudrais faire. J'espère que quand j'aurais compris comment fonctionne un convertisseur A/N, j'arriverais à  utiliser différents capteurs... mais une étape à  la fois.
J'ai commencé à  lire les ressources de BigOnOff dont Yffig à  posté le lien : c'est vraiment très bien fait. J'y ai déjà  trouvé plein d'éclaircissements et je n'en suis qu'au début ;D

En tout cas merci

Curiosus

#7
Bonsoir,

Je ne vais pas faire de long discourt vu l'heure tardive, voila j'ai fini, testé, sa fonctionne, après on pourras le réécrire en  MPLAB (en C)

De plus pas besoin de quartz, car ce pic possède un oscillateur interne de 4 Mhz, il suffit juste de l'activer, chose que j'ai faite.

Le code pour les intéressés



list      p=16f676           ; list directive to define processor
#include <p16F676.inc>        ; processor specific variable definitions

errorlevel  -302              ; suppress message 302 from list file

__CONFIG   _CP_OFF & _CPD_OFF & _BODEN_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT

;*************************************** assignations *****************************************

#DEFINE     led1         PORTC,0          ; led rouge
#DEFINE     led2         PORTC,1          ; led bleu
#DEFINE     led3         PORTC,2          ; led vert

;**********************************************************************************************

CBLOCK H'20'

      loop1         :1
      loop2         :1
      loop3         :1
         
      res8          :1     
      res16         :1     
      res24         :1 
ENDC

;***********************************************************************************************

    ORG H'0'                              ; reset au démarrage
      goto debut                          ;

    ORG H'4'                              ; lieux des interruptions
      retfie

;***********************************************************************************************
debut
  call 0x3FF                          ;

;***********************************************************************************************

  BANKSEL OSCCAL                      ; passer en bank 1

;***********************************************************************************************

      movwf   OSCCAL                      ;

;************************** configuration du registre ANSEL en bank 1 **************************

      movlw B'00000001'                   ; mode numérique  : 13(RA0),
      movwf ANSEL                         ; mode analogique : 12(RA1), 11(RA2), 10(RC0),9(RC1),8(RC2), 7(RC3)

;************************ configuration du registre ADCON1 en bank 1 **************************

      movlw B'00100000'                   ; b6 = ADCS2 choisir la fréquence de fonctionnement du convertisseur
      movwf ADCON1                        ; b5 = ADCS1 en fonction de la fréquence d'horloge du PIC
                                          ; b4 = ADCS0
                                          ; 
;************************ configuration du registre OPTION_REG en bank 1 ***********************

      movlw B'10000000'                   ;  résistances hors service sur PORTA
      movwf OPTION_REG                    ;

;******************** configuration des registres TRISA & TRISC en bank 1 **********************

      movlw B'00000001'                   ; configure les pattes de TRISA
      movwf TRISA                         ; 13(RA0),12(RA1),11(RA2),4(RA3),3(RA4),2(RA5)

      movlw B'00000000'                   ; configure les pattes de TRISC
      movwf TRISC                         ; 10(RC0), 9(RC1), 8(RC2), 7(RC3), 6(RC4), 5(RC5)
                                                 
                                          ; alimentation sous 5 volts patte 14 mettre au moins
     
;***********************************************************************************************

  BANKSEL ADCON0                      ; passer en bank 0

;***********************************************************************************************
     
                                          ; b7 = ADFM détermine si le résultat de la conversion
                                          ; sera justifié à  droite = 1 ou à  gauche = 0 
                                          ; b6 = VCFG 1 = VREF pin, 0 = VDD
      movlw B'00000000'                   ; 
      movwf ADCON0                        ;
     
;************************* configuration du registre CMCON en bank 0 ************************** 

      movlw B'00000111'
      movwf CMCON                         ; éteindre le comparateur                   

;*********************** configuration du registre INTCON en bank 0,1 **************************

      clrf INTCON                         ; désactivé les interruptions

;***********************************************************************************************
reset
      clrf PORTA
      clrf PORTC

;***********************************************************************************************     

; Pour avoir un état bas, milieu, haut, on divise par 3 (255 /3) = 85   

; état bas de 0 à  85, état milieu de 86 à  170, état haut 171 à  255

;********************************* "sélection du canal(RA0)" ***********************************

      call canal_an0                      ;

;***********************************************************************************************     
control_1     
      call numériser                      ; lancer la conversion

      movf res8,W
      sublw D'85'

      btfss STATUS,C
      goto control_2

      bsf led1
      bcf led2       
      bcf led3

      goto control_fin

control_2 
      movf res8,W
      sublw D'170'

      btfss STATUS,C
      goto control_3

      bcf led1
      bsf led2       
      bcf led3

      goto control_fin     

control_3
      bcf led1
      bcf led2       
      bsf led3

control_fin
      call _300ms                         ; petite pause

      goto control_1

;***********************************************************************************************
;**************************** "canaux des convertiseurs numériques" *****************************
;***********************************************************************************************
canal_an0
     bcf ADCON0,CHS0                      ; 000 (RA0)
     bcf ADCON0,CHS1
     bcf ADCON0,CHS2

     return

;***********************************************************************************************
canal_an1
     bsf ADCON0,CHS0                     ; 001 (RA1)
     bcf ADCON0,CHS1
     bcf ADCON0,CHS2

     return
;***********************************************************************************************
canal_an2
     bcf ADCON0,CHS0                      ; 010 (RA2)
     bsf ADCON0,CHS1
     bcf ADCON0,CHS2

     return

;***********************************************************************************************
canal_an3
     bsf ADCON0,CHS0                      ; 011 (RA3)
     bsf ADCON0,CHS1
     bcf ADCON0,CHS2

     return

;***********************************************************************************************
canal_an4
     bcf ADCON0,CHS0                      ; 100 (RC0)
     bcf ADCON0,CHS1
     bsf ADCON0,CHS2

     return

;***********************************************************************************************
canal_an5
     bsf ADCON0,CHS0                      ; 101 (RC1)
     bcf ADCON0,CHS1
     bsf ADCON0,CHS2

     return
;***********************************************************************************************
canal_an6
     bcf ADCON0,CHS0                      ; 110 (RC2)
     bsf ADCON0,CHS1
     bsf ADCON0,CHS2

     return

;***********************************************************************************************
canal_an7
     bsf ADCON0,CHS0                      ; 111 (RC3)
     bsf ADCON0,CHS1
     bsf ADCON0,CHS2

     return

;***********************************************************************************************
numériser
      bsf ADCON0,ADON                     ; on lance l’acquisition (charge du condensateur)
      nop             
      nop                                 ; tempo pour charge du condensateur
      nop
      bsf ADCON0,GO                       ; lancer la conversion A/D

      btfsc ADCON0,GO                     ; si le bit GO est à  1 on va à  la ligne 1       
      goto $-D'1'                         ; conversion n'est pas terminer     
      bcf ADCON0,ADON                     ; fin de conversion, éteindre convertisseur

      movf ADRESH,W                       ; sauver la valeur qui vient d'être numérisé
      movwf res8                          ; dans la variable "res8"
                                           
      return

;***********************************************************************************************
_300ms
      movlw D'51'
      movwf loop1

      movlw D'12'
      movwf loop2

      movlw D'4'
      movwf loop3

      decfsz loop1,F
      goto $-D'1'
      decfsz loop2,F
      goto $-D'3'
      decfsz loop3,F
      goto $-D'5'

      return

;***********************************************************************************************

  End



Fichiers joints : fichier schéma, fichier code, fichier code compilé

A+


olibou

Bonjour,

Super, ça fonctionne parfaitement!
Par contre l'assembleur, pour moi, c'est de l'hébreu!
Il ne me reste plus qu'a apprendre l'assembleur pour essayer de trouver où est le problème dans mon code en C...
Je regarde ça à  tête reposée ce soir...

Bonne journée!
Olibou

Curiosus

Bonjour olibou, et tout le forum,

Tu fais ce que tu veux, si l'assembleur ne te convient pas, ne prends pas, moi j'aime bien l'assembleur,  mais c'est moi.

Il est conseillé d'apprendre un minimum d'assembleur, ça rends service quand une fonction n'existe pas, ou si tu veux de la vitesse pour ton code.

Monsieur Bigonoff et comme Électro-Bidouilleur ils ont fait quelle que chose de super, retransmettre leurs connaissances.

Merci mille fois à  eux.

Je viens de regarder ton code en C, et je m'aperçois que tu n'as pas configuré CMCON,

1)ça veux dire que tes comparateur ne sont pas éteint

2) ADFM et à  1 alors qu'il est plus simple de le mettre à  0, et récupérer le résultat dans ADRESH

Bon à  suivre ......

A+

olibou

#10
Bonjour à  tous,

Je n'ai absolument rien contre l'assembleur, et je te suis également très reconnaissant pour le temps que tu consacre à  mes questions :D
c'est juste que je suis au tout début de mon apprentissage (autant en électronique qu'en programmation) et j'ai déjà  passé pas mal de temps à  déchiffrer la datasheet et à  comprendre comment coder ces quelques lignes en C... qui ne fonctionnent pas d'ailleurs!
Mais j'ai bien compris l’intérêt de l'assembleur et je vais essayer de m'y mettre, mais au rythme ou j'apprends, ça va peut être prendre quelques semaines!
La première étape va être de creuser la question du comparateur (dont je ne connais pas la fonction :-[) pour voir si c'est l'origine de mon problème.
Peut être que si j'utilise une autre broche en entrée cela va contourner le problème?
Je vais faire quelques essais... et je vous tiens au courant! (5A maxi ;D)

A+
Olibou

Curiosus

#11
Bonsoir,

CitationÉcrit par olibou : Peut être que si j'utilise une autre broche en entrée cela va contourner le problème ?

Toutes ses pattes peuvent être configuré en convertisseur analogique RA0, RA1, RA2, RC0, RC1, RC2, RC3 (voir mon code en ASM)

D'après le datasheet en page 41, le comparateur agit seulement sur les pattes : RA0, RA1, RA2.

Pour ne pas utiliser le comparateur, il faut mettre CMCON = B'00000111' , ce qui te permet de les utiliser comme convertisseur analogique.

Voir extrait du datasheet ci-dessous

A+

olibou

Bonjour à  tous,

J'ai enfin trouvé ce qui n'allait pas dans mon code : un bête erreur de débutant qui n'a pas relu attentivement son programme :-[
Ça se passe ici :  while(ADCON0bits.GO_DONE=1){}// attendre que la conversion soit terminée
Il fallait écrire "==" et non "=" ( c'est une condition et non une valeur assignée)

J'en ai profité pour faire quelques test :
L'activation -ou non- du comparateur (CMCON) n'a pas d'impact (ça fonctionne dans les deux cas)
La broche 3 (RA3 - MCLR) ne fonctionne pas comme sortie, bien que le MCLR soit désactivé dans les bits de configuration... si quelqu’un à  la clé du mystère, je suis preneur.

L'autre problème qu'il y a dans mon programme, c'est qu'au changement de valeur (lorsque le registre ADRESL atteint la valeur 100), j’éteins la LED allumée pour allumer l'autre à  son tour :
if (result>100){
            PORTAbits.RA1=1;//allume la LED1
            PORTAbits.RA2=0;//éteint la LED 2
Et c'est là  que ça bloque... je ne peux pas donner deux instructions en même temps. Le même problème se pose si j'essaie d'allumer les deux LED en même temps.

par contre, si je ne donne qu'une seule instruction, ça fonctionne :
if (result<100){
PORTAbits.RA1=1;}//allume la LED1

if (result>=100){
PORTAbits.RA1=2;}//allume la LED2

Ce qui est étrange, c'est que quand je change progressivement la valeur ADRESL à  l'aide du potentiomètre (de 0 à  255 par exemple), la LED1 devrait s'allumer en premier, puis la LED 2, mais sans éteindre la LED 1 puisque je n'ai pas donné l'instruction de l'éteindre... hors elle s'éteint!

Notez que ce sont des questions qui n'ont pas une grande importance... ce n'est même pas un vrai projet mais un petit test pour essayer de comprendre, donc, si la réponse vous paraît évidente et simple, n'hésitez pas à  partager, mais ne passez pas des nuits blanches juste pour ça!
En tous cas, merci beaucoup pour vos conseils et pour les liens!

A+,

Olibou

Yffig

Bonsoir,
Y aurait pas une erreur dans ta ligne ? disons au moins dans ce que tu nous soumets, (pas forcément dans ton code... ;) )
"PORTAbits.RA1=2;}//allume la LED2"
Bonne soirée
Yffig

olibou

Bien vu  :-[
C'est parceque je code en trinaire ;D