DS18B20 + ATTiny13A + 74HC595 прошу помочь разобраться

Дисплеи, датчики и прочие функциональные узлы, управляемые МК.
Ответить
Встал на лапы
Аватара пользователя
Сообщения: 125
Зарегистрирован: Ср май 05, 2010 19:03:14
Откуда: Украина

Сообщение Deals »

Доброго времени суток кто читает данную тему.
Сидел я и думал как бы выучить мне DS18B20 с 74HC595 и пришла мысль использовать ATTiny13A.
Это все чисто для обучения!
Написал программу, семисегментник работает нормально. Но вот общение с DS18B20 перешло в стадию нецензурной лексики :(
В общем привожу код С++:
Спойлер

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

#define F_CPU 1200000UL

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

#define CLOCK            (1 << PINB2)
#define DATA            (1 << PINB0)
#define LATCH            (1 << PINB1)
#define DQ_DATA            (1 << PINB4)

#define SwitchIn(x)        (DDRB &= ~(x))
#define SwitchOut(x)    (DDRB |= x)

#define SetPORTLow(x)    (PORTB &= ~(x))
#define SetPORTHigh(x)    (PORTB |= x)

#define ReadPIN(x)        (PINB & x)

#define EMPTY 10
#define DASH 11
#define letter_n 12
#define letter_o 0
#define letter_k 13
#define letter_e 14
#define letter_r 15

unsigned char number[16] = { 126, 48, 109, 121, 51, 91, 95, 112, 255, 123, 0, 1, 21, 87, 79, 5 };

void DisplayNumber(signed char c, signed char b, signed char a);
void InitSeg( );
void FirstInitTH( );
void InitTH( );
void WriteByte( unsigned char data );
unsigned char ReadByte( );
unsigned char ConvertTemp(unsigned char first, unsigned char second);

int main(void)
{
    InitSeg( );
    FirstInitTH( );
    while(1)
    {
        InitTH( );
        WriteByte( 0xCC );
        WriteByte( 0x44 );
        SwitchIn(DQ_DATA);
        SetPORTHigh(DQ_DATA);
        
        unsigned char x = ReadPIN(DQ_DATA);
    
        while( x == 0 )
        {
            x = ReadPIN(DQ_DATA);
            DisplayNumber(number[EMPTY], number[letter_o], number[letter_n]);
            _delay_ms(1000);
        }

        InitTH( );
        WriteByte( 0xCC );
        WriteByte( 0xBE );
        unsigned char data[3] = { 0, 0, 0 };
        data[0] = ReadByte( );
        data[1] = ReadByte( );
        _delay_ms(200);
        data[2] = ConvertTemp(data[0], data[1]);
        //data[2] = 132;
        unsigned char a, b, c;
        a = data[2] / 100;
        b = (data[2] % 100) / 10;
        c = data[2] % 10;
        if ( a == 0 )
            a = EMPTY;
        if ( a == EMPTY && b == 0 )
            b = EMPTY;
        DisplayNumber(number[a],number[b],number[c]);
        _delay_ms(5000);
    }
}

void InitSeg( )
{
    SwitchOut(CLOCK | DATA | LATCH);
    SetPORTLow(CLOCK | DATA | LATCH);
    
    DisplayNumber(number[EMPTY], number[letter_o], number[letter_n]);
    
    _delay_ms(1500);
}

void DisplayNumber(signed char c, signed char b, signed char a)
{
    SetPORTLow(CLOCK | DATA | LATCH);
    
    unsigned char i, j, k;
    for (i = 0; i < 8; i++)
    {
        if ((a & (1 << i)) != 0)
            SetPORTHigh(DATA);
        else
            SetPORTLow(DATA);
        
        SetPORTHigh(CLOCK);
        SetPORTLow(CLOCK);
    }

    for (j = 0; j < 8; j++)
    {
        if ((b & (1 << j)) != 0)
            SetPORTHigh(DATA);
        else
            SetPORTLow(DATA);
        
        SetPORTHigh(CLOCK);
        SetPORTLow(CLOCK);
    }

    for (k = 0; k < 8; k++)
    {
        if ((c & (1 << k)) != 0)
            SetPORTHigh(DATA);
        else
            SetPORTLow(DATA);
        
        SetPORTHigh(CLOCK);
        SetPORTLow(CLOCK);
    }

    SetPORTHigh(LATCH);
    SetPORTLow(LATCH);
    
    SetPORTLow(CLOCK | DATA | LATCH);
}

void FirstInitTH( )
{
    SwitchOut(DQ_DATA);
    SetPORTLow(DQ_DATA);
    _delay_us(500);
    
    SwitchIn(DQ_DATA);
    SetPORTHigh(DQ_DATA);
    _delay_us(65);
    
    if ( ReadPIN(DQ_DATA) !=0 )
        DisplayNumber(number[letter_e], number[letter_r], number[letter_r]);
    else
        DisplayNumber(number[EMPTY], number[letter_o], number[letter_k]);
        
    _delay_ms(1500);
}

void InitTH( )
{    
    SwitchOut(DQ_DATA);
    SetPORTLow(DQ_DATA);
    _delay_us(500);
    
    SwitchIn(DQ_DATA);
    SetPORTHigh(DQ_DATA);
    _delay_us(500);
}

void WriteByte( unsigned char data )
{
    unsigned char i;
    SwitchOut(DQ_DATA);
    for ( i = 0; i < 8; i++)
    {
        SetPORTLow(DQ_DATA);
        _delay_us(2);
        if ( (data & (1 << i)) != 0 )
            SetPORTHigh(DQ_DATA);
        else
            SetPORTLow(DQ_DATA);
        _delay_us(60);
        SetPORTHigh(DQ_DATA);
        _delay_us(2);
    }
}

unsigned char ReadByte( )
{
    unsigned char data = 0, i;
    for ( i = 0; i < 8; i++)
    {
        SwitchOut(DQ_DATA);
        SetPORTLow(DQ_DATA);
        _delay_us(2);
        SwitchIn(DQ_DATA);
        SetPORTHigh(DQ_DATA);
        _delay_us(5);
        if ( (ReadPIN(DQ_DATA)) != 0 )
            data |= ( 1 << i );
        else
            data &= ~( 1 << i );

        _delay_us(60);
    }
    
    return data;    
}

unsigned char ConvertTemp(unsigned char first, unsigned char second)
{
    unsigned char result = 0, i, j;
    
    for ( i = 4; i < 8; i++)
    {
        if ( (first & (1 << i)) != 0)
            result |= ( 1<<(i-4) );
        else
            result &= ~( 1<<(i-4) );
    }
    
    for ( j = 0; j < 4; j++)
    {
        if ( (second & (1 << j)) != 0 )
            result |= ( 1<<(j+4) );
        else
            result &= ~( 1<<(j+4) );
    }
    
    return result;
}
Прошу строго не пинать, но подскажите где ошибка. DS18B20 реагирует на импульс сброса и подает ответ присутствия. А дальше ничего не работает :( Есть подозрение что ошибка в отправке битов.
Т.к. до данного участка уже не работает(хотя должен "занулить" линию во время конвертации):

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

        while( x == 0 )
        {
            x = ReadPIN(DQ_DATA);
            DisplayNumber(number[EMPTY], number[letter_o], number[letter_n]);
            _delay_ms(1000);
        }
Прошу помощи :dont_know:
На дисплее заветные "255" видно что термометр не реагирует на отправку байтов.
Реклама
Это не хвост, это антенна
Аватара пользователя
Сообщения: 1368
Зарегистрирован: Вс мар 28, 2010 12:52:22
Откуда: Беларусь

Сообщение dr.doc »

В коде не разбирался, но, возможно помогу.
Операция записи бита: Вывод микроконтроллера устанавливается в режим выхода и на нем устанавливается логический ноль. Выдерживается пауза, длительность которой зависит от значения передаваемого бита (0 или 1), затем вывод переводится в режим входа в состоянии Hi-z и снова выдерживается пауза.
Операция чтения бита: Вывод микроконтроллера устанавливается в режим выхода и на нем устанавливается логический ноль.
Выдерживается определенная пауза, вывод переводится в режим входа в состоянии Hi-z, выдерживается пауза, а затем микроконтроллер считывает потенциал вывода.
По этому алгоритму в ассемблере у меня все работает.
«Еще я хотел бы, чтобы наши ученые изобрели какой-то новый источник энергии, чтобы мы на коленях не ползали даже перед нашими братьями, умоляя их и выпрашивая тонну нефти или кубометр газа», — рассказал белорусский президент.
Реклама
Встал на лапы
Аватара пользователя
Сообщения: 125
Зарегистрирован: Ср май 05, 2010 19:03:14
Откуда: Украина

Сообщение Deals »

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

void WriteByte( unsigned char data )
{
    unsigned char i; // для счетчика
    SwitchOut(DQ_DATA); // переводим пин на вывод
    for ( i = 0; i < 8; i++)
    {
        SetPORTLow(DQ_DATA); // устанавливает импульс восстановления
        _delay_us(2); // держим его 2 микросек
        if ( (data & (1 << i)) != 0 ) // проверяем установлен ли бит в байте
            SetPORTHigh(DQ_DATA); // если установлен отпускаем шину
        else
            SetPORTLow(DQ_DATA); // если не установлен садим шину на 0
        _delay_us(60); // держим так 60 микросек
        SetPORTHigh(DQ_DATA); // отпускаем шину
        _delay_us(2); // ждем 2 микросек
    }
}

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

unsigned char ReadByte( )
{
    unsigned char data = 0, i; // инициализируем 8 бит данных и заполняем нулями (=0), i счетчик
    for ( i = 0; i < 8; i++)
    {
        SwitchOut(DQ_DATA); // переводим пин на выход
        SetPORTLow(DQ_DATA); // устанавливает импульс восстановления для терм.
        _delay_us(2); // держим 2 микросек
        SwitchIn(DQ_DATA); // отпускаем шину (тем временем подтягивается через внешний резистор)
        SetPORTHigh(DQ_DATA); // включаем внутреннюю подтяжку на пине (не думаю что надо...)
        _delay_us(5); // ждем 5 микросек для установки бита с терм.
        if ( (ReadPIN(DQ_DATA)) != 0 ) // читаем пин из шины, если бит установлен (1), то..
            data |= ( 1 << i );  // записываем в бит переменной "1"
        else
            data &= ~( 1 << i ); // записываем в бит переменной "0"

        _delay_us(60); // ждем окончания слота времени чтения
    }
   
    return data;   
}
Это не хвост, это антенна
Аватара пользователя
Сообщения: 1368
Зарегистрирован: Вс мар 28, 2010 12:52:22
Откуда: Беларусь

Сообщение dr.doc »

В Протеусе отлаживаете? У меня датчик в нем передает температуру в 0-м (LSB) и 2-м (MSB) байтах, а не 0-м и 1-м, как пишут в datasheet. Возможно у Вас та же беда?
_delay_us(5); // ждем 5 микросек для установки бита с терм.
if ( (ReadPIN(DQ_DATA)) != 0 ) // читаем пин из шины, если бит установлен (1), то..
data |= ( 1 << i ); // записываем в бит переменной "1"
else
data &= ~( 1 << i ); // записываем в бит переменной "0"

_delay_us(60); // ждем окончания слота времени чтения
А 15uS для установки данных, рекомендуемых производителем?
У меня - так:
Спойлерrcall init_out ; Вывод на выход
cbi Dataport,DATA ; сбросим линию
rcall Delay_3uS ; Start slot
rcall init_in ; Линию на вход
rcall Delay_20uS ; ждем установления данных
clc ; Clear Carry
sbic Datapin,DATA ; Пропустим команду при лог. 0
sec ; Установим Carry в 1
ror r16 ; Carry в бит 7 (приемник данных)

rcall Delay_60uS ; Ждем окончания слота времени
«Еще я хотел бы, чтобы наши ученые изобрели какой-то новый источник энергии, чтобы мы на коленях не ползали даже перед нашими братьями, умоляя их и выпрашивая тонну нефти или кубометр газа», — рассказал белорусский президент.
Реклама
Эиком - электронные компоненты и радиодетали
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18785
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

Тактовая частота у вас 12 Мгц - это у вас внешний генератор подключен? У тини13 внутри таких генераторов нет, и внешний кварц не предусмотрен
dr.doc писал(а):В Протеусе отлаживаете? У меня датчик в нем передает температуру в 0-м (LSB) и 2-м (MSB) байтах, а не 0-м и 1-м, как пишут в datasheet.
Это бред. 3 года применяю протеус, датчики использую чуть ли не в каждом проекте, никогда подобного не видел.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Реклама
Встал на лапы
Аватара пользователя
Сообщения: 125
Зарегистрирован: Ср май 05, 2010 19:03:14
Откуда: Украина

Сообщение Deals »

dr.doc, но судя из даташита:
Output data from the DS18B20 is valid for 15µs after the falling edge that initiated the read time slot. Therefore,
the master must release the bus and then sample the bus state within 15µs from the start of the slot.
И если мне не изменяет знание английского (3 балла из 12 :)) в школе было), то:
Исходящая дата от DS18B20 действительна 15микросек после падения линии (сигнала) которая инициирует слот чтения. Следовательно устройство управления должно отпустить шину и тогда проверять состояние шини в течении (within) 15микросек начиная от старта слота.
Хотя это проблему не решает. Слоты чтения работают и читают просто "1" постоянно... термометр просто не реагирует на слоты записи... где-то в них косяк. Немного переписал код здесь чтоб увидеть когда термометр "занулит" шину для конвертирования...

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

		InitTH( );
		WriteByte( 0xCC );
		WriteByte( 0x44 );
		SwitchIn(DQ_DATA);
		SetPORTLow(DQ_DATA);
		
		if ( ReadPIN(DQ_DATA) == 0 )
		{
			DisplayNumber(number[letter_e],number[letter_r],number[letter_r]);
			_delay_ms(1000);
		}
