Comment programmer/acceder au SPI1

Bonjour.
Je travaille sur un atmega328pb dont j'aimerais pouvoir utiliser le SPI1 pour utilisation d'une carte SD (le SPI0 marche, mais je souhaite le reserver pour la programmation de l'uC).

J'ai fais pas mal de recherche sur le net, mais je ne trouve pas de bibliothèque toute faite pour utiliser ce port.
j'ai trouvé ce sujet qui en parle, mais la biblio est très specifique, je ne peux l'utiliser
https://stackoverflow.com/questions/74410725/concurrent-operation-of-spi0-and-spi1-on-atmega328pb

Sachant que je n'ai encore jamais programmé de librarie pour arduino, j'aimerais votre aide pour savoir comment m'y prendre pour utiliser ce port.

Je suppose donc, que le plus facile est de s'inspirer de la bibliotheque "SD", mais je ne sais pas du tout par quoi commencer (quel(s) fichier(s) doit être modifié).

Pouvez-vous m'aiguillez svp ?
Ca sera l'occasion pour moi de m'initier au language arduino et comprendre les liens entre fichiers.h .ino et .cpp

Merci d'avance.

Code essayé et qui ne fonctionne pas ("initializing failed" à chaque tentative, je n'ai pas de signal sur la broche PE2) :

#include <SPI1.h>
#include <SD.h>

#define carteSD PIN_PE2 // pin SS1

Sd2Card card;
SdVolume volume;
SdFile root;

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; 
  }

  Serial.print("\nInitializing SD card...");

  if (!card.init(SPI_HALF_SPEED, carteSD)) { 
    Serial.println("initialization failed. Things to check:");
    Serial.println("* is a card inserted?");
    Serial.println("* is your wiring correct?");
    Serial.println("* did you change the chipSelect pin to match your shield or module?");
    while (1);
  } else {
    Serial.println("Wiring is correct and a card is present.");
  }

Salut.
La librairie SD utilise le SPI de l'ATMega328, qui es équivalent au SPI0 l'ATMega328 je suppose.
Le fait d'inclure SPI1.h ne va rien apporter.
Même la librairie SDFAT, qui est plus récente et maintenue que la librairie SD, ne prévoit pas de paramétrage du bus SPI à utiliser, en tous cas pour les AVR.
En tous cas c'est ce que je vois ici :

inline void SdSpiArduinoDriver::begin(SdSpiConfig spiConfig) {
  (void)spiConfig;
  SPI.begin();
}

spiConfig est une variable ignorée. begin() attaque directement SPI, donc SPI0.

  1. il n'existe pas de langage arduino, c'est très clair.

  2. Le langage utilisé par Arduino pour son ensemble de fonctions et de classes est tout simplement le langage C++.

  3. Le mieux est au minimum de lire un tuto d'initiation, même raz les paquerettes, au C/C++.
    Un autre tuto est très utile celui d'Eskimon, il est complet dans le sens ou il traite aussi bien de programation que de la façon de gérer le matériel.
    eskimon.fr

Qu'est-ce qu'une Bibliothèque :
Je pars du principe que l'on utilise un logiciel qui s'appelle un Environnement de Développement Intégré, soit EDI en français et IDE en anglais.
Comme l'IDE Arduino.

La différence entre la programation en fichiers séparés et une bibliothèque est que les fichiers de bibliothèque sont rangés dans un emplacement connu de l'IDE. Le compilateur sait où aller les chercher.
Un autre coté bien pratique est que l'IDE Arduino possède un gestionnaire de bibliothèque qui simplifie bien les débuts.

Attention : le fichier ma_bibliotheque.h n'est pas une bibliothèque.
Une bibliothèque est un ensemble de fichiers de déclaration (*.h) et de fichiers de code (*.cpp)

Ecrire
#include <ma_bibliotheque.h>
Indique simplement au compilateur qu'il faut ajouter au code le contenu du fichier h (h pour header ou en-tête) mais aussi qu'il aille chercher les différents autres fichiers de la bibliothèque et qu'il les compile.

Fichiers ino , h ,cpp
L'ide arduino ne demande que d'écrire une fonction setup() et une fonction loop().
Mais dans ton dos elle créé un VRAI fichier cpp à partir du fichier *.ino.
On peut trouver le squellette de ce fichier main.cpp dans les les fichiers de l'IDE.

/*
  main.cpp - Main loop for Arduino sketches
  Copyright (c) 2005-2013 Arduino Team.  All right reserved.
..................................................................................................................
  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include <Arduino.h>

// Declared weak in Arduino.h to allow user redefinitions.
int atexit(void (* /*func*/ )()) { return 0; }

