Страница 1 из 2

Фотодатчик на Tiny13

Добавлено: Пн дек 07, 2015 18:04:34
seagull72
Доброго времени суток.

Это моя первая попытка разобраться в микроконтроллерах, так что прошу сильно не пинать, а, наоборот, помочь по возможности.

Итак. Attiny13. Фотодатчик. Алгоритм такой - АЦП снимает напряжение и сравнивает его с двумя пороговыми (заданными в программе). Если напряжение превысит верхний пороговый уровень, то на выходе МК - лог. 1, если напряжение снизится ниже нижнего порогового уровня, то МК ждет 30 сек и еще раз проводит измерение напряжения, если оно и после этого ниже нижнего уровня - то на выходе лог. 0.

Программу писал в Atmel Studio 7, на С. Компилятор ошибок не показал. Загнал программу в Proteus - на выходе постоянно лог. 1.

Сам, ессно, буду разбираться и искать, но если кто подскажет - буду очень признателен. И еще, подскажите, что можно почитать про C для МК AVR. Есть Прокопенко и Белов.

Текст программы:

/*
* project photocell.c
*
* Created: 06.12.2015 15:30:58
* Author : seagull
*/

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


int main(void)
{

// Конфигурирование порта B PORT4 (нога 3) на выход
DDRB=(0<<DDB5) | (1<<DDB4) | (0<<DDB3) | (0<<DDB2) | (0<<DDB1) | (0<<DDB0);
PORTB=(0<<PORTB5) | (1<<PORTB4) | (0<<PORTB3) | (0<<PORTB2) | (0<<PORTB1) | (0<<PORTB0);

// Отключение компаратора
ACSR=(1<<ACD) | (0<<ACBG) | (0<<ACO) | (0<<ACI) | (0<<ACIE) | (0<<ACIS1) | (0<<ACIS0);

// Отключение таймера
TCCR0A=(0<<COM0A1) | (0<<COM0A0) | (0<<COM0B1) | (0<<COM0B0) | (0<<WGM01) | (0<<WGM00);
TCCR0B=(0<<WGM02) | (0<<CS02) | (0<<CS01) | (0<<CS00);
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;

// Запрет прерываний
GIMSK=(0<<INT0) | (0<<PCIE);
MCUCR=(0<<ISC01) | (0<<ISC00);
TIMSK0=(0<<OCIE0B) | (0<<OCIE0A) | (0<<TOIE0);

// Конфигурирование АЦП
ADCSRA=(1<<ADEN) | (0<<ADSC) | (0<<ADATE) | (0<<ADIF) | (0<<ADIE) | (1<<ADPS2) | (1<<ADPS1) | (0<<ADPS0);
ADCSRB=(0<<ADTS2) | (0<<ADTS1) | (0<<ADTS0);
ADMUX=(1<<REFS0) | (1<<ADLAR) |(0<<MUX0)| (0<<MUX1); // Смещение результата влево, Аналоговый сигнал на PB5

unsigned int pcon; // Порог включения
unsigned int pcoff; // Порог выключения
unsigned int tdelay; // Время задержки
char temp; // Стартовое положение
unsigned int vv; // Измереное напряжение
char v_temp;
unsigned int v;

pcon=4.9/5*1024; // Задать порог включения
pcoff=4.4/5*1024; // Задать порог выключения
tdelay=30000; // Задать время задержки
temp=1; // Датчик включен
v_temp=1;

_delay_us(50);

while (1)
{
ADCSRA|=(1<<ADSC); //Начать преобразование
while (!(ADCSRA & (1<<5)));
// loop_until_bit_is_set(ADIF); // while ((ADCSRA&_BV(ADIF))==0x00); //Дождаться окончания преобразования
v=ADCH;
ADCSRA|=(0<<ADSC);
ADCSRA|=(0<<ADIF);;
if (vv>=pcon)
{
temp=1;
v_temp=1;
goto link;
}
if (vv<=pcoff && temp==1)
{
temp=0;
_delay_ms(tdelay);
goto link;
}
if (vv<=pcoff && temp==0)
{v_temp=0;
goto link;
}
link:
PORTB|=(v_temp<<PORTB4);
}
}

