VL53L0x sensor Ranging error. How to solve the error? - arm

I'm trying to interface vl53l0x sensor with my atSamE51A19 controller. I can send the data to the sensor. As it has established connection I send identification code and it responds. But further initialization and ranging is not working.
I'm using stm library implementation on my sam board. [stmArduinoLib][1]
The only changes I made is in the read write functions. It was using stm base functions but I'm using different functions.
/****************** Write and read functions from I2C *************************/
VL53L0X_Error VL53L0X::VL53L0X_WriteMulti(VL53L0X_DEV Dev, uint8_t index, uint8_t *pdata, uint32_t count)
{
int status;
status = VL53L0X_I2CWrite(index, pdata, (uint16_t) count);
if (status == 0)
{
status = VL53L0X_ERROR_CONTROL_INTERFACE;
}
else
{
status = VL53L0X_ERROR_NONE;
}
return status;
}
VL53L0X_Error VL53L0X::VL53L0X_ReadMulti(VL53L0X_DEV Dev, uint8_t index, uint8_t *pdata, uint32_t count)
{
int status;
if (count >= VL53L0X_MAX_I2C_XFER_SIZE)
{
status = VL53L0X_ERROR_INVALID_PARAMS;
}
status = VL53L0X_I2CRead(index, pdata, (uint16_t) count);
return status;
}
VL53L0X_Error VL53L0X::VL53L0X_WrByte(VL53L0X_DEV Dev, uint8_t index, uint8_t data)
{
int status;
status = WriteRegister(index, data); //VL53L0X_I2CWrite(index, &data, 1);
if (status == 0)
{
status = VL53L0X_ERROR_CONTROL_INTERFACE;
}
else
{
status = VL53L0X_ERROR_NONE;
}
return status;
}
VL53L0X_Error VL53L0X::VL53L0X_WrWord(VL53L0X_DEV Dev, uint8_t index, uint16_t data)
{
int status;
int32_t status_int;
alignas(2) uint8_t buffer[2];
buffer[0] = data >> 8;
buffer[1] = data & 0x00FF;
status_int = VL53L0X_I2CWrite(index, (uint8_t*) buffer, 2);
// buffer[0] = MSB
// buffer[1]
// buffer[2]
// buffer[3] = LSB
if (status_int == 0)
{
status = VL53L0X_ERROR_CONTROL_INTERFACE;
}
else
{
status = VL53L0X_ERROR_NONE;
}
return status;
}
VL53L0X_Error VL53L0X::VL53L0X_WrDWord(VL53L0X_DEV Dev, uint8_t index, uint32_t data)
{
int status;
int32_t status_int;
alignas(2) uint8_t buffer[4];
buffer[0] = (data >> 24) & 0xFF; // MSB
buffer[1] = (data >> 16) & 0xFF;
buffer[2] = (data >> 8) & 0xFF;
buffer[3] = (data >> 0) & 0xFF; // LSB
status_int = VL53L0X_I2CWrite(index, buffer, 4);
if (status_int == 0)
{
status = VL53L0X_ERROR_CONTROL_INTERFACE;
}
else
{
status = VL53L0X_ERROR_NONE;
}
return status;
}
VL53L0X_Error VL53L0X::VL53L0X_RdByte(VL53L0X_DEV Dev, uint8_t index, uint8_t *data)
{
int status = VL53L0X_ERROR_NONE;
status = ReadRegister(index, *data); //
if (status)
{
status = VL53L0X_ERROR_NONE;
return status;
}
else
{
status = VL53L0X_ERROR_CONTROL_INTERFACE;
}
return status;
}
VL53L0X_Error VL53L0X::VL53L0X_RdWord(VL53L0X_DEV Dev, uint8_t index, uint16_t *data)
{
int status = 0;
uint8_t buffer[2] =
{ 0, 0 };
status = VL53L0X_I2CRead(index, buffer, 2);
if (status == 0)
{
*data = (uint16_t) (buffer[0] << 8) | (buffer[1] & 0xFF); // | ? +
}
return status;
}
VL53L0X_Error VL53L0X::VL53L0X_RdDWord(VL53L0X_DEV Dev, uint8_t index, uint32_t *data)
{
int status;
uint8_t buffer[4] =
{ 0, 0, 0, 0 };
status = VL53L0X_I2CRead(index, buffer, 4);
if (!status) // use OR
{
*data = ((uint32_t) buffer[0] << 24) | ((uint32_t) buffer[1] << 16) | ((uint32_t) buffer[2] << 8) | (uint32_t) buffer[3];
}
return status;
}
VL53L0X_Error VL53L0X::VL53L0X_UpdateByte(VL53L0X_DEV Dev, uint8_t index, uint8_t AndData, uint8_t OrData)
{
int status;
uint8_t buffer = 0;
/* read data direct onto buffer */
status = VL53L0X_I2CRead(index, &buffer, 1);
if (!status)
{
buffer = (buffer & AndData) | OrData;
status = VL53L0X_I2CWrite(index, &buffer, (uint8_t) 1);
}
if (status == 0)
{
status = VL53L0X_ERROR_CONTROL_INTERFACE;
}
else
{
status = VL53L0X_ERROR_NONE;
}
return status;
}
VL53L0X_Error VL53L0X::VL53L0X_I2CWrite(uint8_t RegisterAddr, uint8_t *pBuffer, uint16_t NumByteToWrite)
{
return WriteRegisters(RegisterAddr, pBuffer, NumByteToWrite);
}
VL53L0X_Error VL53L0X::VL53L0X_I2CRead(uint8_t RegisterAddr, uint8_t *pBuffer, uint16_t NumByteToRead)
{
alignas(2) uint8_t vpBuffer[5];
/* dev_i2c->beginTransmission(((uint8_t) (((DeviceAddr) >> 1) & 0x7F)));
*/
if (ReadRegisters(RegisterAddr, vpBuffer, NumByteToRead))
{
pBuffer = vpBuffer;
return 0;
}
return 1;
}
In this functions I'm not sending device address everytime just sending the index and data. In constructor only I've set the address once. The only error I'm getting when I use Status = VL53L0X_PerformRefSpadManagement(Device, &refSpadCount, &isApertureSpads); This function. In initialization. Don't know how to solve this issue. Tried to change the mode to continous that didn't help. Note: the sensor is working fine with arduino.
[1]: https://github.com/stm32duino/VL53L0X

I found a new possible solution to this problem, as I spent 2 days to solve it, I feel that I need to share it with you guys. Here is the summary of solutions:
Powering problem 1: add a 50ms delay in the setup function (before the sensor init, obviously). This way, the Vin will have time enough to reach the level needed by the sensor, as explained in a previous message (ocrdu)
Powering problem 2: This sensor demands more than others, normally the 5V pin of your board will do it better than the 3v3. If there are many sensors connected at the same time, disconnect all of them to check whether they are using the power our vl53l0x needs.
Powering problem 3: If you feed it with an external source, unify grounds with the board, don't forget it
SCL - SDA weak signals: add a pullup (2k2 - 4k7 from each channel to vin), this way the signals will perform better, that could be the problem
Soldering problem: Make sure that each header pin is well soldered. I've found many cases of poor soldering. The tiny pads don't help. It is advisable to check continuity with a multimeter between the headers and the pads where they should be connected
XSHUT not pulled up: My problem, this was that AZDelivery board says that it has a pull up in XSHUT (which works like an enable). But it could either be false, or badly implemented. Xshut needs to be high, 3v3/5v, otherwise, the sensor won't work. Bridge the XSHUT to Vin or implement a pull up and the problem will be solved.
I am using adafruit's library 1.1.2, and it works, with pullups, without pullups, connected to 5V and also connected to 3V3, with the
50ms delay, and connecting XSHUT to high (3V3 / Vin)
My board is vl53l0x from AZDelivery (5€ amazon)
I have tested the sensor with arduino nano and with ESP32, both work fine
Hope you guys find this useful <3

Related

How can I use esp-idf-mirf nRF24L01/Si24R1 driver to transmit audio sampled with i2s_read?

