NRF24L01+ TX and RX Timing Issue? - stm32

I use a STM32F103C8T6 as a transmitter and an Arduino Uno as a receiver.
I cannot receive the value I am interested in. I have changed delay durations after each send and also CE pulse. I sometimes get it worked on spec by playing with delay durations.
To exemplify, I add a 200ms delay after TX function is executed and receiver receives very well then I disconnect the receiver and then when I connect it again, it receives nothing but zero.
Very similar situations occur when I play with CE pulse duration.
I cannot get it worked neither with auto-ack nor simple rx-tx operation.
I would like to mention that when I change roles (TX device is Arduino and RX device is STM) everything works perfectly.
I've checked STM32 by a logic analyzer to see whether the payload is filled properly or not and could not see any problem.
After I fill the payload, I check FIFO_STATUS register and everything is fine.
After I apply a pulse for a certain duration, I check STATUS register and can see that TX_DS bit is set.
I've discovered that just applying 10us pulse on CE may not be enough. It can take up to 500us.
Then decided to set CE pin high until TX_DS bit is set, but this method also did not work.
void TX_Mode(uint8_t data2send)
{
Flush_TX();
CleanInterrupts();
SetPRIM(PRIM_TX); //set as transmitter
csn_low(); //CSN=0
HAL_SPI_Transmit(&hspi2, &COMD_W_TX_PAYLOAD, 1,150); //send command to write to payload
HAL_SPI_TransmitReceive(&hspi2, &data2send, &dummy, 1, 150); // fill the payload
while(HAL_SPI_GetState(&hspi2) != HAL_SPI_STATE_READY);
csn_high();
ChipEnable_high();
while( !(TXDS_Bit_Is_Set() )); //wait until payload is sent
ChipEnable_low();
}

Related

STM32 UART in DMA mode stops receiving after receiving from a host with wrong baud rate

