I'm having trouble trying to connect my light sensor (MAX44009) with I2C. I will explain what I've done and I hope someone can help me.
I am connecting this connection card on HMI port of my XMC4400 with 80 ports.
I've connected the sensor according to this table.
SCA - pin37
SCL - pin38
GND - pin 80
3.3 - pin 3.3V of XMC4400
Then I've tried to adapt the I2C Master example (available on DAVE tutorials) for my light sensor. I've created an I2C master app with the following settings:
My main.c is this:
Code:
#include <DAVE.h>
#define IO_EXPANDER_ADDRESS (0x4A)
uint8_t tx_buffer[4] = {0x00,0x01,0x02,0x03};
volatile uint8_t tx_completion_0 = 0;
volatile uint8_t rx_completion_0 = 0;
/* Transmit callback handling */
void tx_callback_0(void)
{
tx_completion_0 = 1;
}
/* Receive callback handling */
void rx_callback_0(void)
{
rx_completion_0 = 1;
}
/* Delay */
void delay(uint32_t counter)
{
volatile uint32_t cnt = counter;
while(--cnt);
}
/*
* For this demo the HMI satellite board for the XMC45 CPU board is required.
* It communicates with the IO expander (U360: PCA9502) found in the mentioned satellite board.
* The demo implements a binary counter using the LEDs attached to the IO expander.
*
*/
int main(void)
{
DAVE_Init();
uint8_t received_data;
uint8_t counter = 0;
/* Write data to reset the LEDs through the IO EXPANDER: DIR and 0xFF */
I2C_MASTER_Transmit(&I2C_MASTER_0,true,IO_EXPANDER_ADDRESS,&tx_buffer[1],2,false);
while(tx_completion_0 == 0);
tx_completion_0 = 0;
while(counter < 255)
{
tx_buffer[3] = ~counter;
counter++;
/* Write data to set the STATE of the IO EXPANDER */
I2C_MASTER_Transmit(&I2C_MASTER_0,true,IO_EXPANDER_ADDRESS,&tx_buffer[3],2,false);
while(tx_completion_0 == 0){
tx_callback_0();
}
tx_completion_0 = 0;
/* Receive the data from the IO EXPANDER */
I2C_MASTER_Receive(&I2C_MASTER_0,true,IO_EXPANDER_ADDRESS,&received_data,2,true,true);
printf("%d", received_data);
while(rx_completion_0 == 0){
rx_callback_0();
}
rx_completion_0 = 0;
/* Check if the received data is correct*/
if(tx_buffer[3] != received_data)
{
// while(1);
}
/* Delay to make visible the change */
delay(0xfffff);
}
while(1);
return 0;
}
I think my callback functions are not working, since it stops every time I execute one I2C function. on this case is on line 108. Plus, sometimes it gives me an error/warning:
No source available on 0x00.
import smbus
import time
# Get I2C bus
bus = smbus.SMBus(1)
# MAX44009 address, 0x4A(74)
# Select configuration register, 0x02(02)
# 0x40(64) Continuous mode, Integration time = 800 ms
bus.write_byte_data(0x4A, 0x02, 0x40)
time.sleep(0.5)
# MAX44009 address, 0x4A(74)
# Read data back from 0x03(03), 2 bytes
# luminance MSB, luminance LSB
data = bus.read_i2c_block_data(0x4A, 0x03, 2)
# Convert the data to lux
exponent = (data[0] & 0xF0) >> 4
mantissa = ((data[0] & 0x0F) << 4) | (data[1] & 0x0F)
luminance = ((2 ** exponent) * mantissa) * 0.045
# Output data to screen
print "Ambient Light luminance : %.2f lux" %luminance
I have this Python code that works fine on my sensor light when I'm using raspberry pi, I've tried to do the same thing on XMC but without success. I hope you can help me.
First of all I would recommend to build a if statement arround your code
like
DAVE_STATUS_t init_status;
init_status = DAVE_Init();
if(init_status == DAVE_STATUS_SUCCESS)
{
... code
}
With this you can check if the DAVE APP is initialized correct. If it is the case you can look further into the callback issue. If not it's a problem with the configuration.
You can find further code exampels by rightlciking on the I2C Master field in teh APP Dependency window -> APP Help. There are examples for the methods provided by the APP.
Related
I have a PIC24FJ256GA702 communicating with a AD5724RBREZ quad DAC with a SPI link.
The DAC works fine, writing is no problem, but I am stuck on reading back the control register from it.
I get the correct waveform on the PIC pin that I am expecting, and the read routine runs, but it always returns zeros. The waveform is clearly not zero- the waveform on the scope is correct for the 4 channels and the internal reference being enabled.
Scope view of waveforms - blue = clock, yellow = data input from DAC at PIC pin
(The excessive ringing on the scope image is probably caused by a long distance ground connection- in practice these chips are about 25mm apart.)
I thought that the input pin was configured as an analogue, but it was correctly a digital input.
I connected it to a counter based on Timer1, and that counter does count if I try to read the DAC. This suggests that the PPS is working, the pin is not bust, and the the input signal is clean enough to use.
I think it may be a problem with the code or the decode timing of the SPI module, but as shown in the image the data is stable during the clock cycle so I cannot see what is wrong.
I have searched the forums, and it seems most with this problem trace it to analogue functions being enabled but that is not the case here.
Would anyone like to suggest something else to try, or post some working SPI read code if my code is not looking correct?
The code follows-
void AOUT_init(void)
{
//assume PPS is unlocked (it is on reset) - see note below
//setup SPI1 itself
SPI1CON1L = 0x0160; //TX work read 0
//SPI1CON1L = 0x0060; //TX not work
//SPI1CON1L = 0x0120; //TX work, read 0
//SPI1CON1L = 0x0020; //not tried
SPI1CON1H = 0x3000;
SPI1CON2L = 0x0017; //word length (17 hex = 24 bits)
//BRG
SPI1BRGL = 0x0000; //default = no divisor
//PPS - assume unlocked at this point
ANSBbits.ANSB13 = 0;
TRISBbits.TRISB13 = TRIS_INPUT;
//##########################################################################
RPINR20bits.SDI1R = 13; //set SDI1 data input to PIC to RB13
//##########################################################################
TRISBbits.TRISB15 = TRIS_OUTPUT;
RPOR7bits.RP15R = 8; //RB15 to SDI1 clock out from PIC
TRISBbits.TRISB14 = TRIS_OUTPUT;
RPOR7bits.RP14R = 7; //RB14 to SDI1 data out from PIC
//AD5724R has additional lines - not all used in practice
//setup and set initial level here
//AOUT-/LDAC
TRISBbits.TRISB6 = TRIS_OUTPUT;
LATBbits.LATB6 = 1;
//AOUT-/SYNC
TRISBbits.TRISB7 = TRIS_OUTPUT;
LATBbits.LATB7 = 1;
//AOUT-/CLR
TRISBbits.TRISB12 = TRIS_OUTPUT;
LATBbits.LATB12 = 1;
//turn SPI on
SPI1CON1Lbits.SPIEN = 1;
SPI1CON1Lbits.MSTEN = 1; //included in definition above
//now setup the AD chip
//output range set
AOUT_TX(0x0C00,0x0100); //all channels to 10V
//control
AOUT_TX(0x1900,0x0500);
//power control - enable DACs
AOUT_TX(0x1000,0x1F00);
}
The comms routine below is included for completeness- it just controls the other DAC lines. The /SYNC line is doing a chip select function.
static void AOUT_Comms(bool bSync, bool bLDAC, bool bClr)
{
//AOUT-/LDAC
LATBbits.LATB6 = bLDAC;
//AOUT-/SYNC
LATBbits.LATB7 = bSync;
//AOUT-/CLR
LATBbits.LATB12 = bClr;
}
This is write routine which works fine.
void AOUT_TX(uint16_t dataH, uint16_t dataL)
{
//AOUT uses 24 bit data
//this routine handles /SYNC line
//relies on AD chip having much faster response than chip cycle time
//AD chip limits at about 38MHz so much quicker than PIC.
AOUT_Comms(0,1,1);
SPI1BUFL = dataL;
SPI1BUFH = dataH;
while(SPI1STATLbits.SPIBUSY) //wait until sent
{
;
}
AOUT_Comms(1,1,1);
//
}
This is the read routine, which uses the routines above.
void AOUT_read_control(uint16_t ReadH, uint16_t ReadL)
{
uint16_t temp;
//to read, transmit the register to be read, then transmit a dummy command "NOP" to clock the data out.
//send register
AOUT_TX(0x9000,0x0000);
//read out- this is similar to write but reads the received buffer at the end.
//clear buffer
while (SPI1STATLbits.SPIRBF)
{
temp = SPI1BUFL;
temp = SPI1BUFH;
}
AOUT_Comms(0,1,1);
SPI1BUFL = 0;
SPI1BUFH = 0x1800; //nop operation via control register
while(SPI1STATLbits.SPIBUSY) //wait until sent
{
;
}
while (!SPI1STATLbits.SPIRBF)
{
; //hold until something received
}
ReadH = SPI1BUFH;
ReadL = SPI1BUFL; //these read zero
AOUT_Comms(1,1,1);
//
//dummy so can check counter also connected to same pin
temp = TMR1;
temp = TMR1;
}
Update-
I checked the SPI read decode by sending the received words directly to a display as suggested by the comments. I get 0000 for both words.
Also as suggested I connected a logic analyser at the PIC pins, and it decodes both read and write correctly.Logic Analyser view- Ch1/Blue/MOSI writing 0x180000, Ch2/Green/MISP reading 0x00001F, both correct
I am trying to read data from a meter using a RS485 to TTL Converter to an STM32f407VG. My device slave ID is 121, the baudrate is 9600. I want to read holdingRegisters and InputRegisters.
I am trying this FreeMODBUS RTU port for STM32 HAL library .
I have connected my DI pin to PA_2(Tx), R0 pin to PA_3(Rx), DE&RE pin to GND. But I am not getting any data.
This is my code:
#include "stm32f4xx_hal.h"
#include "cmsis_os.h"
#include "mb.h"
#include "mbport.h"
#define REG_INPUT_START 30005
#define REG_INPUT_NREGS 8
static USHORT usRegInputStart = REG_INPUT_START;
static USHORT usRegInputBuf[REG_INPUT_NREGS];
void ModbusRTUTask(void const * argument)
{
/* ABCDEF */
usRegInputBuf[0] = 11;
usRegInputBuf[1] = 22;
usRegInputBuf[2] = 33;
usRegInputBuf[3] = 44;
usRegInputBuf[4] = 55;
usRegInputBuf[5] = 66;
usRegInputBuf[6] = 77;
usRegInputBuf[7] = 88;
eMBErrorCode eStatus = eMBInit( MB_RTU, 121, 3, 9600, MB_PAR_NONE );
eStatus = eMBEnable();
while(1) {
eMBPoll();
}
}
eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
eMBErrorCode eStatus = MB_ENOERR;
int iRegIndex;
if( ( usAddress >= REG_INPUT_START )
&& ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
{
iRegIndex = ( int )( usAddress - usRegInputStart );
while( usNRegs > 0 )
{
*pucRegBuffer++ =
( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );
*pucRegBuffer++ =
( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );
iRegIndex++;
usNRegs--;
}
HAL_GPIO_TogglePin(LD4_GPIO_Port, LD4_Pin);
}
else
{
HAL_GPIO_TogglePin(LD5_GPIO_Port, LD5_Pin);
eStatus = MB_ENOREG;
}
return eStatus;
}
eMBErrorCode
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,
eMBRegisterMode eMode )
{
return MB_ENOREG;
}
eMBErrorCode
eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils,
eMBRegisterMode eMode )
{
return MB_ENOREG;
}
eMBErrorCode
eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT
usNDiscrete)
{
return MB_ENOREG;
}
Why these variables ?
usRegInputBuf[0] = 11;
usRegInputBuf[1] = 22;
usRegInputBuf[2] = 33;
usRegInputBuf[3] = 44;
usRegInputBuf[4] = 55;
usRegInputBuf[5] = 66;
usRegInputBuf[6] = 77;
usRegInputBuf[7] = 88;
What changes do I need to make?
DE&RE pin to GND
DE and RE pins are used to enable and disable input and output from the converted. You should:
set DE=1 and RE=0 when transmitting
set DE=0 and RE=1 when receiving
(extra) set DE=0 and RE=0 when not using the device (this is dependent on the transceiver, but usually setting both to low saves a lot of energy)
Before sending each character through rs485, you need to set DE=1, RE=0, then write the character, then change back to receive mode and then receive the data.
Inspect the datasheet of the transceiver very carefully. If you use, ex. MAX485 (but really, their're all the same usuallly), you see at page 7:
More about three state logic can be found ex. on wiki.
With RE set to GND the gates will never open, so you will have always high impedance
on RO pin and never receive any data. Other way round, you will never send any data, because DE will be low, and I believe you need to send smth to the meter before it starts transmitting.
You should connect uart TX to DI, uart RX to RO. In your case and if the converter inverts one of the RE/DE pins (usually RE is inverted, as above) you can connect both to the same pin.
#kamilcuk is close, but on all RS485 transceivers, the DE is active high and the RE is active low. Tying DE and RE low will create a universal receiver that cannot send.
The simplest "half-duplex" RS485 is to connect RE and DE to a single GPIO pin. Drive this signal HIGH (GPIO_PIN_SET in HAL lingo) to transmit and set the signal LOW (GPIO_PIN_RESET in HAL lingo) to receive.
If you have RE and DE attached to different GPIO pins, that's okay, just set them to the same value at the same time. I use a couple of macros to do this, so I don't have to always think about the logic.
Then just name the pins accordingly in the CubeMX editor.
// RS485_DE Data Enable, Active High
// RS485_RE Receive En, Active Low
#define ENABLE_TRANSMIT() do { \
/* Disable Receiver */ \
HAL_GPIO_WritePin(RS485_RE_GPIO_Port, RS485_RE_Pin, GPIO_PIN_SET); \
/* Enable Transmitter */ \
HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_SET); \
} while(0)
#define ENABLE_RECEIVE() do { \
/* Enable Receiver */ \
HAL_GPIO_WritePin(RS485_RE_GPIO_Port, RS485_RE_Pin, GPIO_PIN_RESET); \
/* Disable Transmitter */ \
HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_RESET); \
} while(0)
The Github code is messy, i would suggest you to follow the UART communication procedure mentioned here
First check using a serial terminal if you are able to communicate using UART with the stm32f4x board.
Use modbus communication protocol to implement your register reading logic.
I am trying to setup CAN communication between a couple of LPC device nodes. My setup includes a couple of CAN nodes writing on to the CAN bus. For example LPC 2292 CAN controller can write on to the CAN bus and the LPC1758 can receive the data. This works perfectly fine. Now LPC1758 has 2 CAN controllers and I have setup one for receiving data and the other for transmitting data on the bus as a response. I also setup interrupt handlers for LPC 1758 CAN 1 transmit & receive and CAN 2 transmit & receive. ( I dont have code for LPC 2292. its not under my control)
My problem is at the LPC1758 side. Here the CAN 1 receiver is able to get the data from the other CAN nodes as I can see the interrupt vector handler being called. The problem is when the the LPC 1758 CAN 2 tranmistter writes to the bus . It gets a bus error . More specificially "Start of Frame " error . ( I use a Ulink2 debugger). Now reading the CAN specs I know the start frame of the CAN message should start with a low ( dominant) bit CAN specs ; See page 3
How do I go about fixing this error ? Its not a configurable register that I can set the first bit to 0 or 1. I run the default LPC 1758 CAN code that comes with KEIL C:\Keil_v5\ARM\Boards\Keil\MCB1700\CAN I think the code is fine because when I run the code in simulation mode of KEIL I can see the CAN commnication work well.
Is this "Start of Frame" a by product of some other configurations that I am missing ?
Update Code :
I run the default LPC 1758 CAN code that comes with KEIL C:\Keil_v5\ARM\Boards\Keil\MCB1700\CAN I think the code is fine because when I run the code in simulation mode of KEIL I can see the CAN communication work well. Also I did not make any changes to the code except the baudrate.
CAN setup :
/*----------------------------------------------------------------------------
setup CAN interface. CAN controller (1..2)
*----------------------------------------------------------------------------*/
void CAN_setup (uint32_t ctrl) {
LPC_CAN_TypeDef *pCAN = (ctrl == 1) ? LPC_CAN1 : LPC_CAN2;
if (ctrl == 1) {
LPC_SC->PCONP |= (1 << 13); /* Enable power to CAN1 block */
LPC_PINCON->PINSEL0 |= (1 << 0); /* Pin P0.0 used as RD1 (CAN1) */
LPC_PINCON->PINSEL0 |= (1 << 2); /* Pin P0.1 used as TD1 (CAN1) */
NVIC_EnableIRQ(CAN_IRQn); /* Enable CAN interrupt */
} else {
LPC_SC->PCONP |= (1 << 14); /* Enable power to CAN2 block */
LPC_PINCON->PINSEL4 |= (1 << 14); /* Pin P2.7 used as RD2 (CAN2) */
LPC_PINCON->PINSEL4 |= (1 << 16); /* Pin P2.8 used as TD2 (CAN2) */
NVIC_EnableIRQ(CAN_IRQn); /* Enable CAN interrupt */
}
LPC_CANAF->AFMR = 2; /* By default filter is not used */
pCAN->MOD = 1; /* Enter reset mode */
pCAN->IER = 0; /* Disable all interrupts */
pCAN->GSR = 0; /* Clear status register */
CAN_cfgBaudrate(ctrl, /*250000*/ 100000); /* Set bit timing */
pCAN->IER = 0x0003; /* Enable Tx and Rx interrupt */
//pCAN->IER = 0x7FF;
}
Here is my code to transmit and receive:
/*----------------------------------------------------------------------------
wite a message to CAN peripheral and transmit it. CAN controller (1..2)
*----------------------------------------------------------------------------*/
void CAN_wrMsg (uint32_t ctrl, CAN_msg *msg) {
LPC_CAN_TypeDef *pCAN = (ctrl == 1) ? LPC_CAN1 : LPC_CAN2;
uint32_t CANData;
CANData = (((uint32_t) msg->len) << 16) & 0x000F0000 |
(msg->format == EXTENDED_FORMAT ) * 0x80000000 |
(msg->type == REMOTE_FRAME) * 0x40000000;
if (pCAN->SR & (1<<2)) { /* Transmit buffer 1 free */
pCAN->TFI1 = CANData; /* Write frame informations */
pCAN->TID1 = msg->id; /* Write CAN message identifier */
pCAN->TDA1 = *(uint32_t *) &msg->data[0]; /* Write first 4 data bytes */
pCAN->TDB1 = *(uint32_t *) &msg->data[4]; /* Write second 4 data bytes */
//pCAN->CMR = 0x31; /* Select Tx1 for Self Tx/Rx */
pCAN->CMR = 0x21; /* Start transmission without loop-back */ -- Here is when "Start of Frame " error happens
}
}
Receive code is fine but still posting
/*----------------------------------------------------------------------------
read a message from CAN peripheral and release it. CAN controller (1..2)
*----------------------------------------------------------------------------*/
void CAN_rdMsg (uint32_t ctrl, CAN_msg *msg) {
LPC_CAN_TypeDef *pCAN = (ctrl == 1) ? LPC_CAN1 : LPC_CAN2;
uint32_t CANData;
/* Read frame informations */
CANData = pCAN->RFS;
msg->format = (CANData & 0x80000000) == 0x80000000;
msg->type = (CANData & 0x40000000) == 0x40000000;
msg->len = ((uint8_t)(CANData >> 16)) & 0x0F;
msg->id = pCAN->RID; /* Read CAN message identifier */
if (msg->type == DATA_FRAME) { /* Read the data if received message was DATA FRAME */
*(uint32_t *) &msg->data[0] = pCAN->RDA;
*(uint32_t *) &msg->data[4] = pCAN->RDB;
}
}
Baudrate Calculation:
/*----------------------------------------------------------------------------
configure the requested baudrate. CAN controller (1..2)
*----------------------------------------------------------------------------*/
static void CAN_cfgBaudrate (uint32_t ctrl, uint32_t baudrate) {
LPC_CAN_TypeDef *pCAN = (ctrl == 1) ? LPC_CAN1 : LPC_CAN2;
uint32_t result = 0;
uint32_t nominal_time;
/* Determine which nominal time to use for PCLK */
if (((PCLK / 1000000) % 6) == 0) {
nominal_time = 12; /* PCLK based on 72MHz CCLK */
} else {
nominal_time = 10; /* PCLK based on 100MHz CCLK */
}
/* Prepare value appropriate for bit time register */
result = (PCLK / nominal_time) / baudrate - 1;
result &= 0x000003FF;
result |= CAN_BIT_TIME[nominal_time];
pCAN->BTR = result; /* Set bit timing */
}
I am finally able to get rid of the "Start of Frame " error that had being bogging me down. I had a discussion with some of our other CAN bus code authors and he suggested i use the same CAN controller for sending and receiving. In short this:
** Does not work** :
LPC 2292 : CAN 1 Rx & CAN 2 Tx
LPC 1750 : CAN 1 Rx & CAN 2 TX
** WORKS** :
LPC 2292 : CAN 1 Rx & CAN 1 Tx
LPC 1750 : CAN 1 Rx & CAN 1 TX
Being a newbie i may have made this mistake. But the default code provided by KEIL shows using alternate CAN controllers for sending and transmitting. I hope this helps someone .
I am trying to write a device code for C8051F340 to get the data from the host(PC) via USB. I have some example from silicon lab and the code look like below:
void Receive_File(void)
{
ReadStageLength = ((BytesToRead - BytesRead) > MAX_BLOCK_SIZE_READ)? MAX_BLOCK_SIZE_READ:(BytesToRead - BytesRead);
BytesRead += Block_Read((U8*)(&TempStorage[BlockIndex]), ReadStageLength); // Read Block
BlockIndex++;
// If device has received as many bytes as fit on one FLASH page, disable interrupts,
// write page to flash, reset packet index, enable interrupts
// Send handshake packet 0xFF to host after FLASH write
if ((BlockIndex == (BLOCKS_PR_PAGE)) || (BytesRead == BytesToRead))
{
Page_Erase((U8*)(PageIndices[PageIndex]));
Page_Write((U8*)(PageIndices[PageIndex]));
PageIndex++;
Led1 = !Led1;
BlockIndex = 0;
Buffer[0] = 0xFF;
Block_Write(Buffer, 1); // Send handshake Acknowledge to host
}
// Go to Idle state if last packet has been received
if (BytesRead == BytesToRead) {M_State = ST_IDLE_DEV; Led1 = 0;}
}
// Startup code for SDCC to disablt WDT before initializing variables so that
// a reset does not occur
#if defined SDCC
void _sdcc_external_startup (void)
{
PCA0MD &= ~0x40; // Disable Watchdog timer
}
#endif
I have some questions want to ask:
1. Where the data goes? the Buffer [0]?
2. if I got a Hex value transfer from the host, can I just read the Buffer [0] to get it ?
sorry I am a newbie.
Thank you.
Your received data stored in array TempStorage
You used Buufer[0] (the value 0xFF) for to send data to host
I'm testing a serial implementation and when I open a serial port (I have an Arduino spitting out lines of compass data.) sometimes I get a bunch of zeros initially. I had thought this was leftover data from before, but it does not seem to be (flushing IO doesn't seem to help)
This is a programming language serial implementation written in C and I'm testing this on Linux, but had similar results with Windows.
The strace output shows this on the first read:
read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 34815) = 1544
write(1, "== ", 3== ) = 3
write(1, "#{\n00000000000000000000000000000"..., 503#{
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000583A20353333202020593A20313020
20205A3A2033313920202058733A2033393520202059733A203136312020205A
733A20323933202020483A20302E3436202020413A...) = 503
I have tried adding the following line to clear this data just before the port is closed, after it is opened, and after the attributes it has set:
tcflush(ttyfd, TCIOFLUSH);
but it doesn't seem to help with this issue. Any thoughts about how to clean this up?
The code is from a large project and I have collected some of the relevant parts below, the variables are declared even if not shown but should be clear enough
Opening the port:
ttyfd = open(&devpath[0], O_RDWR | O_NOCTTY | O_NONBLOCK); // ttyUSB0 in this case
Changing settings:
if (speeds[n] == 0) speed = B115200; // invalid, use default
cfsetospeed (&attr, speed);
cfsetispeed (&attr, speed);
// C-flags - control modes:
attr.c_cflag |= CREAD | CS8 | CLOCAL;
// L-flags - local modes:
attr.c_lflag = 0; // raw, not ICANON
// I-flags - input modes:
attr.c_iflag |= IGNPAR;
// O-flags - output modes:
attr.c_oflag = 0;
// Control characters:
// device is non-blocking (polled for changes):
attr.c_cc[VMIN] = 0;
attr.c_cc[VTIME] = 0;
// Make sure OS queues are empty:
tcflush(ttyfd, TCIOFLUSH);
// Set new attributes:
if (tcsetattr(ttyfd, TCSANOW, &attr)) return 2;
This is the Arduino code sending data from an OSEPP Compass module
// OSEPP Compass Sensor Example Sketch
// by OSEPP <http://www.osepp.com>
// Modifications by Chris W. to accommodate declination, scaling and origin adjustment 2013-02-13
// This sketch demonstrates interactions with the Compass Sensor
#include <Wire.h>
const uint8_t sensorAddr = 0x1E; // Sensor address (non-configurable)
const float xOffset = 103.0; // Offset required to adjust x coordinate to zero origin
const float yOffset = -165.0; // Offset required to adjust y coordinate to zero origin
const float declination = 70.1; // Enter magnetic declination mrads here (local to your geo area)
// One-time setup
void setup()
{
// Start the serial port for output
Serial.begin(115200);
// Join the I2C bus as master
Wire.begin();
// Configure the compass to default values (see datasheet for details)
WriteByte(sensorAddr, 0x0, 0x70);
WriteByte(sensorAddr, 0x1, 0x20); // +1.3Ga
// Set compass to continuous-measurement mode (default is single shot)
WriteByte(sensorAddr, 0x2, 0x0);
}
// Main program loop
void loop()
{
uint8_t x_msb; // X-axis most significant byte
uint8_t x_lsb; // X-axis least significant byte
uint8_t y_msb; // Y-axis most significant byte
uint8_t y_lsb; // Y-axis least significant byte
uint8_t z_msb; // Z-axis most significant byte
uint8_t z_lsb; // Z-axis least significant byte
int x;
int y;
int z;
// Get the value from the sensor
if ((ReadByte(sensorAddr, 0x3, &x_msb) == 0) &&
(ReadByte(sensorAddr, 0x4, &x_lsb) == 0) &&
(ReadByte(sensorAddr, 0x5, &z_msb) == 0) &&
(ReadByte(sensorAddr, 0x6, &z_lsb) == 0) &&
(ReadByte(sensorAddr, 0x7, &y_msb) == 0) &&
(ReadByte(sensorAddr, 0x8, &y_lsb) == 0))
{
x = x_msb << 8 | x_lsb;
y = y_msb << 8 | y_lsb;
z = z_msb << 8 | z_lsb;
int xs;
int ys;
int zs;
float gScale = .92; // Scale factor for +1.3Ga setting
float adjx = x - xOffset;
float adjy = y - yOffset;
xs = adjx * gScale;
ys = adjy * gScale;
zs = z * gScale;
float heading = atan2(ys, xs);
heading += declination / 1000; // Declination for geo area
if (heading < 0);
heading += 2*PI;
if (heading > 2*PI)
heading -= 2*PI;
float angle = heading * 180/M_PI;
Serial.print("X: ");
Serial.print(x);
Serial.print(" Y: ");
Serial.print(y);
Serial.print(" Z: ");
Serial.print(z);
Serial.print(" Xs: ");
Serial.print(xs);
Serial.print(" Ys: ");
Serial.print(ys);
Serial.print(" Zs: ");
Serial.print(zs);
Serial.print(" H: ");
Serial.print(heading);
Serial.print(" A: ");
Serial.println(angle);
}
else
{
Serial.println("Failed to read from sensor");
}
// Run again in 1 s (1000 ms)
delay(500);
}
// Read a byte on the i2c interface
int ReadByte(uint8_t addr, uint8_t reg, uint8_t *data)
{
// Do an i2c write to set the register that we want to read from
Wire.beginTransmission(addr);
Wire.write(reg);
Wire.endTransmission();
// Read a byte from the device
Wire.requestFrom(addr, (uint8_t)1);
if (Wire.available())
{
*data = Wire.read();
}
else
{
// Read nothing back
return -1;
}
return 0;
}
// Write a byte on the i2c interface
void WriteByte(uint8_t addr, uint8_t reg, byte data)
{
// Begin the write sequence
Wire.beginTransmission(addr);
// First byte is to set the register pointer
Wire.write(reg);
// Write the data byte
Wire.write(data);
// End the write sequence; bytes are actually transmitted now
Wire.endTransmission();
}
I'll try switch the order of the delay and the serial writing in the arduino code as it may resolve this issue, but the code will remain ineffective for a similar future scenario.
Had a look through the IC's datasheet.
See page 14: Link
Continuous-Measurement Mode. In continuous-measurement mode, the device continuously performs measurements and places the result in the data register. RDY goes high when new data is placed in all three registers. After a power-on or a write to the mode or configuration register, the first measurement set is available from all three data output registers after a period of 2/f_DO and subsequent measurements are available at a frequency of f_DO, where f_DO is the frequency of data output
According to Arduino docs, the Wire library runs at 100KHz. 2/100KHz == 20uS. Assuming your Arduino runs at 16MHz (1 cycle == 62.5ns), you probably aren't waiting long enough to read the registers.
To make your code more robust, I recommend checking the status register's RDY bit before performing any reads. The data is likely correct, but you have to account for the startup time taken by the IC.
If you do this, and find the same issue still occurring, then at least you'll have further isolated the issue.
Alternatively: you can put the 500ms delay at the bottom of your main loop at the top if you just want a quick hack.