ADC code on atmega2560 - arduino

I have writing code to measure ADC values on channel numbers stored in ch1 array(first 2 channels correspond to measurement of capacitor voltage Vc; then next 6 channels correspond to measurement of Grid current Ia,Ib,Ic; and last 6 channels correspond to measurement of Grid voltage Va,Vb,Vc ).
I am writing code for Atmega2560 of arduino-Mega.
Over-all structure of code:
1.In function setup(), i have initialized ADC, Serial-monitor and interrupt
2.In function loop(), i am starting ADC conversion for each channels stored in array named ch1 to get capacitor voltage, grid current and grid voltage in sequence.
I am unable to get desired result,by which i mean that nothing is printed on Serial terminal after 5-6 lines.
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
//#include <util/atomic.h>
#define ChannelMax 4
uint8_t ch1[]={0,0,1,1,2,2,3,3,4,4,5,5,6,6};//channels for //Vc,Vc,Ia,Ia,Ib,IB,Ic,Ic,Va,Va,Vb,Vb,Vc,Vc(capacitor voltage,grid current,grid voltage)
volatile unsigned short ci=0;//index for ch1 array
unsigned int ADarray[15];//array to store ADC output data
volatile unsigned short readFlag=0;//it is set from ADC ISR when either Vc
or all I or all V is measured twice
void setupADC();
uint16_t adc_read(uint8_t ch);
void adc_start(uint8_t ch);
//setup function detail for arduino
void setup()
{
cli();
Serial.begin(9600); // connect to the serial port
setupADC();
sei();
}
//looping for infinity
void loop()
{
while(1)
{
//initialize things
ci=0;// start with first channel present in ch1
Serial.print("Vc measuring started with");
Serial.println(ch1[ci]);
adc_start(ch1[ci]);//start adc to measure Vc i.e ch1[0] and ch1[1]
//do something whil Vc is measured
while(1){ //wait till end of ADC measurement of Vc
if(readFlag==0){
Serial.println(readFlag);
Serial.println("Vc beingmeasured");
}
else break;
};
readFlag=0; Serial.println("Vc finally measured");
//use Vc i.e. ADarray[1] for calculation
Serial.print("I measuring started with ");Serial.println(ch1[ci]);
adc_start(ch1[ci]);//start adc to measure grid current Ia,Ib,Ic i.e ch1[2]
//to ch1[7]
//do something while grid current is being measured
while(1){ //wait for end of measurement of grid current
if(readFlag==0){
Serial.println(readFlag);
Serial.println("I beingmeasured");
}
else break;
};
readFlag=0; Serial.println("I finally measured");
//Use i i.e. ADarray[3],5,7 for calculation
Serial.print("V measuring started with ");Serial.println(ch1[ci]);
adc_start(ch1[ci]);//start measurement of grid voltage Va,Vb and Vc
//do something while grid volt is being measured
while(1){//wait till end of measurement of grid voltage
if(readFlag==0){
Serial.println(readFlag);
Serial.println("V beingmeasured");
}
else break;
};
readFlag=0;Serial.println("V finally measured");
//Use V i.e. ADarray[9],11,13 for calculation
}//end of while loop
}//end of loop function
//setup registers for ADC in atmega 2560
void setupADC(){
ADCSRA = 0;
ADCSRB = 0;
ADMUX = ch1[ci]; // Channel 0 only
ADMUX |= (1<<REFS0);//ADMUX|=0b01000000;//use it if u want to use AVCC //as Aref internally ,but dont forget to use external cap at AREF
ADCSRA = _BV(ADEN) ; // Enable ADC,
//ADCSRA |= _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0) ;//prescale at 128.
ADCSRA|=_BV(ADIE);//to interupt enable for ADC
//ADCSRA |= _BV(ADSC); // Start initial ADC cycle
}
//Interrupt service routine for ADC measurment
ISR(ADC_vect){
ADarray[ci]=(unsigned int)ADC;
Serial.print(ch1[ci]);Serial.println("adc read");
ci++;ci++;Serial.print(ci);
//if(ci>=ChannelMax) ci=0;
if((ci==2) ||( ci==8) ||(ci==14)){
Serial.println("finished");
readFlag=1;
return ;
}
//Serial.println("adc started");
adc_start(ch1[ci]);
//Serial.print(ch1[ci]);Serial.println("adc started");
}
//function to start ADC for particular channel
void adc_start(uint8_t ch){
// Serial.print(ch);Serial.println("adc started subroutine");
// select the corresponding channel 0~7
// ANDing with ’7? will always keep the value
// of ‘ch’ between 0 and 7
ch &= 0b00001111; // AND operation with 7
ADMUX = (ADMUX & 0xF0)|ch; // clears the bottom 3 bits before ORing
// start single convertion
// write ’1? to ADSC
ADCSRA |= (1<<ADSC);
}

