jeudi 28 juin 2012

MCU et communication par port série

Dans ma rubrique précédente j'ai décris un convertisseur de tension pour établir une communication via un port COM entre le PC et un MCU installé en développement sur la platine sans soudure. Ajourd'hui j'ai écris un programme pour tester l'utilisation de ce convertisseur. J'ai monté sur la platine un MCU PIC10F202 et utiliser GP2 comme sortie pour contrôler une DEL et GP1 est la sortie de communication avec le port COM du PC tandis que GP3 reçoit du PC.

Code source MPASM

;programme pour tester le convertisseur de tension
;pour port série
; Une DEL est branchée sur GP2 et son intensité est contrôlée par PWM
; la DEL est contrôlée par l'ordinateur via un port série
; lorsque le MCU est réinitialisé il envoie le message "PRET" à l'ordinateur
; les touches suivantes servent à contrôler la DEL
; 'o' allume la DEL à pleine intensité
; 'f' éteint la DEL
; 'u' augmente l'intensitée de la DEL
; 'd' diminue l'intensité de la DEL

include
__config _WDTE_OFF & _MCLRE_OFF & _CP_OFF

;constantes

OPTION_INI EQU B'00000001' ; configuration du registre OPTION pour que le pré-scaler soit attribué au TIMER0 avec un diviseur 1:4
TRIS_INI EQU 0x9 ; confirguration TRIS -> GP0,GP3 entrées, GP1 et GP2 sorties

BAUD_RATE EQU .9600
BIT_DLY EQU .1000000/BAUD_RATE ; microsecondes par bit à 9600BAUD
F_UART_RX_ERR EQU 0 ; indicateur erreur réception UART
F_LED_FULL EQU 1 ; indicateur LED à intensité maximale
F_LED_OFF EQU 2 ; indidcateur LED éteint

;macros pré-processeur
#define UART_OUT GPIO, GP1
#define UART_INP GPIO, GP3
#define LED GPIO, GP2


;variables

cblock 8
flags : 1 ; indicateurs booléins
uart_bit_cnt : 1 ; compteur de bit pour réception et transmission UART
uart_byte : 1 ; octet à transmettre
msg_ptr : 1 ; pointeur de caractères pour l'envoie des chaines de caratères
led_pwm : 1 ; compteur de modulation à largeur d'impulsion pour contrôler l'intensité de la DEL
led_level : 1 ; niveau d'intensité de la DEL
endc

org 0
goto init

;;;;;;; uart_send ;;;;;;;;;;;;;
;; envoie d'un octet vers le port COM du PC
;;
uart_send
bcf UART_OUT ; start bit
movlw ~((BIT_DLY/4)-2)
movwf TMR0
movlw .9
movwf uart_bit_cnt
movfw TMR0 ; délais start bit
skpz
goto $-2
uart_send_bit_loop
movlw ~((BIT_DLY/4)-2)
movwf TMR0
decfsz uart_bit_cnt,F
goto send_next_bit
bsf UART_OUT ; stop bit
movfw TMR0
skpz
goto $-2 ; boucle délais stop bit
return
send_next_bit
rrf uart_byte,F
skpc
goto send_0
bsf UART_OUT
goto uart_send_wait_dly
send_0
bcf UART_OUT
uart_send_wait_dly
movfw TMR0
skpz
goto $-2
goto uart_send_bit_loop


;;;;;;;; uart_receive ;;;;;;;;;;;;;;
; réception d'un octet envoyé par le PC
; via un port COM.
uart_receive
movlw ~(BIT_DLY/4)
movwf TMR0
movfw TMR0 ; délais du start bit
skpz
goto $-2
bcf flags, F_UART_RX_ERR
movlw .8
movwf uart_bit_cnt
uart_rx_bit_loop
movf uart_bit_cnt
skpnz
goto uart_rx_stop_bit
movlw ~(BIT_DLY/4)
movwf TMR0
bsf STATUS, C
btfss UART_INP
bcf STATUS, C
rrf uart_byte, F
movfw TMR0 ; délais de bit
skpz
goto $-2
decf uart_bit_cnt,F
goto uart_rx_bit_loop
uart_rx_stop_bit
btfsc UART_INP
return
;framing error
bsf flags, F_UART_RX_ERR
return