Но не появляется на сегментах заветного "Err" после слотов записи.
Оптимизировал слот чтения и записи...
Спойлер

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

void WriteByte( unsigned char data )
{
	unsigned char i;
	SwitchOut(DQ_DATA);
	for ( i = 0; i < 8; i++)
	{
		SetPORTLow(DQ_DATA);
		_delay_us(2);
		if ( (data & (1 << i)) != 0 )
			SetPORTHigh(DQ_DATA); // убрал else т.к. у нас и так линия находится в Low и нет смысла повторять, только если нам нужо "1"
		_delay_us(60);
		SetPORTHigh(DQ_DATA);
		_delay_us(2);
	}
}
unsigned char ReadByte( )
{
	unsigned char data = 0, i;
	for ( i = 0; i < 8; i++)
	{
		SwitchOut(DQ_DATA);
		SetPORTLow(DQ_DATA);
		_delay_us(2);
		SwitchIn(DQ_DATA);
		_delay_us(8);
		if ( (ReadPIN(DQ_DATA)) != 0 ) // убрал опять else т.к. data у нас и так заполнена нулями data = 0...
			data |= ( 1 << i );

		_delay_us(60);
	}
	
	return data;	
}
Но и это все не помогло. Меня просто волнует вопрос, сколько времени выполняется битовые операции над пинами. Т.е. сколько времени тратится на то чтоб переключить из вывода на вход. Т.к. у нас такт соответствует почти 1us... 1 200 000Hz... 1\1 200 000 = 0.83us..
ARV, у меня стоит 1.2 МГц...

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