Doing things, i understood that One shouldn't use Serial.print inside ISR or any function called from ISR(perhaps because Serial.print uses interrupt)... Then question is that how to debug inside ISR? Can i use Atomic_Block to do something?

Related

ESP32 ADXL345 doesn't get the negative values

Hi I can run this code without any problem on Arduino UNO. My output is like : 0.05 0.10 1.01
But when I run this code on ESP32, my output is : 255.89 255.81 0.99 and I don't see any minus value. What is the problem?
ESP32's X,Y,Z raw values like : 6500 0 , 65000 , 1 . ESP32 can't get the negative values. I have to do something with wire.h and I need to change uint8_t or uint16_t something but I really don't understand how should I solve this problem.
#include <Wire.h> // Wire library - used for I2C communication
int ADXL345 = 0x53; // The ADXL345 sensor I2C address
float X_out, Y_out, Z_out; // Outputs
float X, Y, Z ;
void setup() {
Serial.begin(115200); // Initiate serial communication for printing the results on the Serial monitor
Wire.begin(); // Initiate the Wire library
// Set ADXL345 in measuring mode
Wire.beginTransmission(ADXL345); // Start communicating with the device
Wire.write(0x31);
Wire.write(0x0B);
Wire.endTransmission();
Wire.beginTransmission(ADXL345);
Wire.write(0x20); // Z-axis offset register
Wire.write(-7);
Wire.endTransmission();
Wire.beginTransmission(ADXL345);
Wire.write(0x2D); // Access/ talk to POWER_CTL Register - 0x2D
// Enable measurement
Wire.write(0x08); // (8dec -> 0000 1000 binary) Bit D3 High for measuring enable
Wire.endTransmission();
Wire.beginTransmission(ADXL345);
Wire.write(0x2C);
// Enable measurement
Wire.write(0x09); //For low power 000x x pin set to 1 /1001 determine Hz
Wire.endTransmission();
delay(10);
}
void loop() {
unsigned long Time = millis();
// === Read acceleromter data === //
Wire.beginTransmission(ADXL345);
Wire.write(0x32); // Start with register 0x32 (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(ADXL345, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
X_out = ( Wire.read() | Wire.read() << 8); // X-axis value
X = X_out /256; //For a range of +-2g, we need to divide the raw values by 256, according to the datasheet
Y_out = ( Wire.read() | Wire.read() << 8); // Y-axis value
Y = Y_out /256;
Z_out = ( Wire.read() | Wire.read() << 8); // Z-axis value
Z = Z_out /256;
delay(20);
Serial.print(X);
Serial.print(" ");
Serial.print(Y);
Serial.print(" ");
Serial.print(Z);
Serial.println(" ");
}
On the Arduino Uno there are 16-bit integers. The ADXL345 seems to receive this data format. Therefore everything plays out nicely. You are using 32-bit integers on the ESP32. Therefore you have to choose the right data type explicitly. Otherwise the negative numbers appear in the positive region.
The execution order of the Wire.read()-calls is not defined in your code. Therefore the compiler may join the bytes 0xAA and 0xBB to 0xAABB or 0xBBAA. You should add a sequence point to make sure that the code does what's intended.
I have not tested this code. That should be the correct order, in case I got the datasheet description right:
int16_t readInt16()
{
int lsb = Wire.read();
int msb = Wire.read();
return int16_t(lsb | msb << 8);
}
X_out = readInt16() / 256.f;
Y_out = readInt16() / 256.f;
Z_out = readInt16() / 256.f;

