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;
}
}
Related
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
Within a project of mine I use DMA to share data between the two cores of the STM32H747, the data comes from an ADC controlled by the core M7. While testing my DMA I noticed that the data stored within the memory by the DMA wasn't varying and noticed that my ADC does not work as intended. I wrote a testing code for my ADC for it to work in single ended mode and continuous mode. So first I init my ADC :
void ADC_Init (void) {
uint32_t ISR = *ADC_ISR;
uint32_t IER = *ADC_IER;
uint32_t CR = *ADC_CR;
uint32_t CFGR = *ADC_CFGR;
uint32_t CFGR2 = *ADC_CFGR2;
uint32_t SMPR1 = *ADC_SMPR1;
uint32_t PCSEL = *ADC_PCSEL;
uint32_t DR = *ADC_DR;
uint32_t DIFSEL = *ADC_DIFSEL;
uint32_t SQR1 = *ADC_SQR1;
uint32_t SQR2 = *ADC_SQR2;
uint32_t SQR3 = *ADC_SQR3;
uint32_t SQR4 = *ADC_SQR4;
uint32_t CSR = *ADC1_CSR;
uint32_t CCR = *ADC1_CCR;
*RCC_AHB4ENR |= (1 << 0); //Enable clock GPIO port A
*GPIOA_MODER |= (3 << 0); //Analog mode port 0
/******Enable ADC Voltage regulator******/
CR = 0x00000000; //Fin du deep power down
*ADC_CR = CR;
CR = 0x10000000; //ADC voltage regulator Enable
*ADC_CR = CR;
while (*ADC_CR & (1 << 28) != 0) {} //check volatage enable (peut etre remplacé par un :
//delayMicroseconds(5); mais c'est moins safe)
//Petit interlude Differentiel ou single ended (a faire avant ADEN)---------
DIFSEL = 0x00000000;
*ADC_DIFSEL = DIFSEL;
//digitalWrite(LEDR, LOW);
//while (!(ADC1_ISR & (1<<12))); //LDORDY: ADC LDO output voltage ready bit
/******Calibrate ADC*********/
/******Enable ADC CLOCK******/
*RCC_AHB1ENR |= (1 << 5); //ADC peripheral clock enable
//RCC_D3CCIPR&= ~(7<<16)// ADCSEL[1:0]: SAR ADC kernel clock source selection default
digitalWrite(LEDB, LOW);
delay(2000);
digitalWrite(LEDB, HIGH);
delay(2000);
digitalWrite(LEDB, LOW);
delay(2000);
digitalWrite(LEDB, HIGH);
/******Set the prescalar******/
CCR = 0x000F0000;
*ADC1_CCR = CCR;
/******Set Scan Mode Data Management and resolution******/
CFGR |= (6 << 2); //RES[2:0]: Data resolution 110=12bits
CFGR &= ~(3 << 0); //DMNGT[1:0]: Data Management configuration 00 data stored in DR only
*ADC_CFGR = CFGR;
*ADC_CFGR2 |= (1 << 5);
//ADC regular sequence register 1
SQR1 = 0x0000040; //1st conv correspond au chan 0 (00000) et on réalise une conv par sequence de 1 conv(0000)
*ADC_SQR1 = SQR1;
/******Set the Continuous Conversion, ******/
CFGR |= (1 << 13); //CONT: Single / continuous conversion mode for regular conversions
*ADC_CFGR = CFGR;
/******Set the Sampling Time for the channels in ADC_SMPRx******/
SMPR1 = (7 << 0); //SMP0[2:0]: Channel 0 sampling time selection 011: 16.5 ADC clock cycles
*ADC_SMPR1 = SMPR1;
PCSEL |= (1 << 0); //PCSEL[19:0] :Channel 0 (VINP[i]) pre selection
*ADC_PCSEL = PCSEL;
/******Set singleEnded Input******/
DIFSEL &= ~(1 << 0); //DIFSEL[19:0]: single ended mode for channel 0
*ADC_DIFSEL = DIFSEL;
}
And I start it with :
void ADC_start (void) {
uint32_t ISR = *ADC_ISR;
uint32_t IER = *ADC_IER;
uint32_t CR = *ADC_CR;
uint32_t CFGR = *ADC_CFGR;
uint32_t CFGR2 = *ADC_CFGR2;
uint32_t SMPR1 = *ADC_SMPR1;
uint32_t PCSEL = *ADC_PCSEL;
uint32_t DR = *ADC_DR;
uint32_t DIFSEL = *ADC_DIFSEL;
uint32_t SQR1 = *ADC_SQR1;
uint32_t SQR2 = *ADC_SQR2;
uint32_t SQR3 = *ADC_SQR3;
uint32_t SQR4 = *ADC_SQR4;
uint32_t CSR = *ADC1_CSR;
uint32_t CCR = *ADC1_CCR;
CR &= ~(1 << 30);
*ADC_CR = CR;
//On a deja ADCALDIF en single ended inputs mode par nos initialisations précédentes
CR = (1 << 16); //ADCALLIN calibration linéaire ET offset
*ADC_CR = CR;
//CR=0x90010001;
*ADC_CR |= (1 << 31); //Lancer une calibration ADCAL=1
//dataFromRegister=*ADC_CR;
while (*ADC_CR & (1 << 31) != 0) {
digitalWrite(LEDR, HIGH);
delay(1000);
digitalWrite(LEDR, LOW);
delay(1000);
} digitalWrite(LEDR, HIGH);
//On attends que la calibration soit complète par un reset de ADCAL
//Processus de calibration terminé (Serial.println(/*dataFromRegister,BIN*/"calibration");)
dataFromRegister=*ADC_CR;
Serial.println(dataFromRegister,BIN);
/******Enable ADC******/
*ADC_ISR |= (1 << 0); // Reset ADC ready flag
*ADC_CR |= (1 << 0); //Enable ADC
while (!(*ADC_ISR & (1 << 0))); //Wait for ready flag
*ADC_ISR |= (1 << 0);
//*ADC_CR |= (3 << 8);//11 boost if ADC Clock entre 25 et 50MHz
CR |= (1 << 2); //ADSTART
*ADC_CR = CR;
// if (*ADC_ISR & (1 << 1)) {
// CR |= (1 << 2); //ADSTART
// *ADC_CR = CR;
// }
//while(!(*ADC_ISR & (1 << 1)));
digitalWrite(LEDR, LOW);
}
Now the continuous conversion mode should start a new conversion continuously, and set the EOC (End Of Conversion) flag which I can check to go read ADC_DR (Data Register). That is what I do within my loop :
void loop() {
Serial.println("Begin Loop");
while ((*ADC_ISR & (1 << 2)) == 0) {
Serial.print(".");
delay(100);
}
dataFromADC = *ADC_DR;
*ADC_ISR|=(1<<2);
Serial.println(dataFromADC, BIN);
digitalWrite(LEDB, LOW);
}
According to the datasheet if I read the data from the ADC_DR register the EOC flag should reset himself yet this code does not work without forcing it to reset itself (and even with that it works randomly). I must admit I am quite lost here as to why it does not work, I am currently checking every bit of my code (mainly using digitalWrite to check where the code stop and without the EOC reset it stops within the EOC check but still gives me that same 100000000000 ADC_DR read no matter what signal I put on pin PA_0C). Here is how I defined my registers :
//GPIO REG----------------------------------------
volatile uint32_t* const GPIOA_MODER = (uint32_t *) 0x58020000;//Port GPIOA correspondant à l'ADC0
//--------------------ADC REGISTRES-------------------//
volatile uint32_t* const ADC_ISR = (uint32_t *) 0x40022000;//Interupt and status register
volatile uint32_t* const ADC_IER = (uint32_t *) 0x40022004;//Interupt enable register
volatile uint32_t* const ADC_CR = (uint32_t *) 0x40022008;//Control register
volatile uint32_t* const ADC_CFGR = (uint32_t *) 0x4002200C;//COnfiguration register
volatile uint32_t* const ADC_CFGR2 = (uint32_t *) 0x40022010;//2eme conf regrister
volatile uint32_t* const ADC_SMPR1 = (uint32_t *) 0x40022014; //Sample time reg (directement lié au temps de calc de l'ADC)
volatile uint32_t* const ADC_SMPR2 = (uint32_t *) 0x40022018;
volatile uint32_t* const ADC_PCSEL = (uint32_t*) 0x4002201C;//channel preselection register on choisis un chan pour la conv
volatile uint32_t* const ADC_DR = (uint32_t *) 0x40022040;//Registre où l'on stocke le resultat des conv
volatile uint32_t* const ADC_DIFSEL = (uint32_t *) 0x400220C0;
volatile uint32_t* const ADC_SQR1 = (uint32_t *) 0x40022030;
volatile uint32_t* const ADC_SQR2 = (uint32_t *) 0x40022034;
volatile uint32_t* const ADC_SQR3 = (uint32_t *) 0x40022038;
volatile uint32_t* const ADC_SQR4 = (uint32_t *) 0x4002203C;
volatile uint32_t* const ADC1_CSR = (uint32_t *) 0x40022300;//ADC1 common status register
volatile uint32_t* const ADC1_CCR = (uint32_t *)0x40022308; //ADC1 Common Control Register
//REG RCC-----------------------------------------------
volatile uint32_t* const RCC_APB4ENR = (uint32_t *) 0x580244F4;//to enable sysconf
volatile uint32_t* const RCC_AHB4ENR = (uint32_t *) 0x580244E0;
volatile uint32_t* const RCC_AHB1ENR = (uint32_t *) 0x580244D8;
volatile uint32_t* const RCC_CR = (uint32_t *) 0x58024400;
volatile uint32_t* const RCC_CFGR = (uint32_t *) 0x58024410;
volatile uint32_t* const RCC_D1CFGR = (uint32_t *) 0x58024418;
volatile uint32_t* const RCC_D2CFGR = (uint32_t *) 0x5802441C;
volatile uint32_t* const RCC_D3CFGR = (uint32_t *) 0x58024420;
volatile uint32_t* const RCC_PLLCKSELR = (uint32_t *) 0x58024428;
volatile uint32_t* const RCC_PLLCFGR = (uint32_t *) 0x5802442C;
volatile uint32_t* const RCC_PLL1DIVR = (uint32_t *) 0x58024430;
volatile uint32_t* const RCC_PLL1FRACR = (uint32_t *) 0x58024434;
volatile uint32_t* const RCC_PLL2DIVR = (uint32_t *) 0x58024438;
volatile uint32_t* const RCC_PLL2FRACR = (uint32_t *) 0x5802443C;
volatile uint32_t* const RCC_PLL3DIVR = (uint32_t *) 0x58024440;
volatile uint32_t* const RCC_PLL3FRACR = (uint32_t *) 0x58024444;
volatile uint32_t* const RCC_CIER = (uint32_t *) 0x58024460;
I must say I am no longer sure about the ADC1_CCR address, in the datasheet they say to add a 0x300 offset to the master base address, I considered the base address to be the address of ADC1-ADC2 as referenced page 140 of the datasheet : https://www.st.com/resource/en/reference_manual/dm00176879-stm32h745755-and-stm32h747757-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
However if I misunderstood what the master address for ADC1 and ADC2 is that would explain a lot of things as the clock would no longer be defined.
I will keep trying and I welcome any piece of advice.
Here the working code (I switched to CMSIS) there were quite a lot of errors in my first version it also seems to only work on PA0_C if I use ADC2 and not ADC1:
#include <Arduino.h>
#define HWREG(x) (*((volatile uint32_t *)(x)))
// put your setup code here, to run once:
void GPIOH_Init();
void GPIOH_Port15_Toggle();
//void SystemClock_Config();
void SystemCLCKInit();
//void SysPinConfig(); //For future tests of ADC with EXTI1 IT (put the pin setup PA0 PA0_C here as well)
void ADC_start();
void ADC_Init();
//void VREFBUF_Init();
//void ADC_Reset();
uint32_t dataFromADC = 0;
int dataFromRegister = 0;
int nbSamples = 0;
void setup() {
Serial.begin(115200);
SystemCLCKInit();
// put your setup code here, to run once:
//SystemClock_Config();
GPIOH_Init();
digitalWrite(LEDR, LOW);
delay(1000);
VREFBUF_Init();
digitalWrite(LEDR, HIGH);
digitalWrite(LEDB, LOW);
delay(1000);
ADC_Init();
//analogReference(EXTERNAL);
digitalWrite(LEDB, HIGH);
digitalWrite(LEDG, LOW);
delay(1000);
ADC_Start();
digitalWrite(LEDG, HIGH);
digitalWrite(LEDB, LOW);
}
void loop() {
// put your main code here, to run repeatedly:
while(ADC_ISR_EOC & ADC_ISR_EOC_Msk!=1){//check end of conversion
// dataFromRegister=READ_REG(ADC2->ISR);
// Serial.print("Flag :");
// Serial.println(dataFromRegister,BIN);
//delay(100);
}dataFromADC = READ_REG(ADC2->DR);
Serial.print("ADC_DR:");
Serial.println(dataFromADC);
GPIOH_Port15_Toggle();
}
void VREFBUF_Init(void) {
SET_BIT(RCC->APB4ENR,RCC_APB4ENR_VREFEN_Msk);
//RCC_APB4ENR_VREFEN;
//RCC->APB4ENR|=(1<<RCC_APB4ENR_VREFEN_Msk);
delay(1000);
dataFromRegister=READ_REG(RCC->APB4ENR);
Serial.print("CLCK : ");
Serial.println(dataFromRegister,BIN);
delay(1000);
SET_BIT(VREFBUF->CSR,VREFBUF_CSR_ENVR_Msk);
CLEAR_BIT(VREFBUF->CSR,VREFBUF_CSR_HIZ_Msk);
while(VREFBUF_CSR_VRR & VREFBUF_CSR_VRR_Msk !=1){
dataFromRegister=READ_REG(VREFBUF->CSR);
Serial.println(dataFromRegister,BIN);
}
}
void ADC_Init(void) {
/***************CLOCK and PIN config******************/
// MODIFY_REG(RCC->D3CCIPR,RCC_D3CCIPR_ADCSEL_Msk,0x1L);
// delay(1000);
// dataFromRegister=READ_REG(RCC->D3CCIPR);
// Serial.print("D3CCIPR :");
// Serial.println(dataFromRegister,BIN);
SET_BIT(RCC->APB4ENR,RCC_APB4ENR_SYSCFGEN_Msk);
delay(1000);
dataFromRegister=READ_REG(RCC->APB4ENR);
Serial.print("RCC_APB4ENR:");
Serial.println(dataFromRegister,BIN);
SET_BIT(RCC->APB4ENR,RCC_APB4ENR_RTCAPBEN_Msk);
delay(1000);
dataFromRegister=READ_REG(RCC->APB4ENR);
Serial.print("RCC_APB4ENR1:");
Serial.println(dataFromRegister,BIN);
SET_BIT(RCC->AHB1ENR,RCC_AHB1ENR_ADC12EN_Msk);
delay(1000);
dataFromRegister=READ_REG(RCC->AHB1ENR);
Serial.print("RCC_AHB1ENR:");
Serial.println(dataFromRegister,BIN);
//GPIOA PORT 1------------------------------------
SET_BIT(RCC->AHB4ENR,RCC_AHB4ENR_GPIOAEN_Msk);
delay(1000);
dataFromRegister=READ_REG(RCC->AHB4ENR);
Serial.print("RCC_AHB4ENR:");
Serial.println(dataFromRegister,BIN);
SET_BIT(GPIOA->MODER, GPIO_MODER_MODE1_0);
SET_BIT(GPIOA->MODER, GPIO_MODER_MODE1_1);
CLEAR_BIT(GPIOA->PUPDR, GPIO_PUPDR_PUPD1_0);
CLEAR_BIT(GPIOA->PUPDR, GPIO_PUPDR_PUPD1_1);
SET_BIT(SYSCFG->PMCR, SYSCFG_PMCR_PA0SO_Msk);
delay(1000);
dataFromRegister=READ_REG(SYSCFG->PMCR);
Serial.print("SYSCFG->PMCR:");
Serial.println(dataFromRegister,BIN);
SET_BIT(RCC->AHB1ENR,RCC_AHB1ENR_ADC12EN_Msk);
delay(1000);
dataFromRegister=READ_REG(RCC->AHB1ENR);
Serial.print("RCC->AHB1ENR2:");
Serial.println(dataFromRegister,BIN);
/***************ADC not Enabled yet********************/
//ADC VOLTAGE REGULATOR-----------------------------
CLEAR_BIT(ADC2->CR,ADC_CR_DEEPPWD_Msk);
//dataFromRegister=READ_REG(ADC2->CR);
//Serial.print("ADCR: ");
//Serial.println(dataFromRegister,BIN);
//delay(1000);
SET_BIT(ADC2->CR,ADC_CR_ADVREGEN_Msk);
delay(1000);
dataFromRegister=READ_REG(ADC2->CR);
Serial.print("ADCAL: ");
Serial.println(dataFromRegister,BIN);
delay(1000);
//
//ADC CALIBRATION-----------------------------------
CLEAR_BIT(ADC2->CR,ADC_CR_ADCALDIF_Msk);
SET_BIT(ADC2->CR,ADC_CR_ADCALLIN_Msk);
SET_BIT(ADC2->CR,ADC_CR_ADCAL_Msk);
while(ADC_CR_ADCAL & ADC_CR_ADCAL_Msk!=0){
dataFromRegister=READ_REG(ADC2->CR);
Serial.print("ADC2_CR: ");
Serial.println(dataFromRegister,BIN);
}
//ADC prescaler selection clock and sample time delay
//MODIFY_REG(ADC22_COMMON->CCR,ADC_CCR_CKMODE | ADC_CCR_PRESC | ADC_CCR_DELAY ,0x3UL | 0x3UL | 0x8UL);
SET_BIT(ADC12_COMMON->CCR, ADC_CCR_CKMODE_0 | ADC_CCR_CKMODE_1 | ADC_CCR_PRESC_0 | ADC_CCR_PRESC_1 | ADC_CCR_DELAY_3);
// MODIFY_REG(ADC12_COMMON->CCR,ADC_CCR_PRESC_Msk,0x3UL);
// MODIFY_REG(ADC12_COMMON->CCR,ADC_CCR_DELAY_Msk,0x8UL);
dataFromRegister=READ_REG(ADC12_COMMON->CCR);
Serial.print("ADC_CCR: ");
Serial.println(dataFromRegister,BIN);
delay(1000);
//Set input mode------------------------------------
CLEAR_BIT(ADC2->DIFSEL,ADC_DIFSEL_DIFSEL_0);
/*******************ADC Enable**********************/
SET_BIT(ADC2->ISR,ADC_ISR_ADRDY_Msk);
SET_BIT(ADC2->CR,ADC_CR_ADEN_Msk);
while(ADC_ISR_ADRDY & ADC_ISR_ADRDY_Msk !=1){}
SET_BIT(ADC2->ISR,ADC_ISR_ADRDY_Msk);
/********************ADC Enabled********************/
//ADC CFGR------------------------------------------
SET_BIT(ADC2->CFGR, ADC_CFGR_RES_2 | ADC_CFGR_RES_1 | ADC_CFGR_OVRMOD_Msk | ADC_CFGR_CONT_Msk);
CLEAR_BIT(ADC2->CFGR, ADC_CFGR_DMNGT_0 | ADC_CFGR_DMNGT_1 | ADC_CFGR_RES_0 | ADC_CFGR_EXTEN_0 | ADC_CFGR_EXTEN_1);
//DATA ALIGNEMENT-----------------------------------
// ADC PCSEL----------------------------------------
SET_BIT(ADC2->PCSEL,ADC_PCSEL_PCSEL_0);
//ADC SQR1------------------------------------------
//SET_BIT(ADC2->SQR1,ADC_SQR1_SQ1_1);
//ADC SMPR1-----------------------------------------
SET_BIT(ADC2->SMPR1,ADC_SMPR1_SMP0_0 | ADC_SMPR1_SMP0_1);
}
void ADC_Start(void) {
SET_BIT(ADC2->CR,ADC_CR_ADSTART_Msk);
}
void GPIOH_Init(void) {
SET_BIT(RCC->AHB4ENR,RCC_AHB4ENR_GPIOHEN_Msk);
delay(1000);
SET_BIT(GPIOH->MODER, GPIO_MODER_MODE15_0);
CLEAR_BIT(GPIOH->MODER, GPIO_MODER_MODE15_1);
CLEAR_BIT(GPIOH->OTYPER, GPIO_OTYPER_OT15);
}
void GPIOH_Port15_Toggle(void) {
SET_BIT(GPIOH->BSRR, GPIO_BSRR_BS15);
CLEAR_BIT(GPIOH->BSRR, GPIO_BSRR_BS15);
SET_BIT(GPIOH->BSRR, GPIO_BSRR_BR15);
CLEAR_BIT(GPIOH->BSRR, GPIO_BSRR_BR15);
}
void SystemCLCKInit(void) {
/* Enable the floating-point unit. Any configuration of the
floating-point unit must be done here prior to it being enabled */
HWREG(0xE000ED88) = ((HWREG(0xE000ED88) & ~0x00F00000) | 0x00F00000);
/*------- Reset the RCC clock configuration to the default reset state -------*/
/* Set HSION bit */
RCC->CR |= 0x00000001;
dataFromRegister=READ_REG(RCC->CR);
Serial.print("CR: ");
Serial.println(dataFromRegister,BIN);
delay(1000);
/* Reset CFGR register */
RCC->CFGR = 0x00000000;
dataFromRegister=READ_REG(RCC->CFGR);
Serial.print("RCC->CFGR: ");
Serial.println(dataFromRegister,BIN);
delay(1000);
/* Reset HSEON, CSSON , CSION,RC48ON, CSIKERON PLL1ON, PLL2ON and PLL3ON bits */
RCC->CR &= (uint32_t)0xEAF6ED7F;
dataFromRegister=READ_REG(RCC->CR);
Serial.print("RCC->CR: ");
Serial.println(dataFromRegister,BIN);
delay(1000);
/* Reset D1CFGR register */
RCC->D1CFGR = 0x00000000;
dataFromRegister=READ_REG(RCC->D1CFGR);
Serial.print("RCC->D1CFGR: ");
Serial.println(dataFromRegister,BIN);
delay(1000);
/* Reset D2CFGR register */
RCC->D2CFGR = 0x00000000;
dataFromRegister=READ_REG(RCC->D2CFGR);
Serial.print("RCC->D2CFGR: ");
Serial.println(dataFromRegister,BIN);
delay(1000);
/* Reset D3CFGR register */
RCC->D3CFGR = 0x00000000;
dataFromRegister=READ_REG(RCC->D3CFGR);
Serial.print("RCC->D3CFGR: ");
Serial.println(dataFromRegister,BIN);
delay(1000);
/* Reset PLLCKSELR register */
RCC->PLLCKSELR = 0x00000000;
dataFromRegister=READ_REG(RCC->PLLCKSELR);
Serial.print("RCC->PLLCKSELR: ");
Serial.println(dataFromRegister,BIN);
delay(1000);
/* Reset PLLCFGR register */
RCC->PLLCFGR = 0x00000000;
dataFromRegister=READ_REG(RCC->PLLCFGR);
Serial.print("RCC->PLLCFGR: ");
Serial.println(dataFromRegister,BIN);
delay(1000);
/* Reset PLL1DIVR register */
RCC->PLL1DIVR = 0x00000000;
dataFromRegister=READ_REG(RCC->PLL1DIVR);
Serial.print("RCC->PLL1DIVR: ");
Serial.println(dataFromRegister,BIN);
delay(1000);
/* Reset PLL1FRACR register */
RCC->PLL1FRACR = 0x00000000;
dataFromRegister=READ_REG(RCC->PLL1FRACR);
Serial.print("RCC->PLL1FRACR: ");
Serial.println(dataFromRegister,BIN);
delay(1000);
/* Reset PLL2DIVR register */
RCC->PLL2DIVR = 0x00000000;
dataFromRegister=READ_REG(RCC->PLL2DIVR);
Serial.print("RCC->PLL2DIVR: ");
Serial.println(dataFromRegister,BIN);
delay(1000);
/* Reset PLL2FRACR register */
RCC->PLL2FRACR = 0x00000000;
dataFromRegister=READ_REG(RCC->PLL2FRACR);
Serial.print("RCC->PLL2FRACR: ");
Serial.println(dataFromRegister,BIN);
delay(1000);
/* Reset PLL3DIVR register */
RCC->PLL3DIVR = 0x00000000;
dataFromRegister=READ_REG(RCC->PLL3DIVR);
Serial.print("RCC->PLL3DIVR: ");
Serial.println(dataFromRegister,BIN);
delay(1000);
/* Reset PLL3FRACR register */
RCC->PLL3FRACR = 0x00000000;
dataFromRegister=READ_REG(RCC->PLL3FRACR);
Serial.print("RCC->PLL3FRACR: ");
Serial.println(dataFromRegister,BIN);
delay(1000);
/* Reset HSEBYP bit */
RCC->CR &= (uint32_t)0xFFFBFFFF;
dataFromRegister=READ_REG(RCC->CR);
Serial.print("RCC->CR: ");
Serial.println(dataFromRegister,BIN);
delay(1000);
/* Disable all interrupts */
RCC->CIER = 0x00000000;
dataFromRegister=READ_REG(RCC->CIER);
Serial.print("RCC->CIER : ");
Serial.println(dataFromRegister,BIN);
delay(1000);
/* Change the switch matrix read issuing capability to 1 for the AXI SRAM target (Target 7) */
HWREG(0x51008108) = 0x000000001;
}
Thanks for the help somehow redefining everything with CMSIS highlighted some of my mistakes.
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
i'm monitorating heart rate by the MAX30100(https://img.filipeflop.com/files/download/Datasheet_MAX30100.pdf) using the MCU STM32F4. I'm trying read the IR and RED data from the FIFO, but all returns are ZERO. The method MAX30100_Get_Num_Samples() returns 8. I modeled the code using the pseudo code from datasheet of MAX30100. I tried several solutions for my problem but doesn't work. I dont know if i'm following the right way to get data from the FIFO. My code:
I2C_HandleTypeDef hi2c3; //i2c used
uint16_t RED[50] = {0}, IR[50] = {0}; //buffers for RED and IR
uint8_t buffOximeter[5];
char buffer[32];
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C3_Init();
MX_USB_DEVICE_Init();
MAX30100_Init();
while (1)
{
uint8_t numSamples = MAX30100_Get_Num_Samples();
MAX30100_Read_HeartBeat(numSamples);
for(int i = 0; i < numSamples; i++)
{
sprintf(buffer, "Amostra %d: %d / %d\n\r", i , IR[i], RED[i]);
CDC_Transmit_FS((char*)buffer, 50);
}
}
}
static void MX_I2C3_Init(void)
{
hi2c3.Instance = I2C3;
hi2c3.Init.ClockSpeed = 400000;
hi2c3.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c3.Init.OwnAddress1 = 0;
hi2c3.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c3.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c3.Init.OwnAddress2 = 0;
hi2c3.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c3.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c3) != HAL_OK)
{
Error_Handler();
}
}
void MAX30100_Init(void)
{
HAL_I2C_Mem_Write(&hi2c3,0xAE,0x06,1,0x02,1,1000); //set heart rate mode
HAL_I2C_Mem_Write(&hi2c3,0xAE,0x09,1,0xFF,1,1000); //i50 ledCurrent
uint8_t sr = 0x01, pw = 0x3;
HAL_I2C_Mem_Write(&hi2c3,0xAE,0x07,1,(sr<<2)|pw,1,1000); //sr100, pw1600
HAL_Delay(50);
}
void MAX30100_Read_HeartBeat(uint8_t num)
{
for(int i=0;i<num;i++)
{
HAL_I2C_Master_Transmit(&hi2c3,0x57,0xAE,1,1000); //adress + write mode
HAL_I2C_Master_Transmit(&hi2c3,0x57,0x02,1,1000); //send fifo_wr_ptr
HAL_I2C_Master_Transmit(&hi2c3,0x57,0xAF,1,1000); //adress + read mode
uint8_t data;
HAL_I2C_Master_Receive(&hi2c3,0x57,&data,1,1000); //read fifo_wr_ptr
HAL_Delay(100); //STOP
HAL_I2C_Master_Transmit(&hi2c3,0x57,0xAE,1,1000); //adress + write mode
HAL_I2C_Master_Transmit(&hi2c3,0x57,0x05,1,1000); //send adress fifo data
HAL_I2C_Master_Transmit(&hi2c3,0x57,0xAF,1,1000); //adress + read mode
HAL_I2C_Mem_Read(&hi2c3,0x57,0x05,1,&buffOximeter,4,1000); //read fifo data
IR[i] = (buffOximeter[0] << 8) | buffOximeter[1];
RED[i] = (buffOximeter[2] << 8) | buffOximeter[3];
HAL_Delay(100); //STOP
HAL_I2C_Master_Transmit(&hi2c3,0x57,0xAE,1,1000); //adress + write mode
HAL_I2C_Master_Transmit(&hi2c3,0x57,0x04,1,1000); //send adress fifo_rd_ptr
HAL_Delay(100);
}
}
int MAX30100_Get_Num_Samples(void)
{
uint8_t wrPtr, rdPtr;
HAL_I2C_Mem_Read(&hi2c3,0x57,0x02,1,&wrPtr,1,1000);
HAL_Delay(50);
HAL_I2C_Mem_Read(&hi2c3,0x57,0x04,1,&rdPtr,1,1000);
HAL_Delay(50);
return (abs( 16 + wrPtr - rdPtr ) % 16);
}
connect the interrupt output pin (pin 13 active low) to an interrupt control pin on the cpu.
setup your cpu to be interrupted when the device indicates some event ready
Then, still in your interrupt handler function, read register 0 and determine which data is ready,.
the referenced datasheet, approx page 14 lists the order of communication events needed. Note that the posted code is NOT following the specified order of communication events
page 16, register 7, indicates how to initialize the SPO2 for the desired mode of operation
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;
}