[FL-85][FL-446][FL-720] Dallas key blanks and OneWire lib rework (#313)
* sepate one wire class * TM2004 writer * app mode write ds1990 * test another blanks protocol * new ibutton slave * one wire states * tim1 capture compare and update interrupts * interrupt mgr, new timers IRQ * discard HAL_TIM_PeriodElapsedCallback from main * add exti_14 line * add external interrupt callback * use int mgr in input * better interrupt managment * add interrupt callback enable and disable fns * properly init app * changed timings * rename one wire classes * use new owb classes * properly remove interrupts * new blanks writer * remove unused tests * new core includes * extern c guard * fix api_interrupt_remove usage * remove debug info, new way to detect blanks writing * remove copy constructor * change keys template * fix app sources recipe
This commit is contained in:
		| @@ -1,8 +1,5 @@ | ||||
| #include "one_wire_device.h" | ||||
|  | ||||
| // TODO fix GPL compability | ||||
| // currently we use rework of OneWireHub | ||||
|  | ||||
| OneWireDevice::OneWireDevice( | ||||
|     uint8_t id_1, | ||||
|     uint8_t id_2, | ||||
| @@ -21,6 +18,22 @@ OneWireDevice::OneWireDevice( | ||||
|     id_storage[7] = maxim_crc8(id_storage, 7); | ||||
| } | ||||
|  | ||||
| void OneWireDevice::send_id(OneWireGpioSlave* owner) const { | ||||
|     owner->send(id_storage, 8); | ||||
| OneWireDevice::~OneWireDevice() { | ||||
|     if(bus != nullptr) { | ||||
|         bus->deattach(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void OneWireDevice::send_id() const { | ||||
|     if(bus != nullptr) { | ||||
|         bus->send(id_storage, 8); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void OneWireDevice::attach(OneWireSlave* _bus) { | ||||
|     bus = _bus; | ||||
| } | ||||
|  | ||||
| void OneWireDevice::deattach(void) { | ||||
|     bus = nullptr; | ||||
| } | ||||
|   | ||||
| @@ -1,10 +1,7 @@ | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
| #include "maxim_crc.h" | ||||
| #include "one_wire_slave_gpio.h" | ||||
|  | ||||
| // TODO fix GPL compability | ||||
| // currently we use rework of OneWireHub | ||||
| #include "one_wire_slave.h" | ||||
|  | ||||
| class OneWireDevice { | ||||
| public: | ||||
| @@ -17,18 +14,13 @@ public: | ||||
|         uint8_t id_6, | ||||
|         uint8_t id_7); | ||||
|  | ||||
|     ~OneWireDevice() = default; // TODO: detach if deleted before hub | ||||
|  | ||||
|     // allow only move constructor | ||||
|     OneWireDevice(OneWireDevice&& one_wire_device) = default; | ||||
|     OneWireDevice(const OneWireDevice& one_wire_device) = delete; | ||||
|     OneWireDevice& operator=(OneWireDevice& one_wire_device) = delete; | ||||
|     OneWireDevice& operator=(const OneWireDevice& one_wire_device) = delete; | ||||
|     OneWireDevice& operator=(OneWireDevice&& one_wire_device) = delete; | ||||
|     ~OneWireDevice(); | ||||
|  | ||||
|     uint8_t id_storage[8]; | ||||
|  | ||||
|     void send_id(OneWireGpioSlave* owner) const; | ||||
|     void send_id() const; | ||||
|  | ||||
|     virtual void do_work(OneWireGpioSlave* owner) = 0; | ||||
|     OneWireSlave* bus = nullptr; | ||||
|     void attach(OneWireSlave* _bus); | ||||
|     void deattach(void); | ||||
| }; | ||||
| @@ -1,8 +1,5 @@ | ||||
| #include "one_wire_device_ds_1990.h" | ||||
|  | ||||
| // TODO fix GPL compability | ||||
| // currently we use rework of OneWireHub | ||||
|  | ||||
| DS1990::DS1990( | ||||
|     uint8_t ID1, | ||||
|     uint8_t ID2, | ||||
| @@ -12,16 +9,4 @@ DS1990::DS1990( | ||||
|     uint8_t ID6, | ||||
|     uint8_t ID7) | ||||
|     : OneWireDevice(ID1, ID2, ID3, ID4, ID5, ID6, ID7) { | ||||
| } | ||||
|  | ||||
| void DS1990::do_work(OneWireGpioSlave* owner) { | ||||
|     uint8_t cmd; | ||||
|  | ||||
|     if(owner->receive(&cmd)) return; | ||||
|  | ||||
|     switch(cmd) { | ||||
|     default: | ||||
|         return; | ||||
|         //owner->raiseSlaveError(cmd); | ||||
|     } | ||||
| } | ||||
| @@ -1,9 +1,6 @@ | ||||
| #pragma once | ||||
| #include "one_wire_device.h" | ||||
|  | ||||
| // TODO fix GPL compability | ||||
| // currently we use rework of OneWireHub | ||||
|  | ||||
| class DS1990 : public OneWireDevice { | ||||
| public: | ||||
|     static constexpr uint8_t family_code{0x01}; | ||||
| @@ -16,6 +13,4 @@ public: | ||||
|         uint8_t ID5, | ||||
|         uint8_t ID6, | ||||
|         uint8_t ID7); | ||||
|  | ||||
|     void do_work(OneWireGpioSlave* owner) final; | ||||
| }; | ||||
| @@ -1,41 +1,24 @@ | ||||
| #pragma once | ||||
| #include <furi.h> | ||||
| #include "one_wire_master.h" | ||||
| #include "one_wire_timings.h" | ||||
| 
 | ||||
| class OneWireGpio { | ||||
| private: | ||||
|     const GpioPin* gpio; | ||||
| 
 | ||||
| public: | ||||
|     OneWireGpio(const GpioPin* one_wire_gpio); | ||||
|     ~OneWireGpio(); | ||||
|     bool reset(void); | ||||
|     bool read_bit(void); | ||||
|     uint8_t read(void); | ||||
|     void read_bytes(uint8_t* buf, uint16_t count); | ||||
|     void write_bit(bool value); | ||||
|     void write(uint8_t value); | ||||
|     void start(void); | ||||
|     void stop(void); | ||||
| }; | ||||
| 
 | ||||
| OneWireGpio::OneWireGpio(const GpioPin* one_wire_gpio) { | ||||
| OneWireMaster::OneWireMaster(const GpioPin* one_wire_gpio) { | ||||
|     gpio = one_wire_gpio; | ||||
| } | ||||
| 
 | ||||
| OneWireGpio::~OneWireGpio() { | ||||
| OneWireMaster::~OneWireMaster() { | ||||
|     stop(); | ||||
| } | ||||
| 
 | ||||
| void OneWireGpio::start(void) { | ||||
| void OneWireMaster::start(void) { | ||||
|     gpio_init(gpio, GpioModeOutputOpenDrain); | ||||
| } | ||||
| 
 | ||||
| void OneWireGpio::stop(void) { | ||||
| void OneWireMaster::stop(void) { | ||||
|     gpio_init(gpio, GpioModeAnalog); | ||||
| } | ||||
| 
 | ||||
| bool OneWireGpio::reset(void) { | ||||
| bool OneWireMaster::reset(void) { | ||||
|     uint8_t r; | ||||
|     uint8_t retries = 125; | ||||
| 
 | ||||
| @@ -64,7 +47,7 @@ bool OneWireGpio::reset(void) { | ||||
|     return r; | ||||
| } | ||||
| 
 | ||||
| bool OneWireGpio::read_bit(void) { | ||||
| bool OneWireMaster::read_bit(void) { | ||||
|     bool result; | ||||
| 
 | ||||
|     // drive low
 | ||||
| @@ -82,7 +65,7 @@ bool OneWireGpio::read_bit(void) { | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| void OneWireGpio::write_bit(bool value) { | ||||
| void OneWireMaster::write_bit(bool value) { | ||||
|     if(value) { | ||||
|         // drive low
 | ||||
|         gpio_write(gpio, false); | ||||
| @@ -102,7 +85,7 @@ void OneWireGpio::write_bit(bool value) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| uint8_t OneWireGpio::read(void) { | ||||
| uint8_t OneWireMaster::read(void) { | ||||
|     uint8_t result = 0; | ||||
| 
 | ||||
|     for(uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) { | ||||
| @@ -114,16 +97,20 @@ uint8_t OneWireGpio::read(void) { | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| void OneWireGpio::read_bytes(uint8_t* buffer, uint16_t count) { | ||||
| void OneWireMaster::read_bytes(uint8_t* buffer, uint16_t count) { | ||||
|     for(uint16_t i = 0; i < count; i++) { | ||||
|         buffer[i] = read(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void OneWireGpio::write(uint8_t value) { | ||||
| void OneWireMaster::write(uint8_t value) { | ||||
|     uint8_t bitMask; | ||||
| 
 | ||||
|     for(bitMask = 0x01; bitMask; bitMask <<= 1) { | ||||
|         write_bit((bitMask & value) ? 1 : 0); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void OneWireMaster::skip(void) { | ||||
|     write(0xCC); | ||||
| } | ||||
							
								
								
									
										21
									
								
								lib/onewire/one_wire_master.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								lib/onewire/one_wire_master.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| #pragma once | ||||
| #include <furi.h> | ||||
| #include "one_wire_timings.h" | ||||
|  | ||||
| class OneWireMaster { | ||||
| private: | ||||
|     const GpioPin* gpio; | ||||
|  | ||||
| public: | ||||
|     OneWireMaster(const GpioPin* one_wire_gpio); | ||||
|     ~OneWireMaster(); | ||||
|     bool reset(void); | ||||
|     bool read_bit(void); | ||||
|     uint8_t read(void); | ||||
|     void read_bytes(uint8_t* buf, uint16_t count); | ||||
|     void write_bit(bool value); | ||||
|     void write(uint8_t value); | ||||
|     void skip(void); | ||||
|     void start(void); | ||||
|     void stop(void); | ||||
| }; | ||||
							
								
								
									
										312
									
								
								lib/onewire/one_wire_slave.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										312
									
								
								lib/onewire/one_wire_slave.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,312 @@ | ||||
| #include "one_wire_slave.h" | ||||
| #include "callback-connector.h" | ||||
| #include "main.h" | ||||
| #include "one_wire_device.h" | ||||
|  | ||||
| #define OWET OneWireEmulateTiming | ||||
|  | ||||
| void OneWireSlave::start(void) { | ||||
|     // add exti interrupt | ||||
|     api_interrupt_add(exti_cb, InterruptTypeExternalInterrupt, this); | ||||
|  | ||||
|     // init gpio | ||||
|     gpio_init(one_wire_pin_record, GpioModeInterruptRiseFall); | ||||
|     pin_set_float(); | ||||
|  | ||||
|     // init instructions per us count | ||||
|     __instructions_per_us = (SystemCoreClock / 1000000.0f); | ||||
| } | ||||
|  | ||||
| void OneWireSlave::stop(void) { | ||||
|     // deinit gpio | ||||
|     gpio_init_ex(one_wire_pin_record, GpioModeInput, GpioPullNo, GpioSpeedLow); | ||||
|  | ||||
|     // remove exti interrupt | ||||
|     api_interrupt_remove(exti_cb, InterruptTypeExternalInterrupt); | ||||
|  | ||||
|     // deattach devices | ||||
|     deattach(); | ||||
| } | ||||
|  | ||||
| OneWireSlave::OneWireSlave(const GpioPin* pin) { | ||||
|     one_wire_pin_record = pin; | ||||
|     exti_cb = cbc::obtain_connector(this, &OneWireSlave::exti_callback); | ||||
| } | ||||
|  | ||||
| OneWireSlave::~OneWireSlave() { | ||||
|     stop(); | ||||
| } | ||||
|  | ||||
| void OneWireSlave::attach(OneWireDevice* attached_device) { | ||||
|     device = attached_device; | ||||
|     device->attach(this); | ||||
| } | ||||
|  | ||||
| void OneWireSlave::deattach(void) { | ||||
|     device = nullptr; | ||||
|     device->deattach(); | ||||
| } | ||||
|  | ||||
| void OneWireSlave::set_result_callback(OneWireSlaveResultCallback result_cb, void* ctx) { | ||||
|     this->result_cb = result_cb; | ||||
|     this->result_cb_ctx = ctx; | ||||
| } | ||||
|  | ||||
| void OneWireSlave::pin_set_float() { | ||||
|     gpio_write(one_wire_pin_record, true); | ||||
| } | ||||
|  | ||||
| void OneWireSlave::pin_set_low() { | ||||
|     gpio_write(one_wire_pin_record, false); | ||||
| } | ||||
|  | ||||
| void OneWireSlave::pin_init_interrupt_in_isr_ctx(void) { | ||||
|     hal_gpio_init(one_wire_pin_record, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow); | ||||
|     __HAL_GPIO_EXTI_CLEAR_IT(one_wire_pin_record->pin); | ||||
| } | ||||
|  | ||||
| void OneWireSlave::pin_init_opendrain_in_isr_ctx(void) { | ||||
|     hal_gpio_init(one_wire_pin_record, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); | ||||
|     __HAL_GPIO_EXTI_CLEAR_IT(one_wire_pin_record->pin); | ||||
| } | ||||
|  | ||||
| OneWiteTimeType OneWireSlave::wait_while_gpio_is(OneWiteTimeType time, const bool pin_value) { | ||||
|     uint32_t start = DWT->CYCCNT; | ||||
|     uint32_t time_ticks = time * __instructions_per_us; | ||||
|     uint32_t time_captured; | ||||
|  | ||||
|     do { | ||||
|         time_captured = DWT->CYCCNT; | ||||
|         if(gpio_read(one_wire_pin_record) != pin_value) { | ||||
|             OneWiteTimeType remaining_time = time_ticks - (time_captured - start); | ||||
|             remaining_time /= __instructions_per_us; | ||||
|             return remaining_time; | ||||
|         } | ||||
|     } while((time_captured - start) < time_ticks); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| bool OneWireSlave::show_presence(void) { | ||||
|     // wait while master delay presence check | ||||
|     wait_while_gpio_is(OWET::PRESENCE_TIMEOUT, true); | ||||
|  | ||||
|     // show presence | ||||
|     pin_set_low(); | ||||
|     delay_us(OWET::PRESENCE_MIN); | ||||
|     pin_set_float(); | ||||
|  | ||||
|     // somebody also can show presence | ||||
|     const OneWiteTimeType wait_low_time = OWET::PRESENCE_MAX - OWET::PRESENCE_MIN; | ||||
|  | ||||
|     // so we will wait | ||||
|     if(wait_while_gpio_is(wait_low_time, false) == 0) { | ||||
|         error = OneWireSlaveError::PRESENCE_LOW_ON_LINE; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool OneWireSlave::receive_bit(void) { | ||||
|     // wait while bus is low | ||||
|     OneWiteTimeType time = OWET::SLOT_MAX; | ||||
|     time = wait_while_gpio_is(time, false); | ||||
|     if(time == 0) { | ||||
|         error = OneWireSlaveError::RESET_IN_PROGRESS; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     // wait while bus is high | ||||
|     time = OWET::MSG_HIGH_TIMEOUT; | ||||
|     time = wait_while_gpio_is(time, true); | ||||
|     if(time == 0) { | ||||
|         error = OneWireSlaveError::AWAIT_TIMESLOT_TIMEOUT_HIGH; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     // wait a time of zero | ||||
|     time = OWET::READ_MIN; | ||||
|     time = wait_while_gpio_is(time, false); | ||||
|  | ||||
|     return (time > 0); | ||||
| } | ||||
|  | ||||
| bool OneWireSlave::send_bit(bool value) { | ||||
|     const bool write_zero = !value; | ||||
|  | ||||
|     // wait while bus is low | ||||
|     OneWiteTimeType time = OWET::SLOT_MAX; | ||||
|     time = wait_while_gpio_is(time, false); | ||||
|     if(time == 0) { | ||||
|         error = OneWireSlaveError::RESET_IN_PROGRESS; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     // wait while bus is high | ||||
|     time = OWET::MSG_HIGH_TIMEOUT; | ||||
|     time = wait_while_gpio_is(time, true); | ||||
|     if(time == 0) { | ||||
|         error = OneWireSlaveError::AWAIT_TIMESLOT_TIMEOUT_HIGH; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     // choose write time | ||||
|     if(write_zero) { | ||||
|         pin_set_low(); | ||||
|         time = OWET::WRITE_ZERO; | ||||
|     } else { | ||||
|         time = OWET::READ_MAX; | ||||
|     } | ||||
|  | ||||
|     // hold line for ZERO or ONE time | ||||
|     delay_us(time); | ||||
|     pin_set_float(); | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool OneWireSlave::send(const uint8_t* address, const uint8_t data_length) { | ||||
|     uint8_t bytes_sent = 0; | ||||
|  | ||||
|     pin_set_float(); | ||||
|  | ||||
|     // bytes loop | ||||
|     for(; bytes_sent < data_length; ++bytes_sent) { | ||||
|         const uint8_t data_byte = address[bytes_sent]; | ||||
|  | ||||
|         // bit loop | ||||
|         for(uint8_t bit_mask = 0x01; bit_mask != 0; bit_mask <<= 1) { | ||||
|             if(!send_bit(static_cast<bool>(bit_mask & data_byte))) { | ||||
|                 // if we cannot send first bit | ||||
|                 if((bit_mask == 0x01) && (error == OneWireSlaveError::AWAIT_TIMESLOT_TIMEOUT_HIGH)) | ||||
|                     error = OneWireSlaveError::FIRST_BIT_OF_BYTE_TIMEOUT; | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool OneWireSlave::receive(uint8_t* data, const uint8_t data_length) { | ||||
|     uint8_t bytes_received = 0; | ||||
|  | ||||
|     pin_set_float(); | ||||
|  | ||||
|     for(; bytes_received < data_length; ++bytes_received) { | ||||
|         uint8_t value = 0; | ||||
|  | ||||
|         for(uint8_t bit_mask = 0x01; bit_mask != 0; bit_mask <<= 1) { | ||||
|             if(receive_bit()) value |= bit_mask; | ||||
|         } | ||||
|  | ||||
|         data[bytes_received] = value; | ||||
|     } | ||||
|     return (bytes_received != data_length); | ||||
| } | ||||
|  | ||||
| void OneWireSlave::cmd_search_rom(void) { | ||||
|     const uint8_t key_bytes = 8; | ||||
|     uint8_t* key = device->id_storage; | ||||
|  | ||||
|     for(uint8_t i = 0; i < key_bytes; i++) { | ||||
|         uint8_t key_byte = key[i]; | ||||
|  | ||||
|         for(uint8_t j = 0; j < 8; j++) { | ||||
|             bool bit = (key_byte >> j) & 0x01; | ||||
|  | ||||
|             if(!send_bit(bit)) return; | ||||
|             if(!send_bit(!bit)) return; | ||||
|  | ||||
|             const bool bit_recv = receive_bit(); | ||||
|             if(error != OneWireSlaveError::NO_ERROR) return; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool OneWireSlave::receive_and_process_cmd(void) { | ||||
|     uint8_t cmd; | ||||
|     receive(&cmd, 1); | ||||
|  | ||||
|     if(error == OneWireSlaveError::RESET_IN_PROGRESS) return true; | ||||
|     if(error != OneWireSlaveError::NO_ERROR) return false; | ||||
|  | ||||
|     switch(cmd) { | ||||
|     case 0xF0: | ||||
|         // SEARCH ROM | ||||
|         cmd_search_rom(); | ||||
|         return true; | ||||
|  | ||||
|     case 0x33: | ||||
|         // READ ROM | ||||
|         device->send_id(); | ||||
|         return false; | ||||
|  | ||||
|     default: // Unknown command | ||||
|         error = OneWireSlaveError::INCORRECT_ONEWIRE_CMD; | ||||
|     } | ||||
|  | ||||
|     if(error == OneWireSlaveError::RESET_IN_PROGRESS) return true; | ||||
|     return (error == OneWireSlaveError::NO_ERROR); | ||||
| } | ||||
|  | ||||
| bool OneWireSlave::bus_start(void) { | ||||
|     bool result = true; | ||||
|  | ||||
|     if(device == nullptr) { | ||||
|         result = false; | ||||
|     } else { | ||||
|         pin_init_opendrain_in_isr_ctx(); | ||||
|         error = OneWireSlaveError::NO_ERROR; | ||||
|  | ||||
|         if(show_presence()) { | ||||
|             __disable_irq(); | ||||
|  | ||||
|             // TODO think about multiple command cycles | ||||
|             bool return_to_reset = receive_and_process_cmd(); | ||||
|             result = | ||||
|                 (error == OneWireSlaveError::NO_ERROR || | ||||
|                  error == OneWireSlaveError::INCORRECT_ONEWIRE_CMD); | ||||
|  | ||||
|             __enable_irq(); | ||||
|         } else { | ||||
|             result = false; | ||||
|         } | ||||
|  | ||||
|         pin_init_interrupt_in_isr_ctx(); | ||||
|     } | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| void OneWireSlave::exti_callback(void* _pin, void* _ctx) { | ||||
|     // interrupt manager get us pin constant, so... | ||||
|     uint32_t pin = (uint32_t)_pin; | ||||
|     OneWireSlave* _this = static_cast<OneWireSlave*>(_ctx); | ||||
|  | ||||
|     if(pin == _this->one_wire_pin_record->pin) { | ||||
|         volatile bool input_state = gpio_read(_this->one_wire_pin_record); | ||||
|         static uint32_t pulse_start = 0; | ||||
|  | ||||
|         if(input_state) { | ||||
|             uint32_t pulse_length = (DWT->CYCCNT - pulse_start) / __instructions_per_us; | ||||
|             if(pulse_length >= OWET::RESET_MIN) { | ||||
|                 if(pulse_length <= OWET::RESET_MAX) { | ||||
|                     // reset cycle ok | ||||
|                     bool result = _this->bus_start(); | ||||
|  | ||||
|                     if(_this->result_cb != nullptr) { | ||||
|                         _this->result_cb(result, _this->result_cb_ctx); | ||||
|                     } | ||||
|                 } else { | ||||
|                     error = OneWireSlaveError::VERY_LONG_RESET; | ||||
|                 } | ||||
|             } else { | ||||
|                 error = OneWireSlaveError::VERY_SHORT_RESET; | ||||
|             } | ||||
|         } else { | ||||
|             //FALL event | ||||
|             pulse_start = DWT->CYCCNT; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										74
									
								
								lib/onewire/one_wire_slave.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								lib/onewire/one_wire_slave.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | ||||
| #pragma once | ||||
| #include <furi.h> | ||||
| #include "one_wire_timings.h" | ||||
|  | ||||
| class OneWireDevice; | ||||
| typedef void (*OneWireSlaveResultCallback)(bool success, void* ctx); | ||||
|  | ||||
| class OneWireSlave { | ||||
| private: | ||||
|     enum class OneWireSlaveError : uint8_t { | ||||
|         NO_ERROR = 0, | ||||
|         READ_TIMESLOT_TIMEOUT, | ||||
|         WRITE_TIMESLOT_TIMEOUT, | ||||
|         WAIT_RESET_TIMEOUT, | ||||
|         VERY_LONG_RESET, | ||||
|         VERY_SHORT_RESET, | ||||
|         PRESENCE_LOW_ON_LINE, | ||||
|         READ_TIMESLOT_TIMEOUT_LOW, | ||||
|         AWAIT_TIMESLOT_TIMEOUT_HIGH, | ||||
|         PRESENCE_HIGH_ON_LINE, | ||||
|         INCORRECT_ONEWIRE_CMD, | ||||
|         INCORRECT_SLAVE_USAGE, | ||||
|         TRIED_INCORRECT_WRITE, | ||||
|         FIRST_TIMESLOT_TIMEOUT, | ||||
|         FIRST_BIT_OF_BYTE_TIMEOUT, | ||||
|         RESET_IN_PROGRESS | ||||
|     }; | ||||
|  | ||||
|     const GpioPin* one_wire_pin_record; | ||||
|  | ||||
|     // exti callback and its pointer | ||||
|     void exti_callback(void* _pin, void* _ctx); | ||||
|     void (*exti_cb)(void* _pin, void* _ctx); | ||||
|  | ||||
|     uint32_t __instructions_per_us; | ||||
|  | ||||
|     OneWireSlaveError error; | ||||
|     OneWireDevice* device = nullptr; | ||||
|  | ||||
|     bool bus_start(void); | ||||
|  | ||||
|     void pin_set_float(void); | ||||
|     void pin_set_low(void); | ||||
|     void pin_init_interrupt_in_isr_ctx(void); | ||||
|     void pin_init_opendrain_in_isr_ctx(void); | ||||
|  | ||||
|     OneWiteTimeType wait_while_gpio_is(OneWiteTimeType time, const bool pin_value); | ||||
|  | ||||
|     bool show_presence(void); | ||||
|     bool receive_and_process_cmd(void); | ||||
|  | ||||
|     bool receive_bit(void); | ||||
|     bool send_bit(bool value); | ||||
|  | ||||
|     void cmd_search_rom(void); | ||||
|  | ||||
|     OneWireSlaveResultCallback result_cb = nullptr; | ||||
|     void* result_cb_ctx = nullptr; | ||||
|  | ||||
| public: | ||||
|     void start(void); | ||||
|     void stop(void); | ||||
|  | ||||
|     bool send(const uint8_t* address, const uint8_t data_length); | ||||
|     bool receive(uint8_t* data, const uint8_t data_length = 1); | ||||
|  | ||||
|     OneWireSlave(const GpioPin* pin); | ||||
|     ~OneWireSlave(); | ||||
|  | ||||
|     void attach(OneWireDevice* device); | ||||
|     void deattach(void); | ||||
|  | ||||
|     void set_result_callback(OneWireSlaveResultCallback result_cb, void* ctx); | ||||
| }; | ||||
| @@ -1,517 +0,0 @@ | ||||
| #include "one_wire_slave_gpio.h" | ||||
| #include "one_wire_device.h" | ||||
| #include "one_wire_device_ds_1990.h" | ||||
|  | ||||
| // TODO fix GPL compability | ||||
| // currently we use rework of OneWireHub | ||||
|  | ||||
| static uint32_t __instructions_per_us = 0; | ||||
|  | ||||
| OneWireGpioSlave::OneWireGpioSlave(const GpioPin* one_wire_gpio) { | ||||
|     gpio = one_wire_gpio; | ||||
|     error = OneWireGpioSlaveError::NO_ERROR; | ||||
|     devices_count = 0; | ||||
|     device_selected = nullptr; | ||||
|  | ||||
|     for(uint8_t i = 0; i < ONE_WIRE_MAX_DEVICES; ++i) { | ||||
|         devices[i] = nullptr; | ||||
|     } | ||||
|  | ||||
|     __instructions_per_us = (SystemCoreClock / 1000000.0f); | ||||
| } | ||||
|  | ||||
| OneWireGpioSlave::~OneWireGpioSlave() { | ||||
|     stop(); | ||||
| } | ||||
|  | ||||
| void OneWireGpioSlave::start(void) { | ||||
|     gpio_init(gpio, GpioModeOutputOpenDrain); | ||||
| } | ||||
|  | ||||
| void OneWireGpioSlave::stop(void) { | ||||
|     gpio_init(gpio, GpioModeAnalog); | ||||
| } | ||||
|  | ||||
| bool OneWireGpioSlave::emulate() { | ||||
|     bool anything_emulated = false; | ||||
|     error = OneWireGpioSlaveError::NO_ERROR; | ||||
|  | ||||
|     while(1) { | ||||
|         if(devices_count == 0) return false; | ||||
|  | ||||
|         if(!check_reset()) { | ||||
|             return anything_emulated; | ||||
|         } else { | ||||
|         } | ||||
|  | ||||
|         // OK, we receive reset | ||||
|         osKernelLock(); | ||||
|  | ||||
|         if(!show_presence()) { | ||||
|             return anything_emulated; | ||||
|         } else { | ||||
|             anything_emulated = true; | ||||
|         } | ||||
|  | ||||
|         // and we succefully show our presence on bus | ||||
|         __disable_irq(); | ||||
|  | ||||
|         // TODO think about return condition | ||||
|         if(!receive_and_process_cmd()) { | ||||
|             __enable_irq(); | ||||
|             osKernelUnlock(); | ||||
|         } else { | ||||
|             __enable_irq(); | ||||
|             osKernelUnlock(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| OneWiteTimeType OneWireGpioSlave::wait_while_gpio_is(OneWiteTimeType time, const bool pin_value) { | ||||
|     uint32_t start = DWT->CYCCNT; | ||||
|     uint32_t time_ticks = time * __instructions_per_us; | ||||
|     uint32_t time_captured; | ||||
|  | ||||
|     do { | ||||
|         time_captured = DWT->CYCCNT; | ||||
|         if(gpio_read(gpio) != pin_value) { | ||||
|             OneWiteTimeType remaining_time = time_ticks - (time_captured - start); | ||||
|             remaining_time /= __instructions_per_us; | ||||
|             return remaining_time; | ||||
|         } | ||||
|     } while((time_captured - start) < time_ticks); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| void OneWireGpioSlave::pin_set_float() { | ||||
|     gpio_write(gpio, true); | ||||
| } | ||||
|  | ||||
| void OneWireGpioSlave::pin_set_low() { | ||||
|     gpio_write(gpio, false); | ||||
| } | ||||
|  | ||||
| const char* OneWireGpioSlave::decode_error() { | ||||
|     const char* error_text[16] = { | ||||
|         "NO_ERROR", | ||||
|         "READ_TIMESLOT_TIMEOUT", | ||||
|         "WRITE_TIMESLOT_TIMEOUT", | ||||
|         "WAIT_RESET_TIMEOUT", | ||||
|         "VERY_LONG_RESET", | ||||
|         "VERY_SHORT_RESET", | ||||
|         "PRESENCE_LOW_ON_LINE", | ||||
|         "READ_TIMESLOT_TIMEOUT_LOW", | ||||
|         "AWAIT_TIMESLOT_TIMEOUT_HIGH", | ||||
|         "PRESENCE_HIGH_ON_LINE", | ||||
|         "INCORRECT_ONEWIRE_CMD", | ||||
|         "INCORRECT_SLAVE_USAGE", | ||||
|         "TRIED_INCORRECT_WRITE", | ||||
|         "FIRST_TIMESLOT_TIMEOUT", | ||||
|         "FIRST_BIT_OF_BYTE_TIMEOUT", | ||||
|         "RESET_IN_PROGRESS"}; | ||||
|  | ||||
|     return error_text[static_cast<uint8_t>(error)]; | ||||
| } | ||||
|  | ||||
| uint8_t OneWireGpioSlave::attach(OneWireDevice& device) { | ||||
|     if(devices_count >= ONE_WIRE_MAX_DEVICES) return 255; // hub is full | ||||
|  | ||||
|     uint8_t position = 255; | ||||
|     for(uint8_t i = 0; i < ONE_WIRE_MAX_DEVICES; ++i) { | ||||
|         if(devices[i] == &device) { | ||||
|             return i; | ||||
|         } | ||||
|         if((position > ONE_WIRE_MAX_DEVICES) && (devices[i] == nullptr)) { | ||||
|             position = i; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if(position == 255) return 255; | ||||
|  | ||||
|     devices[position] = &device; | ||||
|     devices_count++; | ||||
|     build_id_tree(); | ||||
|     return position; | ||||
| } | ||||
|  | ||||
| bool OneWireGpioSlave::detach(const OneWireDevice& device) { | ||||
|     uint8_t position = 255; | ||||
|  | ||||
|     for(uint8_t i = 0; i < ONE_WIRE_MAX_DEVICES; ++i) { | ||||
|         if(devices[i] == &device) { | ||||
|             position = i; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if(position != 255) return detach(position); | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool OneWireGpioSlave::detach(uint8_t device_number) { | ||||
|     if(devices[device_number] == nullptr) return false; | ||||
|     if(devices_count == 0) return false; | ||||
|     if(device_number >= ONE_WIRE_MAX_DEVICES) return false; | ||||
|  | ||||
|     devices[device_number] = nullptr; | ||||
|     devices_count--; | ||||
|     build_id_tree(); | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| uint8_t OneWireGpioSlave::get_next_device_index(const uint8_t index_start) const { | ||||
|     for(uint8_t i = index_start; i < ONE_WIRE_MAX_DEVICES; ++i) { | ||||
|         if(devices[i] != nullptr) return i; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| uint8_t OneWireGpioSlave::build_id_tree(void) { | ||||
|     uint32_t device_mask = 0; | ||||
|     uint32_t bit_mask = 0x01; | ||||
|  | ||||
|     // build mask | ||||
|     for(uint8_t i = 0; i < ONE_WIRE_MAX_DEVICES; ++i) { | ||||
|         if(devices[i] != nullptr) device_mask |= bit_mask; | ||||
|         bit_mask <<= 1; | ||||
|     } | ||||
|  | ||||
|     for(uint8_t i = 0; i < ONE_WIRE_MAX_DEVICES; ++i) { | ||||
|         id_tree[i].id_position = 255; | ||||
|     } | ||||
|  | ||||
|     // begin with root-element | ||||
|     build_id_tree(0, device_mask); // goto branch | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| uint8_t OneWireGpioSlave::build_id_tree(uint8_t id_bit_position, uint32_t device_mask) { | ||||
|     if(device_mask == 0) return (255); | ||||
|  | ||||
|     while(id_bit_position < 64) { | ||||
|         uint32_t mask_pos{0}; | ||||
|         uint32_t mask_neg{0}; | ||||
|         const uint8_t pos_byte{static_cast<uint8_t>(id_bit_position >> 3)}; | ||||
|         const uint8_t mask_bit{static_cast<uint8_t>(1 << (id_bit_position & 7))}; | ||||
|         uint32_t mask_id{1}; | ||||
|  | ||||
|         // searchid_tree through all active slaves | ||||
|         for(uint8_t id = 0; id < ONE_WIRE_MAX_DEVICES; ++id) { | ||||
|             if((device_mask & mask_id) != 0) { | ||||
|                 // if slave is in mask differentiate the bitValue | ||||
|                 if((devices[id]->id_storage[pos_byte] & mask_bit) != 0) | ||||
|                     mask_pos |= mask_id; | ||||
|                 else | ||||
|                     mask_neg |= mask_id; | ||||
|             } | ||||
|             mask_id <<= 1; | ||||
|         } | ||||
|  | ||||
|         if((mask_neg != 0) && (mask_pos != 0)) { | ||||
|             // there was found a junction | ||||
|             const uint8_t active_element = get_first_id_tree_el_position(); | ||||
|  | ||||
|             id_tree[active_element].id_position = id_bit_position; | ||||
|             id_tree[active_element].device_selected = get_first_bit_set_position(device_mask); | ||||
|             id_bit_position++; | ||||
|             id_tree[active_element].got_one = build_id_tree(id_bit_position, mask_pos); | ||||
|             id_tree[active_element].got_zero = build_id_tree(id_bit_position, mask_neg); | ||||
|             return active_element; | ||||
|         } | ||||
|  | ||||
|         id_bit_position++; | ||||
|     } | ||||
|  | ||||
|     // gone through the address, store this result | ||||
|     uint8_t active_element = get_first_id_tree_el_position(); | ||||
|  | ||||
|     id_tree[active_element].id_position = 128; | ||||
|     id_tree[active_element].device_selected = get_first_bit_set_position(device_mask); | ||||
|     id_tree[active_element].got_one = 255; | ||||
|     id_tree[active_element].got_zero = 255; | ||||
|  | ||||
|     return active_element; | ||||
| } | ||||
|  | ||||
| uint8_t OneWireGpioSlave::get_first_bit_set_position(uint32_t mask) const { | ||||
|     uint32_t _mask = mask; | ||||
|     for(uint8_t i = 0; i < ONE_WIRE_MAX_DEVICES; ++i) { | ||||
|         if((_mask & 1) != 0) return i; | ||||
|         _mask >>= 1; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| uint8_t OneWireGpioSlave::get_first_id_tree_el_position(void) const { | ||||
|     for(uint8_t i = 0; i < ONE_WIRE_MAX_DEVICES; ++i) { | ||||
|         if(id_tree[i].id_position == 255) return i; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| void OneWireGpioSlave::cmd_search_rom(void) { | ||||
|     uint8_t id_bit_position = 0; | ||||
|     uint8_t trigger_position = 0; | ||||
|     uint8_t active_slave = id_tree[trigger_position].device_selected; | ||||
|     uint8_t trigger_bit = id_tree[trigger_position].id_position; | ||||
|  | ||||
|     while(id_bit_position < 64) { | ||||
|         // if junction is reached, act different | ||||
|         if(id_bit_position == trigger_bit) { | ||||
|             if(!send_bit(false)) return; | ||||
|             if(!send_bit(false)) return; | ||||
|  | ||||
|             const bool bit_recv = receive_bit(); | ||||
|             if(error != OneWireGpioSlaveError::NO_ERROR) return; | ||||
|  | ||||
|             // switch to next junction | ||||
|             trigger_position = bit_recv ? id_tree[trigger_position].got_one : | ||||
|                                           id_tree[trigger_position].got_zero; | ||||
|  | ||||
|             active_slave = id_tree[trigger_position].device_selected; | ||||
|  | ||||
|             trigger_bit = (trigger_position == 255) ? uint8_t(255) : | ||||
|                                                       id_tree[trigger_position].id_position; | ||||
|         } else { | ||||
|             const uint8_t pos_byte = (id_bit_position >> 3); | ||||
|             const uint8_t mask_bit = (static_cast<uint8_t>(1) << (id_bit_position & (7))); | ||||
|             bool bit_send; | ||||
|  | ||||
|             if((devices[active_slave]->id_storage[pos_byte] & mask_bit) != 0) { | ||||
|                 bit_send = true; | ||||
|                 if(!send_bit(true)) return; | ||||
|                 if(!send_bit(false)) return; | ||||
|             } else { | ||||
|                 bit_send = false; | ||||
|                 if(!send_bit(false)) return; | ||||
|                 if(!send_bit(true)) return; | ||||
|             } | ||||
|  | ||||
|             const bool bit_recv = receive_bit(); | ||||
|             if(error != OneWireGpioSlaveError::NO_ERROR) return; | ||||
|  | ||||
|             if(bit_send != bit_recv) return; | ||||
|         } | ||||
|         id_bit_position++; | ||||
|     } | ||||
|  | ||||
|     device_selected = devices[active_slave]; | ||||
| } | ||||
|  | ||||
| bool OneWireGpioSlave::check_reset(void) { | ||||
|     pin_set_float(); | ||||
|  | ||||
|     if(error == OneWireGpioSlaveError::RESET_IN_PROGRESS) { | ||||
|         error = OneWireGpioSlaveError::NO_ERROR; | ||||
|  | ||||
|         if(wait_while_gpio_is( | ||||
|                OWET::RESET_MIN[overdrive_mode] - OWET::SLOT_MAX[overdrive_mode] - | ||||
|                    OWET::READ_MAX[overdrive_mode], | ||||
|                false) == 0) { | ||||
|             // we want to show_presence on high, so wait for it | ||||
|             const OneWiteTimeType time_remaining = wait_while_gpio_is(OWET::RESET_MAX[0], false); | ||||
|  | ||||
|             if(overdrive_mode && | ||||
|                ((OWET::RESET_MAX[0] - OWET::RESET_MIN[overdrive_mode]) > time_remaining)) { | ||||
|                 overdrive_mode = false; | ||||
|             }; | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // if line is low, then just leave | ||||
|     if(gpio_read(gpio) == 0) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     // wait while gpio is high | ||||
|     if(wait_while_gpio_is(OWET::RESET_TIMEOUT, true) == 0) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     // store low time | ||||
|     OneWiteTimeType time_remaining = wait_while_gpio_is(OWET::RESET_MAX[0], false); | ||||
|  | ||||
|     // low time more than RESET_MAX time | ||||
|     if(time_remaining == 0) { | ||||
|         error = OneWireGpioSlaveError::VERY_LONG_RESET; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     // get real reset time | ||||
|     time_remaining = OWET::RESET_MAX[0] - time_remaining; | ||||
|  | ||||
|     // if time, while bus was low, fit in standart reset timings | ||||
|     if(overdrive_mode && ((OWET::RESET_MAX[0] - OWET::RESET_MIN[0]) <= time_remaining)) { | ||||
|         // normal reset detected | ||||
|         overdrive_mode = false; | ||||
|     }; | ||||
|  | ||||
|     bool result = (time_remaining <= OWET::RESET_MAX[0]) && | ||||
|                   time_remaining >= OWET::RESET_MIN[overdrive_mode]; | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| bool OneWireGpioSlave::show_presence(void) { | ||||
|     // wait while master delay presence check | ||||
|     wait_while_gpio_is(OWET::PRESENCE_TIMEOUT, true); | ||||
|  | ||||
|     // show presence | ||||
|     pin_set_low(); | ||||
|     delay_us(OWET::PRESENCE_MIN[overdrive_mode]); | ||||
|     pin_set_float(); | ||||
|  | ||||
|     // somebody also can show presence | ||||
|     const OneWiteTimeType wait_low_time = | ||||
|         OWET::PRESENCE_MAX[overdrive_mode] - OWET::PRESENCE_MIN[overdrive_mode]; | ||||
|  | ||||
|     // so we will wait | ||||
|     if(wait_while_gpio_is(wait_low_time, false) == 0) { | ||||
|         error = OneWireGpioSlaveError::PRESENCE_LOW_ON_LINE; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool OneWireGpioSlave::receive_and_process_cmd(void) { | ||||
|     receive(&cmd); | ||||
|  | ||||
|     if(error == OneWireGpioSlaveError::RESET_IN_PROGRESS) return true; | ||||
|     if(error != OneWireGpioSlaveError::NO_ERROR) return false; | ||||
|  | ||||
|     switch(cmd) { | ||||
|     case 0xF0: | ||||
|         // SEARCH ROM | ||||
|         device_selected = nullptr; | ||||
|         cmd_search_rom(); | ||||
|  | ||||
|         // trigger reinit | ||||
|         return true; | ||||
|  | ||||
|     case 0x33: | ||||
|         // READ ROM | ||||
|  | ||||
|         // work only when one slave on the bus | ||||
|         if((device_selected == nullptr) && (devices_count == 1)) { | ||||
|             device_selected = devices[get_next_device_index()]; | ||||
|         } | ||||
|         if(device_selected != nullptr) { | ||||
|             device_selected->send_id(this); | ||||
|         } | ||||
|         return false; | ||||
|  | ||||
|     default: // Unknown command | ||||
|         error = OneWireGpioSlaveError::INCORRECT_ONEWIRE_CMD; | ||||
|         //error_cmd = cmd; | ||||
|     } | ||||
|  | ||||
|     if(error == OneWireGpioSlaveError::RESET_IN_PROGRESS) return true; | ||||
|     return (error == OneWireGpioSlaveError::NO_ERROR); | ||||
| } | ||||
|  | ||||
| bool OneWireGpioSlave::receive_bit(void) { | ||||
|     // wait while bus is low | ||||
|     OneWiteTimeType time = OWET::SLOT_MAX[overdrive_mode]; | ||||
|     time = wait_while_gpio_is(time, false); | ||||
|     if(time == 0) { | ||||
|         error = OneWireGpioSlaveError::RESET_IN_PROGRESS; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     // wait while bus is high | ||||
|     time = OWET::MSG_HIGH_TIMEOUT; | ||||
|     time = wait_while_gpio_is(time, true); | ||||
|     if(time == 0) { | ||||
|         error = OneWireGpioSlaveError::AWAIT_TIMESLOT_TIMEOUT_HIGH; | ||||
|         error_place = 1; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     // wait a time of zero | ||||
|     time = OWET::READ_MIN[overdrive_mode]; | ||||
|     time = wait_while_gpio_is(time, false); | ||||
|  | ||||
|     return (time > 0); | ||||
| } | ||||
|  | ||||
| bool OneWireGpioSlave::send_bit(bool value) { | ||||
|     const bool write_zero = !value; | ||||
|  | ||||
|     // wait while bus is low | ||||
|     OneWiteTimeType time = OWET::SLOT_MAX[overdrive_mode]; | ||||
|     time = wait_while_gpio_is(time, false); | ||||
|     if(time == 0) { | ||||
|         error = OneWireGpioSlaveError::RESET_IN_PROGRESS; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     // wait while bus is high | ||||
|     time = OWET::MSG_HIGH_TIMEOUT; | ||||
|     time = wait_while_gpio_is(time, true); | ||||
|     if(time == 0) { | ||||
|         error = OneWireGpioSlaveError::AWAIT_TIMESLOT_TIMEOUT_HIGH; | ||||
|         error_place = 2; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     // choose write time | ||||
|     if(write_zero) { | ||||
|         pin_set_low(); | ||||
|         time = OWET::WRITE_ZERO[overdrive_mode]; | ||||
|     } else { | ||||
|         time = OWET::READ_MAX[overdrive_mode]; | ||||
|     } | ||||
|  | ||||
|     // hold line for ZERO or ONE time | ||||
|     delay_us(time); | ||||
|     pin_set_float(); | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool OneWireGpioSlave::send(const uint8_t* address, const uint8_t data_length) { | ||||
|     uint8_t bytes_sent = 0; | ||||
|  | ||||
|     pin_set_float(); | ||||
|  | ||||
|     // bytes loop | ||||
|     for(; bytes_sent < data_length; ++bytes_sent) { | ||||
|         const uint8_t data_byte = address[bytes_sent]; | ||||
|  | ||||
|         // bit loop | ||||
|         for(uint8_t bit_mask = 0x01; bit_mask != 0; bit_mask <<= 1) { | ||||
|             if(!send_bit(static_cast<bool>(bit_mask & data_byte))) { | ||||
|                 // if we cannot send first bit | ||||
|                 if((bit_mask == 0x01) && | ||||
|                    (error == OneWireGpioSlaveError::AWAIT_TIMESLOT_TIMEOUT_HIGH)) | ||||
|                     error = OneWireGpioSlaveError::FIRST_BIT_OF_BYTE_TIMEOUT; | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool OneWireGpioSlave::receive(uint8_t* data, const uint8_t data_length) { | ||||
|     uint8_t bytes_received = 0; | ||||
|  | ||||
|     pin_set_float(); | ||||
|  | ||||
|     for(; bytes_received < data_length; ++bytes_received) { | ||||
|         uint8_t value = 0; | ||||
|  | ||||
|         for(uint8_t bit_mask = 0x01; bit_mask != 0; bit_mask <<= 1) { | ||||
|             if(receive_bit()) value |= bit_mask; | ||||
|         } | ||||
|  | ||||
|         data[bytes_received] = value; | ||||
|     } | ||||
|     return (bytes_received != data_length); | ||||
| } | ||||
| @@ -1,92 +0,0 @@ | ||||
| #pragma once | ||||
| #include <furi.h> | ||||
| #include "one_wire_timings.h" | ||||
|  | ||||
| // TODO fix GPL compability | ||||
| // currently we use rework of OneWireHub | ||||
|  | ||||
| #define ONE_WIRE_MAX_DEVICES 1 | ||||
| #define ONE_WIRE_TREE_SIZE ((2 * ONE_WIRE_MAX_DEVICES) - 1) | ||||
|  | ||||
| #define OWET OneWireEmulateTiming | ||||
|  | ||||
| class OneWireDevice; | ||||
|  | ||||
| enum class OneWireGpioSlaveError : uint8_t { | ||||
|     NO_ERROR = 0, | ||||
|     READ_TIMESLOT_TIMEOUT = 1, | ||||
|     WRITE_TIMESLOT_TIMEOUT = 2, | ||||
|     WAIT_RESET_TIMEOUT = 3, | ||||
|     VERY_LONG_RESET = 4, | ||||
|     VERY_SHORT_RESET = 5, | ||||
|     PRESENCE_LOW_ON_LINE = 6, | ||||
|     READ_TIMESLOT_TIMEOUT_LOW = 7, | ||||
|     AWAIT_TIMESLOT_TIMEOUT_HIGH = 8, | ||||
|     PRESENCE_HIGH_ON_LINE = 9, | ||||
|     INCORRECT_ONEWIRE_CMD = 10, | ||||
|     INCORRECT_SLAVE_USAGE = 11, | ||||
|     TRIED_INCORRECT_WRITE = 12, | ||||
|     FIRST_TIMESLOT_TIMEOUT = 13, | ||||
|     FIRST_BIT_OF_BYTE_TIMEOUT = 14, | ||||
|     RESET_IN_PROGRESS = 15 | ||||
| }; | ||||
|  | ||||
| class OneWireGpioSlave { | ||||
| private: | ||||
|     const GpioPin* gpio; | ||||
|     bool overdrive_mode = false; | ||||
|     uint8_t cmd; | ||||
|     OneWireGpioSlaveError error; | ||||
|     uint8_t error_place; | ||||
|  | ||||
|     uint8_t devices_count; | ||||
|     OneWireDevice* devices[ONE_WIRE_MAX_DEVICES]; | ||||
|     OneWireDevice* device_selected; | ||||
|  | ||||
|     struct IDTree { | ||||
|         uint8_t device_selected; // for which slave is this jump-command relevant | ||||
|         uint8_t id_position; // where does the algorithm has to look for a junction | ||||
|         uint8_t got_zero; // if 0 switch to which tree branch | ||||
|         uint8_t got_one; // if 1 switch to which tree branch | ||||
|     } id_tree[ONE_WIRE_TREE_SIZE]; | ||||
|  | ||||
| public: | ||||
|     OneWireGpioSlave(const GpioPin* one_wire_gpio); | ||||
|     ~OneWireGpioSlave(); | ||||
|  | ||||
|     void start(void); | ||||
|     void stop(void); | ||||
|     bool emulate(); | ||||
|     bool check_reset(void); | ||||
|     bool show_presence(void); | ||||
|     bool receive_and_process_cmd(void); | ||||
|     bool receive(uint8_t* data, const uint8_t data_length = 1); | ||||
|     bool receive_bit(void); | ||||
|     bool send_bit(bool value); | ||||
|     bool send(const uint8_t* address, const uint8_t data_length = 1); | ||||
|  | ||||
|     OneWiteTimeType wait_while_gpio_is(volatile OneWiteTimeType retries, const bool pin_value); | ||||
|  | ||||
|     // set pin state | ||||
|     inline void pin_set_float(); | ||||
|     inline void pin_set_low(); | ||||
|  | ||||
|     // get error text | ||||
|     const char* decode_error(); | ||||
|  | ||||
|     // devices managment | ||||
|     uint8_t attach(OneWireDevice& device); | ||||
|     bool detach(const OneWireDevice& device); | ||||
|     bool detach(uint8_t device_number); | ||||
|     uint8_t get_next_device_index(const uint8_t index_start = 0) const; | ||||
|  | ||||
|     // id tree managment | ||||
|     uint8_t build_id_tree(void); | ||||
|     uint8_t build_id_tree(uint8_t id_bit_position, uint32_t device_mask); | ||||
|  | ||||
|     uint8_t get_first_bit_set_position(uint32_t mask) const; | ||||
|     uint8_t get_first_id_tree_el_position(void) const; | ||||
|  | ||||
|     // commands | ||||
|     void cmd_search_rom(void); | ||||
| }; | ||||
| @@ -1,17 +1,16 @@ | ||||
| #include "one_wire_timings.h" | ||||
|  | ||||
| // fix pre C++17 "undefined reference" errors | ||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::RESET_TIMEOUT; | ||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::RESET_MIN[2]; | ||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::RESET_MAX[2]; | ||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::RESET_MIN; | ||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::RESET_MAX; | ||||
|  | ||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::PRESENCE_TIMEOUT; | ||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::PRESENCE_MIN[2]; | ||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::PRESENCE_MAX[2]; | ||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::PRESENCE_MIN; | ||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::PRESENCE_MAX; | ||||
|  | ||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::MSG_HIGH_TIMEOUT; | ||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::SLOT_MAX[2]; | ||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::SLOT_MAX; | ||||
|  | ||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::READ_MIN[2]; | ||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::READ_MAX[2]; | ||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::WRITE_ZERO[2]; | ||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::READ_MIN; | ||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::READ_MAX; | ||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::WRITE_ZERO; | ||||
|   | ||||
| @@ -3,10 +3,10 @@ | ||||
|  | ||||
| class __OneWireTiming { | ||||
| public: | ||||
|     constexpr static const uint16_t TIMING_A = 6; | ||||
|     constexpr static const uint16_t TIMING_A = 9; | ||||
|     constexpr static const uint16_t TIMING_B = 64; | ||||
|     constexpr static const uint16_t TIMING_C = 60; | ||||
|     constexpr static const uint16_t TIMING_D = 10; | ||||
|     constexpr static const uint16_t TIMING_C = 64; | ||||
|     constexpr static const uint16_t TIMING_D = 14; | ||||
|     constexpr static const uint16_t TIMING_E = 9; | ||||
|     constexpr static const uint16_t TIMING_F = 55; | ||||
|     constexpr static const uint16_t TIMING_G = 0; | ||||
| @@ -23,7 +23,7 @@ public: | ||||
|     constexpr static const uint16_t WRITE_0_DRIVE = __OneWireTiming::TIMING_C; | ||||
|     constexpr static const uint16_t WRITE_0_RELEASE = __OneWireTiming::TIMING_D; | ||||
|  | ||||
|     constexpr static const uint16_t READ_DRIVE = __OneWireTiming::TIMING_A; | ||||
|     constexpr static const uint16_t READ_DRIVE = 3; | ||||
|     constexpr static const uint16_t READ_RELEASE = __OneWireTiming::TIMING_E; | ||||
|     constexpr static const uint16_t READ_DELAY_POST = __OneWireTiming::TIMING_F; | ||||
|  | ||||
| @@ -37,18 +37,17 @@ typedef uint32_t OneWiteTimeType; | ||||
|  | ||||
| class OneWireEmulateTiming { | ||||
| public: | ||||
|     constexpr static const OneWiteTimeType RESET_TIMEOUT = {5000}; | ||||
|     constexpr static const OneWiteTimeType RESET_MIN[2] = {430, 48}; | ||||
|     constexpr static const OneWiteTimeType RESET_MAX[2] = {960, 80}; | ||||
|     constexpr static const OneWiteTimeType RESET_MIN = 430; | ||||
|     constexpr static const OneWiteTimeType RESET_MAX = 960; | ||||
|  | ||||
|     constexpr static const OneWiteTimeType PRESENCE_TIMEOUT = {20}; | ||||
|     constexpr static const OneWiteTimeType PRESENCE_MIN[2] = {160, 8}; | ||||
|     constexpr static const OneWiteTimeType PRESENCE_MAX[2] = {480, 32}; | ||||
|     constexpr static const OneWiteTimeType PRESENCE_TIMEOUT = 20; | ||||
|     constexpr static const OneWiteTimeType PRESENCE_MIN = 160; | ||||
|     constexpr static const OneWiteTimeType PRESENCE_MAX = 480; | ||||
|  | ||||
|     constexpr static const OneWiteTimeType MSG_HIGH_TIMEOUT = {15000}; | ||||
|     constexpr static const OneWiteTimeType SLOT_MAX[2] = {135, 30}; | ||||
|     constexpr static const OneWiteTimeType MSG_HIGH_TIMEOUT = 15000; | ||||
|     constexpr static const OneWiteTimeType SLOT_MAX = 135; | ||||
|  | ||||
|     constexpr static const OneWiteTimeType READ_MIN[2] = {20, 4}; | ||||
|     constexpr static const OneWiteTimeType READ_MAX[2] = {60, 10}; | ||||
|     constexpr static const OneWiteTimeType WRITE_ZERO[2] = {30, 8}; | ||||
|     constexpr static const OneWiteTimeType READ_MIN = 20; | ||||
|     constexpr static const OneWiteTimeType READ_MAX = 60; | ||||
|     constexpr static const OneWiteTimeType WRITE_ZERO = 30; | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user