AVR SPI avec un Arduino UNO

Cet article explique comment utiliser le port SPI présent sur le Atmega328p du Arduino UNO.

Dans cet article, nous utiliserons une boucle pour l’écriture. Cette méthode est simple mais elle limite les performances du microcontrôleur. Je ferai un autre article qui expliquera comment faire cette opération en interruption, ce qui est recommandé afin de libérer le programme principal.

Étape 1 : Initialisation du port SPI

Le port SPI utilise 3 liens pour transmettre et recevoir les données en plus d’un signal pour sélectionner l’esclave visé.

Signal Broche (port) Description
SCLK 13 (PB5) Horloge
MISO 12 (PB4) Données de l’esclave vers le maître
MOSI 11 (PB3) Données du maître vers l’esclave
CS 10 (PB2) Sélection de l’esclave

Les signaux SCLK, MOSI, CS sont donc des sorties pour l’Arduino et le signal MISO est une entrée. La ligne suivante configure les broches requises en sorties.

DDRB |= (1<<2) | (1<<3) | (1<<5) ;

La configuration par défaut correspond à la majorité des situations.  L’horloge principale est divisée par 4 (ce qui donne 4Mhz pour le Arduino UNO), le signal SCLK est à 0 lorsque le SPI est inactif, les données débutent par le MSB. Le registre de contrôle est le SPCR.

La ligne suivante active le SPI et place le SPI en mode maître :

SPCR = (1<<SPE) | (1<<MSTR);

Étape 2 : Utilisation du port SPI 

Après son initialisation, le port SPI est prêt pour envoyer des données. Avant toute communication, il faut sélectionner l’esclave en abaissant le signal CS correspondant. Il suffit ensuite d’écrire dans le registre SPDR l’octet à transmettre. Après chaque écriture, il faut lire le bit d’état SPIF du registre d’état SPSR. Ce dernier devient 1 lorsque la transmission est terminée. Voici une fonction d’écriture qui regroupe ces lignes :

uint8_t spiWrite(uint8_t data)
{
  SPDR = data;
  while(!(SPSR & (1<<SPIF)));
  return SPDR;
}

La fonction retourne le contenu de SPDR, qui contient alors l’octet reçu via le lien MISO simultanément à l’écriture sur MOSI.

Voici un exemple de code qui initialise le port SPI puis envoie 0x40 suivi de 0x05 vers un esclave.

// Dans cet exemple, CS est sur le bit 2 du port B
// Initialisation du SPI
DDRB |= (1<<2) | (1<<3) | (1<<5) ; 
SPCR = (1<<SPE) | (1<<MSTR);
// Transmission vers un esclave
PORTB &= ~(1<<2);   // mise à zéro de CS
spiWrite(0x40);
spiWrite(0x05);
PORTB |= (1<<2);   // mise à un de CS

 


AVR USART avec un Arduino UNO

Cet article explique comment utiliser le lien série asynchrone (lire USART) présent sur le Atmega328p du Arduino UNO.

Dans cet article, nous utiliserons une boucle pour la transmission et la réception des octets. Je ferai un autre article qui expliquera comment faire avec les interruptions, ce qui est recommandé afin de libérer le programme principal.

Étape 1 : Initialisation du USART

Il faut d’abord établir les paramètres du port série. Dans le cas présent, nous allons configurer le port pour une des configurations les plus communes.

Vitesse 9600 bauds
Longueur des données 8 bits
Parité Aucune
Nombre de bit STOP 1

Les explications détaillées des registres utiles du Atmega328p sont disponibles dans la feuille de spécification suivante : ATMEGA328P

Le premier registre que nous allons configurer est en fait deux registres. C’est le UBRR qui est séparé en deux parties UBRR0L et UBRR0H. Ce registre configure la vitesse du port série. La valeur de UBRR dépend donc de l’horloge du microcontrôleur et la vitesse désirée. Dans notre cas, puisque nous utilisons le mode normal du USART. L’équation qui permet de déterminer UBRR est :

Screenshot_7

Où fosc est la fréquence d’opération du CPU et BAUD est la vitesse désirée.

Pour établir cet valeur, il est possible de calculer la valeur et d’utiliser un define ou vous pouvez utiliser une macro qui calculera automatiquement la valeur en fonction des paramètres. Dans le premier cas, avec nos paramètres le résultat serait 103 (le CPU du Arduino utilise un cristal externe de 16MHz). Cependant, je recommande plutôt la seconde manière, plus facile à maintenir. Voici le code de la macro en question :

#define BAUD_PRESCALE(fcpu,br) ((fcpu / 16 / br) - 1)

Les 8 bits les moins significatifs sont dans le registre UBBR0L et les 4 derniers bits dans UBBRoH. Voici la représentation des registres en question:

Screenshot_6

Pour UBRR0H, il faut simplement utiliser un décalage comme ceci :

 UBRR0H = (BAUD_PRESCALE(F_CPU,baudRate) >> 8); 
 UBRR0L = BAUD_PRESCALE(F_CPU,baudRate);

