Проблема с реализацией двухканального АЦП-ШИМ ATTINY13

Павел74
Offline
Зарегистрирован: 27.03.2019

Уважаемые знатоки, помогите с изучением проблемы: реализую на ATTINY13  два канала А и Б.

 два потенциометра( переменные резисторы) - для входа ( ножка 2 и 3) 

на выходах - ножка 5 и 6 - светодиоды



 пробую управлять яркостью в каждом канале : к примеру ножка 2 вход АЦП- ножка 6 выход ШИМ и аналогично 3 и 5 

но одновременно работает 1 канал ( второй, чтоб не мешал , я закоментил),

пробовал в прерывании,   по окончанию преобразования,  инвертировать бит ( биты) для настройки АЦП ( определение входа) и ШИМ ( определение выхода) ,

пробовал заводить переменную, которая по прерыванию инвертируется  каждый раз " HIGH-LOW-HIGH-LOW..."  и далее в основном цикле выбирал по условию  if (x==HIGH) { АЦП с входа А на ШИМ выход А)   в противном случае else{ АЦП с входа Б на ШИМ выход Б}

не сработало - что делаю не так? 

ниже код ( полурабочий - только один канал на выбор) 

//работает одновременно только один канал - или PB3-OCR0B, или PB4-OCR0A. пока код использует 130 байт (12%) в ATTINY13//
#include <avr/io.h> 
#include <avr/interrupt.h>     
#include <util/delay.h> // завел но не использовал
int n=HIGH;
unsigned int u; //пока не использовал
unsigned int w; //не придумал как в прерывании последовальельно "разносить" по выходам результат АЦП-ШИМ
SIGNAL(ADC_vect) 
//SIGNAL(SIG_ADC) на выбор два - один рабочий для attiny13, другой для attiny2313
{  OCR0A = ADCH;   //OCR0B = ADCH для второго канала;
   PORTB ^=0b00000100; //PORTB ^=0b00000100 для второго канала ;подтягивающий резистор на входе  
 }
//void setup() {
  
  // ничего не писал - проверил однократные установки входов - и выходов - НЕ РАБОТАЮТ, функционируют только в теле основной программы 
//}
int main(void)               
{ 
DDRB |= (0 << 4); // вход PB4
DDRB |= (0 << 3); // вход PB3
DDRB |= (1 << 0); // выход PB0
DDRB |= (1 << 1); // выход PB1
   ADMUX  = 0b00100010; //АЦП на входе PB4
   ADCSRA = 0b11101111;//запускаем АЦП, разрешаем прерывания
   DIDR0  = 0b00010000; //запрет на цифровой вход 
   TCCR0A = 0b11000001; //настройка выхода ШИМ OCR0A
   TCCR0B = 0b00000011; // выбор делителя
   
   
  //ADMUX  = 0b00100011;  //АЦП на входе PB3
  //ADCSRA = 0b11101111;//запускаем АЦП, разрешаем прерывания
  //DIDR0  = 0b00001000;  //запрет на цифровой вход 
  //TCCR0A = 0b00110001; //настройка выхода ШИМ OCR0B 
  //TCCR0B = 0b00000011; // выбор делителя
  
       

   sei();
   while (1) {}           
}

 

nik182
Offline
Зарегистрирован: 04.05.2015

Круто! Только не все строчки делают то, что Вы им приписываете. Не проще поставить ядро https://github.com/MCUdude/MicroCore и сделать всё средствами ардуино? Или хотя бы посмотреть в файлах ядра как правильно инициализировать периферию под Вашу задачу?

Павел74
Offline
Зарегистрирован: 27.03.2019

А что именно не так? скажите - если не сложно - я проверю ( возможно менял строки и комментарии оставил старые - код менял неоднократно) а по поводу средств ардуино IDE  - можно , но я начитался даташита и примеров - так вроде даже проще  - в нужном регистре биты выствил и работает ( магия!) 

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Если (магия) работает - вопросов бы не возникло. Значит не работает магия.

А вообще в коде (лично я, со своим скудоумием) только дичь вижу. Почему? Потому что сишный код, какие то действия делает, но... Но что вы хотите увидеть в действии от программы (с регуляторами всякмими там), которая просто висит в вечном цикле ничего при этом не делая?

Павел74
Offline
Зарегистрирован: 27.03.2019

так то конечно не наглядно выходит при чтении, но функционирует

