|
DS18b20 неправильно показывает температуру, Когда работает софтовый USB(от obdev) |
|
|
|
Nov 5 2009, 14:02
|
Местный
  
Группа: Validating
Сообщений: 253
Регистрация: 21-12-08
Пользователь №: 42 646

|
Здраствуйте! Вобщем хочу я сделать USB термомерт используя мегу8 и DS18b20. Вобщем написал програмку, а когда запустил то по USB(CDC) начали приходить неверные данные с термометра, сначала грешил на DS18b20 но когда подключил его к другому девайсу то он там работал нормально. Потом я отключил USB (в самом коде) и отправлял даные с датчика на UART, на компе было видно реальную температуру. Дальше я написал программу так, чтобы юсб был включен но даные передавались на ЮАРТ и тут ошибка повторилась  Вобщем насколько я понял, то комп постоянно отправляет даные на мегу8 и очень часто вызываеться прерывание по INT0 даже тогда, когда меряеться температура а так как задержки между отправкой и приемом команд к DS маленькие, несколько микросекунд(хотя задержка для измерения температуры датчиком минимум 750мс) то или при отправке комманды долго передается бит и DS перезагружаеться, или когда принимаються то МК невовремя реагирует на комманды и получает неправильные данные. Пробовал отключать глобольно прерывания(cli) перед измерением температуры и включать(sei) после измерения но тогда МК вообще не шлет даные по ЮСБ и не отвечает на комманды с ЮСБ(например когда отправляю "1" то он должен прислать "one", 2 - "two") . Ещё пробовал делать так: ------- отключил прерывания перед отправкой комманд к DS чтобы он измерил температуру. ------- включил после этих комманд потому что далее должна быть задержка 800мс. ------- спустя 800 мс отключал, отправлял комманду чтения температуры, когда он присылал даные опять включал ------- эфект тот же! Кароче мне нада сделать так,чтобы как то отключать ЮСБ на время измерения температуры ну или ещё что нибуть  . Частота 12Мгц, питание от ЮСБ. Вот клавный файл и библиотека для измерения температуры которую я скачал с инета, немного переделал после чего она работает отлично, правда тока на 1 датчик http://upwap.ru/619341 . Заранее спасиба!!!!!!! вот кстате те самые даные с термометра: +031.1 +125.7 +127.5 +031.3 +023.7 -000.7 +127.1 +021.3 +031.1 -000.7 -000.7 -000.7 -000.7 -000.7 +125.7 -000.7 -000.7 +119.7 +063.7 -000.7 -000.7 +125.6
|
|
|
|
2 страниц
1 2 >
|
 |
Ответов
(1 - 14)
|
Nov 5 2009, 14:43
|
Группа: Участник
Сообщений: 6
Регистрация: 26-09-07
Пользователь №: 30 853