Un autre registre d’intérêt est le UCSR0C, qui permet de configurer le nombre de bits des données, le bit de polarité ainsi que le nombre de bits STOP.

Screenshot_8

UMSEL Détermine le mode du port
00 est le mode asynchrone
UPM Détermine la parité
00 est aucune parité
USBS Détermine le nombre de bit STOP
0 est 1 bit
UCSZ Est la longueur des données
011 est 8bits. (valeur par défaut)
Notez que UCSZ2 est dans un autre registre

Voici donc le code qui permet de configurer le registre selon la configuration désirée :

UCSR0C = (1<<UCSZ00)|(1<<UCSZ01);

Le dernier registre à configurer sera le UCSR0B, qui active les éléments du port série. Il contient également le dernier bit pour la configuration de la longueur des données (UCSZ2).

Screenshot_9

RXCIE Active l’interruption de fin de réception d’une donnée. Pas utilisé dans cet article.
TXCIE Active l’interruption de fin de transmission d’une donnée. Pas utilisé dans cet article.
RXEN Active la réception de donnée
TXEN Active la transmission de donnée

Voici donc le code de la fonction uartInit au complet (F_CPU est un define utilisé par la librairie delay.h. Il vaut 16000000UL dans le cas présent) :

void usartInit(uint32_t baudRate)
{
 UBRR0L = BAUD_PRESCALE(F_CPU,baudRate);
 UBRR0H = (BAUD_PRESCALE(F_CPU,baudRate) >> 8); 
 UCSR0B = ((1<<TXEN0)|(1<<RXEN0);
 UCSR0C = (1<<UCSZ00)|(1<<UCSZ01);
}

Étape 2 : Envoyer une donnée

Envoyer une donnée est simple, il suffit d’écrire dans le registre UDR0 et la transmission débute automatiquement si la transmission est activée dans le registre UCSR0B. Avant d’écrire, il faut cependant s’assurer que le registre est vide, avec le bit UDRE0 du registre UCSR0A. Voici à quoi peut ressembler la fonction de transmission d’une donnée.

void usartSendByte(uint8_t data)
{
 while((UCSR0A & (1<<UDRE0)) == 0);
 UDR0 = data;
}

Étape 3 : Réception d’une donnée

Pour lire une donnée reçue, il suffit de lire le registre UDR0. Cependant, pour s’assurer que le contenu est bien une donnée reçue, il faut vérifier que le bit RXC0 est activé dans le registre UCSR0A. Pour éviter que le programme se bloque, il est préférable d’utiliser une fonction qui valide la présence d’une donnée avant de lancer la lecture.  Voici les deux fonctions recommandées pour cette étape :

bool usartDataAvailable()
{
 return (UCSR0A & (1<<RXC0));
}

uint8_t usartGetRxByte()
{
 // Option pour attendre la réception d'une donnée... risque de blocage
 // while (!usartDataAvailable());
 return UDR0;
}

Finalement, il restera à vérifier que l’on peut recevoir et envoyer des données à l’aide des fonctions. Je vous recommande HTerm  pour vérifier votre programme.

Voici un petit programme qui utilise les fonctions précédentes et valide le fonctionnement. Les fonctions sont mises dans un fichier usart.cpp et les déclarations de fonctions dans usart.h.

#include <avr/io.h>
  #ifndef F_CPU
    #define F_CPU 16000000UL
  #endif
#include <util/delay.h>
#include "usart.h"

void main(void)
{
  usartInit(19200);
  while(1)
  {
    if(usartDataAvailable())
      data = usartGetRxByte();
    usartSendByte(data);
    _delay_ms(1000);
  }
}

 

 


AVR ADC avec un Arduino UNO

Cet article explique comment utiliser le convertisseur analogique à numérique (ADC) présent sur le Atmega328p du Arduino UNO.

Dans cet article, nous utiliserons une boucle pour la lecture d’un canal. Je ferai un autre article qui expliquera comment faire cette lecture en interruption, ce qui est recommandé afin de libérer le programme principal.

Étape 1 : Initialisation du ADC

Dans l’initialisation, nous devons configurer le registre ADCSRA.

Screenshot_2

Le bit ADEN active le ADC alors que les bits ADPS0-ADPS3 sélectionnent la division de l’horloge du système qui établira la vitesse de l’horloge du ADC. Ce qui est important, c’est que l’horloge de l’ADC ne dépasse pas 200KHz lorsque les 10 bits de résolution sont nécessaires. Puisque nous avons une horloge de 16MHz sur le Arduino UNO, nous devons diviser par 80 ou plus. Selon la table 24-5 du datasheet, nous devrons donc utiliser le facteur de division 128.

Screenshot_3

Nous devons également configurer le registre ADMUX qui sélectionne la tension de référence utilisée par l’ADC.

Screenshot_3

Puisque la broche AREF du Arduino UNO n’est pas reliée, nous allons utiliser AVcc comme référence. La table 24-3 montre que le bit REFS0 doit être activé.

Screenshot_2

Voici à quoi le code de la fonction adcInit devrait ressembler :

void adcInit()
{
 ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) |(1<<ADPS0) ;
 ADMUX =  (1<<REFS0);
}

