Страница 1 из 1
ADC и PWM в ATTiny45 - вопросы начинающего
Добавлено: Ср фев 25, 2015 12:36:17
karambas
Доброго всем времени суток. Прошу совета.
Я пытаюсь потихоньку самостоятельно научиться работать с микроконтроллерами семейства ATTiny без Arduino, взаимодействуя через AVR-GCC. По пути спотыкаюсь о различные камни, которые в процессе изучения документации удается расколоть и пройти дальше, но вот с ADC зашел в тупик. Почему-то не получается корректно настроить контроллер на работу с сенсорами. Проверьте, пожалуйста, код, укажите на ошибки. Комментарии пытаюсь писать на английском, поэтому не пеняйте за грамматику. Заранее спасибо.
Код: Выделить всё
#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
#define setHIGH(pin) PORTB |= (1 << pin)
#define setLOW(pin) PORTB &= ~(1 << pin)
#define isHIGH(pin) PINB & (1 << pin)
#define isLOW(pin) !(PINB & (1 << pin))
//aliases for pins
#define ledPin PB0
void initADC(){
ADMUX =
(0 << REFS1) | //set REFS1 to 0 to use VCC as VREF (attiny24/45/85)
(0 << REFS0) | //set REFS0 to 0 to use VCC as VREF (attiny13/24/45/85)
(1 << ADLAR) | //shift in a 1 and follow 8bit procedure
(1 << MUX0); //set MUX0 to 1 to use PB2 as analog input pin (ADC1)
ADCSRA =
(1 << ADEN) | //enable ADC
(0 << ADSC); //set ADSC to 0 to sure no conversion are happening
}
void initIO(){
DDRB =
(1 << PB0); //set OUTPUT
}
unsigned int analogRead(){
unsigned int result = 0;
ADCSRA |= (1 << ADSC); //start ADC conversation
while(ADCSRA & (1 << ADSC)); //wait for conversation complete
result = ADCW; //get the result of conversation
return result; //return the result
}
int main(void){
initADC(); //setup ADC
initIO(); //setup Input/Output system
while(1){
if(analogRead() < 1023){
setHIGH(ledPin);
}else{
setLOW(ledPin);
}
}
return 0;
}
когда все то же самое выполняю на wiring:
Код: Выделить всё
void setup(){
pinMode(0, OUTPUT);
pinMode(2, INPUT);
}
void loop(){
if(analogRead(2) < 1023){
digitalWrite(0, HIGH);
}else{
digitalWrite(0, LOW);
}
}
то все отрабатывает корректно.
Re: ADC в ATTiny45 - запутался
Добавлено: Ср фев 25, 2015 12:51:27
Gudd-Head
Какими ещё сенсорами? Это же АЦП. Схему подключений нарисуйте, что вы хотите получить и что вы получаете в итоге.
Re: ADC в ATTiny45 - запутался
Добавлено: Ср фев 25, 2015 13:00:11
eess9
Сразу бросаются в глаза две вещи.
Первое, вы не задали предделитель. По умолчанию он у вас 2. Это очень быстро, преобразование тупо не успело выполнится. У вас АЦП 15kSPS. Пусть контроллер работает на 1МГц, частота тактирования АЦП 500кГц. Преобразование 25 тактов по даташит, т.е. 20 тыс за секунду. Дохрена. Уменьшайте из рассчета частоты тактирования АЦП 50...200кГц. Что-то вроде этого:
Второе:
Код: Выделить всё
while(ADCSRA & (1 << ADSC)); //wait for conversation complete
может надо флаг конца преобразования ждать

