[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:
		
							parent
							
								
									a7951ade69
								
							
						
					
					
						commit
						cf1c8fb223
					
				| @ -286,7 +286,7 @@ endif | |||||||
| BUILD_IBUTTON ?= 0 | BUILD_IBUTTON ?= 0 | ||||||
| ifeq ($(BUILD_IBUTTON), 1) | ifeq ($(BUILD_IBUTTON), 1) | ||||||
| CFLAGS		+= -DBUILD_IBUTTON | CFLAGS		+= -DBUILD_IBUTTON | ||||||
| CPP_SOURCES	+= $(APP_DIR)/ibutton/ibutton.cpp | CPP_SOURCES	+= $(wildcard $(APP_DIR)/ibutton/*.cpp) | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| APP_SDNFC ?= 0 | APP_SDNFC ?= 0 | ||||||
|  | |||||||
							
								
								
									
										319
									
								
								applications/ibutton/blanks_writer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										319
									
								
								applications/ibutton/blanks_writer.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,319 @@ | |||||||
|  | #include "blanks_writer.h" | ||||||
|  | 
 | ||||||
|  | class RW1990_1 { | ||||||
|  | public: | ||||||
|  |     constexpr static const uint8_t CMD_WRITE_RECORD_FLAG = 0xD1; | ||||||
|  |     constexpr static const uint8_t CMD_READ_RECORD_FLAG = 0xB5; | ||||||
|  |     constexpr static const uint8_t CMD_WRITE_ROM = 0xD5; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class RW1990_2 { | ||||||
|  | public: | ||||||
|  |     constexpr static const uint8_t CMD_WRITE_RECORD_FLAG = 0x1D; | ||||||
|  |     constexpr static const uint8_t CMD_READ_RECORD_FLAG = 0x1E; | ||||||
|  |     constexpr static const uint8_t CMD_WRITE_ROM = 0xD5; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class TM2004 { | ||||||
|  | public: | ||||||
|  |     constexpr static const uint8_t CMD_READ_STATUS = 0xAA; | ||||||
|  |     constexpr static const uint8_t CMD_READ_MEMORY = 0xF0; | ||||||
|  |     constexpr static const uint8_t CMD_WRITE_ROM = 0x3C; | ||||||
|  |     constexpr static const uint8_t CMD_FINALIZATION = 0x35; | ||||||
|  | 
 | ||||||
|  |     constexpr static const uint8_t ANSWER_READ_MEMORY = 0xF5; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class TM01 { | ||||||
|  | public: | ||||||
|  |     constexpr static const uint8_t CMD_WRITE_RECORD_FLAG = 0xC1; | ||||||
|  |     constexpr static const uint8_t CMD_WRITE_ROM = 0xC5; | ||||||
|  |     constexpr static const uint8_t CMD_SWITCH_TO_CYFRAL = 0xCA; | ||||||
|  |     constexpr static const uint8_t CMD_SWITCH_TO_METAKOM = 0xCB; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class DS1990 { | ||||||
|  | public: | ||||||
|  |     constexpr static const uint8_t CMD_READ_ROM = 0x33; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdarg.h> | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | void BlanksWriter::onewire_release(void) { | ||||||
|  |     gpio_write(gpio, true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void BlanksWriter::onewire_write_one_bit(bool value, uint32_t delay = 10000) { | ||||||
|  |     onewire->write_bit(value); | ||||||
|  |     delay_us(delay); | ||||||
|  |     onewire_release(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BlanksWriter::BlanksWriter(const GpioPin* one_wire_gpio) { | ||||||
|  |     gpio = one_wire_gpio; | ||||||
|  |     onewire = new OneWireMaster(gpio); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BlanksWriter::~BlanksWriter() { | ||||||
|  |     free(onewire); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | WriterResult BlanksWriter::write(KeyType type, const uint8_t* key, uint8_t key_length) { | ||||||
|  |     uint8_t write_result = -1; | ||||||
|  |     WriterResult result = WR_ERROR; | ||||||
|  | 
 | ||||||
|  |     bool same_key = false; | ||||||
|  | 
 | ||||||
|  |     osKernelLock(); | ||||||
|  |     bool presence = onewire->reset(); | ||||||
|  |     osKernelUnlock(); | ||||||
|  | 
 | ||||||
|  |     if(presence) { | ||||||
|  |         switch(type) { | ||||||
|  |         case KeyType::KEY_DS1990: | ||||||
|  |             same_key = compare_key_ds1990(key, key_length); | ||||||
|  | 
 | ||||||
|  |             if(!same_key) { | ||||||
|  |                 // currently we can write:
 | ||||||
|  |                 // RW1990, TM08v2, TM08vi-2 by write_1990_1()
 | ||||||
|  |                 // RW2004, RW2004 with EEPROM by write_TM2004();
 | ||||||
|  | 
 | ||||||
|  |                 if(write_result != 1) { | ||||||
|  |                     write_result = write_1990_1(key, key_length); | ||||||
|  |                 } | ||||||
|  |                 if(write_result != 1) { | ||||||
|  |                     write_result = write_1990_2(key, key_length); | ||||||
|  |                 } | ||||||
|  |                 if(write_result != 1) { | ||||||
|  |                     write_result = write_TM2004(key, key_length); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if(write_result == 1) { | ||||||
|  |                     result = WR_OK; | ||||||
|  |                 } else if(write_result == 0) { | ||||||
|  |                     result = WR_ERROR; | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 write_result = 0; | ||||||
|  |                 result = WR_SAME_KEY; | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool BlanksWriter::write_TM2004(const uint8_t* key, uint8_t key_length) { | ||||||
|  |     uint8_t answer; | ||||||
|  |     bool result = true; | ||||||
|  | 
 | ||||||
|  |     osKernelLock(); | ||||||
|  |     __disable_irq(); | ||||||
|  | 
 | ||||||
|  |     // write rom, addr is 0x0000
 | ||||||
|  |     onewire->reset(); | ||||||
|  |     onewire->write(TM2004::CMD_WRITE_ROM); | ||||||
|  |     onewire->write(0x00); | ||||||
|  |     onewire->write(0x00); | ||||||
|  | 
 | ||||||
|  |     // write key
 | ||||||
|  |     for(uint8_t i = 0; i < key_length; i++) { | ||||||
|  |         // write key byte
 | ||||||
|  |         onewire->write(key[i]); | ||||||
|  |         answer = onewire->read(); | ||||||
|  |         // TODO: check answer CRC
 | ||||||
|  | 
 | ||||||
|  |         // pulse indicating that data is correct
 | ||||||
|  |         delay_us(600); | ||||||
|  |         onewire_write_one_bit(1, 50000); | ||||||
|  | 
 | ||||||
|  |         // read writed key byte
 | ||||||
|  |         answer = onewire->read(); | ||||||
|  | 
 | ||||||
|  |         // check that writed and readed are same
 | ||||||
|  |         if(key[i] != answer) { | ||||||
|  |             result = false; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     onewire->reset(); | ||||||
|  | 
 | ||||||
|  |     __enable_irq(); | ||||||
|  |     osKernelUnlock(); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool BlanksWriter::write_1990_1(const uint8_t* key, uint8_t key_length) { | ||||||
|  |     bool result = true; | ||||||
|  | 
 | ||||||
|  |     osKernelLock(); | ||||||
|  |     __disable_irq(); | ||||||
|  | 
 | ||||||
|  |     // unlock
 | ||||||
|  |     onewire->reset(); | ||||||
|  |     onewire->write(RW1990_1::CMD_WRITE_RECORD_FLAG); | ||||||
|  |     delay_us(10); | ||||||
|  |     onewire_write_one_bit(0, 5000); | ||||||
|  | 
 | ||||||
|  |     // write key
 | ||||||
|  |     onewire->reset(); | ||||||
|  |     onewire->write(RW1990_1::CMD_WRITE_ROM); | ||||||
|  |     for(uint8_t i = 0; i < key_length; i++) { | ||||||
|  |         // inverted key for RW1990.1
 | ||||||
|  |         write_byte_ds1990(~key[i]); | ||||||
|  |         delay_us(30000); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // lock
 | ||||||
|  |     onewire->write(RW1990_1::CMD_WRITE_RECORD_FLAG); | ||||||
|  |     onewire_write_one_bit(1); | ||||||
|  | 
 | ||||||
|  |     __enable_irq(); | ||||||
|  |     osKernelUnlock(); | ||||||
|  | 
 | ||||||
|  |     if(!compare_key_ds1990(key, key_length)) { | ||||||
|  |         result = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool BlanksWriter::write_1990_2(const uint8_t* key, uint8_t key_length) { | ||||||
|  |     bool result = true; | ||||||
|  | 
 | ||||||
|  |     osKernelLock(); | ||||||
|  |     __disable_irq(); | ||||||
|  | 
 | ||||||
|  |     // unlock
 | ||||||
|  |     onewire->reset(); | ||||||
|  |     onewire->write(RW1990_2::CMD_WRITE_RECORD_FLAG); | ||||||
|  |     delay_us(10); | ||||||
|  |     onewire_write_one_bit(1, 5000); | ||||||
|  | 
 | ||||||
|  |     // write key
 | ||||||
|  |     onewire->reset(); | ||||||
|  |     onewire->write(RW1990_2::CMD_WRITE_ROM); | ||||||
|  |     for(uint8_t i = 0; i < key_length; i++) { | ||||||
|  |         write_byte_ds1990(key[i]); | ||||||
|  |         delay_us(30000); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // lock
 | ||||||
|  |     onewire->write(RW1990_2::CMD_WRITE_RECORD_FLAG); | ||||||
|  |     onewire_write_one_bit(0); | ||||||
|  | 
 | ||||||
|  |     __enable_irq(); | ||||||
|  |     osKernelUnlock(); | ||||||
|  | 
 | ||||||
|  |     if(!compare_key_ds1990(key, key_length)) { | ||||||
|  |         result = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TODO: untested
 | ||||||
|  | bool BlanksWriter::write_TM01(KeyType type, const uint8_t* key, uint8_t key_length) { | ||||||
|  |     bool result = true; | ||||||
|  | 
 | ||||||
|  |     osKernelLock(); | ||||||
|  |     __disable_irq(); | ||||||
|  | 
 | ||||||
|  |     // unlock
 | ||||||
|  |     onewire->reset(); | ||||||
|  |     onewire->write(TM01::CMD_WRITE_RECORD_FLAG); | ||||||
|  |     onewire_write_one_bit(1, 10000); | ||||||
|  | 
 | ||||||
|  |     // write key
 | ||||||
|  |     onewire->reset(); | ||||||
|  |     onewire->write(TM01::CMD_WRITE_ROM); | ||||||
|  | 
 | ||||||
|  |     // TODO: key types
 | ||||||
|  |     //if(type == KEY_METAKOM || type == KEY_CYFRAL) {
 | ||||||
|  |     //} else {
 | ||||||
|  |     for(uint8_t i = 0; i < key_length; i++) { | ||||||
|  |         write_byte_ds1990(key[i]); | ||||||
|  |         delay_us(10000); | ||||||
|  |     } | ||||||
|  |     //}
 | ||||||
|  | 
 | ||||||
|  |     // lock
 | ||||||
|  |     onewire->write(TM01::CMD_WRITE_RECORD_FLAG); | ||||||
|  |     onewire_write_one_bit(0, 10000); | ||||||
|  | 
 | ||||||
|  |     __enable_irq(); | ||||||
|  |     osKernelUnlock(); | ||||||
|  | 
 | ||||||
|  |     if(!compare_key_ds1990(key, key_length)) { | ||||||
|  |         result = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     osKernelLock(); | ||||||
|  |     __disable_irq(); | ||||||
|  | 
 | ||||||
|  |     if(type == KEY_METAKOM || type == KEY_CYFRAL) { | ||||||
|  |         onewire->reset(); | ||||||
|  |         if(type == KEY_CYFRAL) | ||||||
|  |             onewire->write(TM01::CMD_SWITCH_TO_CYFRAL); | ||||||
|  |         else | ||||||
|  |             onewire->write(TM01::CMD_SWITCH_TO_METAKOM); | ||||||
|  |         onewire_write_one_bit(1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     __enable_irq(); | ||||||
|  |     osKernelUnlock(); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void BlanksWriter::write_byte_ds1990(uint8_t data) { | ||||||
|  |     for(uint8_t n_bit = 0; n_bit < 8; n_bit++) { | ||||||
|  |         onewire->write_bit(data & 1); | ||||||
|  |         onewire_release(); | ||||||
|  |         delay_us(5000); | ||||||
|  |         data = data >> 1; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool BlanksWriter::compare_key_ds1990(const uint8_t* key, uint8_t key_length) { | ||||||
|  |     uint8_t buff[key_length]; | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     osKernelLock(); | ||||||
|  |     bool presence = onewire->reset(); | ||||||
|  |     osKernelUnlock(); | ||||||
|  | 
 | ||||||
|  |     if(presence) { | ||||||
|  |         osKernelLock(); | ||||||
|  |         __disable_irq(); | ||||||
|  |         onewire->write(DS1990::CMD_READ_ROM); | ||||||
|  |         onewire->read_bytes(buff, key_length); | ||||||
|  |         __enable_irq(); | ||||||
|  |         osKernelUnlock(); | ||||||
|  | 
 | ||||||
|  |         result = true; | ||||||
|  |         for(uint8_t i = 0; i < 8; i++) { | ||||||
|  |             if(key[i] != buff[i]) { | ||||||
|  |                 result = false; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void BlanksWriter::start() { | ||||||
|  |     onewire->start(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void BlanksWriter::stop() { | ||||||
|  |     onewire->stop(); | ||||||
|  | } | ||||||
							
								
								
									
										40
									
								
								applications/ibutton/blanks_writer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								applications/ibutton/blanks_writer.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "one_wire_master.h" | ||||||
|  | #include "maxim_crc.h" | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     KEY_DS1990, /**< DS1990 */ | ||||||
|  |     KEY_CYFRAL, /**< CYFRAL*/ | ||||||
|  |     KEY_METAKOM, /**< METAKOM */ | ||||||
|  | } KeyType; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     WR_OK, | ||||||
|  |     WR_SAME_KEY, | ||||||
|  |     WR_ERROR, | ||||||
|  | } WriterResult; | ||||||
|  | 
 | ||||||
