How to use printf to print spi data in stm32 - stm32

printf() can be used in UART data output, how about SPI data?
I rewrite the fputc() and using hal_spi_transmite(), will the data be printed via SPI?
int fputc(int ch, FILE *f)
{
LoRa_transmit(&myLoRa, &send_data[i], 128, 500);
return ch;
}

printf can be used in uart data output,how about SPI data?
I don't know exactly what LoRa_transmit() do but the data to be printed in this function is int ch and not send_data[i]:
int fputc(int ch, FILE *f)
{
LoRa_transmit(&myLoRa, &ch, 128, 500);
return ch;
}
will the data printed via spi?
It will work, but not as you expect (I guess) because of a few details:
SPI communication involves always one master and at least one slave device. The role cannot be changed because only the master device has control of the chip-select pin.
SPI protocols usually involve sending a control command before sending any data to the slave device.
So what will happen is that only the master device can use the "printf()" approach and it will only work if the slave device is expecting data like an input stream.
Supposing you call printf("Hello SPI");, the master device will start transmission (CS and clock), send the first byte of the string, close the transmission and repeat these steps until reaching the end of the string.

Related

Raspberry Pi 4: Python3 smbus2 prepends 0x00 to all I2C data writes

I have working C & Python3 code, based upon simple examples from the internet, where I can correctly send data from my Raspberry Pi4 to an Atmel SAM-E70 dev kit board. I've got a logic analyzer connected to look at the data being sent, and for every i2c_write_data_block() from my Python3 code, the smbus2 code sends the 7-bit address, followed by 0x00, followed by the byte stream that I want to send. My C code, sending the same byte streams, doesn't have the 0x00 between the address and the data. Finally, sending the byte stream using i2ctransfer() from the shell also works as expected: no extra byte.
Hypothetically, it could be that the smbus2 package is trying to use a 10-bit address, but I cannot find any documentation supporting this supposition. In fact, what I've found indicates that the I2C bus configuration is performed via config file(s) which would lead me to the believe that the language used to communicate on the I2C bus shouldn't matter - it would have the same configuration.
Has anyone else encountered this?
I noticed the same thing using the smbus2 i2c_write_data_block() function.
To avoid sending the 0x00 start byte, use the i2c_rdwr() function.
Example:
bus = SMBus(1)
address = 4
data = 'Some message'.encode()
msg = i2c_msg.write(address, data)
bus.i2c_rdwr(msg)

MPU-6050 Burst Read Auto Increment

