Skip to content

Commit

Permalink
onewire: Implement scan for multiple devices (#139)
Browse files Browse the repository at this point in the history
* onewire: Implement `scan` for multiple devices

The former `scan` command only worked with a single 1-wire device attached.
However, the 1-wire bus is ment as a bus, so there might be several devices
connected to the bus. In that case, there is a suggested bus scanning scheme
(cf. https://www.maximintegrated.com/en/design/technical-documents/app-notes/1/187.html)
that makes clever use of pull-down collisions that will occur in such a
situation. This allows to scan for all devices by basically doing a tree
search. (The algorithm avoids recursion by using a few house-keeping variables
to keep search state while discovering all devices ROM addresses.)

* Reformat code

The file was given to `astyle -t --style=linux --lineend=linux hydrabus_mode_onewire.c`.
However, the crc constants array was left untouched to keep the commas aligned.

* Rework "int search_result" to proper "bool device_found_p"

* Add a comment to reference the bus scanning application note

* Another missed whitespace

* Fix missed variable rename
  • Loading branch information
jbglaw committed Sep 28, 2022
1 parent c2a43c3 commit 927dc50
Show file tree
Hide file tree
Showing 2 changed files with 200 additions and 54 deletions.
241 changes: 188 additions & 53 deletions src/hydrabus/hydrabus_mode_onewire.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,26 @@ static const char* str_prompt_onewire[] = {
"onewire1" PROMPT,
};

static uint8_t onewire_crc_table[] = {
0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65,
157,195, 33,127,252,162, 64, 30, 95, 1,227,189, 62, 96,130,220,
35,125,159,193, 66, 28,254,160,225,191, 93, 3,128,222, 60, 98,
190,224, 2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255,
70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89, 7,
219,133,103, 57,186,228, 6, 88, 25, 71,165,251,120, 38,196,154,
101, 59,217,135, 4, 90,184,230,167,249, 27, 69,198,152,122, 36,
248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91, 5,231,185,
140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205,
17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80,
175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238,
50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115,
202,148,118, 40,171,245, 23, 73, 8, 86,180,234,105, 55,213,139,
87, 9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22,
233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168,
116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53
};


void onewire_init_proto_default(t_hydra_console *con)
{
mode_config_proto_t* proto = &con->mode->proto;
Expand All @@ -47,21 +67,21 @@ static void show_params(t_hydra_console *con)
mode_config_proto_t* proto = &con->mode->proto;

cprintf(con, "Device: onewire%d\r\nGPIO resistor: %s\r\n",
proto->dev_num + 1,
proto->config.onewire.dev_gpio_pull == MODE_CONFIG_DEV_GPIO_PULLUP ? "pull-up" :
proto->config.onewire.dev_gpio_pull == MODE_CONFIG_DEV_GPIO_PULLDOWN ? "pull-down" :
"floating");
proto->dev_num + 1,
proto->config.onewire.dev_gpio_pull == MODE_CONFIG_DEV_GPIO_PULLUP ? "pull-up" :
proto->config.onewire.dev_gpio_pull == MODE_CONFIG_DEV_GPIO_PULLDOWN ? "pull-down" :
"floating");

cprintf(con, "Bit order: %s first\r\n",
proto->config.onewire.dev_bit_lsb_msb == DEV_FIRSTBIT_MSB ? "MSB" : "LSB");
proto->config.onewire.dev_bit_lsb_msb == DEV_FIRSTBIT_MSB ? "MSB" : "LSB");
}

bool onewire_pin_init(t_hydra_console *con)
{
mode_config_proto_t* proto = &con->mode->proto;

bsp_gpio_init(BSP_GPIO_PORTB, ONEWIRE_PIN,
proto->config.onewire.dev_gpio_mode, proto->config.onewire.dev_gpio_pull);
proto->config.onewire.dev_gpio_mode, proto->config.onewire.dev_gpio_pull);
return true;
}

