[FL-84] iButton app, dallas emulate + cyfral read, cyfral emulate (#253)
* maxim crc function * one wire template device and ds1990 classes * 3 fields for addr * cyfral emulator lib * add cyfral read mode, refract rendering and events * add ADC1_IN14, add adc interrupt * cyfral read mode * rename and move api-hal includes folder * build onewire libs only if we build app * start in mode 0
This commit is contained in:
		
							parent
							
								
									758e37e294
								
							
						
					
					
						commit
						1f761d7fbb
					
				| @ -1,11 +1,15 @@ | |||||||
| #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_cyfral_read.h" | ||||||
|  | #include "ibutton_mode_cyfral_emulate.h" | ||||||
| 
 | 
 | ||||||
| // start app
 | // start app
 | ||||||
| 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[3] = new AppiButtonModeCyfralEmulate(this); | ||||||
| 
 | 
 | ||||||
|     switch_to_mode(0); |     switch_to_mode(0); | ||||||
| 
 | 
 | ||||||
| @ -21,7 +25,7 @@ void AppiButton::run() { | |||||||
| 
 | 
 | ||||||
|     AppiButtonEvent event; |     AppiButtonEvent event; | ||||||
|     while(1) { |     while(1) { | ||||||
|         if(get_event(&event, 100)) { |         if(get_event(&event, 20)) { | ||||||
|             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) { | ||||||
| @ -60,6 +64,46 @@ void AppiButton::render(CanvasApi* canvas) { | |||||||
|     mode[state.mode_index]->render(canvas, &state); |     mode[state.mode_index]->render(canvas, &state); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void AppiButton::render_dallas_list(CanvasApi* canvas, AppiButtonState* state) { | ||||||
|  |     const uint8_t buffer_size = 50; | ||||||
|  |     char buf[buffer_size]; | ||||||
|  |     for(uint8_t i = 0; i < state->dallas_address_count; i++) { | ||||||
|  |         snprintf( | ||||||
|  |             buf, | ||||||
|  |             buffer_size, | ||||||
|  |             "%s[%u] %x:%x:%x:%x:%x:%x:%x:%x", | ||||||
|  |             (i == state->dallas_address_index) ? "> " : "", | ||||||
|  |             i + 1, | ||||||
|  |             state->dallas_address[i][0], | ||||||
|  |             state->dallas_address[i][1], | ||||||
|  |             state->dallas_address[i][2], | ||||||
|  |             state->dallas_address[i][3], | ||||||
|  |             state->dallas_address[i][4], | ||||||
|  |             state->dallas_address[i][5], | ||||||
|  |             state->dallas_address[i][6], | ||||||
|  |             state->dallas_address[i][7]); | ||||||
|  |         canvas->draw_str(canvas, 2, 37 + i * 12, buf); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AppiButton::render_cyfral_list(CanvasApi* canvas, AppiButtonState* state) { | ||||||
|  |     const uint8_t buffer_size = 50; | ||||||
|  |     char buf[buffer_size]; | ||||||
|  |     for(uint8_t i = 0; i < state->cyfral_address_count; i++) { | ||||||
|  |         snprintf( | ||||||
|  |             buf, | ||||||
|  |             buffer_size, | ||||||
|  |             "%s[%u] %x:%x:%x:%x", | ||||||
|  |             (i == state->cyfral_address_index) ? "> " : "", | ||||||
|  |             i + 1, | ||||||
|  |             state->cyfral_address[i][0], | ||||||
|  |             state->cyfral_address[i][1], | ||||||
|  |             state->cyfral_address[i][2], | ||||||
|  |             state->cyfral_address[i][3]); | ||||||
|  |         canvas->draw_str(canvas, 2, 37 + i * 12, buf); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void AppiButton::blink_red() { | void AppiButton::blink_red() { | ||||||
|     gpio_write(red_led_record, 0); |     gpio_write(red_led_record, 0); | ||||||
|     delay(10); |     delay(10); | ||||||
| @ -92,12 +136,34 @@ void AppiButton::decrease_mode() { | |||||||
|     release_state(); |     release_state(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void AppiButton::increase_dallas_address() { | ||||||
|  |     if(state.dallas_address_index < (state.dallas_address_count - 1)) { | ||||||
|  |         state.dallas_address_index++; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AppiButton::decrease_dallas_address() { | ||||||
|  |     if(state.dallas_address_index > 0) { | ||||||
|  |         state.dallas_address_index--; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AppiButton::increase_cyfral_address() { | ||||||
|  |     if(state.cyfral_address_index < (state.cyfral_address_count - 1)) { | ||||||
|  |         state.cyfral_address_index++; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AppiButton::decrease_cyfral_address() { | ||||||
|  |     if(state.cyfral_address_index > 0) { | ||||||
|  |         state.cyfral_address_index--; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void AppiButton::switch_to_mode(uint8_t mode_index) { | void AppiButton::switch_to_mode(uint8_t mode_index) { | ||||||
|     acquire_state(); |  | ||||||
|     mode[state.mode_index]->release(); |     mode[state.mode_index]->release(); | ||||||
|     state.mode_index = mode_index; |     state.mode_index = mode_index; | ||||||
|     mode[state.mode_index]->acquire(); |     mode[state.mode_index]->acquire(); | ||||||
|     release_state(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // app enter function
 | // app enter function
 | ||||||
|  | |||||||
| @ -8,13 +8,28 @@ typedef uint8_t event_t; | |||||||
| class AppiButtonState { | class AppiButtonState { | ||||||
| public: | public: | ||||||
|     // state data
 |     // state data
 | ||||||
|     // test key = {0x01, 0xFD, 0x0E, 0x84, 0x01, 0x00, 0x00, 0xDB};
 |     static const uint8_t dallas_address_count = 3; | ||||||
|     uint8_t dallas_address[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; |     uint8_t dallas_address[dallas_address_count][8] = { | ||||||
|  |         {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, | ||||||
|  |         {0x01, 0x41, 0xCE, 0x67, 0x0F, 0x00, 0x00, 0xB6}, | ||||||
|  |         {0x01, 0xFD, 0x0E, 0x84, 0x01, 0x00, 0x00, 0xDB}}; | ||||||
|  | 
 | ||||||
|  |     uint8_t dallas_address_index; | ||||||
|  | 
 | ||||||
|  |     static const uint8_t cyfral_address_count = 3; | ||||||
|  |     uint8_t cyfral_address[cyfral_address_count][4] = { | ||||||
|  |         {0x00, 0x00, 0x00, 0x00}, | ||||||
|  |         {0xBB, 0xBB, 0x7B, 0xBD}, | ||||||
|  |         {0x7B, 0xDE, 0x7B, 0xDE}}; | ||||||
|  |     uint8_t cyfral_address_index; | ||||||
|  | 
 | ||||||
|     uint8_t mode_index; |     uint8_t mode_index; | ||||||
| 
 | 
 | ||||||
|     // state initializer
 |     // state initializer
 | ||||||
|     AppiButtonState() { |     AppiButtonState() { | ||||||
|         mode_index = 0; |         mode_index = 0; | ||||||
|  |         dallas_address_index = 0; | ||||||
|  |         cyfral_address_index = 0; | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -41,16 +56,22 @@ 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 = 2; |     static const uint8_t modes_count = 4; | ||||||
|     AppTemplateMode<AppiButtonState, AppiButtonEvent>* mode[modes_count]; |     AppTemplateMode<AppiButtonState, AppiButtonEvent>* mode[modes_count]; | ||||||
| 
 | 
 | ||||||
|     void run(); |     void run(); | ||||||
|     void render(CanvasApi* canvas); |     void render(CanvasApi* canvas); | ||||||
|  |     void render_dallas_list(CanvasApi* canvas, AppiButtonState* state); | ||||||
|  |     void render_cyfral_list(CanvasApi* canvas, AppiButtonState* state); | ||||||
| 
 | 
 | ||||||
|     void blink_red(); |     void blink_red(); | ||||||
|     void blink_green(); |     void blink_green(); | ||||||
| 
 | 
 | ||||||
|     void increase_mode(); |     void increase_mode(); | ||||||
|     void decrease_mode(); |     void decrease_mode(); | ||||||
|  |     void increase_dallas_address(); | ||||||
|  |     void decrease_dallas_address(); | ||||||
|  |     void increase_cyfral_address(); | ||||||
|  |     void decrease_cyfral_address(); | ||||||
|     void switch_to_mode(uint8_t mode_index); |     void switch_to_mode(uint8_t mode_index); | ||||||
| }; | }; | ||||||
							
								
								
									
										55
									
								
								applications/ibutton/ibutton_mode_cyfral_emulate.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								applications/ibutton/ibutton_mode_cyfral_emulate.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "ibutton.h" | ||||||
|  | #include "cyfral_emulator.h" | ||||||
|  | 
 | ||||||
|  | class AppiButtonModeCyfralEmulate : public AppTemplateMode<AppiButtonState, AppiButtonEvent> { | ||||||
|  | public: | ||||||
|  |     const char* name = "cyfral emulate"; | ||||||
|  |     AppiButton* app; | ||||||
|  |     CyfralEmulator* cyfral_emulator; | ||||||
|  | 
 | ||||||
|  |     void event(AppiButtonEvent* event, AppiButtonState* state); | ||||||
|  |     void render(CanvasApi* canvas, AppiButtonState* state); | ||||||
|  |     void acquire(); | ||||||
|  |     void release(); | ||||||
|  | 
 | ||||||
|  |     AppiButtonModeCyfralEmulate(AppiButton* parent_app) { | ||||||
|  |         app = parent_app; | ||||||
|  | 
 | ||||||
|  |         // TODO open record
 | ||||||
|  |         const GpioPin* one_wire_pin_record = &ibutton_gpio; | ||||||
|  |         cyfral_emulator = new CyfralEmulator(one_wire_pin_record); | ||||||
|  |     }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void AppiButtonModeCyfralEmulate::event(AppiButtonEvent* event, AppiButtonState* state) { | ||||||
|  |     if(event->type == AppiButtonEvent::EventTypeTick) { | ||||||
|  |         // repeat key sending 8 times
 | ||||||
|  |         cyfral_emulator->send(state->cyfral_address[state->cyfral_address_index], 4, 8); | ||||||
|  |         app->blink_green(); | ||||||
|  | 
 | ||||||
|  |     } else if(event->type == AppiButtonEvent::EventTypeKey) { | ||||||
|  |         if(event->value.input.state && event->value.input.input == InputUp) { | ||||||
|  |             app->decrease_cyfral_address(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(event->value.input.state && event->value.input.input == InputDown) { | ||||||
|  |             app->increase_cyfral_address(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AppiButtonModeCyfralEmulate::render(CanvasApi* canvas, AppiButtonState* state) { | ||||||
|  |     canvas->set_font(canvas, FontSecondary); | ||||||
|  |     canvas->draw_str(canvas, 2, 25, "< Cyfral emulate"); | ||||||
|  | 
 | ||||||
|  |     app->render_cyfral_list(canvas, state); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AppiButtonModeCyfralEmulate::acquire() { | ||||||
|  |     cyfral_emulator->start(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AppiButtonModeCyfralEmulate::release() { | ||||||
|  |     cyfral_emulator->stop(); | ||||||
|  | } | ||||||
							
								
								
									
										53
									
								
								applications/ibutton/ibutton_mode_cyfral_read.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								applications/ibutton/ibutton_mode_cyfral_read.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "ibutton.h" | ||||||
|  | #include "cyfral_reader.h" | ||||||
|  | 
 | ||||||
|  | class AppiButtonModeCyfralRead : public AppTemplateMode<AppiButtonState, AppiButtonEvent> { | ||||||
|  | public: | ||||||
|  |     const char* name = "cyfral read"; | ||||||
|  |     AppiButton* app; | ||||||
|  |     CyfralReader* reader; | ||||||
|  | 
 | ||||||
|  |     void event(AppiButtonEvent* event, AppiButtonState* state); | ||||||
|  |     void render(CanvasApi* canvas, AppiButtonState* state); | ||||||
|  |     void acquire(); | ||||||
|  |     void release(); | ||||||
|  | 
 | ||||||
|  |     AppiButtonModeCyfralRead(AppiButton* parent_app) { | ||||||
|  |         app = parent_app; | ||||||
|  |         reader = new CyfralReader(ADC1, ADC_CHANNEL_14); | ||||||
|  |     }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void AppiButtonModeCyfralRead::event(AppiButtonEvent* event, AppiButtonState* state) { | ||||||
|  |     if(event->type == AppiButtonEvent::EventTypeTick) { | ||||||
|  |         uint8_t data[8]; | ||||||
|  |         if(reader->read(data, 4)) { | ||||||
|  |             memcpy(app->state.cyfral_address[app->state.cyfral_address_index], data, 4); | ||||||
|  |             app->blink_green(); | ||||||
|  |         } | ||||||
|  |     } else if(event->type == AppiButtonEvent::EventTypeKey) { | ||||||
|  |         if(event->value.input.state && event->value.input.input == InputUp) { | ||||||
|  |             app->decrease_cyfral_address(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(event->value.input.state && event->value.input.input == InputDown) { | ||||||
|  |             app->increase_cyfral_address(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AppiButtonModeCyfralRead::render(CanvasApi* canvas, AppiButtonState* state) { | ||||||
|  |     canvas->set_font(canvas, FontSecondary); | ||||||
|  |     canvas->draw_str(canvas, 2, 25, "< Cyfral read >"); | ||||||
|  | 
 | ||||||
|  |     app->render_cyfral_list(canvas, state); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AppiButtonModeCyfralRead::acquire() { | ||||||
|  |     reader->start(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AppiButtonModeCyfralRead::release() { | ||||||
|  |     reader->stop(); | ||||||
|  | } | ||||||
| @ -1,59 +1,56 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include "ibutton.h" | #include "ibutton.h" | ||||||
| #include "one_wire_slave_gpio.h" | #include "one_wire_slave_gpio.h" | ||||||
|  | #include "one_wire_device_ds_1990.h" | ||||||
| 
 | 
 | ||||||
| class AppiButtonModeDallasEmulate : public AppTemplateMode<AppiButtonState, AppiButtonEvent> { | class AppiButtonModeDallasEmulate : public AppTemplateMode<AppiButtonState, AppiButtonEvent> { | ||||||
| public: | public: | ||||||
|     const char* name = "dallas emulate"; |     const char* name = "dallas emulate"; | ||||||
|     AppiButton* app; |     AppiButton* app; | ||||||
|     OneWireGpioSlave* onewire_slave; |     OneWireGpioSlave* onewire_slave; | ||||||
|  |     DS1990 key; | ||||||
| 
 | 
 | ||||||
|     void event(AppiButtonEvent* event, AppiButtonState* state); |     void event(AppiButtonEvent* event, AppiButtonState* state); | ||||||
|     void render(CanvasApi* canvas, AppiButtonState* state); |     void render(CanvasApi* canvas, AppiButtonState* state); | ||||||
|     void acquire(); |     void acquire(); | ||||||
|     void release(); |     void release(); | ||||||
| 
 | 
 | ||||||
|     AppiButtonModeDallasEmulate(AppiButton* parent_app) { |     AppiButtonModeDallasEmulate(AppiButton* parent_app) | ||||||
|  |         : 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 OneWireGpioSlave(one_wire_pin_record); | ||||||
|  |         onewire_slave->attach(key); | ||||||
|     }; |     }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void AppiButtonModeDallasEmulate::event(AppiButtonEvent* event, AppiButtonState* state) { | void AppiButtonModeDallasEmulate::event(AppiButtonEvent* event, AppiButtonState* state) { | ||||||
|     if(event->type == AppiButtonEvent::EventTypeTick) { |     if(event->type == AppiButtonEvent::EventTypeTick) { | ||||||
|         app->blink_red(); |         onewire_slave->detach(key); | ||||||
|         /*if(onewire_slave->emulate(state->dallas_address, 8)) {
 |         memcpy(key.id_storage, state->dallas_address[state->dallas_address_index], 8); | ||||||
|             app->blink_green(); |         onewire_slave->attach(key); | ||||||
|         } else { |  | ||||||
| 
 | 
 | ||||||
|         }*/ |         if(onewire_slave->emulate()) { | ||||||
|  |             app->blink_green(); | ||||||
|  |         } | ||||||
|  |     } 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 AppiButtonModeDallasEmulate::render(CanvasApi* canvas, AppiButtonState* state) { | void AppiButtonModeDallasEmulate::render(CanvasApi* canvas, AppiButtonState* state) { | ||||||
|     canvas->set_font(canvas, FontSecondary); |     canvas->set_font(canvas, FontSecondary); | ||||||
|     canvas->draw_str(canvas, 2, 25, "< dallas emulate"); |     canvas->draw_str(canvas, 2, 25, "< Dallas emulate >"); | ||||||
|     canvas->draw_str(canvas, 2, 37, "unimplemented"); | 
 | ||||||
|     { |     app->render_dallas_list(canvas, state); | ||||||
|         const uint8_t buffer_size = 32; |  | ||||||
|         char buf[buffer_size]; |  | ||||||
|         snprintf( |  | ||||||
|             buf, |  | ||||||
|             buffer_size, |  | ||||||
|             "%x:%x:%x:%x:%x:%x:%x:%x", |  | ||||||
|             state->dallas_address[0], |  | ||||||
|             state->dallas_address[1], |  | ||||||
|             state->dallas_address[2], |  | ||||||
|             state->dallas_address[3], |  | ||||||
|             state->dallas_address[4], |  | ||||||
|             state->dallas_address[5], |  | ||||||
|             state->dallas_address[6], |  | ||||||
|             state->dallas_address[7]); |  | ||||||
|         canvas->draw_str(canvas, 2, 50, buf); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void AppiButtonModeDallasEmulate::acquire() { | void AppiButtonModeDallasEmulate::acquire() { | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include "ibutton.h" | #include "ibutton.h" | ||||||
| #include "one_wire_gpio.h" | #include "one_wire_gpio.h" | ||||||
|  | #include "maxim_crc.h" | ||||||
| 
 | 
 | ||||||
| class AppiButtonModeDallasRead : public AppTemplateMode<AppiButtonState, AppiButtonEvent> { | class AppiButtonModeDallasRead : public AppTemplateMode<AppiButtonState, AppiButtonEvent> { | ||||||
| public: | public: | ||||||
| @ -20,8 +21,6 @@ public: | |||||||
|         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 OneWireGpio(one_wire_pin_record); | ||||||
|     }; |     }; | ||||||
| 
 |  | ||||||
|     uint8_t crc_8(uint8_t* buffer, uint8_t count); |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void AppiButtonModeDallasRead::event(AppiButtonEvent* event, AppiButtonState* state) { | void AppiButtonModeDallasRead::event(AppiButtonEvent* event, AppiButtonState* state) { | ||||||
| @ -48,67 +47,32 @@ void AppiButtonModeDallasRead::event(AppiButtonEvent* event, AppiButtonState* st | |||||||
|             } |             } | ||||||
|             printf("\n"); |             printf("\n"); | ||||||
| 
 | 
 | ||||||
|             printf("crc8: %x\n", crc_8(address, 7)); |             printf("crc8: %x\n", maxim_crc8(address, 7)); | ||||||
| 
 | 
 | ||||||
|             if(crc_8(address, 8) == 0) { |             if(maxim_crc8(address, 8) == 0) { | ||||||
|                 printf("CRC valid\n"); |                 printf("CRC valid\n"); | ||||||
|                 memcpy(app->state.dallas_address, address, 8); |                 memcpy(app->state.dallas_address[app->state.dallas_address_index], address, 8); | ||||||
|                 app->blink_green(); |                 app->blink_green(); | ||||||
|             } else { |             } else { | ||||||
|                 printf("CRC invalid\n"); |                 printf("CRC invalid\n"); | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|         } |         } | ||||||
|  |     } 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 AppiButtonModeDallasRead::render(CanvasApi* canvas, AppiButtonState* state) { | void AppiButtonModeDallasRead::render(CanvasApi* canvas, AppiButtonState* state) { | ||||||
|     canvas->set_font(canvas, FontSecondary); |     canvas->set_font(canvas, FontSecondary); | ||||||
|     canvas->draw_str(canvas, 2, 25, "dallas read >"); |     canvas->draw_str(canvas, 2, 25, "Dallas read >"); | ||||||
|     canvas->draw_str(canvas, 2, 37, "touch me, iButton"); |     app->render_dallas_list(canvas, state); | ||||||
|     { |  | ||||||
|         const uint8_t buffer_size = 32; |  | ||||||
|         char buf[buffer_size]; |  | ||||||
|         snprintf( |  | ||||||
|             buf, |  | ||||||
|             buffer_size, |  | ||||||
|             "%x:%x:%x:%x:%x:%x:%x:%x", |  | ||||||
|             state->dallas_address[0], |  | ||||||
|             state->dallas_address[1], |  | ||||||
|             state->dallas_address[2], |  | ||||||
|             state->dallas_address[3], |  | ||||||
|             state->dallas_address[4], |  | ||||||
|             state->dallas_address[5], |  | ||||||
|             state->dallas_address[6], |  | ||||||
|             state->dallas_address[7]); |  | ||||||
|         canvas->draw_str(canvas, 2, 50, buf); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| uint8_t AppiButtonModeDallasRead::crc_8(uint8_t* buffer, uint8_t count) { |  | ||||||
|     const uint8_t maxim_crc8_table[256] = { |  | ||||||
|         0,   94,  188, 226, 97,  63,  221, 131, 194, 156, 126, 32,  163, 253, 31,  65,  157, 195, |  | ||||||
|         33,  127, 252, 162, 64,  30,  95,  1,   227, 189, 62,  96,  130, 220, 35,  125, 159, 193, |  | ||||||
|         66,  28,  254, 160, 225, 191, 93,  3,   128, 222, 60,  98,  190, 224, 2,   92,  223, 129, |  | ||||||
|         99,  61,  124, 34,  192, 158, 29,  67,  161, 255, 70,  24,  250, 164, 39,  121, 155, 197, |  | ||||||
|         132, 218, 56,  102, 229, 187, 89,  7,   219, 133, 103, 57,  186, 228, 6,   88,  25,  71, |  | ||||||
|         165, 251, 120, 38,  196, 154, 101, 59,  217, 135, 4,   90,  184, 230, 167, 249, 27,  69, |  | ||||||
|         198, 152, 122, 36,  248, 166, 68,  26,  153, 199, 37,  123, 58,  100, 134, 216, 91,  5, |  | ||||||
|         231, 185, 140, 210, 48,  110, 237, 179, 81,  15,  78,  16,  242, 172, 47,  113, 147, 205, |  | ||||||
|         17,  79,  173, 243, 112, 46,  204, 146, 211, 141, 111, 49,  178, 236, 14,  80,  175, 241, |  | ||||||
|         19,  77,  206, 144, 114, 44,  109, 51,  209, 143, 12,  82,  176, 238, 50,  108, 142, 208, |  | ||||||
|         83,  13,  239, 177, 240, 174, 76,  18,  145, 207, 45,  115, 202, 148, 118, 40,  171, 245, |  | ||||||
|         23,  73,  8,   86,  180, 234, 105, 55,  213, 139, 87,  9,   235, 181, 54,  104, 138, 212, |  | ||||||
|         149, 203, 41,  119, 244, 170, 72,  22,  233, 183, 85,  11,  136, 214, 52,  106, 43,  117, |  | ||||||
|         151, 201, 74,  20,  246, 168, 116, 42,  200, 150, 21,  75,  169, 247, 182, 232, 10,  84, |  | ||||||
|         215, 137, 107, 53}; |  | ||||||
| 
 |  | ||||||
|     uint8_t crc = 0; |  | ||||||
| 
 |  | ||||||
|     while(count--) { |  | ||||||
|         crc = maxim_crc8_table[(crc ^ *buffer++)]; |  | ||||||
|     } |  | ||||||
|     return crc; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void AppiButtonModeDallasRead::acquire() { | void AppiButtonModeDallasRead::acquire() { | ||||||
|  | |||||||
| @ -1,177 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include "flipper.h" |  | ||||||
| #include "flipper_v2.h" |  | ||||||
| #include "one_wire_timings.h" |  | ||||||
| 
 |  | ||||||
| class OneWireGpioSlave { |  | ||||||
| private: |  | ||||||
|     const GpioPin* gpio; |  | ||||||
| 
 |  | ||||||
| public: |  | ||||||
|     OneWireGpioSlave(const GpioPin* one_wire_gpio); |  | ||||||
|     ~OneWireGpioSlave(); |  | ||||||
|     void start(void); |  | ||||||
|     void stop(void); |  | ||||||
|     bool emulate(uint8_t* buffer, uint8_t length); |  | ||||||
| 
 |  | ||||||
|     bool check_reset(void); |  | ||||||
|     bool show_presence(void); |  | ||||||
|     bool receive_and_process_cmd(void); |  | ||||||
|     bool receive(uint8_t* data, const uint8_t data_length); |  | ||||||
|     bool receiveBit(void); |  | ||||||
| 
 |  | ||||||
|     bool overdrive_mode = false; |  | ||||||
| 
 |  | ||||||
|     OneWiteTimeType wait_while_gpio(volatile OneWiteTimeType retries, const bool pin_value); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| OneWireGpioSlave::OneWireGpioSlave(const GpioPin* one_wire_gpio) { |  | ||||||
|     gpio = one_wire_gpio; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| OneWireGpioSlave::~OneWireGpioSlave() { |  | ||||||
|     stop(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void OneWireGpioSlave::start(void) { |  | ||||||
|     gpio_init(gpio, GpioModeOutputOpenDrain); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void OneWireGpioSlave::stop(void) { |  | ||||||
|     gpio_init(gpio, GpioModeAnalog); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool OneWireGpioSlave::emulate(uint8_t* buffer, uint8_t length) { |  | ||||||
|     if(!check_reset()) { |  | ||||||
|         printf("reset error\n"); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(!show_presence()) { |  | ||||||
|         printf("presence error\n"); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(!receive_and_process_cmd()) { |  | ||||||
|         printf("receive_and_process_cmd error\n"); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     printf("ok\n"); |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| OneWiteTimeType OneWireGpioSlave::wait_while_gpio(OneWiteTimeType time, const bool pin_value) { |  | ||||||
|     uint32_t start = DWT->CYCCNT; |  | ||||||
|     uint32_t time_ticks = time * (SystemCoreClock / 1000000.0f); |  | ||||||
| 
 |  | ||||||
|     while(((DWT->CYCCNT - start) < time_ticks)) { |  | ||||||
|         if(gpio_read(gpio) != pin_value) { |  | ||||||
|             uint32_t time = (DWT->CYCCNT - start); |  | ||||||
|             time /= (SystemCoreClock / 1000000.0f); |  | ||||||
|             return time; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool OneWireGpioSlave::check_reset(void) { |  | ||||||
|     while(gpio_read(gpio) == true) { |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /*if(wait_while_gpio(OneWireEmulateTiming::RESET_TIMEOUT * 20, true) == 0) {
 |  | ||||||
|         printf("RESET_TIMEOUT\n"); |  | ||||||
|         return false; |  | ||||||
|     }*/ |  | ||||||
| 
 |  | ||||||
|     const OneWiteTimeType time_remaining = |  | ||||||
|         wait_while_gpio(OneWireEmulateTiming::RESET_MAX[0], false); |  | ||||||
| 
 |  | ||||||
|     if(time_remaining == 0) { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(overdrive_mode && ((OneWireEmulateTiming::RESET_MAX[0] - |  | ||||||
|                            OneWireEmulateTiming::RESET_MIN[0]) <= time_remaining)) { |  | ||||||
|         // normal reset detected
 |  | ||||||
|         overdrive_mode = false; |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     bool result = (time_remaining <= OneWireEmulateTiming::RESET_MAX[0]) && |  | ||||||
|                   time_remaining >= OneWireEmulateTiming::RESET_MIN[overdrive_mode]; |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool OneWireGpioSlave::show_presence(void) { |  | ||||||
|     wait_while_gpio(OneWireEmulateTiming::PRESENCE_TIMEOUT, true); |  | ||||||
|     gpio_write(gpio, false); |  | ||||||
|     delay_us(OneWireEmulateTiming::PRESENCE_MIN[overdrive_mode]); |  | ||||||
|     gpio_write(gpio, true); |  | ||||||
|     /*OneWiteTimeType wait_time = OneWireEmulateTiming::PRESENCE_MAX[overdrive_mode] -
 |  | ||||||
|                                 OneWireEmulateTiming::PRESENCE_MIN[overdrive_mode]; |  | ||||||
|     if(wait_while_gpio(wait_time, false) == 0) { |  | ||||||
|         return false; |  | ||||||
|     }*/ |  | ||||||
| 
 |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool OneWireGpioSlave::receive_and_process_cmd(void) { |  | ||||||
|     uint8_t cmd; |  | ||||||
|     receive(&cmd, 1); |  | ||||||
|     printf("cmd %x\n", cmd); |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool OneWireGpioSlave::receiveBit(void) { |  | ||||||
|     // wait while bus is HIGH
 |  | ||||||
|     OneWiteTimeType time = OneWireEmulateTiming::SLOT_MAX[overdrive_mode]; |  | ||||||
|     time = wait_while_gpio(time, true); |  | ||||||
|     if(time == 0) { |  | ||||||
|         printf("RESET_IN_PROGRESS\n"); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|     /*while ((DIRECT_READ(pin_baseReg, pin_bitMask) == 0) && (--retries != 0));
 |  | ||||||
|     if (retries == 0) |  | ||||||
|     { |  | ||||||
|         _error = Error::RESET_IN_PROGRESS; |  | ||||||
|         return false; |  | ||||||
|     }*/ |  | ||||||
| 
 |  | ||||||
|     // wait while bus is LOW
 |  | ||||||
|     time = OneWireEmulateTiming::MSG_HIGH_TIMEOUT; |  | ||||||
|     time = wait_while_gpio(time, false); |  | ||||||
|     if(time == 0) { |  | ||||||
|         printf("TIMEOUT_HIGH\n"); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|     /*while ((DIRECT_READ(pin_baseReg, pin_bitMask) != 0) && (--retries != 0));
 |  | ||||||
|     if (retries == 0) |  | ||||||
|     { |  | ||||||
|         _error = Error::AWAIT_TIMESLOT_TIMEOUT_HIGH; |  | ||||||
|         return false; |  | ||||||
|     }*/ |  | ||||||
| 
 |  | ||||||
|     // wait a specific time to do a read (data is valid by then), // first difference to inner-loop of write()
 |  | ||||||
|     time = OneWireEmulateTiming::READ_MIN[overdrive_mode]; |  | ||||||
|     time = wait_while_gpio(time, true); |  | ||||||
|     //while ((DIRECT_READ(pin_baseReg, pin_bitMask) == 0) && (--retries != 0));
 |  | ||||||
| 
 |  | ||||||
|     return (time > 0); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool OneWireGpioSlave::receive(uint8_t* data, const uint8_t data_length) { |  | ||||||
|     uint8_t bytes_received = 0; |  | ||||||
|     for(; bytes_received < data_length; ++bytes_received) { |  | ||||||
|         uint8_t value = 0; |  | ||||||
| 
 |  | ||||||
|         for(uint8_t bitMask = 0x01; bitMask != 0; bitMask <<= 1) { |  | ||||||
|             if(receiveBit()) value |= bitMask; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         data[bytes_received] = value; |  | ||||||
|     } |  | ||||||
|     return (bytes_received != data_length); |  | ||||||
| } |  | ||||||
| @ -14,7 +14,7 @@ include			$(PROJECT_ROOT)/lib/lib.mk | |||||||
| TARGET			?= f2 | TARGET			?= f2 | ||||||
| TARGET_DIR		= targets/$(TARGET) | TARGET_DIR		= targets/$(TARGET) | ||||||
| include			$(TARGET_DIR)/target.mk | include			$(TARGET_DIR)/target.mk | ||||||
| CFLAGS			+= -Itargets/Inc | CFLAGS			+= -Itargets/api-hal-include | ||||||
| 
 | 
 | ||||||
| include			$(PROJECT_ROOT)/make/git.mk | include			$(PROJECT_ROOT)/make/git.mk | ||||||
| include			$(PROJECT_ROOT)/make/toolchain.mk | include			$(PROJECT_ROOT)/make/toolchain.mk | ||||||
|  | |||||||
| @ -55,6 +55,7 @@ void UsageFault_Handler(void); | |||||||
| void DebugMon_Handler(void); | void DebugMon_Handler(void); | ||||||
| void EXTI1_IRQHandler(void); | void EXTI1_IRQHandler(void); | ||||||
| void EXTI2_IRQHandler(void); | void EXTI2_IRQHandler(void); | ||||||
|  | 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); | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| ##########################################################################################################################
 | ##########################################################################################################################
 | ||||||
| # File automatically-generated by tool: [projectgenerator] version: [3.10.0-B14] date: [Thu Nov 05 17:53:24 MSK 2020] 
 | # File automatically-generated by tool: [projectgenerator] version: [3.10.0-B14] date: [Mon Nov 23 12:04:48 VLAT 2020] 
 | ||||||
| ##########################################################################################################################
 | ##########################################################################################################################
 | ||||||
| 
 | 
 | ||||||
| # ------------------------------------------------
 | # ------------------------------------------------
 | ||||||
|  | |||||||
| @ -54,7 +54,7 @@ void MX_ADC1_Init(void) | |||||||
|   } |   } | ||||||
|   /** Configure Regular Channel
 |   /** Configure Regular Channel
 | ||||||
|   */ |   */ | ||||||
|   sConfig.Channel = ADC_CHANNEL_TEMPSENSOR; |   sConfig.Channel = ADC_CHANNEL_14; | ||||||
|   sConfig.Rank = ADC_REGULAR_RANK_1; |   sConfig.Rank = ADC_REGULAR_RANK_1; | ||||||
|   sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5; |   sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5; | ||||||
|   sConfig.SingleDiff = ADC_SINGLE_ENDED; |   sConfig.SingleDiff = ADC_SINGLE_ENDED; | ||||||
| @ -70,6 +70,7 @@ void MX_ADC1_Init(void) | |||||||
| void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) | void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
|  |   GPIO_InitTypeDef GPIO_InitStruct = {0}; | ||||||
|   if(adcHandle->Instance==ADC1) |   if(adcHandle->Instance==ADC1) | ||||||
|   { |   { | ||||||
|   /* USER CODE BEGIN ADC1_MspInit 0 */ |   /* USER CODE BEGIN ADC1_MspInit 0 */ | ||||||
| @ -77,6 +78,19 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) | |||||||
|   /* USER CODE END ADC1_MspInit 0 */ |   /* USER CODE END ADC1_MspInit 0 */ | ||||||
|     /* ADC1 clock enable */ |     /* ADC1 clock enable */ | ||||||
|     __HAL_RCC_ADC_CLK_ENABLE(); |     __HAL_RCC_ADC_CLK_ENABLE(); | ||||||
|  | 
 | ||||||
|  |     __HAL_RCC_GPIOC_CLK_ENABLE(); | ||||||
|  |     /**ADC1 GPIO Configuration
 | ||||||
|  |     PC5     ------> ADC1_IN14 | ||||||
|  |     */ | ||||||
|  |     GPIO_InitStruct.Pin = RFID_RF_IN_Pin; | ||||||
|  |     GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; | ||||||
|  |     GPIO_InitStruct.Pull = GPIO_NOPULL; | ||||||
|  |     HAL_GPIO_Init(RFID_RF_IN_GPIO_Port, &GPIO_InitStruct); | ||||||
|  | 
 | ||||||
|  |     /* ADC1 interrupt Init */ | ||||||
|  |     HAL_NVIC_SetPriority(ADC1_IRQn, 5, 0); | ||||||
|  |     HAL_NVIC_EnableIRQ(ADC1_IRQn); | ||||||
|   /* USER CODE BEGIN ADC1_MspInit 1 */ |   /* USER CODE BEGIN ADC1_MspInit 1 */ | ||||||
| 
 | 
 | ||||||
|   /* USER CODE END ADC1_MspInit 1 */ |   /* USER CODE END ADC1_MspInit 1 */ | ||||||
| @ -93,6 +107,14 @@ void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle) | |||||||
|   /* USER CODE END ADC1_MspDeInit 0 */ |   /* USER CODE END ADC1_MspDeInit 0 */ | ||||||
|     /* Peripheral clock disable */ |     /* Peripheral clock disable */ | ||||||
|     __HAL_RCC_ADC_CLK_DISABLE(); |     __HAL_RCC_ADC_CLK_DISABLE(); | ||||||
|  | 
 | ||||||
|  |     /**ADC1 GPIO Configuration
 | ||||||
|  |     PC5     ------> ADC1_IN14 | ||||||
|  |     */ | ||||||
|  |     HAL_GPIO_DeInit(RFID_RF_IN_GPIO_Port, RFID_RF_IN_Pin); | ||||||
|  | 
 | ||||||
|  |     /* ADC1 interrupt Deinit */ | ||||||
|  |     HAL_NVIC_DisableIRQ(ADC1_IRQn); | ||||||
|   /* USER CODE BEGIN ADC1_MspDeInit 1 */ |   /* USER CODE BEGIN ADC1_MspDeInit 1 */ | ||||||
| 
 | 
 | ||||||
|   /* USER CODE END ADC1_MspDeInit 1 */ |   /* USER CODE END ADC1_MspDeInit 1 */ | ||||||
|  | |||||||
| @ -57,6 +57,7 @@ | |||||||
| 
 | 
 | ||||||
| /* External variables --------------------------------------------------------*/ | /* External variables --------------------------------------------------------*/ | ||||||
| extern PCD_HandleTypeDef hpcd_USB_FS; | extern PCD_HandleTypeDef hpcd_USB_FS; | ||||||
|  | extern ADC_HandleTypeDef hadc1; | ||||||
| extern COMP_HandleTypeDef hcomp1; | extern COMP_HandleTypeDef hcomp1; | ||||||
| extern TIM_HandleTypeDef htim1; | extern TIM_HandleTypeDef htim1; | ||||||
| extern TIM_HandleTypeDef htim2; | extern TIM_HandleTypeDef htim2; | ||||||
| @ -193,6 +194,20 @@ void EXTI2_IRQHandler(void) | |||||||
|   /* USER CODE END EXTI2_IRQn 1 */ |   /* USER CODE END EXTI2_IRQn 1 */ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |   * @brief This function handles ADC1 global interrupt. | ||||||
|  |   */ | ||||||
|  | void ADC1_IRQHandler(void) | ||||||
|  | { | ||||||
|  |   /* USER CODE BEGIN ADC1_IRQn 0 */ | ||||||
|  | 
 | ||||||
|  |   /* USER CODE END ADC1_IRQn 0 */ | ||||||
|  |   HAL_ADC_IRQHandler(&hadc1); | ||||||
|  |   /* USER CODE BEGIN ADC1_IRQn 1 */ | ||||||
|  | 
 | ||||||
|  |   /* USER CODE END ADC1_IRQn 1 */ | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|   * @brief This function handles USB low priority interrupt, USB wake-up interrupt through EXTI line 28. |   * @brief This function handles USB low priority interrupt, USB wake-up interrupt through EXTI line 28. | ||||||
|   */ |   */ | ||||||
|  | |||||||
| @ -238,7 +238,7 @@ void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle) | |||||||
|     HAL_GPIO_Init(IR_RX_GPIO_Port, &GPIO_InitStruct); |     HAL_GPIO_Init(IR_RX_GPIO_Port, &GPIO_InitStruct); | ||||||
| 
 | 
 | ||||||
|     /* TIM2 interrupt Init */ |     /* TIM2 interrupt Init */ | ||||||
|     HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0); |     HAL_NVIC_SetPriority(TIM2_IRQn, 5, 0); | ||||||
|     HAL_NVIC_EnableIRQ(TIM2_IRQn); |     HAL_NVIC_EnableIRQ(TIM2_IRQn); | ||||||
|   /* USER CODE BEGIN TIM2_MspInit 1 */ |   /* USER CODE BEGIN TIM2_MspInit 1 */ | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,6 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include "main.h" |  | ||||||
| 
 |  | ||||||
| void delay(float milliseconds); |  | ||||||
| void delay_us(float microseconds); |  | ||||||
| void delay_us_init_DWT(void); |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,6 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include "main.h" |  | ||||||
| 
 |  | ||||||
| void delay_us(float microseconds); |  | ||||||
| 
 |  | ||||||
| void delay(float milliseconds); |  | ||||||
							
								
								
									
										95
									
								
								lib/cyfral/cyfral_emulator.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								lib/cyfral/cyfral_emulator.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,95 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "flipper.h" | ||||||
|  | #include "flipper_v2.h" | ||||||
|  | 
 | ||||||
|  | class CyfralTiming { | ||||||
|  | public: | ||||||
|  |     constexpr static const uint8_t ZERO_HIGH = 50; | ||||||
|  |     constexpr static const uint8_t ZERO_LOW = 70; | ||||||
|  |     constexpr static const uint8_t ONE_HIGH = 100; | ||||||
|  |     constexpr static const uint8_t ONE_LOW = 70; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class CyfralEmulator { | ||||||
|  | private: | ||||||
|  |     void send_nibble(uint8_t nibble); | ||||||
|  |     void send_byte(uint8_t data); | ||||||
|  |     inline void send_bit(bool bit); | ||||||
|  |     const GpioPin* emulate_pin_record; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     CyfralEmulator(const GpioPin* emulate_pin); | ||||||
|  |     ~CyfralEmulator(); | ||||||
|  |     void send(uint8_t* data, uint8_t count = 1, uint8_t repeat = 1); | ||||||
|  |     void start(void); | ||||||
|  |     void stop(void); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 7 = 0 1 1 1
 | ||||||
|  | // B = 1 0 1 1
 | ||||||
|  | // D = 1 1 0 1
 | ||||||
|  | // E = 1 1 1 0
 | ||||||
|  | 
 | ||||||
|  | void CyfralEmulator::send_nibble(uint8_t nibble) { | ||||||
|  |     for(uint8_t i = 0; i < 4; i++) { | ||||||
|  |         bool bit = nibble & (0b1000 >> i); | ||||||
|  |         send_bit(bit); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CyfralEmulator::send_byte(uint8_t data) { | ||||||
|  |     for(uint8_t i = 0; i < 8; i++) { | ||||||
|  |         bool bit = data & (0b10000000 >> i); | ||||||
|  |         send_bit(bit); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CyfralEmulator::send_bit(bool bit) { | ||||||
|  |     if(!bit) { | ||||||
|  |         gpio_write(&ibutton_gpio, false); | ||||||
|  |         delay_us(CyfralTiming::ZERO_LOW); | ||||||
|  |         gpio_write(&ibutton_gpio, true); | ||||||
|  |         delay_us(CyfralTiming::ZERO_HIGH); | ||||||
|  |         gpio_write(&ibutton_gpio, false); | ||||||
|  |         delay_us(CyfralTiming::ZERO_LOW); | ||||||
|  |     } else { | ||||||
|  |         gpio_write(&ibutton_gpio, true); | ||||||
|  |         delay_us(CyfralTiming::ONE_HIGH); | ||||||
|  |         gpio_write(&ibutton_gpio, false); | ||||||
|  |         delay_us(CyfralTiming::ONE_LOW); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | CyfralEmulator::CyfralEmulator(const GpioPin* emulate_pin) { | ||||||
|  |     emulate_pin_record = emulate_pin; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | CyfralEmulator::~CyfralEmulator() { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CyfralEmulator::send(uint8_t* data, uint8_t count, uint8_t repeat) { | ||||||
|  |     osKernelLock(); | ||||||
|  |     __disable_irq(); | ||||||
|  | 
 | ||||||
|  |     for(uint8_t i = 0; i < repeat; i++) { | ||||||
|  |         // start sequence
 | ||||||
|  |         send_nibble(0x01); | ||||||
|  | 
 | ||||||
|  |         // send data
 | ||||||
|  |         for(uint8_t i = 0; i < count; i++) { | ||||||
|  |             send_byte(data[i]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     __enable_irq(); | ||||||
|  |     osKernelUnlock(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CyfralEmulator::start(void) { | ||||||
|  |     gpio_init(emulate_pin_record, GpioModeOutputOpenDrain); | ||||||
|  |     gpio_write(emulate_pin_record, false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CyfralEmulator::stop(void) { | ||||||
|  |     gpio_init(emulate_pin_record, GpioModeAnalog); | ||||||
|  | } | ||||||
							
								
								
									
										273
									
								
								lib/cyfral/cyfral_reader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										273
									
								
								lib/cyfral/cyfral_reader.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,273 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "flipper.h" | ||||||
|  | #include "flipper_v2.h" | ||||||
|  | 
 | ||||||
|  | enum class CyfralReaderError : uint8_t { | ||||||
|  |     NO_ERROR = 0, | ||||||
|  |     UNABLE_TO_DETECT = 1, | ||||||
|  |     RAW_DATA_SIZE_ERROR = 2, | ||||||
|  |     UNKNOWN_NIBBLE_VALUE = 3, | ||||||
|  |     NO_START_NIBBLE = 4, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class CyfralReader { | ||||||
|  | private: | ||||||
|  |     ADC_HandleTypeDef adc_config; | ||||||
|  |     ADC_TypeDef* adc_instance; | ||||||
|  |     uint32_t adc_channel; | ||||||
|  | 
 | ||||||
|  |     void get_line_minmax(uint16_t times, uint32_t* min_level, uint32_t* max_level); | ||||||
|  |     void capture_data(bool* data, uint16_t capture_size, uint32_t line_min, uint32_t line_max); | ||||||
|  |     bool parse_data(bool* raw_data, uint16_t capture_size, uint8_t* data, uint8_t count); | ||||||
|  |     uint32_t search_array_in_array( | ||||||
|  |         const bool* haystack, | ||||||
|  |         const uint32_t haystack_size, | ||||||
|  |         const bool* needle, | ||||||
|  |         const uint32_t needle_size); | ||||||
|  | 
 | ||||||
|  |     // key is 9 nibbles
 | ||||||
|  |     static const uint16_t bits_in_nibble = 4; | ||||||
|  |     static const uint16_t key_length = 9; | ||||||
|  |     static const uint32_t capture_size = key_length * bits_in_nibble * 2; | ||||||
|  |     CyfralReaderError error; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     CyfralReader(ADC_TypeDef* adc, uint32_t Channel); | ||||||
|  |     ~CyfralReader(); | ||||||
|  |     void start(void); | ||||||
|  |     void stop(void); | ||||||
|  |     bool read(uint8_t* data, uint8_t count); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void CyfralReader::get_line_minmax(uint16_t times, uint32_t* min_level, uint32_t* max_level) { | ||||||
|  |     uint32_t in = 0; | ||||||
|  |     uint32_t min = UINT_MAX; | ||||||
|  |     uint32_t max = 0; | ||||||
|  | 
 | ||||||
|  |     for(uint32_t i = 0; i < 256; i++) { | ||||||
|  |         HAL_ADC_Start(&adc_config); | ||||||
|  |         HAL_ADC_PollForConversion(&adc_config, 100); | ||||||
|  |         in = HAL_ADC_GetValue(&adc_config); | ||||||
|  |         if(in < min) min = in; | ||||||
|  |         if(in > max) max = in; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     *min_level = min; | ||||||
|  |     *max_level = max; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CyfralReader::capture_data( | ||||||
|  |     bool* data, | ||||||
|  |     uint16_t capture_size, | ||||||
|  |     uint32_t line_min, | ||||||
|  |     uint32_t line_max) { | ||||||
|  |     uint32_t input_value = 0; | ||||||
|  |     bool last_input_value = 0; | ||||||
|  | 
 | ||||||
|  |     uint32_t diff = line_max - line_min; | ||||||
|  |     uint32_t mid = line_min + diff / 2; | ||||||
|  | 
 | ||||||
|  |     uint32_t low_threshold = mid - (diff / 4); | ||||||
|  |     uint32_t high_threshold = mid - (diff / 4); | ||||||
|  | 
 | ||||||
|  |     uint16_t capture_position = 0; | ||||||
|  |     uint32_t instructions_per_us = (SystemCoreClock / 1000000.0f); | ||||||
|  |     uint32_t time_threshold = 75 * instructions_per_us; | ||||||
|  |     uint32_t capture_max_time = 140 * (capture_size * 2) * instructions_per_us; | ||||||
|  | 
 | ||||||
|  |     uint32_t start = DWT->CYCCNT; | ||||||
|  |     uint32_t end = DWT->CYCCNT; | ||||||
|  | 
 | ||||||
|  |     memset(data, 0, capture_size); | ||||||
|  | 
 | ||||||
|  |     osKernelLock(); | ||||||
|  | 
 | ||||||
|  |     uint32_t capture_start = DWT->CYCCNT; | ||||||
|  |     while((capture_position < capture_size) && | ||||||
|  |           ((DWT->CYCCNT - capture_start) < capture_max_time)) { | ||||||
|  |         // read adc
 | ||||||
|  |         HAL_ADC_Start(&adc_config); | ||||||
|  |         HAL_ADC_PollForConversion(&adc_config, 100); | ||||||
|  |         input_value = HAL_ADC_GetValue(&adc_config); | ||||||
|  | 
 | ||||||
|  |         // low to high transition
 | ||||||
|  |         if((input_value > high_threshold) && last_input_value == 0) { | ||||||
|  |             last_input_value = 1; | ||||||
|  |             start = DWT->CYCCNT; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // high to low transition
 | ||||||
|  |         if((input_value < low_threshold) && last_input_value == 1) { | ||||||
|  |             last_input_value = 0; | ||||||
|  |             end = DWT->CYCCNT; | ||||||
|  | 
 | ||||||
|  |             // check transition time
 | ||||||
|  |             if(end - start < time_threshold) { | ||||||
|  |                 data[capture_position] = 1; | ||||||
|  |                 capture_position++; | ||||||
|  |             } else { | ||||||
|  |                 data[capture_position] = 0; | ||||||
|  |                 capture_position++; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     osKernelUnlock(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint32_t CyfralReader::search_array_in_array( | ||||||
|  |     const bool* haystack, | ||||||
|  |     const uint32_t haystack_size, | ||||||
|  |     const bool* needle, | ||||||
|  |     const uint32_t needle_size) { | ||||||
|  |     uint32_t haystack_index = 0, needle_index = 0; | ||||||
|  | 
 | ||||||
|  |     while(haystack_index < haystack_size && needle_index < needle_size) { | ||||||
|  |         if(haystack[haystack_index] == needle[needle_index]) { | ||||||
|  |             haystack_index++; | ||||||
|  |             needle_index++; | ||||||
|  |             if(needle_index == needle_size) { | ||||||
|  |                 return (haystack_index - needle_size); | ||||||
|  |             }; | ||||||
|  |         } else { | ||||||
|  |             haystack_index = haystack_index - needle_index + 1; | ||||||
|  |             needle_index = 0; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return haystack_index; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool CyfralReader::parse_data(bool* raw_data, uint16_t capture_size, uint8_t* data, uint8_t count) { | ||||||
|  |     const bool start_nibble[bits_in_nibble] = {1, 1, 1, 0}; | ||||||
|  |     uint32_t start_position = | ||||||
|  |         search_array_in_array(raw_data, capture_size, start_nibble, bits_in_nibble); | ||||||
|  |     uint32_t end_position = 0; | ||||||
|  | 
 | ||||||
|  |     memset(data, 0, count); | ||||||
|  | 
 | ||||||
|  |     if(start_position < capture_size) { | ||||||
|  |         start_position = start_position + bits_in_nibble; | ||||||
|  |         end_position = start_position + count * 2 * bits_in_nibble; | ||||||
|  | 
 | ||||||
|  |         if(end_position >= capture_size) { | ||||||
|  |             error = CyfralReaderError::RAW_DATA_SIZE_ERROR; | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         bool first_nibble = true; | ||||||
|  |         uint8_t data_position = 0; | ||||||
|  |         uint8_t nibble_value = 0; | ||||||
|  | 
 | ||||||
|  |         while(data_position < count) { | ||||||
|  |             nibble_value = !raw_data[start_position] << 3 | !raw_data[start_position + 1] << 2 | | ||||||
|  |                            !raw_data[start_position + 2] << 1 | !raw_data[start_position + 3]; | ||||||
|  | 
 | ||||||
|  |             switch(nibble_value) { | ||||||
|  |             case(0x7): | ||||||
|  |             case(0xB): | ||||||
|  |             case(0xD): | ||||||
|  |             case(0xE): | ||||||
|  |                 break; | ||||||
|  |             default: | ||||||
|  |                 error = CyfralReaderError::UNKNOWN_NIBBLE_VALUE; | ||||||
|  |                 return false; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(first_nibble) { | ||||||
|  |                 data[data_position] |= nibble_value << 4; | ||||||
|  |             } else { | ||||||
|  |                 data[data_position] |= nibble_value; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             first_nibble = !first_nibble; | ||||||
|  | 
 | ||||||
|  |             if(first_nibble) { | ||||||
|  |                 data_position++; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             start_position = start_position + bits_in_nibble; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         error = CyfralReaderError::NO_ERROR; | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     error = CyfralReaderError::NO_START_NIBBLE; | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | CyfralReader::CyfralReader(ADC_TypeDef* adc, uint32_t channel) { | ||||||
|  |     adc_instance = adc; | ||||||
|  |     adc_channel = channel; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | CyfralReader::~CyfralReader() { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CyfralReader::start(void) { | ||||||
|  |     ADC_ChannelConfTypeDef sConfig = {0}; | ||||||
|  | 
 | ||||||
|  |     // init ADC
 | ||||||
|  |     adc_config.Instance = adc_instance; | ||||||
|  |     adc_config.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1; | ||||||
|  |     adc_config.Init.Resolution = ADC_RESOLUTION_12B; | ||||||
|  |     adc_config.Init.DataAlign = ADC_DATAALIGN_RIGHT; | ||||||
|  |     adc_config.Init.ScanConvMode = ADC_SCAN_DISABLE; | ||||||
|  |     adc_config.Init.EOCSelection = ADC_EOC_SINGLE_CONV; | ||||||
|  |     adc_config.Init.LowPowerAutoWait = DISABLE; | ||||||
|  |     adc_config.Init.ContinuousConvMode = DISABLE; | ||||||
|  |     adc_config.Init.NbrOfConversion = 1; | ||||||
|  |     adc_config.Init.DiscontinuousConvMode = DISABLE; | ||||||
|  |     adc_config.Init.ExternalTrigConv = ADC_SOFTWARE_START; | ||||||
|  |     adc_config.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; | ||||||
|  |     adc_config.Init.DMAContinuousRequests = DISABLE; | ||||||
|  |     adc_config.Init.Overrun = ADC_OVR_DATA_PRESERVED; | ||||||
|  |     adc_config.Init.OversamplingMode = DISABLE; | ||||||
|  |     if(HAL_ADC_Init(&adc_config) != HAL_OK) { | ||||||
|  |         Error_Handler(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // init channel
 | ||||||
|  |     sConfig.Channel = adc_channel; | ||||||
|  |     sConfig.Rank = ADC_REGULAR_RANK_1; | ||||||
|  |     sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5; | ||||||
|  |     sConfig.SingleDiff = ADC_SINGLE_ENDED; | ||||||
|  |     sConfig.OffsetNumber = ADC_OFFSET_NONE; | ||||||
|  |     sConfig.Offset = 0; | ||||||
|  |     if(HAL_ADC_ConfigChannel(&adc_config, &sConfig) != HAL_OK) { | ||||||
|  |         Error_Handler(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CyfralReader::stop(void) { | ||||||
|  |     HAL_ADC_DeInit(&adc_config); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool CyfralReader::read(uint8_t* data, uint8_t count) { | ||||||
|  |     uint32_t line_level_min, line_level_max; | ||||||
|  |     bool raw_data[capture_size]; | ||||||
|  |     bool result = false; | ||||||
|  |     error = CyfralReaderError::NO_ERROR; | ||||||
|  | 
 | ||||||
|  |     // calibrate
 | ||||||
|  |     get_line_minmax(256, &line_level_min, &line_level_max); | ||||||
|  | 
 | ||||||
|  |     // TODO think about other detection method
 | ||||||
|  |     // key not on line
 | ||||||
|  |     if(line_level_max > 2000) { | ||||||
|  |         error = CyfralReaderError::UNABLE_TO_DETECT; | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // capturing raw data consisting of bits
 | ||||||
|  |     capture_data(raw_data, capture_size, line_level_min, line_level_max); | ||||||
|  | 
 | ||||||
|  |     // parse captured data
 | ||||||
|  |     if(parse_data(raw_data, capture_size, data, count)) { | ||||||
|  |         result = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								lib/lib.mk
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								lib/lib.mk
									
									
									
									
									
								
							| @ -55,3 +55,17 @@ CFLAGS			+= -I$(LIB_DIR)/app-template | |||||||
| # fnv1a hash library
 | # fnv1a hash library
 | ||||||
| CFLAGS			+= -I$(LIB_DIR)/fnv1a-hash | CFLAGS			+= -I$(LIB_DIR)/fnv1a-hash | ||||||
| C_SOURCES		+= $(LIB_DIR)/fnv1a-hash/fnv1a-hash.c | C_SOURCES		+= $(LIB_DIR)/fnv1a-hash/fnv1a-hash.c | ||||||
|  | 
 | ||||||
|  | # build onewire/cyfral library only if
 | ||||||
|  | # we build iButton application
 | ||||||
|  | ifeq ($(BUILD_IBUTTON), 1) | ||||||
|  | # onewire library
 | ||||||
|  | ONEWIRE_DIR		= $(LIB_DIR)/onewire | ||||||
|  | CFLAGS			+= -I$(ONEWIRE_DIR) | ||||||
|  | CPP_SOURCES		+= $(wildcard $(ONEWIRE_DIR)/*.cpp) | ||||||
|  | 
 | ||||||
|  | # cyfral library
 | ||||||
|  | CYFRAL_DIR		= $(LIB_DIR)/cyfral | ||||||
|  | CFLAGS			+= -I$(CYFRAL_DIR) | ||||||
|  | CPP_SOURCES		+= $(wildcard $(CYFRAL_DIR)/*.cpp) | ||||||
|  | endif | ||||||
							
								
								
									
										48
									
								
								lib/onewire/maxim_crc.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								lib/onewire/maxim_crc.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | |||||||
|  | #include "maxim_crc.h" | ||||||
|  | 
 | ||||||
|  | uint8_t maxim_crc8(const uint8_t* data, const uint8_t data_size, const uint8_t crc_init) { | ||||||
|  |     uint8_t crc = crc_init; | ||||||
|  | 
 | ||||||
|  |     for(uint8_t index = 0; index < data_size; ++index) { | ||||||
|  |         uint8_t input_byte = data[index]; | ||||||
|  |         for(uint8_t bit_position = 0; bit_position < 8; ++bit_position) { | ||||||
|  |             const uint8_t mix = (crc ^ input_byte) & static_cast<uint8_t>(0x01); | ||||||
|  |             crc >>= 1; | ||||||
|  |             if(mix != 0) crc ^= 0x8C; | ||||||
|  |             input_byte >>= 1; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return crc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t maxim_crc16(const uint8_t* address, const uint8_t length, const uint16_t init) { | ||||||
|  |     uint16_t crc = init; | ||||||
|  | 
 | ||||||
|  |     static const uint8_t odd_parity[16] = {0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0}; | ||||||
|  | 
 | ||||||
|  |     for(uint8_t i = 0; i < length; ++i) { | ||||||
|  |         uint16_t cdata = address[i]; | ||||||
|  |         cdata = (cdata ^ crc) & static_cast<uint16_t>(0xff); | ||||||
|  |         crc >>= 8; | ||||||
|  | 
 | ||||||
|  |         if((odd_parity[cdata & 0x0F] ^ odd_parity[cdata >> 4]) != 0) crc ^= 0xC001; | ||||||
|  | 
 | ||||||
|  |         cdata <<= 6; | ||||||
|  |         crc ^= cdata; | ||||||
|  |         cdata <<= 1; | ||||||
|  |         crc ^= cdata; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return crc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t maxim_crc16(uint8_t value, uint16_t crc) { | ||||||
|  |     static const uint8_t odd_parity[16] = {0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0}; | ||||||
|  |     value = (value ^ static_cast<uint8_t>(crc)); | ||||||
|  |     crc >>= 8; | ||||||
|  |     if((odd_parity[value & 0x0F] ^ odd_parity[value >> 4]) != 0) crc ^= 0xC001; | ||||||
|  |     uint16_t cdata = (static_cast<uint16_t>(value) << 6); | ||||||
|  |     crc ^= cdata; | ||||||
|  |     crc ^= (static_cast<uint16_t>(cdata) << 1); | ||||||
|  |     return crc; | ||||||
|  | } | ||||||
							
								
								
									
										6
									
								
								lib/onewire/maxim_crc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								lib/onewire/maxim_crc.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | uint8_t maxim_crc8(const uint8_t* data, const uint8_t data_size, const uint8_t crc_init = 0); | ||||||
|  | uint16_t maxim_crc16(const uint8_t* address, const uint8_t length, const uint16_t init = 0); | ||||||
|  | uint16_t maxim_crc16(uint8_t value, uint16_t crc); | ||||||
							
								
								
									
										26
									
								
								lib/onewire/one_wire_device.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								lib/onewire/one_wire_device.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | |||||||
|  | #include "one_wire_device.h" | ||||||
|  | 
 | ||||||
|  | // TODO fix GPL compability
 | ||||||
|  | // currently we use rework of OneWireHub
 | ||||||
|  | 
 | ||||||
|  | OneWireDevice::OneWireDevice( | ||||||
|  |     uint8_t id_1, | ||||||
|  |     uint8_t id_2, | ||||||
|  |     uint8_t id_3, | ||||||
|  |     uint8_t id_4, | ||||||
|  |     uint8_t id_5, | ||||||
|  |     uint8_t id_6, | ||||||
|  |     uint8_t id_7) { | ||||||
|  |     id_storage[0] = id_1; | ||||||
|  |     id_storage[1] = id_2; | ||||||
|  |     id_storage[2] = id_3; | ||||||
|  |     id_storage[3] = id_4; | ||||||
|  |     id_storage[4] = id_5; | ||||||
|  |     id_storage[5] = id_6; | ||||||
|  |     id_storage[6] = id_7; | ||||||
|  |     id_storage[7] = maxim_crc8(id_storage, 7); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void OneWireDevice::send_id(OneWireGpioSlave* owner) const { | ||||||
|  |     owner->send(id_storage, 8); | ||||||
|  | } | ||||||
							
								
								
									
										34
									
								
								lib/onewire/one_wire_device.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								lib/onewire/one_wire_device.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | #include "maxim_crc.h" | ||||||
|  | #include "one_wire_slave_gpio.h" | ||||||
|  | 
 | ||||||
|  | // TODO fix GPL compability
 | ||||||
|  | // currently we use rework of OneWireHub
 | ||||||
|  | 
 | ||||||
|  | class OneWireDevice { | ||||||
|  | public: | ||||||
|  |     OneWireDevice( | ||||||
|  |         uint8_t id_1, | ||||||
|  |         uint8_t id_2, | ||||||
|  |         uint8_t id_3, | ||||||
|  |         uint8_t id_4, | ||||||
|  |         uint8_t id_5, | ||||||
|  |         uint8_t id_6, | ||||||
|  |         uint8_t id_7); | ||||||
|  | 
 | ||||||
|  |     ~OneWireDevice() = default; // TODO: detach if deleted before hub
 | ||||||
|  | 
 | ||||||
|  |     // allow only move constructor
 | ||||||
|  |     OneWireDevice(OneWireDevice&& one_wire_device) = default; | ||||||
|  |     OneWireDevice(const OneWireDevice& one_wire_device) = delete; | ||||||
|  |     OneWireDevice& operator=(OneWireDevice& one_wire_device) = delete; | ||||||
|  |     OneWireDevice& operator=(const OneWireDevice& one_wire_device) = delete; | ||||||
|  |     OneWireDevice& operator=(OneWireDevice&& one_wire_device) = delete; | ||||||
|  | 
 | ||||||
|  |     uint8_t id_storage[8]; | ||||||
|  | 
 | ||||||
|  |     void send_id(OneWireGpioSlave* owner) const; | ||||||
|  | 
 | ||||||
|  |     virtual void do_work(OneWireGpioSlave* owner) = 0; | ||||||
|  | }; | ||||||
							
								
								
									
										27
									
								
								lib/onewire/one_wire_device_ds_1990.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								lib/onewire/one_wire_device_ds_1990.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | |||||||
|  | #include "one_wire_device_ds_1990.h" | ||||||
|  | 
 | ||||||
|  | // TODO fix GPL compability
 | ||||||
|  | // currently we use rework of OneWireHub
 | ||||||
|  | 
 | ||||||
|  | DS1990::DS1990( | ||||||
|  |     uint8_t ID1, | ||||||
|  |     uint8_t ID2, | ||||||
|  |     uint8_t ID3, | ||||||
|  |     uint8_t ID4, | ||||||
|  |     uint8_t ID5, | ||||||
|  |     uint8_t ID6, | ||||||
|  |     uint8_t ID7) | ||||||
|  |     : OneWireDevice(ID1, ID2, ID3, ID4, ID5, ID6, ID7) { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DS1990::do_work(OneWireGpioSlave* owner) { | ||||||
|  |     uint8_t cmd; | ||||||
|  | 
 | ||||||
|  |     if(owner->receive(&cmd)) return; | ||||||
|  | 
 | ||||||
|  |     switch(cmd) { | ||||||
|  |     default: | ||||||
|  |         return; | ||||||
|  |         //owner->raiseSlaveError(cmd);
 | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								lib/onewire/one_wire_device_ds_1990.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								lib/onewire/one_wire_device_ds_1990.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "one_wire_device.h" | ||||||
|  | 
 | ||||||
|  | // TODO fix GPL compability
 | ||||||
|  | // currently we use rework of OneWireHub
 | ||||||
|  | 
 | ||||||
|  | class DS1990 : public OneWireDevice { | ||||||
|  | public: | ||||||
|  |     static constexpr uint8_t family_code{0x01}; | ||||||
|  | 
 | ||||||
|  |     DS1990( | ||||||
|  |         uint8_t ID1, | ||||||
|  |         uint8_t ID2, | ||||||
|  |         uint8_t ID3, | ||||||
|  |         uint8_t ID4, | ||||||
|  |         uint8_t ID5, | ||||||
|  |         uint8_t ID6, | ||||||
|  |         uint8_t ID7); | ||||||
|  | 
 | ||||||
|  |     void do_work(OneWireGpioSlave* owner) final; | ||||||
|  | }; | ||||||
							
								
								
									
										511
									
								
								lib/onewire/one_wire_slave_gpio.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										511
									
								
								lib/onewire/one_wire_slave_gpio.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,511 @@ | |||||||
|  | #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() { | ||||||
|  |     error = OneWireGpioSlaveError::NO_ERROR; | ||||||
|  | 
 | ||||||
|  |     while(1) { | ||||||
|  |         if(devices_count == 0) return false; | ||||||
|  | 
 | ||||||
|  |         if(!check_reset()) { | ||||||
|  |             return false; | ||||||
|  |         } else { | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // OK, we receive reset
 | ||||||
|  |         osKernelLock(); | ||||||
|  | 
 | ||||||
|  |         if(!show_presence()) { | ||||||
|  |             return false; | ||||||
|  |         } else { | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // and we succefully show our presence on bus
 | ||||||
|  |         __disable_irq(); | ||||||
|  | 
 | ||||||
|  |         if(!receive_and_process_cmd()) { | ||||||
|  |             __enable_irq(); | ||||||
|  |             osKernelUnlock(); | ||||||
|  |             return false; | ||||||
|  |         } else { | ||||||
|  |             __enable_irq(); | ||||||
|  |             osKernelUnlock(); | ||||||
|  |             return (error == OneWireGpioSlaveError::NO_ERROR); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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
 | ||||||
|  |     const 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; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // 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); | ||||||
|  | } | ||||||
							
								
								
									
										93
									
								
								lib/onewire/one_wire_slave_gpio.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								lib/onewire/one_wire_slave_gpio.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,93 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "flipper.h" | ||||||
|  | #include "flipper_v2.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); | ||||||
|  | }; | ||||||
							
								
								
									
										17
									
								
								lib/onewire/one_wire_timings.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								lib/onewire/one_wire_timings.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | |||||||
|  | #include "one_wire_timings.h" | ||||||
|  | 
 | ||||||
|  | // fix pre C++17 "undefined reference" errors
 | ||||||
|  | constexpr const OneWiteTimeType OneWireEmulateTiming::RESET_TIMEOUT; | ||||||
|  | constexpr const OneWiteTimeType OneWireEmulateTiming::RESET_MIN[2]; | ||||||
|  | constexpr const OneWiteTimeType OneWireEmulateTiming::RESET_MAX[2]; | ||||||
|  | 
 | ||||||
|  | constexpr const OneWiteTimeType OneWireEmulateTiming::PRESENCE_TIMEOUT; | ||||||
|  | constexpr const OneWiteTimeType OneWireEmulateTiming::PRESENCE_MIN[2]; | ||||||
|  | constexpr const OneWiteTimeType OneWireEmulateTiming::PRESENCE_MAX[2]; | ||||||
|  | 
 | ||||||
|  | constexpr const OneWiteTimeType OneWireEmulateTiming::MSG_HIGH_TIMEOUT; | ||||||
|  | constexpr const OneWiteTimeType OneWireEmulateTiming::SLOT_MAX[2]; | ||||||
|  | 
 | ||||||
|  | constexpr const OneWiteTimeType OneWireEmulateTiming::READ_MIN[2]; | ||||||
|  | constexpr const OneWiteTimeType OneWireEmulateTiming::READ_MAX[2]; | ||||||
|  | constexpr const OneWiteTimeType OneWireEmulateTiming::WRITE_ZERO[2]; | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 DrZlo13
						DrZlo13