potentiometer adc practice in atmega128 - c

I'm a beginner in this field. My goal is to change the output of 8 LEDs (which are connected to PORTA) according to the potentiometer. I have connected the middle line of the potentiometer to PF0, which is ADC0. I also connected the other two lines to the 5V and ground.
I know there's no problem with the chip or connection because the LEDs are working just fine.
But no matter how I change the code below (what I mean by changing is by slightly changing the ADMUX and ADCSRA registers) no output is shown!
I am using atmega128 with 16MHZ clock. Below is the code that I'm trying to solve.
#include <asf.h>
#include <avr/io.h>
#define F_CPU 16000000L
int init_board(void)
{
DDRA=0xff;
PORTA=0x01;
}
int ADC_init(void)
{
//ADCSRA
ADCSRA = 0b10000111;
//ADMUX
ADMUX = 0b01100000; // middle line connected to ADC0
}
int main (void)
{
init_board();
ADC_init();
ADCSRA |= (ADSC >> 1);
while(1)
{
if(ADSC == 0)
{
uint8_t disp_value = ADCL;
PORTA = disp_value;
delay_ms(200);
ADCSRA |= (ADSC >> 1);
}
}
}
I have no idea why the code doesn't work.
I suppose it's because it didn't set my register correctly, but I've followed all the instructions on the atmega128 datasheet.

First issue is your bit shifting, it should be ADCSRA |= (1 << ADSC).
Next issue is results reading. You set fifth bit of ADMUX to 1, so ADLAR=1 and in that mode result is left adjusted so you should read ADCH.
Moreover when you switch to 10-bit resolution, i.e. you start working with multi-byte results, be aware that reading only ADCL is not enough, see datasheet 23.3 for explanation: "Once ADCL is read, ADC access to data registers is blocked. This means that if ADCL has been read, and a conversion completes before ADCH is read, neither register is updated and the result from the conversion is lost. When ADCH is read, ADC access to the ADCH and ADCL Registers is re-enabled."
Lastly, using hardcoded delays for reading is not good practice especially when you change code later to read ADC as fast as possible. In such case after conversion start you should check if ADIF flag is set or react with interrup when ADEN is set. Refer to datasheet for details.

Related

Different results between step debugging and running program on tiva c