#define F_CPU 1 200 000UL // пробели для наглядности поставил
Фьюзы стандартны вместе c CKDIV8. 9.6MHz\8=1.2MHz...
Все таки думаю дело в таймингах...

P.S.: Сейчас попробую дергать за порты сразу вписав 8 бит на все пины.
P.P.S.: И все равно не помогло. Я уже не знаю где копать и кого туда закопать... :))
Реклама
Открыл глаза
Аватара пользователя
Сообщения: 59
Зарегистрирован: Чт сен 12, 2013 17:10:37

Сообщение Guaho_150 »

Фрагмент работающего кода для ПИКа (на АСМ-е). Код не важен, почитайте комментарий.
Спойлер;Процедура вывода байта, заданного в регистре o_byte, на термодатчик.
out_b clrwdt
movlw 8
movwf n1

out_b11 rrf o_byte,f ;сдвиг выводимого байта с последующим переходом по состоянию флага переноса.
bnc out_10
goto out_11

out_b12 decfsz n1,f
goto out_b11
return

;вывод сброшенного бита:
out_10 call pin_lo ;в течение 60 мкс удерживаем линию данных в 0.
movlw 6
call d_10mks
call pin_hi ;после чего переводим её в 1.
goto out_b12

;вывод установленного бита:
out_11 call pin_lo ;сначала сбрасываем линию данных в 0, после чего сразу же
call pin_hi ;устанавливаем её в 1 и удерживаем в таком состоянии в течение 60 мкс.
movlw 6
call d_10mks
goto out_b12
Специально не оформлял с тегом "код" (сделал в спойлере), чтобы выделить цветом важные моменты. Частота командных циклов - 1 МГц.
И ещё. Возможно, ошибка у вас более "высокоуровневая". Попробуйте для начала наладить работу без ID термодатчика.
Встал на лапы
Аватара пользователя
Сообщения: 125
Зарегистрирован: Ср май 05, 2010 19:03:14
Откуда: Украина

