Translate

Monday, 11 January 2016

Measuring angular displacement using rotary potentiometer

                         Alright, in the previous post we discussed about interfacing a character LCD. Now lets pair it with the built in analog to digital converter (ADC) of an atmega controller. We will a rotary potentiometer to measure the angular displacement.... Its similar to how a servo measures its angle. The process is simple as shown below




Before we proceed please ensure that you know how to built a power supply and a basic LCD interface.... Also a little bit knowledge on ADC operation is required.

Things you need

ADC

The atmega 8,16 and 32 use a 10bit successive approximation ADC. The values range from 0 - 1023 (10bit -> 1024). The atmega has three reference sources but it can use only one at a time Here the reference voltage is set to 5V to keep things simple. I have not discussed the topics on frequency and prescalar,the data sheet contains more details, you can look it up

CIRCUIT DIAGRAM

Alright heres the circuit diagram. As you can see it is similar to that of the LCD post but with the addition of the potentiometer



CODE



         Here is a simple code... this code has a drawback which I will address next


////////////////////////////////////////////////////Code for ADC readout//////////////////////////////////////////////////////

/*
* Angular_displacement_using_atmega.cpp
*
* Created: 8/1/2016 8:35:28 PM
* Author: Hemanth
*/

#ifndef F_CPU
#define F_CPU 1000000UL // CPU frequency
#endif

#define LCD_DATA PORTC
#define control PORTB
#define rs PB2
#define rw PB1
#define en PB0

#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>

void LCD_cmd(unsigned char cmd);
void init_LCD(void);
void LCD_write(unsigned char data);
void stringwrite(const char *q);
void moveto(int line,int pos);
void clearLCD();
void InitialiseADC(char Aref,int prescalar);
int ADC_readout(uint8_t ch);

void InitialiseADC(char Aref,int prescalar)
{
if (Aref=='I')
{ADMUX=(1<<REFS0)|(1<<REFS1);}
else if(Aref=='V')
{ADMUX=(1<<REFS0)|(0<<REFS1);}
else if(Aref=='R')
{ADMUX=(0<<REFS0)|(0<<REFS1);}
if (prescalar==2)
{ADCSRA=(1<<ADEN)|(0<<ADPS2)|(0<<ADPS1)|(1<<ADPS0);}
else if (prescalar==4)
{ADCSRA=(1<<ADEN)|(0<<ADPS2)|(1<<ADPS1)|(0<<ADPS0);}
else if (prescalar==8)
{ADCSRA=(1<<ADEN)|(0<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);}
else if (prescalar==16)
{ADCSRA=(1<<ADEN)|(1<<ADPS2)|(0<<ADPS1)|(0<<ADPS0);}
else if (prescalar==32)
{ADCSRA=(1<<ADEN)|(1<<ADPS2)|(0<<ADPS1)|(1<<ADPS0);}
else if (prescalar==64)
{ADCSRA=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(0<<ADPS0);}
else if (prescalar==128)
{ADCSRA=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);}
}

int ADC_readout(int channel)
{
channel=channel&0b00000111;//selecting single ended channel refer data sheet for details
ADMUX|=channel;
ADCSRA|=(1<<ADSC);
while(!(ADCSRA & (1<<ADIF)));
ADCSRA|=(1<<ADIF);
return(ADC);
}

void clearLCD()

{ LCD_cmd(0x01);}

void init_LCD(void)
{
LCD_cmd(0x38);// initialization of 16X2 LCD in 8bit mode
_delay_ms(10);
LCD_cmd(0x01); // clear LCD
_delay_ms(10);
LCD_cmd(0x0C); // cursor ON
_delay_ms(10);
LCD_cmd(0x80);//8 go to first line and0 is for 0th position
}

void LCD_cmd(unsigned char cmd)
{LCD_DATA=cmd;
control |=(1<<en); // RS and RW as LOW and EN as HIGH
control &=~(1<<rs);
control &=~(1<<rw);
_delay_ms(10);
control &=~(1<<rs);
control &=~(1<<rw);
control &=~(1<<en); // RS, RW , LOW and EN as LOW
_delay_ms(10);
}

void LCD_write(unsigned char data)
{
LCD_DATA= data;
control |= (1<<rs)|(1<<en); // RW as LOW and RS, EN as HIGH
control &=~(1<<rw);
_delay_ms(10);
control |= (1<<rs); // EN and RW as LOW and RS HIGH
control &=~(1<<rw);
control &=~(1<<en);
_delay_ms(10); // delay to get things executed
return ;
}

