Use i2C interface to read from ADT7420 temperature sensor - microcontroller

I am trying to set up my STM32 microcontroller with HAL libraries to read from a ADT7420 temperature sensor using i2c. However I am unable to read the correct value from the sensor as I run my code.
Here is how I have done it so far using HAL libraries:
uint8_t I2C_ADDR = 0x48;
uint8_t TEMP_CONFIG = 0x03;
uint8_t data[2];
HAL_I2C_Master_Transmit(&hi2c1, I2C_ADDR, &TEMP_CONFIG , 1, 10000);
HAL_I2C_Master_Receive(&hi2c1, I2C_ADDR, data, 2, 10000);
uint16_t temp_raw = (uint16_t)((data[0]<<8) | data[1]);
int temp_value = calc_celcius(temp_raw); //TODO: convert to Celsius
Using this code, the temperature stays at zero indicating that something is not working correctly. Am I missing some config settings for the i2c setup in order to read the temperature value? Thanks.

You need to send register address as a first byte of I2C write followed either by data to be written into that register, or I2C repeated start and reading the value. See page 18 and 19 in datasheet, you have linked, for details.
It seems that HAL_I2C_Mem_Write() and HAL_I2C_Mem_Read() functions should take care of this address writing for you. So, this part of your code would look like
HAL_I2C_Mem_Write(&hi2c1, I2C_ADDR, 0x03, I2C_MEMADD_SIZE_8BIT, &TEMP_CONFIG , 1, 10000);
HAL_I2C_Mem_Read(&hi2c1, I2C_ADDR, 0x00, I2C_MEMADD_SIZE_8BIT, data, 2, 10000);
Disclaimer: I have no experience with just HAL library, so my answer is based on quick read through documentation and source code provided only.
Note also that directly after powering up the IC, you need to wait for first conversion to complete in order to get non-zero value. According to the datasheet, first conversion should be done in 6 ms only (with low precision), each other conversion in normal mode takes 240 ms.

Here am using temperature sensor (TMP175)
static void temp_Init(void)
{
HAL_I2C_Mem_Write(&hi2c1, (uint8_t) W_ADDR, (uint8_t) CONFIG_REG ,
(uint8_t) 1,Config, 1, 1000);
HAL_I2C_Mem_Write(&hi2c1, (uint8_t) W_ADDR, (uint8_t) thigh , (uint8_t) 1,
THigh, 1, 1000);
HAL_I2C_Mem_Write(&hi2c1, (uint8_t) W_ADDR, (uint8_t) tlow , (uint8_t) 1, TLow, 1, 1000);
}
The above command are based on the HAL_Drive ...initially give the I2C address..
then create one task in the main program..then in the created task just call the function then u will get the temperature value.. do use the following code.
void StartDefaultTask(void const * argument)
{
HAL_I2C_Mem_Read(&hi2c1, (uint8_t) R_ADDR, (uint8_t)CONFIG_REG , (uint8_t) 1,
&ConfigReg[0], 1, 1000);
printf(" configreg==%x\t \r",ConfigReg[0]);
HAL_I2C_Mem_Read(&hi2c1, (uint8_t) R_ADDR, (uint8_t) tlow, (uint8_t) 1,TLowReg,
2, 1000);
TLow1=(TLowReg[0]<<4);
TLow1|=(TLowReg[1]>>4);
TL=TLow1*0.0625;
printf(" Lower Limit Temperature=%f deg celsius \r ", TL);
HAL_I2C_Mem_Read(&hi2c1, (uint8_t) R_ADDR, (uint8_t) thigh, (uint8_t) 1,THighReg, 2, 1000);
THigh1=(THighReg[0]<<4);
THigh1|=(THighReg[1]>>4);TH=THigh1*0.0625;
printf(" upper Limit Temperature=%f deg celsius \r", TH);
HAL_I2C_Mem_Read(&hi2c1, (uint8_t) R_ADDR, (uint8_t) TEMPERATURE, (uint8_t) 1, TempReg, 2, 1000);
Shift= TempReg[0]<<4;
Shift|= TempReg[1]>>4;
Temp=Shift*0.0625;
printf("Temperature=%f deg Celsius \r",Temp);
}

Related

Convert Byte array to string and than match it with string in arduino