Original post - 12/2022
I'm currently trying to transmit audio via nRF24 with an ESP32 development board using a driver library from nopnop2002. I'm using the i2s-adc lib functions to get data from an analog microphone and save it into a buffer after scaling it down to 8-bit so that the other esp32 can play it directly with its DAC converter.
At first, I had a large i2s_read_len and later I suspected that this could be an issue so I decided to reduce it to 12-bit (the ADC bit width of the esp32) for testing purposes. What is the ideal size for my audio packets?
I'm also not sure how to handle the sampling rate of i2s_read and the rate at which I attempt to send data to the nRF24 via SPI. As far as I know the internal ADC-I2S already has a queue implemented, should I make another one and use vTaskDelay(??? / portTICK_PERIOD_MS); to avoid sending data too fast?
Here's what I currently have ignoring the includes:
#define V_REF 1100
#define I2S_COMM_MODE 0 // ADC/DAC Mode
#define I2S_SAMPLE_RATE 44100
#define I2S_SAMPLE_BITS 16
#define I2S_BUF_DEBUG 0 // enable display buffer for debug
#define I2S_READ_LEN 16 * 1024 // I2S read buffer length
#define I2S_FORMAT (I2S_CHANNEL_FMT_ONLY_RIGHT)
#define I2S_CHANNEL_NUM 0 // I2S channel number
#define I2S_ADC_UNIT ADC_UNIT_1 // I2S built-in ADC unit
#define I2S_ADC_CHANNEL ADC1_CHANNEL_0 // I2S built-in ADC channel GPIO36
#define BIT_SAMPLE 16
#define SPI_DMA_CHAN SPI_DMA_CH_AUTO
#define NUM_CHANNELS 1 // For mono recording only!
#define SAMPLE_SIZE (BIT_SAMPLE * 1024)
#define BYTE_RATE (I2S_SAMPLE_RATE * (BIT_SAMPLE / 8)) * NUM_CHANNELS
/**
* #brief I2S ADC mode init.
*/
void init_microphone(void)
{
int i2s_num = I2S_COMM_MODE;
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN,
.sample_rate = I2S_SAMPLE_RATE,
.bits_per_sample = I2S_SAMPLE_BITS,
.communication_format = I2S_COMM_FORMAT_STAND_MSB,
.channel_format = I2S_FORMAT,
.intr_alloc_flags = 0,
.dma_buf_count = 6,
.dma_buf_len = 256,
.use_apll = 1,
};
// Call driver installation function and adc pad.
ESP_ERROR_CHECK(i2s_driver_install(i2s_num, &i2s_config, 0, NULL));
ESP_ERROR_CHECK(i2s_set_adc_mode(I2S_ADC_UNIT, I2S_ADC_CHANNEL));
}
/**
* #brief Scale data to 8bit for data from ADC.
* Data from ADC are 12bit width by default.
* #param d_buff: destination buffer
* #param s_buff: source buffer
* #param len: length of source buffer
*/
void i2s_adc_data_scale(uint8_t *d_buff, uint8_t *s_buff, uint32_t len)
{
uint32_t j = 0;
uint32_t dac_value = 0;
#if (EXAMPLE_I2S_SAMPLE_BITS == 16)
for (int i = 0; i < len; i += 2)
{
dac_value = ((((uint16_t)(s_buff[i + 1] & 0xf) << 8) | ((s_buff[i + 0]))));
d_buff[j++] = 0;
d_buff[j++] = dac_value * 256 / 4096;
}
#else
for (int i = 0; i < len; i += 4)
{
dac_value = ((((uint16_t)(s_buff[i + 3] & 0xf) << 8) | ((s_buff[i + 2]))));
d_buff[j++] = 0;
d_buff[j++] = 0;
d_buff[j++] = 0;
d_buff[j++] = dac_value * 256 / 4096;
}
#endif
}
#if CONFIG_TRANSMITTER
void transmitter(void *pvParameters)
{
size_t bytes_read;
ESP_LOGI(pcTaskGetName(0), "Start");
int i2s_read_len = (12);
char *i2s_read_buff = (char *)calloc(i2s_read_len, sizeof(char));
uint8_t *i2s_write_buff = (uint8_t *)calloc(i2s_read_len, sizeof(char));
i2s_adc_enable(I2S_CHANNEL_NUM);
NRF24_t dev;
Nrf24_init(&dev);
uint8_t payload = sizeof(i2s_read_buff);
uint8_t channel = 90;
Nrf24_config(&dev, channel, payload);
// Set the receiver address using 5 characters
esp_err_t ret = Nrf24_setTADDR(&dev, (uint8_t *)"FGHIJ");
if (ret != ESP_OK)
{
ESP_LOGE(pcTaskGetName(0), "nrf24l01 not installed");
while (1)
{
vTaskDelay(1);
}
}
#if CONFIG_ADVANCED
AdvancedSettings(&dev);
#endif // CONFIG_ADVANCED
// Print settings
Nrf24_printDetails(&dev);
// Start ADC
while (1)
{
// Read data from I2S bus, in this case, from ADC. //
i2s_read(I2S_CHANNEL_NUM, (void *)i2s_read_buff, i2s_read_len, &bytes_read, portMAX_DELAY);
// process data and scale to 8bit for I2S DAC.
i2s_adc_data_scale(i2s_write_buff, (uint8_t *)i2s_read_buff, i2s_read_len);
// i2s_write_buff needs to be the buffer that is sent via nr24l01.
Nrf24_send(&dev, i2s_write_buff);
if (Nrf24_isSend(&dev, 1000))
{
ESP_LOGI(pcTaskGetName(0), "sending audio data ...");
}
else
{
ESP_LOGW(pcTaskGetName(0), "sending failed ...");
}
}
}
#endif // CONFIG_TRANSMITTER
void app_main(void)
{
// I2S ADC mode microphone init.
init_microphone();
#if CONFIG_TRANSMITTER
xTaskCreate(transmitter, "TRANSMITTER", 1024 * 3, NULL, 2, NULL);
#endif
// Stop I2S driver and destroy
// ESP_ERROR_CHECK(i2s_driver_uninstall(I2S_COMM_MODE));
}
Here is the code where Nrf24_send is defined:
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <driver/spi_master.h>
#include <driver/gpio.h>
#include "esp_log.h"
#include "mirf.h"
#define TAG "NRF24"
// SPI Stuff
#if CONFIG_SPI2_HOST
#define HOST_ID SPI2_HOST
#elif CONFIG_SPI3_HOST
#define HOST_ID SPI3_HOST
#endif
static const int SPI_Frequency = 4000000; // Stable even with a long jumper cable
//static const int SPI_Frequency = 6000000;
//static const int SPI_Frequency = 8000000; // Requires a short jumper cable
//static const int SPI_Frequency = 10000000; // Unstable even with a short jumper cable
const char rf24_datarates[][8] = {"1Mbps", "2Mbps", "250Kbps"};
const char rf24_crclength[][10] = {"Disabled", "8 bits", "16 bits"};
const char rf24_pa_dbm[][8] = {"PA_MIN", "PA_LOW", "PA_HIGH", "PA_MAX"};
void Nrf24_init(NRF24_t * dev)
{
esp_err_t ret;
ESP_LOGI(TAG, "CONFIG_MISO_GPIO=%d", CONFIG_MISO_GPIO);
ESP_LOGI(TAG, "CONFIG_MOSI_GPIO=%d", CONFIG_MOSI_GPIO);
ESP_LOGI(TAG, "CONFIG_SCLK_GPIO=%d", CONFIG_SCLK_GPIO);
ESP_LOGI(TAG, "CONFIG_CE_GPIO=%d", CONFIG_CE_GPIO);
ESP_LOGI(TAG, "CONFIG_CSN_GPIO=%d", CONFIG_CSN_GPIO);
//gpio_pad_select_gpio(CONFIG_CE_GPIO);
gpio_reset_pin(CONFIG_CE_GPIO);
gpio_set_direction(CONFIG_CE_GPIO, GPIO_MODE_OUTPUT);
gpio_set_level(CONFIG_CE_GPIO, 0);
//gpio_pad_select_gpio(CONFIG_CSN_GPIO);
gpio_reset_pin(CONFIG_CSN_GPIO);
gpio_set_direction(CONFIG_CSN_GPIO, GPIO_MODE_OUTPUT);
gpio_set_level(CONFIG_CSN_GPIO, 1);
spi_bus_config_t spi_bus_config = {
.sclk_io_num = CONFIG_SCLK_GPIO,
.mosi_io_num = CONFIG_MOSI_GPIO,
.miso_io_num = CONFIG_MISO_GPIO,
.quadwp_io_num = -1,
.quadhd_io_num = -1
};
ret = spi_bus_initialize( HOST_ID, &spi_bus_config, SPI_DMA_CH_AUTO );
ESP_LOGI(TAG, "spi_bus_initialize=%d",ret);
assert(ret==ESP_OK);
spi_device_interface_config_t devcfg;
memset( &devcfg, 0, sizeof( spi_device_interface_config_t ) );
devcfg.clock_speed_hz = SPI_Frequency;
// It does not work with hardware CS control.
//devcfg.spics_io_num = csn_pin;
// It does work with software CS control.
devcfg.spics_io_num = -1;
devcfg.queue_size = 7;
devcfg.mode = 0;
devcfg.flags = SPI_DEVICE_NO_DUMMY;
spi_device_handle_t handle;
ret = spi_bus_add_device( HOST_ID, &devcfg, &handle);
ESP_LOGI(TAG, "spi_bus_add_device=%d",ret);
assert(ret==ESP_OK);
dev->cePin = CONFIG_CE_GPIO;
dev->csnPin = CONFIG_CSN_GPIO;
dev->channel = 1;
dev->payload = 16;
dev->_SPIHandle = handle;
}
bool spi_write_byte(NRF24_t * dev, uint8_t* Dataout, size_t DataLength )
{
spi_transaction_t SPITransaction;
if ( DataLength > 0 ) {
memset( &SPITransaction, 0, sizeof( spi_transaction_t ) );
SPITransaction.length = DataLength * 8;
SPITransaction.tx_buffer = Dataout;
SPITransaction.rx_buffer = NULL;
spi_device_transmit( dev->_SPIHandle, &SPITransaction );
}
return true;
}
bool spi_read_byte(NRF24_t * dev, uint8_t* Datain, uint8_t* Dataout, size_t DataLength )
{
spi_transaction_t SPITransaction;
if ( DataLength > 0 ) {
memset( &SPITransaction, 0, sizeof( spi_transaction_t ) );
SPITransaction.length = DataLength * 8;
SPITransaction.tx_buffer = Dataout;
SPITransaction.rx_buffer = Datain;
spi_device_transmit( dev->_SPIHandle, &SPITransaction );
}
return true;
}
uint8_t spi_transfer(NRF24_t * dev, uint8_t address) {
uint8_t datain[1];
uint8_t dataout[1];
dataout[0] = address;
//spi_write_byte(dev, dataout, 1 );
spi_read_byte(dev, datain, dataout, 1 );
return datain[0];
}
void spi_csnHi(NRF24_t * dev) {
gpio_set_level( dev->csnPin, 1 );
}
void spi_csnLow(NRF24_t * dev) {
gpio_set_level( dev->csnPin, 0 );
}
// Sets the important registers in the MiRF module and powers the module
// in receiving mode
// NB: channel and payload must be set now.
void Nrf24_config(NRF24_t * dev, uint8_t channel, uint8_t payload)
{
dev->channel = channel;
dev->payload = payload;
Nrf24_configRegister(dev, RF_CH, dev->channel); // Set RF channel
Nrf24_configRegister(dev, RX_PW_P0, dev->payload); // Set length of incoming payload
Nrf24_configRegister(dev, RX_PW_P1, dev->payload);
Nrf24_powerUpRx(dev); // Start receiver
Nrf24_flushRx(dev);
}
// Sets the receiving device address
//void Nrf24_setRADDR(NRF24_t * dev, uint8_t * adr)
esp_err_t Nrf24_setRADDR(NRF24_t * dev, uint8_t * adr)
{
esp_err_t ret = ESP_OK;
Nrf24_writeRegister(dev, RX_ADDR_P1, adr, mirf_ADDR_LEN);
uint8_t buffer[5];
Nrf24_readRegister(dev, RX_ADDR_P1, buffer, sizeof(buffer));
for (int i=0;i<5;i++) {
ESP_LOGD(TAG, "adr[%d]=0x%x buffer[%d]=0x%x", i, adr[i], i, buffer[i]);
if (adr[i] != buffer[i]) ret = ESP_FAIL;
}
return ret;
}
// Sets the transmitting device address
//void Nrf24_setTADDR(NRF24_t * dev, uint8_t * adr)
esp_err_t Nrf24_setTADDR(NRF24_t * dev, uint8_t * adr)
{
esp_err_t ret = ESP_OK;
Nrf24_writeRegister(dev, RX_ADDR_P0, adr, mirf_ADDR_LEN); //RX_ADDR_P0 must be set to the sending addr for auto ack to work.
Nrf24_writeRegister(dev, TX_ADDR, adr, mirf_ADDR_LEN);
uint8_t buffer[5];
Nrf24_readRegister(dev, RX_ADDR_P0, buffer, sizeof(buffer));
for (int i=0;i<5;i++) {
ESP_LOGD(TAG, "adr[%d]=0x%x buffer[%d]=0x%x", i, adr[i], i, buffer[i]);
if (adr[i] != buffer[i]) ret = ESP_FAIL;
}
return ret;
}
// Add the receiving device address
void Nrf24_addRADDR(NRF24_t * dev, uint8_t pipe, uint8_t adr)
{
uint8_t value;
Nrf24_readRegister(dev, EN_RXADDR, &value, 1);
if (pipe == 2) {
Nrf24_configRegister(dev, RX_PW_P2, dev->payload);
Nrf24_configRegister(dev, RX_ADDR_P2, adr);
value = value | 0x04;
Nrf24_configRegister(dev, EN_RXADDR, value);
} else if (pipe == 3) {
Nrf24_configRegister(dev, RX_PW_P3, dev->payload);
Nrf24_configRegister(dev, RX_ADDR_P3, adr);
value = value | 0x08;
Nrf24_configRegister(dev, EN_RXADDR, value);
} else if (pipe == 4) {
Nrf24_configRegister(dev, RX_PW_P4, dev->payload);
Nrf24_configRegister(dev, RX_ADDR_P4, adr);
value = value | 0x10;
Nrf24_configRegister(dev, EN_RXADDR, value);
} else if (pipe == 5) {
Nrf24_configRegister(dev, RX_PW_P5, dev->payload);
Nrf24_configRegister(dev, RX_ADDR_P5, adr);
value = value | 0x20;
Nrf24_configRegister(dev, EN_RXADDR, value);
}
}
// Checks if data is available for reading
extern bool Nrf24_dataReady(NRF24_t * dev)
{
// See note in getData() function - just checking RX_DR isn't good enough
uint8_t status = Nrf24_getStatus(dev);
if ( status & (1 << RX_DR) ) return 1;
// We can short circuit on RX_DR, but if it's not set, we still need
// to check the FIFO for any pending packets
//return !Nrf24_rxFifoEmpty(dev);
return 0;
}
// Get pipe number for reading
uint8_t Nrf24_getDataPipe(NRF24_t * dev) {
uint8_t status = Nrf24_getStatus(dev);
return ((status & 0x0E) >> 1);
}
extern bool Nrf24_rxFifoEmpty(NRF24_t * dev)
{
uint8_t fifoStatus;
Nrf24_readRegister(dev, FIFO_STATUS, &fifoStatus, sizeof(fifoStatus));
return (fifoStatus & (1 << RX_EMPTY));
}
// Reads payload bytes into data array
extern void Nrf24_getData(NRF24_t * dev, uint8_t * data)
{
spi_csnLow(dev); // Pull down chip select
spi_transfer(dev, R_RX_PAYLOAD ); // Send cmd to read rx payload
spi_read_byte(dev, data, data, dev->payload); // Read payload
spi_csnHi(dev); // Pull up chip select
// NVI: per product spec, p 67, note c:
// "The RX_DR IRQ is asserted by a new packet arrival event. The procedure
// for handling this interrupt should be: 1) read payload through SPI,
// 2) clear RX_DR IRQ, 3) read FIFO_STATUS to check if there are more
// payloads available in RX FIFO, 4) if there are more data in RX FIFO,
// repeat from step 1)."
// So if we're going to clear RX_DR here, we need to check the RX FIFO
// in the dataReady() function
Nrf24_configRegister(dev, STATUS, (1 << RX_DR)); // Reset status register
}
// Clocks only one byte into the given MiRF register
void Nrf24_configRegister(NRF24_t * dev, uint8_t reg, uint8_t value)
{
spi_csnLow(dev);
spi_transfer(dev, W_REGISTER | (REGISTER_MASK & reg));
spi_transfer(dev, value);
spi_csnHi(dev);
}
// Reads an array of bytes from the given start position in the MiRF registers
void Nrf24_readRegister(NRF24_t * dev, uint8_t reg, uint8_t * value, uint8_t len)
{
spi_csnLow(dev);
spi_transfer(dev, R_REGISTER | (REGISTER_MASK & reg));
spi_read_byte(dev, value, value, len);
spi_csnHi(dev);
}
// Writes an array of bytes into inte the MiRF registers
void Nrf24_writeRegister(NRF24_t * dev, uint8_t reg, uint8_t * value, uint8_t len)
{
spi_csnLow(dev);
spi_transfer(dev, W_REGISTER | (REGISTER_MASK & reg));
spi_write_byte(dev, value, len);
spi_csnHi(dev);
}
// Sends a data package to the default address. Be sure to send the correct
// amount of bytes as configured as payload on the receiver.
void Nrf24_send(NRF24_t * dev, uint8_t * value)
{
uint8_t status;
status = Nrf24_getStatus(dev);
while (dev->PTX) // Wait until last paket is send
{
status = Nrf24_getStatus(dev);
if ((status & ((1 << TX_DS) | (1 << MAX_RT))))
{
dev->PTX = 0;
break;
}
}
Nrf24_ceLow(dev);
Nrf24_powerUpTx(dev); // Set to transmitter mode , Power up
spi_csnLow(dev); // Pull down chip select
spi_transfer(dev, FLUSH_TX ); // Write cmd to flush tx fifo
spi_csnHi(dev); // Pull up chip select
spi_csnLow(dev); // Pull down chip select
spi_transfer(dev, W_TX_PAYLOAD ); // Write cmd to write payload
spi_write_byte(dev, value, dev->payload); // Write payload
spi_csnHi(dev); // Pull up chip select
Nrf24_ceHi(dev); // Start transmission
}
// Test if chip is still sending.
// When sending has finished return chip to listening.
bool Nrf24_isSending(NRF24_t * dev) {
uint8_t status;
if (dev->PTX)
{
status = Nrf24_getStatus(dev);
if ((status & ((1 << TX_DS) | (1 << MAX_RT)))) {// if sending successful (TX_DS) or max retries exceded (MAX_RT).
Nrf24_powerUpRx(dev);
return false;
}
return true;
}
return false;
}
// Test if Sending has finished or retry is over.
// When sending has finished return trur.
// When reach maximum number of TX retries return false.
bool Nrf24_isSend(NRF24_t * dev, int timeout) {
uint8_t status;
TickType_t startTick = xTaskGetTickCount();
if (dev->PTX) {
while(1) {
status = Nrf24_getStatus(dev);
/*
if sending successful (TX_DS) or max retries exceded (MAX_RT).
*/
if (status & (1 << TX_DS)) { // Data Sent TX FIFO interrup
Nrf24_powerUpRx(dev);
return true;
}
if (status & (1 << MAX_RT)) { // Maximum number of TX retries interrupt
ESP_LOGW(TAG, "Maximum number of TX retries interrupt");
Nrf24_powerUpRx(dev);
return false;
}
// I believe either TX_DS or MAX_RT will always be notified.
// Therefore, it is unusual for neither to be notified for a period of time.
// I don't know exactly how to respond.
TickType_t diffTick = xTaskGetTickCount() - startTick;
if ( (diffTick * portTICK_PERIOD_MS) > timeout) {
ESP_LOGE(TAG, "Status register timeout. status=0x%x", status);
return false;
}
vTaskDelay(1);
}
}
return false;
}
uint8_t Nrf24_getStatus(NRF24_t * dev) {
uint8_t rv;
Nrf24_readRegister(dev, STATUS, &rv, 1);
return rv;
}
void Nrf24_powerUpRx(NRF24_t * dev) {
dev->PTX = 0;
Nrf24_ceLow(dev);
Nrf24_configRegister(dev, CONFIG, mirf_CONFIG | ( (1 << PWR_UP) | (1 << PRIM_RX) ) ); //set device as TX mode
Nrf24_ceHi(dev);
Nrf24_configRegister(dev, STATUS, (1 << TX_DS) | (1 << MAX_RT)); //Clear seeded interrupt and max tx number interrupt
}
void Nrf24_flushRx(NRF24_t * dev)
{
spi_csnLow(dev);
spi_transfer(dev, FLUSH_RX );
spi_csnHi(dev);
}
void Nrf24_powerUpTx(NRF24_t * dev) {
dev->PTX = 1;
Nrf24_configRegister(dev, CONFIG, mirf_CONFIG | ( (1 << PWR_UP) | (0 << PRIM_RX) ) );
}
void Nrf24_ceHi(NRF24_t * dev) {
gpio_set_level( dev->cePin, 1 );
}
void Nrf24_ceLow(NRF24_t * dev) {
gpio_set_level( dev->cePin, 0 );
}
void Nrf24_powerDown(NRF24_t * dev)
{
Nrf24_ceLow(dev);
Nrf24_configRegister(dev, CONFIG, mirf_CONFIG );
}
//Set tx power : 0=-18dBm,1=-12dBm,2=-6dBm,3=0dBm
void Nrf24_SetOutputRF_PWR(NRF24_t * dev, uint8_t val)
{
if (val > 3) return;
uint8_t value;
Nrf24_readRegister(dev, RF_SETUP, &value, 1);
value = value & 0xF9;
value = value | (val<< RF_PWR);
//Nrf24_configRegister(dev, RF_SETUP, (val<< RF_PWR) );
Nrf24_configRegister(dev, RF_SETUP, value);
}
//Select between the high speed data rates:0=1Mbps, 1=2Mbps, 2=250Kbps
void Nrf24_SetSpeedDataRates(NRF24_t * dev, uint8_t val)
{
if (val > 2) return;
uint8_t value;
Nrf24_readRegister(dev, RF_SETUP, &value, 1);
if(val == 2)
{
value = value | 0x20;
value = value & 0xF7;
//Nrf24_configRegister(dev, RF_SETUP, (1 << RF_DR_LOW) );
Nrf24_configRegister(dev, RF_SETUP, value);
}
else
{
value = value & 0xD7;
value = value | (val << RF_DR_HIGH);
//Nrf24_configRegister(dev, RF_SETUP, (val << RF_DR_HIGH) );
Nrf24_configRegister(dev, RF_SETUP, value);
}
}
//Set Auto Retransmit Delay 0=250us, 1=500us, ... 15=4000us
void Nrf24_setRetransmitDelay(NRF24_t * dev, uint8_t val)
{
uint8_t value;
Nrf24_readRegister(dev, SETUP_RETR, &value, 1);
value = value & 0x0F;
value = value | (val << ARD);
Nrf24_configRegister(dev, SETUP_RETR, value);
}
void Nrf24_printDetails(NRF24_t * dev)
{
printf("================ SPI Configuration ================\n" );
printf("CSN Pin \t = GPIO%d\n",dev->csnPin);
printf("CE Pin \t = GPIO%d\n", dev->cePin);
printf("Clock Speed\t = %d\n", SPI_Frequency);
printf("================ NRF Configuration ================\n");
Nrf24_print_status(Nrf24_getStatus(dev));
Nrf24_print_address_register(dev, "RX_ADDR_P0-1", RX_ADDR_P0, 2);
Nrf24_print_byte_register(dev, "RX_ADDR_P2-5", RX_ADDR_P2, 4);
Nrf24_print_address_register(dev, "TX_ADDR\t", TX_ADDR, 1);
Nrf24_print_byte_register(dev, "RX_PW_P0-6", RX_PW_P0, 6);
Nrf24_print_byte_register(dev, "EN_AA\t", EN_AA, 1);
Nrf24_print_byte_register(dev, "EN_RXADDR", EN_RXADDR, 1);
Nrf24_print_byte_register(dev, "RF_CH\t", RF_CH, 1);
Nrf24_print_byte_register(dev, "RF_SETUP", RF_SETUP, 1);
Nrf24_print_byte_register(dev, "CONFIG\t", CONFIG, 1);
Nrf24_print_byte_register(dev, "DYNPD/FEATURE", DYNPD, 2);
//printf("getDataRate()=%d\n",Nrf24_getDataRate(dev));
printf("Data Rate\t = %s\n",rf24_datarates[Nrf24_getDataRate(dev)]);
#if 0
printf_P(PSTR("Model\t\t = "
PRIPSTR
"\r\n"),pgm_read_ptr(&rf24_model_e_str_P[isPVariant()]));
#endif
//printf("getCRCLength()=%d\n",Nrf24_getCRCLength(dev));
printf("CRC Length\t = %s\n", rf24_crclength[Nrf24_getCRCLength(dev)]);
//printf("getPALevel()=%d\n",Nrf24_getPALevel(dev));
printf("PA Power\t = %s\n", rf24_pa_dbm[Nrf24_getPALevel(dev)]);
uint8_t retransmit = Nrf24_getRetransmitDelay(dev);
int16_t delay = (retransmit+1)*250;
printf("Retransmit\t = %d us\n", delay);
}
#define _BV(x) (1<<(x))
void Nrf24_print_status(uint8_t status)
{
printf("STATUS\t\t = 0x%02x RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\r\n", status, (status & _BV(RX_DR)) ? 1 : 0,
(status & _BV(TX_DS)) ? 1 : 0, (status & _BV(MAX_RT)) ? 1 : 0, ((status >> RX_P_NO) & 0x07), (status & _BV(TX_FULL)) ? 1 : 0);
}
void Nrf24_print_address_register(NRF24_t * dev, const char* name, uint8_t reg, uint8_t qty)
{
printf("%s\t =",name);
while (qty--) {
//uint8_t buffer[addr_width];
uint8_t buffer[5];
Nrf24_readRegister(dev, reg++, buffer, sizeof(buffer));
printf(" 0x");
#if 0
uint8_t* bufptr = buffer + sizeof buffer;
while (--bufptr >= buffer) {
printf("%02x", *bufptr);
}
#endif
for(int i=0;i<5;i++) {
printf("%02x", buffer[i]);
}
}
printf("\r\n");
}
void Nrf24_print_byte_register(NRF24_t * dev, const char* name, uint8_t reg, uint8_t qty)
{
printf("%s\t =", name);
while (qty--) {
uint8_t buffer[1];
Nrf24_readRegister(dev, reg++, buffer, 1);
printf(" 0x%02x", buffer[0]);
}
printf("\r\n");
}
uint8_t Nrf24_getDataRate(NRF24_t * dev)
{
rf24_datarate_e result;
uint8_t dr;
Nrf24_readRegister(dev, RF_SETUP, &dr, sizeof(dr));
//printf("RF_SETUP=%x\n",dr);
dr = dr & (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH));
// switch uses RAM (evil!)
// Order matters in our case below
if (dr == _BV(RF_DR_LOW)) {
// '10' = 250KBPS
result = RF24_250KBPS;
} else if (dr == _BV(RF_DR_HIGH)) {
// '01' = 2MBPS
result = RF24_2MBPS;
} else {
// '00' = 1MBPS
result = RF24_1MBPS;
}
return result;
}
uint8_t Nrf24_getCRCLength(NRF24_t * dev)
{
rf24_crclength_e result = RF24_CRC_DISABLED;
uint8_t config;
Nrf24_readRegister(dev, CONFIG, &config, sizeof(config));
//printf("CONFIG=%x\n",config);
config = config & (_BV(CRCO) | _BV(EN_CRC));
uint8_t AA;
Nrf24_readRegister(dev, EN_AA, &AA, sizeof(AA));
if (config & _BV(EN_CRC) || AA) {
if (config & _BV(CRCO)) {
result = RF24_CRC_16;
} else {
result = RF24_CRC_8;
}
}
return result;
}
uint8_t Nrf24_getPALevel(NRF24_t * dev)
{
uint8_t level;
Nrf24_readRegister(dev, RF_SETUP, &level, sizeof(level));
//printf("RF_SETUP=%x\n",level);
level = (level & (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH))) >> 1;
return (level);
}
uint8_t Nrf24_getRetransmitDelay(NRF24_t * dev)
{
uint8_t value;
Nrf24_readRegister(dev, SETUP_RETR, &value, 1);
return (value >> 4);
}
I'm getting the following output:
...
W (19498) NRF24: Maximum number of TX retries interrupt
W (19498) TRANSMITTER: sending failed ...
...
Update: Transmission succeeds but audio is not inteligible - 02/2023
I found out that I'm using Si24R1 (Chinese clone of nRF24L01) and I'm still using the i2s-adc esp-idf lib functions to get data from an analog microphone and I need to get this I2S stream through the SPI interface between the ESP32 and the nRF24L01 breakout board clone. Is this possible?
I've seen some people mention on the internet that Nordic Semiconductors has nRF chips that support I2S but I would rather stick to the hardware I have available.
Here's the link I've consulted.
I've also found this library that supports audio transmission using the nRF24L01/Si24R1 chip using the nrf24.h lib but it is writeen in c++ and it is not clear to me how I could integrate it into my project.
Let me know what you think the problem is or/and if you have good recommendations for learning material about this subject.
Thanks for the help in advance!
Do your hardware works with a simple sender/receiver from sample codes you can find in adruino ide? If you didn't checked, have a try so you can validate the hardware is ok.
If simple sender/receiver doesn't work, check your power supply lines. Sometimes not enough current from the voltage regulator.
Add some caps on the rf24 module power-supply pins to filter some noise can also helps depending on the quality of the power supply.
./X