// Weak empty variant initialization function.
// May be redefined by variant files.
void initVariant() __attribute__((weak));
void initVariant() { }

void setupUSB() __attribute__((weak));
void setupUSB() { }

int main(void)
{
	init();

	initVariant();

#if defined(USBCON)
	USBDevice.attach();
#endif
	
	setup();
    
	for (;;) {
		loop();
		if (serialEventRun) serialEventRun();
	}
        
	return 0;
}

C'est ce fichier qui est transmis au compilateur.
Ce fichier appelle des fonctions dont arduino ne te signale pas l'existance comme ini() et initvariant().
Ces fonctions configurent le microcontroleur.
La configuration à la sauce arduino est une configuration pour que les débutants aient le moins de problèmes.

Fichiers *.h
Si tu lis un tuto de C/C++ tu verra que le compilateur doit être informé de l'existance de variables ou de fonctions avant qu'elles soient appellées dans le programme, sinon il génère des erreurs.
Les fichiers h sont là pour cela.

Fichiers *.cpp
Ce sont les vrais fichiers qui contiennent le code.

Cas particulier de l'IDE avec plusieurs fichiers ino dans des onglets.
C'est de la cosmétique. Cela permet de découper un programme trop long en petit bouts.
Au final tous les fichiers ino sont ajoutés au fichier ino principal dans l'ordre alphabétique de leur nom.
C'est parfois pratique mais ce n'est pas de la programation en fichiers séparés.
Dans la programmation en fichiers séparés chaque fichier est indépendant et peu être réutilisé : c'est très proche de la notion de bibliothèque.

Cas particulier de l'atmega328pB.
Cette évolution de l'atmega328p n'a jamais été prise en charge par Arduino qui s'est toujours limité au 328p.

Il se trouve que Microchip a annoncé le 328p "Non recommandé pour les nouveaux produits" et à multiplié son prix par 2 par rapport au 328pB.
Bien évidement les fabricants de clones, une fois leurs stocks de 328p épuisés ne recommandent plus que des 328pB.

Il doit commencer à y avoir des adaptations non officielles du 328pB au fonctions arduino mais que valent-elles ?
En attendant le 328pB peut se gérer comme un 328p mais sans les petits plus du 328pB.

1 Like

J'ajouterai que réserver le SPI0 pour la programmation du µcontrôleur n'est utile que pour le chargement du bootloader. Si l'on désire charger le bootloader en ayant la carte SD en place, cela va t-il poser problème ? Si oui, retirer la carte SD. Si non, let's go.
A moins d'avoir envie de se passer de bootloader bien entendu. Mais se passer de bootloader pour économiser quelques Ko de Flash en vaut-il la chandelle, surtout si cela implique une lourde modification de la librairie SD/SDFAT ?
Sincèrement, étant donné que apparemment :

ton expérience en logiciel étant assez faiblarde, charge un bootloader et profites-en.

Bonsoir @hbachetti.
En fait, en incluant SPI1.h, je pensais que j'aurai eu une erreur de compilation, mais non, l'IDE est allé le chercher dans la librairie de minicore, dossier (\MiniCore\hardware\avr\2.2.2\libraries) dans lequel existe bien un SPI1.h