void stringwrite(const char *S)
{
while (*S) {
LCD_write(*S++);
}
}

void moveto(int line,int pos)
{
if(line==1)
LCD_cmd(0x80+pos);
if(line==2)
LCD_cmd(0xC0+pos);
if(line==3)
LCD_cmd(0x90+4+pos);
if(line==4)
LCD_cmd(0xD0+4+pos);
}

char buffer[5];

int main(void)
{
int DATA =0;
MCUCSR|=(1<<JTD);//Disable JTAG
MCUCSR|=(1<<JTD);//Have to send it twice
DDRA=0x00;
DDRB=0xFF;
DDRC=0xFF;
DDRD=0xFF;
PORTA=0x00;
PORTB=0X00;
PORTC=0X00;
PORTD=0x00;
PORTD|=(1<<PD7);
init_LCD();
InitialiseADC('V',4);
while(1)
{
moveto(1,0);
stringwrite("ADC Value :");
DATA=ADC_readout(4);
itoa(DATA,buffer,10);
moveto(1,12);
stringwrite(buffer);
}
}
now when you run this code the voltage entering to the adc pin woul be converted to an accurate digital number. heres a little math

referance voltage = 5000mv
ADC resolution = 2^10 bit (1024)

step value = (5000/1024) = 4.88

which means for every 4.88mv increase the ADC would increment by one

example

if the input to the pin is 2.35V the ADC would read out

(2350/4.88) ~  481

the output will always be in the integer format

The drawback

     so did u guys notice the display when moving from a higher to lower value say from 998 to 25....... your display would read as 258..... the 8 being the 8 from the 998 which was not erased from the LCD memory.... the next code has this covered.....you can write it out with a blank space like so

moveto (1,12);
stringwrite ("    ")//four blank spaces
moveto(1,12)// back to the 1000th place
stringwrite (buffer);

Alright so we got the ADC value how do we convert it to degrees?..... well simple math.... and this is the same procedure for distance or temperature sensors which provides an analog output

here we have a potentiometer with the capability of 0 degrees to 270 degrees

and as you learnt from the previous code (try it on an MCU before proceeding) 0 to 270 corresponds to 0 to 1023... so we just have to derive a simple relation...as such

270 * X = 1023

so X = (1023/270) i.e 3.78

so the angle would be the (ADC reading/3.78)... simple.....any doubts comment below

Heres the final board


here's the code

please note for greater accuracy floating variables must be used.. this code is for referance and simple projects only... no error correction methods have been implemented....In theory the rotary potentiometer is assumed as linear... however that is not always practical.

And here's another note if u plan on using more than 1 potentiometer the ADC must be initailsed again and the next channel selected... The first conversion must be discarded

Example

initialiseADC('V',32);// set ADC referance voltage to vcc i.e 5V with a prescalar of 32
DATA1=ADC_readout(2);//discard first conversion
DATA1=ADC_readout(2);
initialiseADC('I',8); // set ADC referance voltage to internal i.e 2.56V with a prescalar of 8
DATA2=ADC_readout(5);//discard first conversion
DATA2=ADC_readout(5);

////////////////////////////////////////////////////Code for angular readout//////////////////////////////////////////////////////
  
/*
* Angular_displacement_using_atmega.cpp
*
* Created: 8/1/2016 8:35:28 PM
* Author: Hemanth
*/

#ifndef F_CPU
#define F_CPU 1000000UL // CPU frequency
#endif

#define LCD_DATA PORTC
#define control PORTB
#define rs PB2
#define rw PB1
#define en PB0

#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>

void LCD_cmd(unsigned char cmd);
void init_LCD(void);
void LCD_write(unsigned char data);
void stringwrite(const char *q);
void moveto(int line,int pos);
void clearLCD();
void InitialiseADC(char Aref,int prescalar);
int ADC_readout(uint8_t ch);

