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.
Related
I also have a problem with DRDY. I need to include DRDY. The pins for DRDY are RD2 and RD5. They are both inputs.
Here is the information for DRDY.
DRDY Pin
DRDY is an open-drain output (in SPI mode) or bidirectional pin (in UART mode) with an internal 20 k – 50 k pullup
resistor.
Most communications failures are the result of failure to properly observe the DRDY timing.
Serial communications pacing is controlled by this pin. Use of DRDY is critical to successful communications with the
QT1481. In either UART or SPI mode, the host is permitted to perform a data transfer only when DRDY has returned
high. Additionally, in UART mode, the QT1481 delays responses to the host if DRDY is being held low by the host.
After each byte transfer, DRDY goes low after a short delay and remains low until the QT1481 is ready for another
transfer. A short delay occurs before DRDY is driven low because the QT1481 may otherwise be busy and requires
a finite time to respond.
DRDY may go low for a microsecond only. During the period from the end of one transfer until DRDY goes low and
back high again, the host should not perform another transfer. Therefore, before each byte transmission the host
should first check that DRDY is high again.
If the host wants to perform a byte transfer with the QT1481 it should behave as follows:
1. Wait at least 100 µs after the previous transfer (time S5 in Figure 3-2 on page 23: DRDY is guaranteed to go
low before this 100 µs expires).
2. Wait until DRDY is high (it may already be high).
3. Perform the next transfer with the QT1481.
In most cases it takes up to 3 ms for DRDY to return high again. However, this time is longer with some commands
or if the STS_DEBUG setup is enabled, as follows:
0x01 (Setups load): <20 ms
0x02 (Low Level Cal and Offset): <20 ms
Add 15 ms to the above times if the STS_DEBUG setup is enabled.
Other DRDY specifications:
Min time DRDY is low: 1 µs
Max time DRDY is low after reset: 100 ms
The timing diagram is this:
How can implement that?
The code I have written with my friend is written here:
#include <xc.h>
#include "PIC.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
//#include <pic18f45k80.h>
#define MSB 1
#define LSB 0
// SPI PIN CONFIGURATION
#define SCK_TRIS TRISCbits.TRISC3 = 0 ;
#define SDO_TRIS TRISCbits.TRISC5 = 0 ;
#define SDI_TRIS TRISCbits.TRISC4 = 1 ;
#define QTA_SS_TRIS TRISDbits.TRISD4 = 0 ;
#define QTB_SS_TRIS TRISEbits.TRISE2 = 0 ;
#define QTA_SS_LAT_LOW LATDbits.LATD4 = 0 ;
#define QTA_SS_LAT_HIGH LATDbits.LATD4 = 1 ;
#define QTB_SS_LAT_LOW LATEbits.LATE2 = 0 ;
#define QTB_SS_LAT_HIGH LATEbits.LATE2 = 1 ;
#define QTA_DRDY_TRIS TRISDbits.TRISD5 = 1 ;
#define QTB_DRDY_TRIS TRISDbits.TRISD2 = 1 ;
#define QTA_DRDY_LAT_LOW LATDbits.LATD5 = 0 ;
#define QTA_DRDY_LAT_HIGH LATDbits.LAT52 = 1 ;
#define QTB_DRDY_LAT_LOW LATDbits.LAT25 = 0 ;
#define QTB_DRDY_LAT_HIGH LATDbits.LATD2 = 1 ;
#define QTB_DRDY PORTDbits.RD2 ;
#define QTA_DRDY PORTDbits.RD5 ;
// FREQUENCY SELECT
#define _XTAL_FREQ 16000000
// PIN SETUP
void PIN_MANAGER_Initialize(void)
{
/**
LATx registers
*/
LATE = 0x00;
LATD = 0x00;
LATA = 0x00;
LATB = 0b00010000;
LATC = 0x00;
/**
TRISx registers
*/
TRISE = 0x00;
TRISA = 0x08;
TRISB = 0x01;
TRISC = 0b00010000;
TRISD = 0xEF;
PORTC = 0b00010010 ;
/**
ANSELx registers
*/
ANCON0 = 0x00;
ANCON1 = 0x00;
/**
WPUx registers
*/
WPUB = 0x00;
INTCON2bits.nRBPU = 1;
}
// SPI
void SPI_Initialize(void)
{
// SMP Middle; CKE Idle to Active;
SSPSTAT = 0b00000000;
// SSPEN enabled; WCOL no_collision; CKP Idle:High, Active:Low; SSPM FOSC/4; SSPOV no_overflow;
SSPCON1 = 0b00111010;
// SSPADD 0;
SSPADD = 0x00;
ADCON0 = 0 ;
ADCON1 = 0x0F ; //Makes all I/O digital
SCK_TRIS ;
SDO_TRIS ;
SDI_TRIS ;
QTA_SS_TRIS ;
QTB_SS_TRIS ;
QTA_DRDY_TRIS ;
QTB_DRDY_TRIS ;
}
signed char WriteSPI( unsigned char data_out )
{
unsigned char TempVar;
TempVar = SSPBUF; // Clears BF
PIR1bits.SSPIF = 0; // Clear interrupt flag
SSPCON1bits.WCOL = 0; //Clear any previous write collision
SSPBUF = data_out; // write byte to SSPBUF register
if ( SSPCON1 & 0x80 ) // test if write collision occurred
return ( -1 ); // if WCOL bit is set return negative #
else
while( !PIR1bits.SSPIF ); // wait until bus cycle complete
return ( 0 ); // if WCOL bit is not set return non-negative#
}
unsigned char ReadSPI( void )
{
unsigned char TempVar;
TempVar = SSPBUF; // Clear BF
PIR1bits.SSPIF = 0; // Clear interrupt flag
SSPBUF = 0x00; // initiate bus cycle
while(!PIR1bits.SSPIF); // wait until cycle complete
return ( SSPBUF ); // return with byte read
}
unsigned char DataRdySPI( void )
{
if ( SSPSTATbits.BF )
return ( +1 ); // data in SSPBUF register
else
return ( 0 ); // no data in SSPBUF register
}
// SOFTWARE EUART
void out_char(char character, char bit_order){
uint8_t i = 0;
RSOUT = 1 ; // MSB
__delay_ms(1);
RSOUT = 0 ; // START
__delay_us(100);
for (i = 8; i>0; --i){
if (bit_order){ // Bit order determines how you will put the bits, from left to right (MSB) or right to left (LSB)
RSOUT = (character & 0x80) ? 1:0; // in MSB you compare the left-most bit doing an AND with 0x80, and put 1 if true, 0 elsewhere.
character <<= 1; // Shift the character to the left, discrading the bit just sent
} else {
RSOUT = (character & 0x01); // in LSB you compare the right-most bit doing an AND with 0x01, and put 1 if true, 0 else.
character >>= 1; // Shift the character to the right, discrading the bit just sent
}
__delay_us(100);
}
RSOUT = 1 ; // STOP
}
void out_str(char * string, uint8_t len, char bit_order){
uint8_t i = 0;
for (i = 0; i< len; i++){
out_char(string[i], bit_order);
}
}
void SYSTEM_Initialize(void)
{
PIN_MANAGER_Initialize() ;
SPI_Initialize() ;
}
void main(void)
{
SYSTEM_Initialize() ;
while (1)
{
QTB_SS_LAT_LOW ; // Transmit data
char temp ;
WriteSPI(0x0F) ; // Send a byte
while(!DataRdySPI()) ; // wait for a data to arrive
temp = ReadSPI(); // Read a byte from the
QTB_SS_LAT_HIGH ; // Stop transmitting data
__delay_us(100) ;
}
}
No. Do not just write a bunch of code, then see what it does. That kind of shotgun (or, if you prefer, spaghetti-to-the-wall) approach is a waste of effort.
First, drop all those macros. Instead, write comments that describe the purpose of each chunk of code, like the first three assignments in your SPI_Initialize() function.
Next, convert your specification to pseudocode. The format does not matter much, just use something that lets you keep your mind focused on what the purpose is, rather than on the details on how to do it.
The datasheet says that with SPI, there are three outputs from the PIC (^SS, SCK, MOSI on the QT1481), and two inputs (^DRDY and MISO on the QT1481). I'll use those names for the data lines, and for their respective I/O pin names on the PIC.
The setup phase on the PIC should be simple:
Make ^DRDY an input
Make ^SS an output, set it HIGH
Make SCK an output, set it LOW
Make MOSI an output, set it LOW
Make MISO an input
Set up SPI using SCK, MOSI, MISO
Each transfer is a bidirectional one. Whenever you send data, you also receive data. The zero command is reserved for receiving multiple data, says the datasheet. So, you only need a function that sends a byte, and at the same time receives a byte:
Function SPITransfer(command):
Make sure at least 0.1ms has passed since the previous transfer.
Do:
Nothing
While (^DRDY is LOW)
Set ^SS LOW
response = Transfer(command)
Set ^SS HIGH
Return response
End Function
As I understand it, for PICs and properly initialized hardware SPI the response = Transfer(command) line is in C
SSPBUF = command;
while (!DataRdySPI())
;
response = SSPBUF;
You can also bit-bang it, in which case it is (in pseudocode):
response = 0
For bit = 7 down to 0, inclusive:
If (command & 128):
Set MOSI high
Else:
Set MOSI low
End If
Set SCK low
Sleep for a half period
command = command / 2
response = response * 2
If MISO high:
response = response + 1
End If
Set SCK high
Sleep for a half period
End For
but obviously the hardware SPI approach is better.
(When you get this working, you can use the hardware SPI without a wait loop from a timer interrupt, making the communications essentially transparent to the "main operation" of the PIC microcontroller. That requires a slightly different approach, with a command and response queues (of a few bytes), but will make it much easier for the PIC to do actual work, other than just scan the QT1481.)
After a reset, you essentially send 0x0F until you get 0xF0 back:
while (SPITransfer(0x0F) != 0xF0)
;
At this point, you have the steps you need to implement in C. OP also has the hardware (an oscilloscope) to verify their code works.
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.
Here I need to output a waveform on oscilloscope in C which should in a rising sawtooth waveform.I am not sure if my code is correct. Any help or suggestions?
while(1)
{
for (i = 1; i < 360; i++);
// Check to see if status.TRDY is 1
while(*(base+2) & 0x40 != 1);
// while shift register is not empty
// Make the sawtooth pattern
if (saw == 0x1fff){
saw = 0x1000;
}
else {
saw = saw+1;
}
// transmit sawtooth to the oscilloscope
*(base+1) = saw;
}
This only tidies up the OP's posted code. It does not answer how to program the DAC. OP is using a 16-bit amplitude value but his register addressing suggests 8-bit registers - perhaps two writes are needed.
I suggest you also need function arguments defining the period of the sawtooth wave, and the number of steps. You also need an exit condition. I leave these points to you.
#Chris Stratton also commented that the I/O port should be the correct language type.
#define MINSAW 0x1000
#define MAXSAW 0x1FFF
unsigned *base = (unsigned *)0xD000; // "insert your value"
int main() {
unsigned saw, i;
while(1) {
for (i = 0; i < 360; i++) {
// ratio the waveform amplitude
saw = MINSAW + i * (MAXSAW - MINSAW) / 359;
// Check to see if status.TRDY is 1
while((*(base+2) & 0x40) != 0x40);
// transmit sawtooth to the oscilloscope
*(base+1) = saw;
}
}
return 1;
}
How do I change slave address of mlx90614 with bcm2835 library? I've tried following code...
int main()
{
// Buffer, where I store data which I'll send
unsigned char buf[6];
// bcm2835 i2c module intialisation code
bcm2835_init();
bcm2835_i2c_begin();
bcm2835_i2c_set_baudrate(25000);
bcm2835_i2c_setSlaveAddress(0x00);
// For debug purposes, I read what reason codes operations give.
bcm2835I2CReasonCodes why;
bcm2835_i2c_begin();
// function which reads and prints what value eeprom address 0x0e has.
// See below the main.
printf("Initial check\n");
check(); // this time it prints a factory default value 0x5a.
// To access eeprom, the command must start with 0x2X, where x determines the
// address, resulting 0x2e.
buf[0] = 0x2e;
// According to datasheet, I first have to clear the address before
// real write operation.
buf[1] = 0x00;
buf[2] = 0x00;
why = bcm2835_i2c_write(buf,3);
reason(why); // resolves and prints the reason code. This time it prints OK
// according to datasheet, eeprom needs 5ms to make a write operation,
// but I give it 2 seconds.
sleep(2);
// Then I check did the value in eeprom 0x0e change. IT DOESN'T!
printf("Check after clear\n");
check();
// Then I try to write a new address to the eeprom but since the clearing
// the register didn't work, this is very unlikely to work either.
buf[0] = 0x2e;
buf[1] = 0x4b;
buf[2] = 0x00;
why = bcm2835_i2c_write(buf,3);
reason(why);
sleep(2);
// The datasheet says that I have to reset the power supply and after that
// the device should respond to the new slave address.
// I do that by pluging off the jumper wires and reconnecting them
// after the program has finnished.
bcm2835_i2c_end();
return 0;
}
// The function I use to determine what the reason code was.
void reason(bcm2835I2CReasonCodes why)
{
printf("Reason is: ");
if(why == BCM2835_I2C_REASON_OK)
{
printf("OK");
}else if(why == BCM2835_I2C_REASON_ERROR_NACK){
printf("NACK");
}else if(why == BCM2835_I2C_REASON_ERROR_CLKT){
printf("Clock stretch");
}else if(why == BCM2835_I2C_REASON_ERROR_DATA ){
printf("Data error");
}else{
printf("Dunno lol");
}
printf("\n");
return;
}
// Here I read eeprom 0x2e.
void check()
{
unsigned char buf[6];
unsigned char reg = 0x2e;
bcm2835I2CReasonCodes why;
// better safe than sorry with the buffer :)
buf[0] = 0;
buf[1] = 0;
buf[2] = 0;
why = bcm2835_i2c_write (®, 1);
reason(why);
why = bcm2835_i2c_read_register_rs(®,&buf[0],3);
reason(why);
printf("Buffer values are: %x ; %x ; %x \n", buf[0], buf[1], buf[2]);
}
The output of the program is following:
Initial check
Reason is: OK
Reason is: OK
Buffer values are: 5a ; be ; dc
Reason is: OK
Check after clear
Reason is: OK
Reason is: OK
Buffer values are: 5a ; be ; dc
Reason is: OK
If I run i2cdetect -y 1 after that, the device doesn't appear in the table, but it responds to programs calling it from either 0x00 or 0x5a. After I've used such a program, the i2cdetect detects the device normally from address 0x5a.
So I guess the real question is, why I can't clear and rewrite the eeprom 0x0e?
The description of Mlx90614 SMBus communication can be found below. The most relevat page is IMO the page 19 which actually gives the pseudocode example of what I'm trying to do.
http://www.melexis.com/Assets/SMBus-communication-with-MLX90614-5207.aspx
Here's the datasheet for mlx90614
http://www.melexis.com/Assets/IR-sensor-thermometer-MLX90614-Datasheet-5152.aspx
And here's the documentation for bcm2835
www.airspayce.com/mikem/bcm2835/group__i2c.html
You have to add an Error-Byte. Take a look at this website for an explanation: https://sf264.wordpress.com/2011/03/10/howto-mlx90614-und-pwm/
Calculating CRC-8 for 00002e4b00 gives 0xa3.
I used for calculating CRC-8 this website: http://smbus.org/faq/crc8Applet.htm
I haven't tested this, but I think this should work:
buf[0] = 0x2e;
buf[1] = 0x4b;
buf[2] = 0x00;
buf[3] = 0xa3;
why = bcm2835_i2c_write(buf,4);
Struggled with the exact same problem with my mlx90614s. Here is the write routine I used to solve it (Please note that the bcm2835-library was properly initalized before the call to the routine).
First I called the write routine with "correct" Slaveaddress, command=0x2E (EEPROMAccess | SMBusAddressReg) and data=0x0000 (for erase). The "correct" slave address can be 0x00 or the factory default 0x5a (or whatever is the chip's true address).
After erasing I used the same write routine but now with data=0x005b, to change from the factory default 0x5a to 0x5b, did a Power Off Reset (POR) and the device showed up with its new address (0x5b) using i2cdetect.
uint8_t memWriteI2C16(uint8_t SlaveAddress, uint8_t command, uint16_t data)
{
unsigned char arr[5];
uint8_t status;
//Prepare for CRC8 calc
arr[0] = SlaveAddress<<1; //NB! 7 bit address + a 0 write bit.
arr[1] = command; //Command byte in packet
arr[2] = *((uint8_t *)(&data)); //Extract data low byte
arr[3] = *((uint8_t *)(&data)+1);//Extract data high byte
arr[4] = crc8(&arr[0],4)&0xFF; //Calculate PEC by CRC8
bcm2835_i2c_setSlaveAddress(SlaveAddress);//Transmit address byte to I2C/SMBus
status = bcm2835_i2c_write (&arr[1], 4); //Transmit Command,DataL, DataH and PEC
bcm2835_delay(5); //Delay at least 5ms
return (status);
}
The CRC8 routine I used was:
// Return CRC-8 of the data, using x^8 + x^2 + x + 1 polynomial.
// A table-based algorithm would be faster, but for only a few bytes
// it isn't worth the code size.
// Ref: https://chromium.googlesource.com/chromiumos/platform/vboot_reference/+/master/firmware/lib/crc8.c
uint8_t crc8(const void *vptr, int len)
{
const uint8_t *data = vptr;
unsigned crc = 0;
int i, j;
for (j = len; j; j--, data++) {
crc ^= (*data << 8);
for(i = 8; i; i--) {
if (crc & 0x8000)
crc ^= (0x1070 << 3);
crc <<= 1;
}
}
return (uint8_t)(crc >> 8);
}
In addition: according to the data sheet for the the mlx90614, its default factory state after power up is PWM output. When hooking an mlx90614 in the factory PWM state to the I2C bus on the RPi2, the i2cdetect reports hundreds of I2C devices on the bus. Trying to access the mlx90614 by using the bcm2835-library fails. What is required is to force the mlx90614 out of its PWM-state by holding the SCL low for at least 2ms. Here is what I did:
uint8_t mlx90614SMBusInit()
{
//Hold SCL low for at leat 2ms in order to force the mlx90614 into SMBus-mode
//Ref Melix app note regarding SMBus comm chapter 6.1 and table 5.
uint8_t SCL1 = 3; //BCM2835 pin no 3 -RPi2 and RevB+. Use if i2cdetect -y 1
uint8_t SCL0 = 1; //BCM2835 pin no 1 -RPi2 and RevB+. Use if i2cdetect -y 0
uint8_t SCL;
SCL = SCL1;
bcm2835_gpio_fsel(SCL, BCM2835_GPIO_FSEL_OUTP);
bcm2835_gpio_write(SCL ,LOW);
bcm2835_delay( 3); //Delay >2 ms
bcm2835_gpio_write(SCL ,HIGH);
return (1);
}
However, this only hold until next power up. Hence it is required to write to the pwmctrl-register in mlx90614's eeprom (disable pwm output and force SDA to OpenDrain). I used the write routine as previously described with command=0x22 (i.e. EEPROMAccess | PWMCTRLAddressRegister) and after erasing the pwmctrl-register content, I wrote 0x0200 to it (the frst 3 nibbles was 020 in my devices...). Power Off Reset (POR) and the device started in SMBus-mode (no jamming of the I2C-bus). The mlx90614 is a tricky little component...
Also if you are using I2C-tools package from any linux distribution (in my case I'm using debian distro) you could change the address with the i2cset command (https://manpages.debian.org/buster/i2c-tools/i2cset.8.en.html), here is an example:
#Find your I2C bus in your linux with the command i2cdetect -l
#(in my case is the i2c-1)
i2cdetect -l
i2c-1 i2c bcm2835 I2C adapter I2C adapter
#Write the word 0x0000 to the address 0x2E and append the PEC check byte.
i2cset -y 1 0x5a 0x2E 0x0000 wp
#Write the new address as a word, to the address 0x2E and append the PEC
#check byte. In my case the new address is 0x005c
i2cset -y 1 0x5a 0x2E 0x005c wp
#Perform a power cycle of the Mlx90614 device
#Check the new address with the command i2cdetect -y 1
i2cdetect -y 1
I'm trying to modify this code in an attempt to make it work on an Arduino Mega. I'm pretty much new to C so, I may have made some major mistakes. By the way, this is for a self balancing skateboard.
This code is taken from an ATmega32 (from here and I'm trying to make it work on a Arduino Mega).
This code was writen for an ATmega32 development board.
But I encounter this error:
o: In function main':</br>
C:\Users\*******\AppData\Local\Temp\build27006.tmp/Test2.cpp:406:</br>
undefined reference tosetup'
How come? I don't even have a reference to setup in here!
Here is my code:
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <math.h>
#define CLOCK_SPEED 16000000
#define OCR1_MAX 1023
typedef unsigned char u8;
void set_motor_idle(void);
void InitPorts(void);
float level = 0;
float Throttle_pedal;
float aa;
float accelraw;
float x_acc;
float accsum;
float x_accdeg;
float gyrosum;
float gangleratedeg;
float gangleraterads;
float ti = 2.2;
float overallgain;
float gaincontrol;
float batteryvolts = 24;
float gyroangledt;
float angle;
float anglerads;
float balance_torque;
float softstart;
float cur_speed;
float cycle_time = 0.0064;
float Balance_point;
float a0, a1, a2, a3, a4, a5, a6; //Savitzky-Golay variables for accelerometer.
float TCCR0;
int i;
int j;
int tipstart;
void InitPorts(void)
{
PORTC = 0x00; //Port C pullups set to low (no output voltage) to begin with.
DDRC = 0xFF; //Port C pins all set as output via the port C direction register.
//PORTC |= (1<<PC1); //Make C1 +ve so disables OSMC during startup.
DDRA = 0x00; //All port A pins set as input.
PORTA = 0x00; //Port A input pullups set to low pullups.
DDRD = 0xFF; //Configure all port D pins as output as prerequisite
//for OCR1A (PinD5) and OCR1B (Pin D4) working properly.
PORTB = 0x00; //Port B pullups set to low (no output voltage) to begin with.
DDRB = 0xFF; //All port B pins set to output.
}
/*
IO:
I am using a ATMega32 16 MHz with an external crystal clock. New planned pin
arrangement to OSMC motor controller.
PC4 Onboard LED
PD5/OC1A ALI -> OSMC pin 6
PD4/OC1B BLI -> OSMC pin 8
PC1 Disable -> OSMC pin 4
PC2 BHI -> OSMC pin 7
PC3 AHI -> OSMC pin 5
PA6/ADC6 Vbatt/10 -> OSMC pin 3
PA1/ADC1 pitch rate gyro
PA0/ADC0 accelerometer
*/
void adc_init(void) {
/* Turn off analogue comparator as we don't use it */
ACSR = (1 << ACD);
/* Select PA0 */
ADMUX = 0;
ADMUX |=(1<<REFS0); //This tells it to use VCC (approx. 5 V) as the reference
//voltage NOT the default which is the internal 2.5V reference
/* Set ADC prescaler to 128, enable ADC, and start conversion. */
ADCSRA = 0 | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0)
| (1<<ADEN) //Enable ADC
| (1<<ADSC); //Start first conversion
/* Wait until bogus first conversion is finished */
while (ADCSRA & (1 << ADSC)) {
}
}
uint16_t adc_read(uint8_t channel) {
/* Select channel */
ADMUX = channel;
ADMUX |= (1<<REFS0); //Here it is again
/* Start conversion */
ADCSRA |= (1 << ADSC);
/* Wait until conversion finished */
while (ADCSRA & (1 << ADSC)) {
}
/* Return the result */
return ADCW;
}
/* 156 cycles per sec, 6.4 ms per cycle MEASURED ON OSCILLOSCOPE. */
/* Read all the ADC inputs and do some conversion. */
void sample_inputs(void) {
uint16_t adc0, adc1, adc2, adc3, adc4, adc5;
gyrosum = 0;
adc0 = adc_read(0); /* Accelerometer pin PA0 */
accelraw = (float) adc0;
for (j=0; j<7; j++) {
adc1 = adc_read(1); //Gyro pin PA1
gyrosum = (float) gyrosum + adc1; //Using a mean of 7 samples per loop for the gyro so
//it gets a complete update with each loop of the program.
}
adc2 = adc_read(2); /* Grey wire overallgain (via cutout switch), position PA2. */
adc3 = adc_read(3); /* Position lever pulled back position PA3. */
adc4 = adc_read(4); /* Throttle_pedal, position PA4. */
adc5 = adc_read(5); /* Position lever pushed forwards, position PA5. */
//adc6 = adc_read(6); /* Vbatt input from OSMC (not used at present), position PA6. */
//Sav Golay filter for accelerometer only.
a0 = a1;
a1 = a2;
a2 = a3;
a3 = a4;
a4 = a5;
a5 = a6;
a6 = (float) accelraw;
accsum = (float) ((-2*a0) + (3*a1) + (6*a2) + (7*a3) +
(6*a4) + (3*a5) + (-2*a6))/21; //Sav Golay calculation
gaincontrol = (float) gaincontrol*0.9 + 0.1*adc2/341; //Smooths any voltage spikes and gives range 0-3.
Throttle_pedal=(float) Throttle_pedal*0.9 + 0.1*adc4/341; //Smooths any voltage spikes and gives range 0-3
//Cuts the motor if the dead mans button is let go
//(gaincontrol variable also wired in through this button to adc2
if (adc2<100) {
Throttle_pedal = 0.001;
gaincontrol = 0.001;
}
overallgain = gaincontrol*softstart;
//What to do if the lever is pulled back or pushed forwards or not doing anything:
Balance_point = 514;
if (adc3 > 100)
Balance_point = 534;
if (adc5>100)
Balance_point = 494;
PORTB |= (1<<PB2); //Port B2 turned on/off once per loop so I can measure
//loop time with an oscilloscope
/*ACCELEROMETER signal processing*/
/*Subtract offsets*/
x_acc = (float) accsum - Balance_point; //accsum is SG value for accelerometer, not
//a true "sum" so no need to divide by 7
if (x_acc < -250)
x_acc = -250; //Cap acceleration values to a range of -250 to +250 (80
//degree tilt each way).
if (x_acc > 250)
x_acc = 250;
/* Accelerometer angle change is about 3.45 units per degree tilt in
range 0-30 degrees(sin theta). Convert tilt to degrees of tilt from
accelerometer sensor. Sin angle roughly = angle for small angles so
no need to do trigonometry. x_acc below is now in DEGREES */
x_accdeg= (float) x_acc/-3.45; //The minus sign corrects for a back
//to front accelerometer mounting!
/* GYRO signal processing*/
/* Subtract offsets: Sensor reading is 0-1024 so "balance point" i.e. my required
zero point will be that reading minus 512. */
/* Gyro angle change of 20mV per deg per sec from datasheet gives change
of 4.096 units (on the scale of 0 - 1023) per degree per sec angle change.
This limits the rate of change of gyro angle to just less than the
maximum rate it is actually capable of measuring (100 deg/sec). Note
all these fractions are rounded up to an integer later just before
it is sent to the PWM generator which in turn is connected to the
motor controller. */
gangleratedeg = (float)((gyrosum/7) - 508)/4.096; //gyrosum is a sum of a group
//of 7 samples so divide by 7 for gyro value
if (gangleratedeg < -92)
gangleratedeg = -92;
if (gangleratedeg > 92)
gangleratedeg = 92;
/* I turn port B2 on and off once per main program cycle so I can attach an
oscilloscope to it and work out the program cycle time.
I use the cycle time to work out gyro angle change per cycle where you
have to know the length of this time interval. */
PORTB &= (0<<PB2);
/* ti represents scaling for the "i" or integral factor (currently 2.2 here)
gyroangledt is anglechange since last CYCLE in degrees from gyro sensor,
where ti is scaling factor (should in theory be about 1 but 2.2 makes
board feel tighter)
ganglerate is now in units of degrees per second.
aa varies the time constant, that is, a smaller aa value makes
accelerometer time constant longer as it slowly corrects for
the gyro drift. */
aa=0.005;
gyroangledt = (float)ti*cycle_time*gangleratedeg;
gangleraterads = (float)gangleratedeg*0.017453;
/* New angle in DEGREES is old angle plus change in angle from gyro
since last cycle with little bit of new accel reading factored in. */
angle = (float)((1-aa) * (angle+gyroangledt)) + (aa * x_accdeg); //The main angle calculating function*/
//Convert angle from degrees to radians
anglerads=(float)angle*0.017453;
balance_torque=(float)(4.5*anglerads) + (0.5*gangleraterads);
cur_speed = (float)(cur_speed + (Throttle_pedal * balance_torque * cycle_time)) * 0.999;
/* The level value is from -1 to +1 and represents the duty cycle to be sent to
the motor. Converting to radians helps us stay within these limits. */
level = (balance_torque + cur_speed) * overallgain;
}
/* Configure timer and set up the output pins OC1A(Pin PD5 on my micro) and
OC1B (Pin PD4 on my micro) as phase-correct PWM channels.
Note: Some strongly feel that locked-antiphase is the way to go as get
regenerative braking and good control around mid-balance point. The downside
is that you can get a lot more noise and voltage spikes in system but
these can be smoothed out with filters.
Others are far more expert on this than I am so need to look into this
for yourself but this is my understanding.
My aim is to start with phase-correct as I just about understand it and
others have used it OK, then develop from there. */
void timer_init()
{
TCCR0 = 0 |
(1<<CS02) | (1<<CS01) | (1<<CS00); // External clock to Pin T0 Clock on rising edge/1024
// PWM mode is "PWM, Phase Correct, 10-bit"
TCCR1A = 0 |
(1<<COM1A1) | (1<<COM1A0) | // set on match up, clear on match down
(1<<COM1B1) | (1<<COM1B0) | // set on match up, clear on match down
(1<<WGM11) | (1<<WGM10); //OCR1_Max is 1023 so these are set like this
TCCR1B = 0 |
(1<<CS10); // Prescaler divide by 1 see P131 datasheet about prescaling
values to change here.
/* 16 MHz / 1 / 1024 / 2 gives 8 kHz, probably about right. */
}
void set_motor()
/* The leveli terms is the level term rescaled from -1023 to +1023 as an
integer ready to send to the PWM motor control ports that are in
turn connected to the OSMC. */
{
//if (level<-0.9) level= -0.9; //Checks we are within sensible limits
//if (level>0.9) level=0.9;
int16_t leveli = (int16_t)(level*1023); //NOTE: here we take the floating
//point value we have ended up with
//for "level", we multiply it by 1023
//and then make it into an integer
//before feeding the value into
//the PWM generator as "leveli"
if (leveli<-1020)
leveli=-1020; //Double-checks that we are within sensible PWM limits as do
//not want to suddenly be thrown off the board
if (leveli>1020)
leveli=1020;
/* Set up LED or buzzer on Port B1 to warn me to slow down if torque to be
delivered is more than 50% of max possible. The reason for this is that
you always need some reserve motor power in case you start tipping
forward at speed. If the motor is already running flat-out you would
be about to fall over at high speed! Some use an auto-tip back routine
to automatically limit top speed. For now I will do it this way as easier. */
if (level<-0.7 || level>0.7) {
PORTB |= (1<<PB1);
}
else {
PORTB &= (0<<PB1);
}
softstart = (float) softstart+0.001;
if (softstart>1.0)
softstart=1.0;
//PORTC |= (0<<PC1); // AHI=1 PinC3, BHI=1 PinC2 set both to ON for OSMC to
//work and both to OFF to shut motor down.
/*NOTE: I am not sure why, but to stop the motor cutting out on direction
changes I had in the end to hard wire AHI and BHI to +12 V. */
/* Un-disabled OSMC by setting PinC1 output to zero, a 1 would disable the OSMC. */
PORTC |= 0x0c; //Make C1 pulled down so un-disables the OSMC i.e. enables it.
PORTC &= ~0x02; //Disable is off
if (leveli<0) {
OCR1A = -leveli; // ALI is PWM. Going backwards as leveli variable is a
//negative signed value, keep the minus sign in here!
OCR1B = 0; // BLI = 0
}
else {
OCR1A = 0; // ALI = 0 going forwards as leveli variable is a positive signed value
OCR1B = leveli; // BLI is PWM
}
}
void loop()
{
InitPorts();
adc_init();
timer_init();
/* Initial tilt-start code
Turn on micro while board tipped to one side, rider about to step
onto it, if tilt angle crosses zero (mid) point balance algorithm
becomes operational otherwise locked in this loop forever until
it is tipped to level position as rider gets onto the board. */
tipstart=0;
accelraw = 0;
while (tipstart<1){
// You need this to allow the SG filter to wind up to the proper
//stable value when you first turn machine on, before looking
//at the value of accsum (below).
for (i=0; i<20; i++) {
sample_inputs();
}
if (accsum<504 || accsum>524) {
// if (x_accdeg>0) {
tipstart=0;
}
else {
tipstart=1;
softstart=0.4;
}
}
angle=0;
cur_speed=0;
/* End of tilt start code. If go beyond this point then machine
has become level and is active. */
sei();
while (1) {
sample_inputs();
set_motor();
}
}
BTW, I'm using the Arduino compiler (you can find it at Download the Arduino Software).
How come? I dont even have a reference to setup in here!
It's because you don't have a reference to setup that it's complaining.
Arduino code generates a main that (for the discussion here) basically looks like this:
int main(int argc, char **argv) {
setup();
while(1) {
loop();
}
}
So, you should rename your loop function to setup and take this part:
while (1) {
sample_inputs();
set_motor();
}
drop the while loop, and put those two function calls into a new function called loop.
The Arduino language requires you to define two functions, setup() and loop().
If you don't have anything to setup(), you should define an empty function to satisfy the language requirements:
void setup()
{
// do nothing
}
The included code doesn't even have a main function. It looks to me like a setup issue in your toolchain, or else you've been investigating the wrong cpp file.
Find Test2.cpp and line 406 (in a function called main)