I have a TIVA tm4c123G I have been trying to create a communication between it and my ADXL345 sensor using I2C protocol which I succeeded in writing and reading from the accelerometer the readings of the device address and the register values that I just wrote to which means everything is running fine. However I have tried this in step by step debugging in keil and it works fine but if I run the program it will give zeroes all the way and I have no idea why? Should I add delays between the write and read from registers or whats going wrong in my code?
Here is my code attached
I am using a clock of 80 MHZ for the system and I think this might be the problem however as the code goes too fast to the execution of a next send and there should be some delay? I am not sure I'm only guessing please help thanks !
also my connection for the adxl is
Vcc -> 3.3 volts
GND -> ground
CS -> 3.3 volts
SDO -> ground
SDA -> PB3
SCL -> PB2
#include "tm4c123gh6pm.h"
#include "stdint.h"
void EnableI2CModule0(void);
uint8_t ReadRegister(uint8_t RegisterAddress);
void PLL_Init(void);
void WriteRegister(uint8_t RegisterAddress,uint8_t Data);
volatile uint8_t X_Axis1,X_Axis2,Y_Axis1,Y_Axis2,Z_Axis1,Z_Axis2=0;
int main()
{
volatile long temp;
PLL_Init();
EnableI2CModule0();
temp=ReadRegister(0x00);
WriteRegister(0x2D,0x08);
temp=ReadRegister(0x2D);
WriteRegister(0x31,0x0B);
temp=ReadRegister(0x31);
while(1)
{
X_Axis1=ReadRegister(0x32);
X_Axis2=ReadRegister(0x33);
Y_Axis1=ReadRegister(0x34);
Y_Axis2=ReadRegister(0x35);
Z_Axis1=ReadRegister(0x36);
Z_Axis2=ReadRegister(0x37);
}
}
void PLL_Init(void){
// 0) Use RCC2
SYSCTL_RCC2_R |= 0x80000000; // USERCC2
// 1) bypass PLL while initializing
SYSCTL_RCC2_R |= 0x00000800; // BYPASS2, PLL bypass
// 2) select the crystal value and oscillator source
SYSCTL_RCC_R = (SYSCTL_RCC_R &~0x000007C0) // clear XTAL field, bits 10-6
+ 0x00000540; // 10101, configure for 16 MHz crystal
SYSCTL_RCC2_R &= ~0x00000070; // configure for main oscillator source
// 3) activate PLL by clearing PWRDN
SYSCTL_RCC2_R &= ~0x00002000;
// 4) set the desired system divider
SYSCTL_RCC2_R |= 0x40000000; // use 400 MHz PLL
SYSCTL_RCC2_R = (SYSCTL_RCC2_R&~ 0x1FC00000) // clear system clock divider
+ (4<<22); // configure for 80 MHz clock
// 5) wait for the PLL to lock by polling PLLLRIS
while((SYSCTL_RIS_R&0x00000040)==0){}; // wait for PLLRIS bit
// 6) enable use of PLL by clearing BYPASS
SYSCTL_RCC2_R &= ~0x00000800;
}
void EnableI2CModule0(void)
{
volatile int Delay=0;
SYSCTL_RCGCI2C_R|=0x00000001; //set i2c module 0 clock active
Delay=SYSCTL_RCGCI2C_R; //delay allow clock to stabilize
SYSCTL_RCGCGPIO_R |=0x00000002; //i2c module 0 is portB so activate clock for port B
Delay = SYSCTL_RCGCGPIO_R; //delay allow clock to stabilize
GPIO_PORTB_AFSEL_R|= 0x0000000C; //enable alternate functions for PB2 and PB3
GPIO_PORTB_ODR_R |= 0x00000008; //set PB3 (I2C SDA) for open drain
GPIO_PORTB_DEN_R |= 0xFF; //Enable digital on Port B
GPIO_PORTB_PCTL_R |=0x03;
I2C0_PP_R |= 0x01;
I2C0_MTPR_R |= 0x00000027; //set SCL clock
I2C0_MCR_R |= 0x00000010; //intialize mcr rigester with that value given in datasheet
}
uint8_t ReadRegister(uint8_t RegisterAddress)
{
volatile uint8_t result=0;
I2C0_MSA_R = 0x000000A6; //write operation
I2C0_MDR_R = RegisterAddress; //place data to send mdr register
I2C0_MCS_R = 0x00000007; //stop start run
while((I2C0_MCS_R &= 0x00000040)==1); //poll busy bit
I2C0_MSA_R = 0x000000A7; // read operation
I2C0_MCS_R = 0x00000007; // stop start run
while((I2C0_MCS_R &= 0x00000040)==1); //poll busy bit
result = I2C0_MDR_R;
return result;
}
void WriteRegister(uint8_t RegisterAddress,uint8_t Data)
{
I2C0_MSA_R = 0x000000A6; //write operation
I2C0_MDR_R = RegisterAddress; //place register address to set in mdr register
I2C0_MCS_R = 0x00000003; //burst send ( multiple bytes send )
while((I2C0_MCS_R &= 0x00000040)==1); //poll busy bit
I2C0_MDR_R = Data; //place data to be sent in mdr register
I2C0_MCS_R = 0x00000005; // transmit followed by stop state
while((I2C0_MCS_R &= 0x00000040)==1); //poll busy bit
}
Your WriteRegister and ReadRegister functions do not follow the flowcharts defined in the TM4C123G data sheet. Apart from not checking or handling the MCS ERROR flag, Figure 16-10 Master TRANSMIT of Multiple Data Bytes shows that when writing the MCS register, you should assert specific bits, while you are writing to all bits, You should instead perform a read-modify-write:
I2C0_MCS_R = 0x00000003; //burst send ( multiple bytes send )
should be:
// I2CMCS = ---0-011
uint32_t mcs = I2C0_MCS_R ;
msc &= ~0x00000014; // ---0-0--
mcs |= 0x00000003; // ------11
I2C0_MCS_R = mcs ;
And similarly:
I2C0_MCS_R = 0x00000005; // transmit followed by stop state
should be
// I2CMCS = ---0-101
mcs = I2C0_MCS_R ;
mcs &= ~0x00000012; // ---0--0-
mcs |= 0x00000005; // -----1-1
I2C0_MCS_R = mcs ;
ReadRegister() has a similar issue (although it is unlikely to be an issue in this case):
I2C0_MCS_R = 0x00000007; //stop start run
should strictly be:
// I2CMCS = ---00111
uint32_t mcs = I2C0_MCS_R ;
mcs &= ~0x00000018; // ---00---
mcs |= 0x00000007; // -----111
I2C0_MCS_R = mcs ;
The datasheet recommends for bits 31:5:
Software should not rely on the value of a reserved bit. To provide
compatibility with future products, the value of a reserved bit should
be preserved across a read-modify-write operation.
The above code does that, but in practice should not be necessary on this specific product, but is good practice in any case.
In any event you should add the recommended error handling code. It may be that no error flag is being set, but we don't know that unless you check for it, and doing so will at least assist debugging - rather then stepping the code, you can simply set a break-point on the error handling and then run at full-speed. This will narrow down the number of possibilities.
as #Clifford had explained that i should follow the flow charts and although his answer is completely correct it didn't give me any results (previously gave values in case of stepping into the function gave zeroes afterwards) but , i noticed something in the flow charts that i hadn't noticed before which contradicts with the initialization and configuration section in the data sheet
now as it says in step 11 that you should be polling the bus busy bit in the MCS register but this is wrong and contradicts with the flow charts , the flow charts are more correct as u should check if the bus is busy before sending anything and then check for the master busy bit before reading from the MDR register or moving on to execute and further steps
basically the correct steps in the initialization and configuration should be :
before step 10 poll the bus busy bit in case any other master is sending which can be omitted in case of a single master
after step 10 poll the busy bit before reading or going to any further step to conclude whether the sending has been completed and the master is idle or not
i'm sorry i feel like a complete idiot now for not reading the flow charts carefully but i followed another part which is the initialization and configuration part accepting a fact which wasn't there that both should imply the same thing .
i also found that it works correctly in the tivaware API following the flow charts and not that other section in the datasheet however i didn't want to use the Tivaware API as i am looking forward for problems like this which lead to a better understanding of how things work
thanks again for your help #Clifford cheers!

