added 1wire search algorithm

sipo
Ondřej Hruška 6 years ago
parent a81190c2e1
commit 0e378b6547
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 34
      units/1wire/_ow_checksum.c
  2. 23
      units/1wire/_ow_checksum.h
  3. 4
      units/1wire/_ow_commands.h
  4. 58
      units/1wire/_ow_internal.h
  5. 2
      units/1wire/_ow_low_level.c
  6. 67
      units/1wire/_ow_low_level.h
  7. 121
      units/1wire/_ow_search.c
  8. 81
      units/1wire/_ow_search.h
  9. 47
      units/1wire/unit_1wire.c
  10. 1
      utils/error.h

@ -0,0 +1,34 @@
//
// Created by MightyPork on 2018/02/01.
//
#include "platform.h"
#include "_ow_checksum.h"
static inline uint8_t crc8_bits(uint8_t data)
{
uint8_t crc = 0;
if(data & 1) crc ^= 0x5e;
if(data & 2) crc ^= 0xbc;
if(data & 4) crc ^= 0x61;
if(data & 8) crc ^= 0xc2;
if(data & 0x10) crc ^= 0x9d;
if(data & 0x20) crc ^= 0x23;
if(data & 0x40) crc ^= 0x46;
if(data & 0x80) crc ^= 0x8c;
return crc;
}
static uint8_t crc8_add(uint8_t cksum, uint8_t byte)
{
return crc8_bits(byte ^ cksum);
}
uint8_t ow_checksum(const uint8_t *buff, uint16_t len)
{
uint8_t cksum = 0;
for(uint16_t i = 0; i < len; i++) {
cksum = crc8_add(cksum, buff[i]);
}
return cksum;
}

@ -0,0 +1,23 @@
//
// Created by MightyPork on 2018/02/01.
//
#ifndef GEX_F072_OW_CHECKSUM_H
#define GEX_F072_OW_CHECKSUM_H
#include <stdint.h>
/**
* Compute a 1-wire type checksum.
* If the buffer includes the checksum, the result should be 0.
*
* (this function may be used externally, or you can delete the implementation
* from the c file if another implementation is already available)
*
* @param[in] buf - buffer of bytes to verify
* @param[in] len - buffer length
* @return checksum
*/
uint8_t ow_checksum(const uint8_t *buf, uint16_t len);
#endif //GEX_F072_OW_CHECKSUM_H

@ -5,10 +5,6 @@
#ifndef GEX_F072_OW_COMMANDS_H
#define GEX_F072_OW_COMMANDS_H
#ifndef OW_INTERNAL
#error bad include!
#endif
#define OW_ROM_SEARCH 0xF0
#define OW_ROM_READ 0x33
#define OW_ROM_MATCH 0x55