Expand Down Expand Up @@ -91,11 +111,11 @@ void onewire_write_bit(t_hydra_console *con, uint8_t bit)
{
onewire_mode_output(con);
onewire_low();
if(bit){
if(bit) {
DelayUs(6);
onewire_high();
DelayUs(64);
}else{
} else {
DelayUs(60);
onewire_high();
DelayUs(10);
Expand Down Expand Up @@ -135,17 +155,32 @@ static void bitr(t_hydra_console *con)
cprintf(con, hydrabus_mode_str_read_one_u8, rx_data);
}

void onewire_start(t_hydra_console *con)
static bool
onewire_start_and_check(t_hydra_console *con)
{
bool devices_present_p;

/* Pull low for >= 480µsec to signal a bus reset. */
onewire_mode_output(con);
onewire_low();
DelayUs(480);
onewire_high();

/* After some 15..60µsec, devices will pull down the bus if anybody recognized the reset pulse. */
DelayUs(70);
onewire_mode_input(con);
// Can check for device presence here
devices_present_p = ! bsp_gpio_pin_read (BSP_GPIO_PORTB, ONEWIRE_PIN);

/* Wait some more to let all devices release their presence pulse. */
DelayUs(410);

return devices_present_p;
}

void onewire_start(t_hydra_console *con)
{
onewire_start_and_check (con);
return;
}

void onewire_write_u8(t_hydra_console *con, uint8_t tx_data)
Expand Down Expand Up @@ -180,58 +215,158 @@ uint8_t onewire_read_u8(t_hydra_console *con)
return value;
}

void onewire_scan(t_hydra_console *con)
static uint8_t onewire_crc8(uint8_t value, struct onewire_scan_state *state)
{
uint8_t id_bit_number = 0;
uint8_t last_zero = 0;
uint8_t id_bit = 0, cmp_id_bit = 0;
uint8_t search_direction = 0;
uint8_t LastDiscrepancy = 0;
uint8_t LastDeviceFlag = 0;
uint8_t i;
uint8_t ROM_NO[8] = {0};
onewire_start(con);
onewire_write_u8(con, 0xf0);
cprintf(con, "Discovered devices : ");
while(!LastDeviceFlag && !hydrabus_ubtn()) {
do{
state->crc8 = onewire_crc_table[state->crc8 ^ value];

return state->crc8;
}

/* onewire_search() is based on the official bus scan example published by
* Dallas and now hosted by Maxim. It can be found at
* https://www.maximintegrated.com/en/design/technical-documents/app-notes/1/187.html
*
* Changes have been made: Variables no longer use camel case and search state
* was move from a bunch of global variables to a struct. But the overall
* design remains unchanged.
*/
static bool onewire_search(t_hydra_console *con, struct onewire_scan_state *state, enum onewire_scan_mode mode)
{
int id_bit_number;
int last_zero, rom_byte_number;
bool device_found_p;
int id_bit, cmp_id_bit;
unsigned char rom_byte_mask, search_direction;

/* Initialize global search state. */
if(mode == onewire_scan_start) {
state->last_discrepancy = 0;
state->last_device_p = false;
state->last_family_discrepancy = 0;
}

/* Initialize for this search. */
id_bit_number = 1;
last_zero = 0;
rom_byte_number = 0;
rom_byte_mask = 1;
device_found_p = false;
state->crc8 = 0;

/* If the last call was not the last one. */
if(!state->last_device_p) {
/* 1-Wire reset. */
if(!onewire_start_and_check(con)) {
/* Reset the search. */
state->last_discrepancy = 0;
state->last_device_p = false;
state->last_family_discrepancy = 0;
return false;
}

/* Issue the search command. */
onewire_write_u8(con, ONEWIRE_CMD_SEARCHROM);

/* Loop to do the search. */
do {
/* Read a bit and its complement. */
id_bit = onewire_read_bit(con);
cmp_id_bit = onewire_read_bit(con);
if(id_bit && cmp_id_bit) {

/* Check for no devices on 1-wire. */
if(id_bit && cmp_id_bit)
break;
} else {
if (!id_bit && !cmp_id_bit) {
if (id_bit_number == LastDiscrepancy) {
search_direction = 1;
} else {
if (id_bit_number > LastDiscrepancy) {
search_direction = 0;
} else {
search_direction =
(ROM_NO[id_bit_number/8]
& (id_bit_number%8))>0;
}
}
else {
/* All devices coupled have 0 or 1. */
if(id_bit != cmp_id_bit)
search_direction = id_bit; /* Bit write value for search. */
else {
/* If this discrepancy if before the Last Discrepancy
on a previous next then pick the same as last time. */
if(id_bit_number < state->last_discrepancy)
search_direction = ((state->ROM_ADDR[rom_byte_number] & rom_byte_mask) > 0);
else
/* If equal to last pick 1, if not then pick 0. */
search_direction = (id_bit_number == state->last_discrepancy);

/* If 0 was picked then record its position in LastZero. */
if(search_direction == 0) {
last_zero = id_bit_number;

/* Check for Last discrepancy in family. */
if(last_zero < 9)
state->last_family_discrepancy = last_zero;
}
} else {
search_direction = id_bit;
}

/* Set or clear the bit in the ROM byte rom_byte_number
with mask rom_byte_mask. */
if(search_direction == 1)
state->ROM_ADDR[rom_byte_number] |= rom_byte_mask;
else
state->ROM_ADDR[rom_byte_number] &= ~rom_byte_mask;

/* Serial number search direction write bit. */
onewire_write_bit(con, search_direction);

/* Increment the byte counter id_bit_number
and shift the mask rom_byte_mask. */
id_bit_number++;
rom_byte_mask <<= 1;

/* If the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask. */
if(rom_byte_mask == 0) {
onewire_crc8(state->ROM_ADDR[rom_byte_number], state); /* Accumulate the CRC. */
rom_byte_number++;
rom_byte_mask = 1;
}
}
ROM_NO[id_bit_number/8] |= search_direction<<(id_bit_number%8);
onewire_write_bit(con, search_direction);
id_bit_number++;
}while(id_bit_number<64);
LastDiscrepancy = last_zero;
if (LastDiscrepancy == 0) {
LastDeviceFlag = true;
}
for(i=0; i<8; i++) {
cprintf(con, "%02X ", ROM_NO[i]);
} while (rom_byte_number < 8); /* Loop until through all ROM bytes 0-7. */

/* If the search was successful then... */
if(!((id_bit_number < 65) || (state->crc8 != 0))) {
/* ...search successful so set last_discrepancy,last_device_p,device_found_p. */
state->last_discrepancy = last_zero;

/* Check for last device. */
if (state->last_discrepancy == 0)
state->last_device_p = true;

device_found_p = true;
}
}

/* If no device found then reset counters so next 'search' will be like a first. */
if(!device_found_p || !state->ROM_ADDR[0]) {
state->last_discrepancy = 0;
state->last_device_p = false;
state->last_family_discrepancy = 0;
device_found_p = false;
}

return device_found_p;
}

static void onewire_scan(t_hydra_console *con)
{
int i;
int count = 0;
bool device_found_p;
struct onewire_scan_state state;

cprintf(con, "Scanning bus for devices.\r\n");

device_found_p = onewire_search(con, &state, onewire_scan_start);
while(device_found_p) {
cprintf(con, "%i: ", ++count);
for(i = 0; i < 8; i++)
cprintf(con, "%02X ", state.ROM_ADDR[i]);
cprintf(con, "\r\n");

device_found_p = onewire_search(con, &state, onewire_scan_continue);
}

return;
}

static int init(t_hydra_console *con, t_tokenline_parsed *p)
Expand Down Expand Up @@ -308,7 +443,7 @@ static uint32_t write(t_hydra_console *con, uint8_t *tx_data, uint8_t nb_data)
cprintf(con, hydrabus_mode_str_mul_write);
for(i = 0; i < nb_data; i++) {
cprintf(con, hydrabus_mode_str_mul_value_u8,
tx_data[i]);
tx_data[i]);
}
cprintf(con, hydrabus_mode_str_mul_br);
}
Expand All @@ -330,7 +465,7 @@ static uint32_t read(t_hydra_console *con, uint8_t *rx_data, uint8_t nb_data)
cprintf(con, hydrabus_mode_str_mul_read);
for(i = 0; i < nb_data; i++) {
cprintf(con, hydrabus_mode_str_mul_value_u8,
rx_data[i]);
rx_data[i]);
}
cprintf(con, hydrabus_mode_str_mul_br);
}
Expand All @@ -342,7 +477,7 @@ static uint32_t dump(t_hydra_console *con, uint8_t *rx_data, uint8_t nb_data)
uint8_t i;

i = 0;
while(i < nb_data){
while(i < nb_data) {
rx_data[i] = onewire_read_u8(con);
i++;
}
Expand Down
13 changes: 12 additions & 1 deletion src/hydrabus/hydrabus_mode_onewire.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,15 @@ void onewire_send_bit(t_hydra_console *con, uint8_t bit);
uint8_t onewire_read_bit(t_hydra_console *con);
void onewire_cleanup(t_hydra_console *con);
void onewire_start(t_hydra_console *con);
void onewire_scan(t_hydra_console *con);

struct onewire_scan_state {
uint8_t ROM_ADDR[8];
int last_discrepancy;
int last_family_discrepancy;
bool last_device_p;
uint8_t crc8;
};
enum onewire_scan_mode {
onewire_scan_start,
onewire_scan_continue,
};

0 comments on commit 927dc50

Please sign in to comment.