HowTo FT232H
Installer le driver et les librairies C de chez FTDI sur Linux
-
Télécharger le driver D2XX Linux x64 sur le site de FTDI
-
Suivre les indications du fichier
README.txt
présent dans l’archiveComme mentionné dans le
README.txt
, afin de rendre possible l’utilisation du driver D2XX, il faut veiller à désactiver le moduleftdi_sio
qui est chargé automatiquement lorsqu’on connecte un FT232H à la machine.Une des méthodes consiste à décharger les modules
ftdi_sio
etusbserial
avec la commandermmod
. Cependant, une méthode plus élégante est présentée dans Résolution automatique du conflit avec le driverftdi_sio
-
S’assurer que le driver fonctionne en compilant et exécutant l’exemple situé dans
/release/examples/Simple/
Le programme doit être exécuté avec
sudo
-
Télécharger la librairie MPSSE
-
Suivre le indications du fichier
README.txt
Accès au FT232H depuis l’espace utilisateur (i.e. sans sudo
) & résolution automatique du conflit avec le driver ftdi_sio
Ces 2 fonctionnalités peuvent être obtenues à l’aide de règles udev
.
Une règle udev
est un ensemble d’instructions qui permet au système Linux de gérer automatiquement les périphériques connectés (ex. : définir les permissions, exécuter des scripts lors de la (dé)connexion d’appareils…).
Les règles udev
sont stockées dans des fichiers avec l’extension .rules
, principalement dans trois répertoires :
-
/etc/udev/rules.d/
(règles définies par l’utilisateur) -
/run/udev/rules.d/
(règles volatiles) -
/lib/udev/rules.d/
(règles fournies par la distribution)
Accès au FT232H depuis l’espace utilisateur
La règle va consister à attribuer les droits rw
au groupe plugdev
sur le périphérique associé au FT232H.
Le groupe plugdev
est un groupe système spécial utilisé dans certaines distributions Linux, notamment Ubuntu et Debian, pour gérer les permissions d’accès aux périphériques amovibles
Procédure pour créer et appliquer la règle :
-
Créer un fichier
/etc/udev/rules.d/10-ft232h.rules
avec le contenu suivant :/etc/udev/rules.d/10-ft232h.rulesSUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6014", GROUP="plugdev", MODE="0660"
La règle cible le périphérique usb dont les PID/VID sont 0x6014/0x0403 pour lui appliquer les droits 0660 pour le groupe
plugdev
-
Appliquer la règle :
sudo udevadm control --reload-rules && sudo udevadm trigger
-
Vérifier que l’utilisateur est bien dans le groupe
plugdev
$ groups jdoe adm cdrom sudo dip plugdev lpadmin sambashare
-
Décharger les modules
ftdi_sio
etusbserial
sudo rmmod ftdi_sio & sudo rmmod usbserial
-
Exécuter — sans
sudo
— le programme de test inclus dans l’archive de la librairielibmpsse
(→LibMPSSE_1.0.5/Linux/release/test/test.c
) pour vérifier la bonne application de la règleudev
:$ ./test (1) Version Check libmpsse: 00010005 libftd2xx: 00010427 Test case 1 - I2C_GetNumChannels I2C_GetNumChannels returned 0; channels=1 Test case 2 - I2C_GetChannelInfo I2C_GetChannelInfo returned 0 for channel =0 Flags=0x2 Type=0x8 ID=0x4036014 LocId=0x205 SerialNumber=FT9H67OF Description=USB <-> Serial Converter ftHandle=(nil) (should be zero) [...]
1 Ce programme de test met en œuvre l’interface I2C du FT232H et non l’interface SPI utilisée dans le projet mais l’important ici est de vérifier que le programme fonctionne sans sudo
.
Résolution automatique du conflit avec le driver ftdi_sio
🕮 Sources :
Comme indiqué plus haut, le fait de brancher le FT232H va automatiquement provoquer le chargement des modules ftdi_sio
et usbserial
.
Les modules ftdi_sio
et usbserial
vont faire en sorte de considérer le FT232H comme un adaptateur USB vers liaison série. Or, le FT232H est un adaptateur USB plus complet qui permet une interface avec des GPIOS, un bus I2C et un bus SPI.
De façon à pouvoir utiliser le driver D2XX de FTDI qui permet l’accès à ces fonctionnalités, il faut empêcher l’utilisation du driver ftdi_sio
avec le FT232H.
Ceci peut se faire en déchargeant systématiquement les modules avec la commande sudo rmmod ftdi_sio & sudo rmmod usbserial
mais cela empêche alors l’utilisation d’éventuels autres adaptateurs USB/série FTDI branchés au système.
Une solution plus élégante consiste à créer une règle udev
qui permettra de “casser” l’association entre le FT232H et le driver ftdi_sio
après son branchement sur le système.
Procédure pour créer et appliquer la règle :
-
Débrancher et rebrancher le FT232H. Ceci provoquera non seulement le chargement automatique des modules
ftdi_sio
etusbserial
mais également l’association de ces drivers au FT232H.
Ceci peut se vérifier avec la commande suivante :$ lsusb -tv /: Bus 001.Port 001: Dev 001, Class=root_hub, Driver=ohci-pci/12p, 12M ID 1d6b:0001 Linux Foundation 1.1 root hub | Port 001: Dev 002, If 0, Class=Human Interface Device, Driver=usbhid, 12M ID 80ee:0021 VirtualBox USB Tablet /: Bus 002.Port 001: Dev 001, Class=root_hub, Driver=ehci-pci/12p, 480M ID 1d6b:0002 Linux Foundation 2.0 root hub | Port 001: Dev 011, If 0, Class=Vendor Specific Class, Driver=ftdi_sio, 480M (1) ID 0403:6014 Future Technology Devices International, Ltd FT232H Single HS USB-UART/FIFO IC
1 le driver ftdi_sio
a été associé au FT232H -
Ajouter au fichier
/etc/udev/rules.d/10-ft232h.rules
créé au paragraphe précédent la règle suivante :/etc/udev/rules.d/10-ft232h.rules# [...] SUBSYSTEM=="usb", DRIVER=="ftdi_sio", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6014", RUN+="/bin/sh -c 'echo $kernel > /sys/bus/usb/drivers/ftdi_sio/unbind"
-
Appliquer la règle :
sudo udevadm control --reload-rules && sudo udevadm trigger
-
Débrancher et rebrancher le FT232H
-
Vérifier que le driver
ftdi_sio
n’est plus associé au FT232H~$ lsusb -tv /: Bus 001.Port 001: Dev 001, Class=root_hub, Driver=ohci-pci/12p, 12M ID 1d6b:0001 Linux Foundation 1.1 root hub | Port 001: Dev 002, If 0, Class=Human Interface Device, Driver=usbhid, 12M ID 80ee:0021 VirtualBox USB Tablet /: Bus 002.Port 001: Dev 001, Class=root_hub, Driver=ehci-pci/12p, 480M ID 1d6b:0002 Linux Foundation 2.0 root hub | Port 001: Dev 010, If 0, Class=Vendor Specific Class, Driver=[none], 480M (1) ID 0403:6014 Future Technology Devices International, Ltd FT232H Single HS USB-UART/FIFO IC
1 le driver ftdi_sio
n’est plus associé au FT232H
Mise en œuvre avec un rhéostat numérique MCP4142
Ci-dessous un exemple de mise en œuvre du module FT232H pour dialoguer avec un rhéostat numérique Microchip réf. MCP4142 .
Schéma :
Programme :
/**
* Programme de test qui met en oeuvre la librairie libmpsse et le driver
* D2XX de chez FTDI pour piloter un rheostat numérique de chez
* Microship (MCP4142-10k) à travers un FT232H.
*
* Le programme fixe d’abord la valeur du rheostat à sa valeur médiane
* (0x40 : ≈5 kΩ) en appelant la fonction 'Write Data' puis
* l’incrémente toutes les secondes jusqu’à sa valeur maximale (0x80)
* avec la commande 'Increment Wiper'. A chaque incrément, on lit la valeur
* effective du rheostat avec la commande 'Read Data'.
* Dès que la valeur atteint la valeur max. du rhéostat (0x80 : ≈10kΩ),
* le programme quite.
* Pendant l’incrémentation, l’ohmètre doit vérifier l’augmentation de la résistance
* de ≈5kΩ à ≈10kΩ entre les broches 5 et 6.
*
* Ce programme met en oeuvre les fonctions suivantes de la libmpsse :
* - Init_libMPSSE()
* - SPI_GetNumChannels()
* - SPI_GetChannelInfo()
* - SPI_OpenChannel()
* - SPI_InitChannel()
* - SPI_Write()
* - SPI_ReadWrite()
* - SPI_CloseChannel()
* - Cleanup_libMPSSE()
*
* compilation : gcc -o mcp4142-cli mcp4142-cli.c -lmpsse
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <ftd2xx.h>
#include <WinTypes.h>
#include <libmpsse_spi.h>
int main() {
FT_STATUS status;
// Initialiser la librairie
Init_libMPSSE();
// Récupérer le nombre de canaux disponibles
uint32_t numChannels;
status = SPI_GetNumChannels(&numChannels);
if(status != FT_OK) {
printf("Impossible de déterminer le nombre de canaux MPSSE.\nBye!");
Cleanup_libMPSSE();
exit(-1);
} else {
printf("Nombre de canaux SPI trouvés : %d\n", numChannels);
}
// Afficher le détail des canaux trouvés
FT_DEVICE_LIST_INFO_NODE channelInfo;
uint32_t ft232hChannel = -1;
for(int i = 0; i < numChannels; i++) {
status = SPI_GetChannelInfo(i, &channelInfo);
if(status != FT_OK) {
printf("Impossible d’obtenir les détails du canal MPSSE n°%d.\nBye!", i);
Cleanup_libMPSSE();
exit(-1);
} else {
printf("Canal n° %d\n", i);
printf(". Description\t: %s\n", channelInfo.Description);
printf(". N° de série\t: %s\n", channelInfo.SerialNumber);
printf(". ID\t\t: 0x%08x\n", channelInfo.ID);
printf(". Flags\t\t: 0x%08x\n", channelInfo.Flags); /* Voir
https://ftdichip.com/wp-content/uploads/2023/09/D2XX_Programmers_Guide.pdf
pour la définition des flags :
"The flag value is a 4-byte bit map containing miscellaneous data as defined in Appendix A – Type
Definitions. Bit 0 (least significant bit) of this number indicates if the port is open (1) or closed (0). Bit 1
indicates if the device is enumerated as a high-speed USB device (2) or a full-speed USB device (0). The
remaining bits (2 - 31) are reserved."
*/
printf(". Type\t\t: %d\n", channelInfo.Type); /* Voir
https://docs.rs/libftd2xx/latest/libftd2xx/enum.DeviceType.html
pour la liste des types (8=FT232H, 5=FT232R …)
*/
if(channelInfo.Type == 8) {
ft232hChannel = i;
}
}
}
// Ouvrir le canal
FT_HANDLE handle;
status = SPI_OpenChannel(ft232hChannel, &handle);
if(status != FT_OK) {
printf("Impossible d’ouvrir le canal MPSSE n°%d associé au FT232H.\nBye!", ft232hChannel);
Cleanup_libMPSSE();
exit(-1);
}
// Initialiser le canal
ChannelConfig channelConf;
channelConf.ClockRate = 1000000; // 1 MHz
channelConf.LatencyTimer = 16; /* Voir
https://ftdichip.com/wp-content/uploads/2020/08/AN232B-04_DataLatencyFlow.pdf
pour une explication du latency timer.
Ce délai n’a de sens que pour la lecture.
La valeur 16ms est la valeur par défaut.
:TODO: tenter de comprendre la signification réelle de ce paramètre
*/
channelConf.configOptions = SPI_CONFIG_OPTION_MODE0 // SPI Mode 0
| SPI_CONFIG_OPTION_CS_DBUS3 // xDBUS3 est utilisé comme Chip Select (CS)
| SPI_CONFIG_OPTION_CS_ACTIVELOW // Chip Select actif à l’état bas
;
channelConf.Pin = 0x00000000; /* Directions et valeurs des broches après
l’appel à SPI_InitChannel et SPI_CloseChannel.
BIT7-BIT0 : Direction des broches après SPI_InitChannel (0=Input, 1=Output)
BIT15-BIT8 : Valeurs des broches après SPI_InitChannel (0=Low, 1=High)
BIT23-BIT16 : Direction des broches après SPI_CloseChannel (0=Input, 1=Output)
BIT31-BIT24 : Valeurs des broches après SPI_CloseChannel (0=Low, 1=High)
*/;
status = SPI_InitChannel(handle,&channelConf);
if (status != FT_OK)
{
printf("Echec de l’initialisation du canal MPSSE n°%d associé au FT232H.\nBye!\n", ft232hChannel);
// Fermer le canal & la librairie
status = SPI_CloseChannel(handle);
if(status != FT_OK) {
printf("Erreur lors de la fermeture du canal MPSSE n°%d associé au FT232H.\n", ft232hChannel);
}
Cleanup_libMPSSE();
exit(-1);
}
// Fixer la valeur du rheostat du MCP4142 à sa valeur médiane (0x40 ⇒ ~5kOhms)
uint8_t inBuffer[2];
uint8_t outBuffer[2] = {0x00, 0x40};
uint32_t sizeToTransfer = 2;
uint32_t sizeTransferred;
uint32_t transferOptions = SPI_TRANSFER_OPTIONS_SIZE_IN_BYTES // BIT0 : taille donnée en octets
| SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE // BIT1 : CS activé avant le transfert
| SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE // BIT2 : CS désactivé après le transfert
;
status = SPI_Write(handle, outBuffer, sizeToTransfer, &sizeTransferred, transferOptions);
if (status != FT_OK)
{
printf("Echec d’écriture du rheostat sur le canal MPSSE n°%d associé au FT232H.\nBye!\n", ft232hChannel);
// Fermer le canal & la librairie
status = SPI_CloseChannel(handle);
if(status != FT_OK) {
printf("Erreur lors de la fermeture du canal MPSSE n°%d associé au FT232H.\n", ft232hChannel);
}
Cleanup_libMPSSE();
exit(-1);
} else {
printf("valeur écrite avec succès\n");
}
int quit = 0;
while(!quit) {
// Incrémenter la valeur du rheostat
outBuffer[0] = 0x04;
sizeToTransfer = 1;
status = SPI_Write(handle, outBuffer, sizeToTransfer, &sizeTransferred, transferOptions);
if (status != FT_OK)
{
printf("Echec d’incrémentation de la valeur du rheostat sur le canal MPSSE n°%d associé au FT232H.\nBye!\n", ft232hChannel);
// Fermer le canal & la librairie
status = SPI_CloseChannel(handle);
if(status != FT_OK) {
printf("Erreur lors de la fermeture du canal MPSSE n°%d associé au FT232H.\n", ft232hChannel);
}
Cleanup_libMPSSE();
exit(-1);
} else {
printf("Valeur incrémentée avec succès !\n");
}
// Lire la valeur du rhéostat du MCP4142
outBuffer[0] = 0x0C;
outBuffer[1] = 0x00;
sizeToTransfer = 2;
status = SPI_ReadWrite(handle, inBuffer, outBuffer, sizeToTransfer, &sizeTransferred, transferOptions);
if (status != FT_OK)
{
printf("Echec de lecture du rheostat sur le canal MPSSE n°%d associé au FT232H.\nBye!\n", ft232hChannel);
// Fermer le canal & la librairie
status = SPI_CloseChannel(handle);
if(status != FT_OK) {
printf("Erreur lors de la fermeture du canal MPSSE n°%d associé au FT232H.\n", ft232hChannel);
}
Cleanup_libMPSSE();
exit(-1);
} else {
uint16_t rValue = (uint16_t)(inBuffer[0] << 8) | inBuffer[1];
printf("valeur lue : 0x%04x\n", rValue);
if(rValue == 0xFE80) {
break;
}
}
sleep(1);
}
// Fermer le canal & la librairie
status = SPI_CloseChannel(handle);
if(status != FT_OK) {
printf("Erreur lors de la fermeture du canal MPSSE n°%d associé au FT232H.\n", ft232hChannel);
}
Cleanup_libMPSSE();
printf("Programme terminé.\nBye !");
return 0;
}
Ressources
-
Application Note AN_178 User Guide For libMPSSE - SPI
→ documentation de la librairielibmpsse
-
Série d’article sur la programmation du FTDI232H en Python :
-
→ proposition de solution au problème de la non détection du FT232H depuis l’API MPSSE qui consiste à reprogrammer l’EEPROM à l’aide du programme Windows FT_Prog.
🞄 🞄 🞄