ADC only working once on ATMEGA324PA

I have some code which should read the values of a couple of ADC pins, each time around the commutator loop.
static uint16_t adc0;
static uint16_t adc1;
void init(void) {
...
hw_configure_adcs();
...
}
void loop(void) {
...
adc0 = hw_read_adc(0);
adc1 = hw_read_adc(1);
...
}
void hw_configure_adcs(void) {
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS0);
}
uint16_t hw_read_adc(uint8_t n) {
ADMUX = (1<<REFS0) | (n & 0x07);
ADCSRA |= (1<<ADSC); // start conversion
uint16_t count;
for (count = 0; !(ADCSRA & (1<<ADIF)); count++); // wait for conversion to complete
// ADCSRA |= (1<<ADIF); // tried with and without this
return (ADCH << 8) | ADCL; // return ADC value
}
What I see is odd: The values of adc0 and adc1 are set to the same value and never change, until the AVR chip is restarted/reflashed.
(The value is 0x00d1 at 0.71V and 0x0128 at 1.00V, which seems reasonable.)
I have tried:
Turning the voltage down: adc0 and adc1 stay constant and only go down when the AVR code is reflashed (and hence the chip restarted).
Turning the voltage up: adc0 and adc1 stay constant and only go up when the AVR code is reflashed (and hence the chip restarted).
Returning count from hw_read_adc() instead of the ADC value: This returns varying numbers between 0x34 and 0x38 which are different for the two ADCs and continuously vary over time.
From these tests, I infer that the ADCs are being read, but that I am missing some "clear ADCH and ADCL and get them ready to accept the new reading" step.
I have re-read section 23 of http://www.atmel.com/images/Atmel-8272-8-bit-AVR-microcontroller-ATmega164A_PA-324A_PA-644A_PA-1284_P_datasheet.pdf many times, but have obviously overlooked something vital.
After much googling, I found: http://www.avrfreaks.net/forum/adc-only-happens-once-reset
The problem was that return (ADCH << 8) | ADCL; was compiled so that it read the high register first (as you might expect).
Page 252 of the datasheet says: "Otherwise, ADCL must be read first, then ADCH".
Changing my code to return ADC fixed the problem.
My guess as to what was happening is:
The read from ADCH occurred.
The read from ADCL had the effect of locking the ADC-result, to prevent tearing.
The next ADC read had nowhere to write its result, as the ADC-result was locked.
Repeat...
The Problem with your code is you read ADCH first.
ADCL must be read first, then ADCH, to ensure that the content of the Data Registers belongs to the same conversion. Once ADCL is read, ADC access to Data Registers is blocked. This means that if ADCL has been read, and a conversion completes before ADCH is read, neither register is updated and the result from the conversion is lost. When ADCH is read, ADC access to the ADCH and ADCL Registers is re-enabled.
So the correct code should be-
return ADCL | (ADCH << 8) ;