message_pret
addwf PCL,F
dt "PRET",0xD,0

message_erreur
addwf PCL, F
dt "ERREUR",0xD,0

;;;;; prompt_ready ;;;;;;;;;;;
;;; envoie du message pret
prompt_ready
clrf msg_ptr
msg_loop
movfw msg_ptr
call message_pret
xorlw 0
skpnz
return
movwf uart_byte
call uart_send
incf msg_ptr,F
goto msg_loop

;;;;;; send_error_msg ;;;;;;;;;;;;;;;
;; signale une erreur lors de la réception
;; d'un octet par uart_receive
send_error_msg
clrf msg_ptr
msg_err_loop
call message_erreur
xorlw 0
skpnz
return
movwf uart_byte
call uart_send
incf msg_ptr,F
goto msg_err_loop

;;;;;;; led_control ;;;;;;;;;;;;;;;;;;;;;
;; contrôle l'intensitée de la DEL
led_control
incf led_pwm, F
movfw led_level
subwf led_pwm, W
skpnc
goto lc_led_off
bsf LED
return
lc_led_off
bcf LED
return

;;;;;; prodécude d'initialisation ;;;;;;;;;;;;;;;;
init
movwf OSCCAL
movlw OPTION_INI
option
movlw TRIS_INI
tris GPIO
clrf flags
bcf LED ; led EDIT
bsf flags, F_LED_OFF
bsf UART_OUT ; cette sortie doit-être au niveau 1 lorsqu'il n'y a pas de transmission
call prompt_ready
clrf led_pwm
clrf led_level

;;;;;;;;;;;; routine principale ;;;;;;;;;;;;;;;;;;;;
main
btfsc UART_INP
goto main02
call uart_receive
btfsc flags, F_UART_RX_ERR
goto send_nack
movlw 'u'
xorwf uart_byte,W
skpnz
goto increase_led_level
movlw 'd'
xorwf uart_byte,W
skpnz
goto decrease_led_level
movlw 'f'
xorwf uart_byte,W
skpnz
goto led_max_level
movlw 'o'
xorwf uart_byte,W
skpz
goto main02
led_off
bsf flags, F_LED_OFF
bcf flags, F_LED_FULL
bcf LED
clrf led_level
movlw 'O'
movwf uart_byte
call uart_send
goto main
led_max_level
bsf LED
movlw 0xFF
movwf led_level
bsf flags, F_LED_FULL
bcf flags, F_LED_OFF
movlw 'F'
movwf uart_byte
call uart_send
goto main02
increase_led_level
bcf flags, F_LED_OFF
movlw 0xFF
xorwf led_level,W
skpnz
goto led_max_level
incf led_level,F
swapf led_level,W
andlw 0xF
addwf led_level,F ; linéarisation du contrôle d'intensité
skpnc
goto led_max_level
goto main02
decrease_led_level
bcf flags, F_LED_FULL
movf led_level,F
skpnz
goto led_off
decf led_level, F
swapf led_level,W
andlw 0xF
skpz
subwf led_level,F ; linéarisation du contrôle d'intensité
main02
btfsc flags, F_LED_FULL
goto main
btfsc flags, F_LED_OFF
goto main
call led_control
goto main
send_nack
call send_error_msg
goto main

end

description du code

Au démarrage du MCU la procédure d'initialisation est exécutée et lorsqu'elle est complétée le message 'PRET' est envoyée à l'ordinateur. Ensuite le programme entre dans la boucle principale.

La boucle principale vérifie l'état de GP3 et si la valeur est à zéro c'est que le PC transmet un start bit au MCU. Dans ce cas la procédure uart_receive est appellée et ensuite le caractère reçu est interprété dans la procédure principale. Si le caratère reçu ne fait pas partie des commandes il est simplement ignoré. Lorsqu'il n'y a pas de réception la procédure principale appelle en boucle la routine led_control qui ajuste l'intensitée de la DEL par modulation PWM.