J'ai donc pensé qu'il suffisait de modifier quelques lignes par ci par là de la librairie SD, pour communiquer avec les bons pins et utiliser pouvoir utiliser SPI.h ... ce n'est donc pas le cas :confused:

Je suis bien d'accord avec ta remarque, mais le problème c'est que les pins du SPI0 ne sont volontairement plus accessibles sur mon pcb après upload du bootloader, c'est pourquoi je prevoyais utiliser le SPI1 pour de la carte SD...

Merci @68tjs pour tes explications, une bonne mise en bouche pour moi pour entrer plus en profondeur dans tout cela.
Je crois bien que je n'aurai d'autre choix, que de me remettre dans le C++. Ca ne pourra que me faire du bien pour comprendre et aller plus loin dans l'exploitation des uC.
En attendant d'avoir le temps de pouvoir m'y mettre, j'espère pouvoir trouver une aide la dessus, sinon je serai pour l'instant obligé de faire une croix sur l'exploitation de ce SPI1

Bonjour,

Si, c'est bien le cas (du moins en théorie). Il "suffit" de remplacer les accès à SPI par des accès à SPI1 dans la librairie.
Cependant je n'arrive pas à comprendre ce qui t'empêche d'utiliser SPI0. Une fois que tu as téléversé le bootloader il est disponible.

c'est bien cela que j'aimerais savoir faire, mais je ne sais pas du tout par quoi commencer, ni où et quoi modifier.

J'utilise en fait les pins du SPI0 pour le bootloader, une fois televersé, je consacre cest pins en tant que I/O.
Pour mon projet, je ne peux donc plus utiliser SPI0, je prevoyais donc utiliser SPI1 pour la SD.

L'accès SPI se situe dans les fichiers Sd2Card.h et Sd2Card.cpp de la librairie SD. Il faut peut être aussi modifier Sd2PinMap.h.
La lecture de ces fichiers est un peu difficile à suivre à cause de la compilation conditionnelle pour différents processeurs.

merci kamill. Je vais donc me pencher sur ces fichiers

Oh que oui.

Il aurait été plus simple de conserver SPI0 comme SPI, et SPI1 comme I/O.

Oui, je me suis fait la même remarque.

Je suis d'accord avec vos remarques @hbachetti et @terwal, sauf que (en autres de mon CDC), j'ai besoin de generer de la PWM sur un des PIN du SPI0. Sur la SPI1, il n'y a aucun PIN permettant de generer la PWM, c'est pourquoi jai choisi la SPI1 pour la SD.

Si quelqu'un veut bien m'aider entre temps, ce serait super sympa. De mon côté, dès que je peux, je ferrai des essais pour savoir où ça va m'emmener.
Si je ne reussi pas, je n'aurai d'autre choix que de revoir mon CDC pour un nouveau PCB où SPI0 sera pour la SD.

Oui, je me doutais que tu voulais utiliser une fonctionnalité que permet l'un mais pas l'autre.
Je n'ai pas regardé le code source de la librairie, mais si tu as bien identifié tout les fichiers concernés, avec Visual code par exemple, tu peux faire un "remplacer tout" pour chacun de tes fichiers, avec le nom des broches du SPI0 par ceux du SPI1.
Tu peux aussi je crois faire une recherche et remplacement dans tout les fichiers d'un répertoire.

Petite recherche ce soir :confused:

J'ai donc décidé de faire mes essais sur la librairie "SD" et non exfat

1 - J'ai remplacé tout les mots SCK, SS, MISO, MOSI, CS, par le numero de pin correspondant à l'atmega328PB dans les fichiers Sd2card.h et sd2card.cpp

Càd pin SS1 = 19 (PE2)
pin MOSI1 = 22 (PE3)
pin MISO1 = 23 (PC0)
pin SCK1 = 24 (PC1)

Bilan 1: Pas d'erreur de compilation, mais rien en sortie SPI1 (mesure oscilloscope)

