I am writing a C program on Eclipse to communicate from my ARM Cortex M4-F microcontroller in I2C with its master, another MCU.
In my I2C library, I use a static global variable to store all the parameters of the communication (address, lenghts, data buffers). The issue is that a part (an array containing the data to be transmitted, which are 8 bits integers) of this variable gets modified when the interrupt (Start condition followed by the slave's address on the I2C bus) happens, even before executing the code I put the handler. It gets assigned to 8, whatever the initial value.
I tried to put breakpoints basically everywhere, and a watchpoint on the variable, the changes arises seemingly from nowhere, not in the while loop, and before the call to my_I2C_Handler(), so the interrupt is the cause apparently.
I also tried setting the variable as volatile, but that changed nothing.
I noticed one interesting thing: putting a printf of the array's data during my_I2C_Init() or my_SlaveAsync(), like so:
printf("%d\n", req.tx_data[0]);
corrects this problem, but why? I want to remove all prints after debugging.
#include <stdint.h>
#include "my_i2c.h"
void I2C1_IRQHandler(void)
{
printf("\nI2C Interrupt\n");
my_I2C_Handler(MXC_I2C1); // MXC_I2C1 is a macro for the registry used
}
int main(void)
{
int error = 0;
printf("\nStarting I2C debugging\n");
// Setup the I2C
my_I2C_Shutdown(MXC_I2C1);
my_I2C_Init(MXC_I2C1);
NVIC_EnableIRQ(I2C1_IRQn); // Enable interrupt
my_I2C_SlaveAsync(MXC_I2C1); // Prepare to receive communication
while (1)
{
LED_On(0);
LED_Off(0);
}
printf("\nDone testing\n");
return 0;
}
The structure of the request containing the parameters of the communication is like this:
typedef struct i2c_req i2c_req_t;
struct i2c_req {
uint8_t addr; // I2C 7-bit Address
unsigned tx_len; // Length of tx data
unsigned rx_len; // Length of rx
unsigned tx_num; // Number of tx bytes sent
unsigned rx_num; // Number of rx bytes sent
uint8_t *tx_data; // Data for mater write/slave read
uint8_t *rx_data; // Data for master read/slave write
};
Is declared like so in the beginning of the file:
static i2c_req_t req;
and assigned this way in my_I2C_Init():
uint8_t rx[1] = {0};
uint8_t tx[1] = {12};
req.addr = 0xAA;
req.tx_data = tx;
req.tx_len = 1;
req.rx_data = rx;
req.rx_len = 1;
req.tx_num = 0;
req.rx_num = 0;
Many thanks for your help
Related
I am using stm32f4. In my project, i have one transmitter and one receiver.
i transmit an 2 byte integer value using
HAL_UART_Transmit(&huart1,&temp_byte1,sizeof(temp_byte1), 500);
HAL_UART_Transmit(&huart1,&temp_byte2,sizeof(temp_byte2), 500);
and on the receiver part. i read these values using
uint8_t temp_data[2];
uint8_t rx_data;
uint8_t rx_index=0;
uint16_t temp;
void USART6_IRQHandler(void)
{
HAL_UART_Receive_IT(&huart6,&rx_data,1);
temp_data[rx_index]=rx_data;
rx_index++;
if(rx_index==2)
{
rx_index=0;
temp = ((temp_data[0]<<8)|temp_data[1]);
if(temp!=79){ //sometimes i see a 79 in my buffer!
hc05_flag = 3; //irrelevant flag with this question
nm=0; //irrelevant flag with this question
}
}
it works fine for one time. but after, i see temp_data[1] in temp_data[0]. it shift 8 bits and i see a big temp value (63744 etc.) . where is the problem?
Assume you are transmitting integer data via UART, In HAL_UART_Transmit you are just passing address of buffer, inside it will be transmitted byte by byte so total 20 bytes will be transmitted.
uint16_t txBuffer[10] = {0xFF11,2,3,4,5,6,7,8,9,10};
HAL_UART_Transmit(&huart1,(uint8_t *) &txBuffer,sizeof(txBuffer), 500);
At Receiver - blocking
uint16_t rxBuffer[10];
void USART6_IRQHandler(void)
{
HAL_UART_Receive(&huart6,&rxBuffer,20,500);
}
At Receiver - Non-blocking
uint16_t rxBuffer[10];
void USART6_IRQHandler(void)
{
HAL_UART_Receive_IT(&huart6,&rxBuffer,20);
}
NOTE: In non blocking mode please ensure that reception is over before proceed ahead to use received data because HAL_UART_Receive_IT just trigger the interrupt and the copy operation shall be carried out in ISR routine. You can also implement received_done flag to ensure the reception is over.
I am trying to receive multiple bytes over SPI. The aim is when the master starts the SPI transfer, slave MCU is interrupted, and it should read the data via SPI and store it in an array, which will be then used by my application for other operations such as determining the device ID and the contents of the packet.
void interrupt __high_priority my_isr_high(void) {
if (PIR1bits.SSP1IF) { // Interrupt from SPI?
rx[buffer_pointer] = SSP1BUF; // Get data from MSSP and store in RX buffer
buffer_pointer++; // Next data
if (buffer_pointer < FRAME_SIZE) // Ended?
SSP1BUF = tx[buffer_pointer]; // Send next byte to SPI
else
buffer_pointer = FRAME_SIZE;
PIR1bits.SSP1IF = 0; // Clear interrupt flag
}
}
However, I am not receiving the 3 bytes correctly. I am sending the following from the master:
dataPacket[0] = 0x43; // Decimal 67
dataPacket[1] = 0x42; //66
dataPacket[2] = 0x41; //65
While I am receiving as follows from the ISR():
rx[0]: 67
rx[1]: 65
rx[2]: 67
Am I missing something or handling the SPI incorrectly?
This will really solve the issue that I am stuck with and maybe will also help others who what to rx multiple bytes.
I am sharing my codes here so that it helps to find the solution quickly. Also included is a .zip file for compiling. Check the Codes here
So far the above code did not work for me properly. Therefore, after a little bit of digging online and other forums I found the following way to read multiple bytes:
uint8_t SPI_ExchangeHandler(uint8_t byte){
static uint8_t i = 0;
for(i=0; i<3; i++){
SSP1BUF =0x00;
while(!SSP1STATbits.BF);
rx_buff[i]=SSP1BUF;
}
State = SEND;
return byte;
}
Although the above codes give me what expected (i.e, correct data packets in the ordered manner), however, it misses two SPI interrupts every time and then displays/captures the correct data. Hence, two sets of data are always lost and then the third one is received correctly.
Is something wrongly configured or missing?
Finally, I managed to receive all the 3 bytes correctly. Sharing the codes below:
My interrupt service routine that triggers the MCU when master SPI has data to send.
void interrupt INTERRUPT_InterruptManager (void){
if(PIE1bits.SSP1IE == 1 && PIR1bits.SSP1IF == 1)
{
SPI_ISR();
}
}
The SPI_ISR code was autogenerated by the MCC GUI.
void SPI_ISR(void)
{
SSP1BUF = SPI_xchgHandler(SSP1BUF);
}
void SPI_setExchangeHandler(uint8_t (* InterruptHandler)(uint8_t))
{
SPI_xchgHandler = InterruptHandler;
}
Then I handle the SPI via a custom function using SPI_setExchangeHandler() as follows:
#define FRAME_SIZE 10 // Frame fixed size
volatile static uint8_t rx_buff[FRAME_SIZE]; //RX buffer
uint8_t SPI_ExchangeHandler(uint8_t byte)
{
static uint8_t i = 0;
rx_buff[i]=SSP1BUF;
i++;
if(i <= 2){
rx_buff[i]=SSP1BUF;
SSP1BUF = 0x00;
while(!SSP1STATbits.BF){};
i++;
}
else{
i = 2;
}
PIR1bits.SSP1IF = 0; // Clear the SPI interrupt flag
State = SEND;
return byte;
}
And I print out the values as follows for debugging:
printf("CMD:%d \n", rx_buff[0]);
printf("BUF1: %d \n", rx_buff[1]);
printf("BUF2: %d \n\n", rx_buff[2]);
However, I am pretty sure this is not the best/optimized way to handle multiple bytes from SPI, therefore, if there is an alternative, share...
I am receiving/reading data from a GPS module sent via USART3 to the STM32F091.
The data gets there just fine which I confirm by sending it to my PC COM3 port and feeding it to 'u-center' (GPS evaulation software).
My problem is that I want to evaluate the data myself in my C program, and for that purpose I feed it into a Ring Buffer, however, every character of the GPS signal is written multiple times to the buffer, instead of one by one.
For example
GGGGGGGPPPPPPPPSSSSSSSS instead of GPS
I am unsure what I'm doing wrong, maybe it's something really obvious I'm overlooking after staring at this code so long.
Here's the relevant code.
stm32f0xx_it.c
#include <main.h>
void USART3_8_IRQHandler(void)
{
if (USART_FLAG_RXNE != RESET)
{
uint16_t byte = 0;
/* Data reception */
/* Clear Overrun Error Flag, necessary when RXNE is used */
USART_GetITStatus(USART3, USART_IT_ORE);
/* Read from Receive Data Register and put into byte */
byte = USART_ReceiveData(USART3);
(*pRXD3).wr = ((*pRXD3).wr + 1) % (*pRXD3).max;
(*pRXD3).Buffer[(*pRXD3).wr] = byte;
/* Send Data to PC, and reset Transmission Complete Flag */
USART_GetITStatus(USART1, USART_IT_TC);
USART_SendData(USART1, byte);
return;
}
return;
}
uartGPS.h
....
struct GPSuart
{
BYTE Buffer[255];
WORD max;
WORD re;
WORD wr;
};
....
main.h
....
extern volatile BYTE B_ser_txd_3[255];
extern volatile BYTE B_ser_rxd_3[255];
extern volatile struct GPSuart TXD_uart_3;
extern volatile struct GPSuart RXD_uart_3;
extern volatile struct GPSuart *pRXD3;
extern volatile struct GPSuart *pTXD3;
....
Let me know if I should provide additional information.
This:
if (USART_FLAG_RXNE != RESET)
does not test a flag, that code is inspecting the flag constant itself, which is not what you meant.
You need more code, to access the UART's status register and check the flag:
if (USART_GetFlagStatus(USARTx, USART_FLAG_RXNE) != RESET)
I'm currently developing a system which involves sending a request string to a sensor device connected via UART to an Atmel SAML21 Xplained Pro board. I'm testing with an Arduino board as the "sensor device", but eventually, it'll be for a Rotronic HC-2 sensor.
The process goes something like this:
MCU sends string { 99RDD} over UART to sensor
-> delay of up to 500ms
-> Response string of 99 bytes sent back via UART
-> Response transmitted to virtual com port on embedded debugger
My issue is that for some reason, I'm either not getting anything sent back, or it's sending back the variable request_msg
I know that the response from the sensor should be 99 bytes of ASCII, and I've tested both the actual sensor, and the Arduino test board over serial connectors to ensure that the readings are coming back correctly.
The software is using Atmel ASF v4.0, which is great when it works, but the documentation is fairly flaky, so I was hoping someone with more experience could point me as to where I'm going wrong in the code.
I have the following code for my main application:
#include "atmel_start.h"
#include "atmel_start_pins.h"
#include <string.h>
static uint8_t example_hello_world[14] = "Hello World!\n";
static uint8_t example_error_msg[13] = "UART Error!\n";
static uint8_t request_msg[24] = "Sending Sensor Request\n";
static uint8_t rotronic_ascii[8] = "{ 99RDD}";
volatile static uint32_t data_arrived = 0;
volatile static uint32_t reading_received = 0;
static void tx_cb_EDBG_COM(const struct usart_async_descriptor *const io_descr)
{
/* Transfer completed */
gpio_toggle_pin_level(LED0);
}
static void rx_cb_EDBG_COM(const struct usart_async_descriptor *const io_descr)
{
/* Receive completed */
data_arrived = 1;
}
static void err_cb_EDBG_COM(const struct usart_async_descriptor *const io_descr)
{
/* error handle */
io_write(&EDBG_COM.io, example_error_msg, 13);
}
static void tx_cb_COM1(const struct usart_async_descriptor *const io_descr)
{
/* Transfer completed */
gpio_toggle_pin_level(LED0);
}
static void rx_cb_COM1(const struct usart_async_descriptor *const io_descr)
{
/* Receive completed */
reading_received = 1;
}
static void err_cb_COM1(const struct usart_async_descriptor *const io_descr)
{
/* error handle */
io_write(&COM1.io, example_error_msg, 13);
}
int main(void)
{
volatile uint8_t recv_char[99];
atmel_start_init();
// Setup the EDBG Serial Port
usart_async_register_callback(&EDBG_COM, USART_ASYNC_TXC_CB, tx_cb_EDBG_COM);
usart_async_register_callback(&EDBG_COM, USART_ASYNC_RXC_CB, rx_cb_EDBG_COM);
usart_async_register_callback(&EDBG_COM, USART_ASYNC_ERROR_CB, err_cb_EDBG_COM);
usart_async_enable(&EDBG_COM);
// Send a test string to ensure EDBG Serial is working
io_write(&EDBG_COM.io, example_hello_world, 14);
// Setup the Rotronic [Arduino] Serial Port
usart_async_register_callback(&COM1, USART_ASYNC_TXC_CB, tx_cb_COM1);
usart_async_register_callback(&COM1, USART_ASYNC_RXC_CB, rx_cb_COM1);
usart_async_register_callback(&COM1, USART_ASYNC_ERROR_CB, err_cb_COM1);
usart_async_enable(&COM1);
while (1) {
if (reading_received == 0)
{
// Delay for a Bit
delay_ms(5000);
// Notify the EDBG COM Port
io_write(&EDBG_COM.io, request_msg, 24);
// Send the Rotronic ASCII
io_write(&COM1.io, rotronic_ascii, 8);
}
// Check if Reading has been Received
if (reading_received == 1)
{
while (io_read(&COM1.io, &recv_char, 99) == 99)
{
// Write what's on the buffer from the receiver
io_write(&EDBG_COM.io, recv_char, 99);
}
// Reset the flag
reading_received = 0;
}
}
}
You seem to be coding for ASFv3 - v4 will trigger your receive callback for any incoming bytes, not only once when your buffer is full (and you have received every 99 characters).
That means that io_read will most probably never return 99 (because it was only a partial read of your message) and you will most probably never send anything back.
Note the docs say (Scroll down to "different read function behavior..."):
In ASFv4 a data reception type callback in a driver with a ring buffer is triggered for every received data.
The UART apparently is a driver with a ring buffer.
You need to repeatedly call io_read and sum up the number of received bytes until you have got 99. Only then proceed. The ASF docs have an example for that. Make sure you copy code from there that fits your version.
I am using an RN42-XV bluetooth module to send characters to the PIC24F from a computer.
The module is connected/paired correctly and the characters that are sent across are also correct (used an oscilloscope).
This is how it is being initialized:
void initUART(){
//Peripheral Pin Mapping
RPINR19bits.U2RXR = 5; //pin 14 UART Receive
RPOR5bits.RP11R = 3; //pin 17 UART Transmit
//Configuring the UART
U2BRG = BRGVAL;
U2MODEbits.UARTEN = 1;
U2MODEbits.UEN = 0;
U2MODEbits.PDSEL = 0;// 8 bit no parity
U2MODEbits.STSEL = 0; // 1 stop bit
U2STAbits.UTXEN = 0;
U2STAbits.URXISEL = 0;
//Putting the UART interrupt flag down.
IFS1bits.U2RXIF = 0;
}
I am also using this function to get the contents of the buffer:
int waitForChar(){
int receivedChar;
// Use the UART RX interrupt flag to wait until we recieve a character.
while(IFS1bits.U2RXIF == 1){
// Clear the UART RX interrupt flag to we can detect the reception
// of another character.
IFS1bits.U2RXIF = 0;
// U2RXREG stores the last character received by the UART. Read this
// value into a local variable before processing.
receivedChar = U2RXREG;
}
return receivedChar;
}
The problem is that the program never goes into the while loop inside the function waitForChar() because the UART interrupt flag is never raised by the hardware.
I have tried different PIC24Fs but all run into the same issue.
The function type is declared as void so it does not return anything. You should be getting compiler warning if you try to assign its return value. Moreover it does not wait for a char. It is "non blocking" that is it returns anyway, but you need a return value to tell you whether it has a char or whether it does a not. If you want it to wait and return a char, it could be like this
int waitForChar(){ // declare a return type
int receivedChar;
while(IFS1bits.U2RXIF == 0); // wait
receivedChar = U2RXREG;
IFS1bits.U2RXIF = 0; // clear status
return receivedChar;
}
I notice a couple of things:
you enable the module (UARTEN) before entirely configuring it.
Shouldn't U2STA.URXDA used as flag to test for receiving?
You don't configure several bits in both registers. That is ok though if you are absolutely sure the startup state is what you like.
The UART initialization code was missing this line:
AD1PCFG = 0xFFFF
The ADC flags have priority over UART. This line disables them.