И сама программа:

Re: Фотодатчик на Tiny13

Добавлено: Пн дек 07, 2015 23:33:31
GRAF
Проверяйте, я уже спать хочу.

Код: Выделить всё

/*
 * project photocell.c
 *
 * Created: 06.12.2015 15:30:58
 * Author : seagull
 */ 

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


int main(void)
{

// Конфигурирование порта B PORT4 (нога 3) на выход
DDRB=(0<<DDB5) | (1<<DDB4) | (0<<DDB3) | (0<<DDB2) | (0<<DDB1) | (0<<DDB0);
PORTB=(0<<PORTB5) | (1<<PORTB4) | (0<<PORTB3) | (0<<PORTB2) | (0<<PORTB1) | (0<<PORTB0);

// Отключение компаратора
ACSR=(1<<ACD);


// Запрет прерываний
GIMSK=(0<<INT0) | (0<<PCIE);
MCUCR=(0<<ISC01) | (0<<ISC00);
TIMSK0=(0<<OCIE0B) | (0<<OCIE0A) | (0<<TOIE0);

// Конфигурирование АЦП
ADCSRA=(1<<ADEN) | (0<<ADSC) | (0<<ADATE) | (0<<ADIF) | (0<<ADIE) | (1<<ADPS2) | (1<<ADPS1) | (0<<ADPS0);
ADCSRB=(0<<ADTS2) | (0<<ADTS1) | (0<<ADTS0);
ADMUX=(1<<REFS0) | (1<<ADLAR) |(0<<MUX0)| (0<<MUX1); // Смещение результата влево, Аналоговый сигнал на PB5

    unsigned char pcon; // Порог включения
    unsigned char pcoff; // Порог выключения
    unsigned int tdelay; // Время задержки
	bit flag=0;
	unsigned char v;
		
	pcon=0xFB;	//4.9*256/5; // Задать порог включения
	pcoff=0xE1;	//4.4*256/5; // Задать порог выключения
	tdelay=30000; // Задать время задержки
	temp=1; // Датчик включен
	
	PORTB|=(1<<4);
	_delay_us(50);
	
	while (1) 
    {
	ADCSRA|=(1<<ADSC);	//Начать преобразование
	while (!(ADCSRA & (1<<5)));
	// loop_until_bit_is_set(ADIF); // while ((ADCSRA&_BV(ADIF))==0x00); //Дождаться окончания преобразования
	v=ADCH;

	if (v>=pcon)
	{
		PORTB|=(1<<4);
	} 
	else if (v<=pcoff)
	{
		if(flag==1)
		{
			PORTB&=~(1<<4);
			flag=0;
		}
		else
		{
			_delay_ms(tdelay);
			flag=1;
		}
	}
	}
}


Re: Фотодатчик на Tiny13

Добавлено: Вт дек 08, 2015 00:35:44
Аlex
GRAF писал(а):

Код: Выделить всё

		if(flag==1)
		{
			PORTB&=~(1<<4);
			flag=0;
		}
		else
		{
			_delay_ms(tdelay);
			flag=1;
		}
После задержки, на следующем входе, попадаем в if, затем снова в else, там задержка, и так по-кругу... При условии "v<=pcoff" программа будет всегда находиться в 30-ти секундной задержке. Это чревато запозданием (макс. = 30 сек.) срабатывания условия "v>=pcon".
Можно добавить ещё 1 флаг, и по нему запрещать повторное вхождение в else с задержкой, а разрешать только после перехода значения выше верхней уставки. Короче, программный триггер надо сделать :)

Re: Фотодатчик на Tiny13

