В предыдущей части урока мы изучили, как надо задействовать команду Search ROM устройств 1-WIRE, чтобы считывать идентификационные коды нескольких устройств на одной шине, создали и настроили проект.
Перейдём в функцию инициализации ds18b20_init, немного изменив там код.
Пока перенесём сброс устройств в условие
uint8_t ds18b20_init(uint8_t mode)
{
if(mode==SKIP_ROM)
{
if(ds18b20_Reset()) return 1;
//SKIP ROM
Добавим в эту функцию несколько локальных переменных
uint8_t ds18b20_init(uint8_t mode)
{
int i = 0, j=0;
uint8_t dt[8];
if(mode==SKIP_ROM)
В этой же функции теперь начнём обрабатывать ещё одно условие, когда мы уже не пропускаем считывание кода ROM
ds18b20_WriteByte(RESOLUTION_12BIT);
}
else
{
for(i=1;i<=8;i++)
{
if(ds18b20_SearhRom(dt))
{
Dev_Cnt++;
memcpy(Dev_ID[Dev_Cnt-1],dt,sizeof(dt));
}
else break;
}
}
return 0;
В теле этого условия мы будем читать код до тех пор, пока он будет не последний (пока функция будет возвращать нормальный результат), и копировать последний считанный код в наш двухмерный массив для дальнейшего использования. Но так как функция поиска идентификатора у нас пока только начата, то возвращать она будет всегда 0, что приведёт к тому, что выполнится она только один раз, что нам, собственно и нужно для начала изучения отклика устройств на шине.
Перейдём в файл main.c, удалим весь код в бесконечном цикле и изменим входной аргумент в вызове функции инициализации датчика
status = ds18b20_init(NO_SKIP_ROM);
Вернёмся в файл ds18d20.c в функцию поиска идентификатора ds18b20_SearhRom и в тело нашего условия добавим цикл считывания 8 байтов кода
ds18b20_WriteByte(0xF0);
do
{
} while(rom_byte_number < 8); // считываем байты с 0 до 7 в цикле
}
А в теле цикла считаем 2 бита, как мы и условились в нашей инструкции по считыванию, и поместим результаты считывания в разные переменные, и затем выйдем принудительно из цикла. Это временная мера, чтобы нам посмотреть процесс в программе анализа и чтобы в это время код не зацикливался
do
{
id_bit = ds18b20_ReadBit();
cmp_id_bit = ds18b20_ReadBit();
break;
} while(rom_byte_number < 8); // считываем байты с 0 до 7 в цикле
Соберём код, прошьём контроллер и посмотрим в анализаторе, какие биты мы получили
А получили мы 01, что соответствует тому, что оба наших устройства (а мы то хорошо знаем, что у нас их только 2) имеют одинаковый младший бит. И мало того, мы можем судить по результату, что этот бит — именно 0.
Идём дальше. Теперь мы должны по инструкции послать в шину бит 0.
Но прежде чем послать, давайте ещё немного отфильтруемся от лишнего. Вдруг нам придёт 11, чего вообще не может быть. То тогда мы выйдем из цикла
ds18b20_WriteByte(0xF0);
cmp_id_bit = ds18b20_ReadBit();
if ((id_bit == 1) && (cmp_id_bit == 1))
break;
break;
}
Дальше пишем противный случай (который для нас будет наоборот хорошим
break;
else
{
}
break;
Затем начнём дальше фильтроваться в теле оператора else. Это случай, если пришло что-то нормальное (не 11).
else
{
if (id_bit != cmp_id_bit)
search_direction = id_bit; // bit write value for search
}
Данным кодом мы обработали условие, если биты не равны и занесли в определённую переменную значение первого бита. То есть это и есть значение бита, который записан в обеих датчиках.
Далее противный случай уже этого условия. То есть тот случай, когда к нам из шины пришло 00, то есть когда биты в устройствах не равны. Добавим этот случай сразу с кодом, с которым потом разберёмся
search_direction = id_bit; // bit write value for search
else
{
if (id_bit_number < LastDiscrepancy)
search_direction = ((ROM_NO[rom_byte_number] & rom_byte_mask) > 0);
else
search_direction = (id_bit_number == LastDiscrepancy);
if (search_direction == 0)
{
last_zero = id_bit_number;
if (last_zero < 9)
LastFamilyDiscrepancy = last_zero;
}
}
}
Сначала мы обрабатываем здесь условие, что это не тот бит, где в прошлом проходе произошло последнее несовпадение. В этом случае мы записываем в результат из предыдущего прохода. Но у нас переменная, отвечающая за номер бита последнего несовпадения равна пока нулю, если мы зашли в функцию впервые, соответственно нам пока читать всё это неоткуда и мы пока сюда не попадём.
Пы попадём в противный случай, в теле которого мы занесём в результат значение переменной id_bit_number, которое мы проинициализировали как 1.
Дальше мы исследуем результат на ноль. Если истина то мы записываем значение вышеуказанной переменной ещё в переменную last_zero (последний ноль).
Потом сравниваем данную переменною с 9 и, если значение меньше, то ещё присваиваем это значение переменной LastFamilyDiscrepancy, которая будет в себе нести номер бита последнего несовпадения вендора устройства (в самом младшем байте хранится уникальный идентификатор типа устройства). Мы затем увидим, что у нас данный байт будет одинаковым у обоих устройств. Эта переменная нам не потребуется, но тем не менее пусть будет. Это может пригодиться для того, если мы нашу библиотеку будем использовать для разных устройств и дальнейший код будет зависеть уже от типа устройства.
Идём дальше по функции. Выйдем из тел двух условий и добавим следующий код
LastFamilyDiscrepancy = last_zero;
}
}
if (search_direction == 1)
ROM_NO[rom_byte_number] |= rom_byte_mask;
}
Данный код при условии результата со значением 1 занесёт его в нужный бит байта согласно маске, в которой у нас пока 1, то есть работаем мы пока с 0 битом.
А в противном случае мы данный бит сбрасываем
ROM_NO[rom_byte_number] |= rom_byte_mask;
else
ROM_NO[rom_byte_number] &= ~rom_byte_mask;
}
Ну и далее мы этот бит отправляем в шину
ROM_NO[rom_byte_number] &= ~rom_byte_mask;
ds18b20_WriteBit(search_direction);
}
Давайте посмотрим, что у нас получится. Для этого соберём код, прошьём контроллер и посмотрим результат в программе логического анализа
Мы видим, что от нас в шину ушёл 0, что мы и делаем постоянно. Это же во-первых первый проход, а во-вторых — нам не нужно пока отключать устройство с единичкой.
Далее всё просто. Мы инкрементируем номер бита и сдвигаем маску, так как в следующем повторении цикла мы уже будем читать другой бит
ds18b20_WriteBit(search_direction);
id_bit_number++;
rom_byte_mask <<= 1;
}
Дальше исследуем исчерпывание маски, то есть случай, когда мы пробежались уже по всем битам очередного байта
rom_byte_mask <<= 1;
if (rom_byte_mask == 0)
{
rom_byte_number++;
rom_byte_mask = 1;
}
}
В этом случае мы наращиваем номер байта и опять в маске текущим устанавливаем самый младший бит (бит 0).
Уберём наш временный break, так как мы уже дописали цикл до конца
}
break;
} while(rom_byte_number < 8); // считываем байты с 0 до 7 в цикле
Выйдя из тела цикла, отфильтруемся на то, что мы можем достигнуть количества битов больше 64, нам этого не надо, так как наш идентификатор ровно 64 бита
} while(rom_byte_number < 8); // считываем байты с 0 до 7 в цикле
if (!(id_bit_number < 65))
{
}
}
return search_result;
Давайте соберём код и прошьём контроллер. Посмотрим результат нашей работы в программе логического анализа
Мы видим, что код устройства, а также код типа устройства, ну и соответственно контрольная сумма у нас считаны. Правда код считан только одного устройства, но это уже неплохо. Просто мы ещё не определили количество устройств. То есть нам нужно в случае, если было хотя бы одно несовпадение битов в проходе, проинкрементировать количество устройств на шине, ну и ещё кое-что проделать с некоторами переменными и флагами.
Пока мы ничего не инкрементируем.
Напишем в функции ds18b20_SearhRom код тела условия дочитывания до конца всех 64 бит
if (!(id_bit_number < 65))
{
// search successful so set LastDiscrepancy,LastDeviceFlag,search_result
LastDiscrepancy = last_zero;
// check for last device
if (LastDiscrepancy == 0)
LastDeviceFlag = 1;
search_result = 1;
}
В данном коде мы присваиваем значение переменной номера бита последнего несовпадения, а затем в случае, если несовпадений в данном проходе не было, то устанавливаем флаг последнего усройства и присваиваем значение результату поиска 1. Это значение никак не зависит от того, последнее это устройство или не последнее. Это означает лишь то, что мы успешно достигли 63 бита.
Далее мы выходим из всех условий и циклов и напишем ещё одно условие сразу вместе с телом
search_result = 1;
}
}
if (!search_result || !ROM_NO[0])
{
LastDiscrepancy = 0;
LastDeviceFlag = 0;
LastFamilyDiscrepancy = 0;
search_result = 0;
}
return search_result;
В данное условие мы попадём в случае если мы не дойдём до 63 бита либо когда в самом младшем байте (коде типа устройства) у нас будут все нули. В этом случае мы присвоим всем нашим флагам нули, включая и переменную возврата результата. Но, надеюсь, что мы сюда не попадём никогда.
Поэтому выходим из тела данного условия и перепишем наши байты в наш двумерный массив
search_result = 0;
}
else
{
for (int i = 0; i < 8; i++) Addr[i] = ROM_NO[i];
}
return search_result;
}
На этом наша многострадальная функция поиска кода закончена.
В очередной раз соберём код, прошьём контроллер и посмотрим результат в программе логического анализа
Мы видим, что у нас считаны коды обоих датчиков. К сожалению данная программа не реагирует на расширение областей, поэтому полностью я их показать не могу, хотя, поднося к каждому из них курсор мыши, мы всё же можем их увидеть.
Но не смотря на этот недочёт, мы их всё же увидим. Не стоит забывать о том, что кроме программы логического анализа, у нас ещё есть терминальная программа, в которой, конечно, не всё так наглядно можно посмотреть, как в программе для логического анализатора, но всё же мы зато увидим все коды.
В следующей части урока мы закончим код всех наших функций и проверим весь наш код на практике, измеряя температуру двумя датчиками и отображая данные показания в терминальной программе.
Предыдущая часть Программирование МК STM32 Следующая часть
Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6
Программатор недорогой можно купить здесь ST-Link V2
Логический анализатор 16 каналов можно приобрести здесь
Датчик температуры DS18B20 в экране с проводом можно приобрести здесь DS18B20
Переходник USB to TTL можно приобрести здесь ftdi ft232rl
Смотреть ВИДЕОУРОК (нажмите на картинку)