The scenario: I have a STM32 MCU, which uses an UART in DMA Mode with Idle Interrupt for RS485 data transfer. The baud rate of the UART is set in CubeMX, in this case to 115200. My Code works fine, when the Host uses the correct baud rate, it is also "long time" stable, no issues or worries.
BUT: when I set the wrong baud rate at the host, e.g. 56700 instead of 115200, the UART stops receiving data, even if I later set the baud rate at the host to the same baud rate the Microcontroller uses, it won't work. The only way to solve this issue so far is: reset the MCU and connect again with the correct baud rate.
To give you some (Pseudo-)Code:
uint8_t UART_Buf[128];
HAL_UART_Receive_DMA(&huart2, UART_Buf, 128);
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
Or in Plain Words: there is a UART Buffer for DMA (UART_Buf[128]) and the UART is started with HAL_UART_Receive_DMA(...), DMA Rx is set to circular mode in CubeMX, also the Idle-Interrupt is activated, using the HAL Macro: __HAL_UART_ENABLE_IT(...); This code works fine so far.
Works fine means:
when I transmit data from my PC to the Micro, the (one) Idle Interrupt is triggered (correctly) by the MCU. In the ISR I set a flag, to start the data parsing afterwards. I receive exactly the number of bytes I have sent, and all is fine.
BUT: when I make the wrong setting in my Terminal Program and instead of the (correct) baud rate of 115200, the baud rate select menu is set to e.g. 57600, the trouble begins:
The idle interrupt will still trigger after each transmission.
But it triggers 2-4 times in a quick "burst" (depending on the baud rate) and the number of bytes received is 0. I'd expect at least some bs data, but there is exactly 0 data in the buffer - which I can check with the debugger. There is obviously received nothing. When I change the baud rate in my terminal program and restart it, there is still nothing received on the MCU.
I could live with 0 received bytes, if the baud rate of the host is incorrect, but it's pretty uncool that one incoming transmission of a host with the wrong baud rate disables the UART until a hardware reset is done.
My attempts to resolve this were so far:
count the "Idle Interrupt Bursts" in combination with 0 received bytes to trigger a "self reset" routine, that stops the UART and restarts it, using the MX_USART2_UART_Init(); Routine. With zero effect. I can see the Idle Interrupt is still triggered correctly, but the buffer remains empty and no data is transferred into the buffer. The UART remains in a non-receiving state.
The Question
Has anyone out there experienced similar issues, and if yes: how did you solve that?
Additional Info: this happens on a STM32F030 as well as on a STM32G03x
When you send to the UART at the wrong baud rate it will appear to the receiver as framing errors and/or noise errors. It could also appear as random characters being received correctly, but this is less likely so don't be surprised to have nothing in your buffer.
When you are receiving with DMA, it is normal to turn the error interrupt on or else poll the error bits. When an error is detected you would then re-initialize everything and restart the DMA. This sounds like what you are trying to do by counting the idle interrupts, but you are just not checking the right bits.
If you don't want to do that, it is not impossible to imagine that you have nothing to do at the driver level and want to try to do the resynchronisation at a higher level (eg: start reading again and discard everything until a newline character) but you will have to bear in mind at least two things:
First, make sure you clear the DDRE bit in the USART_CR3 register. The name "DMA Disable on Reception Error" speaks for itself.
Second, the UART peripheral is able to self resynchronize, as long as you have an idle gap between bytes. If you switch the transmitter to the correct baud rate but keep blasting out data then the receiver may never correctly identify which bit is a start bit.
After investigating this issue a little bit further, i found a solution.
Abstract:
When a host connects to the MCU to an UART with an other baud rate than the UART is set to, it will go into an error state and stop DMA transmission to the RX Buffer. You can check if there is an error with the HAL_UART_GetError(...) function. If there is an error, stop the UART/DMA and restart it.
The Details:
First of all, it was not the DDRE bit in the USART_CR2 register. This was set to 0 by CubeMX. But the hint of Tom V led me into the right direction.
I tried to recover the UART by playing around with the register bits. I read through the UART section of the reference manual multiple times and tried to figure out, which bits to set in which order, to resolve the error condition manually.
What I found out:
When a transmission with the wrong baud rate is received by the UART the following changes in the UART Registers occur (on an STM32F030):
Control register 1 (USART_CR1) - Bit 8 (PEIE) goes from 1 to 0. PEIE is the Parity Interrupt Enable Bit.
Control register 2 (USART_CR2) - remains unchanged
Control register 3 (USART_CR3) - changes from 0d16449 to 0d16384, which means
Bit 0 (EIE - Error Interrupt enable) goes from 1 to 0
Bit 6 (DMAR - DMA enable receiver) goes from 1 to 0
Bit 14 (DEM - Driver enable mode) remains unchanged at 1
USART_CR3.DEM makes sense. I am using the RS485-Functionality of the F030, so the UART handles the Driver-Enable GPIO by itself.
the transition from 1 to 0 at USART_CR3.EIE and USART_CR3.DMAR are most probably the reason why no more data are transfered to the DMA buffer.
Besides that, the error Flags in the Interrupt and status register (USART_ISR) for ORE and FE are set. ORE stands for Overrun Error and FE for Frame Error. Although these bit can be cleared by writing a 1 to the corresponding bit of the Interrupt flag clear register (USART_ICR), the ErrorCode in the hUART Struct remains at the intial error value.
At the end of my try&error process, I managed to have all registers at the same values they had during valid transmissions, but there were still no bytes received. Whatever i tried, id had no effect. The UART remained in a non receiving state. So i decided to use the "brute force" approach and use the HAL functions, which I know they work.
Finally the solution is pretty simple:
if an Idle Interrupt is detected, but the number of received bytes is 0
=> check the Error-Status of the UART with HAL_UART_GetError(...)
If there is an error, stop the UART with HAL_UART_DMAStop(...) and restart it with HAL_UART_Receive_DMA(...)
The code:
if(RxLen) {
// normal execution, number of received bytes > 0
if(UA_RXCallback[i]) (*UA_RXCallback[i])(hUA); // exec RX callback function
} else {
if(HAL_UART_GetError(&huart2)) {
HAL_UART_DMAStop(&huart2); // STOP Uart
MX_USART2_UART_Init(); // INIT Uart
HAL_UART_Receive_DMA(&huart2, UA2_Buf, UA2_BufSz); // START Uart DMA
__HAL_UART_CLEAR_IDLEFLAG(&huart2); // Clear Idle IT-Flag
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); // Enable Idle Interrupt
}
}
I had a similar issue. I'm using a DMA to receive data, and then periodically checking how many bytes were received. After a bit error, it would not recover. The solution for me was to first subscribe to ErrorCallback on the UART_HandleTypeDef.
In the error handler, I then call UART_Start_Receive_DMA(...) again. This seems to restart the UART and DMA without issue.

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.
*

