Mbed SPI Communication with Digital Sensor - sensors

I have a Mbed and a digital gyroscope sensor. I want to make sure two of them communications through SPI where Mbed (master) able to read the data from the gyroscope sensor (slave). Can anyone help out?
#include "mbed.h"
SPI spi(p5, p6, p7); // mosi, miso, sclk
DigitalOut cs(p8);
Serial pc(USBTX, USBRX);
int main() {
// Slave select disabled //
cs = 1;
// Setup the spi for 8 bit data, high steady state clock,
// Second edge capture, with a 1MHz clock rate
spi.format(8,3);
spi.frequency(500000);
// Slave select enabled, it is active low //
cs = 0;
// Send 0x8F, the command to read the WHO_AM_I register //
// 0x8F 1000 1111, MSB = 0 means write and MSB = 1 means read //
spi.write(0x8F);
// Send a dummy byte to receive the contents of the WHO_AM_I register //
// The default response 1101 0011 //
int whoami = spi.write(0x00);
pc.printf("WHOAMI register = 0x%X\n", whoami);
// Slave select disabled //
cs = 1;
while(1) {
cs = 0 ;
// 0x28 is the register for OUT_X_L of the gyroscope //
spi.write(0x28);
int data = spi.write(0x00);
pc.printf("data output 0x%X\n", data);
wait(0.5);
cs = 1;
}
}

A longshot answer. The gyro spec may specify a time between CS goes low and you can start sending data. Hence, try with a delay after cs = 0;
Secondly, why are you setting MSB when you intend to read WHO_AM_I register. MSB=1 is for writing and not reading.

Related

How to correctly read data returned by MFRC522 via SPI using STM8S103F3?

