Проблема совместного использования кода 1-Wire и V-USB |
![]() |
Добавил(а) microsin |
К сожалению, простое использование кода чтения шины 1-Wire (например, термодатчиков DS1820 или DS18B20) вместе с библиотекой V-USB приводит к ошибкам. Причина в том, что шина 1-Wire требует формирования точных и коротких интервалов времени. Но к сожалению, прерывания и сигналов USB и код обработки протокола USB не дают возможности точно выдерживать такие интервалы. Вот пример низкоуровневых подпрограмм, работающих с шиной 1-Wire: //=============================================================================
// Функции для работы с 1-Wire
// Тексты независящих от платформы функций (поиск устройств - OWFirst, OWNext и
// OWSearch) взяты из документа Dallas Semiconductor:
// "Application Note 187: 1-Wire Search Algorithm"
// http://www.maxim-ic.com/appnotes.cfm/appnote_number/187
//=============================================================================
// Отправка одного бита данных по шине 1-Wire.
//-----------------------------------------------------------------------------
void OWWriteBit(u8 bit_value) { #ifdef ONE_WIRE_HAS_PRIORITY
cli();
#endif if(bit_value == 0) { DQDDR |= _BV(DQ); _delay_us(60); DQDDR &= ~_BV(DQ); _delay_us(10); } else { DQDDR |= _BV(DQ); _delay_us(3); DQDDR &= ~_BV(DQ); _delay_us(64); } #ifdef ONE_WIRE_HAS_PRIORITY
sei();
#endif
}
//-----------------------------------------------------------------------------
// Отправка 8 бит данных по шине 1-Wire.
//-----------------------------------------------------------------------------
void OWWriteByte(u8 byte_value) { u8 i; for(i = 0; i < 8; i++) { OWWriteBit(byte_value & 0x01); byte_value >>= 1; } } //-----------------------------------------------------------------------------
// Чтение одного бита данных с шины 1-Wire.
// Вернет 1, если бит читается как 1, иначе вернет 0.
//----------------------------------------------------------------------------- u8 OWReadBit(void) { u8 ret; #ifdef ONE_WIRE_HAS_PRIORITY
cli();
#endif DQDDR |= _BV(DQ); _delay_us(6); DQDDR &= ~_BV(DQ); _delay_us(9); ret = (DQPIN & _BV(DQ)); _delay_us(55); #ifdef ONE_WIRE_HAS_PRIORITY
sei();
#endif return ret >> DQ; // Значение бита - в младшем разряде } //-----------------------------------------------------------------------------
// Чтение байта с шины 1-Wire.
//----------------------------------------------------------------------------- u8 OWReadByte(void) { u8 i, mask = 0x01, ret = 0x00; for(i = 0; i < 8; i++) { if(OWReadBit()) ret |= mask; mask << = 1; } return ret; } u8 OWReset (void) { u8 ret; #ifdef ONE_WIRE_HAS_PRIORITY
cli();
#endif DQDDR |= (1 << DQ); _delay_us(480); DQDDR &= ~(1 << DQ); _delay_us(70); ret = (DQPIN & (1 << DQ)); _delay_us(410); #ifdef ONE_WIRE_HAS_PRIORITY
sei();
#endif return (0 == ret); } Здесь для формирования задержек протокола 1Wire используются функции _delay_us. Очевидно, что для точных задержек требуется, чтобы во время формирования задержки использование процессора было монопольным, т. е. нужно запретить прерывания. Это как раз и делает макрос ONE_WIRE_HAS_PRIORITY - если его определить, то в приоритете будет читаться шина 1-Wire, а если нет, то приоритет будет отдан прерываниям библиотеки V-USB. Однако проблему это никак не решает - либо будет надежно читаться шина 1-Wire, либо будет надежно работать код обслуживания USB. Если дать приоритет шине 1-Wire (определить макрос ONE_WIRE_HAS_PRIORITY), то обычно работа USB становится непредсказуемой, зависимой от частоты опроса 1-Wire и момента вызова опроса. Устройство USB отваливается, перестает передавать данные и т. п. Если дать приоритет USB, то тогда начинает ненадежно работать шина 1-Wire. Например, при чтении термодатчика DS18B20 некоторые чтения будут давать совершенно неправильный результат - вместо 28 градусов Цельсия может быть прочитано 48, или даже 6144 градуса, и т. п. В случае чтения температуры надежность чтения термодатчика можно повысить, если воспользоваться статистической обработкой и проверками на предельно допустимые значения. Статистическая обработка основана на том, что температура не может меняться быстро, и как минимум, к примеру, 3 последовательных чтения с интервалом в 1 секунду должны совпадать. Т. е. если измеренная температура не совпадает с 3 предыдущими измерениями, то это измерение ошибочное, и его можно отбросить. Проверка на предельные значения еще проще. По паспорту датчик DS18B20 может работать в диапазоне температур -55 .. +125 градуса по Цельсию. Следовательно, нужно просто отбрасывать результаты чтения термодатчика, которые не попадают в этот диапазон. Пример кода, который выполняет такую фильтрацию измерений, приведен ниже. Он может работать, когда приоритет коду V-USB (т. е. прерывания во время формирования задержек разрешены), и рассчитан на то, что датчик DS18B20 опрашивается по шине 1-Wire с интервалом 1 раз в секунду. //Объединение, сделанное для удобного преобразования
// байтового представления температуры в число со знаком.
typedef union { u16 u; s16 s; }convT; //Функция принимает на входе прочитанное значение температуры
// (в формате с фиксированной точкой b000000000000000.0).
//Вернет 0, если чтение температуры было ошибочно (не совпадает
// с предыдущими тремя значениями, накопленными в массиве etalons),
// иначе вернет 1.
static bool isReadingTemperatureOK (u16 temperature) { #define DEEP_CHECK 3 convT cv; static u16 etalons[DEEP_CHECK]; u8 idx; bool alleq; //Проверка 1, на диапазон: cv.u = temperature >> 1; if ((cv.s <= -55) || (cv.s >= 125)) return false; //Проверка 2, на "одинаковость": alleq = true; for (idx=0; idx < DEEP_CHECK; idx++) { //Проверка на одинаковость: if (cv.u != etalons[idx]) alleq = false; if(idx < (DEEP_CHECK-1)) { //Сдвиг стека "одинаковостей" влево. etalons[idx] = etalons[idx+1]; } else { //Сохранение последнего результата. etalons[idx] = cv.u; } } return alleq; } У Сергея Кухтецкого [1] каким-то чудом получилось читать термодатчик DS1820 без дополнительной обработки - наверное потому, что момент обращения по шине 1-Wire совпадал по времени с подачей запроса по шине USB. Однако не факт, что код, который нормально работает на 16 МГц, будет также работать на других частотах, допустимых для библиотеки V-USB. Примечание: капитан Очевидность конечно заметит, что все проблемы будут решены, если совместно с протоколом 1-Wire для работы с USB использовать микроконтроллеры AVR USB (т. е. с аппаратной поддержкой USB). Ну что же, он прав на 100%. [Ссылки] |