Ethernet with STM32 and ENC28J60

I'm trying to run SPI Ethernet module ENC28J60 with STM32H7. I wrote some functions for ENC28J60 library. Now, I want to get ping from PC or Router, but I can not. If anyone can help me to run it, please answer this question.
After Ping, I want to access communication stm32 microcontrollers and ENC28J60. Unfortunately, I could not find suitable library and sample code.
Thanks,
Sajix
my .C file of ENC28J60:
#include "stm32_enc28j60.h"
extern SPI_HandleTypeDef hspi1;
static uint8_t Enc28_Bank;
uint8_t dataWatch8;
uint16_t dataWatch16;
//-> SPI Instruction Set
uint8_t ENC28_readOp(uint8_t oper, uint8_t addr){
uint8_t spiData[2];
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);
spiData[0] = (oper| (addr & ADDR_MASK));
HAL_SPI_Transmit(&hspi1, spiData, 1, 100);
if(addr & 0x80)
{
HAL_SPI_Receive(&hspi1, &spiData[1], 1, 100);
}
HAL_SPI_Receive(&hspi1, &spiData[1], 1, 100);
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);
return spiData[1];
}
void ENC28_writeOp(uint8_t oper, uint8_t addr, uint8_t data){
uint8_t spiData[2];
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);
spiData[0] = (oper| (addr & ADDR_MASK));
spiData[1] = data;
HAL_SPI_Transmit(&hspi1, spiData, 2, 100);
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);
}
//-> Select Bank
void ENC28_setBank(uint8_t addr)
{
if ((addr & BANK_MASK) != Enc28_Bank)
{
ENC28_writeOp(ENC28_BIT_FIELD_CLR, ECON1, ECON1_BSEL1|ECON1_BSEL0);
Enc28_Bank = addr & BANK_MASK;
ENC28_writeOp(ENC28_BIT_FIELD_SET, ECON1, Enc28_Bank>>5);
}
}
//-> Read & Write Control Registers
uint8_t ENC28_readReg8(uint8_t addr){
ENC28_setBank(addr);
return ENC28_readOp(ENC28_READ_CTRL_REG, addr);
}
void ENC28_writeReg8(uint8_t addr, uint8_t data){
ENC28_setBank(addr);
ENC28_writeOp(ENC28_WRITE_CTRL_REG, addr, data);
}
uint16_t ENC28_readReg16( uint8_t addr)
{
return ENC28_readReg8(addr) + (ENC28_readReg8(addr+1) << 8);
}
void ENC28_writeReg16(uint8_t addrL, uint16_t data)
{
ENC28_writeReg8(addrL, data);
ENC28_writeReg8(addrL+1, data >> 8);
}
//-> Read & Write Buf
void ENC28_readBuf(uint16_t len, uint8_t* data)
{
uint8_t spiData[2];
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);
spiData[0] = ENC28_READ_BUF_MEM;
HAL_SPI_Transmit(&hspi1, spiData, 1, 100);
for(uint16_t x = 1; x < len; ++x){
HAL_SPI_Receive(&hspi1, &spiData[1], 1, 100);
};
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);
}
void ENC28_writeBuf(uint16_t len, uint8_t* data)
{
uint8_t spiData[2];
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);
spiData[0] = ENC28_WRITE_BUF_MEM;
HAL_SPI_Transmit(&hspi1, spiData, 1, 100);
HAL_SPI_Transmit(&hspi1, data, len, 100);
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);
}
//-> Read & Write PHY Registers
uint16_t ENC28_readPhy(uint8_t addr)
{
ENC28_writeReg8(MIREGADR, addr); // Pass the PHY address to the MII
ENC28_writeReg8(MICMD, MICMD_MIIRD); // Enable Read bit
while (ENC28_readReg8(MISTAT) & MISTAT_BUSY); // Poll for end of reading
ENC28_writeReg8(MICMD, 0x00); // Disable MII Read
return ENC28_readReg8(MIRD) + (ENC28_readReg8(MIRD+1) << 8);
}
void ENC28_writePhy(uint8_t addr, uint16_t data)
{
ENC28_writeReg8(MIREGADR, addr);
ENC28_writeReg8(MIWR, data);
while (ENC28_readReg8(MISTAT) & MISTAT_BUSY);
}
//-> Initialization ENC28J60
void ENC28_Init(void)
{
//uint8_t spiData[2];
// (1): Disable the chip CS pin
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);
HAL_Delay(1);
// (2): Perform soft reset to the ENC28J60 module
ENC28_writeOp(ENC28_SOFT_RESET, 0, ENC28_SOFT_RESET);
HAL_Delay(2);
// (3): Wait untill Clock is ready
while(!(ENC28_readOp(ENC28_READ_CTRL_REG, ESTAT) & ESTAT_CLKRDY));
// (4): Initialise RX and TX buffer size
ENC28_writeReg16(ERXST, RXSTART_INIT);
ENC28_writeReg16(ERXND, RXSTOP_INIT);
ENC28_writeReg16(ETXST, TXSTART_INIT);
ENC28_writeReg16(ETXND, TXSTOP_INIT);
ENC28_writeReg16(ERXRDPT, RXSTART_INIT);
ENC28_writeReg16(ERXWRPT, RXSTART_INIT);
// dataWatch16 = ENC28_readReg16(ERXND);
// (5): Reviece buffer filters
ENC28_writeReg8(ERXFCON, ERXFCON_UCEN|ERXFCON_ANDOR|ERXFCON_CRCEN);
// ENC28_writeReg8(ERXFCON, ERXFCON_UCEN|ERXFCON_PMEN|ERXFCON_CRCEN|ERXFCON_BCEN);
// dataWatch8 = ENC28_readReg8(ERXFCON);
// (6): MAC Control Register 1
ENC28_writeReg8(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS|MACON1_PASSALL);
// dataWatch8 = ENC28_readReg8(ERXFCON);
// (7): MAC Control Register 3
ENC28_writeOp(ENC28_BIT_FIELD_SET, MACON3,MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN);
// (8): NON/Back to back gap
ENC28_writeReg16(MAIPG, 0x0C12); // NonBackToBack gap
ENC28_writeReg8(MABBIPG, 0x12); // BackToBack gap
// (9): Set Maximum framelenght
ENC28_writeReg16(MAMXFL, MAX_FRAMELEN); // Set Maximum frame length (any packet bigger will be discarded)
// (10): Set the MAC address of the device
ENC28_writeReg8(MAADR1, MAC_1);
ENC28_writeReg8(MAADR2, MAC_2);
ENC28_writeReg8(MAADR3, MAC_3);
ENC28_writeReg8(MAADR4, MAC_4);
ENC28_writeReg8(MAADR5, MAC_5);
ENC28_writeReg8(MAADR6, MAC_6);
dataWatch8 = ENC28_readReg8(MAADR1);
dataWatch8 = ENC28_readReg8(MAADR2);
dataWatch8 = ENC28_readReg8(MAADR3);
dataWatch8 = ENC28_readReg8(MAADR4);
dataWatch8 = ENC28_readReg8(MAADR5);
dataWatch8 = ENC28_readReg8(MAADR6);
if(dataWatch8==MAC_6) HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);
//**********Advanced Initialisations************//
// (1): Initialise PHY layer registers
ENC28_writePhy(PHLCON, PHLCON_LED);
ENC28_writePhy(PHCON2, PHCON2_HDLDIS);
// (2): Enable Rx interrupt line
ENC28_setBank(ECON1);
ENC28_writeOp(ENC28_BIT_FIELD_SET, ECON1, ECON1_RXEN);
// ENC28_writeOp(ENC28_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE);
// ENC28_writeOp(ENC28_BIT_FIELD_SET, EIR, EIR_PKTIF);
}
//-> Transmitting & Recieving Packets
void ENC28_packetSend(uint16_t len, uint8_t* dataBuf)
{
uint8_t retry = 0;
while(1)
{
ENC28_writeOp(ENC28_BIT_FIELD_SET, ECON1, ECON1_TXRST);
ENC28_writeOp(ENC28_BIT_FIELD_CLR, ECON1, ECON1_TXRST);
ENC28_writeOp(ENC28_BIT_FIELD_CLR, EIR, EIR_TXERIF|EIR_TXIF);
// prepare new transmission
if(retry == 0)
{
ENC28_writeReg16(EWRPT, TXSTART_INIT);
ENC28_writeReg16(ETXND, TXSTART_INIT+len);
ENC28_writeOp(ENC28_WRITE_BUF_MEM, 0, 0x00); //line 485 enc28j60.cpp
ENC28_writeBuf(len, dataBuf);
}
// initiate transmission
ENC28_writeOp(ENC28_BIT_FIELD_SET, ECON1, ECON1_TXRTS);
uint16_t count = 0;
while ((ENC28_readReg8(EIR) & (EIR_TXIF | EIR_TXERIF)) == 0 && ++count < 1000U);
if (!(ENC28_readReg8(EIR) & EIR_TXERIF) && count < 1000U)
{
// no error; start new transmission
break;
}
// cancel previous transmission if stuck
ENC28_writeOp(ENC28_BIT_FIELD_CLR, ECON1, ECON1_TXRTS);
break;
}
}