I had tested reading Mifare RFID 1K cards with an RFID-RC522 module via UART using a Pyhton script on my PC. Now I am using STM8S103F3 to interface with the same RFID-RC522 (MFRC522) via SPI, but I have the problem of not getting the correct values/addresses returned by MFRC522 when sending the same commands as I did with the Python script/Terminal. It's the 1st time I use SPI so I suspect myself of not having properly configured SPI or doing something wrong with the write/read sequence, but I could not manage to troubleshoot it myself.
Here is my main() function:
int main ( void ) {
initClockHSI();
initGPIO();
initUART1();
initMasterSPI();
initMFRC522();
return 0;
}
The init functions are as follows:
void initClockHSI ( void ) {
CLK_DeInit(); // Deinitializes the CLK peripheral registers to their default reset
CLK_SYSCLKConfig ( CLK_PRESCALER_CPUDIV1 ); // (uint8_t)0x80 CPU clock division factors 1
CLK_SYSCLKConfig ( CLK_PRESCALER_HSIDIV1 ); // (uint8_t)0x00 High speed internal clock prescaler: 1
CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, // (uint8_t)0x01 Enable the automatic clock switching mode
CLK_SOURCE_HSI, // (uint8_t)0xE1 Clock Source HSI
DISABLE, // DISABLE = 0
CLK_CURRENTCLOCKSTATE_DISABLE); // CLK_CURRENTCLOCKSTATE_DISABLE = (uint8_t)0x00 Current clock disable
}
void initGPIO ( void ) {
// UART1:
GPIO_DeInit(GPIOD);
GPIO_Init(GPIOD, GPIO_PIN_5, GPIO_MODE_OUT_PP_HIGH_SLOW); // (uint8_t)0xD0 Output push-pull, high level, 2MHz
GPIO_Init(GPIOD, GPIO_PIN_6, GPIO_MODE_IN_PU_NO_IT); // UART1 Rx (uint8_t)0x40 Input pull-up, no external interrupt
// MFRC522:
GPIO_DeInit(GPIOC);
// MOSI:
GPIO_Init (GPIOC, GPIO_PIN_6, GPIO_MODE_OUT_PP_HIGH_SLOW);// (uint8_t)0xD0 Output push-pull, high level, 2MHz
// SCK:
GPIO_Init (GPIOC, GPIO_PIN_5, GPIO_MODE_OUT_PP_LOW_SLOW); // (uint8_t)0xC0 Output push-pull, low level, 2MHz
// MISO (Input):
GPIO_Init (GPIOC, GPIO_PIN_7, GPIO_MODE_IN_PU_NO_IT); // (uint8_t)0x40 Input pull-up, no external interrupt
GPIO_DeInit(GPIOB);
// NRSTPD:
GPIO_Init(GPIOB, GPIO_PIN_4, GPIO_MODE_OUT_PP_HIGH_SLOW); // (uint8_t)0xD0 Output push-pull, high level, 2MHz
// NSS (pulled-up externally via 10k R):
GPIO_Init(GPIOB, GPIO_PIN_5, GPIO_MODE_OUT_PP_HIGH_SLOW); // (uint8_t)0xD0 Output push-pull, high level, 2MHz
}
void initMasterSPI ( void ) {
SPI_DeInit(); // Deinitializes the SPI peripheral registers to their default reset values
SPI_Init(SPI_FIRSTBIT_MSB, // (uint8_t)0x00 MSB bit will be transmitted first
SPI_BAUDRATEPRESCALER_16, // (uint8_t)0x18 SPI frequency = frequency(CPU)/16 == 1 MHz baud
SPI_MODE_MASTER, // (uint8_t)0x04 SPI Master configuration
SPI_CLOCKPOLARITY_LOW, // (uint8_t)0x00 Clock to 0 when idle
SPI_CLOCKPHASE_1EDGE, // (uint8_t)0x00 The first clock transition is the first data capture edge
SPI_DATADIRECTION_2LINES_FULLDUPLEX, // (uint8_t)0x00 2-line uni-directional data mode enable
SPI_NSS_SOFT, // (uint8_t)0x02 Software slave management disabled ?! isn't it enabled, when it's called SOFT ?!
0x00); // CRCPolynomial
SPI_Cmd(ENABLE); // Enables or disables the SPI peripheral; parameter can be: ENABLE or DISABLE
}
void initMFRC522 ( void ) {
char result;
// 1. hard reset MFRC522:
result = resetHardMFRC522 ();
// 3. set timer to start automatically at the end of the TRANSMISSION:
result = writeMFRC522 ( TModeReg, 0x80 ); // TModeReg = (0x2A == 42):defines settings for the internal timer; 0x80 == 1000 0000 --> MSB = 1 => timer starts automatically at the end of the TRANSMISSION in all communication modes at all speeds
}
char resetHardMFRC522 () {
unsigned int i;
// 1. Pull reset line LOW:
GPIO_WriteLow(NRSTPD_PORT, NRSTPD_PIN);
// 2. keep reset line LOW for some time; reset timing requirements: min 100ns (page 34)
for ( i = 0; i < 1000; i++) // # 16 Mhz: 1 clock cycle == 62,5 ns;
nop();
//// 1000 == 340 - 370 us, rectangular
// 3. Pull reset line HIGH:
GPIO_WriteHigh(NRSTPD_PORT, NRSTPD_PIN);
// 4. wait for a stable oscillator; oscillator start-up time is the start up time of the crystal + 37,74µs;
for ( i = 0; i < 60000; i++) // # 16 Mhz: 1 clock cycle == 62,5 ns; 60000 == 22,4 ms
nop(); // ;
// 5. check for wake-up procedure end (hard Power-Down mode): bit 'PowerDown' 1 --> 0:
if ( ( readMFRC522( CommandReg ) & 0x10 ) ) // if 1 returned ; 0x10 == 0001 0000 (BIt 4: PowerDown)
return ERROR; // 1
return OK; // 0 => Bit 4 (PoweDown) of CommandReg is cleared => chip should be ready to work
}
char readMFRC522 ( unsigned char address ) { // 0x01
char dataMFRC522;
// 2. Before sending data, the user must pull low an SS signal to let the slave device know it is the recipient of the message:
GPIO_WriteLow(NSS_PORT, NSS_PIN); // pull NSS line LOW before start of SPI communication
// 3. wait while SPI is busy communicating, exit when any ongoing SPI transfer has finished
while(SPI_GetFlagStatus(SPI_FLAG_BSY)) // RESET(0) or SET(1)
;
// 4. send serially the address to MFRC522 with MSB set (R mode) & LSB cleared:
SPI_SendData( ( ( address << 1 ) & 0x7E ) | 0x80 ); // highest MFRC522 address is 0x3f == 0011 1111; 0x82 == 1000 0010
// 5. check that the address is indeed sent:
while( ! SPI_GetFlagStatus(SPI_FLAG_TXE) )
;
// 6. check that data has been received:
while( ! SPI_GetFlagStatus(SPI_FLAG_RXNE) ) // data received ?
;
// 7. read the value returned serially:
dataMFRC522 = SPI_ReceiveData(); // Returns the most recent received data by the SPI peripheral; resets SPI_FLAG_RXNE
// 8. wait while SPI is busy communicating, exit when SPI transfer has finished
while(SPI_GetFlagStatus(SPI_FLAG_BSY)) // RESET(0) or SET(1)
;
// 9. raise NSS line HIGH afer end of SPI communication:
GPIO_WriteHigh(NSS_PORT, NSS_PIN);
return dataMFRC522;
}
char writeMFRC522 ( unsigned char address, unsigned char value ) {
char addressMFRC522;
// 2. Before sending data, the user must pull low an SS signal to let the slave device know it is the recipient of the message:
GPIO_WriteLow(NSS_PORT, NSS_PIN); // pull NSS line LOW before start of SPI communication
// 3. wait while SPI is busy communicating, exit when any ongoing SPI transfer has finished
while(SPI_GetFlagStatus(SPI_FLAG_BSY)) // RESET(0) or SET(1)
;
// 4. send serially the address to MFRC522 with MSB cleared (W mode) & LSB cleared:
SPI_SendData( ( address << 1 ) & 0x7E ); // highest MFRC522 address is 0x3f == 0011 1111;
// 5. check that the address is indeed sent:
while( ! SPI_GetFlagStatus(SPI_FLAG_TXE) )
;
// 6. send serially the data to MFRC522:
SPI_SendData( value );
// 7. check that the data is indeed sent:
while(!SPI_GetFlagStatus(SPI_FLAG_TXE))
;
// 8. check that data has been received as reposnse to sent address:
while( ! SPI_GetFlagStatus(SPI_FLAG_RXNE) ) // data received ?
;
// 9. read the address returned serially:
addressMFRC522 = SPI_ReceiveData(); // Returns the most recent received data by the SPI peripheral; resets SPI_FLAG_RXNE
// 10. wait while SPI is busy communicating, exit when SPI transfer has finished:
while(SPI_GetFlagStatus(SPI_FLAG_BSY)) // RESET(0) or SET(1)
;
// 11. raise NSS line HIGH afer end of SPI communication
GPIO_WriteHigh(NSS_PORT, NSS_PIN);
if ( addressMFRC522 == address )
return OK; // 0
else
return ERROR; // 1
}
My problem arises within the call of the last function initMFRC522() within my main() function:
when I only execute the first part, resetHardMFRC522 (); , then I get the expected reset value 0x20 for the CommandRegister of the MFRC522 after the hard reset;
however, when I execute also the second part, the function result = writeMFRC522 ( TModeReg, 0x80 );, then I don't get the expected reset value 0x20 for the CommandRegister of the MFRC522 after the hard reset, plus I don't receive the same address returned as the one I write to (as per MFRC522 protocol). I cannot figure out where my problem is ? I checked the SPI settings, but the setting of the clock polarity and phase seem to be the correct ones. The pin input/output directions also seem to be correct. I guess I am misusing / overusing / not using correctly the SPI flags or I don't have the correct command sequence while reading/writing, but I could not figure out where my mistake is from reading the datasheet and looking at other people's code on the net ?
the issue was that, when trying to read a register value, I need to send a 2nd dummy byte via MOSI after the register address itself because the 1st byte returned is a non-sense byte and the real register content is returned as a 2nd byte which requires a CLK signal (which itself is generated by the 2nd dummy byte sent). That was different in comparison to using UART but I got some help interpreting the datasheet :-)