Triggering Interrupt for any byte received

I'm trying to get a code to work that triggers an interrupt for a variable data size coming to a RX input of a STM32 board (not discovery) in DMA Circular mode. ex.:CONNECTED\r\nDATAREQUEST\r\n
So far so good, I'm being able to receive data and all, while also triggering the DMA interrupt.
I will then create a sub RX message processing buffer breaking down each \r\n to a different char array pointer.
msgProcessingBuffer[0] = "COM_OK"
msgProcessingBuffer[1] = "DATAREQUEST"
msgProcessingBuffer[n] = "BlahBlahBlah"
My problem comes actually from the trigger of the interrupt. I would like to trigger the interrupt from any amount of data and processing any data received.
If I use the interrupt request bellow:
HAL_UART_Receive_DMA(&huart1,uart1RxMsgBuffer, 30);
The input buffer will take 30 bytes to trigger the interrupt, but that's too much time to wait because I would like to process the RX data as soon as a \r\n is found in the string. So I cannot wait for the full buffer to fill to begin processing it.
If I use the interrupt request bellow:
HAL_UART_Receive_DMA(&huart1,uart1RxMsgBuffer, 1;
It will trigger as I want, but there is no point on using DMA in this case because it will trigger the interrupt for every byte and will create a buffer of just 1 byte (duh) just like in "polling mode".
So my question is, how do I trigger the DMA for the first byte received but still receive/process all data that might come after it in a single interrupt? I believe I might be missing some basic concept here.
Best regards,
Blukrr
In short: HAL/SPL libraries don't provide such feachures.
Generally some MCUs, for example STM32F091VCT6 have hardware supporting of Modbus and byte flow analysis (interrupt by recieve some control byte) - so if you will use such MCU in you project, you can configure receive by circular DMA with interrupts by receive '\r' or '\n' byte.
And I repeat: HAL or SPL don't support this features, you can use it only throught work with registers (see reference manuals).
I was taking a look at some other forums and I've found there a work around for this problem.
I'm using a DMA in circular mode and then I monitor the NDTR which updates its value every time a byte is received through the UART interface. Then I cyclically call a function (in while 1 loop or in a cyclic interrupt handler) that break down each message part always looking for /n /r chars. This function also saves the current NDTR value for comparison if it has changed since the last "while 1" cycle. If the NDTR has changed since last cycle I wait a couple milliseconds to receive the remaining message (UART it's too slow to transmit) and then save those received messages in a char buffer array for post processing.
If you create a circular DMA buffer of about 50 bytes (HAL_UART_Receive_DMA(&huart1,uart1RxMsgBuffer, 50)) I think it's enough to compensate any fluctuations in the program cycle.
In the mean time I opened a ticket to ST and they confirmed what you just said they also added:
SOLUTION PROPOSED BY SUPPORTER - 14/4/2016 16:45:22 :
Hi Gilberto,
The DMA interrupt requests available are listed on Table 50 of the Reference Manual, RM0090, http://www.st.com/web/en/resource/technical/document/reference_manual/DM00031020.pdf. Therefore, basically, the DMA interrupt can only trigger at the end of one of these events.
• Half-transfer reached
• Transfer complete
• Transfer error
• Fifo error (overrun, underrun or FIFO level error)
• Direct mode error
Getting a DMA interrupt to trigger upon reception of a specific character in your receive data stream is not possible. You may want to trigger the interrupt when you receive packets of say 30 bytes each and then process the datastring to check if your \r\n chars have arrived so you can process the data block.
Regards,
MCU Tech Support

Gstreamer 1.0 Pause signal

