📜 ⬆️ ⬇️

(Not) for protothreads lovers dedicated to: High-level functions for working with 1-Wire

It is understood that we will write the firmware under the "bare iron". Otherwise, the use of protothreads does not make sense, because multitasking should be provided by OS. It is also understood that we need to implement several more or less complex algorithms associated with input-output operations. Well, as always in microcontrollers, the obvious requirements for saving RAM and power consumption.

As an example, consider the task of servicing devices on a 1-Wire bus. You can see the implementation of asynchronous primitives for it in my previous articles here and here .

For the PnP implementation, it is necessary that the program can independently determine the characteristics of the bus, such as the maximum allowable exchange rate, the list of identifiers of the devices currently connected and the conditions for their power supply.
The maximum permissible exchange rate, we define in order to subsequently communicate with high-speed devices as quickly as possible. At the same time, slow devices will not “notice” this exchange and will not interfere with us.

Power supply conditions must be known in order to (if necessary) after issuing a command to perform measurements (or programming an EEPROM) enable active pullup mode. Otherwise, when using parasite power, we get an error when trying to read the measurement result (well, or you will have to install low-impedance pull-up resistors, which is probably not a beautiful solution).
')
Well, the list of identifiers of currently connected devices is simply vital for us. Otherwise, how are we going to contact them?

Tire performance determination algorithm:
  1. We will try to perform the RESET procedure in OVERDRIVE mode. If a PRESENCE was detected at the same time, then at least some of the connected devices can operate at high speed. In this case, go to paragraph 3.
  2. We will try to perform the RESET procedure in normal mode. If a PRESENCE was detected at the same time, then there is at least one connected device on the 1-Wire bus and we proceed to step 3. Otherwise, there are no devices currently connected to the bus.
  3. We send the “Address all devices” command to the bus and then the “Read power conditions” command. In case at least one of the connected devices uses the parasite power mode,
    set the appropriate flag.

But the algorithm for determining the identifiers of connected devices is quite cumbersome. Its synchronous implementation is given in APPLICATION NOTE 187 , I just redid it into an asynchronous one.

At all stages of the algorithms, it is advisable to track errors that may occur due to interference on the 1-Wire bus. Depending on the point of error, you can either automatically try to repeat the operation being performed, or return a negative completion status.

Further, it is assumed that the reader has minimal knowledge of protothreads . It’s hard to understand English texts, maybe, for starters, read here and here .

Under the spoiler, an example of calling the procedures for determining bus parameters and detecting connected devices from the main program.

Main program
PT_INIT(&ptSearchContext.pt); /*          */ while(PT_SCHEDULE(c = ptOneWireProbeBus(&ptSearchContext.pt, &nested))) { if(PT_WAITING == c) { /*    ,   -  */ waitComplete(); continue; } } /*      */ ptOneWireInitWalkROM(&ptSearchContext); /*      1-Wire  */ while(PT_SCHEDULE(c = ptOneWireWalkROM(&ptSearchContext))) { if(PT_WAITING == c) { /*    ,   -  */ waitComplete(); continue; } /*   1-Wire   . *  S/N    ptSearchContext.romid */ __no_operation(); } /*    */ __no_operation(); 


This is a demo. In a real program, instead of calling the waitComplete () function, we can switch to serving other protothreads (and if not, then go into low-power mode).

Macros used in implementation
 #define OVERDRIVE() \ drv_onewire_context.overdrive #define PRESENCE_DETECTED() \ drv_onewire_context.presence #define PARASITE_POWER \ drv_onewire_context.parasite #define STATUS \ drv_onewire_context.status #define PT_WAIT_IO_COMPLETE() \ PT_WAIT_WHILE(TASK_CONTEXT, ONEWIRE_STATUS_PROGRESS == (dummy = drvOneWireStatus())) #define IO_SUCCESS() \ (ONEWIRE_STATUS_COMPLETE == dummy) #define PT_TX_BITS(_v,_n) do { \ if(drvOneWireTxBits((_v),(_n))) { \ PT_WAIT_IO_COMPLETE(); } else { \ dummy = ONEWIRE_STATUS_ERROR; } } while(0) #define PT_TX_BYTE(_v) \ PT_TX_BITS((_v), 8) #define PT_RX_BITS(_n) \ PT_TX_BITS(~0,(_n)) #define PT_TX_BYTE_CONST(_v) do { \ PT_TX_BYTE((_v)); \ if(!IO_SUCCESS() || ((_v) != drvOneWireRxBits(8))) { \ STATUS = ONEWIRE_STATUS_ERROR; } } while(0) 


