ESP32 method of reading and writing SD card using HSPI

Keywords: Eclipse Single-Chip Microcomputer

It has been almost a year since I used ESP32. I have a lot of experience and experience. Recently, I began to write articles to record my experience and process. The advantage is that it is convenient for me to find information and review, and may also help others. It's also a good thing.

Let's record today's experience first

Today, we will run the NES simulator on the esp32 wroom module (this module has 4M FLASH but no PSRAM). The hardware includes the 240X240 color screen of ST7789 and the SD card. Read the ROM file on the SD card, display the image on the color screen, and then output the sound through I2s. There is also the extended keyboard of I2C. Then the problem comes. The color screen and SD card are connected through SPI bus. Generally, SD card reading and writing are realized through Lexin's official libraries SPI.H and sd.h, but this library uses VSPI by default.

Knowledge points:
ESP32 has four SPI controllers SPI0, SPI1, SPI2 and SPI3, which are used to connect devices supporting SPI protocol. SPI0 control
The controller is used as the interface for the cache to access the external storage unit, SPI1 is used as the host, and SPI2 and SPI3 controllers can be used as both host and host
Used as a slave. When used as a host, each SPI controller can use multiple chip selection signals (CS0 ~ CS2) to connect multiple SPI slave devices.
SPI1 ~ SPI3 controllers share two DMA channels.
SPI0 and SPI1 controllers share a group of signal buses through an arbiter. This group of signal buses with prefix SPI consists of D, Q, CS0 ~ CS2, CLK
The composition of WP and HD signals is shown in table 25.
Accordingly, controllers SPI2 and SPI3 use signal buses prefixed with HSPI and VSPI, respectively.
The input and output signal lines contained in these signal buses can pass through GPIO switching matrix and IO_MUX module realizes the mapping with chip pins

Although the display screen and sd card are connected to the pins of esp32 through different pins, there will be a conflict if they are not handled in the software, because both use VSPI controller by default.


In the actual test, when the sd card can read and write, the color screen cannot be displayed, but if the sd card code is deleted, the color screen can be displayed

The problem to be solved is that the two need to use different SPI. How can the SD card use HSPI communication?

  First declare an SPI object using HSPI bus, and then bind this object to the begin of sd card, as follows:
 

  SPIClass MySPI(HSPI);//Declare a bus object called MySPI that uses HSPI to read and write SD cards

  pinMode(SD_CS, OUTPUT);//SD card CS pin
  digitalWrite(SD_CS, HIGH);//Low level is selected and high level is not selected


  //Wiring remapping of custom SPI controller (i.e. actual sd card hardware wiring)
  MySPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
  MySPI.setFrequency(1000000);//Set the SPI bus frequency to 1M   You don't have to



 //Use MySPI SPI bus to operate SD card
  if( !SD.begin(SD_CS,MySPI) )
  {
        Serial.println("Card Mount Failed");//Failed to return 0
        return;
  }

  uint64_t cardSize = SD.cardSize() / (1024 * 1024);
  Serial.printf("SD Card Size: %lluMB\n", cardSize);

Through this change, the conflict can be perfectly solved. The file content on the sd card can be read out and displayed on the color screen, and the audio file on the sd card can also be played. The 25 / 26 pin left and right channel analog audio output is output through the built-in DAC of esp32. The sound quality is OK. After all, it is 8 bits.

The following is the code that reads the WAV file in the SD card and outputs it through the built-in DAC:

 

 I2S_Init();

 Serial.print("Play to :music");
 File file = SD.open("/8.wav");  // 44100Hz, 16bit, stereo, linear PCM
 

  //Playback cycle
  while (file.readBytes(data, sizeof(data)))  I2S_Write(data, sizeof(data));

  file.close();





void I2S_Init() 
{
//Use the I2S configuration of the built-in DAC. Note that the built-in DAC is 8 bits

     i2s_config_t i2s_config = 
     {
     .mode =(i2s_mode_t) ( I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN ),
     .sample_rate =22050,
     .bits_per_sample =(i2s_bits_per_sample_t) 16, /* the DAC module will only take the 8bits from MSB */
     .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
     .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_PCM_SHORT),
     .intr_alloc_flags = 0, // default interrupt priority
     .dma_buf_count = 16,
     .dma_buf_len = 60,
     .use_apll = false
};

    i2s_driver_install((i2s_port_t)0, &i2s_config, 5, NULL);   //install and start i2s driver
    i2s_set_pin((i2s_port_t)0, NULL); //for internal DAC
   // i2s_ set_ pin((i2s_port_t)0, &pin_config);// The external DAC needs to set the control pin


    //You can call i2s_set_dac_mode to set built-in DAC output mode.
    //for internal DAC, this will enable both of the internal channels
    i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);

 //   i2s_set_sample_rates((i2s_port_t)i2s_num, 22050); //set sample rates
 //   i2s_driver_uninstall(i2s_num); //stop & destroy i2s driver

  
}

void I2S_Write(char* data, int numData) 
{
    i2s_write_bytes(I2S_NUM_0, (const char *)data, numData, portMAX_DELAY);
}

Posted by welshmike on Mon, 06 Dec 2021 17:51:49 -0800