La modulation PWM fonctionne de la façon suivante. la variable led_pwm est un compteur qui est incrémenté à chaque appelle de la routine et la valeur de cette variable est comparée avec led_level. Lorsque led_pwm est plus grand ou égal à led_level la DEL est éteinte autrement elle est allumée. si on regarde le signal à la sortie GP2 avec un oscilloscope on voie un onde carré dont la durée de la portion HAUTE varie en fonction de l'intensité. A intensité faible cette portion est étroite et à intensité élevée elle est large. En fait la DEL clignote à une fréquence trop rapide pour que ce soit perceptible à l'oeil. On ne perçoit qu'une variation d'intensité.

Notez la présence des indicateurs booléens F_LED_FULL et F_LED_OFF. Lorsque l'un ou l'autre de ces indicateurs est actif il n'est pas nécessaire d'appeller la routine led_control.

Si vous étudiez les portions de code increase_led_level et decrease_led_level vous constaterez que l'incrémentation n'est pas une constante mais une fraction de la valeur de la variable led_level. Ceci a pour but de linéariser la fonction d'intensité. Si on utilisait une constante l'effet sur l'intensité serait beaucoup plus prononcé à faible intensité qu'à haute intensité. l'incrément est 1/16 de la valeur led_level avec un planché de 1. la division par 16 est obtenu seulement avec 2 instructions.

swapf led_level,W
andlw 0xF

La procédure uart_send est utilisée pour envoyer l'octet qui est dans la variable uart_byte vers la sortie GP2 qui est reliée à l'adapteur RS-232.
la variable uart_bit_cnt est utilisée pour compter les bits à envoyer. Le TMR0 du MCU sert à contrôler le délais de bit. à 9600BAUD la durée d'un bit est d'environ 104 micro-secondes. Cette valeur est critique et j'ai du l'ajustée en vérifiant le signal avec un oscilloscope1. Le pré-scaler du timer étant réglé à 1:4 je divise 104/4. Puisque le timer est incrémenté plutôt que décrémenté il faut inverser la valeur avec l'opérateur '~'. Mais avec ce compte la durée de bit était de 113usec et le PC ne reconnaissait pas les octets qu'ils recevait du MCU. J'ai donc soustrait 2 comptes ce qui correspond à 8usec. Avec cette valeur ça fonctionne très bien.

capture d'écran de la session terminal (windows 7)


La prodécure uart_receive utilise les mêmes variables que uart_send ainsi que le timer0 de la même façon. La communication est donc semi-duplex. C'est à dire que le MCU ne peut recevoir et envoyer en même temps.

suggestion de projet

Serait-il possible de modifier ce programme pour contrôler une servo-moteur à partir de l'ordinateur plutôt qu'une DEL. Habituellement les servo-moteurs fonctionnent avec une impulsion de largeur variable qui doit se répéter à toutes les 20 msec. La largeur de l'impulsion détermine la position du servo. Tel que je conçois le problème, il faut que la vérification de GP3 soit intégrée à l'intérieur du délais qui contrôle le servo-moteur pour ne pas manquer l'envoie des commandes par le PC. l'intervalle maximale entre chaque vérification de GP3 doit-être inférieur à 52 usec.

Cette modification n'est pas aussi simple qu'il peut sembler à prime abord. La réception ou la transmission d'un octet dure plus de 1 msec hors même pendant la réception ou la transmission d'un octet il faut respecter la durée et la fréquence des impulsions envoyées au servo si on veut qu'il conserve sa position. Est-ce possible avec un MCU comme le PIC10F202 qui a un cycle d'instruction de 1 usec et un seul TIMER?
Il s'agit d'un problème multi-tâche en temps réel.