sending audio via bluetooth a2dp source esp32

I am trying to send measured i2s analogue signal (e.g. from mic) to the sink device via Bluetooth instead of the default noise.
Currently I am trying to change the bt_app_a2d_data_cb()
static int32_t bt_app_a2d_data_cb(uint8_t *data, int32_t i2s_read_len)
{
if (i2s_read_len < 0 || data == NULL) {
return 0;
}
char* i2s_read_buff = (char*) calloc(i2s_read_len, sizeof(char));
bytes_read = 0;
i2s_adc_enable(I2S_NUM_0);
while(bytes_read == 0)
{
i2s_read(I2S_NUM_0, i2s_read_buff, i2s_read_len,&bytes_read, portMAX_DELAY);
}
i2s_adc_disable(I2S_NUM_0);
// taking care of the watchdog//
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
TIMERG0.wdt_feed=1;
TIMERG0.wdt_wprotect=0;
uint32_t j = 0;
uint16_t dac_value = 0;
// change 16bit input signal to 8bit
for (int i = 0; i < i2s_read_len; i += 2) {
dac_value = ((((uint16_t) (i2s_read_buff[i + 1] & 0xf) << 8) | ((i2s_read_buff[i + 0]))));
data[j] = (uint8_t) dac_value * 256 / 4096;
j++;
}
// testing for loop
//uint8_t da = 0;
//for (int i = 0; i < i2s_read_len; i++) {
// data[i] = (uint8_t) (i2s_read_buff[i] >> 8);// & 0xff;
// da++;
// if(da>254) da=0;
//}
free(i2s_read_buff);
i2s_read_buff = NULL;
return i2s_read_len;
}
I can hear the sawtooth sound from the sink device.
Any ideas what to do?
your data can be an array of some float digits representing analog signals or analog signal variations, for example, a 32khz sound signal contains 320000 float numbers to define captures sound for every second. if your data have been expected to transmit in offline mode you can prepare your outcoming data in the form of a buffer plus a terminator sign then send buffer by Bluetooth module of sender device which is connected to the proper microcontroller. for the receiving device, if you got terminator character like "\r" you can process incoming buffer e.g. for my case, I had to send a string array of numbers but I often received at most one or two unknown characters and to avoid it I reject it while fulfill receiving container.
how to trim unknown first characters of string in code vision
if you want it in online mode i.e. your data must be transmitted and played concurrently. you must consider delays and reasonable time to process for all microcontrollers and devices like Bluetooth, EEprom iCs and...
I'm also working on a project "a2dp source esp32".
I'm playing a wav-file from spiffs.
If the wav-file is 44100, 16-bit, stereo then you can directly write a stream of bytes from the file to the array data[ ].
When I tried to write less data than in the len-variable and return less (for example 88), I got an error, now I'm trying to figure out how to reduce this buffer because of big latency (len=512).
Also, the data in the array data[ ] is stored as stereo.
Example: read data from file to data[ ]-array:
size_t read;
read = fread((void*) data, 1, len, fwave);//fwave is a file
if(read<len){//If get EOF, go to begin of the file
fseek(fwave , 0x2C , SEEK_SET);//skip wav-header 44bytesт
read = fread((void*) (&(data[read])), 1, len-read, fwave);//read up
}
If file mono, I convert it to stereo like this (I read half and then double data):
int32_t lenHalf=len/2;
read = fread((void*) data, 1, lenHalf, fwave);
if(read<lenHalf){
fseek(fwave , 0x2C , SEEK_SET);//skip wav-header 44bytesт
read = fread((void*) (&(data[read])), 1, lenHalf-read, fwave);//read up
}
//copy to the second channel
uint16_t *data16=(uint16_t*)data;
for (int i = lenHalf/2-1; i >= 0; i--) {
data16[(i << 1)] = data16[i];
data16[(i << 1) + 1] = data16[i];
}
I think you have got sawtooth sound because:
your data is mono?
in your "return i2s_read_len;" i2s_read_len less than len
you // change 16bit input signal to 8bit, in the array data[ ] data as 16-bit: 2ByteLeft-2ByteRight-2ByteLeft-2ByteRight-...
I'm not sure, it's a guess.