I'm trying to write a driver for the MPU-6050 and I'm stuck on how to proceed regarding reading the raw accelerometer/gyroscope/temperature readings. For instance, the MPU-6050 has the accelerometer X readings in 2 registers: ACCEL_XOUT[15:8] at address 0x3B and ACCEL_XOUT[7:0] at address 0x3C. Of course to read the raw value I need to read both registers and put them together.
BUT
In the description of the registers (in the register map and description sheet, https://invensense.tdk.com/wp-content/uploads/2015/02/MPU-6000-Register-Map1.pdf) it says that to guarantee readings from the same sampling instant I must use burst reads b/c as soon as an idle I2C bus is detected, the sensor registers are refreshed with new data from a new sampling instant. The datasheet snippet shows the simple I2C burst read:
However, this approach (to the best of my understanding) would only work reading the ACCEL_X registers from the same sampling instant if the auto-increment was supported (such that the first DATA in the above sequence would be from ACCEL_XOUT[15:8] # address 0x3B and the second DATA would be from ACCEL_XOUT[7:0] # address 0x3C). But the datasheet (https://invensense.tdk.com/wp-content/uploads/2015/02/MPU-6000-Datasheet1.pdf) only mentions that I2C burst writes support the auto-increment feature. Without auto-increment on the I2C read side how would I go about reading two different registers whilst maintaining the same sampling instant?
I also recognize that I could use the sensor's FIFO feature or the interrupt to accomplish what I'm after, but (for my own curiosity) I would like a solution that didn't rely on either.
I also have the same problem, looks like the documentation on this topic is incomplete.
Reading single sample
I think you can burst read the ACCEL_*OUT_*, TEMP_OUT_* and GYRO_*OUT_*. In fact I tried reading the data one register at once, but I got frequent data corruption.
Then, just to try, I requested 6 bytes from ACCEL_XOUT_H, 6 bytes from GYRO_XOUT_H and 2 bytes from TEMP_OUT_H and... it worked! No more data corruption!
I think they simply forgot to mention this in the register map.
How to
Here is some example code that can work in the Arduino environment.
These are the function that I use, they are not very safe, but it works for my project:
////////////////////////////////////////////////////////////////
inline void requestBytes(byte SUB, byte nVals)
{
Wire.beginTransmission(SAD);
Wire.write(SUB);
Wire.endTransmission(false);
Wire.requestFrom(SAD, nVals);
while (Wire.available() == 0);
}
////////////////////////////////////////////////////////////////
inline byte getByte(void)
{
return Wire.read();
}
////////////////////////////////////////////////////////////////
inline void stopRead(void)
{
Wire.endTransmission(true);
}
////////////////////////////////////////////////////////////////
byte readByte(byte SUB)
{
requestBytes(SUB, 1);
byte result = getByte();
stopRead();
return result;
}
////////////////////////////////////////////////////////////////
void readBytes(byte SUB, byte* buff, byte count)
{
requestBytes(SUB, count);
for (int i = 0; i < count; i++)
buff[i] = getByte();
stopRead();
}
At this point, you can simply read the values in this way:
// ACCEL_XOUT_H
// burst read the registers using auto-increment:
byte data[6];
readBytes(ACCEL_XOUT_H, data, 6);
// convert the data:
acc_x = (data[0] << 8) | data[1];
// ...
Warning!
Looks like this cannot be done for other registers. For example, to read the FIFO_COUNT_* I have to do this (otherwise I get incorrect results):
uint16_t FIFO_size(void)
{
byte bytes[2];
// this does not work
//readBytes(FIFO_COUNT_H, bytes, 2);
bytes[1] = readByte(FIFO_COUNT_H);
bytes[2] = readByte(FIFO_COUNT_L);
return unisci_bytes(bytes[1], bytes[2]);
}
Reading the FIFO
Looks like the FIFO works differently: you can burst read by simply requesting multiple bytes from the FIFO_R_W register and the MPU6050 will give you the bytes in the FIFO without incrementing the register.
I found this example where they use I2Cdev::readByte(SAD, FIFO_R_W, buffer) to read a given number of bytes from the FIFO and if you look at I2Cdev::readByte() (here) it simply requests N bytes from the FIFO register:
// ... send FIFO_R_W and request N bytes ...
for(...; ...; count++)
data[count] = Wire.receive();
// ...
How to
This is simple since the FIFO_R_W does not auto-increment:
byte data[12];
void loop() {
// ...
readBytes(FIFO_R_W, data, 12); // <- replace 12 with your burst size
// ...
}
Warning!
Using FIFO_size() is very slow!
Also my advice is to use 400kHz I2C frequency, which is the MPU6050's maximum speed
Hope it helps ;)
As Luca says, the burst read semantic seems to be different depending on the register the read operation starts at.
Reading consistent samples
To read a consistent set of raw data values, you can use the method I2C.readRegister(int, ByteBuffer, int) with register number 59 (ACCEL_XOUTR[15:8]) and a length of 14 to read all the sensor data ACCEL, TEMP, and GYRO in one operation and get consistent data.
Burst read of FIFO data
However, if you use the FIFO buffer of the chip, you can start the burst read with the same method signature on register 116 (FIFO_R_W) to read the given amount of data from the chip-internal fifo buffer. Doing so you must keep in mind that there is a limit on the number of bytes that can be read in one burst operation. If I'm interpreting https://github.com/joan2937/pigpio/blob/c33738a320a3e28824af7807edafda440952c05d/pigpio.c#L3914 right, a maximum of 31 bytes can be read in a single burst operation.

Accessing STM32L4 bootloader over USART: No ACK

I'm trying to access the bootloader for a Nucleo L476RG "slave" board.
The "master" board is a Nucleo L496ZG board. In my program, I have a DigitalOut defined on the master board called extBoot0, extReset. These go off to the boot0 and NRST pins on the slave board. Additionally, I have a Serial instance called usart on the master, which is attached to UART2 on the slave board. Also, it appears that there BOOT1 is preset to run the bootloader, i.e. it's asserted low and cannot be changed to run whatever's in SRAM.
Currently, in resetToBootloader, I set BOOT0 high and drop NRST low for 0.1 seconds, and bring it back up high. I've observed that running this function indeed resets the device and prevents the program from running.
In initBootloader, I format the serial per AN2606: 8-bit, even parity, 1 stop bit. I then send 0x7F over that serial bus to the slave board. I'm not getting any response and using a logic analyzer, I've confirmed that the slave is getting it on the right pin and there is no changes in the slave's TX input. What else needs to be done to start the bootloader?
Here's my relevant code:
DigitalOut extBoot0(D7);
DigitalOut extBoot1(D6);
DigitalOut extReset(D5);
Serial usart(/* tx, rx */ D1, D0);
uint8_t rxBuffer[1];
event_callback_t serialEventCb;
void serialCb(int events) {
printf("something happened!\n");
}
void initBootloader() {
wait(5); // just in case?
// Once initialized the USART1 configuration is: 8-bits, even parity and 1 Stop bit
serialEventCb.attach(serialCb);
usart.format(8, SerialBase::Even, 1);
uint8_t buffer[1024];
// write 0x7F
buffer[0] = 0x7F;
usart.write(buffer, 1, 0, 0);
printf("sending cmd\n");
// should ack 0x79
usart.read(rxBuffer, 1, serialEventCb, SERIAL_EVENT_RX_ALL, 0x79);
}
If it helps at all, here's a picture of my board setup.
I believed I solved this by using USART1 instead of USART2. The documentation states that both USART1 and USART2 can be used, but I only receive a 0x79 from USART1.
Additionally, I had to switch from Serial to UARTSerial. The slave first sends an incorrect packet, 0xC0 with an incorrect parity bit. Not really sure why it does that, but it causes the regular Serial instance to not handle the proceeding byte.

NRF24L01+ RX Mode and Flush

I have been trying to write my own code for NRF24L01+. I have a problem and I can not solve it
As a receiver, I use STM32F103C8T6 and as a transmitter I use Arduino Uno.
The problem is related to RX Operation.
As I've mentioned above,
As a receiver, I use STM32F103C8T6 and as a transmitter I use Arduino Uno.
Both sides;
Are communicationg through the same address.
Have the same CRC Length
Do not use Enhanced Mode
Have the same address width
Have the same payload width
have the same communication data rate. (1Mbps)
Here is the algorith I use to get coming data from the transmitter. By the way, I do not use IRQ Pin.
Set CE high
Check RX_DR bit in STATUS register. If a value arrives RX FIFO this bit is set. If so, bring CE low to stop RX operation. (Datasheet says RX_DR bit is Data Ready RX FIFO interrupt. Asserted when new data arrives RX FIFO)
Use R_RX_Payload command described in the datasheet and assign the data to a variable.
Clear RX FIFO
Clear RX_DR bit in STATUS register,( write 1)
But it does not work.
void RX_Mode()
{
ChipEnable_high(); // CE=1
//Check RX_DR bit. Wait until a value appears.
while(check)
{
ReadRegister(REG_STATUS,1);
if( (reg_data & 0x40) == 0x40 ){check = false;}
}
ChipEnable_low(); // CE=0
csn_low(); //CSN=0
HAL_SPI_TransmitReceive(&hspi1, (uint8_t*)COMD_R_RX_PAYLOAD, &received_data, 1, 150); // Read the data
csn_high(); // CSN=1
Flush_RX(); //Clear RX FIFO
// Clear RX_DR bit. (Write 1)
ReadRegister(REG_STATUS,1);
data2write = ( reg_data | 0x40);
WriteRegister(REG_STATUS,data2write,1);
CDC_Transmit_FS(&received_data,1); // Print the received data out.
}
When I disable while loop in the code, I continuously read 0x0E.
Edit: My new problem is related to Flush command.
I want to flush RX FIFO when a data arrives. I keep reading registers while transmitter is sending data and I can observe that a new data arrives RX FIFO which means RX_DR bit is set and RX_FIFO status is changed. Then I turn the tx off and execute FLUSH_RX command on the rx side, can not flush fifo. The registers still say that there is data in RX FIFO.
void Flush_RX()
{
csn_low();
HAL_SPI_Transmit(&hspi1, (uint8_t *)COMD_FLUSH_RX, 1, 150);
while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
csn_high();
}
Any suggestion, help, guidance will be appreciated.
Thanks in advance.
For the RX Mode problem I may help, but I have no idea about Flush operation.
On page 59 in datasheet of NRF24L01+, it says
The RX_DR IRQ is asserted by a new packet arrival event. The procedure for handling this interrups should be
1)Read payload
2)Clear RX_dR
3)Read FIFO_STATUS
4)If there are more available payloads in FIFO repeat step 1.
Could you please use IRQ pin and check whether an interrupt occurs or not. If so, go through these steps above.
I have made some changes on my code. I am trying to make a unidirectional communication for now. So, one side is only RX and the other one is only TX. I am applying the steps described on p.59 in the datasheet.
void RX_Mode(){
ChipEnable_high(); // receiver monitors for a data
while( !(IRQ_Pin_Is_Low() && RXDR_Bit_Is_Set()) ); // Wait until IRQ occurs and RX_DR bit is set.
ChipEnable_low(); // when the data arrives, bring CE low to read payload
ReadPayload(); // read the payload
ClearInterrupts(); // clear all interrupt bits
// This while loop is to check FIFO_STATUS register, if there are more data in FIFO read them.
while(check)
{
ReadRegister(REG_FIFO_STATUS,1);
if((reg_data & 0x01)==0x00)
{
ReadPayload();
ClearInterrupts(); // clear all interrupt bits
}
else
check = false;
}
Flush_RX(); //Flush RX FIFO
check = true; }
The code to read payload is :
void ReadPayload()
{
csn_low(); //CSN=0
HAL_SPI_TransmitReceive(&hspi1, (uint8_t *)COMD_R_RX_PAYLOAD, &received_data ,1, 1500); // READ RX FIFO
while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
CDC_Transmit_FS(&received_data,1); // print the received data out
csn_high(); // CSN=1
}
BUT; when I turn the tx device on, I read the value of STATUS register which is 0x42 (means that RX_DR bit is set) then I continuously read 0x02 (means RX_DR bit is cleaned a). The data I sent is 0x36.
There are two communication mode as mentioned in PDF nRF24L01Pluss_Preliminary_Product_Specification_v1_0_1 page no. 72,73,74.
please just go through it. I worked with stm32 micro-controller with external interrupt. The sequence of commands passed to NRF chip will be dependent on two modes as mentioned below:
one way communication( tx will transmit and rx will receive only)
both side communication((tx+rx) <----> (rx+tx))
In 1st mode, you have to enable auto-acknowledgement,
In 2nd, disable the auto-acknowledgement.
Hereby writing some steps for 2nd mode,
1> For transmitter side while transmitting:
a)flush transmitter
b)clear the tx_ds flag
c)pass the command for writing the payload
d)fill the payload
e)prepare for transmission
f)check the status
g)clear the all flags(maxtx,rx_dr,tx_ds)
2>for Receiver side while receiving:
Note: Receiver should be in reception mode always. If interrupt is used then no need to check the status bit.
when interrupt arrives;
a) read the payload
b)check the status
c)clear RX_DR flag
d)flush RX_FIFO
e)again configure as a receiver.
Try this one
Thank you, all the best.
*hi,
for one way communication you have to enable the auto-acknowledgment,
and better to flush the receiver fifo after reading one packet. whatever data Tx is transmitting just check it on serial port or in other method because if payload length doesn't match with predefined one in rx, then RX_DR interrupt on NRF chip will not occur, so you will not get data on rx side.
for the testing just enable one pipe, check whether data is received by rx.
chip enable and SPI chip select plays vital role in reading from payload or writing a payload.
*