NOTES:
1)Un oscilloscope est un instrument coûteux et cependant quasiment indispensable pour faire ce genre de travail. Seeed studio cependant vend des oscilloscopes numérique de poche très peut coûteux. Ainsi le DSO nano coûte 89US$. Pour des signaux de moins de 500Khz il peut-être très utile. Personnellement il m'a été indispensable dans le développement de mes projets PIC. Seeed Studio vend aussi le DSO quad pour 199US$. Ces 2 appareils sont aussi disponible chez Roboshop. Le quad a une fréquence d'échantillonage de 72Ms/sec. C'est très bien pour un appareil de ce prix.

dimanche 24 juin 2012

adapteur niveaux RS-232 à TTL

Voici un circuit simple qui peut-être très utile sur la plaquette de prototypage sans soudure lorsqu'on veut faire communiquer un MCU avec un ordinateur via un port RS-232.




Il s'agit d'une copie presque conforme du circuit qui est sur la plaquette de développement PIC-PG4 vendu par Olimex.

Si vous utilisez ce circuit il est important de comprendre que les niveaux logiques TTL côté MCU sont inversés par rapport au standard RS-232. C'est à dire que pour le standard RS-232 un 1 logique est représenté par une tension négative qui doit se maintentir entre -3 et -12 volt et un 0 logique par une tension positive entre +3 et +12volt. En réception le transistor Q2 convertis les niveaux de tension et inverse la polarité du signal, de sorte que le start bit est signalé par un niveau de tension nul et le stop bit pas un niveau TTL positif. En transmission c'est le transistor Q1 qui inverse et adapte les niveaux de tension. Notez l'astuce pour obtenir un voltage négatif nécessaire à la transmission RS-232. Normalement lorsque le PC ne transmet pas la broche 3 du connecteur RS-232 se tient à un niveau négatif et charge le condensateur C1 au travers de la diode D1. C'est ce voltage négatif qui est utilisé pour la transmission. Même si le PC transmet il y aura quand même un voltage négatif, ne serait-ce que le stop bit qui rechargera le condensateur C1. Du moment que le voltage à la jonction D1 et C1 demeure inférieur à -3 volts le circuit est fonctionnel.

Ce circuit peut-être monté sur une plaquette de prototypage RB-Spa-215 vendu chez Robotshop.






samedi 23 juin 2012

pixdel version 1.0

J'ai complété la première version du logiciel pour le projet pixdel et fait le montage d'un premier prototype.


Si vous faites le montage et que vous voulez tester le pixdel à partir de windows, le convertisseur de tension suivant sera nécessaire entre la sortie RS-232 de l'ordinateur et le pixdel.



Le programme terminal qui vient avec windows 7 est parfais pour effectuer ces tests car il permet d'envoyer des séquences en hexadécimal. Les commandes pixdel sont d'une longueur fixe de 7 octets. Pour les détails voir le commentaire au début du fichier pixdel.asm

Conclusion

Les résultats de cette expérience ne sont pas vraiment à la hauteur de ce que j'espérais. Au départ je voulais que la tâche PWM qui contrôle la DEL RGB et la tâche qui reçoit les commandes soient multiplexées de sortent que la réception d'une commande n'affecte pas l'exécution de la tâche PWM. Mais ce n'est pas possible. Il aurait fallut que je diminue la vitesse de transmission des commandes qui est de 9600 BAUD actuellement ou que je ralentisse la fréquence de fonctionnement de la tâche PWM ce qui aurait créé un scintillement de la DEL. Un MCU tournant à 8Mhz plutôt qu'à 4Mhz aurait permis de faire le multiplexage en question mais aurait fait grimpé le coût.
La conséquence est que pendant la réception et le traitement d'une commande la tâche PWM est suspendue pendant quelques milisecondes, l'effet est perceptible à l'oeil.

Il existe sur le marché des DEL dont l'intensité varie continuellement dans le but d'imiter la flamme d'une chandelle. Cette variation d'intensité est assurée par un microcontroleur qui est incorporé dans la capsule de plastique de la DEL. Ce qui serait intéressait est que le concept de pixdel soit repris par un industriel qui fabriquerais les pixdels sur le même principe. Le chip du MCU serait incorporé dans la capsule de la DEL RGB et il y aurait 3 fils qui sortiraient de la capsule, 2 pour l'alimentation et 1 pour la réception des commandes. Fabriqué à grande échelle, le coût de revient d'un pixdel serait suffisamment bas pour en faire un produit intéressant. Un tel pixdel simplifirait la fabrication des cubes de DEL et autre dispositifs du genre.