?:
Код: Выделить всё
while(!(ADCSRA & (1 << ADIF))) ;
ADCSRA |= (1 << ADIF);
Re: ADC в ATTiny45 - запутался
Добавлено: Ср фев 25, 2015 13:01:20
karambas
На PB2 (ADC1) висит выход с делителя напряжения, состоящего из фоторезистора и резистора.
Re: ADC в ATTiny45 - запутался
Добавлено: Ср фев 25, 2015 13:09:40
Engineer_Keen
karambas писал(а):
if(analogRead(2) < 1023)
А это точно работало? У AVR АЦП всего 10 бит, там больше 1023 по определению быть не должно, остается только один отсчет (максимум). Или это так и задумано?
Re: ADC в ATTiny45 - запутался
Добавлено: Ср фев 25, 2015 13:12:08
Gudd-Head
(1 << ADLAR)
С этим вроде должно прокатить?
Re: ADC в ATTiny45 - запутался
Добавлено: Ср фев 25, 2015 13:34:44
eess9
Gudd-Head писал(а):С этим вроде должно прокатить?
Это же костыль. Может все таки вместо 1023 записать необходимый порог срабатывания фотодатчика?
Re: ADC в ATTiny45 - запутался
Добавлено: Ср фев 25, 2015 13:55:30
Engineer_Keen
Gudd-Head писал(а):ADLAR
Мда, как-то не подумал о таком варианте, привык что ADLAR используют чтобы обойтись 8-ю битами.
Re: ADC в ATTiny45 - запутался
Добавлено: Ср фев 25, 2015 19:04:51
karambas
Возился, возился, заставил заработать, сбросив бит ADLAR. В связи с этим вопрос: практически заучив даташит, все же не могу понять предназначение бита ADLAR регистра ADMUX в практическом смысле. В даташите говорится, что:
выставляя бит ADLAR мы устанавливаем порядок записи результата преобразования в регистры ADCL и ADCH; когда ADLAR = 1 два младших бита пишутся в ADCL, остальные в ADCH, когда ADLAR = 0, два старших байта в ADCH, остальные в ADCL.
Как это в практическом смысле работает, не могу понять.
Допустим на входе АЦП есть 4В как они будут преобразованы в том и в другом случае?
eess9 писал(а):Сразу бросаются в глаза две вещи.
Первое, вы не задали предделитель. Уменьшайте из рассчета частоты тактирования АЦП 50...200кГц.
Поправил initADC() с учетом Вашего замечания по prescaler:
Код: Выделить всё
void initADC(){
ADMUX =
(0 << REFS1) | //set REFS1 to 0 to use VCC as VREF (attiny24/45/85)
(0 << REFS0) | //set REFS0 to 0 to use VCC as VREF (attiny13/24/45/85)
(0 << ADLAR) | //bit ADLAR remains clear to follow 10bits procedure
(1 << MUX0); //set MUX0 to 1 to use PB2 as analog input pin (ADC1)
ADCSRA =
(1 << ADEN) | //enable ADC
(0 << ADSC) | //set ADSC to 0 to sure no conversion are happening
(1 << ADPS1) | //set ADPS1 and ADPS0 bits, which set a prescaler to 8
(1 << ADPS0); //for 1MHz system clock to set ADC input clock equal 125KHz
}
eess9 писал(а):
Второе:
Код: Выделить всё
while(ADCSRA & (1 << ADSC)); //wait for conversation complete
может надо флаг конца преобразования ждать

?:
Код: Выделить всё
while(!(ADCSRA & (1 << ADIF))) ;
ADCSRA |= (1 << ADIF);
Попробовал Ваш вариант с ADIF и свой без. Разницы в работе не заметил.
Если смотреть в даташит, то увидим, что:
ADSC will read as one as long as a conversion is in progress. When the conversion is complete, it returns to zero.
Writing zero to this bit has no effect.
Какие приемущества дает использование конструкции с ожиданием флага прерывания АЦП в режиме однократного преобразования?
Re: ADC и PWM в ATTiny45 - вопросы начинающего
Добавлено: Ср фев 25, 2015 20:51:16
karambas
Следом вопрос про PWM, который с моими настройками тоже отрабатывает со странностями.
Код на wiring:
Код: Выделить всё
void setup() {
// put your setup code here, to run once:
pinMode(0, OUTPUT);
pinMode(4, INPUT);
digitalWrite(4, HIGH);
}
void loop() {
// put your main code here, to run repeatedly:
if(digitalRead(4) == LOW){
analogWrite(0,50);
}else{
analogWrite(0,0);
}
}
Отрабатывает корректно.
Код на AVR-GCC:
Код: Выделить всё
#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
#define setHIGH(pin) PORTB |= (1 << pin)
#define setLOW(pin) PORTB &= ~(1 << pin)
#define setPWM(pin, dutycycle) pin = dutycycle
#define isHIGH(pin) PINB & (1 << pin)
#define isLOW(pin) !(PINB & (1 << pin))
//aliases for pins
#define ledPin OCR0A //PB0 led is using PWM
#define buttonPin PB4 //PB4 on the button
void initIO(){
DDRB =
(1 << PB0) | //set OUTPUT
PORTB =
(1 << PB4); //switch ON pull-up resistor on PB4
}
void initPWM(){
TCCR0A =
(0 << COM0A0) | //bit COM0A0 remains clear and
(1 << COM0A1) | //set bit COM0A1, which (in Fast PWM mode) clears OC0A on compare-match, and sets OC0A at BOTTOM
(1 << WGM00) | //set bits WGM00 and WGM01, which (when combined with WGM02
(1 << WGM01); //from TCCR0B below) enables Fast PWM mode
TCCR0B =
(0 << WGM02) | //bit WGM02 remains clear, which (when combined with WGM00 and WGM01 from TCCR0A above) enables Fast PWM mode
(1 << CS00); //set bits CS01 (leaving CS01 and CS02 clear), which tells Timer/Counter-0 to not use a prescalar
}
int main(void){
initIO(); //setup Input/Output system
initPWM(); //setup PWM
while(1){
if(isLOW(buttonPin)){
setPWM(ledPin, 50);
}else{
setPWM(ledPin, 0);
}
}
return 0;
}
тоже отрабатывает корректно, однако сразу же после включения питания на PB0 появляется паразитное напряжение, которое едва заметно зажигает светодиод. Это гасится включением между PB0 и землей керамического конденсатора на 0.1uF, однако после заливки в МК скетча из Ардуино такого эффекта не возникает.
Re: ADC в ATTiny45 - запутался
Добавлено: Ср фев 25, 2015 22:29:14
Morroc
karambas писал(а):Возился, возился, заставил заработать, сбросив бит ADLAR. В связи с этим вопрос: практически заучив даташит, все же не могу понять предназначение бита ADLAR регистра ADMUX в практическом смысле. В даташите говорится, что:
выставляя бит ADLAR мы устанавливаем порядок записи результата преобразования в регистры ADCL и ADCH; когда ADLAR = 1 два младших бита пишутся в ADCL, остальные в ADCH, когда ADLAR = 0, два старших байта в ADCH, остальные в ADCL.
вы учите даташит на русском ?
The ADC generates a 10-bit result which is presented in the ADC Data Registers, ADCH and ADCL. By default, the result is presented right adjusted, but can optionally be presented left adjusted by setting the ADLAR bit in ADMUX.
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, to ensure that the content of the Data Registers belongs to the same conversion.
Re: ADC и PWM в ATTiny45 - вопросы начинающего
Добавлено: Ср фев 25, 2015 23:21:38
karambas
Morroc писал(а):вы учите даташит на русском ?
Спецификация на английском. Я своими словами описал процесс. Вот диаграмма регистров:

Подскажите, пожалуйста, правильный принцип использования бита ADLAR в ADMUX.
C PWM, к слову, разобрался. Порывшись достаточное время в сети, наткнулся на данный ресурс:
http://letsmakerobots.com/content/robot ... on-journal описывающий подобное поведение МК.
Изменил немного макрос setPWM и функцию initPWM():
Код: Выделить всё
#define setPWM(pin, dutycycle) \
if (dutycycle > 0){ \
TCCR0A |= (1 << COM0A1); \
pin = dutycycle; \
}else{ \
TCCR0A &= ~(1 << COM0A1); \
}
void initPWM(){
TCCR0A =
(0 << COM0A0) | //bits COM0A0 remains clear and COM0A1 should be set as 1, which (in Fast PWM mode) clears OC0A
(0 << COM0A1) | //on compare-match, and sets OC0A at BOTTOM; bit COM0A1 will be set later by setPWM macro in code
(1 << WGM00) |
(1 << WGM01); //set bits WGM00 and WGM01, which (when combined with WGM02 from TCCR0B below) enables Fast PWM mode
TCCR0B =
(0 << WGM02) | //bit WGM02 remains clear, which (when combined with WGM00 and WGM01 from TCCR0A above) enables Fast PWM mode
(1 << CS00); //set bits CS01 (leaving CS01 and CS02 clear), which tells Timer/Counter-0 to not use a prescalar
}
И ШИМ заработал должным образом.
Re: ADC и PWM в ATTiny45 - вопросы начинающего
Добавлено: Чт фев 26, 2015 07:24:10
Engineer_Keen
karambas писал(а):Подскажите, пожалуйста, правильный принцип использования бита ADLAR в ADMUX.
Если вам нужно все 10 бит, то ADLAR не используете (=0), читаете ADCL и ADCH (или сразу ADCW, если это не ассемблер) в 16-битную переменную. Если вам нужно только 8 бит, ставите ADLAR и читаете только ADCH (или ADCW и сдвигаете вправо на 8 бит) в 8-битную переменную.
Re: ADC и PWM в ATTiny45 - вопросы начинающего
Добавлено: Чт фев 26, 2015 16:11:53
karambas
Еще такой вопрос. При написании кода на С и компиляции его с помощью AVR-GCC посредством CodeBlocks не верно задается частота тактирования МК.
Например я выставляю фьюзы на 1MHz для ATTiny45: extended fuse: 0xFF high fuse:0xDF low fuse:0x62 ? что устанавливает делитель 8 для частоты в 8МHz, на выходе должен получиться 1MHz. В коде пишем #define F_CPU 1000000UL для _delay_ms(), однако при создании мигалки на светодиоде необходимая задержка не выполняется.
Если исходник скопировать в среду Arduino IDE, то все билдится должным образом и светодиод мигает с заданным интервалом.
Какие я не учитываю настройки в проекте? Заранее всем спасибо.
Re: ADC и PWM в ATTiny45 - вопросы начинающего
Добавлено: Чт фев 26, 2015 16:20:22
eess9
По умолчанию в delay.h стоит 10МГц. Убедитесь что дефайн у вас раньше библиотеки:
Код: Выделить всё
#define F_CPU 1000000UL
#include <util/delay.h>
Это гарантирует, что библиотека вашу частоту "подхватит" правильно. В противном случае будет warning, который должен быть у вас в окне Error List:
"F_CPU not defined for <util/delay.h>"
Посмотрите внимательно, скорей всего, он у вас есть.
Re: ADC и PWM в ATTiny45 - вопросы начинающего
Добавлено: Чт фев 26, 2015 16:22:40
karambas
Нет, все корректно.
Код: Выделить всё
#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
Однако тактируется не корректно. Я все же думаю, что в параметрах сборки что-нибудь не учитываю, но отсутствие опыта не дает понять, что именно.
Re: ADC и PWM в ATTiny45 - вопросы начинающего
Добавлено: Чт фев 26, 2015 22:09:43
karambas
Проверил работу avrdude с разными файлами конфига. HEX файл, собранный в Arduino IDE корректно работает с тактированием, файл, собранный в C::B некорректно. Устал гуглить на эту тему, не могу сообразить, что в опциях сборщика указано не корректно или не указано. Вот логи сборщиков.
Arduino IDE:
Код: Выделить всё
/opt/arduino-1.0.6/hardware/tools/avr/bin/avr-g++ -c -g -Os -Wall -fno-exceptions -ffunction-sections -fdata-sections -mmcu=attiny45 -DF_CPU=1000000L -MMD -DUSB_VID=null -DUSB_PID=null -DARDUINO=106 -I/opt/arduino-1.0.6/hardware/arduino/cores/arduino -I/home/me/sketchbook/hardware/attiny/variants/tiny8 /tmp/build6896905435354427440.tmp/TestFCPU.cpp -o /tmp/build6896905435354427440.tmp/TestFCPU.cpp.o
...
opt/arduino-1.0.6/hardware/tools/avr/bin/avr-gcc -Os -Wl,--gc-sections -mmcu=attiny45 -o /tmp/build6896905435354427440.tmp/TestFCPU.cpp.elf /tmp/build6896905435354427440.tmp/TestFCPU.cpp.o /tmp/build6896905435354427440.tmp/core.a -L/tmp/build6896905435354427440.tmp -lm
/opt/arduino-1.0.6/hardware/tools/avr/bin/avr-objcopy -O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0 /tmp/build6896905435354427440.tmp/TestFCPU.cpp.elf /tmp/build6896905435354427440.tmp/TestFCPU.cpp.eep
/opt/arduino-1.0.6/hardware/tools/avr/bin/avr-objcopy -O ihex -R .eeprom /tmp/build6896905435354427440.tmp/TestFCPU.cpp.elf /tmp/build6896905435354427440.tmp/TestFCPU.cpp.hex
Binary sketch size: 132 bytes (of a 4,096 byte maximum)
Code::Blocks:
Код: Выделить всё
avr-gcc -mmcu=attiny45 -Os -Wall -DF_CPU=1000000L -Os -I/usr/include -c main.c -o obj/Release/main.o
avr-g++ -L/usr/lib -o bin/Release/TestFCPU.elf obj/Release/main.o -mmcu=attiny45
Output file is bin/Release/TestFCPU.elf with size 3,66 KB
Running project post-build steps
avr-size bin/Release/TestFCPU.elf
text data bss dec hex filename
82 0 0 82 52 bin/Release/TestFCPU.elf
avr-objcopy -R .eeprom -R .fuse -R .lock -R .signature -O ihex bin/Release/TestFCPU.elf bin/Release/TestFCPU.hex
avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex bin/Release/TestFCPU.elf bin/Release/TestFCPU.eep
Re: ADC и PWM в ATTiny45 - вопросы начинающего
Добавлено: Пт фев 27, 2015 18:33:06
karambas
Решил проблему. Заключалась она в дефолтных настройках компилятора Code::Blocks.
В Settings => Compiler... изменил:
- Linker for dynamic libs в Toolchain executables с изначального "avr-g++" на "avr-gcc":

- Search directories для Linker и Compiler с "/usr/include" на "/usr/lib/avr/include" и с "/usr/lib" на "/usr/lib/avr/lib":

Всем спасибо за помощь. Пойду дальше постигать многогранные коротконожки.