, в ардуине например , я иногда в цикле делал дополнительную функцию - мигалку какую нибудь , чтоб определять, что цикл проходит, а тут просто по факту - меняется яркость свечения и даже осциллограф использовал - полюбоваться  ШИМ сигналом - ну посмотрел форму, хотя и по светодиоду понятно, что он есть. Мне сейчас нужно алгоритм правильный найти , чтоб по прерыванию ( в данном случае - когда АЦП закончит преобразование, а возможно по таймеру) в программе "перекидывались бы каналы: последовательно "принял на входе -преобразовал - выдан ШИМ в нагрузку одноименного канала  ....И переключился на второй канал"

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Я конечно же новичек на форуме. Но не понимаю вообще ничего из вышеописанного. Как может работать код с миганиями разными, если в коде написано - «сделай что то один раз и крутись в вечном цикле»? Прерывания могут работать. Но их работу тут я тоже не вижу (код) или не умею так глубоко его читать (поясните). 

Павел74
Offline
Зарегистрирован: 27.03.2019

К примеру в  строке 26 и 27  два регистра, по даташиту смотрим - какой бит за что отвечает.

TCCR0A -  "1" в определенном бите :  в 1100 0000 - выход на OCR0A  - это ножка 5 dip8, а 00110000  - ножка 6 dip8  - если инвертировать эти биты - то "перекидываем" выход  с OCR0A  на OCR0B.

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Круто. Но это один раз до вечного цикла. А далее либо прерывание по таймеру должно быть и менять состояния или в цикле. Ни того ни другого не вижу. Поясните. 

Павел74
Offline
Зарегистрирован: 27.03.2019

Вот тут и проблема: работает один канал  - прерывание ниже - в нем на выход OCR0A записываем результат из регистра хранящего данные  после обработки АЦП - это регистр ADCL|ADCH  - нам интересен младший байт ADCH -  запись результата происходит по прерыванию "окончание цикла преобразования" , чтоб получить не промежуточный результат на выходе, а финальное значение.

08 SIGNAL(ADC_vect)
09 //SIGNAL(SIG_ADC) на выбор два - один рабочий для attiny13, другой для attiny2313
10 {  OCR0A = ADCH;   //OCR0B = ADCH для второго канала;
11    PORTB ^=0b00000100; //PORTB ^=0b00000100 для второго канала ;подтягивающий резистор на входе 
12

 }

в строках 30-34 натроен втрой канал - он у меня работает - если закоментить строки 23-27 , но нужно еще местами променять команду и комментарии в строках 10 и 11   

В результате мы будем принимать сигнал с другого входа и управлять нагрузкой на другом выходе : был канал вход PB3 и выход OCR0A, а перекидываем на вход PB4 - выход OCR0B.

Павел74
Offline
Зарегистрирован: 27.03.2019

как прерывание по таймеру реализовать, чтоб не прервать цикл обработки , по окончании которого я записываю результат на выход - в нагрузку?

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Мне кажется по прерываниям такое реализовать маловероятно. Нельзя прерыванием прервать прерывание (или выполнить что то ещё во время прерывания). Или я не до конца понял вопрос. 

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

BOOM пишет:
Нельзя прерыванием прервать прерывание (или выполнить что то ещё во время прерывания). Или я не до конца понял вопрос. 

Прерывания можно прерывать прерываниями и логику работы можно (в некоторых случаях) полностью реализовать внутри прерываний. Но работа с прерываниями требует более глубокого понимания работы микроконтроллера и двумя строчками кода, как надеется ТС, тут не обойдешься.

Павел74
Offline
Зарегистрирован: 27.03.2019

Хорошо, пусть не двумя строками, а какой алгоритм можете рекомендовать .По главному прерыванию понятно - закончился процесс в АЦП - передали данные в регистр, далее повторяется цикл, вот как мне его перенастроить на второй канал?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Павел74, как-то так:

// ADC2/3(PB4/3) - analog input;  PB0/1 -output
#include <avr/io.h>
#include <avr/interrupt.h>
int main(void){
	DDRB=(1<<DDB1)|(1<<DDB0);
	ADMUX= (1<<ADLAR)|(1<<MUX1);// REF=Vcc, adc2(pb4)=default input
	ADCSRA = (1<<ADEN)|(1<<ADSC)|(1<<ADATE)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);//запускаем АЦП, разрешаем прерывания
	ADCSRB=0;
	DIDR0  =(1<<ADC2D)|(1<<ADC3D); //запрет на цифровой вход
	TCCR0A = (1<<COM0A1)|(1<<COM0A0)|(1<<COM0B1)|(1<<COM0B0)|(1<<WGM00); //PWM(PC) Mode.
	TCCR0B = (1<<CS01)|(1<<CS00);// clock /64   (частота pwm= 9E6/64/256/2=293Hz)
	sei();
while(1);
}

ISR(ADC_vect) { ADMUX&1 ? (OCR0A = ADCH) : (OCR0B = ADCH); ADMUX^=1<<0; }


Павел74
Offline
Зарегистрирован: 27.03.2019

Вы волшебник, нет правильнее так ВОЛШЕБНИК. В прерывании так изящно расписано