Serial driver limitations on iMX processor

I'm developing on an embedded Linux device that uses an ARM iMX6 processor. The main purpose is to read an incoming serial stream from an external source.
Due to the atypical nature of the serial stream, I've run into a few roadblocks with the Linux serial driver for imx processors. But nothing that is beyond the capability of the iMX6. For example, the incoming serial stream is inverted logic. The iMX6 has a specific register setting to invert the RX signal. From what I can tell, the Linux driver does not expose it.
Another complication is that the incoming serial data arrives in 3ms bursts. The external source transmits continuously for 3ms, then 3ms of idle, then 3ms of data, then idle, etc. In order to sync up with the first byte of each burst, it's very useful to be able to detect when the line is idle. Again, the iMX6 has a register value specifically for indicating that the RX line is idle, but the Linux driver doesn't expose it.
I am also very confused how buffering works in the driver. I know the iMX6 has a 32byte FIFO buffer, but I can't tell if the driver uses that buffer or uses external RAM for buffering. I'm running into an issue where the read command hangs for a second every so often when I'm in blocking mode, which should never happen because the data stream is continuous.
For reference, here's how I configured the serial port in my C code and read 50 bytes (I've changed it to non-blocking for now):
#include <stropts.h>
#include <asm/termios.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
int fd;
struct termios2 terminal;
unsigned char v[50];
fd = open ("/dev/ttymxc2", O_RDONLY | O_NOCTTY | O_NONBLOCK );
ioctl(fd, TCGETS2, &terminal);
terminal.c_cflag |= (CLOCAL | CREAD) ;
terminal.c_cflag |= PARENB ; //enable parity
terminal.c_cflag &= ~PARODD ; //even parity
terminal.c_cflag |= CSTOPB ; //2 stop bits
terminal.c_cflag &= ~CSIZE ;
terminal.c_cflag |= CS8 ;
terminal.c_lflag &= ~(ICANON | IEXTEN | ECHO | ECHOE | ISIG) ;
terminal.c_oflag &= ~OPOST ;
terminal.c_cflag &= ~CBAUD;
terminal.c_cflag |= BOTHER;
terminal.c_ispeed = 100000; //100kHz baud
terminal.c_ospeed = 100000;
ioctl(fd, TCSETS2, &terminal);
...
for(i=0;i<50;i++)
{
read(fd,v+i,1)
}
...
}
So I have two questions:
What is the "proper" way to get the capability out of the serial port that the processor has available but the driver doesn't expose? I can't imagine I'm the first person to want to use such basic functionality of the processor, but I don't want to reinvent the wheel. Do I need to get into writing my own drivers?
Does comprehensive documentation on the iMX serial driver exist anywhere? The code is poorly commented and I get lost quickly trying to find my way around it. For example, I don't know where to start investigating the buffering problem that causes it to hang when receiving a continuous stream of data.
I've forgone with the serial driver entirely and instead wrote some functions to access the register memory directly (modeled after devmem2.c source code). Now I can directly set the INVR bit to invert the RX signal, use the IDLE bit to detect when the line has gone idle, and retrieve the incoming data bytes as soon as they arrive without delay.
I found something on another forum about the UART DMA needs the RX line to go idle for at least 8ms before it services the buffer. That was apparently the cause of the 1sec lag I was experiencing.