Добавлено: Вт дек 08, 2015 09:11:59
seagull72
GRAF писал(а):Проверяйте, я уже спать хочу.
Спасибо, вечером проверю, на работе, боюсь, некогда будет.
Аlex писал(а): Можно добавить ещё 1 флаг, и по нему запрещать повторное вхождение в else с задержкой, а разрешать только после перехода значения выше верхней уставки. Короче, программный триггер надо сделать :)
И это посмотрю. Спасибо.

P.S. Черт побери, а ведь когда-то неплохо программировал. Правда не на С. И было это лет 20 назад.

Re: Фотодатчик на Tiny13

Добавлено: Вт дек 08, 2015 11:55:29
GRAF
Если лет 20 назад программировали, то, наверное, любую программу начинали с блок-схемы? Она очень помогает построить нужную логику работы.

Re: Фотодатчик на Tiny13

Добавлено: Вт дек 08, 2015 13:42:39
seagull72
У меня сейчас главная проблема не в блок-схеме. С битовыми операциями в СИ разобраться. Записи типа PORTB|=(1<<PORTB1) пока сильно пугают, каждый раз в шпаргалку смотреть приходится. :shock:

P.S.
GRAF писал(а):Проверяйте, я уже спать хочу.
Проверил. Не работает.
Компилятор выругался "unknown type name 'bit', заменил тип на char, тогда откомпилировалось. В Протеус загнал - не работает, постоянный лог. 1 на выходе.
Может я чего не то делаю в atmel studio?

Re: Фотодатчик на Tiny13

Добавлено: Ср дек 09, 2015 07:50:33
ddimochka
Строчка
pcon=4.9/5*1024; // Задать порог включения

даст значение 0,0000957. Если его записать в переменную типа unsigned int, то запишется 0. Соответственно результат преобразования всегда будет выше значения pcon.
Попробуйте скобки поставить

pcon=4.9/(5*1024); // Задать порог включения

Re: Фотодатчик на Tiny13

Добавлено: Ср дек 09, 2015 07:55:57
ddimochka
В примере присланном GRAF результат помоему тоже всегда будет больше pcon, т.к. переменная имеет 8 бит, а результат измерения 10 и результат сдвигается влево.

Re: Фотодатчик на Tiny13

Добавлено: Ср дек 09, 2015 10:26:05
ARV
seagull72 писал(а):Алгоритм такой - АЦП снимает напряжение и сравнивает его с двумя пороговыми (заданными в программе). Если напряжение превысит верхний пороговый уровень, то на выходе МК - лог. 1, если напряжение снизится ниже нижнего порогового уровня, то МК ждет 30 сек и еще раз проводит измерение напряжения, если оно и после этого ниже нижнего уровня - то на выходе лог. 0.
попробуем записать алгоритм в виде кода:

Код: Выделить всё

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

// определяем верхний порог (пока просто цифра, не имеющая отношения к сути)
#define HI_LEVEL   1000
// определяем нижний порог (условия те же)
#define LO_LEVEL   100
// определяем задержку в миллисекундах
#define DELAY   30000

static uint16_t get_adc(void); // прототип функции "опроса" датчика
static void initialize(void); // прототип функции инициализации периферии

int main(void){
   int adc; // тут будем хранить значение сигнала с датчика

   initialize(); // проинициализировали периферию

   while(1){
      adc = get_adc();
      if(adc > HI_LEVEL){
         // включаем на нужном порту 1
         continue;
      }
      if(adc < LO_LEVEL){
         // здесь можно проверить состояние порта, чтобы не ждать понапрасну, если там 0
         // если проверка покажет, что в порту 0, просто выполнить снять ремарку с continue;
         // continue;
         _delay_ms(DELAY); // ждем 30 секунд
         if(get_adc() < LO_LEVEL){
            // выключаем нужный порт
         }
      }
   }
}
проверьте: этот код на 100% соответствует заданному алгоритму?