Сообщение Deals »

Guaho_150, посмотрите внимательно на мой код.
void WriteByte( unsigned char data ) // функция не возвращает ничго, принимает байт данных
{
unsigned char i; // объявляем счетчик
SwitchOut(DQ_DATA); // переводим на вывод данних пин
for ( i = 0; i < 8; i++) // начало цикла 0 до 7 (по битам)
{
SetPORTLow(DQ_DATA); // переводим линию в 0
_delay_us(2); // держим 2 микросек
if ( (data & (1 << i)) != 0 ) // если в байте данных бит установлен, если не установлен
SetPORTHigh(DQ_DATA); // отпускаем линию
_delay_us(60); // держим линию в таком состоянии 60 микросекдержим линию в таком состоянии 60 микросек
SetPORTHigh(DQ_DATA); // переводим ее в 1
_delay_us(2); // держим 2 микросек
}
}
По поводу частоты, я пробовал уже без задержек кроме 60 микросек удержки для считывания бита.
В данном случаем на датчик я посылаю Skip ROM и с его ID не работаю. После Skip ROM я сразу ему посылаю команду на конвертирование температуры. спустя 750MS (12 bit конвертации) отсылаю сброс, принимаю ответ, отправляю опять Skip ROM и уже читаю саму температуру... по крайнем мере сейчас такой алгоритм, но до задержки в 750мс не доходит.