initializing and using SD card after migration from vivado 2015.2 to 2016.4 does not work

--------------- EDIT: Additional Note -----------------------------------------
We just tried only migrating to Vivado 2016.1. With that version the SD card is working with the new functions, even if it destroys the audio codec somehow. Which is pretty interesting because we looked up every patch note from 2015.2 to 2016.4 and the only thing mentioned is that they added an additional data type for sd card I/O whoch is taken out again in the next version.
----------------END EDIT--------------------------------------------------------
We just migrated our robot project from vivado 2015.2 to 2016.4, after the upgrade the sd image is flashed when the fpga (zynq 7020) ist started but the processor code will not be executed. After some debugging we found out, that we have to create a new SDK project with new FSBL and BSP and include the source files with an new empty application. After this the program got stuck in a loop so we had to debug further. We then found out we had to replace our actual SD card functions ( those recommended in TRM UG585 ) with our old ones.
New SD functions:
void readBlock(unsigned char sd_id, unsigned int sector, unsigned int* buf){
unsigned int baseaddress = sd_id == 0 ? SD0_BASEADDRESS : SD1_BASEADDRESS; // Choose baseaddress based on the desired SD-slot
// START
TO_REG(baseaddress + SD_BLOCK_SIZE_REG_OFFSET) = SD_BLOCKCOUNT1_BLOCKSIZE512; // (1) Set Block Size Reg -> 512 Bytes, (2) Set Block Count Reg -> 1 Block
TO_REG(baseaddress + SD_ARGUMENT_REG_OFFSET) = sector; // (3) Set Argument Reg -> Readaddress
TO_REG(baseaddress + SD_TRANSFER_MODE_COMMAND_REG_OFFSET) = SD_SINGLEBLOCK_READ; // (4/5) Set Transfer Mode / Command Reg -> CMD17, Normal, Data Present, Enable Index-Check, Enable CRC-Check, Response length 48, Single Block, Read, Disable Auto CMD12, Disable Block Count, Disable DMA
while(!SD_CMD_COMPLETE_INTERRUPT(baseaddress)); // (6) Wait for Command Complete Interrupt
TO_REG(baseaddress + SD_INTERRUPT_STATUS_REG_OFFSET) = SD_INTERRUPT_STATUS_CMD_COMPLETE_MASK; // (7) Clear Command Complete Status
// (8) Get Response Data -> ignored, maybe checked for errors and retry
// (9) Write or Read -> Read
while(!SD_BUFFER_READ_RDY_INTERRUPT(baseaddress)); // (10-R) Wait for Buffer Read Ready Interrupt
TO_REG(baseaddress + SD_INTERRUPT_STATUS_REG_OFFSET) = SD_INTERRUPT_STATUS_BUFFER_READ_RDY_MASK; // (11-R) Clear Buffer Read Ready Status
for(unsigned char i = 0; i< 128; i++) // (12-R) Get Block Data
buf[i] = TO_REG(baseaddress + SD_BUFFER_DATA_PORT_REG_OFFSET);
// (13-R) More Blocks? -> No
// (14) Single/Multi/Infinite Block Transfer? -> Single
while(!SD_TRANSFER_COMPLETE_INTERRUPT(baseaddress)); // (15) Wait for Transfer Complete Interrupt
TO_REG(baseaddress + SD_INTERRUPT_STATUS_REG_OFFSET) = SD_INTERRUPT_STATUS_TRANSFER_COMPLETE_MASK; // (16) Clear Transfer Complete Status
// END
}
void writeBlock(unsigned char sd_id, unsigned int sector, unsigned int* buf){
unsigned int baseaddress = sd_id == 0 ? SD0_BASEADDRESS : SD1_BASEADDRESS; // Choose baseaddress based on the desired SD-slot
// START
TO_REG(baseaddress + SD_BLOCK_SIZE_REG_OFFSET) = SD_BLOCKCOUNT1_BLOCKSIZE512; // (1) Set Block Size Reg -> 512 Bytes, (2) Set Block Count Reg -> 1 Block
TO_REG(baseaddress + SD_ARGUMENT_REG_OFFSET) = sector; // (3) Set Argument Reg -> Readaddress
TO_REG(baseaddress + SD_TRANSFER_MODE_COMMAND_REG_OFFSET) = SD_SINGLEBLOCK_WRITE; // (4/5) Set Transfer Mode / Command Reg -> CMD24, Normal, Data Present, Enable Index-Check, Enable CRC-Check, Response length 48, Single Block, Write, Disable Auto CMD12, Disable Block Count, Disable DMA
while(!SD_CMD_COMPLETE_INTERRUPT(baseaddress)); // (6) Wait for Command Complete Interrupt
TO_REG(baseaddress + SD_INTERRUPT_STATUS_REG_OFFSET) = SD_INTERRUPT_STATUS_CMD_COMPLETE_MASK; // (7) Clear Command Complete
Status
// (8) Get Response Data -> ignored, maybe checked for errors and retry
// (9) Write or Read -> Write
while(!SD_BUFFER_WRITE_RDY_INTERRUPT(baseaddress)); // (10-W) Wait for Buffer Write Ready Interrupt
TO_REG(baseaddress + SD_INTERRUPT_STATUS_REG_OFFSET) = SD_INTERRUPT_STATUS_BUFFER_WRITE_RDY_MASK; // (11-W) Clear Buffer Write Ready Status
for(unsigned char i = 0; i< 128; i++) // (12-W) Set Block Data
TO_REG(baseaddress + SD_BUFFER_DATA_PORT_REG_OFFSET) = buf[i];
// (13-W) More Blocks? -> No
// (14) Single/Multi/Infinite Block Transfer? -> Single
while(!SD_TRANSFER_COMPLETE_INTERRUPT(baseaddress)); // (15) Wait for Transfer Complete Interrupt
TO_REG(baseaddress + SD_INTERRUPT_STATUS_REG_OFFSET) = SD_INTERRUPT_STATUS_TRANSFER_COMPLETE_MASK; // (16) Clear Transfer Complete Status
// END
}
Old SD functiony:
DRESULT readBlock(unsigned char sd_id, unsigned long sector, unsigned char* buff){
unsigned int count = 1;
if(sd_id > 1) return RES_ERROR; //only id = 0 or id = 1 is valid
s32 Status;
DWORD LocSector = sector;
/* Convert LBA to byte address if needed */
if ((SdInstance[sd_id].HCS) == 0U) {
LocSector *= (DWORD)XSDPS_BLK_SIZE_512_MASK;
}
Status = XSdPs_ReadPolled(&SdInstance[sd_id], (u32)LocSector, count,(unsigned char *) buff);
if (Status != XST_SUCCESS) {
return RES_ERROR;
}
return RES_OK;
}
DRESULT writeBlock(unsigned char sd_id, unsigned long sector, unsigned char* buff){
unsigned int count = 1;
if(sd_id > 1) return RES_ERROR; //only id = 0 or id = 1 is valid
s32 Status;
DWORD LocSector = sector;
/* Convert LBA to byte address if needed */
if ((SdInstance[sd_id].HCS) == 0U) {
LocSector *= (DWORD)XSDPS_BLK_SIZE_512_MASK;
}
Status = XSdPs_WritePolled(&SdInstance[sd_id], (u32)LocSector, count,buff);
if (Status != XST_SUCCESS) {
return RES_ERROR;
}
return RES_OK;
}
This fixed the problem with the processor code in general but we are still not able to initialize or do IO operations on the SD card. Additionally we found out that when initializing the SD the function getBusWidth (bsp) failes when it tries to call XSdPs_CmdTransfer() -> XSdPs_ReadReg(). Which also seems to be the case when we try to do IO ops on the SD card with our old functions.
SD init function:
unsigned char initSD(unsigned char sd_id){
if(sd_id > 1) return 0xFF; //only id = 0 or id = 1 is valid
DSTATUS s = 0;
s32 Status;
u8 SCR[8] = {0U};
u8 ReadBuff[64] = {0U};
XSdPs_Config *SdConfig = NULL;
/*
* Initialize the host controller
*/
SdConfig = &XSdPs_ConfigTable[sd_id];
Status = XSdPs_CfgInitialize(&SdInstance[sd_id], SdConfig, SdConfig->BaseAddress);
if (Status != XST_SUCCESS) {
s |= STA_NOINIT;
return s;
}
Status = XSdPs_SdCardInitialize(&SdInstance[sd_id]);
if (Status != XST_SUCCESS) {
s |= STA_NOINIT;
return s;
}
Status = XSdPs_Change_ClkFreq(&SdInstance[sd_id], SD_CLK_25_MHZ);
if (Status != XST_SUCCESS) {
s |= STA_NOINIT;
return s;
}
Status = XSdPs_Select_Card(&SdInstance[sd_id]);
if (Status != XST_SUCCESS) {
s |= STA_NOINIT;
return s;
}
Status = XSdPs_Get_BusWidth(&SdInstance[sd_id], SCR);
if (Status != XST_SUCCESS) {
s |= STA_NOINIT;
return s;
}
Status = XSdPs_Get_BusSpeed(&SdInstance[sd_id], ReadBuff);
if (Status != XST_SUCCESS) {
s |= STA_NOINIT;
return s;
}
if((ReadBuff[13] & HIGH_SPEED_SUPPORT) != 0U){
Status = XSdPs_Change_BusSpeed(&SdInstance[sd_id]);
if (Status != XST_SUCCESS) {
s |= STA_NOINIT;
return s;
}
}
Status = XSdPs_Pullup(&SdInstance[sd_id]);
if (Status != XST_SUCCESS) {
s |= STA_NOINIT;
return s;
}
if ((SCR[1] & WIDTH_4_BIT_SUPPORT) != 0U) {
Status = XSdPs_Change_BusWidth(&SdInstance[sd_id]);
if (Status != XST_SUCCESS) {
s |= STA_NOINIT;
return s;
}
}
Status = XSdPs_SetBlkSize(&SdInstance[sd_id], (u16)XSDPS_BLK_SIZE_512_MASK);
if (Status != XST_SUCCESS) {
s |= STA_NOINIT;
return s;
}
After this quite short description of our problem ;), now to my question. Has anyone of you encountered a similar problem and knows a workaround or can point us in a direction where we may find a solution?
Thanks in advance :).
Delet0r
So it worked before, which means your SD configuration seems to be right.
Have you tried the build in XSdPs_CardInitialize(...) function, instead of your customized initSD(...) function? The XSdPs_CardInitialize(...) is inside the xsdps.c of your sdps driver. This function is doing a lot more checks and also a few things in different order, as you are in your initSD(...).
So try this one:
unsigned char initSD(unsigned char sd_id){
if(sd_id > 1) return 0xFF; //only id = 0 or id = 1 is valid
DSTATUS s = 0;
s32 Status;
XSdPs_Config *SdConfig = NULL;
/*
* Initialize the host controller
*/
SdConfig = &XSdPs_ConfigTable[sd_id];
Status = XSdPs_CfgInitialize(&SdInstance[sd_id], SdConfig, SdConfig->BaseAddress);
if (Status != XST_SUCCESS) {
s |= STA_NOINIT;
return s;
}
Status = XSdPs_CardInitialize(&SdInstance[sd_id]);
if (Status != XST_SUCCESS) {
s |= STA_NOINIT;
return s;
}
return Status;
}
We also tried it with Vivado 2016.3 which works for the SD cards even if we dont know why. We have not checked yet if everything else works, but it seems like a workaround for now.
Hopefully it gets fixed in 2017.1