мне кажется - да. поэтому остается совсем чуток: реализовать настройку периферии (вы уже делали, просто скопипастить) и функцию измерения (тож делали, но функцией не оформляли) и под комментарием добавить изменение уровней в порту - я не стал это делать, чтобы не путать вас и себя, это просто, надеюсь.
ну и в заключении сделать все расчеты для значений LO_VALUE и HI_VALUE калькулятором и вставить в соответствующее место значение (это если вы немного плаваете в особенностях работы с константами и выражениями в Си - а вы плаваете, как я понял)

все, задача решена.

чтобы понять, почему так, и как вообще следует поступать в подобных случаях начинающим, рекомендую ознакомиться: https://www.simple-devices.ru/articles/ ... 0-16-40-05

Re: Фотодатчик на Tiny13

Добавлено: Ср дек 09, 2015 16:31:02
YS

Код: Выделить всё

(0<<DDB3)
Сдвигать ноль бессмысленно.

Запись единицы в бит N переменной var:

Код: Выделить всё

var|=(1<<N);
сброс единицы в позиции N переменной var:

Код: Выделить всё

var&=~(1<<N);

Код: Выделить всё

DDRB=(0<<DDB5) | (1<<DDB4) | (0<<DDB3) | (0<<DDB2) | (0<<DDB1) | (0<<DDB0);
Нули при присвоении записывать вообще не надо. Устанавливаем только единицы, а нули записываются сами по себе, т.к. это начальное значение битов формируемой нами константы, если можно так выразиться.

Код: Выделить всё

GIMSK=(0<<INT0) | (0<<PCIE);
MCUCR=(0<<ISC01) | (0<<ISC00);
Соответственно, эти операции эквивалентны простому присвоению нуля в соответствующие регистры. Чтобы убедиться, что в нужных битах ноль, и при этом не затронуть остальные (если вдруг так надо), следует сделать по типу описанного выше:

Код: Выделить всё

GIMSK&=~((1<<INT0) | (1<<PCIE));
Обратите внимание, каким образом записывается операция одновременного сброса нескольких битов.

Код: Выделить всё

unsigned int pcon;
Лучше использовать типы с гарантированной разрядностью из stdint.h, так как разрядность обычного типа int зависит от архитектуры.

Код: Выделить всё

_delay_ms(tdelay);
В функциях семейства _delay_xx() лучше не использовать переменные, эти функции спроектированы под константы в качестве параметра. Хотя в вашем случае компилятор скорее всего оптимизирует код, выкинув переменную tdelay.

Re: Фотодатчик на Tiny13

Добавлено: Ср дек 09, 2015 17:46:41
GRAF
seagull72 писал(а):У меня сейчас главная проблема не в блок-схеме. С битовыми операциями в СИ разобраться. Записи типа PORTB|=(1<<PORTB1) пока сильно пугают, каждый раз в шпаргалку смотреть приходится. :shock:

P.S.
GRAF писал(а):Проверяйте, я уже спать хочу.
Проверил. Не работает.
Компилятор выругался "unknown type name 'bit', заменил тип на char, тогда откомпилировалось. В Протеус загнал - не работает, постоянный лог. 1 на выходе.
Может я чего не то делаю в atmel studio?
Конечно не работает, проверьте строку

Код: Выделить всё

while (!(ADCSRA & (1<<5)));
Идем дальше

Код: Выделить всё

ADMUX=(1<<REFS0) | (1<<ADLAR) |(0<<MUX0)| (0<<MUX1); // Смещение результата влево, Аналоговый сигнал на PB5
Включен внутренний ИОН в качестве опоры для АЦП. Это 1.0-1.2В. У вас делитель на входе АЦП стоит?
ddimochka писал(а):В примере присланном GRAF результат помоему тоже всегда будет больше pcon, т.к. переменная имеет 8 бит, а результат измерения 10 и результат сдвигается влево.
В примере, присланном GRAF, всё верно. Разрядность АЦП 10 бит, но результат сдвинут влево, считывание идет из ADCH. Это 8 бит, посему