ISR(ADC_vect) { ADMUX&1 ? (OCR0A = ADCH) : (OCR0B = ADCH); ADMUX^=1<<0; }

- когда я  такое самостоятельно осилю - главное, что читается все понято!

видно , как вход поменяли, я пробовал  - но просто проводил инверсию бита в прерывании - не сработало.

спасибо!

 

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Мда..... красиво, но мне ардуиновый код понять для начала )

Павел74
Offline
Зарегистрирован: 27.03.2019

Ардуиновский  почти как бейсик в школе : слова  и команды почти на "человесеском языке", а тут вроде одни цифры - но это проще , выставил код в регистре и получил нужную настройку функций процессора.

Код,  который мне DIMAX  предоставил, за что ему огромная благодарность, позволяет внутри цикла прерывания добавить дополнительное преобразование сигнала, что я и сделал для эксперимента

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Понятно, что чем код «ближе процессору» тем он компактнее и более производительный. Жалко, что с развитием многие об этом забывают. У меня браузер файрфокс жрет почти гиг памяти при 10 вкладках. Когда то на 256мб озу и ос крутилась и браузер столько не жрал. (Может я и ошибаюсь)

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

BOOM пишет:

Мда..... красиво, но мне ардуиновый код понять для начала )

Ну кстати "ардуиновским" его сложно назвать, я даже писал этот код не в arduino ide, а в атмел студии :)

Павел74
Offline
Зарегистрирован: 27.03.2019

У меня корректно только IDE ардуиновкая установлена, поставил AVR Studio 4  - пока не разобрался с программой, в окружении рядом никого нет, что б подсмотреть работу и освоить.

Павел74
Offline
Зарегистрирован: 27.03.2019

DIMAX, добрый вечер!

я продолжаю эксперименты с Attiny13 - делал разные примеры для прерываниям по различным событиям , намигался всякими светодиодами, проверял код, а точнее значение переменной по количеству вспышек .

И вот в очередном эксперименте вернулся к нашей старой теме, которую вы помогли мне освоить: двухканальный шим,  в тюни13 из можно реализовать 4!!!, это здорово, но мне пока хватает двух, а вот выходов на управление уже недостает.  Задачи реализовать задуманное любой ценой не стоиит - это больше вопрос освоения новых знаний- задействовать процессор с ограниченным колич выводов - так интереснее ( иначе бы продолжал на ардуинке) .

 

На сей момент задумал задействовать два входа АЦП : по одному контролирую параметр, а вторым вношу корректировку порога сравнения этого параметра.

решил использовать Вашу подсказку со сменой входа ацп в прерывании по окончанию преобразования:  

ISR(ADC_vect) {
ADMUX&1 ? (OCR0A = ADCH) : (OCR0B = ADCH); ADMUX^=1<<0; }

 ранее в своей программке я добавил небольшую обработку значения ADCH и она работает, корректируя выходное значение:



ISR(ADC_vect) {unsigned int x;
if (ADCH>=141) x=2*ADCH+2020;
else if (ADCH>=77) x=5*ADCH+1600;
else if (ADCH>=42) x=9*ADCH+1330;
else if (ADCH>=26) x=15*ADCH+1110;
else if (ADCH>=13) x=40*ADCH+600;
else if (ADCH>0) x=160*ADCH+50;
if (ADCH==0) x=0;
ADMUX&1 ? (OCR0A = x/10) : (OCR0B = x/10); ADMUX^=1<<0; }

Тогда была задача вывести эти значения на стрелочный индикатор, а сейчас выходы ШИМ мне не нужен. 

Как можно хранить промежуточные значения преобразований каждого канала АЦП, чтоб по сравнению с пороговым значением выставлять "1" или "0" на выходах PB1 или PB0?

Ниже полностью Ваш код - только схему обвязки поменял - есть 1 аналоговый датчик и 1 потенциометр, задающий значение:

#include <avr/io.h>
#include <avr/interrupt.h>
int main(void){
  DDRB=(0<<DDB4)|(1<<DDB0)|(1<<DDB1)|(0<<DDB3);//входы 3 -4  и выходы 0-1
  ADMUX= (0<<REFS0)|(1<<ADLAR)|(1<<MUX1)|(1<<MUX0);// REF=5.0 Vcc, adc3(pb3)=default input
  ADCSRA = (1<<ADEN)|(1<<ADSC)|(1<<ADATE)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);//запускаем АЦП, разрешаем прерывания
  ADCSRB=0;
  DIDR0  =(1<<ADC2D)|(1<<ADC3D); //запрет на цифровой вход
  TCCR0A = (1<<COM0A1)|(1<<COM0A0)|(1<<COM0B1)|(1<<COM0B0)|(1<<WGM00); //PWM(PC) Mode.
  TCCR0B = (0<<CS02)|(1<<CS01)|(0<<CS00);// clock /8   
  sei();