void InitialiseADC(char Aref,int prescalar)
{
    if (Aref=='I')
    {ADMUX=(1<<REFS0)|(1<<REFS1);}
    else if(Aref=='V')
    {ADMUX=(1<<REFS0)|(0<<REFS1);}
    else if(Aref=='R')
    {ADMUX=(0<<REFS0)|(0<<REFS1);}
    if (prescalar==2)
    {ADCSRA=(1<<ADEN)|(0<<ADPS2)|(0<<ADPS1)|(1<<ADPS0);}
    else if (prescalar==4)
    {ADCSRA=(1<<ADEN)|(0<<ADPS2)|(1<<ADPS1)|(0<<ADPS0);}
    else if (prescalar==8)
    {ADCSRA=(1<<ADEN)|(0<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);}
    else if (prescalar==16)
    {ADCSRA=(1<<ADEN)|(1<<ADPS2)|(0<<ADPS1)|(0<<ADPS0);}
    else if (prescalar==32)
    {ADCSRA=(1<<ADEN)|(1<<ADPS2)|(0<<ADPS1)|(1<<ADPS0);}
    else if (prescalar==64)
    {ADCSRA=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(0<<ADPS0);}
    else if (prescalar==128)
    {ADCSRA=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);}
}

int ADC_readout(int channel)
{
    channel=channel&0b00000111;//selecting single ended channel refer data sheet for details
    ADMUX|=channel;
    ADCSRA|=(1<<ADSC);
    while(!(ADCSRA & (1<<ADIF)));
    ADCSRA|=(1<<ADIF);
    return(ADC);
}

void clearLCD()

{ LCD_cmd(0x01);}


void init_LCD(void)
{
    LCD_cmd(0x38);// initialization of 16X2 LCD in 8bit mode
    _delay_ms(1);
    LCD_cmd(0x01); // clear LCD
    _delay_ms(10);
    LCD_cmd(0x0C); // cursor ON
    _delay_ms(1);
    LCD_cmd(0x80);//8 go to first line and0 is for 0th position
}

void LCD_cmd(unsigned char cmd)
{LCD_DATA=cmd;
    control |=(1<<en); // RS and RW as LOW and EN as HIGH
    control &=~(1<<rs);
    control &=~(1<<rw);
    _delay_ms(1);
    control &=~(1<<rs);
    control &=~(1<<rw);
    control &=~(1<<en); // RS, RW , LOW and EN as LOW
    _delay_ms(1);
}

void LCD_write(unsigned char data)
{
    LCD_DATA= data;
    control |= (1<<rs)|(1<<en); // RW as LOW and RS, EN as HIGH
    control &=~(1<<rw);
    _delay_ms(1);
    control |= (1<<rs); // EN and RW as LOW and RS HIGH
    control &=~(1<<rw);
    control &=~(1<<en);
    _delay_ms(1); // delay to get things executed
    return ;
}

void stringwrite(const char *S)
{
    while (*S) {
        LCD_write(*S++);
    }
}

void moveto(int line,int pos)
{
    if(line==1)
    LCD_cmd(0x80+pos);
    if(line==2)
    LCD_cmd(0xC0+pos);
    if(line==3)
    LCD_cmd(0x90+4+pos);
    if(line==4)
    LCD_cmd(0xD0+4+pos);
}

char buffer[5];

int main(void)
{
    int DATA =0;
    int ANGLE =0;
   
    MCUCSR|=(1<<JTD);//Disable JTAG
    MCUCSR|=(1<<JTD);//Have to send it twice
    DDRA=0x00;
    DDRB=0xFF;
    DDRC=0xFF;
    DDRD=0xFF;
    PORTA=0x00;
    PORTB=0X00;
    PORTC=0X00;
    PORTD=0x00;
    PORTD|=(1<<PD7);
    init_LCD();
    InitialiseADC('V',4);
    moveto(1,0);
    stringwrite("-----Angular----");
    moveto(2,0);
    stringwrite("measuring device");
    _delay_ms(2000);
    clearLCD();
    moveto(1,0);
    stringwrite("ADC Value :");
    moveto(2,0);
    stringwrite("Angle :   degree");
    InitialiseADC('V',4);
    while(1)
    { DATA=ADC_readout(4);
       
        moveto(2,7);
        stringwrite("   ");// to clear previous value
        moveto(1,12);
        stringwrite("     ");// to clear previous value
        itoa(DATA,buffer,10);
        moveto(1,12);
        stringwrite(buffer);
        ANGLE=DATA/3.78;
        itoa(ANGLE,buffer,10);
        moveto(2,7);
        stringwrite(buffer);
        _delay_ms(200);//delay to reduce flickering
    }
}

--------------------------------------------------------------------------------------------------------------------------

So thats it , hope this helps ...... comment if any doubts



No comments:

Post a Comment