Unable to trigger ADC interrupt - atmega

I'm trying to simply get an analog reading from A0 on my arduino Pro mini, but I cannot get the values stored in the adc register.
What is supposed to happen is the analog signal will be put in registers ADCL & ADCH and then the ADC interrupt will trigger.
I have a voltage divider set up on A0 that should just read 2.5V but my interrupt is not triggering and i cannot get values from ADCH or ADCL. I could get a reading in arduino so I know the hardware works. when i run this program i get a continuous print of 0 to my terminal which tells me the values of low and high never change as they should in the interrupt.
Here is my code:
# define F_CPU 16000000L
#include "time.h"
#include <avr/io.h>
#include <math.h>
#include <util/delay.h> // including the avr delay lib
#include <util/delay.h> // Including the avr delay lib
#include "usart.h"
#include "usart.cpp"
#include <stdio.h>
#include <avr/interrupt.h>
uint8_t low,high;
int main(void)
{
TCCR0B = (1<<CS02) | (1<<CS00);
USART_Init(MYUBRR);
DDRC = 0x0 ;
ADCSRA = (1<<ADEN) | (0<<ADIF); //activate adc & clear ADIF
ADMUX = (0<<ADLAR); //keep right adjusted
ADMUX = (1<<REFS0) | (0<<MUX2) | (0<<MUX1) | (0<<MUX0);//set ref as vcc, input as 0
ADCSRA = (1<<ADIE) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);//enable adc interrupt & divide clock by 124
DIDR0 = (0<<ADC0D);
sei();
ADCSRA = (1<<ADSC);//begin conversion
while (1)
{
while(ADSC==1)//wait for conversion to complete
{
sei();
}
USART_Send_int(low);
USART_Send_int(high);
_delay_ms(100);
ADCSRA = (1<<ADSC);
}
}
ISR(ADC_vect) //interrupt to trigger when conversion is complete
{
low = ADCL;
high = ADCH;
//ADCSRA = (1<<ADSC);
}

You should declare low and high as volatile since you are accessing them from an interrupt and the main loop:
volatile uint8_t low;
volatile uint8_t high;
Otherwise, the compiler is free to optimize your loop by moving the readings of those variables to be before the loop.
Alternatively, you could try putting a memory barrier in your loop, but that is the less-traveled road: most embedded developers tend to just use volatile.
I didn't test your code so there might still be other errors.
To debug this further, you should try putting low = 44; and high += 1; in your interrupt just to make sure the interrupt is running.

Related

PORTB and INT external interrupts stucks the code