ADC conversion error in code ATMEGA328p

I am trying to read values from ADC0 on the ATMEGA328p. The values expected are between 0-5v. This is due to ADC0 being connected to a potentiometer connected to the 5v output of a Xplained mini. I am getting either 0v or 5v usually. With no variation when the potentiometer is changed. I have looked at multiple ADC examples and tutorials online but cant find the error in my code.
void adc_initialise (){
//set vref to AVcc, channel selection is initially ADC0 as wanted
ADMUX |= (1<<6);
//set ADC enable, Set adc clock prescalar 64
ADCSRA |= (1<<7)|(1<<2)|(1<<1);
}
uint16_t adc_read (){
ADCSRA |= (1<<6); // start conversion
while( ADCSRA & (1<<ADSC) ); //wait until conversion is complete
return ADCW;
}
float adc_calculation(uint16_t adcValue){
float stepSize = (5.0/1024.0);
float voltageIn = adcValue*stepSize;
return voltageIn;
}
then in my main i have
while(1){
adc_initialise();
uint16_t adcValue = adc_read();
float voltageIn = adc_calculation(adcValue);
adcConverterToUART(voltageIn);//I know that this part of the code is working as I have hardcoded many test values and all have transmitted correctly.
}
And as mentioned above I know the error is not in my UART code but somewhere in the above ADC code. Cheers in advance for any help.
I could mention a few things. You could try it if it help.
You should do your adc_initialise() before the while(1).
You initialize it again and again.
while( ADCSRA & (1<<ADSC) ); here you should add maybe NOPs that the compiler don't optimize it out of the code.
The rest looks good in my eyes.
Do you get any value of the conversion ?
mfg
EDIT1:
I looked in one of my old files.
There we made it like this to get the value from the ADC.
// Get count value
adValue = ADCL;
adValue |= (UI_16_t)(ADCH << 8);
ADCL is the low byte of the value and ADCH the high byte.
We shifted the high byte in the front of the low byte to get the value.

Trying to bit-bang TPIC6C595 shift register but no output

