Отсчет времени attiny13 и неверна прошивка чипа

Обсуждаем контроллеры компании Atmel.
Ответить
Прорезались зубы
Сообщения: 228
Зарегистрирован: Пн окт 04, 2010 11:55:41

Сообщение neid »

Всем привет! Решил потихоньку освоить таймеры, отсчет времени на attiny13, начал с самого просто проекта датчика света. Но вот столкнулся с двумя проблемами, первая получилось прошить чип один раз, второй раз выдает:

Chip signature mismatch
Read: Byte0=0x00 Byte1=0x03 Byte2=0x05
Must be: Byte0=0x1E Byte2=0x90 Byte2=0x07

так же не могу считать фьюзы с чипа, работает только ресет... прошивал в CVAVR, программатором AVR910, на фьюзах выставил вот такие галочки: CKSEL1, SUT0, SUT1, CKDIV8. Контакты проверял, все нормально все работает.

Вторая проблема. Вроде рассчитал время правильно. Чип от внутреннего генератора 4,8MHz. Настраивал счетчик лопатит на 18,75кГц, указываем значение регистра сравнения равное 0хF0 равно 250, 18750/250=75 в секунду. Задаю в коде 20 секунд, потом включение... в протусе насчитает 20, в железе за 40 переваливает((
код представил ниже, помогите разобраться до конца, что не так сделал с прошивкой тиньки и код, где косяк по отсчету секунд?

Алгоритм следующий, если на улице стемнело, больше указного порога который мы записали в ЕППРОМ, ждем 20 сек, если все так же темно, но включаем прожектор. И наоборот если стало светло, то через 20 сек выключаем свет. Кнопкой на PORTB.0 мы программируем порог включения-отключения света простым нажатием. Светодиод на PORTB2 показывает что якобы записали.

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

#include <tiny13.h>
#include <delay.h>
unsigned int timer; // наша переменная 
unsigned int s;     // наша переменная для секунд
int s1=0;  // наша переменная флага
eeprom int adc;     //объявляем численную переменную, хранящийся в ЕППРОМ


// Timer 0 output compare A interrupt service routine
interrupt [TIM0_COMPA] void timer0_compa_isr(void)
{
timer++;// увеличиваем переменную

if(timer==75) {s++; timer=0;} //75 равно 1 секунде, и обнуляем 
TCNT0=0x00; // после выполнения кода счетчик сбрасываем 
}

#define ADC_VREF_TYPE 0x00

// Read the AD conversion result
unsigned int read_adc(unsigned char adc_input)
{
ADMUX=adc_input | (ADC_VREF_TYPE & 0xff);
// Delay needed for the stabilization of the ADC input voltage
delay_us(10);
// Start the AD conversion
ADCSRA|=0x40;
// Wait for the AD conversion to complete
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;
return ADCW;
}

// Declare your global variables here

void main(void)
{
int i;           // переменная хранящаяся в SRAM
int eeprom *adc; // указатель ЕЕПРОМ

// Crystal Oscillator division factor: 1
#pragma optsize-
CLKPR=0x80;
CLKPR=0x00;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif

// Input/Output Ports initialization
// Port B initialization
// Func5=In Func4=In Func3=In Func2=Out Func1=Out Func0=In 
// State5=T State4=T State3=T State2=0 State1=0 State0=P 
PORTB=0x01;
DDRB=0x06;

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 18,750 kHz
// Mode: Normal top=0xFF
// OC0A output: Disconnected
// OC0B output: Disconnected
// Timer Period: 13,653 ms
TCCR0A=(0<<COM0A1) | (0<<COM0A0) | (0<<COM0B1) | (0<<COM0B0) | (0<<WGM01) | (0<<WGM00);
TCCR0B=(0<<WGM02) | (1<<CS02) | (0<<CS01) | (0<<CS00);
TCNT0=0x00;
OCR0A=0xFA;// 18750/250=75 тиков в сек, FA=250
OCR0B=0x00;

// External Interrupt(s) initialization
// INT0: Off
// Interrupt on any change on pins PCINT0-5: Off
GIMSK=0x00;
MCUCR=0x00;


// Timer/Counter 0 Interrupt(s) initialization
TIMSK0=(0<<OCIE0B) | (1<<OCIE0A) | (0<<TOIE0);


// ADC initialization
// ADC Clock frequency: 75,000 kHz
// ADC Bandgap Voltage Reference: Off
// ADC Auto Trigger Source: ADC Stopped
// Digital input buffers on ADC0: On, ADC1: On, ADC2: On, ADC3: On
DIDR0|=(0<<ADC0D) | (0<<ADC2D) | (0<<ADC3D) | (0<<ADC1D);
ADMUX=ADC_VREF_TYPE;
ADCSRA=(1<<ADEN) | (0<<ADSC) | (0<<ADATE) | (0<<ADIF) | (0<<ADIE) | (1<<ADPS2) | (0<<ADPS1) | (0<<ADPS0);
ADCSRB=(0<<ADTS2) | (0<<ADTS1) | (0<<ADTS0);

// Global enable interrupts
#asm("sei")
s1=0; //флаг сброшен
i=*adc; //считали значения из памяти по указателю

while(1){

//*************БЛОК ВКЛЮЧЕНИЯ ПРОЖЕКТОРА*************    
        if((read_adc(2) < i) && (s1 == 0)){//если света меньше заданного порога и флаг сброшен
            s1 = 1;//включаем флаг
            
            s = 0;//секунды в ноль
        }

        if(s1 == 1){//если по прежнему сохраняется условие
            if(s == 20){//насчитал больше 20 сек
                PORTB.1 = 1;//вкл. прожектор
                s1 = 0;}//сбросил флаг
                }
                
//*****************************************************                
                
//************БЛОК ВЫКЛЮЧЕНИЯ ПРОЖЕКТОРА***************
        if((read_adc(2) > i) && (s1 == 0)){//если света больше заданного порога
            s1 = 2;//меняем флаг 
            
            s = 0;//секунды в ноль
        }

        if(s1 == 2){//условие сохраняется
            if(s == 20){//отсчитали 20 сек
                PORTB.1 = 0;//выкл. прожектор
                s1 = 0;}//флаг сбросили
                }
                
//****************************************************** 

//***********Запись в ЕЕПРОМ****************************
if (PINB.0==0){//нажали на кнопку
                PORTB.2=1;
                delay_ms(200);//светодиод моргнул
                PORTB.2=0;
                delay_ms(2000);//подождали 2 сек

                i=read_adc(2);*adc=i;//переменная равна значению АЦП, записываем значение АЦП по указателю в ЕППРОМ

                PORTB.2=1;
                delay_ms(200);
                PORTB.2=0;
                delay_ms(500);
                PORTB.2=1;
                delay_ms(200);//три раза моргнули светодиодом, фигня полнейшая
                PORTB.2=0;
                delay_ms(500);
                PORTB.2=1;
                delay_ms(200);
                PORTB.2=0;
                }                       
    }
}
Реклама
Прорезались зубы
Сообщения: 228
Зарегистрирован: Пн окт 04, 2010 11:55:41

Сообщение neid »

первую проблему решил сам, в программаторе нашел режим на пониженной частоте, замкнул джампер и все заработало. Остался код, народ от вас помощи, в этой теме профан.
Реклама
Друг Кота
Аватара пользователя
Сообщения: 15703
Зарегистрирован: Вт мар 16, 2010 22:02:27
Откуда: ДОНЕЦК

Сообщение BOB51 »

БЫРРР...
Для работ с 4,8 надо константу калибровки весьма нудным способом извлекать и вручную загружать.
Посему сразу лучше использовать родной RC "по умолчанию" 9,6 МГц - там калибровочное значение будет загружено автоматически.
А понизить системную частоту можно позднее с помощью предделителя.
Ошибка сигнатуры - в зависимости от типа программатора новые ATtiny13A оболочка может не знать - вот и матюкается при автоопределении кристалла. Решение - указываем МК вручную (или меняем программатор).
:dont_know:
Прорезались зубы
Сообщения: 228
Зарегистрирован: Пн окт 04, 2010 11:55:41

Сообщение neid »

BOB51 писал(а):БЫРРР...
Для работ с 4,8 надо константу калибровки весьма нудным способом извлекать и вручную загружать.
Посему сразу лучше использовать родной RC "по умолчанию" 9,6 МГц - там калибровочное значение будет загружено автоматически.
А понизить системную частоту можно позднее с помощью предделителя.
Ошибка сигнатуры - в зависимости от типа программатора новые ATtiny13A оболочка может не знать - вот и матюкается при автоопределении кристалла. Решение - указываем МК вручную (или меняем программатор).
:dont_know:
поставил 9,6... пересчитал
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 37,500 kHz
// Mode: Normal top=0xFF
// OC0A output: Disconnected
// OC0B output: Disconnected
// Timer Period: 6,8267 ms
TCCR0A=(0<<COM0A1) | (0<<COM0A0) | (0<<COM0B1) | (0<<COM0B0) | (0<<WGM01) | (0<<WGM00);
TCCR0B=(0<<WGM02) | (1<<CS02) | (0<<CS01) | (0<<CS00);
TCNT0=0x00;
OCR0A=0xFA; //37500/250 = 150 раз в секунду
OCR0B=0x00;

...ошибается примерно на 30 сек.. что не так делаю
Реклама
Эиком - электронные компоненты и радиодетали
Друг Кота
Аватара пользователя
Сообщения: 15703
Зарегистрирован: Вт мар 16, 2010 22:02:27
Откуда: ДОНЕЦК

Сообщение BOB51 »

Увы...
Я на ассемблере прожки пишу...
:dont_know:
Совет...
У тиньки 13-й кроме прочего на входе в Т/C0 может использоваться предделитель.
Итогом "по умолчанию" ежли ничего в фузах и clkpr не трогать системная частота (она же на входе предделителя Т/С0) будет
9600000/8 = 0,000000833S.
Ежли без участия предделителя (1:1) на 8-разрядном счетчике будем иметь максимальный интервал
0,000000833*256=0,000213333
Посему вроде предделитель воткнуть "в самый раз" (0,000000833*1024*256=0,21845333 на прерывание в максимуме).
Только воть... Тот предделитель предпочтителен в цикличном режиме.
Ежли однократный замер - его (предделитель) также надо обнулять. Да и запуск таймера в комплекте с предделителем на 13-й делается иначе -
смотри флажки TSM И PSR10 В GTCCR.
Точность хода на внутреннем RC весьма высока - отклонение не более минуты за пару часов.
8)
Реклама
Прорезались зубы
Сообщения: 228
Зарегистрирован: Пн окт 04, 2010 11:55:41

Сообщение neid »

BOB51 писал(а):Увы...
Я на ассемблере прожки пишу...
:dont_know:
Совет...
У тиньки 13-й кроме прочего на входе в Т/C0 может использоваться предделитель.
Итогом "по умолчанию" ежли ничего в фузах и clkpr не трогать системная частота (она же на входе предделителя Т/С0) будет
9600000/8 = 0,000000833S.
Ежли без участия предделителя (1:1) на 8-разрядном счетчике будем иметь максимальный интервал
0,000000833*256=0,000213333
Посему вроде предделитель воткнуть "в самый раз" (0,000000833*1024*256=0,21845333 на прерывание в максимуме).
Только воть... Тот предделитель предпочтителен в цикличном режиме.
Ежли однократный замер - его (предделитель) также надо обнулять. Да и запуск таймера в комплекте с предделителем на 13-й делается иначе -
смотри флажки TSM И PSR10 В GTCCR.
Точность хода на внутреннем RC весьма высока - отклонение не более минуты за пару часов.
8)
У меня 1:1, переделал фьюзы по вашей рекомендации, вроде добился правильного отсчета времени, по крайней мере 30 сек. четко отсчитывает.
Далее появилась новая задача, если с настройками я не мог справиться из за невнимательности, то тут просто из за незнания и не каждый же день я сижу с кодами. Вопрос стал в следующем, если значение ацп упало ниже порога, и продержалось там более 20 сек, то включаю свет. Но если значение ацп, стало выше порога + 20 сек, то выключить свет не могу. Было несколько вариантов, как это реализовать, но условие включения перекрывало выключение, мешались друг другу... приложил свои ошибки внизу....
//*************БЛОК ВКЛЮЧЕНИЯ ПРОЖЕКТОРА*************
if((read_adc(2) < i) && (s1 == 0)){//если света меньше заданного порога и флаг сброшен
s1 = 1;//включаем флаг

s = 0;//секунды в ноль
}