Incremental 65535 steps to increase LED brightness to maximum over time (in hours), stay on at maximum (for hours) and ramp down (opposite of starup)

Trying to understand timing / dimming and interrupts using an Arduino Uno (or any other AVR) is being made very difficult by a serious lack of example code. Having found a sketch that starts from zero and ramps up the brightness, I have tried to adapt the code to prevent the continuous loop which occurs when the 16-bit register overflows.
The attached sketch starts up from zero light output and increases over a period of time - currently using the delay() function.
Attempting to adapt the code to prevent the loop from starting the entire process again and to allow the led to remain at the "top" brightness output for x (variable) number of hours has proved to be most elusive. As one of the contributors have noted this area of coding is one of the most difficult to master.
Any advice or guidance which will put me in the right direction will be most appreciated.
...
//fade over 65535 steps
// 16 bit PWM on any pin
// Example uses built in LED on pin 13 (PORTB bit 5)
// https://forum.arduino.cc/index.php?topic=348170.0
void setup() {
pinMode(13, OUTPUT);
cli(); // Disable all interrupts
TCCR1A = 0; // Clear all flags in control register A
TCCR1B = 0; // Clear all flags in control register B
TCNT1 = 0; // Zero timer 1 count
OCR1A = 32768; // Preload compare match register (50% duty cycle)
// No prescaler
//TCCR1B |= _BV(CS12);
//TCCR1B |= _BV(CS11);
TCCR1B |= _BV(CS10);
TIMSK1 |= _BV(OCIE1A); // Enable timer compare interrupt
TIMSK1 |= _BV(TOIE1); // Enable timer overflow interrupt
sei(); // enable all interrupts
}
void loop() {
for (unsigned int x = 1; x < 65535; x++) {
//cli();
OCR1A = x;
//sei();
delay(20);
}
}
ISR(TIMER1_OVF_vect) { // Timer1 overflow interrupt service routine
PORTB |= _BV(PORTB5); // Turn LED (pin 13) on
}
ISR(TIMER1_COMPA_vect) { // Timer1 compare interrupt service routine
PORTB &= ~_BV(PORTB5); // Turn LED off
}
...

Arduino timer4 custom PWM issue