I cannot able to match my serial monitor data to my string.
The way I am sending data to serial is data.getBytes() where data is a string and I have to keep it that way.
So now I have to convert that data to string and than match it to its equivalent string but I couldn't figure that out.
aduino code inside void loop() is:
if(Serial.available()){ // only send data back if data has been sent
byte msg = Serial.read();
byte plain[a.length()]; // String a = "2"
byte inString = a.getBytes(plain, a.length(), 0); // read the incoming data
if(inString == msg){
sevenSeg(0, 1, 1, 0, 1, 1, 1); // 2
delay(500);
}else{
sevenSeg(0, 0, 0, 1, 1, 0, 0); // 1
delay(500);
}
else{
sevenSeg(1, 1, 1, 1, 1, 1, 0); //0
}
All I end up with is displaying 1. Even though I am sending data to the serial monitor. Any help or ideas are greatly appreciated. Thanks.

PLL Voltage scalling STM32

I am working with the STM32F767 Nucleo board and currently, I trying to set the PLL as the system clock. While I have been able, thanks to an example generated by the CubeMX, I really don't understand why it must be done so. The setup is:
HSI = 16MHz
PLLM = 8
VCO_Input_Frequency = 16/8 = 2MHz
PLLN = 144
Frequency = 144*2 = 288MHz
PLLP = 6
PLL_Output_Frequency = 288/6 = 48MHz
PPRE1 = 2
APB1_Frequency = 24MHz
APB1_Frequency_Timer = 2*24MHz = 48MHz
The following line of code is what is buging me:
//Sets the voltage scaling mode to 3, VOS = 0x1 = b1
PWR->CR1 |= PWR_CR1_VOS_0;
a = PWR->CR1; //Small delay
When this line is commented the period is 19.7ms and when is active the period is 20ms, as expected. It is very strange, this is how the generated code from CubeMX does. It makes the voltage scaling to 1 (low performance). I don't understand how seting the voltage scaling equals to 1 makes the PLL works correctly.
Down below is the code that configure the PLL:
void sys_clock_init(void){
int a;
//Sets the wait states to 1
FLASH->ACR |= 0x01;
a = FLASH->ACR; //Small delay
//Enables the power interface (for the power controller)
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
a = RCC->APB1ENR; //Small delay
//Clears the bits for the voltage scaling
PWR->CR1 &= ~(PWR_CR1_VOS);
//Sets the voltage scaling mode to 3, VOS = 0x1 = b1
PWR->CR1 |= PWR_CR1_VOS_0;
a = PWR->CR1; //Small delay
//Makes HSI the source of the PLL
RCC->PLLCFGR &= ~(0x400000);
//Clears the bits for the different factors
RCC->PLLCFGR &= ~(RCC_PLLCFGR_PLLM);
//Sets the PLLM = 0x08 = b100
RCC->PLLCFGR |= (RCC_PLLCFGR_PLLM_3);
//Clears the PLLN bits
RCC->PLLCFGR &= ~(RCC_PLLCFGR_PLLN);
//Sets PLLN = 0x90 = b10010000
RCC->PLLCFGR |=(RCC_PLLCFGR_PLLN_7 | RCC_PLLCFGR_PLLN_4);
//Clears the PLLP bits
RCC->PLLCFGR &= ~(RCC_PLLCFGR_PLLP);
//Sets the PLLP = 0x02 = b10
RCC->PLLCFGR |=(RCC_PLLCFGR_PLLP_1);.
//Clears the PPRE1 bits
RCC->CFGR &= ~(RCC_CFGR_PPRE1_2 | RCC_CFGR_PPRE1_1 | RCC_CFGR_PPRE1_0);
//Set bit PPRE1 = 0x02 = b100
RCC->CFGR |= (RCC_CFGR_PPRE1_2);// | RCC_CFGR_PPRE1_0);
//Turns the PLL ON
RCC->CR |= RCC_CR_PLLON;
//Waits for the PLL to be ready
while(!((RCC->CR & RCC_CR_PLLRDY) == RCC_CR_PLLRDY));
//Clears the switch bits
RCC->CFGR &= ~(RCC_CFGR_SW);
//Set the PLL as the System Clock
RCC->CFGR |= (RCC_CFGR_SW_1);}
I have also tested commenting the lines that sets VOS bits on the CubeMX code and the period is 19.75ms like mine.
This is my code to get that board to 16Mhz using the PLL and the external clock.
static int clock_init ( void )
{
unsigned int ra;
//switch to external clock.
ra=GET32(RCC_CR);
ra|=1<<16;
PUT32(RCC_CR,ra);
while(1) if(GET32(RCC_CR)&(1<<17)) break;
if(1)
{
ra=GET32(RCC_CFGR);
ra&=~3;
ra|=1;
PUT32(RCC_CFGR,ra);
while(1) if(((GET32(RCC_CFGR)>>2)&3)==1) break;
}
//HSE ready
//PLLM aim for 2mhz so 8/4=2
//PLLN input is 2, want >=100 and <=432 so between 50 and 216
//PLLP 16Mhz*8 = 128, 16MHz*6 = 96, not enough
//so PLLP is 8 VCO 128 so PLLN is 64
//don't really care about PLLQ but have to have something so 8
PUT32(RCC_PLLCFGR,0x20000000|(8<<24)|(1<<22)|(3<<16)|(64<<6)|(4<<0));
ra=GET32(RCC_CR);
ra|=1<<24;
PUT32(RCC_CR,ra);
while(1) if(GET32(RCC_CR)&(1<<25)) break;
ra=GET32(RCC_CFGR);
ra&=~3;
ra|=2;
PUT32(RCC_CFGR,ra);
while(1) if(((GET32(RCC_CFGR)>>2)&3)==2) break;
return(0);
}
PUT32/GET32 are abstraction functions to do str/ldr. I will try either 48 HSE or 48Mhz HSI, and post what I find.
static int clock_init ( void )
{
unsigned int ra;
//PLLM aim for 2mhx so 16/8 = 2;
//PLLN input is 2, want >=100 and <=432 so between 50 and 216
//PLLN = 144, VCO 288
//PLLP = 6, output 288/6 = 48MHz
//don't really care about PLLQ but have to have something so 6
PUT32(RCC_PLLCFGR,0x20000000|(6<<24)|(0<<22)|(2<<16)|(144<<6)|(8<<0));
ra=GET32(RCC_CR);
ra|=1<<24;
PUT32(RCC_CR,ra);
while(1) if(GET32(RCC_CR)&(1<<25)) break;
ra=GET32(RCC_CFGR);
ra&=~3;
ra|=2;
PUT32(RCC_CFGR,ra);
while(1) if(((GET32(RCC_CFGR)>>2)&3)==2) break;
return(0);
}
The uart works, but that is not saying much.
With the default of scale 1 I do see that a little fast. but if I use the 8MHz HSE then it looks better. I use systick to count 120 seconds. set systick to roll over every 1million counts then wait for 120 rollovers, compare to a stopwatch/timer.
Next using 16000000 in systick and counting 900 rollovers that should be 5 minutes and it is to within a second since comparing visually to a timer is only that accurate. Using HSE, scaling 1 (default)
Using HSI scaling 1 it is off by a few seconds to get 19.7ns though would be a lot of seconds and I don't see that many.
Now using HSI scaling 3:
static int clock_init ( void )
{
unsigned int ra;
ra=GET32(RCC_APB1ENR);
ra|=1<<28; //PWR enable
PUT32(RCC_APB1ENR,ra);
PUT32(FLASH_ACR,0x00000001);
PUT32(PWR_CR1,0x4000);
GET32(PWR_CR1);
//PLLM aim for 2mhx so 16/8 = 2;
//PLLN input is 2, want >=100 and <=432 so between 50 and 216
//PLLN = 144, VCO 288
//PLLP = 6, output 288/6 = 48MHz
//don't really care about PLLQ but have to have something so 6
PUT32(RCC_PLLCFGR,0x20000000|(6<<24)|(0<<22)|(2<<16)|(144<<6)|(8<<0));
ra=GET32(RCC_CR);
ra|=1<<24;
PUT32(RCC_CR,ra);
while(1) if(GET32(RCC_CR)&(1<<25)) break;
ra=GET32(RCC_CFGR);
ra&=~3;
ra|=2;
PUT32(RCC_CFGR,ra);
while(1) if(((GET32(RCC_CFGR)>>2)&3)==2) break;
return(0);
}
Does appear to be more accurate. 5 minutes measured as 5 minutes to the second. So it appears perhaps that the documentation isn't correct with respect to the accuracy of HSI (as there is this exception using default scaling).
48Mhz -> 20.83ns
20.62 - 21.04 with the documented error.
There is a reason for using external clocks. If you are interested in more accuracy since you have the NUCLEO board use the external clock HSE not the internal HSI.
Hmmm, actually 1% for the 16Mhz which is 3% when multiplied by 3 to get 48MHz. I think using the divisor in the PLL makes it worse, but I would have to ponder that some more.
20.21 to 21.46 is the range you should see at the calibration temperature, then vary from that based on die temp.

Appropriate sample for PIC ADC after converting from analog voltage.

if I'm reading an analog signal from my pressure sensor at 500mSec. my instructor told me that you should make the ADC Timr0 interrupt double what you are reading from analog Oscilloscope (500mSec.).i.e. 2fc. My code is down below.
Should I configure my timer0 to be 20Hz or less or more?
enter code here
char temp[5];
unsigned int adc_value;
char uart_rd;
int i;
unsigned int d[10]={0};
int average = 0;
int counter =0;
void interrupt(){
if (INTCON.T0IF) {
INTCON.T0IF = 0 ;// clear T0IF (Timer interrupt flag).
}
TMR0 = 178;
}
void main() {
temp[0]='1';
temp[1]='2';
temp[2]='3';
temp[3]='4';
temp[4]=' ';
OSCCON= 0x77; //8MHz
ANSEL = 0b00000100; //ANS2
CMCON0 = 0X07; //
TRISA = 0b00001100;
UART1_Init(9600);
TMR0 = 178 ;
//CMCON0 = 0X04; // turn off compartor.
OPTION_REG = 0x87; //
INTCON =0xA0;
while(1){
average= ADC_Read(2);
temp[0] = average/1000+48;
temp[1] = (average/100)%10+48;
temp[2] = (average/10)%10+48;
temp[3] = average%10+48;
for (i=0;i<5; i++)
{
UART1_Write(temp[i]);
}
}
}
When preform sampling on a signal you are not capturing all of is information but only parts of it with a given sampling period.
The Nyquist–Shannon sampling theorem claims that if you can actual sample at above of some given frequency you can get all the information of a finite bandwidth of the signal. This frequency is twice the maximum frequency of that bandwidth.
If you don't do comply with that frequency you will suffer from an effect called aliasing.
You can learn more about here: https://en.wikipedia.org/wiki/Aliasing

Strange magnetometer readout

I am attempting to readout the data from my MPU-9150 magnetometer and getting some odd numbers. I have accessed the magnetometer within the IMU and am getting data which changes with the orientation of the IMU but is not within the range specified by the product specification guide. I'm think its probably something to do with either the variable type i am using to store the data or the method i am using to manipulate the twos compliment data to make it readable, so here is the code anyway...
void MPU9150::getMag(double* mag_X, double* mag_Y, double* mag_Z){
uint8_t asax, asay, asaz;
I2Cdev::writeByte(MPU9150_ADDRESS, INT_PIN_CFG, 0x02);//Set i2c bypass enable pin to true to access magnetometer.
I2Cdev::writeByte(MPU9150_MAG_ADDRESS, MPU9150_MAG_CNTRL, 0x0f);//Fuse Rom access mode.
I2Cdev::readBytes(MPU9150_MAG_ADDRESS, MPU9150_MAG_ASAX, 3, buffer);//Get sensitivity adjustment values.
asax = buffer[0];
asay = buffer[1];
asaz = buffer[2];
// Serial.print("asax = "); Serial.print(asax); Serial.print("\n");
// Serial.print("asay = "); Serial.print(asay); Serial.print("\n");
// Serial.print("asaz = "); Serial.print(asaz); Serial.print("\n\n");
I2Cdev::writeByte(MPU9150_MAG_ADDRESS, MPU9150_MAG_CNTRL, 0X01);//Enable the magnetometer.
delay(10);
I2Cdev::readBytes(MPU9150_MAG_ADDRESS, MPU9150_MAG_XOUT_L, 6, buffer);//Read magnetometer readings.
mx = ((((int16_t)buffer[0]) << 8) | buffer[1]) * 0.3;
my = ((((int16_t)buffer[2]) << 8) | buffer[3]) * 0.3;
mz = ((((int16_t)buffer[4]) << 8) | buffer[5]) * 0.3;
*mag_X = mx * ((((asax - 128)*0.5)/(128)) + 1);//Adjust readings with sensitivity adjustment values.
*mag_Y = my * ((((asay - 128)*0.5)/(128)) + 1);
*mag_Z = mz * ((((asaz - 128)*0.5)/(128)) + 1);
}
The decimal range for each axis should be between 4096 and -4096, and there are 6 data registers each containing 8 bit high and low values for each axis. The data that i am getting seems to be in the range >10000 to <-10000 with the highest value i have seen at 9830. If anyone has any ideas they would be appreciated, thanks :)
EDIT: Buffer is a private integer array, used to hold the bytes from the axis registers.
private:
uint8_t buffer[14];
Also reversing the order of the bytes (as i have realised should be done as the first byte in each register is low and the second high) has resulted in the range of the output reduced to a range of about -170 to 170...
mx = (((int16_t)buffer[1]) << 8) | buffer[0];
my = (((int16_t)buffer[3]) << 8) | buffer[2];
mz = (((int16_t)buffer[5]) << 8) | buffer[4];

