I2C non bloquant encapsulé dans des fonctions propres !

This commit is contained in:
Samuel 2023-01-04 21:03:54 +01:00
parent ae0e476d70
commit 951e0b0c54
4 changed files with 267 additions and 9 deletions

View File

@ -6,6 +6,8 @@ Le but est de présenter un code assurant toutes les fonctions de déplacement d
Ce code est conçu pour s'exécuter sur un Raspberry Pi Pico.
Nous en profitons pour proposer également les fonctions bas niveau développée pour le Raspberry Pi Pico.
![Architecture du programme](doc/ProgrammeHolonome2023.png)
Voici un bref paragraphe explicatif pour chaque bloc fonctionnel.
@ -63,4 +65,16 @@ Cette fonction permet de parcourir une trajectoire en tenant compte des contrain
2. Calcul de l'avancement sur la trajectoire.
3. Obtention de la nouvelle consigne de position.
Pour se déplacer sur une trajectoire, cette fonction utilise les outils de gestion des trajectoires définis dans les fichiers `trajectoire*`.
Pour se déplacer sur une trajectoire, cette fonction utilise les outils de gestion des trajectoires définis dans les fichiers `trajectoire*`.
Fonctions bas niveau
===================
I2C Maître, non bloquant
------------------------
Le fichier [i2c_maitre.c](i2c_maitre.c) propose une implémentation non-bloquante de l'i2c. L'utilisation s'effectue ainsi :
- La fonction *i2c_gestion* doit être appelée régulièrement.
- La fonction *i2c_transmission* (ou une fonction englobant celle-ci, telle que *i2c_lire_registre_nb*) doit être appelée jusqu'à ce qu'elle renvoie I2C_SUCCES.
Pour un exemple concret lisant une valeur dans une mémoire i2c, voir *test_i2c_lecture_pico_annex_nb2* de [test.c](test.c)

41
Test.c
View File