J'imagines un concept décoratif de mur lumineux constitué de tuiles en acrylique blanche translucide. a l'intérieur de chaque tuile il y aurait un micro-controleur et une DEL RGB de 1 watt avec le circuit de puissance nécessaire. On fixerais des rails conducteurs sur le mur sur lesquels les tuiles viendrait s'enfichées. Les rails contiendrait les conducteurs d'alimentation et le bus de donnée et serait relié soit à un controleur ou un ordinateur sur lequel tournerait un logiciel d'effets visuels qui enverrait les commandes aux tuiles. Le concept est extensible, le nombre de tuiles étant limité seulement par la taille de l'identifiant unique de chaque tuile. Avec un identifiant de 16 bits on aurait la possibilitée d'installer plus de 65500 tuiles sur le même bus de données.



samedi 9 juin 2012

PIC10F202 + DEL RGB (projet pixdel)

Cette expérience est la première étape d'un projet qui me trotte dans la tête. Dans cette expérience il s'agit d'une DEL RGB contrôlée par un PIC10F202. La DEL change continuellement de couleur mais la transition d'une couleur à l'autre est progressive ce qui produit un effet intéressant. Le fichier source est ici. Le circuit est des plus simple.

pixdel

L'idée que j'ai en tête est la suivante. Étant donné le faible coùt des PIC10F202, environ 0,42CAN$ en quantité de 25 ou plus, je me dis qu'il est abordable d'associer un PIC10F202 à une DEL RGB au lieu d'utiliser 1 MCU pour plusieurs DEL. J'ai baptisé ce projet PIXDEL1. Donc à chaque DEL il y a un contrôleur associé et chaque contrôleur a un identifiant unique. Chaque PIXDEL forme un noeud sur un bus commun. Un contrôleur principal ou un ordinateur envoie des commandes à chaque pixdel en utilisant le protocole RS-2322. GP0,GP1 et GP2 contrôle chacun une des électrodes RGB sur la DEL et GP3 sert d'entrée pour la réception des commandes RS-232 transmisent sur la ligne de données. Comme chaque pixdel possède un identifiant unique il ne répond qu'aux commandes qui lui sont adressés ou au commandes de diffusion.


Physiquement les pixels peuvent-être organisés en grille sur un plan ou en cube, peut importe la topologie physique, il n'y aura que 3 conducteurs qui relira tous les pixdel ensemble. Je prévois un Id de 8 bits, ce qui autorise jusqu'à 253 identifiants uniques sur le bus de donnée. Le id 0 est réservé pour les commandes de groupes et le 255 pour la diffusion.

Cette première étape du projet m'a permis de confirmer qu'il est possible de faire de la modulation PWM en software sur un PIC10F202 sans effet de scintillement sur la DEL. Dans le code montré en exemple ici la fréquence PWM est d'environ 108Hz. A partir 65Hz je ne perçois aucun scintillement. Même si j'arrive à faire une boucle à 108Hz, ça ne veut pas dire que la fréquence au final du projet sera aussi élevée car il faut que le contrôleur prenne le temps de lire et traiter les commandes reçues sur GP3. Quoiqu'il en soit la fréquence minimale pour éviter tout scintillement doit-être de 65Hz.



NOTES:

1) PIXDEL contraction de pixel, point lumineux sur un écran et DEL. Car enfin l'idée est de fabriquer une grille ou un cube avec les pixdel.

2) Il faudra que le niveau de tension RS-232 soit convertie en niveau TTL. De plus si on imagine un bus sur lequel il y a le maximum de pixdels la capacitance d'entrée des GP3 sont toutes en parralèles ce qui donne 50pF*253=13nF.