I am working on a project with PIC16F877 (using MPLABX). I use RB0 pin external interrupt and RB4 pin portb interrupt to detect zero cross detection. I did everything correct, in proteus simulation everything was okey. Then I set up the circuit on breadboard, the LCD wasnt displaying the numbers (just the white dots). I thought the problem is the RB0 and PORTB interrupt. I wrote a simple code just includeshe PORTB interrupt and LCD and simulated. Everything is okey until the interrupt occures, when interrupt comes the code stops. I am new to PIC, this is the code I wrote:
/*
* File: lcd_deneme_16f877a.c
* Author: BATUHAN
*
* Created on 28 Aral?k 2022 Çar?amba, 13:52
*/
#include <xc.h>
#include <stdio.h>
#include <stdint.h>
#pragma config FOSC = XT // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = ON // Data EEPROM Memory Code Protection bit (Data EEPROM code-protected)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = ON // Flash Program Memory Code Protection bit (All program memory code-protected)
#define _XTAL_FREQ 4000000
void __interrupt() interrpt()
{
if(INTF)
{
uint8_t dummy = PORTB; // Read PORTB to end mismatch condition
INTF=0;
RD0=RD0^1;
}
if(RBIF==1 && RB4==1)
{
uint8_t dummy = PORTB; // Read PORTB to end mismatch condition
RBIF=0;
RD0=RD0^1;
}
}
void main(void)
{
TRISD=0X00;
PORTD=0X00;
TRISB=0b00010001;
PORTB=0X00;
INTCON=0b11011000; // GIE PEIE TMR0IE INTE RBIE TMR0IF INTF RBIF
OPTION_REGbits.nRBPU = 1;
INTEDG=1;
int V=0;
while(1)
{
V++;
__delay_ms(200);
}
return;
}
I tried the PORTB and RB0 interrupts separately and the problem still occurs.
What could be the problem. Thanks in advance
This is because your program stucks in interrupt routine due to the lack of proper handling of interrupts. You don't seem to handle the INT interrupt at all. For RB interrupt-on-change (IOC), you have to handle it sort of a little different and end the mismatch condition before clearing the flag. According to the PIC16F877A Datasheet this how the IOC works and must be handled:
Four of the PORTB pins, RB7:RB4, have an interrupt-on-change feature. Only pins configured as inputs can cause this interrupt to occur (i.e., any RB7:RB4 pin configured as an output is excluded from the interrupt-on-change comparison). The input pins (of RB7:RB4)are compared with the old value latched on the last read of PORTB. The “mismatch” outputs of RB7:RB4
are OR’ed together to generate the RB port change interrupt with flag bit RBIF (INTCON<0>). This interrupt can wake the device from Sleep. The user, in the Interrupt Service Routine, can clear the interrupt in the following manner:
a) Any read or write of PORTB. This will end the mismatch condition.
b) Clear flag bit RBIF.
A mismatch condition will continue to set flag bit RBIF. Reading PORTB will end the mismatch condition and allow flag bit RBIF to be cleared.
So your interrupt service code should look like the following:
void __interrupt() interrpt()
{
if(RBIF && RB4)
{
volatile uint8_t dummy = PORTB; // Read PORTB to end mismatch condition
RBIF=0;
RD1=RD1^1;
}
else if(INTIF) {
INTIF = 0;
RD0 = !RD0; // Toggle D0 for INT interrupt
}
}
A friendly reminder
The proteus simulation is ok for some cases. However the simulation runs in ideal conditions. That's why you may not get the same expected behaviour in the real world conditions compared to proteus' ideal simulation conditions.
void __interrupt() interrpt()
{
if(RBIF)
{
PORTB; // Read PORTB to end the mismatch condition
RBIF=0;
if(RB4)
RD1=RD1^1;
}
else if(INTF) {
INTF = 0;
RD0 = !RD0; // Toggle D0 for INT interrupt
}
}

SPI not working on atmega328p