How to convert arduino's digital output in terms of frequency

I am using a analog-output sound sensor module where the output of the sensor module is connected to the arduino and can see arduino is doing Ato D conversion and displaying integers from range 0 to 1023.
But I need to calculate the frequency of the sound getting measure from the sensor.
so could you help me, hwo to calculate the frequecy from this Ato D converted values from arduino.
You don't really need to to ADC conversions do you? All you need to do is detect a rising edge on the input and then count those. Since your sensor will output low-high-low sequences, and since the Arduino will register HIGH as over a certain voltage, that should be adequate.
This code will measure up to around 200 kHz, from an input connected to digital pin 8 on the board:
// Input: Pin D8
volatile boolean first;
volatile boolean triggered;
volatile unsigned long overflowCount;
volatile unsigned long startTime;
volatile unsigned long finishTime;
// timer overflows (every 65536 counts)
ISR (TIMER1_OVF_vect)
{
overflowCount++;
} // end of TIMER1_OVF_vect
ISR (TIMER1_CAPT_vect)
{
// grab counter value before it changes any more
unsigned int timer1CounterValue;
timer1CounterValue = ICR1; // see datasheet, page 117 (accessing 16-bit registers)
unsigned long overflowCopy = overflowCount;
// if just missed an overflow
if ((TIFR1 & bit (TOV1)) && timer1CounterValue < 0x7FFF)
overflowCopy++;
// wait until we noticed last one
if (triggered)
return;
if (first)
{
startTime = (overflowCopy << 16) + timer1CounterValue;
first = false;
return;
}
finishTime = (overflowCopy << 16) + timer1CounterValue;
triggered = true;
TIMSK1 = 0; // no more interrupts for now
} // end of TIMER1_CAPT_vect
void prepareForInterrupts ()
{
noInterrupts (); // protected code
first = true;
triggered = false; // re-arm for next time
// reset Timer 1
TCCR1A = 0;
TCCR1B = 0;
TIFR1 = bit (ICF1) | bit (TOV1); // clear flags so we don't get a bogus interrupt
TCNT1 = 0; // Counter to zero
overflowCount = 0; // Therefore no overflows yet
// Timer 1 - counts clock pulses
TIMSK1 = bit (TOIE1) | bit (ICIE1); // interrupt on Timer 1 overflow and input capture
// start Timer 1, no prescaler
TCCR1B = bit (CS10) | bit (ICES1); // plus Input Capture Edge Select (rising on D8)
interrupts ();
} // end of prepareForInterrupts
void setup ()
{
Serial.begin(115200);
Serial.println("Frequency Counter");
// set up for interrupts
prepareForInterrupts ();
} // end of setup
void loop ()
{
// wait till we have a reading
if (!triggered)
return;
// period is elapsed time
unsigned long elapsedTime = finishTime - startTime;
// frequency is inverse of period, adjusted for clock period
float freq = F_CPU / float (elapsedTime); // each tick is 62.5 ns at 16 MHz
Serial.print ("Took: ");
Serial.print (elapsedTime);
Serial.print (" counts. ");
Serial.print ("Frequency: ");
Serial.print (freq);
Serial.println (" Hz. ");
// so we can read it
delay (500);
prepareForInterrupts ();
} // end of loop
More discussion and information at Timers and counters.
As I suggested you in the other thread, the best is to
filter
amplify
apply a threshold
measure the time between edges
Steps 1, 2, 3 can be performed in software but it is MUCH better to perform them in hardware. The fourth step is what Nick Gammon solution is about... But you have to first make steps 1,2,3 in HW otherwise you will receive a lot of "noisy" readings