Short description:

PT_WAIT_IO_COMPLETE ()
Waiting for I / O completion. Designed for use only inside the protothread.

PT_TX_BITS (_v, _n)
Passing _n bits from _v to the bus while waiting for an I / O operation to complete. Designed for use only inside the protothread.

PT_TX_BYTE (_v)
Sending a _v byte to the bus, waiting for an I / O operation to complete. Designed for use only inside the protothread.

PT_RX_BITS (_n)
Receiving the _n bit with waiting for completion of an I / O operation. Designed for use only inside the protothread.

PT_TX_BYTE_CONST (_v)
Sending a command byte (constant) to the bus with checking the absence of distortions of the transmitted data and waiting for the completion of an I / O operation. Designed for use only inside the protothread.

It should be noted that “waiting for completion of input-output” in this case means not a dead loop with checking of any condition, but an interruption of the current protothread with the status PT_WAITING. This allows you to perform other protothreads with periodic checking of the current until the end of the activated I / O operation.

Passing the addressing command of all devices
 PT_THREAD(ptOneWireTargetAll(struct pt * _pt)) { uint8_t dummy; PT_BEGIN(TASK_CONTEXT); PT_TX_BYTE_CONST(OP_SKIP_ROM); PT_END(TASK_CONTEXT); } 


The addressing operation of all devices can be used with other 1-Wire bus commands, so it was framed as a separate protothreads.

The procedure for determining the parameters of the bus
 PT_THREAD(ptOneWireProbeBus(struct pt * _pt, struct pt * _nested)) { uint8_t dummy; PT_BEGIN(TASK_CONTEXT); /* Parasite power not detected */ PARASITE_POWER = 0; /* Try overdrive procedure first */ if(drvOneWireReset(1)) { PT_WAIT_IO_COMPLETE(); if(!IO_SUCCESS() || !PRESENCE_DETECTED()) { /* Overdrive RESET procedure failed */ if(drvOneWireReset(0)) { PT_WAIT_IO_COMPLETE(); if(!IO_SUCCESS() || !PRESENCE_DETECTED()) { /* No devices on the bus */ PT_EXIT(TASK_CONTEXT); } } else { /* Hardware BUSY */ PT_EXIT(TASK_CONTEXT); } } } else { /* Hardware BUSY */ PT_EXIT(TASK_CONTEXT); } PT_SPAWN(TASK_CONTEXT, _nested, ptOneWireTargetAll(_nested)); if(ONEWIRE_STATUS_COMPLETE == STATUS) { PT_TX_BYTE_CONST(OP_READ_POWER_SUPPLY); if(IO_SUCCESS()) { /* Read one bit after command */ PT_RX_BITS(1); if(IO_SUCCESS()) { /* Fetch bit value */ int16_t value = drvOneWireRxBits(1); if(value < 0) { /* Rx bit decode failed */ STATUS = ONEWIRE_STATUS_ERROR; } else { /* If any device sent "0" then it used parasite power */ PARASITE_POWER = value ? 0 : 1; } } } } PT_END(TASK_CONTEXT); } 


The code is quite simple and implements the algorithm described above.