I need to detect when the current playing audio/video is paused. I cannot find anything for 1.0. My app is a bit complex but here is condensed code
/* This function is called when the pipeline changes states. We use it to
* keep track of the current state. */
static void state_changed_cb(GstBus *bus, GstMessage *msg, CustomData *data)
{
GstState old_state, new_state, pending_state;
gst_message_parse_state_changed(msg, &old_state, &new_state, &pending_state);
if(GST_MESSAGE_SRC(msg) == GST_OBJECT(data->playbin))
{
g_print("State set to %s\n", gst_element_state_get_name(new_state));
}
}
gst_init(&wxTheApp->argc, &argv);
m_playbin = gst_element_factory_make("playbin", "playbin");
if(!m_playbin)
{
g_printerr("Not all elements could be created.\n");
exit(1);
}
CustomData* data = new CustomData(xid, m_playbin);
GstBus *bus = gst_element_get_bus(m_playbin);
gst_bus_set_sync_handler(bus, (GstBusSyncHandler) create_window, data, NULL);//here I do video overly stuffs
g_signal_connect (G_OBJECT (bus), "message::state-changed", (GCallback)state_changed_cb, &data);
What do I do wrong? I cannot find working example on connecting such events on Gstreamer 1.0 and 0.x seems a bit different than 1.0 so the vast exaples there don't help
UPDATE
I have found a way to get signals. I run wxWidgets timer with 500ms time span and each time timer fires I call
GstMessage* msg = gst_bus_pop(m_bus);
if(msg!=NULL)
{
g_print ("New Message -- %s\n", gst_message_type_get_name(msg->type));
}
Now I get a lot of 'state-change' messages. Still I want to know if that message is for Pause or Stop or Play or End of Media (I mean way to differentiate which message is this) so that I can notify the UI.
So while I get signals now, the basic problem, to get specific signals, remains unsolved.
You have to call gst_bus_add_signal_watch() (like in 0.10) to enable emission of the signals. Without that you can only use the other ways to get notified about GstMessages on that bus.
Also just to be sure, you need a running GLib main loop on the default main context for this to work. Otherwise you need to do things a bit different.
For the updated question:
Check the documentation: gst_message_parse_state_changed() can be used to parse the old, new and pending state from the message. This is also still the same as in 0.10. From the application point of view, and conceptionally nothing much has changed really between 0.10 and 1.0
Also you shouldn't do this timeout-waiting as it will block your wxwidget main loop. Easiest solution would be to use a sync bus handler (which you already have) and dispatch all messages from there to some callback on the wxwidget main loop.

packets lost xbee series 1

I have two xbee's series 1. I have them as endpoint devices working in API mode and talking to each other. The first xbee is attached at a raspberry pi, while the other is on my pc where I see the terminal tab of XCTU program. The baud rate I use is 125000.
From raspberry pi I try to send a jpg image which is 30Kbytes. I send data frames 100 byte long (the biggest as it is said in the xbee documentation). Inside a loop I create and send the packets, I have also a cout statement that prints the loop number. Everything is fine and all bytes are sent. When I comment out the cout statement not all bytes are sent.
From what I have understood the cout statement works as a delay between packets, but I still cannot understand why is this happening as it is supposed that I use the half speed ...
I hope I was clear and look forward for a reply.
UPDATE
Just to summarize, i changed baud rate to 250000 where there is the same behavior as in 125000. I also implemented hardware flow control by checking cts signal. When xbees are in transparent mode I need a delay between sending characters at around 150us. The same goes for api mode too. The difference with 125000 baud rate in api mode was that the delay needed, was enough to be betwween each data packet, but in 250000 the delay is needed between each byte that i send. If i do the above everything goes well.
The next thing i did was to plug both xbees in my pc in transparent mode. I went to terminal tab of xctu software where i chose assemble packet and sent at around 3000 bytes to the other xbee. The result was the same. The second xbee received at about 1500 bytes and then each time that i was sending one byte from the first to the second, the "lost bytes" were being received at packets of 1000. :/
So could anyone know what am I doing wrong?
You should connect the /CTS pin from the XBee module into the Raspberry Pi, and have your routine stop sending data when the XBee de-asserts it.
At higher baud rates, it's possible to stream data into the XBee module faster than it can send to the remote module. The local XBee module uses the /CTS pin to notify the host when its buffers are almost full and the host should stop sending. People refer to this as hardware flow control.
It may be necessary to modify the serial driver on the Raspberry Pi to make use of that signal -- it should pause the transmit buffer when de-asserted, and automatically resume sending when re-asserted.