Can't write to SD card file with SPI using Chan FatFs library on a STM32F407

I am using STM32F4-development board with a STM32F407 chip. To communicate with the SD card I use SPI1, and are using the FatFs library created by Chan.
So the gist of the problem is that I have successfully managed to create a file on the SD card, I am able to read from it. But when I try to write to the file it either corrupts the file or prints garbage data like this "{46040EDD-C". If I look at the memmory I can see the stuff I wrote, but somehow it is being written to the wrong memmory address.
Another problem that I have is that the first time I create a file and try to write to it with f_write I get the reply FR_DISK_ERR. The error comes form trying to create a cluster chain, when I step trough the code it works fine. So It might be some delay missing. The next time I run the programm it works, and f_write returns FR_OK.
I am not sure if these problems are related or not. I have been trying to get this to work for about two weeks now and would love any help I can get. Thank you.
The code:
main.c
int main(void)
{
int i;
//SD CARD INIT
//Fatfs object
FATFS FatFs;
//File object
FIL fil;
UINT fa;
FRESULT res_mount, res_open, res_seek, res_write;
delay(10ms);
res_mount = f_mount(&FatFs, "", 1);
if (res_mount == FR_OK) {
GPIO_ToggleBits(GPIOD, GPIO_Pin_12);
delay(10ms);
res_open = f_open(&fil, "test.txt", FA_OPEN_ALWAYS | FA_READ | FA_WRITE);
if (res_open == FR_OK) {
GPIO_ToggleBits(GPIOD, GPIO_Pin_13);
delay(10ms);
res_seek = f_lseek(&fil, f_size(&fil));
if(res_seek == FR_OK)
{
delay(10ms);
GPIO_ToggleBits(GPIOD, GPIO_Pin_14);
res_write = f_write(&fil, "Alpha Beta\n", 11, &fa);
if (fa > 0 && res_write == FR_OK) {
GPIO_ToggleBits(GPIOD, GPIO_Pin_15);
f_sync(&fil);
}
}
f_close(&fil);
}
f_mount(0, "", 1);
}
while(1);
//SD CARD INIT END
}
Chans diskio.c file that I have edited.
#include "diskio.h" /* FatFs lower layer API */
/* Definitions of physical drive number for each drive */
#define DEV_RAM 0 /* Example: Map Ramdisk to physical drive 0 */
#define DEV_MMC 1 /* Example: Map MMC/SD card to physical drive 1 */
#define DEV_USB 2 /* Example: Map USB MSD to physical drive 2 */
static volatile DSTATUS Stat = STA_NOINIT; /* Disk status */
static BYTE CardType; /* Card type flags (b0:MMC, b1:SDv1, b2:SDv2, b3:Block addressing) */
/*-----------------------------------------------------------------------*/
/* Get Drive Status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
if(pdrv)
return STA_NOINIT; // Supports only drive 0
return Stat;
}
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
if (Stat & STA_NODISK)
return Stat; /* No card in the socket? */
//SLOW
uint8_t ty = 0;
ty = SD_CARD_InitialiseCard();
CardType = ty;
if(ty)
Stat &= ~STA_NOINIT;
return Stat;
}
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, /* Start sector in LBA */
UINT count /* Number of sectors to read */
)
{
//DRESULT res;
//int result;
if(pdrv || !count)
return RES_PARERR;
if (Stat & STA_NOINIT)
return RES_NOTRDY;
if(!(CardType & SDCARD_BLOCK))
sector *= 512;
if(count == 1)
{
if((SD_CARD_Cmd(READ_SINGLE_BLOCK, sector) == 0x00) && SD_CARD_Read(buff, 512))
count = 0;
}
else
{
if(SD_CARD_Cmd(READ_MULTIPLE_BLOCKS, sector) == 0)
{
do
{
if(!SD_CARD_Read(buff, 512))
break;
buff += 512;
}
while(--count);
SD_CARD_Cmd(STOP_TRANSMISSION, 0);
}
}
return count ? RES_ERROR : RES_OK;
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Start sector in LBA */
UINT count /* Number of sectors to write */
)
{
//DRESULT res;
//int result;
if (pdrv || !count)
return RES_PARERR;
if (Stat & STA_NOINIT)
return RES_NOTRDY;
if (Stat & STA_PROTECT)
return RES_WRPRT;
if(!(CardType & SDCARD_BLOCK))
sector *= 512;
if(count == 1)
{
if((SD_CARD_Cmd(WRITE_SINGLE_BLOCK, sector) == 0x00) && SD_CARD_Write(buff, 0xFE))
count = 0;
}
else
{
if (CardType & SDCARD_SDC)
SD_CARD_Cmd(SET_WR_BLOCK_ERASE_COUNT, count);
if(SD_CARD_Cmd(WRITE_MULTIPLE_BLOCKS, sector) == 0)
{
do
{
if(!SD_CARD_Write(buff, 0xFC))
break;
buff += 512;
}
while(--count);
if (!SD_CARD_Write(0, 0xFD)) /* STOP_TRAN token */
count = 1;
}
}
return count ? RES_ERROR : RES_OK;
}
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
//DRESULT res;
//int result;
//NOT NEEDED AT THE MOMENT
return RES_PARERR;
}
DWORD get_fattime (void)
{
/* Pack date and time into a DWORD variable */
return ((DWORD)(2017 - 1980) << 25)
| ((DWORD)1 << 21)
| ((DWORD)1 << 16)
| ((DWORD)0 << 11)
| ((DWORD)0 << 5)
| ((DWORD)0 >> 1);
}
SD_CARD.c:
#include "SD_CARD.h"
/*
SPI1(GPIOA): - Type: - SD CARD:
Pin4 - CS - Pin2
Pin5 - SCLK - Pin5
Pin6 - MISO - Pin7
Pin7 - MOSI - Pin3
*/
uint8_t SD_CARD_InitialiseCard()
{
INT i = 0;
SPI1ENABLE();
ChipSelect(SPI1, HIGH);
for(i = 0;i < 16;i++)
SPI_Send(SPI1, 0xFF);
for(i = 0;i < 0xFFFF;i++);
while(SD_CARD_Cmd(GO_IDLE_STATE, 0) != R1_IDLE_STATE); //CMD0
uint32_t r = SD_CARD_Cmd(SEND_IF_COND, 0x1AA); //CMD8
if(r == 0x1AA)
return SD_CARD_InitialiseCardV2();
else if(r == (R1_IDLE_STATE | R1_ILLEGAL_COMMAND))
return SD_CARD_InitialiseCardV1();
else
return SDCARD_FAIL;
}
uint8_t SD_CARD_WriteRead(INT arg)
{
SPI_Send(SPI1, arg);
uint8_t test = SPI_Read(SPI1);
return test;
}
uint8_t SD_CARD_InitialiseCardV1()
{
uint8_t cmd;
INT i = 0;
if(SD_CARD_Cmd(SD_SEND_OP_COND, 0x40040000) <= 1) //ACMD41 - set to 3V, use 0x40200000 for 3V3
cmd = SD_SEND_OP_COND;
else
cmd = SEND_OP_COND;
for(i = 0; i < SD_COMMAND_TIMEOUT;i++)
{
if(SD_CARD_Cmd(cmd, 0) == 0) //CMD1 or ACMD41
{
// Set block length to 512 (CMD16)
if(SD_CARD_Cmd(SET_BLOCKLEN, 512) != 0) //CMD16
return SDCARD_FAIL;
//Init: SEDCARD_V1
if(cmd == SD_SEND_OP_COND)
return SDCARD_V1;
else
return SDCARD_MMCV3;
}
}
//Timeout waiting for v1.x card
return SDCARD_FAIL;
}
uint8_t SD_CARD_InitialiseCardV2()
{
INT i = 0;
INT j = 0;
for(i = 0;i < SD_COMMAND_TIMEOUT;i++)
{
for(j = 0;j < 0xFF;j++);
if(SD_CARD_Cmd(SD_SEND_OP_COND, 0x40040000) == 0) //ACMD41 - set to 3V, use 0x40200000 for 3V3
{
uint32_t ocr = SD_CARD_Cmd(READ_OCR, 0); //CMD58
return (ocr & 0x40000000) ? SDCARD_V2 | SDCARD_BLOCK : SDCARD_V2;
}
}
//Timed out waiting for v2.x card
return SDCARD_FAIL;
}
uint32_t SD_CARD_Cmd(INT cmd, INT arg)
{
struct command_fields com;
com.start_bit = 0;
com.transmitter_bit = 1;
com.index = cmd;
com.argument = arg;
if(cmd == GO_IDLE_STATE)
com.crc = 0x4A;
else if(cmd == SEND_IF_COND)
com.crc = 0x43;
else
com.crc = 0x7F;
com.end_bit = 1;
if(cmd == SD_STATUS | cmd == SET_WR_BLOCK_ERASE_COUNT | cmd == SD_SEND_OP_COND) //ACMDx
SD_CARD_Cmd(APP_CMD, 0); //CMD55
SD_CARD_WriteCom(&com);
if(cmd == SEND_IF_COND)
return SD_CARD_RecieveR7();
else if(cmd == READ_OCR)
return SD_CARD_RecieveR3();
else
return SD_CARD_RecieveR1();
}
void SD_CARD_WriteCom(struct command_fields *com)
{
ChipSelect(SPI1, LOW);
SPI_Send(SPI1, 0xFF);
SPI_Send(SPI1, (0xFF & ((com->start_bit << 7) | (com->transmitter_bit << 6) | com->index)));
SPI_Send(SPI1, (0xFF & (com->argument >> 24)));
SPI_Send(SPI1, (0xFF & (com->argument >> 16)));
SPI_Send(SPI1, (0xFF & (com->argument >> 8)));
SPI_Send(SPI1, (0xFF & com->argument));
SPI_Send(SPI1, (0xFF & ((com->crc << 1) | com->end_bit)));
}
INT SD_CARD_Write(const BYTE *buffer, BYTE token)
{
INT i = 0;
ChipSelect(SPI1, LOW);
while(SD_CARD_WriteRead(0xFF) != 0xFF);
// indicate start of block
SPI_Send(SPI1, token);
if(token != 0xFD)
{
// write the data
for(i = 0;i < 512;i++)
{
SPI_Send(SPI1, *buffer);
buffer++;
}
// write the checksum
SPI_Send(SPI1, 0xFF);
SPI_Send(SPI1, 0xFF);
// check the repsonse token
if(((SD_CARD_WriteRead(0xFF)) & 0x1F) != 0x05)
{
ChipSelect(SPI1, HIGH);
SPI_Send(SPI1, 0xFF);
return SUCCESS;
}
}
// wait for write to finish
while(SD_CARD_WriteRead(0xFF) != 0xFF);
ChipSelect(SPI1, HIGH);
SPI_Send(SPI1, 0xFF);
return ERROR;
}
INT SD_CARD_Read(BYTE *buffer, INT length)
{
INT i = 0;
ChipSelect(SPI1, LOW);
for(i = 0; i < SD_COMMAND_TIMEOUT;i++)
{
// read until start byte (0xFF)
if(SD_CARD_WriteRead(0xFF) == 0xFE)
{
// read data
for(i = 0;i < length;i++)
buffer[i] = SD_CARD_WriteRead(0xFF);
SPI_Send(SPI1, 0xFF); // checksum
SPI_Send(SPI1, 0xFF);
ChipSelect(SPI1, HIGH);
SPI_Send(SPI1, 0xFF);
return SUCCESS;
}
}
return ERROR;
}
uint8_t SD_CARD_RecieveR1()
{
INT i;
uint8_t response = 0xFF;
for(i = 0;i < SD_COMMAND_TIMEOUT;i++)
{
response = SD_CARD_WriteRead(0xFF);
if((response == 0x00) || (response == 0x01))
{
ChipSelect(SPI1, HIGH);
SPI_Send(SPI1, 0xFF);
return response;
}
}
ChipSelect(SPI1, HIGH);
SPI_Send(SPI1, 0xFF);
return 0xFF;
}
uint32_t SD_CARD_RecieveR7()
{
INT i = 0, j = 0;
for(i = 0;i < (SD_COMMAND_TIMEOUT * 1000);i++)
{
uint8_t response[5];
response[0] = SD_CARD_WriteRead(0xFF);
if(!(response[0] & 0x80))
{
for(j = 1;j < 5;j++)
{
response[j] = SD_CARD_WriteRead(0xFF);
}
ChipSelect(SPI1, HIGH);
SPI_Send(SPI1, 0xFF);
return ((response[1] << 24) | (response[2] << 16) | (response[3] << 8) | response[4]);
}
}
ChipSelect(SPI1, HIGH);
SPI_Send(SPI1, 0xFF);
return 0xFFFFFFFF; // timeout
}
uint32_t SD_CARD_RecieveR3()
{
uint32_t ocr = 0;
INT response;
for(int i=0; i < SD_COMMAND_TIMEOUT; i++)
{
response = SD_CARD_WriteRead(0xFF);
if(!(response & 0x80))
{
ocr = SD_CARD_WriteRead(0xFF) << 24;
ocr |= SD_CARD_WriteRead(0xFF) << 16;
ocr |= SD_CARD_WriteRead(0xFF) << 8;
ocr |= SD_CARD_WriteRead(0xFF);
ChipSelect(SPI1, HIGH);
SPI_Send(SPI1, 0xFF);
return ocr;
}
}
ChipSelect(SPI1, HIGH);
SPI_Send(SPI1, 0xFF);
return 0xFFFFFFFF; // timeout
}
INT SD_CARD_InitialiseDisk()
{
if(SD_CARD_InitialiseCard() == SDCARD_FAIL)
return SDCARD_FAIL;
SPI_SetSpeed(SPI1, SPI_SPEED_1300KHz);
return SUCCESS;
}
SPI.c:
#include "SPI.h"
void SPI1ENABLE()
{
GPIO_InitTypeDef GPIO_InitStruct;
SPI_InitTypeDef SPI_InitStruct;
static uint8_t SPI1_ENABLED = 0;
if(SPI1_ENABLED)
return;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
GPIO_InitStruct.GPIO_Pin = SPI1_SCLK | SPI1_MISO | SPI1_MOSI;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = SPI1_CS;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_Init(GPIOA, &GPIO_InitStruct);
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_Init(SPI1, &SPI_InitStruct);
SPI_Cmd(SPI1, ENABLE);
SPI1_ENABLED = 1;
}
uint8_t SPI_Read(SPI_TypeDef* SPIx)
{
while(SPI1->SR & SPI_I2S_FLAG_BSY); // wait until SPI is not busy anymore
while(!(SPI1->SR & SPI_I2S_FLAG_RXNE)); // wait until receive complete
return SPI_ReceiveData(SPIx);
}
void SPI_Send(SPI_TypeDef* SPIx, unsigned char Data)
{
while(SPI1->SR & SPI_I2S_FLAG_BSY); // wait until SPI is not busy anymore
while(!(SPI1->SR & SPI_I2S_FLAG_TXE)); // wait until transmit complete
SPI_SendData(SPIx, Data);
while(!(SPI1->SR & SPI_I2S_FLAG_RXNE)); // wait until receive complete
}
So I found out my problem. As stupid as I am, in my write function I forgot to return success when sending STOP_TRANs token. Here is how the write function looks like after editing.
INT SD_CARD_Write(const BYTE *buffer, BYTE token)
{
INT i = 0;
ChipSelect(SPI1, LOW);
while(SD_CARD_WriteRead(0xFF) != 0xFF);
// indicate start of block
SPI_Send(SPI1, token);
if(token != 0xFD)
{
// write the data
for(i = 0;i < 512;i++)
{
SPI_Send(SPI1, *buffer);
buffer++;
}
// write the checksum
SPI_Send(SPI1, 0xFF);
SPI_Send(SPI1, 0xFF);
// check the repsonse token
uint8_t resp = 0x00;
do
{
resp = SD_CARD_WriteRead(0xFF);
}
while(resp == 0x00);
if((resp & 0x1F) != 0x05)
{
// wait for write to finish
while(SD_CARD_WriteRead(0xFF) != 0xFF);
SPI_Send(SPI1, 0xFF);
ChipSelect(SPI1, HIGH);
SPI_Send(SPI1, 0xFF);
return SUCCESS;
}
}
// wait for write to finish
while(SD_CARD_WriteRead(0xFF) != 0xFF);
SPI_Send(SPI1, 0xFF);
ChipSelect(SPI1, HIGH);
SPI_Send(SPI1, 0xFF);
if(token == 0xFD)
return SUCCESS;
return ERROR;
}