|
Во время чтения/записи данных по шине 1-Wire yужно запрещать прерывания на время tREC+tRDV (см. документацию на DS18b20). В течение этого промежутка времени данные спокойно пишутся/читаются с шины. У меня нижеприведенный код вполне успешно работает совместно с прерываниями и проблем нет. CODE #include <iom168.h> //#include <inavr.h> #include <ina90.h> #include "OneWire.h"
#define _1W_CLK 8000000 #define _1W_TKSPer10uS _1W_CLK/100000 #define MILLION 1000000 #define tRSTL 48*_1W_TKSPer10uS //480uS #define tPDH 65*_1W_CLK/MILLION //65uS #define tRSTH tRSTL-tPDH #define tSAMPLE 12*_1W_CLK/MILLION #define tSLOT 12*_1W_TKSPer10uS //120uS #define tLOW1 0*_1W_CLK/MILLION //3uS #define t1uS 1*_1W_CLK/MILLION //3uS #define tLOW0 10*_1W_TKSPer10uS //120uS #define tREC 7*_1W_CLK/MILLION //7uS
//================================================================== //Выходы 1Wire //1Wire port/ SCK ( D INPUT/OUTPUT) #define OWIREIOOUT DDRC|=(1<<2) #define OWIREIOIN DDRC&=~(1<<2) #define OWIREIOH PORTC|=(1<<2) #define OWIREIOL PORTC&=~(1<<2) #define OWIRE (PINC&(1<<2))
//================================================================== //1wire atmega DRIVERS // //------------------------------------------------------------------------- //This procedure calculates the cumulative Dallas Semiconductor 1ЦWire CRC of all bytes passed to it. The result //accumulates in the global variable CRC. unsigned char CRC8(unsigned char inData, unsigned char seed) {//from AVR318 unsigned char bitsLeft; unsigned char temp;
for (bitsLeft = 8; bitsLeft > 0; bitsLeft--) { temp = ((seed ^ inData) & 0x01); if (temp == 0) { seed >>= 1; } else { seed ^= 0x18; seed >>= 1; seed |= 0x80; } inData >>= 1; } return seed; } //------------------------------------------------------------------------- //Сброс устройств на шине 1W и чтение Presence Pulse // Возврат 0- ничего нет // 1- Есть устройство // 2- Линия в КЗ char Reset1W(void) { OWIREIOOUT; __disable_interrupt(); OWIREIOL; __delay_cycles(tRSTL); OWIREIOH; OWIREIOIN; __delay_cycles(tPDH); if(!OWIRE) { __enable_interrupt(); __delay_cycles(tRSTL); if(OWIRE) { return 1;//Presence pulse }//if(OWIRE) else return 2;//Short circuit } __enable_interrupt(); __delay_cycles(tRSTL); return 0;//Nothing } //------------------------------------------------------------------------- //Проверка наличия 1W устройства char Check1W(void) { if(Reset1W()==1) { __delay_cycles(tRSTL); if(Reset1W()==1) return 1; } return 0; } //------------------------------------------------------------------------- //Передача данных по шине 1Wire void Write1W(char d) { char i; OWIREIOOUT; for(i=0;i<8;i++) { __disable_interrupt(); OWIREIOL; __delay_cycles(tLOW1); if(d&1) { OWIREIOH; __delay_cycles(tSLOT); } else { __delay_cycles(tLOW0); OWIREIOH; } __enable_interrupt(); __delay_cycles(tREC); d=d>>1; } OWIREIOIN; } //------------------------------------------------------------------------- //Прием данных по шине 1WIRE char Read1W(void) { char i,d=0; for(i=0;i<8;i++) { OWIREIOOUT; d=d>>1; __disable_interrupt(); OWIREIOL; __delay_cycles(t1uS); OWIREIOH; OWIREIOIN; __delay_cycles(tSAMPLE); if(OWIRE) d|=0x80; __enable_interrupt(); __delay_cycles(tSLOT);//tRELEASE 30uS } return d; }
//--------------------------------------------------------------------------- void StartT(char BusNo)//запуск измерения температуры по одной из шин { Reset1W(); Write1W(SKIPROM); Write1W(CONVERTT); }
//--------------------------------------------------------------------------- float GetT18B20(char *x)//Считывание температуры с датчиков DS18B20,DS1822 { char i,crc=0; int j; char *p; if(Reset1W()!=1) return -100;//Нет такого датчика p=x; // Write1W(SKIPROM); Write1W(MATCHROM); for(i=0;i<8;i++) { //OutChar(*p); Write1W(*p++);//Передача 8 байт адреса } Write1W(READSCRATCHPAD); p=x; for(i=0;i<8;i++) { *p=Read1W(); crc=CRC8(*p,crc); //OutChar(*p); *p++; } if(Read1W()!=crc) return -101;//CRC error p=x; j=p[1]<<8; j|=p[0]; return (float)j/16.0; }
//--------------------------------------------------------------------------- float GetT18S20(char *x)//Считывание температуры с датчика DS18S20 { char i,crc=0; int j; char *p=x; if(Reset1W()!=1) return -100;//Нет такого датчика Write1W(SKIPROM); // for(i=0;i<8;i++) // Write1W(*p++);//Передача 8 байт адреса Write1W(READSCRATCHPAD); p=x; for(i=0;i<8;i++) { *p=Read1W(); crc=CRC8(*p,crc); *p++; } if(Read1W()!=crc) return -101;//CRC error p=x; j=p[0]; return (float)j/2.0; } Код же с http://upwap.ru/619341 абсолютно не имеет никаких критических секций и правильно работать в системе с прерываниями без модификации не будет.
|
|
|
|
|
Nov 5 2009, 15:56
|
Местный
  
