Ви не увійшли.
Всім миру та добра.
Працюю з МК Silabs (C8051F310, ...F320, ...F330, ...F340, ...F480) в середовищі "Silicon Labs Simplicity Studio v3 Keil", тому створюю самостійно "бібліотеки" для роботи з різними периферійними модулями. Недавно тут, на arduino.ua магазині, придбав модуль Honey для того щоб зчитувати та зберігати значення газового лічильника.
По даташиту розібрався як зчитувати значення лічильників...
//======================================================================================//
U8 DS2423_getCounters(U8 *vROM, U8 nomCounters)
{
lng xClk;
U8 data vB;
vDS2423_Err=1; //якщо перевірка буде хибна, то помилка 0х01.
if (*vROM!=0xFF) //перевіряю, чи в масиві є S/N.
{ //в масиві є прописаний S/N, тоді...
vDS2423_Err++; //якщо перевірка буде хибна, то помилка 0х02.
if (*vROM==0x1D) //перевіряю, чи це S/N датчиків DS2423.
{ //так, це S/N цих датчиків температури, тоді...
if (lWire_Macth_ROM(vROM)==0) // зброс всіх датчиків і передача S/N потрібного датчика.
{ // якщо вдало, то.....
vDS2423_Err=0; // помилок не має.
lWire_WR(0xA5); // запис команди "READ_MEM_COUNTER"
if (nomCounters==0) lWire_WR(0xC0); // запис молодшого адресу сторінки лічильника "А"
else lWire_WR(0xE0); // ------------------------------------------ "В"
lWire_WR(0x01); // запис старшого адресу сторінки (16 сторінок по 32 байта)
for (vB=0; vB<42; vB++) vArrayRAM[vB]=lWire_RD();// зчитую з датчика в буфер vArrayRAM[], 42 байта.
//........тепер розбираю байти масива vArrayRAM[].....................................................//
// vArrayRAM[ 0]...vArrayRAM[31] - 32 байта RAM-сторінки.
xClk.b[3]=vArrayRAM[32]; // 4 байта
xClk.b[2]=vArrayRAM[33]; // лічильника
xClk.b[1]=vArrayRAM[34]; //
xClk.b[0]=vArrayRAM[35]; //
// vArrayRAM[36]...vArrayRAM[39] - 4 байта з нульовими значеннями.
// vArrayRAM[40]...vArrayRAM[41] - 2 байта CRC.
if (xClk.l==0xFFFFFFFF) vDS2423_Err=2; // помилка зчитування RAM.
else // інакше - все ОК, тоді...
{
vDS2423_Counter.l =xClk.l; // зберігаю значення лічильника та
vDS2423_Bat =((5.0-0.68)*vArrayRAM[24]/256)*1000;// значення батареї живлення DS2423.
}
}
else vDS2423_Err=lWire_Err; // помилка збросу датчиків(0х80-лінія зайнята;0х81-датчики відсутні;0х82-лінія зайнята)
}
}
return(vDS2423_Err);
}
//======================================================================================//
, а от як підраховувати CRC щось ні як не получається.
Допоможіть, будь ласка, з алгоритмом підрахунку CRC, бо сам осилити поліном Х16+Х15+Х2+1 не зможу.
Пробував функцію "DS2423.prototype._check_crc16 = function (buf)" яка є в пост#10 першої сторінки цієї теми, тохи перероблено...
U16 crc16(const U8 * input, U16 len)
{
static const U8 oddparity[16] = { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 };
U16 cdata;
U16 xCRC;
U16 iInx;
xCRC=0;
for (iInx = 0 ; iInx < len ; iInx++)
{
// Even though we're just copying a byte from the input, we'll be doing 16-bit computation with it.
cdata = input[iInx];
cdata = (cdata ^ xCRC) & 0xFF;
xCRC >>= 8;
if (oddparity[cdata & 0x0F] ^ oddparity[cdata >> 4]) xCRC ^= 0xC001;
cdata <<= 6;
xCRC ^= cdata;
cdata <<= 1;
xCRC ^= cdata;
}
return xCRC;
}
щось не те получається.
Знайшов ще таку реалізацію...
U16 CalcCRCbyAlgorithm(U8 * pDataBuffer, U16 usDataLen)
{
/* Use the Modbus algorithm as detailed in the Watlow comms guide */
//const U16 POLYNOMIAL = 0xA001;
const U16 POLYNOMIAL = 0xA001;
U16 wCrc;
U16 iByte, iBit;
/* Initialize CRC */
wCrc = 0xFFFF;
for (iByte = 0; iByte < usDataLen; iByte++)
{
wCrc ^= *(pDataBuffer + iByte); /* Exclusive-OR the byte with the CRC */
for (iBit = 0; iBit <= 7; iBit++) /* Loop through all 8 data bits */
{
/* If the LSB is 1, shift the CRC and XOR the polynomial mask with the CRC */
/* Note - the bit test is performed before the rotation, so can't move the << here */
if (wCrc & 0x0001)
{
wCrc >>= 1;
wCrc ^= POLYNOMIAL;
}
else
{
wCrc >>= 1; /* Just rotate it */
}
}
}
return wCrc;
}
Теж не той результат.
Прошу вашої допомоги.
Наперед...Дякую.
Остання редакція Cherep_VM (2024-09-08 20:40:41)
Неактивний
Ось вам приклад, щоб розібратися:
#include <stdio.h>
#include <stdint.h>
uint16_t crc16_update(uint16_t crc, uint8_t a)
{
int i;
crc ^= a;
for (i = 0; i < 8; ++i) {
if (crc & 1)
crc = (crc >> 1) ^ 0xA001;
else
crc = (crc >> 1);
}
return crc;
}
int main()
{
uint8_t buf[10] = {11, 12, 13, 14, 15, 16, 17, 18};
uint16_t crc;
int i;
/* додавання crc до повідомлення */
crc = 0;
for (i = 0; i < 8; i++)
crc = crc16_update(crc, buf[i]);
buf[i++] = ~(crc & 0xff);
buf[i++] = ~(crc >> 8);
/* перевірка повідомлення з crc */
crc = 0;
for (i = 0; i < 10; i++)
crc = crc16_update(crc, buf[i]);
if (crc == 0xb001)
printf("crc okn");
else
printf("crc errorn");
}
Перевіряти контрольну суму можна тим же способом, що і заповнювали, або використати властивість, що контрольна сума повідомлення разом його інвертованою контрольною сумою завжди буде 0xb001.
Звичайно crc16_update() можна оптимізувати, наприклад, як у вашому прикладі з oddparity.
Зверніть увагу, що в ds2423 при першому читанні сторінки контрольна сума обчислюється не лише над даними, чотирма нульовими байтами і чотирма байтами лічильника, але ще спочатку і над самою командою (0xa5) і двома байтами адреси (а для наступних сторінок, якщо читаємо далі підряд, вже лише над всіма байтами сторінки, 4 нульовими байтами і 4 байтами лічильника). Також не обов'язково вичитувати всі 32 байти сторінки, якщо потрібен лише лічильник, можна почати з останнього байту сторінки, ось тут приклад, як найпростіше прочитати лише байти лічильника.
Активний
Для вашого прикладу на фото:
#include <stdio.h>
#include <stdint.h>
uint16_t crc16_update(uint16_t crc, uint8_t a)
{
int i;
crc ^= a;
for (i = 0; i < 8; ++i) {
if (crc & 1)
crc = (crc >> 1) ^ 0xA001;
else
crc = (crc >> 1);
}
return crc;
}
int main()
{
uint8_t buf[45] = {
0xa5, 0xc0, 0x01, // READMEMCOUNTER, address
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xbb,0x00,0x00,0x60,0x00,0xed,0x00,0x41,
0x31,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
0xe3, 0x1b
};
uint16_t crc;
int i;
crc = 0;
for (i = 0; i < 43; i++)
crc = crc16_update(crc, buf[i]);
printf("crc = 0x%02x 0x%02xn", (uint8_t)~(crc & 0xff), (uint8_t)~(crc >> 8));
crc = 0;
for (i = 0; i < 45; i++)
crc = crc16_update(crc, buf[i]);
if (crc == 0xb001)
printf("crc okn");
else
printf("crc errorn");
}
Вивід:
crc = 0xe3 0x1b
crc ok
Активний
Ага, цікаво получається...., не сподівався що ще треба включати в підрахунок код команди та адрес сторінки. Попробую.
Дякую за поради!!!
Взагалі-то мені підрахунок CRC потрібно для того щоб щось прописувати в RAM-сторінки..., готуюсь до таких операцій. Якщо тільки зчитувати лічильники, то можна обходитись і без CRC.
Дуже вдячний за допомогу! Вечерком буду пробувати.
Неактивний
Цікаво, цікаво...., планую прикупити ще 3шт ваших DS2423-аналогів і за одно прикуплю парочку модулів розширення портів.
В процесі освоєння модуля DS2423-аналога, виявив те що він чутливий до рівня напруги в шині(використовував паразитне живлення модуля). При "стандартних" формуваннях часових затримок імпульсів для слотів читання/запису, цей модуль, час від часу, перезавантажувався(зчитував 0xFFFFFFFF лічильника), а DS18B20 працював без боїв. Підключивши осциллок, побачив, що є нестабільність, просадка рівня імпульсів, нижче 4,5В(шина підтягнута до +5В резистором 1кОм), через що модуль перезавантажувався. Тоді, "погрався" з тивалостями імпульсів(трохи збільшив) до тих пір щоб рівень був одинакових, що на передачу, що на прийом, і не нижчий за 4,8В. Проблема зникла! Обкатав на кабелі 10м більше доби, збоїв не було.
Може комусь пригодиться, формування сигналів....
#define inBit1Wire(inBit)
LineWire =0; delay_us( 8);
LineWire =1; delay_us( 1);
inBit =LineWire; delay_us(40);
LineWire =1; delay_us(90);
#define ouBit1Wire(ouBit)
LineWire =0; delay_us( 9);
LineWire =ouBit; delay_us(40);
LineWire =1; delay_us(90);
А це були формування "старих" сигналів....
#define inBit1Wire(inBit)
pin_1Wire =0; delay_us(2);
pin_1Wire =1; delay_us(12);
inBit =pin_1Wire; delay_us(70);
pin_1Wire =1; delay_us(5);
#define ouBit1Wire(ouBit)
pin_1Wire =0; delay_us(5);
pin_1Wire =ouBit; delay_us(70);
pin_1Wire =1; delay_us(5);
pin_1Wire - біт порта МК, до якого підєднані датчики.
inBit, ouBit - програмний біт, читання/передачі, які групуються в байти.
Остання редакція Cherep_VM (2024-09-09 12:41:41)
Неактивний
Це штатний режим роботи, при напрузі зовнішнього живлення <~4.5V (VCC або паразитного через DQ), лічильник вимикає 1-wire інтерфейс але продовжує рахувати імпульси. Пов'язано це з тим, що споживання лічильника йде з трьох джерел - з батарейки, з VCC, з DQ - кожне через діод, яка напруга більша, звідти і споживання, коли зовнішнє живлення (VCC, DQ) лічильник вважає низьким, то щоб менше споживати від батарейки, він вимикає інтерфейсну частину. Тобто VCC або DQ має бути >4.5V, про це сказано в характеристиках пристрою, що VCC і DQ мають бути в межах 4.5 .. 5.5V.
При паразитному живленні струм споживання йде через резистор підтяжки і напруга DQ, звичайно, просідає. При підтяжці 1кОм можна заживити 1, максимум 2 лічильники (я не робив детальних досліджень). Мінімум, що допускає специфікація 1-wire це підтяжка 500 Ом.
Активний
У вас трохи неправильні таймінги. Відправка нуля - це ~59мкс нуль на шині і не менше 1мкс одиниця, відправка одиниці - 5..10мкс нуль і ~55мкс одиниця. Слейв робить зчитування шини через 30-35мкс після падаючого фронту (тобто десь посередині) і визначає, там передали 0 чи 1. А якщо слейв хоче передати нуль, то він по падаючому фронту замикає шину на землю на 30-40мкс, тобто мастер повинен робити зчитування десь посередині - приблизно на 15..20й мікросекунді.
Активний
Напевно, вхід DQ має певну ємність, більша ніж у DS18B20, яка "поволі" заряджається і часу в 5мкс між слотами(старий варіант формування імпульсів), коли лінія звільнена, не встигає досягти +5В. А от коли час між слотами збільшив до 90мкс, то все стало нормально. Да, можна було зменшити опір резистора і можливо все теж стало-б нормальок.
У вас трохи неправильні таймінги.......
Так, я знаю...ви маєте рацію!
Раніше формував їх по специфікації на DS18B20. Тепер, коли "вирівнював" рівні імпульсів, емпірично, підігнав їх до стабільної роботи протоколу. Тобто щоб затримки були мінімально-оптимальні.
При нагоді, ще раз їх перегляну.
Неактивний
Я б ось це
#define inBit1Wire(inBit)
LineWire =0; delay_us( 8);
LineWire =1; delay_us( 1);
inBit =LineWire; delay_us(40);
LineWire =1; delay_us(90);
змінив на таке
#define inBit1Wire(inBit)
LineWire =0; delay_us( 8);
LineWire =1; delay_us(11);
inBit =LineWire; delay_us(30);
LineWire =1; delay_us(90);
бо лінія може не встигнути піднятись за 1мкс
Активний
Так, я вас правильно зрозумів. Якраз з цією паузою(delay_us( 1);) і посилено працював. Через те що мій переносний осцилл не може нормально, на таких тривалостях, працювати, синхронізуватись, я не можу сказати, що вказавши паузу в 1мкс, то вона дійсно буде в 1мкс....підозрюю, що там буде трохи більше і тому протокол працює без проблем. Зараз запущу домашній осцилл і ретельніше все перевірю.
З 2019 року реалізував дома щось типу "розумний дім". Срільнуло мені в голову зробити керування батареями опалення контролерами і показувати їхню роботу на ПК(планшет). Так і зробив і вже 5 років система працює. Контролери батарей мають по три датчики DS18B20(температура кімнати, температура батареї, температура теплотраси) і керує електроклапаном(12В) який то відкриває батарею, то закриває в залежності від заданої температури. Я до чого веду....обкатавши нові таймінги протоколу 1Wire, прописав їх в контролери(7шт) і вже тиждень все працює нормально. Але менш з тим...зараз попробую реалізувати ваші поради.
https://forum.arduino.ua/img/members/29 … shot_3.jpg
Остання редакція Cherep_VM (2024-09-09 21:03:23)
Неактивний
Встановив ваші, рекомендовані, таймінги...все Ок! Буду з ними працювати...це ж буде по феншую!
З підрахунком CRC визначився, все "грає та співає" !!!
Вдячний за ваші поради та підказки.
Інтуїтивно розумію, що треба, але про всяк випадок спитаю...
Тепер, коли буду щось писати в сторінку, то теж, в підрахунок CRC включати байти команди та адреса, я правильно розумію?
Неактивний
Тепер, коли буду щось писати в сторінку, то теж, в підрахунок CRC включати байти команди та адреса, я правильно розумію?
Треба дивитися даташит, шукаєте там Memory function flow chart, вам спочатку потрібна буде команда OFh для запису в скретчпад, бачите там "Master RX CRC16 of Command, Address, Data" - це значить, що потрібно включати, наступною командою 55h потрібно скопіювати скретчпад в сторінку.
Активний
Доброго дня.
Завершив створення алгоритму читати/писати в сторінки модуля DS2423..., все грає та співає!
В кінцевому варіанті реалізовано всього три функції:
U8 DS2423_getCounters (U8 nomCounters); //зчитую значення лічильника та значення напруги батарейки живлення модуля.
U8 DS2423_ReadRAM_COUNTER (U16 vAdr, U8 vKolB); //зчитую сторінку повністю(vKolB=42), або частково(vKolB<42).
U8 DS2423_WriteRAM (U16 vAdr, U8 *vRAM, U8 vKolB);//запис в сторінку "write_Scratchpad"+"read_Scratchpad"+"copy_Scratchpad".
Тепер залишилось модуль прилаштувати до газового лічильника, підключити до контролера і там модернізувати програму(під цей модуль).
Ще "погрався" з читанням/запису в користувацьку область(24байта) пам'яті модуля...чудово, можна де що зберігати.
Щиро вдячний за допомогу!
Всім здоров'я та вдачі!
Остання редакція Cherep_VM (2024-09-15 15:19:29)
Неактивний
Ще стикнувся з проблемою...МК контролера 3,2 вольтове, тому живлення відбувається від одного літіонового акума 3,6В, а лінію DQ треба "підвязати" до +5В...звідки взяти? Прийшлось "приліпити" маленьку повишалку(DC-DC 2,5..4,2В --> 5,1В). Добре що в коробочці контролера ще було вільне місце.
Неактивний
Якщо піни вашого МК не толерантні до 5В, то скористайтесь такою схемою (фактично схема перетворювача логічних рівнів):
Між GPIO4 на схемі (це ваш вихід МК) і +3.3V можна додати резистор 10-100кОм, або програмно використати вбудований в МК.
Активний
Проблема не в пінах МК(сімейство C8051F300...F380 та інші), вони мають толерантність до 5В, а в тому, що напруга джерела живлення схеми не перевищує 4,2В. Бо схема розроблялась коли я ще не знав про ваш модуль і для простоти живлення, вибрав варіант джерела від одного елемента акума. Чому від акума, бо треба було забезпечити автономність контролера, бо датчик з газ.лічильника - геркон і треба було мати постійну можливість його контролювати. Тепер, автономність забезпечує ваш модуль. І потреба в акумі відпадає, але переробляти вже не буду. Ну то таке..., добре що були в наявності мініатюрні модулі DC-DCповишалки.
Неактивний
датчик з газ.лічильника - геркон
Можна використати SS49E (аналоговий датчик Холла), але він потребує налаштування threshold-ів, це робиться записом в пам'ять лічильника і я для цієї процедури написав веб-інтерфейс (інструкція), що вже вимагає щоб лічильник був підключений до лінукса на якому має бути запущений owhttpd. Тут один користувач викладав урізану лише для ds2423 "емуляцію" owhttpd на мікроконтролері.
Активний
Ага, читав про "емуляцію", але, коли буде потреба, бажання і натхнення, то зможу, під Windows, на Delphi7, написати програму для налаштування DS2423, чи якогось іншого.
Неактивний
Коли відточите навички читання/записування сторінок, спробуйте ось цей мій пристрій.
Завдяки вашим посиланням на документацію по DS2450 та Googl-перекладачу, вдалось зрозуміти алгоритми взаємодії між МК та модулем. Особливо закручено було в записі даних в модуль, тільки переклавши з англійської на українську алгоритм виконання команди WRITE MEMORY [55H], вдалось зрозуміти роботу вашої функції "uint8_t write(uint8_t *addr, uint8_t *buf, uint8_t start, uint8_t cnt)" яку ви, люб'язно, надали для ознайомлення.
"Спілкування" з модулем працює, тепер буду, по тихеньку, вивчати/освоювати його функціонал.
Щиро вдячний за поради!
Остання редакція Cherep_VM (2024-09-21 21:04:50)
Неактивний