Étape 2 : Sélection du canal

Le Atmega328P utilisé par le Arduino UNO possède 6 canaux sur son ADC. Lorsqu’il n’y a qu’un canal utilisé, vous devez sélectionner le canal une seule fois. Sinon, vous devez sélectionner le bon canal avant chaque conversion. La sélection du canal s’effectue via le registre ADMUX avec les bits MUX3 à MUX0.

Screenshot_4

Voici à quoi la fonction adcSetCh devrait ressembler :

void adcSetCh(uint8_t ch)
{
 ADMUX &= 0xF0; 
 ADMUX |= (ch & 0x0F);
}

Étape 3 : Lecture de l’ADC dans une boucle

Il faut maintenant démarrer l’ADC. Cette opération s’effectue à l’aide du bit ADSC du registre ADCSRA. Lorsque ce bit est mis à 1, une conversion démarre. Le bit retourne à zéro lorsque la conversion est terminée.

Voici à quoi le code de la fonction adcRead devrait ressembler :

uint16_t adcRead(uint8_t ch)
{
 ADCSRA |= (1<<ADSC);
 while (ADCSRA & (1<<ADSC)); 
 return ADCW;
}

Exemple d’utilisation de l’ADC

Dans cet exemple, un potentiomètre est relié au canal 0 de l’ADC selon l’image suivante.

20171012_194317[1]

Le code fait la lecture de la broche aux 100ms puis il affiche le résultat sur le port série.

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
#include "usart.h"
#include "adc.h"

uint16_t adcVal = 0;

int main(void)
{
  char strVal[6] = {0};
  usartInit(19200);
  adcInit();
  while (1) 
  {
    adcVal = adcRead(0);
    itoa(adcVal,strVal,10);
    usartSendStr(strVal);
    usartSendStr("\n\r");
    _delay_ms(100);
  }
}

Et voici le résultat observé, en variant de 0V à 5V le potentiomètre.

Screenshot_5


Utiliser AVRDUDE dans AVR Studio

Dans cet article, vous verrez comment télécharger votre programme dans votre Arduino sans utiliser de programmeur externe.

Une version vidéo de cette article est disponible sur mon canal Youtube à https://youtu.be/HB7oB3Bfg4k.

Les cartes Arduino sont livrées avec ce qu’on appel un bootloader. Il s’agit d’un petit programme qui demeure dans votre microcontrôleur et qui permet de charger le fichier hex via un port de communication. Dans le car du Arduino, il utilise le port série qui est relié au port USB de la carte.

La première étape consiste à ouvrir Arduino IDE et de charger un exemple simple, puis de le télécharger sur la carte. Après le chargement, trouvez dans la liste des commandes celles qui réfère à Avrdude.

etape1

Copiez ensuite la ligne complète dans un éditeur de texte.

Séparez la commande en deux partie.

Partie 1 (Commande)

C:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avrdude

Vous devez ajouter .exe à la fin de cette commande, ce qui vous donnera ceci :

C:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avrdude.exe

Partie 2 (Arguments)

-CC:\Program Files (x86)\Arduino\hardware\tools\avr/etc/avrdude.conf -v -patmega328p -carduino -PCOM47 -b115200 -D -Uflash:w:C:\Users\MARC~1.JUN\AppData\Local\Temp\arduino_build_327788/Blink.ino.hex:i

La première partie des arguments donne le chemin vers le fichier de configuration d’Avrdude. Vous devez ajouter des «  » à cette partie.

Ensuite, il faut modifier l’argument du port comme suit :
-PCOM47 deviendra -P\\.\COM47

Finalement, il faudra remplacer le chemin du fichier hex pour celui de votre projet. Sous AVR Studio, il y a des constantes de path qui simplifient cette tâche. L’argument « $(ProjectDir)Debug\$(TargetName).hex » pointera sur le hex de votre projet à moins que vous aillez changer ce chemin manuellement.

La ligne d’arguments devrait donc ressembler à ceci :

-C »C:\Program Files (x86)\Arduino\hardware\tools\avr/etc/avrdude.conf » -v -patmega328p -carduino -P\\.\COM47 -b115200 -D -Uflash:w: »$(ProjectDir)Debug\$(TargetName).hex »:i

Maintenant, ouvrez un nouveau projet dans AVR Studio pour le Atmega328p.

etape2

 

etape3

Faites un petit programme qui fait clignoter le LED du Arduino. Dans le cas du UNO Rev3 c’est PB5 qui contrôle la LED.
(Voici le schéma : Arduino_Uno_Rev3-schematic)

etape4

Compilez et vérifiez qu’il n’y a pas d’erreur.

etape7

Ajoutez AVRDude comme outil externe.

etape5

Copiez la ligne de commande et la ligne d’argument puis cliquez sur Apply.

epate6

Retournez dans le menu Tools et sélectionnez AVRDUDE.

etape8

Notez que vous pourriez avoir à changer l’argument du port si jamais ce dernier venait à changer lors d’une prochaine connexion du câble USB.