Summary: Internal temp having trouble connecting to the right channel.
Want to get right value from the channel.
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <avr/interrupt.h>
uint8_t ReADC()//read value ad
{
ADMUX = (1 << MUX3) | (1 << REFS0) | (1 << REFS1) | (1 << ADLAR);
//single conversion
ADCSRA |= (1 << ADEN) | (1<<ADSC) | (1 << ADPS2);
ISR(TIMER0_COMPA_vect){
ADC();
}
void SPI_MASTER(){
DDRB = (1 << PB0) | (1 << PB3) | (1 << PB5);
SPCR = (1 << CPOL) | (1 << MSTR) | (1 << SPR0) | (1 << SPI2X) | (1 << SPE);
}
}
I agree with comments — you should read DS, ADC Conversion Result and Temperature Measurement sections.
But there is a bug in measurement process (ADC usage issue) that prevent you from reading right ADC result for temperature calculation.
See:
uint8_t ReadADC()
{
ADMUX = (1 << MUX3) | (1 << REFS0) | (1 << REFS1) | (1 << ADLAR); // (1)
ADCSRA |= (1 << ADEN) | (1<<ADSC) | (1 << ADPS2) | (1 << ADPS1)
| (1 << ADPS0) | (1 << ADIE);
while( ADCSRA & (1<<ADSC) );
return ADC; // (2)
}
You (1) select ADLAR, so ADC result is shifted toward high bit of 16-bit ADCH:ADCL pair. But then (2) you read the 16-bit value and convert it into 8-bit value by throwing out high 8 bits. As a result, you get two least significant bits of ADC result in two most significant bits of returned value. So, these bits can encode 0, 64, 128, 192 only.
uint8_t SPI_transmit_data()
{
PORTB &= ~(1 << PB0);
SPDR = ADC; // (2)
while(!SPSR & (1<<SPIF));
PORTB |= (1 << PB0);
return(SPDR);
}
The same is here, the lowest 8 bits of ADC result is writed into 8-bit SPDR and higher 8 bits are thrown out.
Use ADCH instead of ADC
Related
Im trying to display A on TERA TERM simulator but when i run my code on ATMEL STUDIO 7 the code doesn't stop running.
This is my code below i am running the program on the simulator tool available on atmel studio 7 and i believe i am configuring the UART correctly
#include <avr/io.h>
void configureUART(void);
void sendUART(unsigned char);
int main(void)
{
configureUART();
sendUART('A');
}
void sendUART(unsigned char transmitByte)
{
while ((UCSRA & (1 << UDRE))== 0);
// Data register is ready, transmit out the byte.
UDR = transmitByte;
}
void configureUART()
{
//Baud Rate Config 9600 => 0x4D
UBRRH = 0x00;
UBRRL = 0x4D;
//Need to use UCSRC Register w/ asynchronous op. and 1 stop bit w/ 8 data bits
UCSRC = (1 << URSEL) | (0 << USBS) | (1 << UCSZ0) | (1 << UCSZ0) ;
//Transmit and Receive
UCSRB = (1 << RXEN) | (1 << TXEN);
}
I went through the entire configuration of the Analog-to-digital converter [ADC]. When I worked on the registers, I made a mistake somewhere. Below are the configurations. The debugger via ST-Link after connecting 3.3 [V] to pins used in the project, during the measurement assigns them a value of 0x00 which indicates a failure. What am I doing wrong?
int main(void)
{
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
//ADC attach
RCC->IOPENR = RCC_IOPENR_GPIOAEN | RCC_IOPENR_GPIOBEN;
GPIOB->MODER = GPIO_MODER_MODE14_1 | GPIO_MODER_MODE15_1;
//ADC_IN8 & ADC_IN9
/* configure ADC */
ADC1->ISR &= ~ADC_ISR_EOCAL & ~ADC_ISR_AWD;
//calibration flag, WATCHDOG flag
ADC1->ISR |= ADC_ISR_ADRDY;
ADC1->CR &= ~ADC_CR_ADSTART;
//The software is allowed to write smp bit only when ADSTART=0
ADC1->SMPR |= ADC_SMPR_SMP_0 | ADC_SMPR_SMP_1 | ADC_SMPR_SMP_2;
//111: 160.5 ADC clock cycles
ADC1->CFGR1 &= ~ADC_CFGR1_SCANDIR;
//Scan Direction 0: Upward scan (from CHSEL0 to CHSEL18)
ADC1->CFGR1 |= ADC_CFGR1_AWDCH_3 | ADC_CFGR1_AWDEN | ADC_CFGR1_WAIT | ADC_CFGR1_CONT | ADC_CFGR1_AUTOFF;
//AWDCH[4:0]: Analog watchdog channel selection, Continuous Mode
ADC->CCR |= ADC_CCR_LFMEN | ADC_CCR_VREFEN;
//Low Frequency Mode, V REFINT enable
ADC1->CHSELR |= ADC_CHSELR_CHSEL8 | ADC_CHSELR_CHSEL9 | ADC_CHSELR_CHSEL17;
//Channel Select 8 & 9
ADC1->IER |= ADC_IER_EOCIE | ADC_IER_EOSEQIE | ADC_IER_OVRIE | ADC_IER_EOSMPIE;
NVIC_EnableIRQ(ADC1_COMP_IRQn);
NVIC_SetPriority(ADC1_COMP_IRQn,3);
while(1)
{
ADC1->CR |= ADC_CR_ADEN;// | ADC_CR_ADSTART;
//Start the ADC conversion
while ((ADC1->ISR & ADC_ISR_ADRDY));
//Wait for stand up
while ((ADC1->ISR & ADC_ISR_EOC));
//wait for conversion flag
ADC1->CR |= ADC_CR_ADCAL;
//End of the calibration
delay(100);
uint16_t napiecie = ADC1->DR;
uint8_t hi = ((napiecie >> 8) & 0xff);
uint8_t lo = ((napiecie >> 0) & 0xff);
//DISABLE ADC
if ((ADC1->CR & ADC_CR_ADSTART) != 0){
ADC1->CR |= ADC_CR_ADSTP;
while ((ADC1->CR & ADC_CR_ADSTP) != 0);
}
ADC1->CR |= ADC_CR_ADDIS; //ADC disable command
while ((ADC1->CR & ADC_CR_ADEN) != 0);
ADC1->CR &= ~ADC_CR_ADSTART & ~ADC_CR_ADEN;
//ADC1->CR &= ~ADC_ISR_ADRDY; //Clear the ADRDY bit in ADC_ISR register by programming this bit to 1 (optional).
}
void ADC1_COMP_IRQHandler(void)
/* Interupt ADC */
{
if(ADC1->ISR & ADC_ISR_EOC){
uint16_t napiecie = ADC1->DR;
uint8_t hi = ((napiecie >> 8) & 0xff);
uint8_t lo = ((napiecie >> 0) & 0xff);
}
}
Two times you used lines like
ADC1->ISR &= ~ADC_ISR_EOCAL | ~ADC_ISR_AWD;
which looks really strange to me, as if the defines are 1 bit wide, which they most likely are, their bitwise OR is 0xFFFFFFFF (all F, no 0) and you're not changing the ISR and the CR (later in the code) at all! You need to use a bitwise AND, no?
ADC1->ISR &= ~ADC_ISR_EOCAL & ~ADC_ISR_AWD;
...
ADC1->CR &= ~ADC_CR_ADSTART & ~ADC_CR_ADEN;
Else some working code is available at https://electronics.stackexchange.com/questions/287073/get-internal-temperature-or-voltage-stm32l0/287162
https://github.com/ChristopherJD/STM32L053R8/blob/master/Intern_Project/ADC.c
and https://www.digikey.com/eewiki/pages/viewpage.action?pageId=47644832
Using IDE : AtmelStudio, uC = atmega328p, sensor DHT11
Im trying to read the values from DHT11.
I have set the start signal
DDRC |= 1 << TEMP_SENZOR; //output
_delay_ms(2000); // wait for 2s according to datasheet
PORTC &= ~ (1 << TEMP_SENZOR); //set low for at least 18 ms
_delay_ms(20);
PORTC |= 1 << TEMP_SENZOR; // set high
Then i have set getResponse metod
DDRC &= ~ (1 << TEMP_SENZOR); // set for input
while(PINC & (1 << TEMP_SENZOR));
while(PINC & (1 << TEMP_SENZOR) == 0);
while(PINC & (1 << TEMP_SENZOR));
I have tried to code function several times, to get values but unfortunately i get some values that have nothing to do with reading from Proteus DHT11.
Im new to this, so sorry if question is trivial.
I write TFT LCD driver for stm32 and I need write 8 bit data to LCD. I found this macro in mcufriend's library, but I'm not sure how it works and how can I edit it. Can somebody write how to write macro like this and how it works ?
Macro from mcufriend's library:
#define write_8(d) { \
GPIOA->regs->BSRR = 0x0700 << 16; \
GPIOB->regs->BSRR = 0x0438 << 16; \
GPIOC->regs->BSRR = 0x0080 << 16; \
GPIOA->regs->BSRR = (((d) & (1<<0)) << 9) \
| (((d) & (1<<2)) << 8) \
| (((d) & (1<<7)) << 1); \
GPIOB->regs->BSRR = (((d) & (1<<3)) << 0) \
| (((d) & (1<<4)) << 1) \
| (((d) & (1<<5)) >> 1) \
| (((d) & (1<<6)) << 4); \
GPIOC->regs->BSRR = (((d) & (1<<1)) << 6); \
}
That looks like STM32duino syntax:
PORT->regs->REGISTER = value
with REGISTER being BSRR, the bit set reset register. The value you assign to this sets pins 0-15 of PORT high, if the corresponding bit is set. Bits 16-31 act the same as bits 0-15 of the BRR register; if the bit is set, the corresponding pin is set low. If a bit is zero, the state of the corresponding pin is unchanged.
Lets look at each individual assignment in the macro.
GPIOA->regs->BSRR = 0x0700 << 16;
This sets pins GPIOA8, GPIOA9, and GPIOA10 low. ((1<<8) | (1<<9) | (1<<10) == 0x0700, and high 16 bits of BSRR sets the corresponding bits low.)
GPIOB->regs->BSRR = 0x0438 << 16;
This sets pins GPIOB3, GPIOB4, GPIOB5, and GPIOB10 low.
GPIOC->regs->BSRR = 0x0080 << 16;
This sets pin GPIOC7 low.
GPIOA->regs->BSRR =
(((d) & (1<<0)) << 9)
| (((d) & (1<<2)) << 8)
| (((d) & (1<<7)) << 1);
If bit 0 of d is set, then pin GPIOA9 is set high.
If bit 2 of d is set, then pin GPIOA10 is set high.
If bit 7 of d is set, then pin GPIOA8 is set high.
GPIOB->regs->BSRR =
(((d) & (1<<3)) << 0)
| (((d) & (1<<4)) << 1)
| (((d) & (1<<5)) >> 1)
| (((d) & (1<<6)) << 4);
If bit 3 of d is set, pin GPIOB3 is set high.
If bit 4 of d is set, pin GPIOB5 is set high.
If bit 5 of d is set, pin GPIOB4 is set high.
If bit 6 of d is set, pin GPIOB10 is set high.
GPIOC->regs->BSRR =
(((d) & (1<<1)) << 6);
If bit 1 of d is set, pin GPIOC7 is set high.
The first three lines set pins GPIOA8, GPIOA9, GPIOA10, GPIOB3, GPIOB4, GPIOB5, GPIOB10, and GPIOC7 low.
The rest of the lines set pins GPIOA9, GPIOC7, GPIOA10, GPIOB3, GPIOB5, GPIOB4, GPIOB10, and GPIOA8 high, if the corresponding bit in d is set.
In other words, the macro sets the pins A9,C7,A10,B3,B5,B4,B10,A8 high if the corresponding bit 0-7 in d is set, and low if clear.
I would hazard a guess that the macro is used to communicate with a peripheral (likely a display) using 8-bit parallel bus, with pin A9 corresponding to the least significant bit of each parallel byte sent, and pin A8 to the most significant bits; the other pins as listed above in between.
I'm tinkering around with an ATMega328P right now and wanted to read an analogue value from a pin through the ADC and simply output the value to 4 LEDs. Really simple
#define F_CPU 20000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define BRIGHTNESS_PIN 2
#define ADC_SAMPLES 5
void init_adc()
{
//set ADC VRef to AVCC
ADMUX |= (1 << REFS0);
//enable ADC and set pre-scaler to 128
ADCSRA = (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2) | (1 << ADEN);
}
uint16_t read_adc(unsigned char channel)
{
//clear lower 4 bits of ADMUX and select ADC channel according to argument
ADMUX &= (0xF0);
ADMUX |= (channel & 0x0F); //set channel, limit channel selection to lower 4 bits
//start ADC conversion
ADCSRA |= (1 << ADSC);
//wait for conversion to finish
while(!(ADCSRA & (1 << ADIF)));
ADCSRA |= (1 << ADIF); //reset as required
return ADC;
}
int main(void)
{
uint32_t brightness_total;
uint16_t brightness = 0;
uint32_t i = 0;
init_adc();
sei();
while (1)
{
//reset LED pins
PORTB &= ~(1 << PINB0);
PORTD &= ~(1 << PIND7);
PORTD &= ~(1 << PIND6);
PORTD &= ~(1 << PIND5);
PORTB |= (1 << PINB1); //just blink
read_adc(BRIGHTNESS_PIN); //first throw-away read
//read n sample values from the ADC and average them out
brightness_total = 0;
for(i = 0; i < ADC_SAMPLES; ++i)
{
brightness_total += read_adc(BRIGHTNESS_PIN);
}
brightness = brightness_total / ADC_SAMPLES;
//set pins for LEDs depending on read value.
if(brightness > 768)
{
PORTB |= (1 << PINB0);
PORTD |= (1 << PIND7);
PORTD |= (1 << PIND6);
PORTD |= (1 << PIND5);
}
else if (brightness <= 768 && brightness > 512)
{
PORTB |= (1 << PINB0);
PORTD |= (1 << PIND7);
PORTD |= (1 << PIND6);
}
else if (brightness <= 512 && brightness > 256)
{
PORTB |= (1 << PINB0);
PORTD |= (1 << PIND7);
}
else if (brightness <= 256 && brightness >=64)
{
PORTB |= (1 << PINB0);
}
_delay_ms(500);
PORTB &= ~(1 << PINB1); //just blink
_delay_ms(500);
}
}
This works kind of fine, except the channel selection. When I select a channel it works fine, but independently from the selected channel, channel 0 also always reads and converts. What I mean with that is that if I plug the cable into the selected channel pin, it reads the values correctly. When I plug it into any other channel pin it obviously doesn't, except for ADC0. No matter what channel I set, not only does that one read but also ADC0.
Why is that and how do I fix that?
I already checked my PCB for solder bridges, but there are none and I would also expect some slightly different behaviour with that.
Also ADC4 and ADC5 don't seem to properly convert either. Any idea why that is? The only clue I found in the datasheet is, that those two use digital power, while all the other ADCs use analogue. What's the difference, why does it matter and why does it not correctly convert my anlogue signal?
Both ARef and AVCC are connected according to the datasheet, with the exception that the inductor for ARef is missing.
I think what is happening is that
ADMUX &= (0xF0);
is setting the channel to 0, and
ADMUX |= (channel & 0x0F);
is then setting the channel to the one you want. You're then taking a reading and throwing the result away, which should mean that the initial channel being set to 0 doesn't matter.
Howevever, when you then try to take an actual reading, you are setting the channel again, by using read_adc to take the reading. So, you don't ever throw a reading away.
What I would do is replace your ADMUX setting commands with:
ADMUX = (0xF0) | (channel & 0x0F)
Then move this into a separate function called something like set_adc_channel(int channel). Include a throw away read in that function, then remove the ADMUX setting from your read_adc function. Just start a conversion and get the result.
Also note that since you only ever use one channel, you could move the channel setting part to init_adc(). I assume it's in a separate function so you could later read more than one channel.
I hope that's clear. Let me know if not.
EDIT: So as you stated, ADIF is really reset by writing logic 1.
I've just tested your adc_read function and it is working for me (if you don't mind Arduino mixture)
uint16_t read_adc(unsigned char channel)
{
//clear lower 4 bits of ADMUX and select ADC channel according to argument
ADMUX &= (0xF0);
ADMUX |= (channel & 0x0F); //set channel, limit channel selection to lower 4 bits
//start ADC conversion
ADCSRA |= (1 << ADSC);
//wait for conversion to finish
while(!(ADCSRA & (1 << ADIF)));
ADCSRA |= (1 << ADIF); //reset as required
return ADC;
}
void setup() {
Serial.begin(57600);
//set ADC VRef to AVCC
ADMUX |= (1 << REFS0);
//enable ADC and set pre-scaler to 128
ADCSRA = (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2) | (1 << ADEN);
pinMode(A0, INPUT_PULLUP);
pinMode(A1, INPUT_PULLUP);
pinMode(A2, INPUT_PULLUP);
pinMode(A3, INPUT_PULLUP);
}
void loop() {
Serial.println(read_adc(0));
Serial.println(read_adc(1));
Serial.println(read_adc(2));
Serial.println(read_adc(3));
delay(1000);
}
I just connect one of these channels to 3.3V pin and it'll read 713 on it. Other channels are pulled up to levels about 1017.