if(s1 == 1){//если по прежнему сохраняется условие
if(s == 20){//насчитал больше 20 сек
PORTB.1 = 1;//вкл. прожектор
s1 = 0;}//сбросил флаг
}

//*****************************************************

//************БЛОК ВЫКЛЮЧЕНИЯ ПРОЖЕКТОРА***************
if((read_adc(2) > i) && (s1 == 0)){//если света больше заданного порога
s1 = 2;//меняем флаг

s = 0;//секунды в ноль
}

if(s1 == 2){//условие сохраняется
if(s == 20){//отсчитали 20 сек
PORTB.1 = 0;//выкл. прожектор
s1 = 0;}//флаг сбросили
}

//******************************************************

или вот еще идея была:

// Timer 0 output compare A interrupt service routine
interrupt [TIM0_COMPA] void timer0_compa_isr(void)
{
timer++;
if(timer==150) {s++;timer=0;}
if(s==5){PORTB.1==1;} else
TCNT0=0x00;
}
...
while (1)
{
if ((read_adc(2)<409)){ // если напряжение на входе меньше порога
TCCR0B=0x04;//вкл.таймер
}
else{ // если напряжение на входе больше порога
TCCR0B = 0x00; // таймер ВЫКЛ.
s=0;}// сбросили секунды

if ((read_adc(2)>409)){ // если напряжение на входе меньше порога
TCCR0B=0x04;//вкл.таймер
}
else{ // если напряжение на входе больше порога
TCCR0B = 0x00; // таймер ВЫКЛ.
s=0;}// сбросили секунды
}
Реклама
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