I made a nice code which generates fast PWM with 50% duty cycle and I can change the frequency with a potentiometer. It outputs straight and inverted channels with some dead time. I am using Arduino Micro aka ATmega32U4. The code is actually "Atmel" code. Code is working fine until I power Arduino Micro off and then on again.
I have programmed the code and registers so that the frequency is changeable from 10kHz to 100kHz. But after power on/off the frequency changes from 5kHz to 50kHz. After this has happened I have to program the board again using Arduino IDE, to make it work correctly. Again after power on/off it has changed. I am quite sure that one of the registers is overwritten by the "Arduino hardware abstraction layer" or however we should name it. I have not yet read out all the registers so I do not know which one is overwritten. I guess it's the prescaler.
How do I prevent this from happening? Should I write the register contents somewhere else? Or should I write it few times to be sure?
Why or how this is happening anyway?
Here's the code:
#define OSC1 5
#define OSC2 13
uint8_t read_reg1;
uint8_t read_reg2;
int pot, freq;
void setup() {
pinMode(OSC1, OUTPUT);
pinMode(OSC2, OUTPUT);
Serial.begin(9600);
cli(); // disable global interrupts
TCCR4A=0; // clear register
TCCR4B=0x06; // configure prescaler to 64 (CK = CLK / 64 = 1.5 MHz)
TCCR4C=0;
TCCR4D=0; // select Fast PWM operation (0 << WGM41)|(0 << WGM40)
PLLFRQ=(PLLFRQ&0xCF)|0x30; // select clock source and frequency
OCR4C=150; // select PWM frequency
OCR4A=150/2; // set duty cycle
DT4 = 0x55; // set dead times. DT = (1 / 48Mhz) * 0...15
// enable interrupt on timer4 overflow
TIMSK4|=(1 << TOIE4);
// This register write has to be after others. Otherwise the PWM generation will not work. I do not know why.
TCCR4A=0x42; // COM4A1..0 = 01, OC4A and !OC4A connected. PWM4A = 1 (activate channel A PWM output)
sei(); // enable global interrupts
}
void loop() {
//cli();
pot = analogRead(A0);
freq = map(pot, 0, 1023, 14, 166);
//sei();
/*
Serial.print("Pot value: ");
Serial.print(pot);
Serial.print("\tFreq value: ");
Serial.println(1500000/freq);
*/
}
ISR(TIMER4_OVF_vect){
OCR4C = freq;
OCR4A = freq / 2;
}
I am not sure exactly why you got different behavior right after programming, but the bootloader that the Arduino Micro uses (Caterina) does not perform a full reset after it runs, so changes that the bootloader made to the AVR's registers are often visible to the user's sketch.
I was able to fix the problem by removing the line that modifies PLLFRQ. Here is a simplified version of your code that always produces 3.31 kHz PWM:
void setup()
{
pinMode(5, OUTPUT);
pinMode(13, OUTPUT);
TCCR4A = 0;
TCCR4B = 0x06; // configure prescaler to 64 (CK = CLK / 64 = 1.5 MHz)
TCCR4C = 0;
TCCR4D = 0; // select Fast PWM operation (0 << WGM41)|(0 << WGM40)
OCR4C = 150; // select PWM frequency
OCR4A = 150 / 2; // set duty cycle
DT4 = 0x55; // set dead times. DT = (1 / 48Mhz) * 0...15
// This register write has to be after others.
// Otherwise the PWM generation will not work. I do not know why.
// COM4A1..0 = 01, OC4A and !OC4A connected.
// PWM4A = 1 (activate channel A PWM output)
TCCR4A = 0x42;
}
void loop()
{
}
It's not a great idea to mess with the PLL postscaler since it will probably affect every other Arduino library that uses timers, including the USB stack.

“Read Analog Voltage” sample rate