Write to I2C I/O device

I am trying to talk to a Bosch Sensortec BNO055 sensor. I am using the shuttleboard. VDD and VDDIO are connected to 3.3V, on pin 17 and 18 Are SDA and SCL. These connected to a embedded linux board. An other sensor is on the same bus, I can see its values on the scope.
I have the following code:
BNO055_RETURN_FUNCTION_TYPE Bno055I2cBusWrite(u8 dev_addr, u8 reg_addr, u8* reg_data, u8 wr_len){
//According to https://www.kernel.org/doc/Documentation/i2c/dev-interface
int file = 0;
char filename[20];
snprintf(filename, 19, "/dev/i2c-%d", ADAPTER_NR);
if(open(filename, O_RDWR) < 0){ /*error*/ }
if(ioctl(file, I2C_SLAVE, dev_addr) < 0){ /*error*/ }
char buf[1 + wr_len];
buf[0] = reg_addr;
memcpy(&buf[1], reg_data, wr_len);
int written_bytes = 0;
if(write(file, buf, wr_len) != wr_len){
printf("Error BusWrite-write: %s.\n", strerror(errno));
exit(-1);
}
}
The first two if-statements are passed fine.
The write-operation fails. On the oscilloscope I see the correct device-address (which is then not acknowledged).
What I've done:
Added the device-address to buf (not covered in this code example).
Read and understood page 90-92 from the datasheet https://ae-bst.resource.bosch.com/media/products/dokumente/bno055/BST_BNO055_DS000_12~1.pdf
Soldered 1k8 ohm resistors to get steeper edges on clock and data
Made sure that the device address and the read/write bit are set correct
My sensor just does not acknowledges when its device address appears on the line.
What is exactly done by ioctl(file, I2C_SLAVE, dev_addr)? Does that send out the device-address on the I2C-bus?
Does the linuxkernel send the device-address by itself? I expect so.
Resuming:
Can someone point me in the right direction to let the sensor react?
Well... it just seemed that a wire to the scope interfered too much. And the device-address is sent by the driver when writing or reading, to answer my own question.