P.S.: Все таки я склоняюсь к тому что нужно отключить CKDIV8 и проверить так. Увы конечно потеряю 1 контроллер.
Это не хвост, это антенна
Аватара пользователя
Сообщения: 1368
Зарегистрирован: Вс мар 28, 2010 12:52:22
Откуда: Беларусь

Сообщение dr.doc »

SetPORTHigh(DQ_DATA); // отпускаем линию - Эта функция должна переводить порт на вход даже в подпрограмме передачи данных.
#define SetPORTHigh(x) (PORTB |= x)

#define ReadPIN(x) (PINB & x)
- не эту ли (последнюю) функцию нужно применять?
PS Возможно 750 мС никак не 750? И Вы передаете банальный reset?
«Еще я хотел бы, чтобы наши ученые изобрели какой-то новый источник энергии, чтобы мы на коленях не ползали даже перед нашими братьями, умоляя их и выпрашивая тонну нефти или кубометр газа», — рассказал белорусский президент.
Встал на лапы
Аватара пользователя
Сообщения: 125
Зарегистрирован: Ср май 05, 2010 19:03:14
Откуда: Украина

Сообщение Deals »

Всё, как я и думал код в порядке. Плюнул, выставил CKDIV8 = 1 и все ЗАРАБОТАЛО!
Как я и думал, для процессора с частотой менее 1-2 МГц уже не выйдет ничего. Потому что битовая операция будет занимать более 5 микросек.
И самое хорошее что МК остался виден для программатора.
Спасибо всем. Тему в клоуз.
Ответить

Вернуться в «Периферия»