La Raspberry pico est une carte équipée du microcontrôleur à faible coût RP2040 qui peut être programmée à l'aide d'un SDK dédié ou bien à l'aide de l'IDE Arduino ou encore en python adapté au microcontrôleur : le micropython. La description complète de cette carte est disponible sur la page raspberry rubrique microcontrôleur.
Cette carte dispose de 29 GPIOs qui assurent plusieurs fonctions : entrée sortie binaire, UART, I2C, SPI, PWM , PIO (Programmable Input Output), SIO (Special Input Output).
La broche GPIO25, qui n'apparaît pas, est reliée à une LED sur la carte (LED_BUILTIN de l'exemple blink d'Arduino). Sur la carte certains GPIOs sont dédiés à ces fonctions par défaut. Les 8 blocs (slices) PWM possèdent chacun deux canaux A et B qui sont répartis sur les broches du circuit. Pour chaque bloc les canaux A et B occupent deux GPIOs successifs. C'est cette configuration qui sera utilisée dans les exemples qui seront présentés dans les paragraphes suivants.
Alimenter la carte en maintenant l'appui sur le bouton BOOTSEL permet de positionner la carte en mode programmation. Ce protocole permet de transformer la liaison USB en un espace de stockage (RPI-RP2) accessible depuis l'ordinateur. Le dépôt d'un fichier d'extension uf2 (produit par les outils de développement) dans cet espace de stockage programme la carte et la redémarre. Ce redémarrage ferme l'espace de stockage. Pour la reprogrammer, il faut débrancher la carte et la reconnecter en appuyant sur BOOTSEL. Ce protocole n'est pas nécessaire avec l'IDE Arduino.
Notez que je décline toutes responsabilités quant aux conséquences que pourraient avoir l'utilisation des programmes présentés. Ceux-ci pourraient être erronés ou obsolètes.
Le SDK est disponible sur le dépôt github. L'installation est parfaitement décrite sur le document Getting started with raspberry pico. Après avoir installé le SDK, il est possible d'essayer le programme blink qui fait clignoter la led de la carte (nommée ici PICO_DEFAULT_LED_PIN).
Il existe également de nombreux exemples sur ce dépôt github.
cmake .. make
Note : la fonction main possède une valeur de retour qui implique normalement l'utilisation de return. Dans le chapitre 1.2. Anatomy of a SDK Application du document pdf Raspberry Pi Pico C/C++ SDK, on trouve une note qui précise que la valeur de retour n'est pas utilisée et que le processus est bloqué à la fin de la fonction.
De plus une application définitive signale toutes les erreurs et ne bloque pas le processus. L'affichage sur la sortie standard est remplacé par un écran spécifique ou bien des leds.
L'accès aux GPIOs utilisent des fonctions dont les prototypes sont définies dans pico/stdlib.h :
Contenu du fichier CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(perga_projet C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()
add_executable(blink
blink.c
)
pico_enable_stdio_usb(blink 1)
pico_enable_stdio_uart(blink 1)
pico_add_extra_outputs(blink)
target_link_libraries(blink pico_stdlib)
Contenu du fichier blink.c
#include "pico/stdlib.h"
#define LED 25
int main() {
gpio_init(LED);
gpio_set_dir(LED, GPIO_OUT);
while (true) {
gpio_put(LED, 0 );
sleep_ms(250);
gpio_put(LED, 1 );
sleep_ms(250);
}
}
On n'oublie pas la boucle infinie de la programmation des microcontrôleurs.
On commence par l'exemple du chenillard qui utilise les GPIOs de 6 à 13.
Contenu du fichier CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(perga_projet C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()
add_executable(chenillard
chenillard.c
)
pico_enable_stdio_usb(chenillard 1)
pico_enable_stdio_uart(chenillard 1)
pico_add_extra_outputs(chenillard)
target_link_libraries(chenillard pico_stdlib)
Contenu du fichier chenillard.c
#include "pico/stdlib.h"
const int leds[8] = {6,7,8,9,10,11,12,13};
void initport(uint8_t masque) {
int pindir;
for(int i=0;i<8;i+=1) {
pindir = (masque >> i) & 1;
gpio_init(leds[i]);
if (pindir == 0) {
gpio_set_dir(leds[i], GPIO_IN);
}
else {
gpio_set_dir(leds[i], GPIO_OUT);
}
}
}
void writeport(uint8_t valeur) {
int pinvalue;
for(int i=0;i<8;i+=1) {
pinvalue = (valeur >> i) & 1;
gpio_put(leds[i], pinvalue );
}
}
int main() {
int valeur;
valeur = 1 ;
initport(0xff);
while (true) {
valeur = (valeur << 1) % 255;
writeport(valeur);
sleep_ms(250);
}
}
Contenu du fichier CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(perga_projets C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()
add_executable(pwm
pwm.c
)
pico_enable_stdio_usb(pwm 1)
pico_enable_stdio_uart(pwm 1)
pico_add_extra_outputs(pwm)
target_link_libraries(pwm hardware_pwm pico_stdlib)
On ajoute la librairie de gestion du pwm : hardware_pwm
Contenu du fichier pwm.c
#include "pico/stdlib.h"
#include "hardware/pwm.h"
#include <math.h>
#define LED 25
#define PERIODE 500
#define DELAI_MS 2
int main() {
unsigned long n;
unsigned long y;
gpio_set_function(LED, GPIO_FUNC_PWM);
pwm_config config= pwm_get_default_config();
uint slice = pwm_gpio_to_slice_num(LED);
pwm_init(slice,&config,true);
while (true) {
pwm_set_gpio_level(LED,y);
sleep_ms(DELAI_MS);
y=32767*(1+sin(2*M_PI*n/PERIODE));
n = (n + 1) % PERIODE;
}
}
Le GPIO 25 correspond au PWM4 canal B.
Contenu du fichier CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(perga_projets C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()
add_executable(chenillard_timer
chenillard_timer.c
)
pico_enable_stdio_usb(chenillard_timer 1)
pico_enable_stdio_uart(chenillard_timer 1)
pico_add_extra_outputs(chenillard_timer)
target_link_libraries(chenillard_timer hardware_timer pico_stdlib)
La fonction etape_chenillard utilise une variable statique, qui est initialisée à 128 (0x80) au premier appel de la fonction. Ensuite le contenu de cette variable est conservé.
Le programme principal fait clignoter la LED 14, la fonction associée au timer commande le chenillard.
Contenu du fichier chenillard_timer.c
#include "pico/stdlib.h"
#include "hardware/timer.h"
#define LED 14
#define DELAI_LED_MS 200
#define PERIODE_MS 250
const int leds[8] = {6,7,8,9,10,11,12,13};
void initport(uint8_t masque) {
int pindir;
for(int i=0;i<8;i+=1) {
pindir = (masque >> i) & 1;
gpio_init(leds[i]);
if (pindir == 0) {
gpio_set_dir(leds[i], GPIO_IN);
}
else {
gpio_set_dir(leds[i], GPIO_OUT);
}
}
}
void writeport(uint8_t valeur) {
int pinvalue;
for(int i=0;i<8;i+=1) {
pinvalue = (valeur>> i) & 1;
gpio_put(leds[i], pinvalue );
}
}
bool etape_chenillard(struct repeating_timer *t) {
static int valeur = 0x80 ;
valeur = (valeur << 1) % 255;
writeport(valeur);
return true;
}
int main() {
struct repeating_timer timer;
gpio_init(LED);
gpio_set_dir(LED, GPIO_OUT);
initport(0xff);
add_repeating_timer_ms(PERIODE_MS, etape_chenillard, NULL, &timer);
while (true) {
gpio_put(LED, 0 );
sleep_ms(DELAI_LED_MS);
gpio_put(LED, 1 );
sleep_ms(DELAI_LED_MS);
}
}
Exemple avec le capteur de luminosité bh1745 déjà utilisé avec la raspberry pi et l'arduino.
Contenu du fichier bh1745.h
#ifndef __BH1745_H
#define __BH1745_H
#include "bh1745_defs.h"
int bh1745manutactureId(void);
int bh1745Init(void);
int bh1745Mesure(void);
int bh1745Pret(void) ;
int bh1745lire(unsigned int );
void bh1745close(void);
#endif
On utilise le fichier de définitions bh1745_defs.h déjà utilisé avec la raspberry pi et arduino. On ne réécrit pas le code, on utilise le code des projets arduino et raspberry pi. Ce qui change se sont les fonctions de gestion du bus I2C.
La structure de données i2c est globale car elle est utilisée par toutes les fonctions de gestion du bus I2C.
Contenu du fichier bh1745.c
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "bh1745.h"
const uint sda_pin = 4;
const uint scl_pin = 5;
i2c_inst_t *i2c = i2c0;
void bh1745EcrireReg(unsigned char reg,unsigned char valeur) {
unsigned char buffer[2];
buffer[0] = reg;
buffer[1] = valeur;
i2c_write_blocking(i2c,BH1745_I2CADR,buffer,2,false);
}
int bh1745LireReg(unsigned char reg) {
int resultat = 0;
unsigned char buffer[2];
buffer[0] = reg;
i2c_write_blocking(i2c,BH1745_I2CADR,buffer,1,true);
int nread = i2c_read_blocking(i2c,BH1745_I2CADR,buffer,1,false);
if (nread != 1) {
return -1 ;
}
resultat = (int)buffer[0];
return resultat;
}
int bh1745Init() {
int resultat=0;
i2c_init(i2c, 100000UL);
gpio_set_function(sda_pin, GPIO_FUNC_I2C);
gpio_set_function(scl_pin, GPIO_FUNC_I2C);
bh1745EcrireReg(BH1745_SYSTEM_CTRL,BH1745_SYSCTRL_SW_RST);
bh1745EcrireReg(BH1745_CTRL1,BH1745_CTRL1_640ms);
bh1745EcrireReg(BH1745_CTRL2,BH1745_CTRL2_RGBC_EN|BH1745_CTRL2_ADC_G_2);
bh1745EcrireReg(BH1745_SYSTEM_CTRL,0);
bh1745EcrireReg(BH1745_CTRL3,BH1745_CTRL3_VALUE);
return resultat;
}
int bh1745manutactureId() {
int resultat = 0 ;
resultat = bh1745LireReg(BH1745_ID);
return resultat;
}
int bh1745Mesure() {
int resultat = 0;
bh1745EcrireReg(BH1745_CTRL3,BH1745_CTRL3_VALUE);
return resultat ;
}
int bh1745Pret() {
int resultat =0;
resultat = bh1745LireReg(BH1745_CTRL2);
if (resultat < 0) {
return resultat;
}
return resultat & BH1745_CTRL2_VALID;
}
int bh1745lire(unsigned int LSBReg) {
int valeur = 0;
int lsb,msb;
int resultat =0;
resultat = bh1745LireReg(LSBReg);
if (resultat < 0) {
return resultat;
}
lsb = resultat ;
resultat = bh1745LireReg(LSBReg + 1);
if (resultat < 0) {
return resultat;
}
msb = resultat;
valeur = ((msb & 0xff) << 8) + (lsb & 0xff) ;
return valeur;
}
void bh1745close() {
i2c_deinit(i2c);
}
Contenu du fichier CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(perga_projets C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()
add_executable(luminosite
luminosite.c
bh1745.c
)
pico_enable_stdio_usb(luminosite 1)
pico_enable_stdio_uart(luminosite 0)
pico_add_extra_outputs(luminosite)
target_link_libraries(luminosite hardware_i2c pico_stdlib)
On ajoute le fichier bh1745.c dans la liste des fichiers sources et la librairie hardware_i2c dans la liste des librairies.
Pour utiliser la sortie standard qui correspond à la connexion USB de la carte raspberry pico, il faut, bien évidement, inclure stdio.h, puis initialiser la communication avec la fonction stdio_init_all(), après on peut utiliser les fonctions comme printf.
Du côté de l'ordinateur, il faut utiliser un terminal série avec une vitesse de 921600 bauds. L'identifiant du port série dépend du système d'exploitation, il peut être, par exemple, /dev/ttyACM0.
Contenu du fichier luminosite.c
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "bh1745.h"
int main() {
unsigned short red,green,blue,clear;
stdio_init_all();
bh1745Init();
int id = bh1745manutactureId();
printf("id = %d\n",id);
while (true) {
if (bh1745Pret()) {
red = bh1745lire(BH1745_RED_LSB);
green = bh1745lire(BH1745_GREEN_LSB);
blue = bh1745lire(BH1745_BLUE_LSB);
clear = bh1745lire(BH1745_CLEAR_LSB);
printf("%u;%u;%u;%u\n",red,green,blue,clear);
sleep_ms(500);
}
}
}
Exemple avec l'accéléromètre LSM303D déjà utilisé avec la raspberry pi et l'arduino.
Contenu du fichier LSM303D.h
#ifndef __LSM303D_H
#define __LSM303D_H
#include "LSM303D_defs.h"
// interface SPI
#define LSM303D_CANAL 0
#define LSM303D_FREQUENCE 1000000L
// calibre +-2
#define DIV_2 16384.0
void LSM303DInit(void);
void LSM303DEcrireReg(unsigned char ,unsigned char);
unsigned char LSM303DLireReg(int);
int LSM303DAccPret(void);
short LSM303DLireAxe(int );
void LSM303Dclose(void);
#endif
Contenu du fichier LSM303D.c
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "LSM303D.h"
const uint miso_pin = 16;
const uint cs_pin = 17;
const uint sck_pin = 18;
const uint mosi_pin = 19;
spi_inst_t *spi = spi0;
void LSM303DEcrireReg(unsigned char registre,unsigned char valeur) {
unsigned char buffer[2];
buffer[0] = registre;
buffer[1] = valeur;
gpio_put(cs_pin, 0);
spi_write_blocking(spi, buffer, 2);
gpio_put(cs_pin, 1);
}
unsigned char LSM303DLireReg(int registre) {
unsigned char resultat = 0;
unsigned char buffer[2];
buffer[0] = registre | 0x80 ;
buffer[1] = 0;
gpio_put(cs_pin, 0);
spi_write_blocking(spi, buffer, 2);
int nread = spi_read_blocking(spi, 0, buffer, 2);
gpio_put(cs_pin, 1);
resultat = buffer[1];
return resultat;
}
void LSM303DInit(void) {
gpio_init(cs_pin);
gpio_set_dir(cs_pin, GPIO_OUT);
gpio_put(cs_pin, 1);
gpio_set_function(sck_pin, GPIO_FUNC_SPI);
gpio_set_function(mosi_pin, GPIO_FUNC_SPI);
gpio_set_function(miso_pin, GPIO_FUNC_SPI);
spi_init(spi, 1000000UL);
LSM303DEcrireReg(LSM303D_CTRL_1,LSM303D_CTRL_1_AZEN|LSM303D_CTRL_1_AYEN|LSM303D_CTRL_1_AXEN | LSM303D_CTRL_1_AODR_0);
LSM303DEcrireReg(LSM303D_CTRL_2,0);
LSM303DEcrireReg(LSM303D_CTRL_3,0);
LSM303DEcrireReg(LSM303D_CTRL_4,0);
LSM303DEcrireReg(LSM303D_CTRL_5,0);
LSM303DEcrireReg(LSM303D_CTRL_6,0);
LSM303DEcrireReg(LSM303D_CTRL_7,0);
}
int LSM303DAccPret() {
unsigned char valeur = LSM303DLireReg(LSM303D_STATUS_A);
return ((valeur & LSM303D_STATUS_A_ZYXADA) == LSM303D_STATUS_A_ZYXADA);
}
short LSM303DLireAxe(int regbase) {
short resultat =0;
unsigned char lsb,msb;
lsb=LSM303DLireReg(regbase);
msb=LSM303DLireReg(regbase+1);
resultat = (short)lsb | (((short)msb) << 8) ;
return resultat;
}
void LSM303Dclose() {
spi_deinit (spi);
}
Contenu du fichier CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(perga_projets C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()
add_executable(accelmag
accelmag.c
LSM303D.c
)
pico_enable_stdio_usb(accelmag 1)
pico_enable_stdio_uart(accelmag 0)
pico_add_extra_outputs(accelmag)
target_link_libraries(accelmag hardware_spi pico_stdlib)
On ajoute le fichier LSM303D.c dans la liste des fichiers sources et la librairie hardware_spi dans la liste des librairies.
Contenu du fichier accelmag.c
#include <stdio.h>
#include <math.h>
#include "pico/stdlib.h"
#include "LSM303D.h"
int main() {
unsigned char id;
int ax,ay,az;
double rax,ray,raz;
int mx,my,mz;
double rmx,rmy,rmz,mm;
stdio_init_all();
LSM303DInit();
id = LSM303DLireReg(LSM303D_WHO_AM_I);
printf("ID = %x\n",id);
while (true) {
if (LSM303DAccPret()) {
ax = LSM303DLireAxe(LSM303D_OUT_X_L_A);
ay = LSM303DLireAxe(LSM303D_OUT_Y_L_A);
az = LSM303DLireAxe(LSM303D_OUT_Z_L_A);
rax = (double)ax / DIV_2 ;
ray = (double)ay / DIV_2 ;
raz = (double)az / DIV_2 ;
mx = LSM303DLireAxe(LSM303D_OUT_X_L_M);
my = LSM303DLireAxe(LSM303D_OUT_Y_L_M);
mz = LSM303DLireAxe(LSM303D_OUT_Z_L_M);
rmx = (double)mx / DIV_2 ;
rmy = (double)my / DIV_2 ;
rmz = (double)mz / DIV_2 ;
mm = sqrt(rmx*rmx+rmy*rmy+rmz*rmz);
printf("x=%+03.2lf g,y=%+03.2lf g,z=%+03.2lf g\tx=%+03.2lf G,y=%+03.2lf G,z=%+03.2lf G,M=%+03.2lf G\n",rax,ray,raz,rmx,rmy,rmz,mm);
}
sleep_ms(500);
}
}
L'installation de la carte n'est pas forcément disponible dans l'IDE arduino, mais il est possible d'utiliser la solution arduino pico. L'installation peut se faire en ajoutant le lien https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json dans le gestionnaire de sites du menu préférences.
Ensuite on utilise le gestionnaire de carte pour ajouter Raspberry Pi Pico/RP2040.
La programmation de la carte nécessite le démarrage avec BOOTSEL uniquement lors de la première utilisation avec l'IDE. Pour les programmations suivantes c'est l'IDE qui gère automatiquement la programmation.
On reprend les exemples utilisés avec l'arduino et la carte uno. Le code reste le même, il suffit de changer les valeurs des GPIOs pour utiliser les valeurs de la carte raspberry pico que ce soit pour le chenillard, le bus SPI ou encore le bus I2C.
Contenu du fichier chenillard.ino
const int leds[8] = {6,7,8,9,10,11,12,13};
void initport(uint8_t masque) {
int pindir;
for(int i=0;i<8;i+=1) {
pindir = (masque >> i) & 1;
pinMode(leds[i], pindir);
}
}
void writeport(uint8_t valeur) {
int pinvalue;
for(int i=0;i<8;i+=1) {
pinvalue = (valeur>> i) & 1;
digitalWrite(leds[i], pinvalue);
}
}
int valeur;
void setup() {
initport(0xff);
valeur = 1;
}
void loop() {
writeport(valeur);
delay(200);
valeur = (valeur << 1) % 255;
}
La seule différence avec le programme de l'arduino uno est la liste des ID des 8 leds.
Contenu du fichier pwm.ino
#define LED 25
#define PERIODE 500
#define DELAI_MS 2
unsigned int n;
unsigned int y;
void setup() {
pinMode(LED,OUTPUT);
y=0;
}
void loop() {
analogWrite(LED,y);
delay(DELAI_MS);
y=127*(1+sin(2*PI*n/PERIODE));
n = (n + 1) % PERIODE;
}
Il suffit juste de changer la valeur de la sortie GPIO
La version utilisée pour l'arduino uno ne fonctionne pas avec la raspberry pico car elle fait appel au timer matériel de l'Atmega. Il faut donc utiliser une librairie adaptée RPI_PICO_TimerInterrupt au processeur RP2040 de la carte raspberry pico. Cette Librairie s'installe à partir de l'IDE.
La portabilité des programmes Arduino sur tous les processeurs et plates-formes a ses limites.
Contenu du fichier chenillard_timer.ino
// These define's must be placed at the beginning before #include "TimerInterrupt_Generic.h"
// _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4
// Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system.
#define TIMER_INTERRUPT_DEBUG 0
#define _TIMERINTERRUPT_LOGLEVEL_ 0
// Can be included as many times as necessary, without `Multiple Definitions` Linker Error
#include "RPi_Pico_TimerInterrupt.h"
#define PERIODE_US 250000UL
#define DELAI_MS 200
#define LED 14
const int leds[8] = {6,7,8,9,10,11,12,13};
uint8_t compteur;
int bascule=0;
// Init RPI_PICO_Timer
RPI_PICO_Timer ITimer0(0);
void initport() {
for(int i=0;i<8;i+=1) {
pinMode(leds[i],OUTPUT);
digitalWrite(leds[i],LOW);
}
}
bool TimerChenillard(struct repeating_timer *t) {
if (compteur == 0) {
digitalWrite(leds[7],LOW);
}
else {
digitalWrite(leds[compteur-1],LOW);
}
digitalWrite(leds[compteur],HIGH);
compteur = (compteur + 1) % 8;
return true;
}
void setup() {
pinMode(LED,OUTPUT);
digitalWrite(LED,bascule);
initport();
compteur = 0 ;
ITimer0.attachInterruptInterval(PERIODE_US, TimerChenillard);
}
void loop() {
delay(DELAI_MS);
bascule = (~bascule & 1);
digitalWrite(LED,bascule);
}
Ce code, inspiré des exemples disponibles, ressemble à celui de la carte Arduino car l'interface est quasi identique. Ici on construit l'objet ITimer alors que pour la carte uno, il est déjà construit, il n'y pas de méthode Init comme c'est le cas pour la carte uno. La période est en microsecondes au lieu de millisecondes. La fonction d'interruption à une valeur de retour de type booléen alors qu'elle n'en a pas pour la carte uno.
Contenu du fichier luminosite.ino
#include <Wire.h>
#include "bh1745_defs.h"
#define GP_SDA 16
#define GP_SCL 17
void ecrireRegistre(byte registre, byte valeur) {
Wire.beginTransmission(BH1745_I2CADR);
Wire.write(registre);
Wire.write(valeur);
Wire.endTransmission();
}
int lireRegistre(byte registre) {
uint8_t recu;
Wire.beginTransmission(BH1745_I2CADR);
Wire.write(registre);
Wire.endTransmission();
Wire.requestFrom(BH1745_I2CADR, 1);
if (Wire.available() == 1) {
recu = Wire.read();
return recu;
}
else {
return -1;
}
}
void bh1745Init() {
ecrireRegistre(BH1745_SYSTEM_CTRL,BH1745_SYSCTRL_SW_RST);
ecrireRegistre(BH1745_SYSTEM_CTRL,0);
ecrireRegistre(BH1745_CTRL1,BH1745_CTRL1_320ms);
ecrireRegistre(BH1745_CTRL2,BH1745_CTRL2_RGBC_EN);
ecrireRegistre(BH1745_CTRL3,BH1745_CTRL3_VALUE);
ecrireRegistre(BH1745_TH_LSB,0);
ecrireRegistre(BH1745_TH_MSB,0);
ecrireRegistre(BH1745_TL_LSB,0xff);
ecrireRegistre(BH1745_TL_MSB,0xff);
ecrireRegistre(BH1745_INTERRUPT,0);
}
unsigned char bh1745manutactureId() {
unsigned char mid;
mid = lireRegistre(BH1745_ID);
return mid;
}
void bh1745Mesure() {
ecrireRegistre(BH1745_CTRL3,BH1745_CTRL3_VALUE);
}
boolean bh1745Pret() {
int valide = lireRegistre(BH1745_CTRL2);
return ((valide & BH1745_CTRL2_VALID) == BH1745_CTRL2_VALID);
}
unsigned short bh1745lire(unsigned int LSBReg) {
unsigned short valeur=0;
unsigned short lsb,msb;
lsb = lireRegistre(LSBReg);
msb = lireRegistre(LSBReg + 1);
valeur = lsb + (msb << 8);
return valeur;
}
void bh1745Led(int etat) {
if (etat) {
ecrireRegistre(BH1745_INTERRUPT,BH1745_INTERRUPT_ENABLE);
}
else {
ecrireRegistre(BH1745_INTERRUPT,0);
}
}
unsigned short red,green,blue,luminosite;
boolean pret;
void setup() {
Serial.begin(19200);
Wire.setSDA(GP_SDA);
Wire.setSCL(GP_SCL);
Wire.setClock(100000);
Wire.begin();
bh1745Init();
unsigned char manufacture = bh1745manutactureId();
Serial.print("id=");
Serial.println(manufacture,HEX);
}
void loop() {
pret = bh1745Pret();
if (pret) {
red = bh1745lire(BH1745_RED_LSB);
green = bh1745lire(BH1745_GREEN_LSB);
blue = bh1745lire(BH1745_BLUE_LSB);
luminosite = bh1745lire(BH1745_CLEAR_LSB);
Serial.print("rouge=");
Serial.print(red);
Serial.print(",vert=");
Serial.print(green);
Serial.print(",bleu=");
Serial.print(blue);
Serial.print(",clear=");
Serial.println(luminosite);
}
delay(200);
}
L'objet Wire utilise le port I2C 0, pour le port i2C 1, il faut utiliser l'objet Wire1.
L'utilisation des fonctions Wire.setSDA(ID) et Wire.setSCL(ID) ne sont utilisées que si on utilise pas les valeurs par défaut des GPIOs 4 et 5. Ce qui signifie que si on utilise le port I2C par défaut, il n'y a rien à changer dans le programme destiné à la carte uno.
Contenu du fichier luminosite.ino
#include <SPI.h>
#include "LSM303D_defs.h"
//#define NODEFAULT
#ifdef NODEFAULT
#define GP_MISO 4
#define GP_SS 5
#define GP_SCK 6
#define GP_MOSI 7
#else
#define GP_SS SS
#endif
SPISettings LSM303Dsettings(1000000UL, MSBFIRST, SPI_MODE0);
// calibre +-2
#define DIV_2 16384.0
void LSM303DEcrireReg(unsigned char registre,unsigned char valeur) {
digitalWrite(GP_SS, LOW);
SPI.transfer(registre);
SPI.transfer(valeur);
digitalWrite(GP_SS, HIGH);
delayMicroseconds(1);
}
unsigned char LSM303DLireReg(unsigned char registre) {
unsigned char resultat = 0;
digitalWrite(GP_SS, LOW);
SPI.transfer(registre | 0x80);
resultat = SPI.transfer(0);
digitalWrite(GP_SS, HIGH);
delayMicroseconds(1);
return resultat;
}
void LSM303DInit(void) {
LSM303DEcrireReg(LSM303D_CTRL_1,LSM303D_CTRL_1_AZEN|LSM303D_CTRL_1_AYEN|LSM303D_CTRL_1_AXEN | LSM303D_CTRL_1_AODR_0);
LSM303DEcrireReg(LSM303D_CTRL_2,0);
LSM303DEcrireReg(LSM303D_CTRL_3,0);
LSM303DEcrireReg(LSM303D_CTRL_4,0);
LSM303DEcrireReg(LSM303D_CTRL_5,0);
LSM303DEcrireReg(LSM303D_CTRL_6,0);
LSM303DEcrireReg(LSM303D_CTRL_7,0);
}
bool LSM303DAccPret() {
unsigned char valeur = LSM303DLireReg(LSM303D_STATUS_A);
return ((valeur & LSM303D_STATUS_A_ZYXADA) == LSM303D_STATUS_A_ZYXADA);
}
short LSM303DLireAxe(unsigned int regbase) {
short resultat =0;
unsigned char lsb,msb;
lsb=LSM303DLireReg(regbase);
msb=LSM303DLireReg(regbase+1);
resultat = (short)lsb | (((short)msb) << 8) ;
return resultat;
}
unsigned char id;
int ax,ay,az;
double rax,ray,raz;
int mx,my,mz;
double rmx,rmy,rmz,mm;
void printData(const String nom,double valeur) {
Serial.print(nom);
Serial.print(valeur);
}
void setup() {
Serial.begin(19200);
pinMode(GP_SS, OUTPUT);
digitalWrite(GP_SS, HIGH);
#ifdef NODEFAULT
SPI.setRX(GP_MISO);
SPI.setCS(GP_SS);
SPI.setSCK(GP_SCK);
SPI.setTX(GP_MOSI);
#endif
SPI.begin();
SPI.beginTransaction(LSM303Dsettings);
LSM303DInit();
id = LSM303DLireReg(LSM303D_WHO_AM_I);
Serial.print("ID = ");
Serial.println(id,HEX);
delay(100);
}
void loop() {
while (!LSM303DAccPret());
ax = LSM303DLireAxe(LSM303D_OUT_X_L_A);
ay = LSM303DLireAxe(LSM303D_OUT_Y_L_A);
az = LSM303DLireAxe(LSM303D_OUT_Z_L_A);
rax = (double)ax / DIV_2 ;
ray = (double)ay / DIV_2 ;
raz = (double)az / DIV_2 ;
mx = LSM303DLireAxe(LSM303D_OUT_X_L_M);
my = LSM303DLireAxe(LSM303D_OUT_Y_L_M);
mz = LSM303DLireAxe(LSM303D_OUT_Z_L_M);
rmx = (double)mx / DIV_2 ;
rmy = (double)my / DIV_2 ;
rmz = (double)mz / DIV_2 ;
mm = sqrt(rmx*rmx+rmy*rmy+rmz*rmz);
printData("ax=",rax);
printData(" ay=",ray);
printData(" az=",raz);
printData(", mx=",rmx);
printData(" my=",rmy);
printData(" mz=",rmz);
printData(", M=",mm);
Serial.println("");
}
Comme pour le port I2C, l'objet SPI utilise le port SPI 0, l'objet SPI1 utilise le port SPI 1.
De la même façon, si on utilise le port SPI par défaut (16,17,18,19), il n'y a rien à changé par rapport au code destiné à la carte uno, si on utilise un autre port SPI, il faut donc définir les broches utilisées comme cela est réalisé dans le code. La broche de sélection de circuit SS est également définie par défaut.
On utilise micropython qui est une version de python adaptée aux microcontrôleurs
Pour installer le firmware micropython, il faut suivre la documentation sur le SDK python ou encore directement télécharger le firmware de la raspberry pico le tout disponible sur la page micropython du site raspberry, en faisant attention à bien choisir le firmware (uf2) en fonction du type de raspberry pico (avec ou sans wifi). Ensuite, il suffit de déposer le fichier uf2 sur la pico après l'avoir démarrée en activant le poussoir bootsel.
Pour l'utilisation, il est conseillé d'utiliser le logiciel recommandé Thony qui permet de se connecter à la carte et de tester les programmes python. Ce Comme cela est décrit dans le paragraphe 4.1.1. Blinking the LED from Thonny de la documentation Raspberry Pi Pico Python SDK, si on sauve le programme dans la pico avec le nom main.py, celui-ci s'exécutera automatiquement au démarrage de la pico.
Très Important : cette opération peut s'avérer être irréversible , car après on n'a plus accès à la pico avec le logiciel. De plus, recharger le firmware micropython ne résout pas le problème. On évitera donc d'appeler le programme principal python main.py.
La gestion des périphériques nécessitent d'utiliser un ensemble de modules micropython adaptés à la pico.
L'accès aux GPIOs se fait avec la classe Pin du module machine. Les principales méthodes sont :
from machine import Pin
import time
GPIOLED = const(25)
led = Pin(GPIOLED, Pin.OUT)
led.off()
while True:
led.toggle()
time.sleep(1)
En micropython, il existe une fonction const() qui permet de déclarer une valeur constante. Le module time permet d'utiliser la fonction sleep avec le temps en secondes
La boucle infinie peut être interrompue à l'aide du logiciel Thony.
On utilise le GPIO 25 qui correspond à la led disponible sur la pico qui est initialisée en sortie. Cette constante 25 peut être remplacée par la chaîne "LED" qui rend le programme plus portable :
led = Pin("LED", Pin.OUT)
from machine import Pin,Timer
import time
GPIOS = (6,7,8,9,10,11,12,13 )
def initPort(gpios):
leds=[]
for i in range(8):
led = Pin(gpios[i], Pin.OUT)
leds.append(led)
return leds
def EcrirePort(leds,valeur):
masque = 1
for i in range(8):
leds[i].value(valeur & masque)
masque <<= 1
leds=initPort(GPIOS)
valeur = 1
EcrirePort(leds,valeur)
while True:
pwm4b.duty_u16(y)
time.sleep_ms(DELAI_MS)
y=int(32767*(1+sin(2*pi*n/PERIODE)))
n = (n+1) % PERIODE
On a une fonction d'initialisation du port 8 bits qui renvoie un tableau d'objets GPIOs qui seront ensuite utilisés par la méthode d'écriture.
La liste est crée avant d'ajouter chaque objet à cette liste.
La fonction d'écriture utilise un masque pour sélectionner chaque bit de l'octet.
Le décalage d'un bit avec l'opérateur de décalage. Le modulo 255 permet de revenir au bit 0 après le bit 7.
A chaque fois que cela est possible, on doit donc remplacer la boucle infinie while True par une fonction appelée par un timer, ce qui a pour effet de ne pas bloquer le système.
La classe Timer du module machine qui fournit les méthodes
from machine import Pin,Timer
def clignote(timer):
global led
led.toggle()
led = Pin("LED", Pin.OUT)
led.off()
timer = Timer()
timer.init(freq=1, mode=Timer.PERIODIC, callback=clignote)
L'utilisation d'un timer à la place de la boucle infinie while doit être privilégiée à chaque fois que cela est possible afin de ne pas bloquer le programme principal et rendre la main au système.
La fonction est appelée par le système, le programme principal ne peut donc pas lui transmettre des paramètres. L'objet led du programme principal est accessible à l'intérieur de la fonction s'il est déclaré global dans la fonction.
from machine import Pin,Timer
import time
GPIOS = (6,7,8,9,10,11,12,13 )
def initPort(gpios):
leds=[]
for i in range(8):
led = Pin(gpios[i], Pin.OUT)
leds.append(led)
return leds
def EcrirePort(leds,valeur):
masque = 1
for i in range(8):
leds[i].value(valeur & masque)
masque <<= 1
def chenille(x):
global leds,valeur
valeur = (valeur << 1) % 255
EcrirePort(leds,valeur)
leds=initPort(GPIOS)
valeur = 1
EcrirePort(leds,valeur)
clignotement = Timer()
clignotement.init(mode=Timer.PERIODIC, freq=10, callback=chenille)
time.sleep(20)
clignotement.deinit()
Le timer est initialisé en mode périodique avec une fréquence de 10 Hz, la fonction appelée est la fonction chenille. Cette doit contenir un paramètre.
Après avoir lancer le timer, on attend 20 secondes avant de terminer le programme et désactiver le timer.
L'utilisation des sorties PWM se fait avec la classe PWM du module machine. Les principales fonctions sont :
from machine import Pin,PWM
from math import *
import time
GPIOLED = const(25)
PERIODE = const(500)
DELAI_MS = const(4)
pwm4b = PWM(Pin(GPIOLED), freq=100, duty_u16=0)
n = int(0)
y = int(0)
while True:
pwm4b.duty_u16(y)
time.sleep_ms(DELAI_MS)
y=int(32767*(1+sin(2*pi*n/PERIODE)))
n = (n+1) % PERIODE
On utilise la LED de la carte qui correspond au PWM4B.
Ce programme produit une variation sinusoïdale de la luminosité en sachant que la luminosité n'est pas linéaire avec la tension.
La période de la sinusoïde est de 500×4ms soit 2 secondes
La valeur du signal de sortie est comprise entre 0 et la tension maximale.
L'utilisation du bus I2C de fait avec la classe I2C du module machine. Les principales fonctions sont :
On reprend l'exemple de la raspberry pi avec le capteur BH1745
Fichier de définition des constantes bh1745_defs.py
class BH1745_defs():
I2CADR = 0x38
SYSTEM_CTRL = 0x40
CTRL1 = 0x41
CTRL2 = 0x42
CTRL3 = 0x44
RED_LSB = 0x50
RED_MSB = 0x51
GREEN_LSB = 0x52
GREEN_MSB = 0x53
BLUE_LSB = 0x54
BLUE_MSB = 0x55
CLEAR_LSB = 0x56
CLEAR_MSB = 0x57
DINT_LSB = 0x58
DINT_MSB = 0x59
INTERRUPT = 0x60
PERSISTENCE = 0x61
TH_LSB = 0x62
TH_MSB = 0x63
TL_LSB = 0x64
TL_MSB = 0x65
ID = 0x92
MANUFACTURER_ID = 0xE0
SYSCTRL_SW_RST = 0x80
SYSCTRL_INT_RST = 0x40
CTRL1_160ms = 0x00
CTRL1_320ms = 0x01
CTRL1_640ms = 0x02
CTRL1_1280ms = 0x03
CTRL1_2560ms = 0x04
CTRL1_5120ms = 0x05
CTRL2_VALID = 0x80
CTRL2_RGBC_EN = 0x10
CTRL2_ADC_G_1 = 0x00
CTRL2_ADC_G_2 = 0x01
CTRL2_ADC_G_16 = 0x02
CTRL3_VALUE = 0x02
INTERRUPT_STATUS = 0x80
INTERRUPT_LATCH = 0x10
INTERRUPT_SOURCE = 0xC0
INTERRUPT_ENABLE = 0x01
LED_ON = 1
LED_OFF = 0
Programme de test luminosite.py
from bh1745_defs import BH1745_defs
from bh1745 import BH1745
bh1745 = BH1745(0)
bh1745.device_init()
identifiant = bh1745.manutactureId()
print(hex(identifiant))
pret = bh1745.Pret()
while (not pret):
pret = bh1745.Pret()
red = bh1745.lire(BH1745_defs.RED_LSB);
green = bh1745.lire(BH1745_defs.GREEN_LSB);
blue = bh1745.lire(BH1745_defs.BLUE_LSB);
clear = bh1745.lire(BH1745_defs.CLEAR_LSB);
print(f"valeurs {red};{green};{blue};{clear}");
bh1745.device_close()
from bh1745_defs import BH1745_defs
from machine import I2C
class BH1745():
def __init__(self,numero=0):
self.numport = numero
self.i2c = I2C(numero)
def device_init(self):
buffer = bytearray([ BH1745_defs.SYSTEM_CTRL , BH1745_defs.SYSCTRL_SW_RST ] )
self.i2c.writeto(BH1745_defs.I2CADR, buffer)
buffer = bytearray([ BH1745_defs.SYSTEM_CTRL , 0 ])
self.i2c.writeto(BH1745_defs.I2CADR, buffer )
buffer = bytearray([ BH1745_defs.CTRL1 , BH1745_defs.CTRL1_640ms ] )
self.i2c.writeto(BH1745_defs.I2CADR, buffer )
buffer = bytearray([ BH1745_defs.CTRL2 , BH1745_defs.CTRL2_RGBC_EN | BH1745_defs.CTRL2_ADC_G_2 ] )
self.i2c.writeto(BH1745_defs.I2CADR, buffer)
buffer = bytearray([ BH1745_defs.CTRL3 , BH1745_defs.CTRL3_VALUE ])
self.i2c.writeto(BH1745_defs.I2CADR,buffer )
def device_close(self):
pass
def manutactureId(self):
buffer_in = bytearray([BH1745_defs.ID])
self.i2c.writeto(BH1745_defs.I2CADR, buffer_in)
buffer_out = self.i2c.readfrom(BH1745_defs.I2CADR,1)
return buffer_out[0]
def Mesure(self):
buffer = bytearray([BH1745_defs.CTRL3 , BH1745_defs.CTRL3_VALUE])
self.i2c.writevto(BH1745_defs.I2CADR, buffer )
def Pret(self):
buffer_in = bytearray([BH1745_defs.CTRL2])
self.i2c.writeto(BH1745_defs.I2CADR, buffer_in)
buffer_out = self.i2c.readfrom(BH1745_defs.I2CADR, 1)
return bool(buffer_out[0] & BH1745_defs.CTRL2_VALID)
def lire(self,LSBReg):
buffer_in = bytearray([LSBReg])
self.i2c.writeto(BH1745_defs.I2CADR, buffer_in)
lsb = self.i2c.readfrom(BH1745_defs.I2CADR, 1)
buffer_in = bytearray([LSBReg + 1])
self.i2c.writeto(BH1745_defs.I2CADR, buffer_in)
msb = self.i2c.readfrom(BH1745_defs.I2CADR,1)
valeur = lsb[0] + (msb[0] << 8);
return valeur
Avant l'écriture, le buffer est construit à partir d'une liste qui contient le registre et la valeur.
Pour lire le contenu d'un registre, il faut effectuer une écriture avec la valeur du registre, suivi d'une lecture d'un octet. L'octet renvoyé est disponible à l'indice 0 du buffer de réception.
Pour la lecture d'une donnée on effectue l'écriture et l'écriture pour chaque registre de poids faible et de poids fort. Pour construire une valeur non signée à partir des deux octets, on utilise le décalage et l'addition
L'utilisation du bus SPI se fait avec la classe SPI du module machine. Les principales fonctions sont :
On reprend l'exemple de la raspberry pi avec le capteur LSM303D
Fichier de définition des constantes lsm303d_defs.py
class LSM303D_defs():
DIV_2 = 16384
TEMP_OUT_L = 0x05
TEMP_OUT_H = 0x06
STATUS_M = 0x07
OUT_X_L_M = 0x08
OUT_X_H_M = 0x09
OUT_Y_L_M = 0x0A
OUT_Y_H_M = 0x0B
OUT_Z_L_M = 0x0C
OUT_Z_H_M = 0x0D
WHO_AM_I = 0x0F
CTRL_0 = 0x1F
CTRL_1 = 0x20
CTRL_2 = 0x21
CTRL_3 = 0x22
CTRL_4 = 0x23
CTRL_5 = 0x24
CTRL_6 = 0x25
CTRL_7 = 0x26
STATUS_A = 0x27
OUT_X_L_A = 0x28
OUT_X_H_A = 0x29
OUT_Y_L_A = 0x2A
OUT_Y_H_A = 0x2B
OUT_Z_L_A = 0x2C
OUT_Z_H_A = 0x2D
STATUS_M_ZXYMOR = 0x80
STATUS_M_ZMOR = 0x40
STATUS_M_YMOR = 0x20
STATUS_M_XMOR = 0x10
STATUS_M_ZYXMDA = 0x08
STATUS_M_ZMDA = 0x04
STATUS_M_YMDA = 0x02
STATUS_M_XMDA = 0x01
STATUS_A_ZXYAOR = 0x80
STATUS_A_ZAOR = 0x40
STATUS_A_YAOR = 0x20
STATUS_A_XAOR = 0x10
STATUS_A_ZYXADA = 0x08
STATUS_A_ZADA = 0x04
STATUS_A_YADA = 0x02
STATUS_A_XADA = 0x01
CTRL_0_BOOT = 0x80
CTRL_0_FIFO_EN = 0x40
CTRL_0_FTH_EN = 0x20
CTRL_0_HP_CLICK = 0x04
CTRL_0_HPIS1 = 0x02
CTRL_0_HPIS2 = 0x01
CTRL_1_AODR_MASK = 0xF0
CTRL_1_AODR_3 = 0x80
CTRL_1_AODR_2 = 0x40
CTRL_1_AODR_1 = 0x20
CTRL_1_AODR_0 = 0x10
CTRL_1_BDU = 0x08
CTRL_1_AZEN = 0x04
CTRL_1_AYEN = 0x02
CTRL_1_AXEN = 0x01
CTRL_2_ABW_1 = 0x80
CTRL_2_ABW_0 = 0x40
CTRL_2_AFS_2 = 0x20
CTRL_2_AFS_1 = 0x10
CTRL_2_AFS_0 = 0x08
CTRL_2_AST = 0x02
CTRL_2_SIM = 0x01
CTRL_5_TEMP_EN = 0x80
CTRL_5_M_RES_1 = 0x40
CTRL_5_M_RES_0 = 0x20
CTRL_5_M_ODR_2 = 0x10
CTRL_5_M_ODR_1 = 0x08
CTRL_5_M_ODR_0 = 0x04
CTRL_5_LIR2 = 0x02
CTRL_5_LIR1 = 0x01
CTRL_6_MFS1 = 0x40
CTRL_6_MFS0 = 0x20
CTRL_7_AHPM_1 = 0x80
CTRL_7_AHPM_0 = 0x40
CTRL_7_AFDS = 0x20
CTRL_7_T_ONLY = 0x10
CTRL_7_MLP = 0x04
CTRL_7_MD_1 = 0x02
CTRL_7_MD_0 = 0x01
Programme de test accelmag.py
from lsm303d_defs import LSM303D_defs
from lsm303d import LSM303D
import math
lsm303d = LSM303D(0)
lsm303d.Init()
identifiant = lsm303d.LireReg(LSM303D_defs.WHO_AM_I)
print(f"ID = {hex(identifiant)}")
while (True):
if (lsm303d.AccPret()):
ax = lsm303d.LireAxe(LSM303D_defs.OUT_X_L_A)
ay = lsm303d.LireAxe(LSM303D_defs.OUT_Y_L_A)
az = lsm303d.LireAxe(LSM303D_defs.OUT_Z_L_A)
rax = ax / LSM303D_defs.DIV_2
ray = ay / LSM303D_defs.DIV_2
raz = az / LSM303D_defs.DIV_2
mx = lsm303d.LireAxe(LSM303D_defs.OUT_X_L_M)
my = lsm303d.LireAxe(LSM303D_defs.OUT_Y_L_M)
mz = lsm303d.LireAxe(LSM303D_defs.OUT_Z_L_M)
rmx = mx / LSM303D_defs.DIV_2
rmy = my / LSM303D_defs.DIV_2
rmz = mz / LSM303D_defs.DIV_2
mm = math.sqrt(rmx*rmx+rmy*rmy+rmz*rmz)
print(f"x={rax:.3f} m/s2,y={ray:.3f} m/s2,z={raz:.3f} m/s2 x={rmx:.3f} g,y={rmy:.3f} g,z={rmz:.3f} g, M={mm:.3f}")
lsm303d.device_close()
from lsm303d_defs import LSM303D_defs
from machine import Pin,SPI
import struct
class LSM303D():
def __init__(self,numero=0):
self.numport = numero
self.spi = spi = SPI(numero, 1000000)
self.cs = Pin(SPI_CS,mode=Pin.OUT,value=1)
def device_close(self):
self.spi.deinit()
def EcrireReg(self,registre,valeur):
buffer = bytearray([ registre , valeur])
try:
self.cs(0)
self.spi.write(buffer)
finally:
self.cs(1)
def LireReg(self,registre):
buffer_out = bytearray([ registre | 0x80 ,0 ])
buffer_in = bytearray(len(buffer_out))
try:
self.cs(0)
self.spi.write_readinto(buffer_out,buffer_in)
finally:
self.cs(1)
return buffer_in[1]
def Init(self):
self.EcrireReg(LSM303D_defs.CTRL_1,LSM303D_defs.CTRL_1_AZEN|LSM303D_defs.CTRL_1_AYEN|LSM303D_defs.CTRL_1_AXEN | LSM303D_defs.CTRL_1_AODR_0)
self.EcrireReg(LSM303D_defs.CTRL_2,0)
self.EcrireReg(LSM303D_defs.CTRL_3,0)
self.EcrireReg(LSM303D_defs.CTRL_4,0)
self.EcrireReg(LSM303D_defs.CTRL_5,0)
self.EcrireReg(LSM303D_defs.CTRL_6,0)
self.EcrireReg(LSM303D_defs.CTRL_7,0)
def AccPret(self):
valeur = self.LireReg(LSM303D_defs.STATUS_A)
bitpret = valeur & LSM303D_defs.STATUS_A_ZYXADA
return bitpret == LSM303D_defs.STATUS_A_ZYXADA
def LireAxe(self,regbase):
lsb = self.LireReg(regbase)
msb = self.LireReg(regbase+1)
buffer = bytearray([msb,lsb])
resultat = (struct.unpack(">h",buffer))
return resultat[0]
Pour l'écriture, le buffer est constitué de la valeur du registre suivi du contenu du registre
Pour la lecture simultanée, le buffer d'écriture buffer_out est constitué du registre suivi de la valeur 0 qui correspond à la lecture des données. Le buffer de lecture buffer_in contient un premier octet (0xff) suivi de l'octet qui contient la réponse.
Il ne faut pas oublier de positionner le signal CS afin de sélectionner le circuit.
L'accès au bus utilise une gestion d'erreur déjà vue dans le chapitre sur la programmation en python. on a ajouté la directive finally qui permet de repositionner le signal cs même en cas d'erreur.
La conversion des deux octets en entier signé n'est pas possible avec int.bytes_from() en micropython. Ici on doit utiliser la fonction unpack du module struct. Cette fonction va décompacter le contenu du buffer dans une liste d'entiers signés 16 bits (symbole h) respectant le format big endian ( symbole >). La liste ne contient qu'un seul élément car le buffer ne contient que deux octets.