I want to make sure my code looks like working, since I don't have a lot of time with a signal generator tomorrow and I want to know how to set the sample rate.
I want to sample a 2kHz signal with a samplerate of 6kHz with a Arduino MEGA 2560.
It, doesn't have to be in real time, so i'm thinking of filling a buffer and then sending those over the serial connection.
Can anyone say if this code defenitly wouldn't work for this?
And how could i set the samplerate to 6kHz?
void setup() {
Serial.begin(9600);
}
void loop() {
for(int x = 0; x < 1000; x++){
// read the input on analog pin 0:
int sensorValue[x] = analogRead(A0);
}
for( x = 0; x < 1000; x++){
// Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
float voltage[x] = sensorValue[x] * (5.0 / 1023.0);
// print out the value you read:
Serial.println(voltage[x]);
}
}
Thank you.
Well, as I've mentioned in another thread, you can use auto triggering mode of ADC (for UNO and ATMega328p based Arduinos):
void setup() {
Serial.begin(256000);
// ADC setup is done by arduino framework, but it's possible to change it slightly (for ATMega328) :
ADCSRB = _BV(ADTS2) | _BV(ADTS1) | _BV(ADTS0); // ADTS2..0 = 111, Timer 1 input capture event trigger source
ADCSRA |= _BV(ADATE); // enable auto trigger mode
ADCSRA |= _BV(ADIF); // reset conversion end flag (= interrupt flag)
// timer 1 setting:
TCCR1A = 0; // clear all
ICR1 = F_CPU/6000U; // 1 should be substracted here but result is about 2665.7 and it will be truncated to 2665
TCCR1B = _BV(WGM12) | _BV(WGM13) | _BV(CS10); // CTC mode with ICR1 as TOP value, enabled with no prescaling
TIMSK1 = _BV(ICF1); // not working without this... Flag must be cleaned up after the trigger ADC, otherwise it's stucked
analogRead(A0); // dummy read to set correct channel and to start auto trigger mode
pinMode(13, OUTPUT);
}
void loop() {
if (ADCSRA & _BV(ADIF)) {
ADCSRA |= _BV(ADIF); // reset flag by writing logic 1
Serial.println(ADC);
}
}
ISR(TIMER1_CAPT_vect) { // to clear flag
PINB = _BV(PB5); // and toggle d13 so frequency can be measured (it'd be half of real rate)
// it might be enabled on PWM pin too by setting force output compare and some compare register to half of value ICR1
}
This sketch uses baud rate 250000 but it's still too slow. The space character can be used as an separator, this'll save one character (as new line are usually two characters: \r\n). One value can be 1 to 4 characters long so for values:
0-9 - 3B you need baud rate 3*10*6000 = 180000
10-99 - 4B and you need baud rate 240000
and for the rest of cases you're too slow.
So the only way is sending those integers binary and without separator it'd be even better. The 2B per value results into minimal baud rate around 120000 baud/s.

Programming Arduino with ADXL345 to raise interrupt on inactivity