|  | class BlanksWriter { | ||||||
|  | private: | ||||||
|  |     const GpioPin* gpio; | ||||||
|  |     OneWireMaster* onewire; | ||||||
|  | 
 | ||||||
|  |     void onewire_release(void); | ||||||
|  |     void onewire_write_one_bit(bool value, uint32_t delay); | ||||||
|  | 
 | ||||||
|  |     bool write_TM2004(const uint8_t* key, uint8_t key_length); | ||||||
|  |     bool write_1990_1(const uint8_t* key, uint8_t key_length); | ||||||
|  |     bool write_1990_2(const uint8_t* key, uint8_t key_length); | ||||||
|  |     bool write_TM01(KeyType type, const uint8_t* key, uint8_t key_length); | ||||||
|  | 
 | ||||||
|  |     void write_byte_ds1990(uint8_t data); | ||||||
|  |     bool compare_key_ds1990(const uint8_t* key, uint8_t key_length); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     BlanksWriter(const GpioPin* one_wire_gpio); | ||||||
|  |     ~BlanksWriter(); | ||||||
|  | 
 | ||||||
|  |     WriterResult write(KeyType type, const uint8_t* key, uint8_t key_length); | ||||||
|  |     void start(); | ||||||
|  |     void stop(); | ||||||
|  | }; | ||||||
| @ -1,6 +1,7 @@ | |||||||
| #include "ibutton.h" | #include "ibutton.h" | ||||||
| #include "ibutton_mode_dallas_read.h" | #include "ibutton_mode_dallas_read.h" | ||||||
| #include "ibutton_mode_dallas_emulate.h" | #include "ibutton_mode_dallas_emulate.h" | ||||||
|  | #include "ibutton_mode_dallas_write.h" | ||||||
| #include "ibutton_mode_cyfral_read.h" | #include "ibutton_mode_cyfral_read.h" | ||||||
| #include "ibutton_mode_cyfral_emulate.h" | #include "ibutton_mode_cyfral_emulate.h" | ||||||
| 
 | 
 | ||||||