Код: Выделить всё

pcon=0xFB;   //4.9*256/5; // Задать порог включения
pcoff=0xE1;   //4.4*256/5; // Задать порог выключения
Считаем сами в калькуляторе и подставляем в HEX, операции с float для тиньки тяжеловаты.

Автор, исправляйте программу по замечаниям. Она работает.

Re: Фотодатчик на Tiny13

Добавлено: Ср дек 09, 2015 19:33:16
seagull72
Спасибо всем ответившим. Проблема, похоже, решена. Теперь буду препарировать программу. Отдельно посмотрю как работает АЦП, потом отдельно if-else, ибо подсказки и шпаргалки хорошо, но надо самому почувствовать и понять.

Re: Фотодатчик на Tiny13

Добавлено: Ср дек 09, 2015 22:02:11
YS
подсказки и шпаргалки хорошо, но надо самому почувствовать и понять.
Неистово плюсую. Очень правильный подход! :beer:

Единственно что могу добавить, так это то, что экспериментировать именно с конструкциями языка удобнее на "большом" ПК, ибо коренных изменений в них при переходе к МК нет. Но об этом, я думаю, вы и сами догадываетесь. :)

Re: Фотодатчик на Tiny13

Добавлено: Чт дек 10, 2015 18:59:53
seagull72
Итак, программа в Proteuse работает так, как надо. Непонятен остался один момент.

_delay_ms() задает задержку в миллисекундах, если я правильно понимаю. Тогда почему, чтобы получить задержку 30 сек, мне пришлось записать _delay_ms(4500), а не _delay_ms(30000)?

Частота в настройках проекта Atmel Studuo задана как F_CPU=8000000UL. Насколько я понимаю - это 8 МГц. Правильно ли это?

Re: Фотодатчик на Tiny13

Добавлено: Чт дек 10, 2015 19:36:08
GRAF
seagull72, идёте в раздел System Clock and Clock Options.
F_CPU=8000000UL- да, это 8МГц.
В настройках МК ATtiny13 в протеусе по умолчанию стоит частота 9.6МГц.
Кроме того, может быть выставлен предделитель тактовой частоты на 8.

Re: Фотодатчик на Tiny13

Добавлено: Чт дек 10, 2015 19:53:22
seagull72
GRAF писал(а):идёте в раздел System Clock and Clock Options.
Это где? Даташит? Или Atmel? Извините за глупый вопрос.
GRAF писал(а):В настройках МК ATtiny13 в протеусе по умолчанию стоит частота 9.6МГц.
Кроме того, может быть выставлен предделитель тактовой частоты на 8.
Да, именно так и выставлен в Протеусе. Чем может грозить разная частота в Atmel и Proteus?

Re: Фотодатчик на Tiny13

Добавлено: Чт дек 10, 2015 19:56:43
GRAF
Документация на ATtiny13.
Параметры для delay рассчитываются на этапе компиляции по заранее выставленной Вами частоте в AVR Studio. В протеусе Вы ставите другую частоту тактирования.

Re: Фотодатчик на Tiny13

Добавлено: Чт дек 10, 2015 20:05:24
seagull72
А где предделитель в протеусе пишется? CKSEL fuses - это частота. SUT fuses - это оно?

Re: Фотодатчик на Tiny13

Добавлено: Чт дек 10, 2015 20:10:27
GRAF
С английским у Вас как? CLKDIV8.

Re: Фотодатчик на Tiny13

Добавлено: Чт дек 10, 2015 20:14:50
seagull72
Пока неважно с английским, но это поправится скоро.

Стоит (0)Programmed - то есть задается в программе. Стоял бы (1)Unprogrammed - был бы включен предделитель принудительно. Я правильно понимаю?
Все, разобрался. Ровно наоборот.