@ -9,7 +9,7 @@
#error bad include!
#endif
#include "_ow_commands.h"
#include "_ow_search.h"
/** Private data structure */
struct priv {
@ -24,63 +24,9 @@ struct priv {
bool busy; // flag used when the timer is running
uint32_t busyStart;
TF_ID busyRequestId;
struct ow_search_state searchState;
};
// Prototypes
/**
* Reset the 1-wire bus
*/
bool ow_reset(Unit *unit);
/**
* Write a bit to the 1-wire bus
*/
void ow_write_bit(Unit *unit, bool bit);
/**
* Read a bit from the 1-wire bus
*/
bool ow_read_bit(Unit *unit);
/**
* Write a byte to the 1-wire bus
*/
void ow_write_u8(Unit *unit, uint8_t byte);
/**
* Write a halfword to the 1-wire bus
*/
void ow_write_u16(Unit *unit, uint16_t halfword);
/**
* Write a word to the 1-wire bus
*/
void ow_write_u32(Unit *unit, uint32_t word);
/**
* Write a doubleword to the 1-wire bus
*/
void ow_write_u64(Unit *unit, uint64_t dword);
/**
* Read a byte form the 1-wire bus
*/
uint8_t ow_read_u8(Unit *unit);
/**
* Read a halfword form the 1-wire bus
*/
uint16_t ow_read_u16(Unit *unit);
/**
* Read a word form the 1-wire bus
*/
uint32_t ow_read_u32(Unit *unit);
/**
* Read a doubleword form the 1-wire bus
*/
uint64_t ow_read_u64(Unit *unit);
#endif //GEX_F072_OW_INTERNAL_H

@ -5,10 +5,10 @@
//
#include "platform.h"
#include "unit_base.h"
#define OW_INTERNAL
#include "_ow_internal.h"
#include "_ow_low_level.h"
static inline void ow_pull_high(Unit *unit)
{

@ -0,0 +1,67 @@
//
// Created by MightyPork on 2018/02/01.
//
#ifndef GEX_F072_OW_LOW_LEVEL_H
#define GEX_F072_OW_LOW_LEVEL_H
#include "platform.h"
#include "unit_base.h"
#include "_ow_low_level.h"
/**
* Reset the 1-wire bus
*/
bool ow_reset(Unit *unit);
/**
* Write a bit to the 1-wire bus
*/
void ow_write_bit(Unit *unit, bool bit);
/**
* Read a bit from the 1-wire bus
*/
bool ow_read_bit(Unit *unit);
/**
* Write a byte to the 1-wire bus
*/
void ow_write_u8(Unit *unit, uint8_t byte);
/**
* Write a halfword to the 1-wire bus
*/
void ow_write_u16(Unit *unit, uint16_t halfword);
/**
* Write a word to the 1-wire bus
*/
void ow_write_u32(Unit *unit, uint32_t word);
/**
* Write a doubleword to the 1-wire bus
*/
void ow_write_u64(Unit *unit, uint64_t dword);
/**
* Read a byte form the 1-wire bus
*/
uint8_t ow_read_u8(Unit *unit);
/**
* Read a halfword form the 1-wire bus
*/
uint16_t ow_read_u16(Unit *unit);
/**
* Read a word form the 1-wire bus
*/
uint32_t ow_read_u32(Unit *unit);
/**
* Read a doubleword form the 1-wire bus
*/
uint64_t ow_read_u64(Unit *unit);
#endif //GEX_F072_OW_LOW_LEVEL_H

@ -0,0 +1,121 @@
//
// Created by MightyPork on 2018/02/01.
//
#include "platform.h"
#include "unit_1wire.h"
#define OW_INTERNAL
#include "_ow_search.h"
#include "_ow_internal.h"
#include "_ow_low_level.h"
#include "_ow_checksum.h"
#include "_ow_commands.h"
void ow_search_init(Unit *unit, uint8_t command, bool test_checksums)
{
if (unit->driver != &UNIT_1WIRE)
trap("Wrong unit type - %s", unit->driver->name);
assert_param(command == OW_ROM_SEARCH || command == OW_ROM_ALM_SEARCH);
struct priv *priv = unit->data;
struct ow_search_state *state = &priv->searchState;
state->prev_last_fork = 64;
memset(state->prev_code, 0, 8);
state->status = OW_SEARCH_MORE;
state->command = command;
state->first = true;
state->test_checksums = test_checksums;
}
uint16_t ow_search_run(Unit *unit, ow_romcode_t *codes, uint16_t capacity)
{
if (unit->driver != &UNIT_1WIRE)
trap("Wrong unit type - %s", unit->driver->name);
assert_param(codes);
struct priv *priv = unit->data;
struct ow_search_state *state = &priv->searchState;
if (state->status != OW_SEARCH_MORE) return 0;
uint16_t found_devices = 0;
while (found_devices < capacity) {
uint8_t index = 0;
ow_romcode_t code = {};
int8_t last_fork = -1;
// Start a new transaction. Devices respond to reset
if (!ow_reset(unit)) {
state->status = OW_SEARCH_FAILED;
goto done;
}
// Send the search command (SEARCH_ROM, SEARCH_ALARM)
ow_write_u8(unit, state->command);
uint8_t *code_byte = &code[0];
bool p, n;
while (index != 64) {
// Read a bit and its complement
p = ow_read_bit(unit);
n = ow_read_bit(unit);
if (!p && !n) {
// A fork: there are devices on the bus with different bit value
// (the bus is open-drain, in both cases one device pulls it low)
if ((found_devices > 0 || !state->first) && index < state->prev_last_fork) {
// earlier than the last fork, take the same turn as before
p = ow_code_getbit(state->prev_code, index);
if (!p) last_fork = index; // remember for future runs, 1 not explored yet
}
else if (index == state->prev_last_fork) {
p = 1; // both forks are now exhausted
}
else { // a new fork
last_fork = index;
}
}
else if (p && n) {
// No devices left connected - this doesn't normally happen
state->status = OW_SEARCH_FAILED;
goto done;
}
// All devices have a matching bit here, or it was resolved in a fork
if (p) *code_byte |= (1 << (index & 7));
ow_write_bit(unit, p);
index++;
if((index & 7) == 0) {
code_byte++;
}
}
memcpy(state->prev_code, code, 8);
if (!state->test_checksums || 0 == ow_checksum(code, 8)) {
// Record a found address
memcpy(codes[found_devices], code, 8);
found_devices++;
}
// Stop condition
if (last_fork == -1) {
state->status = OW_SEARCH_DONE;
goto done;
}
state->prev_last_fork = last_fork;
}
done:
state->first = false;
return found_devices;
}

@ -0,0 +1,81 @@
//
// Created by MightyPork on 2018/02/01.
//
#ifndef GEX_F072_OW_SEARCH_H
#define GEX_F072_OW_SEARCH_H
#ifndef OW_INTERNAL
#error bad include!
#endif
#include <stdint.h>
#include <stdbool.h>
#include "unit_base.h"
// --------------------------------------------------------------------------------------
/**
* Data type holding a romcode
*/
typedef uint8_t ow_romcode_t[8];
/**
* Get a single bit from a romcode
*/
#define ow_code_getbit(code, index) (bool)((code)[(index) >> 3] & (1 << ((index) & 7)))
/**
* Convert to unsigned 64-bit integer
* (works only on little-endian systems - eg. OK on x86/x86_64, not on PowerPC)
*/
#define ow_romcode_to_u64(code) (*((uint64_t *) (void *)(code)))
/**
* States of the search algorithm
*/
enum ow_search_result {
OW_SEARCH_DONE = 0,
OW_SEARCH_MORE = 1,
OW_SEARCH_FAILED = 2,
};
/**
* Internal state of the search algorithm.
* Check status to see if more remain to be read or an error occurred.
*/
struct ow_search_state {
int8_t prev_last_fork;
ow_romcode_t prev_code;
uint8_t command;
enum ow_search_result status;
bool first;
bool test_checksums;
};
/**
* Init the search algorithm state structure
*
* @param[out] state - inited struct
* @param[in] command - command to send for requesting the search (e.g. SEARCH_ROM)
* @param[in] test_checksums - verify checksums of all read romcodes
*/
void ow_search_init(Unit *unit, uint8_t command, bool test_checksums);
/**
* Perform a search of the 1-wire bus, with a state struct pre-inited
* using ow_search_init().
*
* Romcodes are stored in the provided array in a numerically ascending order.
*
* This function may be called repeatedly to retrieve more addresses than could fit
* in the address buffer.
*
* @param[in,out] state - search state, used for multiple calls with limited buffer size
* @param[out] codes - buffer for found romcodes
* @param[in] capacity - buffer capacity
* @return number of romcodes found. Search status is stored in state->status
*/
uint16_t ow_search_run(Unit *unit, ow_romcode_t *codes, uint16_t capacity);
#endif //GEX_F072_OW_SEARCH_H

@ -10,6 +10,10 @@
// 1WIRE master
#define OW_INTERNAL
#include "_ow_internal.h"
#include "_ow_commands.h"
#include "_ow_search.h"
#include "_ow_checksum.h"
#include "_ow_low_level.h"
// ------------------------------------------------------------------------
@ -176,7 +180,8 @@ enum PinCmd_ {
CMD_CHECK_PRESENCE = 0, // simply tests that any devices are attached
CMD_SEARCH_ADDR = 1, // perform a scan of the bus, retrieving all found device ROMs
CMD_SEARCH_ALARM = 2, // like normal scan, but retrieve only devices with alarm
CMD_READ_ADDR = 3, // read the ROM code from a single device (for single-device bus)
CMD_SEARCH_CONTINUE = 3, // continue the previously started scan, retrieving more devices
CMD_READ_ADDR = 4, // read the ROM code from a single device (for single-device bus)
CMD_SKIP_WRITE = 10, // write multiple bytes using the SKIP_ROM command
CMD_SKIP_READ = 11, // write and read multiple bytes using the SKIP_ROM command
@ -213,14 +218,38 @@ static error_t U1WIRE_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command,
if (priv->busy) return E_BUSY;
uint8_t search_cmd = OW_ROM_SEARCH;
switch (command) {
case CMD_SEARCH_ALARM:
search_cmd = OW_ROM_ALM_SEARCH;
// pass-through
case CMD_SEARCH_ADDR:
// TODO
return E_NOT_IMPLEMENTED;
ow_search_init(unit, search_cmd, true);
// pass through
case CMD_SEARCH_CONTINUE:;
ow_romcode_t *codes = (void *) unit_tmp512;
uint16_t count = ow_search_run(unit, codes, UNIT_TMP_LEN/8);
if (priv->searchState.status == OW_SEARCH_FAILED) {
return E_HW_FAULT;
}
case CMD_SEARCH_ALARM:
// TODO
return E_NOT_IMPLEMENTED;
// First byte of the response is a flag whether there are more devices
// to be found using CMD_SEARCH_CONTINUE
uint8_t status_code = (uint8_t) (priv->searchState.status == OW_SEARCH_MORE);
TF_Msg msg = {
.frame_id = frame_id,
.type = MSG_SUCCESS,
.len = (TF_LEN) (count * 8 + 1),
};
TF_Respond_Multipart(comm, &msg);
TF_Multipart_Payload(comm, &status_code, 1);
// the codes are back-to-back stored inside the buffer, we send it directly
// (it's already little-endian, as if built by PayloadBuilder)
TF_Multipart_Payload(comm, (uint8_t *) unit_tmp512, (uint32_t) (count * 8));
TF_Multipart_Close(comm);
return E_SUCCESS;
/** Simply check presence of any devices on the bus. Responds with SUCCESS or HW_TIMEOUT */
case CMD_CHECK_PRESENCE:
@ -234,7 +263,7 @@ static error_t U1WIRE_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command,
case CMD_READ_ADDR:
// reset
presence = ow_reset(unit);
if (!presence) return E_HW_TIMEOUT;
if (!presence) return E_HW_FAULT;
// command
ow_write_u8(unit, OW_ROM_READ);
@ -259,7 +288,7 @@ static error_t U1WIRE_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command,
case CMD_SKIP_WRITE:
// reset
presence = ow_reset(unit);
if (!presence) return E_HW_TIMEOUT;
if (!presence) return E_HW_FAULT;
// MATCH_ROM+addr, or SKIP_ROM
cmd_match_skip(unit, command, pp);
@ -282,7 +311,7 @@ static error_t U1WIRE_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command,
case CMD_SKIP_READ:;
// reset
presence = ow_reset(unit);
if (!presence) return E_HW_TIMEOUT;
if (!presence) return E_HW_FAULT;
// MATCH_ROM+addr, or SKIP_ROM
cmd_match_skip(unit, command, pp);

@ -21,6 +21,7 @@
X(MALFORMED_COMMAND, NULL) \
X(NOT_APPLICABLE, NULL) \
X(HW_TIMEOUT, NULL) \
X(HW_FAULT, NULL) \
X(NO_SUCH_UNIT, NULL) \
X(PROTOCOL_BREACH, NULL) /* eating with the wrong spoon */ \
X(BAD_UNIT_TYPE, NULL) \

Loading…
Cancel
Save