| @ -8,8 +9,9 @@ | |||||||
| void AppiButton::run() { | void AppiButton::run() { | ||||||
|     mode[0] = new AppiButtonModeDallasRead(this); |     mode[0] = new AppiButtonModeDallasRead(this); | ||||||
|     mode[1] = new AppiButtonModeDallasEmulate(this); |     mode[1] = new AppiButtonModeDallasEmulate(this); | ||||||
|     mode[2] = new AppiButtonModeCyfralRead(this); |     mode[2] = new AppiButtonModeDallasWrite(this); | ||||||
|     mode[3] = new AppiButtonModeCyfralEmulate(this); |     mode[3] = new AppiButtonModeCyfralRead(this); | ||||||
|  |     mode[4] = new AppiButtonModeCyfralEmulate(this); | ||||||
| 
 | 
 | ||||||
|     switch_to_mode(0); |     switch_to_mode(0); | ||||||
| 
 | 
 | ||||||
| @ -21,6 +23,7 @@ void AppiButton::run() { | |||||||
|     gpio_init(red_led_record, GpioModeOutputOpenDrain); |     gpio_init(red_led_record, GpioModeOutputOpenDrain); | ||||||
|     gpio_init(green_led_record, GpioModeOutputOpenDrain); |     gpio_init(green_led_record, GpioModeOutputOpenDrain); | ||||||
| 
 | 
 | ||||||
|  |     api_hal_timebase_insomnia_enter(); | ||||||
|     app_ready(); |     app_ready(); | ||||||
| 
 | 
 | ||||||
|     AppiButtonEvent event; |     AppiButtonEvent event; | ||||||
| @ -29,9 +32,10 @@ void AppiButton::run() { | |||||||
|             if(event.type == AppiButtonEvent::EventTypeKey) { |             if(event.type == AppiButtonEvent::EventTypeKey) { | ||||||
|                 // press events
 |                 // press events
 | ||||||
|                 if(event.value.input.state && event.value.input.input == InputBack) { |                 if(event.value.input.state && event.value.input.input == InputBack) { | ||||||
|                     printf("[ibutton] bye!\n"); |  | ||||||
|                     // TODO remove all widgets create by app
 |  | ||||||
|                     widget_enabled_set(widget, false); |                     widget_enabled_set(widget, false); | ||||||
|  |                     gui_remove_widget(gui, widget); | ||||||
|  |                     api_hal_timebase_insomnia_exit(); | ||||||
|  | 
 | ||||||
|                     osThreadExit(); |                     osThreadExit(); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -56,7 +56,7 @@ public: | |||||||
|     const GpioPin* red_led_record; |     const GpioPin* red_led_record; | ||||||
|     const GpioPin* green_led_record; |     const GpioPin* green_led_record; | ||||||
| 
 | 
 | ||||||
|     static const uint8_t modes_count = 4; |     static const uint8_t modes_count = 5; | ||||||
|     AppTemplateMode<AppiButtonState, AppiButtonEvent>* mode[modes_count]; |     AppTemplateMode<AppiButtonState, AppiButtonEvent>* mode[modes_count]; | ||||||
| 
 | 
 | ||||||
|     void run(); |     void run(); | ||||||
|  | |||||||
| @ -1,38 +1,50 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include "ibutton.h" | #include "ibutton.h" | ||||||
| #include "one_wire_slave_gpio.h" | #include "one_wire_slave.h" | ||||||
| #include "one_wire_device_ds_1990.h" | #include "one_wire_device_ds_1990.h" | ||||||
|  | #include "callback-connector.h" | ||||||
|  | #include <atomic> | ||||||
| 
 | 
 | ||||||
| class AppiButtonModeDallasEmulate : public AppTemplateMode<AppiButtonState, AppiButtonEvent> { | class AppiButtonModeDallasEmulate : public AppTemplateMode<AppiButtonState, AppiButtonEvent> { | ||||||
|  | private: | ||||||
|  |     void result_callback(bool success, void* ctx); | ||||||
|  | 
 | ||||||
| public: | public: | ||||||
|     const char* name = "dallas emulate"; |     const char* name = "dallas emulate"; | ||||||
|     AppiButton* app; |     AppiButton* app; | ||||||
|     OneWireGpioSlave* onewire_slave; |  | ||||||
|     DS1990 key; |     DS1990 key; | ||||||
|  |     OneWireSlave* onewire_slave; | ||||||
| 
 | 
 | ||||||
|     void event(AppiButtonEvent* event, AppiButtonState* state); |     void event(AppiButtonEvent* event, AppiButtonState* state); | ||||||
|     void render(Canvas* canvas, AppiButtonState* state); |     void render(Canvas* canvas, AppiButtonState* state); | ||||||
|     void acquire(); |     void acquire(); | ||||||
|     void release(); |     void release(); | ||||||
| 
 | 
 | ||||||
|  |     std::atomic<bool> emulated_result{false}; | ||||||
|  | 
 | ||||||
|     AppiButtonModeDallasEmulate(AppiButton* parent_app) |     AppiButtonModeDallasEmulate(AppiButton* parent_app) | ||||||
|         : key(1, 2, 3, 4, 5, 6, 7) { |         : key(1, 2, 3, 4, 5, 6, 7) { | ||||||
|         app = parent_app; |         app = parent_app; | ||||||
| 
 | 
 | ||||||
|         // TODO open record
 |         // TODO open record
 | ||||||
|         const GpioPin* one_wire_pin_record = &ibutton_gpio; |         const GpioPin* one_wire_pin_record = &ibutton_gpio; | ||||||
|         onewire_slave = new OneWireGpioSlave(one_wire_pin_record); |         onewire_slave = new OneWireSlave(one_wire_pin_record); | ||||||
|         onewire_slave->attach(key); |         onewire_slave->attach(&key); | ||||||
|  | 
 | ||||||
|  |         auto cb = cbc::obtain_connector(this, &AppiButtonModeDallasEmulate::result_callback); | ||||||
|  |         onewire_slave->set_result_callback(cb, this); | ||||||
|     }; |     }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | void AppiButtonModeDallasEmulate::result_callback(bool success, void* ctx) { | ||||||
|  |     AppiButtonModeDallasEmulate* _this = static_cast<AppiButtonModeDallasEmulate*>(ctx); | ||||||
|  |     _this->emulated_result = success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void AppiButtonModeDallasEmulate::event(AppiButtonEvent* event, AppiButtonState* state) { | void AppiButtonModeDallasEmulate::event(AppiButtonEvent* event, AppiButtonState* state) { | ||||||
|     if(event->type == AppiButtonEvent::EventTypeTick) { |     if(event->type == AppiButtonEvent::EventTypeTick) { | ||||||
|         onewire_slave->detach(key); |         if(emulated_result) { | ||||||
|         memcpy(key.id_storage, state->dallas_address[state->dallas_address_index], 8); |             emulated_result = false; | ||||||
|         onewire_slave->attach(key); |  | ||||||
| 
 |  | ||||||
|         if(onewire_slave->emulate()) { |  | ||||||
|             app->blink_green(); |             app->blink_green(); | ||||||
|         } |         } | ||||||
|     } else if(event->type == AppiButtonEvent::EventTypeKey) { |     } else if(event->type == AppiButtonEvent::EventTypeKey) { | ||||||
| @ -44,6 +56,10 @@ void AppiButtonModeDallasEmulate::event(AppiButtonEvent* event, AppiButtonState* | |||||||
|             app->increase_dallas_address(); |             app->increase_dallas_address(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     onewire_slave->deattach(); | ||||||
|  |     memcpy(key.id_storage, state->dallas_address[state->dallas_address_index], 8); | ||||||
|  |     onewire_slave->attach(&key); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void AppiButtonModeDallasEmulate::render(Canvas* canvas, AppiButtonState* state) { | void AppiButtonModeDallasEmulate::render(Canvas* canvas, AppiButtonState* state) { | ||||||
|  | |||||||
| @ -1,13 +1,13 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include "ibutton.h" | #include "ibutton.h" | ||||||
| #include "one_wire_gpio.h" | #include "one_wire_master.h" | ||||||
| #include "maxim_crc.h" | #include "maxim_crc.h" | ||||||
| 
 | 
 | ||||||
| class AppiButtonModeDallasRead : public AppTemplateMode<AppiButtonState, AppiButtonEvent> { | class AppiButtonModeDallasRead : public AppTemplateMode<AppiButtonState, AppiButtonEvent> { | ||||||
| public: | public: | ||||||
|     const char* name = "dallas read"; |     const char* name = "dallas read"; | ||||||
|     AppiButton* app; |     AppiButton* app; | ||||||
|     OneWireGpio* onewire; |     OneWireMaster* onewire; | ||||||
| 
 | 
 | ||||||
|     void event(AppiButtonEvent* event, AppiButtonState* state); |     void event(AppiButtonEvent* event, AppiButtonState* state); | ||||||
|     void render(Canvas* canvas, AppiButtonState* state); |     void render(Canvas* canvas, AppiButtonState* state); | ||||||
| @ -19,7 +19,7 @@ public: | |||||||
| 
 | 
 | ||||||
|         // TODO open record
 |         // TODO open record
 | ||||||
|         const GpioPin* one_wire_pin_record = &ibutton_gpio; |         const GpioPin* one_wire_pin_record = &ibutton_gpio; | ||||||
|         onewire = new OneWireGpio(one_wire_pin_record); |         onewire = new OneWireMaster(one_wire_pin_record); | ||||||
|     }; |     }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -33,30 +33,17 @@ void AppiButtonModeDallasRead::event(AppiButtonEvent* event, AppiButtonState* st | |||||||
|         osKernelUnlock(); |         osKernelUnlock(); | ||||||
| 
 | 
 | ||||||
|         if(result) { |         if(result) { | ||||||
|             printf("device on line\n"); |  | ||||||
| 
 |  | ||||||
|             delay(50); |  | ||||||
|             osKernelLock(); |             osKernelLock(); | ||||||
|  |             __disable_irq(); | ||||||
|             onewire->write(0x33); |             onewire->write(0x33); | ||||||
|             onewire->read_bytes(address, 8); |             onewire->read_bytes(address, 8); | ||||||
|  |             __enable_irq(); | ||||||
|             osKernelUnlock(); |             osKernelUnlock(); | ||||||
| 
 | 
 | ||||||
|             printf("address: %x", address[0]); |  | ||||||
|             for(uint8_t i = 1; i < 8; i++) { |  | ||||||
|                 printf(":%x", address[i]); |  | ||||||
|             } |  | ||||||
|             printf("\n"); |  | ||||||
| 
 |  | ||||||
|             printf("crc8: %x\n", maxim_crc8(address, 7)); |  | ||||||
| 
 |  | ||||||
|             if(maxim_crc8(address, 8) == 0) { |             if(maxim_crc8(address, 8) == 0) { | ||||||
|                 printf("CRC valid\n"); |  | ||||||
|                 memcpy(app->state.dallas_address[app->state.dallas_address_index], address, 8); |                 memcpy(app->state.dallas_address[app->state.dallas_address_index], address, 8); | ||||||
|                 app->blink_green(); |                 app->blink_green(); | ||||||
|             } else { |  | ||||||
|                 printf("CRC invalid\n"); |  | ||||||
|             } |             } | ||||||
|         } else { |  | ||||||
|         } |         } | ||||||
|     } else if(event->type == AppiButtonEvent::EventTypeKey) { |     } else if(event->type == AppiButtonEvent::EventTypeKey) { | ||||||
|         if(event->value.input.state && event->value.input.input == InputUp) { |         if(event->value.input.state && event->value.input.input == InputUp) { | ||||||
|  | |||||||
							
								
								
									
										64
									
								
								applications/ibutton/ibutton_mode_dallas_write.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								applications/ibutton/ibutton_mode_dallas_write.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "ibutton.h" | ||||||
|  | #include "blanks_writer.h" | ||||||
|  | #include "maxim_crc.h" | ||||||
|  | 
 | ||||||
|  | class AppiButtonModeDallasWrite : public AppTemplateMode<AppiButtonState, AppiButtonEvent> { | ||||||
|  | public: | ||||||
|  |     const char* name = "dallas read"; | ||||||
|  |     AppiButton* app; | ||||||
|  |     BlanksWriter* writer; | ||||||
|  | 
 | ||||||
|  |     void event(AppiButtonEvent* event, AppiButtonState* state); | ||||||
|  |     void render(Canvas* canvas, AppiButtonState* state); | ||||||
|  |     void acquire(); | ||||||
|  |     void release(); | ||||||
|  | 
 | ||||||
|  |     const GpioPin* one_wire_pin_record; | ||||||
|  | 
 | ||||||
|  |     AppiButtonModeDallasWrite(AppiButton* parent_app) { | ||||||
|  |         app = parent_app; | ||||||
|  | 
 | ||||||
|  |         // TODO open record
 | ||||||
|  |         one_wire_pin_record = &ibutton_gpio; | ||||||
|  |         writer = new BlanksWriter(one_wire_pin_record); | ||||||
|  |     }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void AppiButtonModeDallasWrite::event(AppiButtonEvent* event, AppiButtonState* state) { | ||||||
|  |     if(event->type == AppiButtonEvent::EventTypeTick) { | ||||||
|  |         WriterResult result = | ||||||
|  |             writer->write(KEY_DS1990, state->dallas_address[state->dallas_address_index], 8); | ||||||
|  | 
 | ||||||
|  |         if(result == WR_SAME_KEY) { | ||||||
|  |             app->blink_green(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(result == WR_OK) { | ||||||
|  |             app->blink_red(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } else if(event->type == AppiButtonEvent::EventTypeKey) { | ||||||
|  |         if(event->value.input.state && event->value.input.input == InputUp) { | ||||||
|  |             app->decrease_dallas_address(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(event->value.input.state && event->value.input.input == InputDown) { | ||||||
|  |             app->increase_dallas_address(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AppiButtonModeDallasWrite::render(Canvas* canvas, AppiButtonState* state) { | ||||||
|  |     canvas_set_font(canvas, FontSecondary); | ||||||
|  |     canvas_draw_str(canvas, 2, 25, "< Dallas write >"); | ||||||
|  |     app->render_dallas_list(canvas, state); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AppiButtonModeDallasWrite::acquire() { | ||||||
|  |     writer->start(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AppiButtonModeDallasWrite::release() { | ||||||
|  |     writer->stop(); | ||||||
|  | } | ||||||
| @ -18,6 +18,8 @@ static InputState input_state = { | |||||||
|     false, |     false, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | static void exti_input_callback(void* _pin, void* _ctx); | ||||||
|  | 
 | ||||||
| void input_task(void* p) { | void input_task(void* p) { | ||||||
|     uint32_t state_bits = 0; |     uint32_t state_bits = 0; | ||||||
|     uint8_t debounce_counters[INPUT_COUNT]; |     uint8_t debounce_counters[INPUT_COUNT]; | ||||||
| @ -38,6 +40,8 @@ void input_task(void* p) { | |||||||
|     furi_record_create("input_state", &input_state_record); |     furi_record_create("input_state", &input_state_record); | ||||||
|     furi_record_create("input_events", &input_events_record); |     furi_record_create("input_events", &input_events_record); | ||||||
| 
 | 
 | ||||||
|  |     api_interrupt_add(exti_input_callback, InterruptTypeExternalInterrupt, NULL); | ||||||
|  | 
 | ||||||
|     // we ready to work
 |     // we ready to work
 | ||||||
|     initialized = true; |     initialized = true; | ||||||
| 
 | 
 | ||||||
| @ -103,7 +107,10 @@ void input_task(void* p) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void HAL_GPIO_EXTI_Callback(uint16_t pin) { | static void exti_input_callback(void* _pin, void* _ctx) { | ||||||
|  |     // interrupt manager get us pin constant, so...
 | ||||||
|  |     uint32_t pin = (uint32_t)_pin; | ||||||
|  | 
 | ||||||
| #ifdef APP_NFC | #ifdef APP_NFC | ||||||
|     if(pin == NFC_IRQ_Pin) { |     if(pin == NFC_IRQ_Pin) { | ||||||
|         nfc_isr(); |         nfc_isr(); | ||||||
|  | |||||||
| @ -330,7 +330,8 @@ void lf_rfid_workaround(void* p) { | |||||||
|                     api_interrupt_add( |                     api_interrupt_add( | ||||||
|                         comparator_trigger_callback, InterruptTypeComparatorTrigger, comp_ctx); |                         comparator_trigger_callback, InterruptTypeComparatorTrigger, comp_ctx); | ||||||
|                 } else { |                 } else { | ||||||
|                     api_interrupt_remove(comparator_trigger_callback); |                     api_interrupt_remove( | ||||||
|  |                         comparator_trigger_callback, InterruptTypeComparatorTrigger); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 hal_pwmn_set( |                 hal_pwmn_set( | ||||||
|  | |||||||
| @ -1,97 +0,0 @@ | |||||||
| #include <stdio.h> |  | ||||||
| #include <string.h> |  | ||||||
| #include <furi.h> |  | ||||||
| #include "minunit.h" |  | ||||||
| #include "furi-new.h" |  | ||||||
| 
 |  | ||||||
| const int int_value_init = 0x1234; |  | ||||||
| const int int_value_changed = 0x5678; |  | ||||||
| osMessageQueueId_t test_messages; |  | ||||||
| 
 |  | ||||||
| typedef struct { |  | ||||||
|     char text[256]; |  | ||||||
|     bool result; |  | ||||||
| } test_message; |  | ||||||
| 
 |  | ||||||
| #define SEND_MESSAGE(value, data)                                            \ |  | ||||||
|     {                                                                        \ |  | ||||||
|         message.result = value;                                              \ |  | ||||||
|         snprintf(message.text, 256, "Error at line %d, %s", __LINE__, data); \ |  | ||||||
|         osMessageQueuePut(test_messages, &message, 0U, 0U);                  \ |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| void _furi_new_wait() { |  | ||||||
|     osThreadFlagsWait(0x0001U, osFlagsWaitAny, osWaitForever); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void _furi_new_continue(FuriAppId thread_id) { |  | ||||||
|     osThreadFlagsSet(thread_id, 0x0001U); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void _furi_new_main_app(void* p) { |  | ||||||
|     test_message message; |  | ||||||
| 
 |  | ||||||
|     _furi_new_wait(); |  | ||||||
| 
 |  | ||||||
|     int another_test_value = int_value_init; |  | ||||||
|     furi_record_create("test/another_app_record", &another_test_value); |  | ||||||
| 
 |  | ||||||
|     SEND_MESSAGE(false, "dummy text"); |  | ||||||
| 
 |  | ||||||
|     new_flapp_app_exit(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void test_furi_new() { |  | ||||||
|     test_message message; |  | ||||||
|     test_messages = osMessageQueueNew(1, sizeof(test_message), NULL); |  | ||||||
| 
 |  | ||||||
|     // init core
 |  | ||||||
|     new_furi_init(); |  | ||||||
| 
 |  | ||||||
|     // launch test thread
 |  | ||||||
|     FuriAppId main_app = new_flapp_app_start(_furi_new_main_app, "main_app", 512, NULL); |  | ||||||
|     _furi_new_continue(main_app); |  | ||||||
| 
 |  | ||||||
|     while(1) { |  | ||||||
|         if(osMessageQueueGet(test_messages, &message, NULL, osWaitForever) == osOK) { |  | ||||||
|             if(message.result == true) { |  | ||||||
|                 break; |  | ||||||
|             } else { |  | ||||||
|                 mu_assert(false, message.text); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     /*
 |  | ||||||
|     // test that "create" wont affect pointer value
 |  | ||||||
|     furi_record_create("test/record", &test_value); |  | ||||||
|     mu_assert_int_eq(test_value, int_value_init); |  | ||||||
| 
 |  | ||||||
|     // test that we get correct pointer
 |  | ||||||
|     int* test_value_pointer = furi_record_open("test/record"); |  | ||||||
|     mu_assert_pointers_not_eq(test_value_pointer, NULL); |  | ||||||
|     mu_assert_pointers_eq(test_value_pointer, &test_value); |  | ||||||
| 
 |  | ||||||
|     *test_value_pointer = int_value_changed; |  | ||||||
|     mu_assert_int_eq(test_value, int_value_changed); |  | ||||||
| 
 |  | ||||||
|     // start another app
 |  | ||||||
|     new_record_available = osSemaphoreNew(1, 1, NULL); |  | ||||||
|     osSemaphoreAcquire(new_record_available, osWaitForever); |  | ||||||
| 
 |  | ||||||
|     osThreadAttr_t another_app_attr = {.name = "another_app", .stack_size = 512}; |  | ||||||
|     osThreadId_t player = osThreadNew(another_app, NULL, &another_app_attr); |  | ||||||
| 
 |  | ||||||
|     // wait until app create record
 |  | ||||||
|     osSemaphoreAcquire(new_record_available, osWaitForever); |  | ||||||
| 
 |  | ||||||
|     // open record, test that record pointed to int_value_init
 |  | ||||||
|     test_value_pointer = furi_record_open("test/another_app_record"); |  | ||||||
|     mu_assert_pointers_not_eq(test_value_pointer, NULL); |  | ||||||
|     mu_assert_int_eq(*test_value_pointer, int_value_init); |  | ||||||
| 
 |  | ||||||
|     // test that we can close, (unsubscribe) from record
 |  | ||||||
|     bool close_result = new_furi_close("test/another_app_record"); |  | ||||||
|     mu_assert(close_result, "cannot close record"); |  | ||||||
|     */ |  | ||||||
| } |  | ||||||
| @ -16,7 +16,6 @@ void test_furi_value_manager(); | |||||||
| void test_furi_event(); | void test_furi_event(); | ||||||
| 
 | 
 | ||||||
| void test_furi_memmgr(); | void test_furi_memmgr(); | ||||||
| void test_furi_new(); |  | ||||||
| 
 | 
 | ||||||
| static int foo = 0; | static int foo = 0; | ||||||
| 
 | 
 | ||||||
| @ -63,10 +62,6 @@ MU_TEST(mu_test_furi_memmgr) { | |||||||
|     test_furi_memmgr(); |     test_furi_memmgr(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(mu_test_furi_new) { |  | ||||||
|     test_furi_new(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| MU_TEST(mu_test_furi_value_expanders) { | MU_TEST(mu_test_furi_value_expanders) { | ||||||
|     test_furi_value_composer(); |     test_furi_value_composer(); | ||||||
|     test_furi_value_manager(); |     test_furi_value_manager(); | ||||||
| @ -92,7 +87,6 @@ MU_TEST_SUITE(test_suite) { | |||||||
|     MU_RUN_TEST(mu_test_furi_event); |     MU_RUN_TEST(mu_test_furi_event); | ||||||
| 
 | 
 | ||||||
|     MU_RUN_TEST(mu_test_furi_memmgr); |     MU_RUN_TEST(mu_test_furi_memmgr); | ||||||
|     MU_RUN_TEST(mu_test_furi_new); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int run_minunit() { | int run_minunit() { | ||||||
|  | |||||||
| @ -1,23 +1,36 @@ | |||||||
| #include "api-interrupt-mgr.h" | #include "api-interrupt-mgr.h" | ||||||
|  | #include "mlib/m-dict.h" | ||||||
| 
 | 
 | ||||||
| #include <m-list.h> | #include <m-list.h> | ||||||
| #include <cmsis_os2.h> | #include <cmsis_os2.h> | ||||||
| 
 | 
 | ||||||
| LIST_DEF(list_interrupt, InterruptCallbackItem, M_POD_OPLIST); | LIST_DEF(list_interrupt, InterruptCallbackItem, M_POD_OPLIST); | ||||||
| list_interrupt_t interrupts; | DICT_DEF2(dict_interrupt, uint32_t, M_DEFAULT_OPLIST, list_interrupt_t, M_A1_OPLIST); | ||||||
| osMutexId_t interrupt_list_mutex; | 
 | ||||||
|  | dict_interrupt_t interrupts_dict; | ||||||
|  | osMutexId_t interrupt_mutex; | ||||||
| 
 | 
 | ||||||
| bool api_interrupt_init() { | bool api_interrupt_init() { | ||||||
|     interrupt_list_mutex = osMutexNew(NULL); |     dict_interrupt_init(interrupts_dict); | ||||||
|     return (interrupt_list_mutex != NULL); |     interrupt_mutex = osMutexNew(NULL); | ||||||
|  |     return (interrupt_mutex != NULL); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void api_interrupt_add(InterruptCallback callback, InterruptType type, void* context) { | void api_interrupt_add(InterruptCallback callback, InterruptType type, void* context) { | ||||||
|     if(osMutexAcquire(interrupt_list_mutex, osWaitForever) == osOK) { |     if(osMutexAcquire(interrupt_mutex, osWaitForever) == osOK) { | ||||||
|  |         list_interrupt_t* list = dict_interrupt_get(interrupts_dict, (uint32_t)type); | ||||||
|  | 
 | ||||||
|  |         if(list == NULL) { | ||||||
|  |             list_interrupt_t new_list; | ||||||
|  |             list_interrupt_init(new_list); | ||||||
|  |             dict_interrupt_set_at(interrupts_dict, (uint32_t)type, new_list); | ||||||
|  |             list = dict_interrupt_get(interrupts_dict, (uint32_t)type); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         // put uninitialized item to the list
 |         // put uninitialized item to the list
 | ||||||
|         // M_POD_OPLIST provide memset(&(a), 0, sizeof (a)) constructor
 |         // M_POD_OPLIST provide memset(&(a), 0, sizeof (a)) constructor
 | ||||||
|         // so item will not be ready until we set ready flag
 |         // so item will not be ready until we set ready flag
 | ||||||
|         InterruptCallbackItem* item = list_interrupt_push_new(interrupts); |         InterruptCallbackItem* item = list_interrupt_push_new(*list); | ||||||
| 
 | 
 | ||||||
|         // initialize item
 |         // initialize item
 | ||||||
|         item->callback = callback; |         item->callback = callback; | ||||||
| @ -28,26 +41,66 @@ void api_interrupt_add(InterruptCallback callback, InterruptType type, void* con | |||||||
|         // TODO remove on app exit
 |         // TODO remove on app exit
 | ||||||
|         //flapp_on_exit(api_interrupt_remove, callback);
 |         //flapp_on_exit(api_interrupt_remove, callback);
 | ||||||
| 
 | 
 | ||||||
|         osMutexRelease(interrupt_list_mutex); |         osMutexRelease(interrupt_mutex); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void api_interrupt_remove(InterruptCallback callback) { | void api_interrupt_remove(InterruptCallback callback, InterruptType type) { | ||||||
|     if(osMutexAcquire(interrupt_list_mutex, osWaitForever) == osOK) { |     if(osMutexAcquire(interrupt_mutex, osWaitForever) == osOK) { | ||||||
|         // iterate over items
 |         list_interrupt_t* list = dict_interrupt_get(interrupts_dict, (uint32_t)type); | ||||||
|         list_interrupt_it_t it; |  | ||||||
|         for(list_interrupt_it(it, interrupts); !list_interrupt_end_p(it); |  | ||||||
|             list_interrupt_next(it)) { |  | ||||||
|             const InterruptCallbackItem* item = list_interrupt_cref(it); |  | ||||||
| 
 | 
 | ||||||
|             // if the iterator is equal to our element
 |         if(list != NULL) { | ||||||
|             if(item->callback == callback) { |             // iterate over items
 | ||||||
|                 list_interrupt_remove(interrupts, it); |             list_interrupt_it_t it; | ||||||
|                 break; |             list_interrupt_it(it, *list); | ||||||
|  |             while(!list_interrupt_end_p(it)) { | ||||||
|  |                 if(it->current->data.callback == callback) { | ||||||
|  |                     list_interrupt_remove(*list, it); | ||||||
|  |                 } else { | ||||||
|  |                     list_interrupt_next(it); | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         osMutexRelease(interrupt_list_mutex); |         osMutexRelease(interrupt_mutex); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void api_interrupt_enable(InterruptCallback callback, InterruptType type) { | ||||||
|  |     if(osMutexAcquire(interrupt_mutex, osWaitForever) == osOK) { | ||||||
|  |         list_interrupt_t* list = dict_interrupt_get(interrupts_dict, (uint32_t)type); | ||||||
|  | 
 | ||||||
|  |         if(list != NULL) { | ||||||
|  |             // iterate over items
 | ||||||
|  |             list_interrupt_it_t it; | ||||||
|  |             for(list_interrupt_it(it, *list); !list_interrupt_end_p(it); list_interrupt_next(it)) { | ||||||
|  |                 // if the iterator is equal to our element
 | ||||||
|  |                 if(it->current->data.callback == callback) { | ||||||
|  |                     it->current->data.ready = true; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         osMutexRelease(interrupt_mutex); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void api_interrupt_disable(InterruptCallback callback, InterruptType type) { | ||||||
|  |     if(osMutexAcquire(interrupt_mutex, osWaitForever) == osOK) { | ||||||
|  |         list_interrupt_t* list = dict_interrupt_get(interrupts_dict, (uint32_t)type); | ||||||
|  | 
 | ||||||
|  |         if(list != NULL) { | ||||||
|  |             // iterate over items
 | ||||||
|  |             list_interrupt_it_t it; | ||||||
|  |             for(list_interrupt_it(it, *list); !list_interrupt_end_p(it); list_interrupt_next(it)) { | ||||||
|  |                 // if the iterator is equal to our element
 | ||||||
|  |                 if(it->current->data.callback == callback) { | ||||||
|  |                     it->current->data.ready = false; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         osMutexRelease(interrupt_mutex); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -55,14 +108,16 @@ void api_interrupt_call(InterruptType type, void* hw) { | |||||||
|     // that executed in interrupt ctx so mutex don't needed
 |     // that executed in interrupt ctx so mutex don't needed
 | ||||||
|     // but we need to check ready flag
 |     // but we need to check ready flag
 | ||||||
| 
 | 
 | ||||||
|     // iterate over items
 |     list_interrupt_t* list = dict_interrupt_get(interrupts_dict, (uint32_t)type); | ||||||
|     list_interrupt_it_t it; |  | ||||||
|     for(list_interrupt_it(it, interrupts); !list_interrupt_end_p(it); list_interrupt_next(it)) { |  | ||||||
|         const InterruptCallbackItem* item = list_interrupt_cref(it); |  | ||||||
| 
 | 
 | ||||||
|         // if the iterator is equal to our element
 |     if(list != NULL) { | ||||||
|         if(item->type == type && item->ready) { |         // iterate over items
 | ||||||
|             item->callback(hw, item->context); |         list_interrupt_it_t it; | ||||||
|  |         for(list_interrupt_it(it, *list); !list_interrupt_end_p(it); list_interrupt_next(it)) { | ||||||
|  |             // if the iterator is equal to our element
 | ||||||
|  |             if(it->current->data.ready) { | ||||||
|  |                 it->current->data.callback(hw, it->current->data.context); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -11,6 +11,9 @@ typedef void (*InterruptCallback)(void*, void*); | |||||||
| typedef enum { | typedef enum { | ||||||
|     InterruptTypeComparatorTrigger, |     InterruptTypeComparatorTrigger, | ||||||
|     InterruptTypeTimerCapture, |     InterruptTypeTimerCapture, | ||||||
|  |     InterruptTypeTimerOutputCompare, | ||||||
|  |     InterruptTypeTimerUpdate, | ||||||
|  |     InterruptTypeExternalInterrupt, | ||||||
| } InterruptType; | } InterruptType; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
| @ -22,7 +25,9 @@ typedef struct { | |||||||
| 
 | 
 | ||||||
| bool api_interrupt_init(); | bool api_interrupt_init(); | ||||||
| void api_interrupt_add(InterruptCallback callback, InterruptType type, void* context); | void api_interrupt_add(InterruptCallback callback, InterruptType type, void* context); | ||||||
| void api_interrupt_remove(InterruptCallback callback); | void api_interrupt_remove(InterruptCallback callback, InterruptType type); | ||||||
|  | void api_interrupt_enable(InterruptCallback callback, InterruptType type); | ||||||
|  | void api_interrupt_disable(InterruptCallback callback, InterruptType type); | ||||||
| void api_interrupt_call(InterruptType type, void* hw); | void api_interrupt_call(InterruptType type, void* hw); | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
|  | |||||||
| @ -62,7 +62,9 @@ void ADC1_IRQHandler(void); | |||||||
| void USB_LP_IRQHandler(void); | void USB_LP_IRQHandler(void); | ||||||
| void COMP_IRQHandler(void); | void COMP_IRQHandler(void); | ||||||
| void EXTI9_5_IRQHandler(void); | void EXTI9_5_IRQHandler(void); | ||||||
|  | void TIM1_UP_TIM16_IRQHandler(void); | ||||||
| void TIM1_TRG_COM_TIM17_IRQHandler(void); | void TIM1_TRG_COM_TIM17_IRQHandler(void); | ||||||
|  | void TIM1_CC_IRQHandler(void); | ||||||
| void TIM2_IRQHandler(void); | void TIM2_IRQHandler(void); | ||||||
| void EXTI15_10_IRQHandler(void); | void EXTI15_10_IRQHandler(void); | ||||||
| void HSEM_IRQHandler(void); | void HSEM_IRQHandler(void); | ||||||
|  | |||||||
| @ -64,6 +64,9 @@ extern COMP_HandleTypeDef hcomp1; | |||||||
| extern RTC_HandleTypeDef hrtc; | extern RTC_HandleTypeDef hrtc; | ||||||
| extern TIM_HandleTypeDef htim1; | extern TIM_HandleTypeDef htim1; | ||||||
| extern TIM_HandleTypeDef htim2; | extern TIM_HandleTypeDef htim2; | ||||||
|  | extern TIM_HandleTypeDef htim16; | ||||||
|  | extern TIM_HandleTypeDef htim17; | ||||||
|  | 
 | ||||||
| /* USER CODE BEGIN EV */ | /* USER CODE BEGIN EV */ | ||||||
| 
 | 
 | ||||||
| /* USER CODE END EV */ | /* USER CODE END EV */ | ||||||
| @ -292,6 +295,21 @@ void EXTI9_5_IRQHandler(void) | |||||||
|   /* USER CODE END EXTI9_5_IRQn 1 */ |   /* USER CODE END EXTI9_5_IRQn 1 */ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |   * @brief This function handles TIM1 update interrupt and TIM16 global interrupt. | ||||||
|  |   */ | ||||||
|  | void TIM1_UP_TIM16_IRQHandler(void) | ||||||
|  | { | ||||||
|  |   /* USER CODE BEGIN TIM1_UP_TIM16_IRQn 0 */ | ||||||
|  | 
 | ||||||
|  |   /* USER CODE END TIM1_UP_TIM16_IRQn 0 */ | ||||||
|  |   HAL_TIM_IRQHandler(&htim1); | ||||||
|  |   HAL_TIM_IRQHandler(&htim16); | ||||||
|  |   /* USER CODE BEGIN TIM1_UP_TIM16_IRQn 1 */ | ||||||
|  | 
 | ||||||
|  |   /* USER CODE END TIM1_UP_TIM16_IRQn 1 */ | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|   * @brief This function handles TIM1 trigger and commutation interrupts and TIM17 global interrupt. |   * @brief This function handles TIM1 trigger and commutation interrupts and TIM17 global interrupt. | ||||||
|   */ |   */ | ||||||
| @ -306,6 +324,20 @@ void TIM1_TRG_COM_TIM17_IRQHandler(void) | |||||||
|   /* USER CODE END TIM1_TRG_COM_TIM17_IRQn 1 */ |   /* USER CODE END TIM1_TRG_COM_TIM17_IRQn 1 */ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |   * @brief This function handles TIM1 capture compare interrupt. | ||||||
|  |   */ | ||||||
|  | void TIM1_CC_IRQHandler(void) | ||||||
|  | { | ||||||
|  |   /* USER CODE BEGIN TIM1_CC_IRQn 0 */ | ||||||
|  | 
 | ||||||
|  |   /* USER CODE END TIM1_CC_IRQn 0 */ | ||||||
|  |   HAL_TIM_IRQHandler(&htim1); | ||||||
|  |   /* USER CODE BEGIN TIM1_CC_IRQn 1 */ | ||||||
|  | 
 | ||||||
|  |   /* USER CODE END TIM1_CC_IRQn 1 */ | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|   * @brief This function handles TIM2 global interrupt. |   * @brief This function handles TIM2 global interrupt. | ||||||
|   */ |   */ | ||||||
| @ -333,7 +365,7 @@ void EXTI15_10_IRQHandler(void) | |||||||
|   HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12); |   HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12); | ||||||
|   HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13); |   HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13); | ||||||
|   /* USER CODE BEGIN EXTI15_10_IRQn 1 */ |   /* USER CODE BEGIN EXTI15_10_IRQn 1 */ | ||||||
| 
 |   HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_14); | ||||||
|   /* USER CODE END EXTI15_10_IRQn 1 */ |   /* USER CODE END EXTI15_10_IRQn 1 */ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,6 +2,11 @@ | |||||||
| 
 | 
 | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| /* Initialize timebase
 | /* Initialize timebase
 | ||||||
|  * Configure and start tick timer |  * Configure and start tick timer | ||||||
|  */ |  */ | ||||||
| @ -25,3 +30,8 @@ void api_hal_timebase_insomnia_enter(); | |||||||
|  * Must be paired with api_hal_timebase_insomnia_enter |  * Must be paired with api_hal_timebase_insomnia_enter | ||||||
|  */ |  */ | ||||||
| void api_hal_timebase_insomnia_exit(); | void api_hal_timebase_insomnia_exit(); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -4,10 +4,33 @@ | |||||||
| extern void api_interrupt_call(InterruptType type, void* hw); | extern void api_interrupt_call(InterruptType type, void* hw); | ||||||
| 
 | 
 | ||||||
| /* interrupts */ | /* interrupts */ | ||||||
|  | 
 | ||||||
|  | /* Comparator trigger event */ | ||||||
| void HAL_COMP_TriggerCallback(COMP_HandleTypeDef* hcomp) { | void HAL_COMP_TriggerCallback(COMP_HandleTypeDef* hcomp) { | ||||||
|     api_interrupt_call(InterruptTypeComparatorTrigger, hcomp); |     api_interrupt_call(InterruptTypeComparatorTrigger, hcomp); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Timer input capture event */ | ||||||
| void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef* htim) { | void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef* htim) { | ||||||
|     api_interrupt_call(InterruptTypeTimerCapture, htim); |     api_interrupt_call(InterruptTypeTimerCapture, htim); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | /* Output compare event */ | ||||||
|  | void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef* htim) { | ||||||
|  |     api_interrupt_call(InterruptTypeTimerOutputCompare, htim); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Timer update event */ | ||||||
|  | void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim) { | ||||||
|  |     api_interrupt_call(InterruptTypeTimerUpdate, htim); | ||||||
|  | 
 | ||||||
|  |     // handle HAL ticks
 | ||||||
|  |     if(htim->Instance == TIM17) { | ||||||
|  |         HAL_IncTick(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* External interrupt event */ | ||||||
|  | void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { | ||||||
|  |     api_interrupt_call(InterruptTypeExternalInterrupt, GPIO_Pin); | ||||||
|  | } | ||||||
| @ -256,7 +256,7 @@ void CyfralReaderComp::stop(void) { | |||||||
| 
 | 
 | ||||||
|     // disconnect comparator callback
 |     // disconnect comparator callback
 | ||||||
|     auto cmp_cb = cbc::obtain_connector(this, &CyfralReaderComp::comparator_trigger_callback); |     auto cmp_cb = cbc::obtain_connector(this, &CyfralReaderComp::comparator_trigger_callback); | ||||||
|     api_interrupt_remove(cmp_cb); |     api_interrupt_remove(cmp_cb, InterruptTypeComparatorTrigger); | ||||||
|     osMessageQueueDelete(comp_event_queue); |     osMessageQueueDelete(comp_event_queue); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,8 +1,5 @@ | |||||||
| #include "one_wire_device.h" | #include "one_wire_device.h" | ||||||
| 
 | 
 | ||||||
| // TODO fix GPL compability
 |  | ||||||
| // currently we use rework of OneWireHub
 |  | ||||||
| 
 |  | ||||||
| OneWireDevice::OneWireDevice( | OneWireDevice::OneWireDevice( | ||||||
|     uint8_t id_1, |     uint8_t id_1, | ||||||
|     uint8_t id_2, |     uint8_t id_2, | ||||||
| @ -21,6 +18,22 @@ OneWireDevice::OneWireDevice( | |||||||
|     id_storage[7] = maxim_crc8(id_storage, 7); |     id_storage[7] = maxim_crc8(id_storage, 7); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void OneWireDevice::send_id(OneWireGpioSlave* owner) const { | OneWireDevice::~OneWireDevice() { | ||||||
|     owner->send(id_storage, 8); |     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 | #pragma once | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include "maxim_crc.h" | #include "maxim_crc.h" | ||||||
| #include "one_wire_slave_gpio.h" | #include "one_wire_slave.h" | ||||||
| 
 |  | ||||||
| // TODO fix GPL compability
 |  | ||||||
| // currently we use rework of OneWireHub
 |  | ||||||
| 
 | 
 | ||||||
| class OneWireDevice { | class OneWireDevice { | ||||||
| public: | public: | ||||||
| @ -17,18 +14,13 @@ public: | |||||||
|         uint8_t id_6, |         uint8_t id_6, | ||||||
|         uint8_t id_7); |         uint8_t id_7); | ||||||
| 
 | 
 | ||||||
|     ~OneWireDevice() = default; // TODO: detach if deleted before hub
 |     ~OneWireDevice(); | ||||||
| 
 |  | ||||||
|     // 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; |  | ||||||
| 
 | 
 | ||||||
|     uint8_t id_storage[8]; |     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" | #include "one_wire_device_ds_1990.h" | ||||||
| 
 | 
 | ||||||
| // TODO fix GPL compability
 |  | ||||||
| // currently we use rework of OneWireHub
 |  | ||||||
| 
 |  | ||||||
| DS1990::DS1990( | DS1990::DS1990( | ||||||
|     uint8_t ID1, |     uint8_t ID1, | ||||||
|     uint8_t ID2, |     uint8_t ID2, | ||||||
| @ -13,15 +10,3 @@ DS1990::DS1990( | |||||||
|     uint8_t ID7) |     uint8_t ID7) | ||||||
|     : OneWireDevice(ID1, ID2, ID3, ID4, ID5, ID6, 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 | #pragma once | ||||||
| #include "one_wire_device.h" | #include "one_wire_device.h" | ||||||
| 
 | 
 | ||||||
| // TODO fix GPL compability
 |  | ||||||
| // currently we use rework of OneWireHub
 |  | ||||||
| 
 |  | ||||||
| class DS1990 : public OneWireDevice { | class DS1990 : public OneWireDevice { | ||||||
| public: | public: | ||||||
|     static constexpr uint8_t family_code{0x01}; |     static constexpr uint8_t family_code{0x01}; | ||||||
| @ -16,6 +13,4 @@ public: | |||||||
|         uint8_t ID5, |         uint8_t ID5, | ||||||
|         uint8_t ID6, |         uint8_t ID6, | ||||||
|         uint8_t ID7); |         uint8_t ID7); | ||||||
| 
 |  | ||||||
|     void do_work(OneWireGpioSlave* owner) final; |  | ||||||
| }; | }; | ||||||
| @ -1,41 +1,24 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include <furi.h> | #include "one_wire_master.h" | ||||||
| #include "one_wire_timings.h" | #include "one_wire_timings.h" | ||||||
| 
 | 
 | ||||||
| class OneWireGpio { | OneWireMaster::OneWireMaster(const GpioPin* one_wire_gpio) { | ||||||
| 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) { |  | ||||||
|     gpio = one_wire_gpio; |     gpio = one_wire_gpio; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| OneWireGpio::~OneWireGpio() { | OneWireMaster::~OneWireMaster() { | ||||||
|     stop(); |     stop(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void OneWireGpio::start(void) { | void OneWireMaster::start(void) { | ||||||
|     gpio_init(gpio, GpioModeOutputOpenDrain); |     gpio_init(gpio, GpioModeOutputOpenDrain); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void OneWireGpio::stop(void) { | void OneWireMaster::stop(void) { | ||||||
|     gpio_init(gpio, GpioModeAnalog); |     gpio_init(gpio, GpioModeAnalog); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool OneWireGpio::reset(void) { | bool OneWireMaster::reset(void) { | ||||||
|     uint8_t r; |     uint8_t r; | ||||||
|     uint8_t retries = 125; |     uint8_t retries = 125; | ||||||
| 
 | 
 | ||||||
| @ -64,7 +47,7 @@ bool OneWireGpio::reset(void) { | |||||||
|     return r; |     return r; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool OneWireGpio::read_bit(void) { | bool OneWireMaster::read_bit(void) { | ||||||
|     bool result; |     bool result; | ||||||
| 
 | 
 | ||||||
|     // drive low
 |     // drive low
 | ||||||
| @ -82,7 +65,7 @@ bool OneWireGpio::read_bit(void) { | |||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void OneWireGpio::write_bit(bool value) { | void OneWireMaster::write_bit(bool value) { | ||||||
|     if(value) { |     if(value) { | ||||||
|         // drive low
 |         // drive low
 | ||||||
|         gpio_write(gpio, false); |         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; |     uint8_t result = 0; | ||||||
| 
 | 
 | ||||||
|     for(uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) { |     for(uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) { | ||||||
| @ -114,16 +97,20 @@ uint8_t OneWireGpio::read(void) { | |||||||
|     return result; |     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++) { |     for(uint16_t i = 0; i < count; i++) { | ||||||
|         buffer[i] = read(); |         buffer[i] = read(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void OneWireGpio::write(uint8_t value) { | void OneWireMaster::write(uint8_t value) { | ||||||
|     uint8_t bitMask; |     uint8_t bitMask; | ||||||
| 
 | 
 | ||||||
|     for(bitMask = 0x01; bitMask; bitMask <<= 1) { |     for(bitMask = 0x01; bitMask; bitMask <<= 1) { | ||||||
|         write_bit((bitMask & value) ? 1 : 0); |         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" | #include "one_wire_timings.h" | ||||||
| 
 | 
 | ||||||
| // fix pre C++17 "undefined reference" errors
 | // fix pre C++17 "undefined reference" errors
 | ||||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::RESET_TIMEOUT; | constexpr const OneWiteTimeType OneWireEmulateTiming::RESET_MIN; | ||||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::RESET_MIN[2]; | constexpr const OneWiteTimeType OneWireEmulateTiming::RESET_MAX; | ||||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::RESET_MAX[2]; |  | ||||||
| 
 | 
 | ||||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::PRESENCE_TIMEOUT; | constexpr const OneWiteTimeType OneWireEmulateTiming::PRESENCE_TIMEOUT; | ||||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::PRESENCE_MIN[2]; | constexpr const OneWiteTimeType OneWireEmulateTiming::PRESENCE_MIN; | ||||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::PRESENCE_MAX[2]; | constexpr const OneWiteTimeType OneWireEmulateTiming::PRESENCE_MAX; | ||||||
| 
 | 
 | ||||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::MSG_HIGH_TIMEOUT; | 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_MIN; | ||||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::READ_MAX[2]; | constexpr const OneWiteTimeType OneWireEmulateTiming::READ_MAX; | ||||||
| constexpr const OneWiteTimeType OneWireEmulateTiming::WRITE_ZERO[2]; | constexpr const OneWiteTimeType OneWireEmulateTiming::WRITE_ZERO; | ||||||
|  | |||||||
| @ -3,10 +3,10 @@ | |||||||
| 
 | 
 | ||||||
| class __OneWireTiming { | class __OneWireTiming { | ||||||
| public: | 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_B = 64; | ||||||
|     constexpr static const uint16_t TIMING_C = 60; |     constexpr static const uint16_t TIMING_C = 64; | ||||||
|     constexpr static const uint16_t TIMING_D = 10; |     constexpr static const uint16_t TIMING_D = 14; | ||||||
|     constexpr static const uint16_t TIMING_E = 9; |     constexpr static const uint16_t TIMING_E = 9; | ||||||
|     constexpr static const uint16_t TIMING_F = 55; |     constexpr static const uint16_t TIMING_F = 55; | ||||||
|     constexpr static const uint16_t TIMING_G = 0; |     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_DRIVE = __OneWireTiming::TIMING_C; | ||||||
|     constexpr static const uint16_t WRITE_0_RELEASE = __OneWireTiming::TIMING_D; |     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_RELEASE = __OneWireTiming::TIMING_E; | ||||||
|     constexpr static const uint16_t READ_DELAY_POST = __OneWireTiming::TIMING_F; |     constexpr static const uint16_t READ_DELAY_POST = __OneWireTiming::TIMING_F; | ||||||
| 
 | 
 | ||||||
| @ -37,18 +37,17 @@ typedef uint32_t OneWiteTimeType; | |||||||
| 
 | 
 | ||||||
| class OneWireEmulateTiming { | class OneWireEmulateTiming { | ||||||
| public: | public: | ||||||
|     constexpr static const OneWiteTimeType RESET_TIMEOUT = {5000}; |     constexpr static const OneWiteTimeType RESET_MIN = 430; | ||||||
|     constexpr static const OneWiteTimeType RESET_MIN[2] = {430, 48}; |     constexpr static const OneWiteTimeType RESET_MAX = 960; | ||||||
|     constexpr static const OneWiteTimeType RESET_MAX[2] = {960, 80}; |  | ||||||
| 
 | 
 | ||||||
|     constexpr static const OneWiteTimeType PRESENCE_TIMEOUT = {20}; |     constexpr static const OneWiteTimeType PRESENCE_TIMEOUT = 20; | ||||||
|     constexpr static const OneWiteTimeType PRESENCE_MIN[2] = {160, 8}; |     constexpr static const OneWiteTimeType PRESENCE_MIN = 160; | ||||||
|     constexpr static const OneWiteTimeType PRESENCE_MAX[2] = {480, 32}; |     constexpr static const OneWiteTimeType PRESENCE_MAX = 480; | ||||||
| 
 | 
 | ||||||
|     constexpr static const OneWiteTimeType MSG_HIGH_TIMEOUT = {15000}; |     constexpr static const OneWiteTimeType MSG_HIGH_TIMEOUT = 15000; | ||||||
|     constexpr static const OneWiteTimeType SLOT_MAX[2] = {135, 30}; |     constexpr static const OneWiteTimeType SLOT_MAX = 135; | ||||||
| 
 | 
 | ||||||
|     constexpr static const OneWiteTimeType READ_MIN[2] = {20, 4}; |     constexpr static const OneWiteTimeType READ_MIN = 20; | ||||||
|     constexpr static const OneWiteTimeType READ_MAX[2] = {60, 10}; |     constexpr static const OneWiteTimeType READ_MAX = 60; | ||||||
|     constexpr static const OneWiteTimeType WRITE_ZERO[2] = {30, 8}; |     constexpr static const OneWiteTimeType WRITE_ZERO = 30; | ||||||
| }; | }; | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 DrZlo13
						DrZlo13