У вас идет постоянный опрос АЦП. Стоит один раз считать в переменную, а потом использовать ее.
Кроме того, константы лучше объявить отдельно

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

//уровни АЦП, лучше всего использовать не числа, а макроформулы, это проще в настройке
#define ADC_OFF_LVL 10
#define ADC_ON_LVL 100
#define ADC_TIMEOUT 20
//макросы включение и отключения света
#define LIGHT_ON() {...}
#define LIGHT_OFF() {...}
//флаги. Пусть два флага хранятся в одном байте. Тут надо быть осторожнее, неизвестно во что компилятор это развернет
static struct{
  char f_wait_on:1; //флаг того что идет проверка надо ли включать
  char f_wait_off:1;
  char reserved:6; //добить размер структуры до 1 байта. Возможно, это необязательно
}flag;

int adc = read_adc(2); //сохраняем АЦП в отдельную переменную
if( flag.f_wait_on ){ //если идет проверка надо ли включать свет
  if( time > ADC_TIMEOUT ){ //если прошло достаточно времени, включаем свет и сбрасываем флаг
    LIGHT_ON();
    flag.f_wait_on=0;
  }
  if( adc > ADC_ON_LVL )flag.f_wait_on=0; //если АЦП вышло за пределы искомого диапазона сбрасываем флаг
}else if( flag.f_wait_off ){ //аналогично с флагом выключения
  if( time > ADC_TIMEOUT ){
    LIGHT_OFF();
    flag.f_wait_off=0;
  }
  if( adc < ADC_LVL_OFF )flag.f_wait_off=0;
}else if( adc < ADC_ON_LVL ){ //если ни один из флагов не выставлен (считаем что на прошлом измерении значение было между диапазонами)
  flag.f_wait_on = 1; time=0; //если текущее измерение попало в один из диапазонов сбрасываем счетчик времени и выставляем флаг
}else if( adc > ADC_OFF_LVL ){
  flag.f_wait_off = 1; time=0;
}
Прорезались зубы
Сообщения: 228
Зарегистрирован: Пн окт 04, 2010 11:55:41