@ -45,6 +45,7 @@ int test_i2c_bus(void);
void affiche_localisation(void);
int test_i2c_lecture_pico_annex();
int test_i2c_lecture_pico_annex_nb();
int test_i2c_lecture_pico_annex_nb2();
// Mode test : renvoie 0 pour quitter le mode test
@ -153,7 +154,7 @@ int mode_test(){
case 'X':
case 'x':
while(test_i2c_lecture_pico_annex_nb());
while(test_i2c_lecture_pico_annex_nb2());
break;
case PICO_ERROR_TIMEOUT:
@ -322,6 +323,44 @@ int test_i2c_lecture_pico_annex_nb(){
return test_continue_test();
}
int test_i2c_lecture_pico_annex_nb2(){
i2c_maitre_init();
uint8_t tampon[10];
uint8_t registre=0;
uint8_t adresse = 0x17;
uint32_t time_i2c[5];
const uint8_t T_MAX_I2C = 10;
enum i2c_resultat_t retour_i2c = I2C_EN_COURS;
time_i2c[0] = time_us_32();
time_i2c[2] = 0;
while(retour_i2c == I2C_EN_COURS){
time_i2c[1] = time_us_32(); // Pour mesurer le temps d'execution
i2c_gestion(i2c0);
retour_i2c = i2c_lire_registre_nb(adresse, registre, tampon, T_MAX_I2C);
time_i2c[2] += time_us_32() - time_i2c[1]; // Pour mesurer le temps d'execution
sleep_us(100); // Attente, ou le reste du code
}
time_i2c[3] = time_us_32() - time_i2c[0];
// Affichage
for(int i=0; i<T_MAX_I2C; i++){
printf("%c", tampon[i]);
}
printf("\n");
for(int i=0; i<T_MAX_I2C; i++){
printf("%2x ", tampon[i]);
}
printf("\n");
printf("Temps lecture : %u microsecondes, temps specifique i2c : %u microsecondes.\n", time_i2c[3], time_i2c[2]);
return test_continue_test();
}
int test_i2c_bus(){
// Adresse I2C : 0b0100 000 R/W
// Lecture des broches sur les registres 0 et 1

View File

@ -7,8 +7,30 @@
#define I2C_SDA_PIN 16
#define I2C_SCL_PIN 17
#define I2C_NB_MAX_TAMPON 20
enum i2c_statu_t{
I2C_STATU_LIBRE,
I2C_STATU_OCCUPE
} i2c_statu_i2c0;
uint16_t I2C_tampon_envoi[I2C_NB_MAX_TAMPON];
uint8_t I2C_tampon_reception[I2C_NB_MAX_TAMPON];
uint16_t I2C_nb_a_envoyer, I2C_nb_a_recevoir;
uint8_t adresse_7_bits;
uint32_t i2c_error_code; // value of i2c->hw->tx_abrt_source if anything wrong happen, 0 if everything was fine.
enum transaction_statu_t{
TRANSACTION_EN_COURS,
TRANSACTION_TERMINEE
} statu_emission, statu_reception;
void i2d_set_adresse_esclave(uint8_t _adresse_7bits);
void i2c_charger_tampon_envoi(uint8_t* emission, uint16_t nb_envoi, uint16_t nb_reception);
enum i2c_resultat_t i2c_transmission(uint8_t _adresse_7bits, uint8_t* emission, uint16_t nb_envoi, uint16_t nb_reception);
void i2c_maitre_init(void){
stdio_init_all();
//stdio_init_all();
i2c_init(i2c0, 100 * 1000);
printf("Initialisation des broches\n");
@ -22,9 +44,187 @@ void i2c_maitre_init(void){
printf("%d et %d en I2C\n", I2C_SDA_PIN, I2C_SCL_PIN);
gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C);
gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C);
i2c_statu_i2c0 = I2C_STATU_LIBRE;
}
/// @brief Pour l'instant bloquant, mais devrait passer en non bloquant bientôt
/// @brief Fonction à appeler régulièrement ou en interruption.
/// @param i2c
void i2c_gestion(i2c_inst_t *i2c){
// on veut gérer l'i2c avec cette fonction.
// 2 cas :
// - Soit écriture simple (plusieurs octets (W))
// - Soit écriture + lecture (Adresse (W), registre (W), données (R))
// Pour écrire 1 octet, i2c->hw->data_cmd = xxx, (avec CMD:8 à 0, )
// Pour lire 1 octet, i2c->hw->data_cmd = xxx (avec CMD:8 à 1)
// Il faut mettre CMD:9 à 1 pour le dernier octet.
// Envoi des données (ou des demandes de lecture)
static uint16_t index_envoi=0, index_reception=0;
// Acquitement des erreurs, pas 100% fonctionnel ! TODO !
if(i2c->hw->tx_abrt_source !=0){
i2c_error_code = i2c->hw->tx_abrt_source;
printf("Erreur I2C tx_abrt_source : %#x\n", i2c_error_code);
// on efface l'erreur en lisant le registre clr_tx_abrt
index_envoi = i2c->hw->clr_tx_abrt;
I2C_nb_a_envoyer = 0;
index_reception = 0;
I2C_nb_a_recevoir = 0;
statu_emission = TRANSACTION_TERMINEE;
statu_reception = TRANSACTION_TERMINEE;
i2c_statu_i2c0 = I2C_STATU_LIBRE;
printf("Erreur acquitee\n");
}
while( (index_envoi < I2C_nb_a_envoyer) && (i2c_get_write_available(i2c)) ){
bool restart = false;
bool last = false;
if (index_envoi == 0){
// Début de l'envoi, assurons nous d'avoir la bonne adresse de l'esclave
i2c->hw->enable = 0;
i2c->hw->tar = adresse_7_bits;
i2c->hw->enable = 1;
}else{
// Passage de l'écriture à la lecture, on envoie un bit de restart.
if( !(I2C_tampon_envoi[index_envoi-1] & I2C_IC_DATA_CMD_CMD_BITS) &&
(I2C_tampon_envoi[index_envoi] & I2C_IC_DATA_CMD_CMD_BITS)){
restart = true;
}
}
if(index_envoi + 1 == I2C_nb_a_envoyer){
// Fin de la trame, nous devons envoyer un bit de stop.
last = true;
}
i2c->hw->data_cmd =
I2C_tampon_envoi[index_envoi] |
bool_to_bit(restart) << I2C_IC_DATA_CMD_RESTART_LSB |
bool_to_bit(last) << I2C_IC_DATA_CMD_STOP_LSB;
if(last){
statu_emission = TRANSACTION_TERMINEE;
index_envoi = 0;
I2C_nb_a_envoyer = 0;
//printf("I2C emission terminee\n");
}else{
index_envoi++;
}
}
// Réception des données - Lecture des données présentes dans le tampon
while( (index_reception < I2C_nb_a_recevoir) && (i2c_get_read_available(i2c)) ){
I2C_tampon_reception[index_reception] = (uint8_t) i2c->hw->data_cmd;
index_reception++;
}
if(index_reception == I2C_nb_a_recevoir && I2C_nb_a_recevoir > 0 ){
statu_reception = TRANSACTION_TERMINEE;
index_reception = 0;
I2C_nb_a_recevoir = 0;
}
if(statu_reception == TRANSACTION_TERMINEE && statu_emission == TRANSACTION_TERMINEE){
i2c_statu_i2c0 = I2C_STATU_LIBRE;
}
}
/// @brief Charge le tampon d'émission pour pré-mâcher le travail à la fonction i2c_gestion
/// @param emission
/// @param nb_envoi
/// @param nb_reception
void i2c_charger_tampon_envoi(uint8_t* emission, uint16_t nb_envoi, uint16_t nb_reception){
// Données à envoyer
for(unsigned int index=0; index<nb_envoi; index++){
I2C_tampon_envoi[index] = (uint16_t) emission[index];
}
// Données à lire
for(unsigned int index=0; index<nb_reception; index++){
I2C_tampon_envoi[nb_envoi + index] = (uint16_t) 0x0100;
}
}
/// @brief Stock l'adresse de l'esclave avec lequel communiquer
/// @param _adresse_7bits
void i2d_set_adresse_esclave(uint8_t _adresse_7bits){
adresse_7_bits =_adresse_7bits;
}
/// @brief Initialise la transmission I2, sur l'i2c0. Une transmission se compose de 2 trames I2C, une pour écrire (Adresse + données), une pour lire
/// Si nb_reception = 0, alors la trame pour lire ne sera pas envoyée.
/// @param emission : données à envoyer
/// @param nb_envoi : nombre de données à envoyer
/// @param nb_reception : nombre de données à recevoir
/// @return 1 en cas d'échec, 0 en cas de succès
enum i2c_resultat_t i2c_transmission(uint8_t _adresse_7bits, uint8_t* emission, uint16_t nb_envoi, uint16_t nb_reception){
static enum m_statu_t{
I2C_STATU_INIT,
I2C_STATU_EN_COURS,
}m_statu = I2C_STATU_INIT;
switch(m_statu){
case I2C_STATU_INIT:
// I2C libre ?
if(i2c_statu_i2c0 == I2C_STATU_OCCUPE){
return I2C_EN_COURS;
}
// Alors il est à nous !
i2c_statu_i2c0 = I2C_STATU_OCCUPE;
statu_emission = TRANSACTION_EN_COURS;
statu_reception = TRANSACTION_EN_COURS;
i2c_error_code = 0;
i2d_set_adresse_esclave(_adresse_7bits);
i2c_charger_tampon_envoi(emission, nb_envoi, nb_reception);
// Nous devons envoyer aussi une commande pour chaque octet à recevoir.
I2C_nb_a_envoyer = nb_envoi + nb_reception;
I2C_nb_a_recevoir = nb_reception;
// On appelle le fonction gestion pour gagner du temps.
i2c_gestion(i2c0);
m_statu = I2C_STATU_EN_COURS;
break;
case I2C_STATU_EN_COURS:
if(i2c_statu_i2c0 == I2C_STATU_LIBRE){
m_statu = I2C_STATU_INIT;
if(i2c_error_code){
return I2C_ECHEC;
}else{
return I2C_SUCCES;
}
}
break;
}
return I2C_EN_COURS;
}
/// @brief
enum i2c_resultat_t i2c_lire_registre_nb(uint8_t adresse_7_bits, uint8_t registre, uint8_t * reception, uint8_t len){
uint8_t emission[1];
emission[0] = registre;
enum i2c_resultat_t i2c_resultat;
i2c_resultat = i2c_transmission(adresse_7_bits, emission, 1, len);
if(i2c_resultat == I2C_SUCCES){
for(uint32_t i = 0; i < len; i++){
reception[i] = I2C_tampon_reception[i];
}
return I2C_SUCCES;
}else if(i2c_resultat == I2C_ECHEC){
return I2C_ECHEC;
}
return I2C_EN_COURS;
}
/// @brief Pour l'instant bloquant, mais devrait passer en non bloquant bientôt => Non, voir i2c_lire_registre_nb
/// @param adresse_7_bits
/// @param
/// @return 0: en cours,

View File

@ -1,9 +1,14 @@
#define I2C_EN_COURS 0
#define I2C_SUCCES 1
#define I2C_ECHEC -1
#include "pico/stdlib.h"
#include "hardware/i2c.h"
enum i2c_resultat_t {
I2C_EN_COURS,
I2C_SUCCES,
I2C_ECHEC
};
void i2c_maitre_init(void);
void i2c_gestion(i2c_inst_t *i2c);
enum i2c_resultat_t i2c_lire_registre_nb(uint8_t adresse_7_bits, uint8_t registre, uint8_t * reception, uint8_t len);
int i2c_ecrire_registre(char adresse_7_bits, char registre, char valeur_registre);
int i2c_lire_registre(char adresse_7_bits, char registre, char * reception, char len);
int i2c_lire_registre(char adresse_7_bits, char registre, char * reception, char len);