The code is for an AVR atamega168xplained mini board, with an ATmega168pb MCU. The shift register I am using is a Texas Instruments TPIC6C595 I have the drain outputs of the shift register connected to the anodes of 8 LEDs. The OE(G) pin of shift register is tied to GND, and CLR tied to 5V. There is a 100nF ceramic capacitor between shift register VCC and GND. SER OUT is left unconnected to anything since I am trying to bit-bang just this one before I move up to chaining shift registers.
What happens is that I get no output from the shift register, all drain outputs are low (tested with multimeter). When I disconnect the SER IN, SRCK, and RCK from the microcontroller i get some flickering on only one of the LEDs which I guess is a result of those pins floating and being in an undefined state. I would have expected at least to get some kind of garbage output even if the code was wrong, but I get more of an output with the microcontroller completely disconnected. I know it is outputting a signal because I can connect it to the LEDS without the shift register and see they are lit up at various levels of intensity but do not have an oscilloscope to be able to actually look at the signals.
This is the code, with the defines for the output port at the top of the file included so it's clear what's being done:
#define DDR_SREG DDRD
#define PORT_SREG PORTD
#define SRCK _BV(PORTD0)
#define RCK _BV(PORTD1)
#define SER _BV(PORTD2)
void display_write(uint8_t data)
{
char i;
PORT_SREG &= ~RCK; // latch low
for (i = 0; i < 8; ++i) {
PORT_SREG &= ~SRCK; // clock low
if (data & 1) // serial out
PORT_SREG |= SER;
else
PORT_SREG &= ~SER;
PORT_SREG |= SRCK; // clock high
data >>= 1; // shift data
}
PORT_SREG |= RCK; // latch high
}
Solved it. After doing some more research, it was apparent that this shift register has open drain outputs and cannot source current (they can only sink current). Adjusting the wiring accordingly I was able to get the shift register working to my satisfaction.

ADC not working with ATMEGA8

I'm trying to execute the following piece of code on ATMEGA8 but the ADC doesn't seem to be working.
#include <avr/io.h>
#include "LCD.h"
int main()
{
int val=0;
ADCSRA=0x87;//ADc enabled and prescaler set to fosc/128
ADMUX= 0xC0;//REFS0 and REFS1 set using internal 2.5 volts as Vref
DDRC=0x00;// as input for the adc
PORTC=0x00;
DDRB=0xff;
while (1)
{
ADCSRA |=(1<<ADSC);
while(!(ADCSRA&(1<<ADIF)));
lcd_string("Done Conversion");
val=ADCL;
PORTB=ADCL;
ADCSRA |=(1<<ADIF);//(reseting ADIF to 1)
lcd_print(2,1,val,3);
}
return 0;
}
You have not read ADCH. The data sheet says
When ADCL is read, the ADC Data Register is not updated until ADCH is
read. Consequently, if the result is left adjusted and no more than
8-bit precision is required, it is sufficient to read ADCH. Otherwise,
ADCL must be read first, then ADCH.
val = ADCL;
val = ((ADCH<<8) | val) & 0x3F;
You are writing the result to an 8-bit port. If you want an 8-bit conversion then set the ADLAR bit in ADMUX. The 10-bit conversion will then be left-shifted by 6 bits and you can ignore the ls 2 bits in ADCL.
ADMUX = 0xE0;
...
val = ADCH;
BTW read-modify-write of ADCSRA is not recomended. To clear bit 4 – ADIF, the ADC Interrupt Flag, you could try
ADCSRA = 0x97; // rewrite config and clear ADIF
Which is your original configuration with the ADIF bit set to clear that flag. Alternatively, you could test bit 6 ADSC which remains high until the conversion is complete, and no action is required to clear it. Since you have not enabled the ADC interrupt, there is no need to clear the ADIF flag.
while (ADCSRA & (1<<ADSC)); // wait for conversion to complete

Resources