while(1);
}

ISR(ADC_vect) {int a=OCR0B;
  ADMUX&1 ? (OCR0B=ADCH) : (OCR0A = ADCH-a/2); ADMUX^=1<<0; } // канал А для изменения, канал В для коррекции

Компиляция без ошибок, код залился , работает следующим образом:

контролируемый параметр могу принудительно менять в некотором диапазоне, это видно на выходе PB0 - ШИМ светодиода ( можно задать прямой или инверсный), ручной регулятора потенциометра смещаю точку задания сравнения и изменение наблюдаю на втором канале - PB1 ШИМ светодиода.

Напрашивалось редактирование строки :

int a=OCR0B; 

(OCR0B=ADCH) : 

в 

int a;
  ADMUX&1 ? ( a=ADCH) : 

но так параметр не работает.

Как обойтись без OCR0A   или OCR0B ?

В этом и увяз - как мне высвободить PB1 или PB0 под управление исполнительным элементом, а один ШИМ в принципе для индикации можно оставить.

 

 

Павел74
Offline
Зарегистрирован: 27.03.2019

Или единственная возможность - запись во флеш память?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Павел74, два раза прочитал, но так и не понял, что вам нужно.

Для упрощения действия с каналами можно развернуть в обычный if/else

ISR(ADC_vect) {
if (ADMUX&1) { какие-то действия для одного канала}
else { какие-то действия для другого канала } 
ADMUX^=1<<0; 
}

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

dimax пишет:

Павел74, два раза прочитал, но так и не понял, что вам нужно.

Для упрощения действия с каналами можно развернуть в обычный if/else

ISR(ADC_vect) {
if (ADMUX&1) { какие-то действия для одного канала}
else { какие-то действия для другого канала } 
ADMUX^=1<<0; 
}

высвободить ноги по видимому хотел )))

Павел74
Offline
Зарегистрирован: 27.03.2019

Краткая запись тоже очень удобна - но я попробую развернутую версию обязательно попробую прописать нужными командами.

Сейчас проблема исключительно для 8pin корпуса - мало ножек и заняв   OCR0A   и     OCR0B свободных не остается  а  у меня есть задача не занимая этих выводов PB0 и PB1 под ШИМ, сохранять ГДЕ-ТО  временные значения ADCH для некоторых арифметических операция и последующего сравнения, по результатам которого выдавать управляющий сигнал на PB1 или PB0

а в коде я обращаюсь к OCR0A и OCR0B 

вот тут я остановился

 

Павел74
Offline
Зарегистрирован: 27.03.2019

именно - освободить ноги, не выводя ADCH на эти выводы , а сигналы обрабатывать программно

ELITE
ELITE аватар
Offline
Зарегистрирован: 11.01.2018

может я не совсем прав - но разве у тини13 не ОДИН АЦП? который просто можно 2 ноги по очереди читать?... тоетсь одновременно без перенастройки МК не получится ведь считывать аналоговый сигнал....

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

ELITE пишет:

может я не совсем прав - но разве у тини13 не ОДИН АЦП? который просто можно 2 ноги по очереди читать?... тоетсь одновременно без перенастройки МК не получится ведь считывать аналоговый сигнал....

Да практически у всех атмелов и многих других микроконтроллерах АЦП один. Плюс аналоговый коммутатор который позволяет считывать сигнал с разных ножек, но, естественно, не одновременно.

Павел74
Offline
Зарегистрирован: 27.03.2019

Именно так и делается : срабатывает прерывание настроенное на окончание преобразования, результат преобразования из АЦП заносится в рег сравнения OCR0A ( или OCR0B)  а далее переключается вход, соответственно  меняется регистр хранения результата преобразования

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

Павел74 пишет:

Именно так и делается : срабатывает прерывание настроенное на окончание преобразования, результат преобразования из АЦП заносится в рег сравнения OCR0A ( или OCR0B)  а далее переключается вход, соответственно  меняется регистр хранения результата преобразования

 

Все смешалось в доме обломских :) .  ОСR0A/B это регистры совпадения для таймера. К АЦП они никаким боком не отностятся.

Павел74
Offline
Зарегистрирован: 27.03.2019
ISR(ADC_vect) {int a=OCR0B;
  ADMUX&1 ? (OCR0B=ADCH) : (OCR0A = ADCH-a/2); ADMUX^=1<<0; } 

 

именно для того и использовал - в верхней части темы было , это данные для управления шщим на выходах

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

Да, я просто #28 не так  понял. 

А в чем проблема с "сохранить ГДЕ-ТО  временные значения ADCH для некоторых арифметических операция и последующего сравнения" ? Памяти не хватает?