Arduino shield, SD card Data logge, why are my SD cards dying?

I'm having a real anoying problem that I carnt seem to find a solution on.
I have created a simple arduino code for sweeping a curve on 2 analog pin, controlled by a DAC unit. The sweeps are being done in real time, ones a second, and is stored on a SD card in files split into hours.
My problem is that my SPI interface on the SD cards stop responding during continues used.
I use the following arduino mega shield
http://pcb.daince.net/doku.php?id=data_logger
and a "Kingston MicroSDHC 4GB Secure Digital Card, 66X" for storage
I ran the the following code for 3 days and when I came back the SPI was none responding (I have done 1 hours tests before with no problem, and I have checked that the system changes files correctly)
#include "SPI.h"
#include <SD.h>
#include "Wire.h"
//Static definitions
#define ADG509_A0_pin 39
#define ADG509_A1_pin 38
#define SS_DAC_pin 53
#define voltage_analog_pin 11
#define current_analog_pin 12
#define led 13
#define DS1307_ADDRESS 0x68
#define chip_select 53
//Adjustable definitions
#define ADC_to_voltage 1/213 // the value for converting the arduino ADC to voltage
#define number_of_samples 100 //number of measurements, should be able to be divided by 4 without giving decimals
#define time_per_data_set 500 //the delay between datasets in milisecondss, note the the "delay = system_computation_time + time_per_data_set"
#define current_gain_ratio 2 //the realtion between the voltage level on the pin and current on from the solar pannel
#define voltage_gain_ratio 10 //the realtion between the voltage level on the pin and the voltage level from the solar panel
#define number_samples_per_measurement 5 //the number of ADC readings used to make an average of each measuring point, note this number greatly effects sweeping time
#define delay_per_measurement 1
void set_DAC(float value); // gets an input between 0.0 and 5.0 and sets the voltage to the value
void write_measurement_to_SD(float current, float voltage, int number); //writes the numbers to the SD card if initialized
void write_to_file(); //check is a new SD card file should be created and makes it ef nessesary. Also handles the data writing to the file
void get_time(); //Get the current time of the system, and store them in global variables
int bcdToDec(byte val); //Converts bytes to decimals numbers
char intToChar(int val, int number); //Converts integers to chars, only exept up to 2 dicimals
word output_word_for_DAC = 0; //used to type cast input of set_DAC to word before usage
byte data = 0; //temp variable need for DAC, it is the byte send over the communication
float volt; //stores a temporary voltage measurement
float current; //stores a temporary current measurement
float current_level; //stores the short curciut current level, it is used to find the optimal placement for measuring points
char filename[13] = "F0000000.txt"; //stores the current active filename for writing on the SD card
File dataFile; //the current version of the datafile on the SD card
int years, months, monthsDay, weekDay, hours, minute, seconds; //global varibles used to store the last time reading
float current_array[number_of_samples]; //used to store all current measurements points during sweep
float voltage_array[number_of_samples]; //used to store all voltage measurements points during sweep
int typecast_int; // used for typecasting float to int
int last_measurement_time;
int is_SDcard_there;
void setup() {
pinMode(ADG509_A0_pin, OUTPUT);
pinMode(ADG509_A1_pin, OUTPUT);
pinMode(SS_DAC_pin, OUTPUT);
pinMode(led, OUTPUT);
digitalWrite(led, LOW);
digitalWrite(ADG509_A1_pin, LOW);
digitalWrite(ADG509_A0_pin, LOW);
SPI.begin(); // start up the SPI bus
SPI.setBitOrder(MSBFIRST);
Wire.begin();
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {;}
Serial.print("Initializing SD card...");
if (!SD.begin(chip_select)) {
Serial.println("Card failed, or not present");
is_SDcard_there = 0;
return;+
}
Serial.println("card initialized.");
is_SDcard_there = 1;
}
void loop() {
//finding the shortcurcuit current of the solar panel
get_time();
last_measurement_time = seconds;
set_DAC(5); //open fet
delayMicroseconds(1000);
current_level = analogRead(current_analog_pin);
for(float counter2 = 0; counter2 < 10; counter2++){
current_level += analogRead(current_analog_pin);
}
current_level = (current_level/10)* 1.1 * ADC_to_voltage;
//fully opening the fet to insure that the solar pannel is stable at max voltage before beginning the sweep
set_DAC(0); //ground fet
delayMicroseconds(10000);
if(is_SDcard_there == 1){
digitalWrite(led, HIGH);
}
//sweeping the first 80% current on the curve with 25% of the measuring points
//note no calculations should be put in here, since it slow the sweep down
for(float counter = 0; counter < number_of_samples * 0.25; counter++){
set_DAC((counter*current_level * 0.80) / (number_of_samples * 0.25));
delayMicroseconds(200);
current = analogRead(current_analog_pin);
volt = analogRead(voltage_analog_pin);
for(float counter2 = 0; counter2 < number_samples_per_measurement - 1; counter2++){
current += analogRead(current_analog_pin);
volt += analogRead(voltage_analog_pin);
}
current = current / number_samples_per_measurement;
volt = volt / number_samples_per_measurement;
typecast_int = counter;
current_array[typecast_int] = current;
voltage_array[typecast_int] = volt;
}
//sweeping the last 20% current on the curve with 75% of the measuring points
//note no calculations should be put in here, since it slow the sweep down
for(float counter = 0; counter < number_of_samples * 0.75; counter++){
set_DAC(current_level * 0.80 + (counter*current_level * 0.20) / (number_of_samples * 0.75));
delayMicroseconds(200);
current = analogRead(current_analog_pin);
volt = analogRead(voltage_analog_pin);
for(float counter2 = 0; counter2 < number_samples_per_measurement - 1; counter2++){
current += analogRead(current_analog_pin);
volt += analogRead(voltage_analog_pin);
}
current = current / number_samples_per_measurement;
volt = volt / number_samples_per_measurement;
typecast_int = counter + number_of_samples * 0.25;
current_array[typecast_int] = current;
voltage_array[typecast_int] = volt;
}
set_DAC(5); //sets DAC high to prepare for next short curcuit measurement
digitalWrite(led, LOW);
//converting data to desired values and writing them to the file
String to_write = "";
int check;
char buffer[20];
to_write = "0,0,-2";
to_write += '\r';
to_write += '\n';
to_write += "0,";
to_write += intToChar(minute,0);
to_write += intToChar(minute,1);
to_write += intToChar(seconds,0);
to_write += intToChar(seconds,1);
to_write += ",-1";
to_write += '\r';
to_write += '\n';
for(int counter = 0; counter < number_of_samples-1; counter++){
to_write += dtostrf(current_array[counter] = current_array[counter] * ADC_to_voltage * current_gain_ratio, 0, 2, buffer);
to_write += ",";
to_write += dtostrf(voltage_array[counter] = voltage_array[counter] * ADC_to_voltage * voltage_gain_ratio, 0, 2, buffer);
to_write += ",";
to_write += counter;
to_write += '\r';
to_write += '\n';
}
to_write += dtostrf(current_array[99] = current_array[99] * ADC_to_voltage * current_gain_ratio, 0, 2, buffer);
to_write += ",";
to_write += dtostrf(voltage_array[99] = voltage_array[99] * ADC_to_voltage * voltage_gain_ratio, 0, 2, buffer);
to_write += ",99";
Serial.println(to_write); //should only be used for debugging since it slow down the system to much
compute_filename(); //initialize a new filename if needed
check = 0;
while(check == 0 && is_SDcard_there == 1){
dataFile = SD.open(filename,FILE_WRITE);
check = dataFile.println(to_write);
if (dataFile){
dataFile.close();
}
}
//wait for next seconds
while(last_measurement_time + delay_per_measurement > seconds){
get_time();
if(seconds < last_measurement_time){seconds = seconds + 60;}
}
}
// gets an input between 0.0 and 5.0 and sets the voltage to the value
void set_DAC(float value){
if(value <= 5){
value = value*819;
int conveter = value;
output_word_for_DAC = conveter;
digitalWrite(SS_DAC_pin, LOW);
data = highByte(output_word_for_DAC);
data = 0b00001111 & data;
data = 0b00110000 | data;
SPI.transfer(data);
data = lowByte(output_word_for_DAC);
SPI.transfer(data);
digitalWrite(SS_DAC_pin, HIGH);
}
}
//writes the numbers to the SD card if initialized
void write_measurement_to_SD(float current, float voltage, int number){
dataFile = SD.open(filename,FILE_WRITE);
dataFile.print(current);
dataFile.print(',');
dataFile.print(voltage);
dataFile.print(',');
dataFile.print(number);
dataFile.println(';');
if (dataFile){
dataFile.close();
}
}
//Converts bytes to decimals numbers
int bcdToDec(byte val){
return ( (val/16*10) + (val%16) );
}
//Converts integers to chars, only exept up to 2 dicimals
char intToChar(int val, int number){ // note it only take ints with 2 digits
String tempString = "";
char returnValue[3];
if(val < 10){
tempString += 0;
}
tempString += val;
tempString.toCharArray(returnValue,3);
return returnValue[number];
}
//Get the current time of the system, and store them in global variables
void get_time(){
Wire.beginTransmission(DS1307_ADDRESS);
byte zero = 0x00;
Wire.write(zero);
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDRESS, 7);
seconds = bcdToDec(Wire.read());
minute = bcdToDec(Wire.read());
hours = bcdToDec(Wire.read() & 0b111111); //24 hours time
weekDay = bcdToDec(Wire.read()); //0-6 -> sunday - Saturday
monthsDay = bcdToDec(Wire.read());
months = bcdToDec(Wire.read());
years = bcdToDec(Wire.read());
}
//check if the system need a new filename
void compute_filename(){
get_time();
filename[1] = intToChar(years,1);
filename[2] = intToChar(months,0);
filename[3] = intToChar(months,1);
filename[4] = intToChar(monthsDay,0);
filename[5] = intToChar(monthsDay,1);
filename[6] = intToChar(hours,0);
filename[7] = intToChar(hours,1);
if (dataFile){
dataFile.close();
}
}
I know that this is a big code dump and I'm sorry for that, but I carnt realy cut it down when I have no idea what is killing my SD cards.
Any help on the matter is appreciated
and any ideas on what I can test to isolate or locate the error is also welcome
there can be 2 possible errors:
1 resource leak
2 RAM overflow
for 1 you can put some code that write on serial RAM usage evry loop(), if that number grow, some library (as you aren't allocating memory with alloc() )is broken. For 2, don't build up to_write, but use directly Serial.println(), alyas try to use array/string wicth use LESS than 100 byte or you might have problem (String dinamically allocate space, and that allocation can fail.. silently. then bad things appen)
I've done a lot of work with SD cards during the past 10 years and I've noticed that the SD card is very easily bricked into a non-responsive mode if it receives garbage in the CMD line. It seems that at least some kind of garbage is interpreted as a command to set the card password, thus locking the card. It has happened to me at least 10 times in as many years when I've been working with SD library code or new hardware designs, so it looks to me that it must be some bit patterns which is easily generated somehow.
I've been able to rescue the (micro)SD cards by putting them into my old Symbian phone which immediately recognizes that the card is locked and offers to reformat the card and reset the password. You might try the same with your SD card, to see if your problem is the same I've experienced. I remember vaguely that some cheap Canon camera was also able to rescue some card in a similar way.

Resources