Группа: Validating
Сообщений: 253
Регистрация: 21-12-08
Пользователь №: 42 646

|
вобщем сделал на "молекулярном уровне": CODE #include "therm_ds18b20.h" #include "ascii_conv.h" #include <avr/interrupt.h>
volatile char THERM_DQ = 0; /* void therm_delay(uint16_t delay) { while(delay--) asm volatile("nop"); } */
uint8_t therm_reset() { uint8_t i; //Pull line low and wait for 480uS cli(); ds_cbi(THERM_PORT,THERM_DQ); ds_sbi(THERM_DDR,THERM_DQ); sei(); _delay_us(480); cli(); //Release line and wait for 60uS ds_cbi(THERM_DDR,THERM_DQ); sei(); _delay_us(60); //Store line value and wait until the completion of 480uS period cli(); i=(THERM_PIN & (1<<THERM_DQ)); sei(); _delay_us(420); //Return the value read from the presence pulse (0=OK, 1=WRONG) return i; }
void therm_write_bit(uint8_t bit) { //Pull line low for 1uS cli(); ds_cbi(THERM_PORT,THERM_DQ); ds_sbi(THERM_DDR,THERM_DQ); _delay_us(1); //If we want to write 1, release the line (if not will keep low) if(bit) ds_cbi(THERM_DDR,THERM_DQ); //Wait for 60uS and release the line sei(); _delay_us(60); ds_cbi(THERM_DDR,THERM_DQ); }
uint8_t therm_read_bit(void) { uint8_t bit=0; cli(); //Pull line low for 1uS ds_cbi(THERM_PORT,THERM_DQ); ds_sbi(THERM_DDR,THERM_DQ); _delay_us(1); //Release line and wait for 14uS ds_cbi(THERM_DDR,THERM_DQ); _delay_us(14); //Read line value if(THERM_PIN&(1<<THERM_DQ)) bit=1; //Wait for 45uS to end and return read value _delay_us(45); sei(); return bit; }
uint8_t therm_read_byte(void) { uint8_t i=8, n=0; cli(); while(i--) { //Shift one position right and store read value n>>=1; n|=(therm_read_bit()<<7); } sei(); return n; }
void therm_write_byte(uint8_t byte) { uint8_t i=8; cli(); while(i--) { //Write actual bit and shift one position right to make the next bit ready therm_write_bit(byte&1); byte>>=1; } sei(); }
void therm_read_temperature(char *buffer, volatile char PIN_DS) { THERM_DQ = PIN_DS; // Buffer length must be at least 12bytes long! ["+XXX.XXXX C"] uint8_t temperature[2]; // uint16_t temper_16bit; int8_t digit; uint16_t decimal; //Reset, skip ROM and start temperature conversion therm_reset(); therm_write_byte(THERM_CMD_SKIPROM); therm_write_byte(THERM_CMD_CONVERTTEMP); //Wait until conversion is complete //while(!therm_read_bit()); _delay_ms(750); //Reset, skip ROM and send command to read Scratchpad therm_reset(); therm_write_byte(THERM_CMD_SKIPROM); therm_write_byte(THERM_CMD_RSCRATCHPAD); //Read Scratchpad (only 2 first bytes) temperature[0]=therm_read_byte(); temperature[1]=therm_read_byte(); therm_reset(); //Store temperature integer digits and decimal digits digit=temperature[0]>>4; digit|=(temperature[1]&0x07)<<4; //Store decimal digits decimal=temperature[0]&0xf; decimal*=5; if (temperature[1]>0xFB) { digit = 127-digit; buffer[0] = '-'; } else buffer[0] = '+';
buffer[1] = to_ascii(digit/100); buffer[2] = to_ascii((digit%100)/10); buffer[3] = to_ascii(digit%10); buffer[4] = '.'; buffer[5] = to_ascii((decimal%100)/10);
} юсб опять не отвечает на комманды  но компом распознаеться. Андрей, а вы пробовали вашу либу совместно с драйвером obdev?
Причина редактирования: Общирные не оформленные исходные тексты
|
|
|
|
|
Nov 5 2009, 20:57
|
Группа: Участник
Сообщений: 6
Регистрация: 26-09-07
Пользователь №: 30 853

|
C OBDEV не использовал. В файле usbdrvasm есть строка - "max allowable interrupt latency: 34 cycles -> max 25 cycles interrupt disable". Судя по всему, максимально допустимое время с запрешёнными прерываниями - 25 тактов. На мой взгляд, совместить OBDEV и 1-Wire очень тяжело. Разноси эти задачи по двум микроконтроллерам
|
|
|
|
|
Nov 5 2009, 21:00
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Цитата(Br.Misha @ Nov 5 2009, 22:06)  мож тут ещё че не так? Попробуйте нарисовать ванварный мастер как конечный автомат на прерываниях - внешнему по спаду и от (8-битного) таймера. Естесно, без delayxxx(). (хинт: у мастера 3 операции - сброс, чтение и запись, операции состоят соответственно из 3, 3 и 2 фаз. Всё это легко помещается в 1 байт и отрабатывается computational goto) С 12 МГц клоком можно позволить себе не зависать в прерывании по спаду даже на 6 или 9 мкс (при чтении с шины), а отрабатывать их (6 и 9 мкс) таймером. Когда всё с ванварью получится, добавьте obdev usb. Заведите программный таймер на интервал, меньший интервала опроса по usb, как признак разрешения обмена по ванвари, и перезапускайте его по окончании обмена по usb. Заведите программный таймер на интервал, равный или больший времени преобразования DS1820, и перезапускайте его по запуску преобразования. Цитата Я уже и кварц на 16МГц ставил а оно всеравно не работает  И не должно. Читайте доки от obdev.
|
|
|
|
|
Nov 6 2009, 01:02
|
Местный
  
Группа: Validating
Сообщений: 253
Регистрация: 21-12-08
Пользователь №: 42 646

|
фух блин...... переделал код но опять не работает...... вот что я написал: CODE #include <avr/io.h> #include "therm_ds18b20.h" #include "ascii_conv.h"
volatile char THERM_DQ = 0; /* void therm_delay(uint16_t delay) { while(delay--) asm volatile("nop"); } */
uint8_t therm_reset() { uint8_t i; //Pull line low and wait for 480uS ds_cbi(THERM_PORT,THERM_DQ); ds_sbi(THERM_DDR,THERM_DQ); //_delay_us(480); TCNT1 = 0; while(TCNT1<(480*12)); //Release line and wait for 60uS ds_cbi(THERM_DDR,THERM_DQ); //_delay_us(60); TCNT1 = 0; while(TCNT1<(60*12)); //Store line value and wait until the completion of 480uS period i=(THERM_PIN & (1<<THERM_DQ)); //_delay_us(420); TCNT1 = 0; while(TCNT1<(420*12)); //Return the value read from the presence pulse (0=OK, 1=WRONG) return i; }
void therm_write_bit(uint8_t bit) { //Pull line low for 1uS ds_cbi(THERM_PORT,THERM_DQ); ds_sbi(THERM_DDR,THERM_DQ); //_delay_us(1); TCNT1 = 0; while(TCNT1<12); //If we want to write 1, release the line (if not will keep low) if(bit) ds_cbi(THERM_DDR,THERM_DQ); //Wait for 60uS and release the line //_delay_us(60); TCNT1 = 0; while(TCNT1<(60*12)); ds_cbi(THERM_DDR,THERM_DQ); }
uint8_t therm_read_bit(void) { uint8_t bit=0; //Pull line low for 1uS ds_cbi(THERM_PORT,THERM_DQ); ds_sbi(THERM_DDR,THERM_DQ); //_delay_us(1); TCNT1 = 0; while(TCNT1<12); //Release line and wait for 14uS ds_cbi(THERM_DDR,THERM_DQ); //_delay_us(14); TCNT1 = 0; while(TCNT1<(14*12)); //Read line value if(THERM_PIN&(1<<THERM_DQ)) bit=1; //Wait for 45uS to end and return read value //_delay_us(45); TCNT1 = 0; while(TCNT1<(45*12)); return bit; }
uint8_t therm_read_byte(void) { uint8_t i=8, n=0; while(i--) { //Shift one position right and store read value n>>=1; n|=(therm_read_bit()<<7); } return n; }
void therm_write_byte(uint8_t byte) { uint8_t i=8; while(i--) { //Write actual bit and shift one position right to make the next bit ready therm_write_bit(byte&1); byte>>=1; } }
void therm_read_temperature(char *buffer, volatile char PIN_DS) { THERM_DQ = PIN_DS; // Buffer length must be at least 12bytes long! ["+XXX.XXXX C"] uint8_t temperature[2]; // uint16_t temper_16bit; int8_t digit; uint16_t decimal; //Reset, skip ROM and start temperature conversion therm_reset(); therm_write_byte(THERM_CMD_SKIPROM); therm_write_byte(THERM_CMD_CONVERTTEMP); //Wait until conversion is complete //while(!therm_read_bit()); //_delay_ms(750); for (volatile char gggg=0;gggg<151;gggg++) { TCNT1 = 0; while(TCNT1<(5000*12)); } //Reset, skip ROM and send command to read Scratchpad therm_reset(); therm_write_byte(THERM_CMD_SKIPROM); therm_write_byte(THERM_CMD_RSCRATCHPAD); //Read Scratchpad (only 2 first bytes) temperature[0]=therm_read_byte(); temperature[1]=therm_read_byte(); therm_reset(); //Store temperature integer digits and decimal digits digit=temperature[0]>>4; digit|=(temperature[1]&0x07)<<4; //Store decimal digits decimal=temperature[0]&0xf; decimal*=5; if (temperature[1]>0xFB) digit = 127-digit; buffer[0] = to_ascii(digit/100); buffer[1] = to_ascii((digit%100)/10); buffer[2] = to_ascii(digit%10); buffer[3] = '.'; buffer[4] = to_ascii((decimal%100)/10);
} Обьясняю что наделал: Частота 12000000 Гц получаеться что timer1 с пределителем на 1 будет делать 12 тиков каждую микросекунду. Теперь чтобы сделать задержку в 1 микросекунду нада написать: TCNT1 = 0;while(TCNT1<(1*12)); тоесть теперь спустя 12 тиков(1мкс) програма будет выполняться дальше а так как таймер1 будет тикать постоянно, даже когда выполняеться прерывание INT0 то задержка будет более точной чем писать просто через _delay_us. А чтобы сделать иную задержку нада заменить 1 на нужное кол-во микросекунд(тока не больше 5400). Тут как и раньше с выключеным ЮСБ работает а с включеным нет.
Причина редактирования: Нарушение п.3.4 Правил форума.
|
|
|
|
|
Nov 6 2009, 07:43
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Цитата(Br.Misha @ Nov 6 2009, 04:02)  Обьясняю что наделал: Некоторый объём бесполезной работы? Цитата даже когда выполняеться прерывание INT0 то задержка будет более точной чем писать просто через _delay_us. Вы не поняли причину проблемы и потому не с тем боретесь. С точки зрения контроллера между //_delay_us(480); и TCNT1 = 0; while(TCNT1<(480*12)); абсолютно никакой разницы нет - его на 480 мкс завесили в тупом цикле, и он не в состоянии отвлечься на другие задачи (н-р, на обработку обмена по usb, приоритет которой разумно сделать выше приоритета обмена по ванвари). Я предлагал построить автомат не на задержках, а на интервалах, когда, обрабатывая (в прерывании) текущее состояние автомата и зная/предполагая его следующее состояние и время возникновения этого состояния, Вы можете задать это время таймером, выйти из обработки текущего прерывания, сделать что-нить полезное и по прерыванию от таймера вернуться к обработке следующего состояния автомата. Цитата Тут как и раньше с выключеным ЮСБ работает а с включеным нет. Угу.
|
|
|
|
|
Nov 6 2009, 09:45
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Цитата(Br.Misha @ Nov 6 2009, 12:03)  но почему когда выполняеться _delay_us он не может переключиться на обмен данными по usb? а еси например появляеться прерывание INT0 которое вызывает комп по USB? Может, но тогда может нарушаться обмен по ванвари - собственно, этим Вы и начали топик. 480 мкс - самый длинный, но и самый некритичный по допуску интервал в этом обмене. Я уже сказал, что, имхо, обмен по usb должен иметь приоритет перед обменом по ванвари. Т.к. запросами с компа контроллер управлять не может (почти), то остаётся только подстроить обмен по ванвари под паузы между обменом по usb, н-р, с помощью программного таймера, но это второй шаг в решении. Не как решение задачи, а костыль - можно, коль Вы читаете из 1820 полностью скрэчпэд, просто проверять CRC прочитанного.
|
|
|
|
|
Nov 6 2009, 11:33
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
Господа, о чем вы? Понятное дело, что единственный способ работать с 1W софтово - это вклеить 1W в драйвер USB. Между битиками в USB ловить битики в 1W. Иначе - никак. Другое дело - если есть свободный UART, вопрос с 1W решается легко и небрежно: CODE static UREG OWByte(UREG b ) { UREG i=8; UBRR0=10; //115200 do { UREG d=0x00; if (b&1) d=0xFF; __disable_interrupt(); UDR0=d; UCSR0A=(1<<TXC0); __enable_interrupt(); while(!UCSR0A_TXC0); b>>=1; if (UDR0>0xFE) b|=128; } while(--i); return b&255; }
static UREG OWReset(void) { UREG c; UCSR0B=(1<<RXEN0)|(1<<TXEN0); UBRR0=129; //9600 while(UCSR0A_RXC0) UDR0; //Зачистка буферов __disable_interrupt(); UDR0=0xF0; UCSR0A=(1<<TXC0); __enable_interrupt(); while(!UCSR0A_TXC0); c=UDR0; if (c!=0xF0) return 1; return 0; }
void StartMeasureTemp(void) { if (!OWReset()) return; OWByte(0xCC); //Skip ROM OWByte(0x44); //Convert Temperature }
REG16 ExtractMeasureTemp_one(void) { if (!OWReset()) return -273*16; OWByte(0xCC); //Skip ROM OWByte(0xBE); //Read scratchpad REG16 v=OWByte(0xFF); v|=OWByte(0xFF)<<8; return v; }
Только нужно заточить значения для UBRR под 12МГц (в приведенном исходнике - под 20). И подключить термометр - между ножками TXD и RXD процессора резистор 2.2к, вывод DQ термометра подключается к RXD. Питание не паразитное.
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|