AVR UART - Java Android bluetooth communication

I have bluetooth module connected to AVR (Atmega32A) via UART. Some bytes that are transmit from bluetooth module to AVR are not properly recived.
For example the bytes that are properly transmit/recived (UTF-8):
Bluetooth module transmit byte X->recived byte X'
'w'->'w'
's'->'s'
'z'->'z'
'm'->'m'
bytes recived not properly:
'q'->'y'
'p'->'~'
'1'->'9'
Bluetooth connection settings:
Bps/Par/Bits: 115200 8N1
init UART:
#define F_CLK 16000000
#define BAUD 115200
uint16_t ubrr_value = (uint16_t) (((F_CLK)/(16 * BAUD)) - 1);
UBRRL = ubrr_value;
UBRRH = (ubrr_value>>8);
// 8 bit frame, async mode
UCSRC=(1<<URSEL) | (3<<UCSZ0);
//recive and transmit mode
UCSRB = (1<<TXEN) | (1 << RXEN);
transmit/recive byte by uart:
char USART_ReceiveByte()
{
while(!(UCSRA & (1<<RXC)));
return UDR;
}
void uart_sendRS(char VALUE)
{
while(!(UCSRA & (1<<UDRE)));
UDR = VALUE;
}
main loop:
while(1)
{
recivedByte = USART_ReceiveByte();
uart_sendRS(recivedByte);
}
i would be so glad to know why it does not work properly
EDIT: if i change the order there is result:
'y'->'y'
'~'->'~'
'9'->'9'
EDIT2: probably there is something wrong with setting UBRRL and UBRRH (ubrr_value = 7 in this case), does someone can confirm if it is proper and if the microcontroller can handle such a high BAUD?
#define F_CLK 16000000
#define BAUD 115200
uint16_t ubrr_value = (uint16_t) (((F_CLK)/(16 * BAUD)) - 1);
UBRRL = ubrr_value;
UBRRH = (ubrr_value>>8);
The problem here is that you are not initialising the UART properly. You need to set the U2X bit in UCSRA if you wish to use the baud rate as you wish it configured. If you are using avr-libc you may use the following code to properly compute the BAUD rate.
void uart0_init(void) {
# define BAUD 115200
# include <util/setbaud.h>
UBRRH = UBRRH_VALUE;
UBRRL = UBRRL_VALUE;
# if USE_2X
UCSRA |= _BV(U2X);
# else
UCSRA &= ~_BV(U2X);
# endif
# undef BAUD
/* other uart stuff you may need */
}
If you look at the datasheet for your microcontroller, section 20.12, you will find a table with this information precomputed for you. Cheers.

Resources