Сообщение neid »

COKPOWEHEU писал(а):У вас идет постоянный опрос АЦП. Стоит один раз считать в переменную, а потом использовать ее.
Кроме того, константы лучше объявить отдельно
Привет! Спасибо, что откликнулись и помогли с кодом, в наглядном примере. У меня к вам вопрос куда мне смотреть, что искать - по поводу макро формул и с чем их едят? тоже самое по поводу объявления флагов? т.к. не хочу на этом останавливаться, добавить интерфейс по одной кнопке, чтение/запись в память мк порога вкл/выкл, и длительности задержки времени настраеваемой. Единственное, что мне не понятно структура, зачем её объявление и её роль. В остальном понятно все более менее, подставил, скомпилировал... проверял по секундомеру, отсчет времени... расхождения 1-3 сек на 1 мин., и около 20 сек на 5 мин., и то тут закрыл датчик, нажал пальцем на секундомер.
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

По использованию макросов в Си посмотрите литературу по языку. Именно для АЦП я имел в виду что-то вроде такого

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

#define ON_LVL_mV 1100 // пусть уровень включения 1,1 В.
#define ADC_AREF_mV 2500 //для примера, реальное опорное напряжение оказалось 2,5 В
//расчетные значения
#define ADC_ON_LVL (1024*ON_LVL_mV / ADC_AREF_mV) //вот макроформула, переводящая из человеческих единиц измерения в машинные
Флаги у меня сделаны как однобитные элементы структуры. В Си нельзя сделать отдельную переменную размером 1 бит, зато можно в 1 байте хранить 8 отдельных битов. Первый вариант понятен из кода. Проблема что gcc иногда вместо нормального кода генерирует неоптимальное хз что, так что можно попробовать другой вариант

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

char flags; //переменная ждя хранения флагов
#define F_TEST( x ) (flags & x) //макрос проверки флага
#define F_SET( x ) {flags |= x;} //макрос установки флага
#define F_CLR( x ) {flags &=~x;} //макрос снятия флага

#define FLAG_WAIT_ON (1<<0)
#define FLAG_WAIT_OFF (1<<1)

if( F_TEST( FLAG_WAIT_ON ) ){...}
F_SET( FLAG_WAIT_ON );
Конечно, никто не отменял и дубового варианта - по переменной (по байту) на флаг. По скорости работы этот вариант, кстати, может оказаться самым быстрым.
проверял по секундомеру, отсчет времени...
Без кварца особой точности добиться будет сложно. Впрочем, не знаю насколько она нужна.
Ответить

Вернуться в «AVR»