J'ai donc décidé d'essayé de remplacer les numeros de pin par les références minicore, çad PIN_PE1, PIN_PE2 etc...
Bilan 2: erreur de compilation, je ne peux aller plus loin.

Je reviens sur la config du bilan et ayant remarqué de Sd2card.cpp fait appel à #include <SPI.h>
Je donc ouvert le fichier SPI.cpp present dans packages\arduino\hardware\avr\1.8.6\libraries\SPI\src et j'ai remplacé les termes SS, SCK, MOSI, SS par les numeros de pin souhaité.

Bilan 3: Pas d'erreur de compilation, mais rien en sortie SPI1 (mesure oscilloscope)

Je me demandais donc, est-ce les numeros de pin que j'ai utilisé correspondent vraiment au pin réel de l'Uc ? Ne faudrait-il pas que je mette plutot un numero en hexadecimal ?

salut @tonynyny

je n'ai pas encore jeté un œil à la doc du "PB" mais je pense qu'il ne suffit pas de modifier les numéros de broches : il faut sans doute aussi aller voir du côté des registres ... s'il y a un SPI supplémentaire il doit posséder ses registres de configuration "perso"

Si j'avais à (essayer de) faire ça j'aurai remplacé dans SD2Card.cpp
#include <SPI.h>
par
#include <SPI1.h>

et
#define SDCARD_SPI SPI
par
#define SDCARD_SPI SPI1

Salut @5_cylindres
https://ww1.microchip.com/downloads/en/DeviceDoc/40001906A.pdf
à la page 389, on peut direct aller sur la page des PORTE et PORTC qui correspondent au SPI1.
De là, quand je consulte par exemple P.110, le PORTE, je ne vois pas de description sur la fonction des bits, donc... bloqué pour moi ^^

erreur de compilation ^^

c:\Users\PC\Documents\Arduino\libraries\SD\src\utility\Sd2Card.cpp:32:10: error: 'SPISettings' does not name a type; did you mean 'SPI1Settings'?
   static SPISettings settings;
          ^~~~~~~~~~~
          SPI1Settings
c:\Users\PC\Documents\Arduino\libraries\SD\src\utility\Sd2Card.cpp: In member function 'void Sd2Card::chipSelectLow()':
c:\Users\PC\Documents\Arduino\libraries\SD\src\utility\Sd2Card.cpp:191:33: error: 'settings' was not declared in this scope
     SDCARD_SPI.beginTransaction(settings);
                                 ^~~~~~~~
c:\Users\PC\Documents\Arduino\libraries\SD\src\utility\Sd2Card.cpp:191:33: note: suggested alternative: 'String'
     SDCARD_SPI.beginTransaction(settings);
                                 ^~~~~~~~
                                 String
c:\Users\PC\Documents\Arduino\libraries\SD\src\utility\Sd2Card.cpp: In member function 'uint8_t Sd2Card::init(uint8_t, uint8_t)':
c:\Users\PC\Documents\Arduino\libraries\SD\src\utility\Sd2Card.cpp:284:3: error: 'settings' was not declared in this scope
   settings = SPISettings(250000, MSBFIRST, SPI_MODE0);
   ^~~~~~~~
c:\Users\PC\Documents\Arduino\libraries\SD\src\utility\Sd2Card.cpp:284:3: note: suggested alternative: 'String'
   settings = SPISettings(250000, MSBFIRST, SPI_MODE0);
   ^~~~~~~~
   String
c:\Users\PC\Documents\Arduino\libraries\SD\src\utility\Sd2Card.cpp:284:14: error: 'SPISettings' was not declared in this scope
   settings = SPISettings(250000, MSBFIRST, SPI_MODE0);
              ^~~~~~~~~~~

Une partie du code uniquement

Essaie de faire ce qui est suggéré c'est à dire de remplacer
static SPISettings settings;
par
static SPI1Settings settings;