I'm trying to program an atmega328p, but the SPI bus isn't working. It's not sending any data over the bus.
My code is as follows:
#include <asf.h>
#include <stdio.h>
#include <main.h>
int main (void)
{
board_init();
SPI_MasterInit();
DDRD = (1<<DDD5);
while(1)
{
PORTB &= ~(1<<DDB2);
SPI_MasterTransmit(0xAB);
PORTB |= (1<<DDB2);
PORTD ^= (1<<DDD5);
}
}
void SPI_MasterInit(void)
{
/* Set MOSI and SCK output, all others input */
DDRB = (1<<5) || (1<<3) || (1<<2);
/* Enable SPI, Master, set clock rate fck/16 */
SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
}
void SPI_MasterTransmit(char cData)
{
/* Start transmission */
SPDR = cData;
/* Wait for transmission complete */
while(!(SPSR & (1<<SPIF)));
}
I'm trying to send some random data over the bus (0xAB) to check if the bus works properly so I can add further code. In the while loop I also set a pin.
On my scope image I see no data being send on the SPI pin MOSI and the CLK pin is also not sending information. PB5 (the pin I'm inverting everytime I try to send data) is working and has a period of about 20 microseconds.
I'm programming the Atmega328p through an Atmel ICE. The programming interface is also through SPI, I read somewhere that this might be an issue. I'm not completely sure.
Does anyone know what might be the problem?
My first guess was not setting SS pin (PB2) as output. It may cause switching to the slave mode almost randomly. But it seems to be set as an output (it's not stated in comments).
But after closer look to this expression, it's obvious it's not set at all:
DDRB = (1<<5) || (1<<3) || (1<<2);
There is a huge difference between logical or || and bitwise or |.

Measuring the period of a square wave using microcontroller

I am new to microcontroller. The following code measures the period of a square wave. I have marked some lines which I haven't understood. The code is as follows:
#include <avr/io.h>
#include <avr/interrupt.h>
ISR(TIMER1_CAPT_vect)
{
int counter_value = ICR1; //16 bit value
PORTB = (counter_value >> 7); // What has been done here?
TCNT1 = 0; // why this line?
}
int main(void)
{
DDRB = 0xFF;
TCCR1A = 0x00;
TCCR1B = 0b11000010;
TIMSK = 0b00100000;
sei();
while(1);
cli();
}
What has actually been done in those lines?
ISR(TIMER1_CAPT_vect)
{
int counter_value = ICR1; //16 bit value
PORTB = (counter_value >> 7); // What has been done here?
PORTB is a set of 8 output lines. Presumably, they are connected by a bus to some device you haven't mentioned. Maybe even a set of LEDS to display a binary number.
The result from the counter is 16 bits. To get the most significant bits, shift the result to the right to discard the less significant bits. (This operation loses precision, but you only have 8 bits of output, not 16.) As to why the shift is only 7 instead of 8, or why the unsigned value of the counter is saved as a signed int first, I don't know. I suspect it is a mistake. I would have done PORTB = (ICR1 >> 8); instead.
TCNT1 = 0; // why this line?
Since we have recorded the time of the capture and sent it out PORTB, we now want to reset the timer for the next capture.
}

ADC code on atmega2560

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?

PWM Issue with arduino due (cortex-m3)

I am using PWM on an arduino due board which uses SAM3X8E(cortex-m3) microcontroller. When I use PWM enable and disable on this board, the waveform goes analog on disabling the channel, instead of staying at 0 or 1. Please see the attached waveform. I tried the code by directly writing to registers also, but it was the same. I also tried a 4.7k pulldown resistor at pwm output, but got the same results. Please tell me how to fix it in software.
If a hardware solution is also possible with some external components, that is also ok. Scope images are attached. The blue scope output is for line 34 (PC02) and yellow for line 35 (PC03).
// include all arduino libraries here.. these are only accepted from ino files.
#include <Arduino.h>
#include <SD.h>
#include <SPI.h>
#include <Ethernet.h>
void setup() {
// put your setup code here, to run once:
pmc_enable_periph_clk (PWM_INTERFACE_ID) ; // turn on clocking to PWM unit
PWMC_ConfigureChannel (PWM, 0, 1, 0, PWM_CMR_CPOL) ; // PWM channel 0, clock = MCLK/2 = 42MHz
PWMC_SetPeriod (PWM, 0, 700) ; // period = 700 pwm clocks (60kHz)
PWMC_SetDutyCycle (PWM, 0, 80*700/100) ; // duty set to 80%
PWMC_EnableChannel (PWM, 0) ; // enable
// Configure pin 34 (PC2) to be driven by peripheral B (PWM channel 0 L)
// enable pin PC02 and PC03, they are complimentary
PIOC->PIO_PDR = 0xC ; // disable PIO control
PIOC->PIO_IDR = 0xC ; // disable PIO interrupts
PIOC->PIO_ABSR |= 0xC ; // switch to B peripheral
}
void loop() {
// put your main code here, to run repeatedly:
// From these settings, I got these numbers from the scope - 13.3us on time and 3.32us off time
//
PWMC_EnableChannel (PWM, 0) ; // enable
delayMicroseconds(100);
PWMC_DisableChannel (PWM, 0) ; // enable
delayMicroseconds(100);
}
Ans here's the scope images:
It takes some time to disable the channel.
Try to add a waiting loop after disabling:
void loop() {
PWMC_EnableChannel (PWM, 0) ; // enable
delayMicroseconds(100);
PWMC_DisableChannel (PWM, 0) ; // disable
while ((PWM->PWMC_SR & 1) != 0); //add this
delayMicroseconds(100);
}

Resources