The procedure for determining the identifiers of connected devices
 PT_THREAD(ptOneWireWalkROM(pt_onewire_search_context_t * _ctx)) { PT_BEGIN(TASK_CONTEXT); while(!LAST_DEVICE_FLAG) { int16_t dummy; /* initialize for search */ ID_BIT_NUMBER = 1; LAST_ZERO = 0; ROM_BYTE_NUMBER = 0; ROM_BYTE_MASK = 1; /* 1-Wire reset (dependent on OVERDRIVE flag) */ PT_ONEWIRE_RESET(); if(!IO_SUCCESS() || !PRESENCE_DETECTED()) { // reset the search LAST_DISCREPANCY = 0; LAST_DEVICE_FLAG = 0; LAST_FAMILY_DISCREPANCY = 0; /* If presence not detected then no devices on the bus */ PT_EXIT(TASK_CONTEXT); } /* issue the search command */ PT_TX_BYTE(OP_SEARCH_ROM); if(!IO_SUCCESS() || (OP_SEARCH_ROM != drvOneWireRxBits(8))) { /* Send command error, repeat procedure from RESET point */ /* Other solution is abort search procedure */ continue; } // loop to do the search do { // read a bit and its complement PT_RX_BITS(2); if(!IO_SUCCESS()) { /* Error while receiving 2 bits. * As ID_BIT_NUMBER less than 65 search procedure * resumed from state such as original task entry. */ break; } if((RX_VALUE = drvOneWireRxBits(2)) < 0) { __no_operation(); break; } uint8_t id_bit = (RX_VALUE & 0x01) ? 1 : 0; uint8_t cmp_id_bit = (RX_VALUE & 0x02) ? 1 : 0; uint8_t search_direction; /* check for no devices on 1-wire */ if ((id_bit == 1) && (cmp_id_bit == 1)) { /* Same bit values equ "1" indicate no devices on the bus */ break; } 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 < LAST_DISCREPANCY) { search_direction = (ROMID_BYTE_REF(ROM_BYTE_NUMBER) & ROM_BYTE_MASK) ? 1 : 0; } else { /* if equal to last pick 1, if not then pick 0 */ search_direction = (ID_BIT_NUMBER == LAST_DISCREPANCY) ? 1 : 0; } /* if 0 was picked then record its position in LAST_ZERO */ if (search_direction == 0) { LAST_ZERO = ID_BIT_NUMBER; } /* check for LAST_FAMILY_DISCREPANCY in family */ if (LAST_ZERO < 9) { LAST_FAMILY_DISCREPANCY = LAST_ZERO; } } } /* set or clear the bit in the ROM byte ROM_BYTE_NUMBER with mask rom_byte_mask */ if (search_direction == 1) { ROMID_BYTE_REF(ROM_BYTE_NUMBER) |= ROM_BYTE_MASK; } else { ROMID_BYTE_REF(ROM_BYTE_NUMBER) &= ~ROM_BYTE_MASK; } /* serial number search direction write bit */ PT_TX_BITS(search_direction, 1); /* search_direction not stored, therefore we can't check echo */ if(!IO_SUCCESS()) { /* Sending direction failed. * As ID_BIT_NUMBER less than 65 search procedure * resumed from state such as original task entry. */ break; } /* 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) { ROM_BYTE_NUMBER++; ROM_BYTE_MASK = 1; } } while(ROM_BYTE_NUMBER < 8); /* loop until through all ROM bytes 0-7 */ /* if the search was successful then */ if(!(ID_BIT_NUMBER < 65)) { uint8_t i; /* Calculate CRC */ uint8_t crc = 0; for(i = 0;i < sizeof(ROMID);i++) { crc = modOneWireUpdateCRC(crc, ROMID_BYTE_REF(i)); } if(crc) { /* CRC error. * Repeat procedure from original point */ continue; } /* search successful so set LAST_DISCREPANCY and LAST_DEVICE_FLAG */ LAST_DISCREPANCY = LAST_ZERO; // check for last device if (LAST_DISCREPANCY == 0) { LAST_DEVICE_FLAG = 1; } /* Next device detection complete */ } else { /* I/O error. * Retry procedure from original point */ continue; } if(!ROMID.id.familyCode) { /* familyCode     0! */ break; } /* Return detected device S/N */ PT_YIELD(TASK_CONTEXT); } /* Reset state for next scan loop (if need) */ ptOneWireInitWalkROM(CONTEXT); PT_END(TASK_CONTEXT); } /* * Initialize device search procedure */ void ptOneWireInitWalkROM(pt_onewire_search_context_t * _ctx) { /* Prepare ptOneWireWalkROM() for first call */ LAST_DISCREPANCY = 0; LAST_DEVICE_FLAG = 0; LAST_FAMILY_DISCREPANCY = 0; /* Initialize protothreads data */ PT_INIT(TASK_CONTEXT); } 


I just took the synchronous implementation from Maxim and replaced the calls to the I / O procedures with asynchronous macros. All of this, along with running the test cases, took me about half an hour. I wonder how much would have to mess around without using the protothreads wrapper?

The complete project source code for the STM8L-Discovery board is available on github . To create an assembly with the above examples, it is necessary to define the HIGH_LEVEL symbol when compiling.

Bibliography:

  1. Project code on github
  2. Primitives for implementing 1-Wire master using PWM and ICP for STM8L and STM32
  3. Primitives for implementing 1-Wire master using PWM and ICP on AVR AtMega microcontrollers
  4. Protothreads from Adam Dunkels
  5. Habr from ldir Multitasking in microcontrollers based on the sequels
  6. Habr from LifeV Protothread and cooperative multitasking
  7. APPLICATION NOTE 187. 1-Wire Search Algorithm

Source: https://habr.com/ru/post/326320/


All Articles