Published on

Découverte plateforme RISC V avec le GD32VF103 - Partie 2

Authors
  • Name
    Anthony Rabine

Nous voici donc avec une petite carte bien sympatique, la Longan Nano. Avançons un peu dans notre projet en ajoutant quelques fonctions bien pratiques.

On utilise le lecteur de carte SD embarqué

La carte Longan Nano dispose d'un connecteur de carte SD. Cela tombe bien, nous allons l'utiliser pour notre exemple ! Comme cela tout le monde peut suivre l'expérience car il n'y a aucune électronique à prévoir si câblage spécifique, tout est déjà relié sur les broches PB12 à PB15 ; ce qui par ailleurs les condamne sur le brochage du module.

Comment fonctionne le dialogue avec une carte mémoire de type SD Card ? Eh bien, en couches, comme d'habitude.

C'est important, car notre code va y ressembler comme deux gouttes d'eau, là où les exemples habituellement observés mélange un peu (beaucoup) les couches CD Card et SPI. Notre architecture sera ainsi facilité pour porter notre code sur d'autres plate-formes.

image

Il existe deux librairies célèbres qui implémentent le système de fichiers FAT pour une cible embarqué :

  • FatFs, vraiment petite et bourrée d'options, peu de fichiers à ajouter au projet
  • SdFat, en C++ orientée Arduino

La première est vraiment simple à porter, elle est faite pour cela : il y a quelques fonctions à implémenter et vous pouvez partir d'un exemple tout fait. La deuxième contient beaucoup de fichier et est (un peu) liée à l'écosystème Arduino et sa bibliothèque standard.

Les deux librairies supportent le système de fichiers exFat ce qui est un pré-requis dans notre cas, pour cibler le plus de cartes possibles.

Notre besoin se limitera en plus à un accès en lecture seule ce qui permettra de retirer encore du code à ces librairies si de telles options le permettent.

Donc, le travail a consisté à écrire un fichier SPI qui aura la charge d'implémenter les fonctions suivantes :

c
void spi_set_fclk_slow();
void spi_set_fclk_fast();

void spi_cs_high();
void spi_cs_low();


uint8_t xchg_spi (uint8_t dat);

/**
 * Pointer to data buffer
 * Number of bytes to receive (even number)
 */
void rcvr_spi_multi (uint8_t *buff, uint32_t btr);

/**
 * Pointer to the data
 * Number of bytes to send (even number)
 */
void xmit_spi_multi (const uint8_t *buff, uint32_t btx);

/**
 * Setup SPI block ID
 */
void spi_initialize(uint8_t id);

Rien de bien sorcier ; nous allons utiliser les codes d'exemples en utilisant le SPI en mode polling. Nous verrons plus tard pour passer en DMA si le besoin s'en fait sentir, notamment lors de l'ouverture des fichiers de sons.

La principale difficulté de la librairie FatFs est que nous devons activer le support des noms longs si nous voulons lire des partitions exFat. Or, dans ce cas, il faut intégrer un fichier dédié au codage/décodage de l'unicode qui fait ... une soixantaines de kilo-octets, la moitié de la flash interne ! Non merci 😃

Dès lors, nous allons développer des stubs, des bouchons qui ne ferons rien (ou pas grand chose) et qui remplaceront les fonctions d'origine de FatFs :

c
#include "ff.h"
#include "diskio.h"

WCHAR ff_uni2oem (	/* Returns OEM code character, zero on error */
	DWORD	uni,	/* UTF-16 encoded character to be converted */
	WORD	cp		/* Code page for the conversion */
)
{
	WCHAR c = 0;
	if (uni < 0x80) {	/* ASCII? */
		c = (WCHAR)uni;
	}

	return c;
}

WCHAR ff_oem2uni (	/* Returns Unicode character in UTF-16, zero on error */
	WCHAR	oem,	/* OEM code to be converted */
	WORD	cp		/* Code page for the conversion */
)
{
	WCHAR c = 0;
	if (oem < 0x80) {	/* ASCII? */
		c = oem;
	} 
	return c;
}

WCHAR ff_convert (WCHAR wch, UINT dir) 
{ 
    if (wch < 0x80) { 
            /* ASCII Char */ 
            return wch; 
    }  

    /* I don't support unicode it is too big! */ 
    return 0; 
}  

DWORD ff_wtoupper (DWORD uni)
{ 
    if (uni < 0x80) {      
        /* ASCII Char */ 
        if (uni >= 'a' && uni <= 'z') { 
                    uni &= ~0x20; 
            } 
            return uni; 
    }  

    /* I don't support unicode it is too big! */ 
    return 0; 
}

La couche "Carte SD" se situe dans un fichier séparé. Il est repris d'un exemple pour le STM32, sans rien toucher ou presque (nous avons supprimé les fonctions d'écriture).

Dans le corps du main, l'initialisation de FatFs consiste à monter le disque et à afficher le contenu à la racine, dans notre cas deux fichiers WAV :

c
uint32_t retries = 3;
FRESULT fr;
do 
{
    fr = f_mount(&fs, "", 1); // 0: mount successful ; 1: mount failed
    delay_1ms(10);
} while (--retries && fr);

if (fr)
{
    printf("[OST] No Card Found! Err=%d\r\n", (int)fr);
}

printf("[OST] SD Card File System = %d\r\n", fs.fs_type); // FS_EXFAT = 4

scan_files("");

Nous avons implémenté un système de re-tentatives lors de l'initialisation au cas où la carte aurait du mal à se monter, ce qui m'ai arrivé lors de mes tests. D'ailleurs, la fréquence du bus SPI a été drastiquement baisser pour que la détection fonctionne quasiment à tous les coups. En espérant que cela n'arrive pas lors du chargement des fichiers.

Dernière fonction utilitaire : un afficheur de fichiers. C'est très utile pour le débogage et voir rapidement les fichiers stockés sur la carte SD. Attention, cette fonction n'est pas récursive, elle n'affiche donc que les fichiers situés à la racine d'un répertoire à spécifier en argument.

c
FIL File[2];		/* File object */
DIR Dir;			/* Directory object */
FILINFO Finfo;

static FRESULT scan_files (
	char* path		/* Pointer to the working buffer with start path */
)
{
	DIR dirs;
	FRESULT fr;

	fr = f_opendir(&dirs, path);
	if (fr == FR_OK)
    {
		while (((fr = f_readdir(&dirs, &Finfo)) == FR_OK) && Finfo.fname[0])
        {
			if (Finfo.fattrib & AM_DIR)
            {			
                printf("/%s\r\n", Finfo.fname);
				if (fr != FR_OK) break;
			}
            else
            {
				printf("%s\r\n", Finfo.fname);
			}
		}
	}
    else
    {
        printf("[OST] Cannot open directory\r\n");
    }
    

	return fr;
}