STM32 USB OTG HOST Library hangs trying to create file with FatFs

I am trying to create a file with FatFs on USB flash, but my f_open call trying to read boot sector for first time file system mount hangs on this function.
DRESULT disk_read (
BYTE drv, /* Physical drive number (0) */
BYTE *buff, /* Pointer to the data buffer to store read data */
DWORD sector, /* Start sector number (LBA) */
BYTE count /* Sector count (1..255) */
)
{
BYTE status = USBH_MSC_OK;
if (drv || !count) return RES_PARERR;
if (Stat & STA_NOINIT) return RES_NOTRDY;
if(HCD_IsDeviceConnected(&USB_OTG_Core))
{
do
{
status = USBH_MSC_Read10(&USB_OTG_Core, buff,sector,512 * count);
USBH_MSC_HandleBOTXfer(&USB_OTG_Core ,&USB_Host);
if(!HCD_IsDeviceConnected(&USB_OTG_Core))
{
return RES_ERROR;
}
}
while(status == USBH_MSC_BUSY ); // Loop which create hanging state
}
if(status == USBH_MSC_OK)
return RES_OK;
return RES_ERROR;
}
The main problem is the loop which creates hanging state
while(status == USBH_MSC_BUSY );
So I do not know what to do to avoid this. Using debugger I discover that state is caused by parameter CmdStateMachine of structure USBH_MSC_BOTXferParam, type USBH_BOTXfer_TypeDef is equal CMD_UNINITIALIZED_STATE which actually cause miss up of switch statement of USBH_MSC_Read10 function.
/**
* #brief USBH_MSC_Read10
* Issue the read command to the device. Once the response received,
* it updates the status to upper layer
* #param dataBuffer : DataBuffer will contain the data to be read
* #param address : Address from which the data will be read
* #param nbOfbytes : NbOfbytes to be read
* #retval Status
*/
uint8_t USBH_MSC_Read10(USB_OTG_CORE_HANDLE *pdev,
uint8_t *dataBuffer,
uint32_t address,
uint32_t nbOfbytes)
{
uint8_t index;
static USBH_MSC_Status_TypeDef status = USBH_MSC_BUSY;
uint16_t nbOfPages;
status = USBH_MSC_BUSY;
if(HCD_IsDeviceConnected(pdev))
{
switch(USBH_MSC_BOTXferParam.CmdStateMachine)
{
case CMD_SEND_STATE:
/*Prepare the CBW and relevant field*/
USBH_MSC_CBWData.field.CBWTransferLength = nbOfbytes;
USBH_MSC_CBWData.field.CBWFlags = USB_EP_DIR_IN;
USBH_MSC_CBWData.field.CBWLength = CBW_LENGTH;
USBH_MSC_BOTXferParam.pRxTxBuff = dataBuffer;
for(index = CBW_CB_LENGTH; index != 0; index--)
{
USBH_MSC_CBWData.field.CBWCB[index] = 0x00;
}
USBH_MSC_CBWData.field.CBWCB[0] = OPCODE_READ10;
/*logical block address*/
USBH_MSC_CBWData.field.CBWCB[2] = (((uint8_t*)&address)[3]);
USBH_MSC_CBWData.field.CBWCB[3] = (((uint8_t*)&address)[2]);
USBH_MSC_CBWData.field.CBWCB[4] = (((uint8_t*)&address)[1]);
USBH_MSC_CBWData.field.CBWCB[5] = (((uint8_t*)&address)[0]);
/*USBH_MSC_PAGE_LENGTH = 512*/
nbOfPages = nbOfbytes/ USBH_MSC_PAGE_LENGTH;
/*Tranfer length */
USBH_MSC_CBWData.field.CBWCB[7] = (((uint8_t *)&nbOfPages)[1]) ;
USBH_MSC_CBWData.field.CBWCB[8] = (((uint8_t *)&nbOfPages)[0]) ;
USBH_MSC_BOTXferParam.BOTState = USBH_MSC_SEND_CBW;
/* Start the transfer, then let the state machine
manage the other transactions */
USBH_MSC_BOTXferParam.MSCState = USBH_MSC_BOT_USB_TRANSFERS;
USBH_MSC_BOTXferParam.BOTXferStatus = USBH_MSC_BUSY;
USBH_MSC_BOTXferParam.CmdStateMachine = CMD_WAIT_STATUS;
status = USBH_MSC_BUSY;
break;
case CMD_WAIT_STATUS:
if((USBH_MSC_BOTXferParam.BOTXferStatus == USBH_MSC_OK) && \
(HCD_IsDeviceConnected(pdev)))
{
/* Commands successfully sent and Response Received */
USBH_MSC_BOTXferParam.CmdStateMachine = CMD_SEND_STATE;
status = USBH_MSC_OK;
}
else if (( USBH_MSC_BOTXferParam.BOTXferStatus == USBH_MSC_FAIL ) && \
(HCD_IsDeviceConnected(pdev)))
{
/* Failure Mode */
USBH_MSC_BOTXferParam.CmdStateMachine = CMD_SEND_STATE;
}
else if ( USBH_MSC_BOTXferParam.BOTXferStatus == USBH_MSC_PHASE_ERROR )
{
/* Failure Mode */
USBH_MSC_BOTXferParam.CmdStateMachine = CMD_SEND_STATE;
status = USBH_MSC_PHASE_ERROR;
}
else
{
/* Wait for the Commands to get Completed */
/* NO Change in state Machine */
}
break;
default:
break;
}
}
return status;
}
Here is USBH_BOTXfer_TypeDef type declaration;
typedef struct _BOTXfer
{
uint8_t MSCState;
uint8_t MSCStateBkp;
uint8_t MSCStateCurrent;
uint8_t CmdStateMachine;
uint8_t BOTState;
uint8_t BOTStateBkp;
uint8_t* pRxTxBuff;
uint16_t DataLength;
uint8_t BOTXferErrorCount;
uint8_t BOTXferStatus;
} USBH_BOTXfer_TypeDef;
During the debug I discover that all fields of it is 0x00.
Here are my FatFs calls
int main(void)
{
FATFS Fat;
FIL file;
FRESULT fr;
RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;
/* Enable SWO output */
DBGMCU->CR = 0x00000020;
GPIOD->MODER=0x55000000;
GPIOD->OTYPER = 0x00000000;
GPIOD->OSPEEDR = 0x00000001;
while(1)
{
if (!USB_MSC_IsInitialized())
{
USB_MSC_Initialize();
}
if (USB_MSC_IsConnected())
{
GPIOD->ODR = (1 << 15);
disk_initialize(0);
fr = f_mount(0, &Fat);
if(fr == FR_OK)
{
fr = f_open(&file,"0:DP_lab8.pdf",(FA_CREATE_ALWAYS | FA_WRITE));
if (fr == FR_OK)
{
f_close(&file);
}
f_mount(0, NULL);
}
}
else
{
GPIOD->ODR = (1 << 14);
}
USB_MSC_Main();
}
}
USB_MSC_IsConnected function is:
int USB_MSC_IsConnected(void)
{
if (g_USB_MSC_HostStatus == USB_DEV_NOT_SUPPORTED)
{
USB_MSC_Uninitialize();
}
return !(g_USB_MSC_HostStatus == USB_DEV_DETACHED ||
g_USB_MSC_HostStatus == USB_HOST_NO_INIT ||
g_USB_MSC_HostStatus == USB_DEV_NOT_SUPPORTED);
}
And device states are:
typedef enum
{
USB_HOST_NO_INIT = 0, /* USB interface not initialized */
USB_DEV_DETACHED, /* no device connected */
USB_SPEED_ERROR, /* unsupported USB speed */
USB_DEV_NOT_SUPPORTED, /* unsupported device */
USB_DEV_WRITE_PROTECT, /* device is write protected */
USB_OVER_CURRENT, /* overcurrent detected */
USB_DEV_CONNECTED /* device connected and ready */
} USB_HostStatus;
The value of g_USB_MSC_HostStatus is received by standard USB HOST user callbacks.
I think it is a bug in ST host library. I've hunted it down, as my usb host was unable to pass enumeration stage. After a fix the stack is Ok.
There is union _USB_Setup in usbh_def.h file in "STM32Cube/Repository/STM32Cube_FW_F7_V1.13.0/Middlewares/ST/STM32_USB_Host_Library/Core/Inc" (any chip, not only F7, any version, not only V1.13.0). It has uint16_t bmRequestType and uint16_t bRequest. These two fileds must be uint8_t as of USB specs. Fixing this issue made usb host go as needed. Enumeration stage passes ok, and all other stages as well.

Resources