I need to use a sparkfun breakout board ADXL345 to detect when my motor system has stopped vibrating. I am also using a Sparkfun RedBoard (Arduino uno).
Things I am doing to configure for this behavior:
enable INACTIVITY event
route INACTIVITY events to INT 1 (pin 2 on the RedBoard)
raise INACTIVITY interrupt without delay
set low threshold for INACTIVITY (rule out too high of a setting)
INACTIVITY considers all axes
clear interrupt data register
Having done all these things I do not receive interrupts after going from shaking the devise to setting it down.
//Add the SPI library so we can communicate with the ADXL345 sensor
#include <SPI.h>
//Assign the Chip Select signal to pin 10.
int CS=10;
//This is a list of some of the registers available on the ADXL345.
//To learn more about these and the rest of the registers on the ADXL345, read the datasheet!
char POWER_CTL = 0x2D; //Power Control Register
char DATA_FORMAT = 0x31;
char DATAX0 = 0x32; //X-Axis Data 0
char DATAX1 = 0x33; //X-Axis Data 1
char DATAY0 = 0x34; //Y-Axis Data 0
char DATAY1 = 0x35; //Y-Axis Data 1
char DATAZ0 = 0x36; //Z-Axis Data 0
char DATAZ1 = 0x37; //Z-Axis Data 1
char THRESH_ACT = 0x24; // Activity threshold
char THRESH_INACT = 0x38; // Inactivity threshold to 3g
char TIME_INACT = 0x26; // time before raising interrupt
char INT_ENABLE = 0x2E; // Enabling the interrupt lines
char INT_MAP = 0x2F;
char ACT_INACT_CTL = 0x27; // mask byte for controlling
char INT_SOURCE = 0x30;
//This buffer will hold values read from the ADXL345 registers.
char values[10];
//These variables will be used to hold the x,y and z axis accelerometer values.
int x,y,z;
void setup(){
//Initiate an SPI communication instance.
SPI.begin();
//Configure the SPI connection for the ADXL345.
SPI.setDataMode(SPI_MODE3);
//Create a serial connection to display the data on the terminal.
Serial.begin(9600);
//Set up the Chip Select pin to be an output from the Arduino.
pinMode(CS, OUTPUT);
//Before communication starts, the Chip Select pin needs to be set high.
digitalWrite(CS, HIGH);
// Create an interrupt that will trigger when inactivity is detected
attachInterrupt(0, interruptHandler, RISING);
//Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register.
writeRegister(DATA_FORMAT, 0x01);
//Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register.
writeRegister(POWER_CTL, 0x08); //Measurement mode
// Send the inactivity && activity to PIN 1
// 0xF7 && 0xEF
writeRegister(INT_MAP,0xF7 && 0xEF);
// Set the inactivity threshold to 3g (0x38)
// writeRegister(THRESH_INACT,0x38);
writeRegister(THRESH_INACT,1);
// Raise the inact interrupt immediately after going below threshold
writeRegister(TIME_INACT,0);
// Map INACT event (only) to PIN 1
writeRegister(ACT_INACT_CTL, 0x0F);
// Enab le inactivity to generate interrupts
writeRegister(INT_ENABLE, 0x08);
readRegister(INT_SOURCE, 1, values); // Clear the INT_SOURCE register
Serial.println("Waiting for interrupt!");
}
void interruptHandler(){
// readRegister(INT_SOURCE, 1, values); // Clear the INT_SOURCE register
Serial.println("something raise an interrupt!");
}
void loop(){
//Reading 6 bytes of data starting at register DATAX0 will retrieve the x,y and z acceleration values from the ADXL345.
//The results of the read operation will get stored to the values[] buffer.
readRegister(DATAX0, 6, values);
//The ADXL345 gives 10-bit acceleration values, but they are stored as bytes (8-bits). To get the full value, two bytes must be combined for each axis.
//The X value is stored in values[0] and values[1].
x = ((int)values[1]<<8)|(int)values[0];
//The Y value is stored in values[2] and values[3].
y = ((int)values[3]<<8)|(int)values[2];
//The Z value is stored in values[4] and values[5].
z = ((int)values[5]<<8)|(int)values[4];
//Print the results to the terminal.
Serial.print(x, DEC);
Serial.print(',');
Serial.print(y, DEC);
Serial.print(',');
Serial.println(z, DEC);
delay(500);
}
//This function will write a value to a register on the ADXL345.
//Parameters:
// char registerAddress - The register to write a value to
// char value - The value to be written to the specified register.
void writeRegister(char registerAddress, char value){
//Set Chip Select pin low to signal the beginning of an SPI packet.
digitalWrite(CS, LOW);
//Transfer the register address over SPI.
SPI.transfer(registerAddress);
//Transfer the desired register value over SPI.
SPI.transfer(value);
//Set the Chip Select pin high to signal the end of an SPI packet.
digitalWrite(CS, HIGH);
}
//This function will read a certain number of registers starting from a specified address and store their values in a buffer.
//Parameters:
// char registerAddress - The register addresse to start the read sequence from.
// int numBytes - The number of registers that should be read.
// char * values - A pointer to a buffer where the results of the operation should be stored.
void readRegister(char registerAddress, int numBytes, char * values){
//Since we're performing a read operation, the most significant bit of the register address should be set.
char address = 0x80 | registerAddress;
//If we're doing a multi-byte read, bit 6 needs to be set as well.
if(numBytes > 1)address = address | 0x40;
//Set the Chip select pin low to start an SPI packet.
digitalWrite(CS, LOW);
//Transfer the starting register address that needs to be read.
SPI.transfer(address);
//Continue to read registers until we've read the number specified, storing the results to the input buffer.
for(int i=0; i<numBytes; i++){
values[i] = SPI.transfer(0x00);
}
//Set the Chips Select pin high to end the SPI packet.
digitalWrite(CS, HIGH);
}
Here is a tutorial, arduino library and example sketch. If you haven't run through something like this, might be worth a try starting with someone else's code that is working (maybe you've already done that).
In the example sketch from above, they are enabling interrupts in the code, they just don't seem to tie them into the Arduino's external interrupt system. Once you verify that the example code is working, you can call attachInterrupt() and abandon the polling approach (as you are doing in your example).

Resources