From d6c2dbc12b47751ba959493b207acc9108c58279 Mon Sep 17 00:00:00 2001 From: mmontee <34085121+mmontee@users.noreply.github.com> Date: Fri, 12 Jul 2024 11:12:30 -0700 Subject: [PATCH 1/8] 1 wire driver addition Driver and project example for DS18B20 --- scm_v3c/1_wire.c | 501 +++++++++++++++++ scm_v3c/1_wire.h | 80 +++ .../applications/1_wire/1_wire_project.uvoptx | 440 +++++++++++++++ .../1_wire/1_wire_project.uvprojx | 517 ++++++++++++++++++ scm_v3c/applications/1_wire/1_wire_test.c | 230 ++++++++ scm_v3c/gpio.c | 18 + scm_v3c/gpio.h | 19 + 7 files changed, 1805 insertions(+) create mode 100644 scm_v3c/1_wire.c create mode 100644 scm_v3c/1_wire.h create mode 100644 scm_v3c/applications/1_wire/1_wire_project.uvoptx create mode 100644 scm_v3c/applications/1_wire/1_wire_project.uvprojx create mode 100644 scm_v3c/applications/1_wire/1_wire_test.c diff --git a/scm_v3c/1_wire.c b/scm_v3c/1_wire.c new file mode 100644 index 00000000..755148b8 --- /dev/null +++ b/scm_v3c/1_wire.c @@ -0,0 +1,501 @@ +#include +#include +#include +#include + +#include "gpio.h" +#include "rftimer.h" +#include "scm3c_hw_interface.h" +#include "memory_map.h" +#include "1_wire.h" + +// 1-wire search rom, code taken from; =====================================* +// https://www.analog.com/en/resources/app-notes/1wire-search-algorithm.html +unsigned char ROM_NO[8]; // Rom bytes are put here on search +int LastDiscrepancy; +int LastFamilyDiscrepancy; +int LastDeviceFlag; +unsigned char crc8; +//==========================================================================* +// Returns all roms in linked list +bus_roms_root_prt_t OWSearch_bus(void) +{ + bus_roms_root_prt_t my_roms_root = malloc(sizeof(rom_list_root_t)); + my_roms_root->next = malloc(sizeof(rom_list_t)); + int cnt = 0; + int rslt = OWFirst(); + while(rslt) + { + cnt++; + OWStore_rom(ROM_NO, my_roms_root->next); + rslt = OWNext(); + } + my_roms_root->item_count = cnt; + if(cnt == 0) + { + free(my_roms_root); + my_roms_root = NULL; + } + return my_roms_root; +} + +// Write a given rom to the bus. +void OWWrite_rom(uint64_t device_rom) +{ + int count = 0; + while(count < 8) + { + uint8_t byte = 0x0; + byte |= device_rom; + OWWriteByte(byte); + device_rom = (device_rom >> 8); + count++; + } +} + +// Stores the IDs returned from OWsearch into a linked list element. +void OWStore_rom(unsigned char* rom_bytes, bus_roms_list_prt_t base) +{ + uint64_t new_rom = 0; + for(int i = 7; i >= 0; i--) + { + new_rom |= rom_bytes[i]; + if(i > 0) + { + new_rom = (new_rom << 8); + } + } + if(base->next == NULL) + { + base->rom = new_rom; + base->next = malloc(sizeof(rom_list_t)); + } + else + { + while(base->next != NULL) + { + base = base->next; + } + base->rom = new_rom; + base->next = malloc(sizeof(rom_list_t)); + } +} + +// Moves roms from linked list to array. +void OWGet_rom_array(uint64_t* array, bus_roms_root_prt_t root) +{ + int cnt = root->item_count; + bus_roms_list_prt_t rom_n = root->next; + free(root); + for(int i = 0; i < cnt; i++) + { + bus_roms_list_prt_t prev_rom = rom_n; + array[i] = rom_n->rom; + rom_n = rom_n->next; + free(prev_rom); + } +} + +// NOP for short delays. Exact timing needs work. +// For now it is working +void OWDelay_us(int us) +{ + for(int i = us; i > 0; i--) + { + __asm("NOP"); + } +} + +// Reads single byte, use in OWRead_bytes +uint8_t OWRead_byte(void) +{ + int count = 8; + uint8_t data = 0x00; + while(count > 0) + { + data = (data >> 1); + int new_bit = OWReadBit(); + if(new_bit & 0x01)// if new_bit is a 1 + { + new_bit = (new_bit << (7)); // set MSb + data |= new_bit; + } + count--; + } + return data; +} + +// Read sizeof(buffer) bytes into buffer array. +void OWRead_bytes(uint8_t* buffer, int size) +{ + int n = size; + int count = 0; + while(count < n) + { + uint8_t new_byte = OWRead_byte(); + buffer[count] = new_byte; + count++; + } +} + + +// CRC bytes, read elements 0 - size through crc generator. +// crc8 value should match the crc produced my device if only the data is +// used to generate the crc value. +// Alternitivly, pass the data and the give crc, the function should +// return 0. +int OWCRC_bytes(uint8_t* byte_array, int size) +{ + crc8 = 0; + for(int i = 0; i < size - 1; i++) + { + docrc8(byte_array[i]); + } + return crc8; +} + +/* +isolating a device by; get presence responce, send MATCHROM command, +send ROM +*/ +int OWIsolate_device(uint64_t device_rom) +{ + int ret = 0; + ret = OWReset(); + if(ret != 0) + { + OWWriteByte(MATCHROM); + OWWrite_rom(device_rom); + } + return ret; +} + +// Platform specific function used in 1wire-search-algorithm================= +// Write a bit to the bus +void OWWriteBit(uint8_t bit) +{ + switch(bit) + { + case 0x00: + BUS_LOW(); + OWDelay_us(DELAY_C); + BUS_RELEASE(); + OWDelay_us(DELAY_D); + break; + default: + BUS_LOW(); + OWDelay_us(DELAY_A); + BUS_RELEASE(); + OWDelay_us(DELAY_B); + break; + } +} + + +// Reads a byte from the bus. +uint8_t OWReadBit(void) +{ + BUS_LOW(); + OWDelay_us(DELAY_A); + BUS_RELEASE(); + OWDelay_us(DELAY_E); + uint8_t bit = BUS_READ(); + OWDelay_us(DELAY_F); + return bit; +} + +// Return is 1 for presence is detected. +int OWReset(void) +{ + BUS_LOW(); + OWDelay_us(DELAY_H); + BUS_RELEASE(); + OWDelay_us(DELAY_I); + int state = BUS_READ(); + OWDelay_us(DELAY_J); + return !state; +} + +// Sends a byte +void OWWriteByte(uint8_t byte) +{ + int count = 8; + while(count > 0) + { + uint8_t temp_byte = byte & 0x01; + if(temp_byte == 1) + { + OWWriteBit(0x01); + } + else + { + OWWriteBit(0x00); + } + byte = (byte >> 1); + count--; + } +} + +//========================================================================== +// 1wire-search-algorithm, code taken from; +// https://www.analog.com/en/resources/app-notes/1wire-search-algorithm.html +static unsigned char dscrc_table[] = { + 0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65, + 157,195, 33,127,252,162, 64, 30, 95, 1,227,189, 62, 96,130,220, + 35,125,159,193, 66, 28,254,160,225,191, 93, 3,128,222, 60, 98, + 190,224, 2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255, + 70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89, 7, + 219,133,103, 57,186,228, 6, 88, 25, 71,165,251,120, 38,196,154, + 101, 59,217,135, 4, 90,184,230,167,249, 27, 69,198,152,122, 36, + 248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91, 5,231,185, + 140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205, + 17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80, + 175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238, + 50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115, + 202,148,118, 40,171,245, 23, 73, 8, 86,180,234,105, 55,213,139, + 87, 9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22, + 233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168, + 116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53}; +//-------------------------------------------------------------------------- +// Calculate the CRC8 of the byte value provided with the current +// global 'crc8' value. +// Returns current global crc8 value +// +unsigned char docrc8(unsigned char value) +{ + // See Application Note 27 + + // TEST BUILD + crc8 = dscrc_table[crc8 ^ value]; + return crc8; +} + +// Find the 'first' devices on the 1-Wire bus +// Return TRUE : device found, ROM number in ROM_NO buffer +// FALSE : no device present +// +int OWFirst(void) +{ + // reset the search state + LastDiscrepancy = 0; + LastDeviceFlag = FALSE; + LastFamilyDiscrepancy = 0; + return OWSearch(); +} +//-------------------------------------------------------------------------- +// Find the 'next' devices on the 1-Wire bus +// Return TRUE : device found, ROM number in ROM_NO buffer +// FALSE : device not found, end of search +// +int OWNext(void) +{ + // leave the search state alone + return OWSearch(); +} +//-------------------------------------------------------------------------- +// Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing +// search state. +// Return TRUE : device found, ROM number in ROM_NO buffer +// FALSE : device not found, end of search +// +int OWSearch(void) +{ + int id_bit_number; + int last_zero, rom_byte_number, search_result; + int id_bit, cmp_id_bit; + unsigned char rom_byte_mask, search_direction; + // initialize for search + id_bit_number = 1; + last_zero = 0; + rom_byte_number = 0; + rom_byte_mask = 1; + search_result = 0; + crc8 = 0; + // if the last call was not the last one + if(!LastDeviceFlag) + { + // 1-Wire reset + if(!OWReset()) + { + // reset the search + LastDiscrepancy = 0; + LastDeviceFlag = FALSE; + LastFamilyDiscrepancy = 0; + return FALSE; + } + // issue the search command + OWWriteByte(SEARCHROM); + // loop to do the search + do + { + // read a bit and its complement + id_bit = OWReadBit(); + cmp_id_bit = OWReadBit(); + // check for no devices on 1-wire + if((id_bit == 1) && (cmp_id_bit == 1)) + { + break; + } + else + { + // all devices coupled have 0 or 1 + if(id_bit != cmp_id_bit) + { + search_direction = id_bit; // bit write value for search + } + else + { + // if this discrepancy if before the Last Discrepancy + // on a previous next then pick the same as last time + if (id_bit_number < LastDiscrepancy) + { + search_direction = ((ROM_NO[rom_byte_number] & rom_byte_mask) > 0);\ + } + else + { + // if equal to last pick 1, if not then pick 0 + search_direction = (id_bit_number == LastDiscrepancy); + // if 0 was picked then record its position in LastZero + } + + if(search_direction == 0) + { + last_zero = id_bit_number; + // check for Last discrepancy in family + if(last_zero < 9) + { + LastFamilyDiscrepancy = last_zero; + } + } + } + // set or clear the bit in the ROM byte rom_byte_number + // with mask rom_byte_mask + if(search_direction == 1) + { + ROM_NO[rom_byte_number] |= rom_byte_mask; + } + else + { + ROM_NO[rom_byte_number] &= ~rom_byte_mask; + } + // serial number search direction write bit + OWWriteBit(search_direction); + // increment the byte counter id_bit_number + // and shift the mask rom_byte_mask + id_bit_number++; + rom_byte_mask <<= 1; + // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask + if(rom_byte_mask == 0) + { + docrc8(ROM_NO[rom_byte_number]); // accumulate the CRC + rom_byte_number++; + rom_byte_mask = 1; + } + } + } + while(rom_byte_number < 8); // loop until through all ROM bytes 0-7 + // if the search was successful then + { + if(!((id_bit_number < 65) || (crc8 != 0))) + { + // search successful so set LastDiscrepancy,LastDeviceFlag,search_result + LastDiscrepancy = last_zero; + // check for last device + if(LastDiscrepancy == 0) + { + LastDeviceFlag = TRUE; + } + search_result = TRUE; + } + } + } + + // if no device found then reset counters so next 'search' will be like a first + if(!search_result || !ROM_NO[0]) + { + LastDiscrepancy = 0; + LastDeviceFlag = FALSE; + LastFamilyDiscrepancy = 0; + search_result = FALSE; + } + return search_result; +} +//-------------------------------------------------------------------------- +// Verify the device with the ROM number in ROM_NO buffer is present. +// Return TRUE : device verified present +// FALSE : device not present +// +int OWVerify(void) +{ + unsigned char rom_backup[8]; + int i,rslt,ld_backup,ldf_backup,lfd_backup; + // keep a backup copy of the current state + for (i = 0; i < 8; i++) + { + rom_backup[i] = ROM_NO[i]; + } + ld_backup = LastDiscrepancy; + ldf_backup = LastDeviceFlag; + lfd_backup = LastFamilyDiscrepancy; + // set search to find the same device + LastDiscrepancy = 64; + LastDeviceFlag = FALSE; + if (OWSearch()) + { + // check if same device found + rslt = TRUE; + for (i = 0; i < 8; i++) + { + if (rom_backup[i] != ROM_NO[i]) + { + rslt = FALSE; + break; + } + } + } + else + + rslt = FALSE; + // restore the search state + for (i = 0; i < 8; i++) + { + ROM_NO[i] = rom_backup[i]; + } + LastDiscrepancy = ld_backup; + LastDeviceFlag = ldf_backup; + LastFamilyDiscrepancy = lfd_backup; + // return the result of the verify + return rslt; +} +//-------------------------------------------------------------------------- +// Setup the search to find the device type 'family_code' on the next call +// to OWNext() if it is present. +// +void OWTargetSetup(unsigned char family_code) +{ + int i; + // set the search state to find SearchFamily type devices + ROM_NO[0] = family_code; + for (i = 1; i < 8; i++) + { + ROM_NO[i] = 0; + } + LastDiscrepancy = 64; + LastFamilyDiscrepancy = 0; + LastDeviceFlag = FALSE; +} +//-------------------------------------------------------------------------- +// Setup the search to skip the current device type on the next call +// to OWNext(). +// +void OWFamilySkipSetup(void) +{ + // set the Last discrepancy to last family discrepancy + LastDiscrepancy = LastFamilyDiscrepancy; + LastFamilyDiscrepancy = 0; + // check for end of list + if (LastDiscrepancy == 0) + { + LastDeviceFlag = TRUE; + } +} diff --git a/scm_v3c/1_wire.h b/scm_v3c/1_wire.h new file mode 100644 index 00000000..6d3eb8aa --- /dev/null +++ b/scm_v3c/1_wire.h @@ -0,0 +1,80 @@ +#ifndef __1_WIRE_H +#define __1_WIRE_H + +#include +#include +#include +#include +#include + +//=========================== defines ========================================= + +#define READROM 0x33 +#define SKIPROM 0xCC +#define MATCHROM 0x55 +#define SEARCHROM 0xF0 + +#define DELAY_A 6 +#define DELAY_B 64 +#define DELAY_C 60 +#define DELAY_D 10 +#define DELAY_E 9 +#define DELAY_F 55 +#define DELAY_G 0 +#define DELAY_H 480 +#define DELAY_I 70 +#define DELAY_J 410 + +#define TX_PIN 0 +#define RX_PIN 1 + +// Function Macros +#define BUS_LOW() GPIO_REG__OUTPUT |= (1 << TX_PIN); // Pulls bus LOW +#define BUS_RELEASE() GPIO_REG__OUTPUT &= ~(1 << TX_PIN); // Release bus to Pull-up +#define BUS_READ() (GPIO_REG__INPUT &= (1 << RX_PIN)) ? 1 : 0; +//=================================== +// 1-wire search rom, code taken from; +// https://www.analog.com/en/resources/app-notes/1wire-search-algorithm.html +#define FALSE 0 +#define TRUE 1 + +//=========================== variables ======================================= +// ROMs found on the bus are stored in a linked list structure +typedef struct rom_list_root{ + uint32_t item_count; + struct rom_list* next; +} rom_list_root_t, *bus_roms_root_prt_t; + +typedef struct rom_list{ + uint64_t rom; + struct rom_list* next; +} rom_list_t, *bus_roms_list_prt_t; + +//=========================== prototypes ====================================== +// User +uint8_t OWRead_byte(void);// Reads a byte from bus +void OWRead_bytes(uint8_t* buffer, int size);// Reads n bytes from bus +int OWIsolate_device(uint64_t device_rom); // Isolate a device +void OWWrite_rom(uint64_t device_rom); // Write rom to bus +bus_roms_root_prt_t OWSearch_bus(void);// Read all roms on bus, returns base to linked list +void OWGet_rom_array(uint64_t* array, bus_roms_root_prt_t root);// Create array of ROMs from linked list +int OWCRC_bytes(uint8_t* byte_array, int size); + +// Driver +void OWDelay_us(int us); +void OWStore_rom(unsigned char* rom_bytes, bus_roms_list_prt_t base); +void OWWriteBit(uint8_t bit); +uint8_t OWReadBit(void); +int OWReset(void); +void OWWriteByte(uint8_t byte); +//=================================== +// 1-wire search rom, code taken from; +// https://www.analog.com/en/resources/app-notes/1wire-search-algorithm.html +int OWFirst(void); +int OWNext(void); +int OWVerify(void); +void OWTargetSetup(unsigned char family_code); +void OWFamilySkipSetup(void); +int OWSearch(void); +unsigned char docrc8(unsigned char value); +#endif diff --git a/scm_v3c/applications/1_wire/1_wire_project.uvoptx b/scm_v3c/applications/1_wire/1_wire_project.uvoptx new file mode 100644 index 00000000..eb12b389 --- /dev/null +++ b/scm_v3c/applications/1_wire/1_wire_project.uvoptx @@ -0,0 +1,440 @@ + + + + 1.0 + +
### uVision Project, (C) Keil Software
+ + + *.c + *.s*; *.src; *.a* + *.obj; *.o + *.lib + *.txt; *.h; *.inc; *.md + *.plm + *.cpp; *.cc; *.cxx + 0 + + + + 0 + 0 + + + + oneWire + 0x4 + ARM-ADS + + 12000000 + + 1 + 1 + 0 + 1 + 0 + + + 1 + 65535 + 0 + 0 + 0 + + + 79 + 66 + 8 + .\Listings\ + + + 1 + 1 + 1 + 0 + 1 + 1 + 0 + 1 + 0 + 0 + 0 + 0 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + + + 1 + 0 + 1 + + 7 + + 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 1 + 1 + 1 + 1 + 0 + 0 + 1 + 0 + 0 + 0 + + + + + + + + + + + BIN\UL2CM3.DLL + + + + 0 + UL2CM3 + UL2CM3(-S0 -C0 -P0 -FD20000000 -FC1000) + + + + + 0 + + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + + + 0 + 0 + 0 + + + + + + + + + + + + + app + 1 + 0 + 0 + 0 + + 1 + 1 + 1 + 0 + 0 + 0 + .\1_wire_test.c + 1_wire_test.c + 0 + 0 + + + + + drv + 1 + 0 + 0 + 0 + + 2 + 2 + 2 + 0 + 0 + 0 + ..\..\cm0dsasm.s + cm0dsasm.s + 0 + 0 + + + 2 + 3 + 5 + 0 + 0 + 0 + ..\..\memory_map.h + memory_map.h + 0 + 0 + + + 2 + 4 + 1 + 0 + 0 + 0 + ..\..\retarget.c + retarget.c + 0 + 0 + + + 2 + 5 + 5 + 0 + 0 + 0 + ..\..\optical.h + optical.h + 0 + 0 + + + 2 + 6 + 1 + 0 + 0 + 0 + ..\..\optical.c + optical.c + 0 + 0 + + + 2 + 7 + 1 + 0 + 0 + 0 + ..\..\adc.c + adc.c + 0 + 0 + + + 2 + 8 + 5 + 0 + 0 + 0 + ..\..\adc.h + adc.h + 0 + 0 + + + 2 + 9 + 1 + 0 + 0 + 0 + ..\..\uart.c + uart.c + 0 + 0 + + + 2 + 10 + 5 + 0 + 0 + 0 + ..\..\uart.h + uart.h + 0 + 0 + + + 2 + 11 + 1 + 0 + 0 + 0 + ..\..\gpio.c + gpio.c + 0 + 0 + + + 2 + 12 + 5 + 0 + 0 + 0 + ..\..\gpio.h + gpio.h + 0 + 0 + + + 2 + 13 + 1 + 0 + 0 + 0 + ..\..\rftimer.c + rftimer.c + 0 + 0 + + + 2 + 14 + 5 + 0 + 0 + 0 + ..\..\rftimer.h + rftimer.h + 0 + 0 + + + 2 + 15 + 1 + 0 + 0 + 0 + ..\..\scm3c_hw_interface.c + scm3c_hw_interface.c + 0 + 0 + + + 2 + 16 + 5 + 0 + 0 + 0 + ..\..\scm3c_hw_interface.h + scm3c_hw_interface.h + 0 + 0 + + + 2 + 17 + 1 + 0 + 0 + 0 + ..\..\radio.c + radio.c + 0 + 0 + + + 2 + 18 + 5 + 0 + 0 + 0 + ..\..\radio.h + radio.h + 0 + 0 + + + 2 + 19 + 5 + 0 + 0 + 0 + ..\..\scum_defs.h + scum_defs.h + 0 + 0 + + + 2 + 20 + 1 + 0 + 0 + 0 + ..\..\1_wire.c + 1_wire.c + 0 + 0 + + + 2 + 21 + 5 + 0 + 0 + 0 + ..\..\1_wire.h + 1_wire.h + 0 + 0 + + + +
diff --git a/scm_v3c/applications/1_wire/1_wire_project.uvprojx b/scm_v3c/applications/1_wire/1_wire_project.uvprojx new file mode 100644 index 00000000..e20d4f8c --- /dev/null +++ b/scm_v3c/applications/1_wire/1_wire_project.uvprojx @@ -0,0 +1,517 @@ + + + + 2.1 + +
### uVision Project, (C) Keil Software
+ + + + oneWire + 0x4 + ARM-ADS + 5060960::V5.06 update 7 (build 960)::.\ARMCC + 0 + + + ARMCM0 + ARM + ARM.CMSIS.5.8.0 + http://www.keil.com/pack/ + IRAM(0x20000000,0x00020000) IROM(0x00000000,0x00040000) CPUTYPE("Cortex-M0") CLOCK(12000000) ESEL ELITTLE + + + UL2CM3(-S0 -C0 -P0 -FD20000000 -FC1000) + 0 + $$Device:ARMCM0$Device\ARM\ARMCM0\Include\ARMCM0.h + + + + + + + + + + + 0 + 0 + + + + + + + 0 + 0 + 0 + 0 + 1 + + .\Objects\ + 1_wire + 1 + 0 + 1 + 1 + 1 + .\Listings\ + 1 + 0 + 0 + + 0 + 0 + + + 0 + 0 + 0 + 0 + + + 0 + 0 + + + 0 + 0 + 0 + 0 + + + 1 + 1 + fromelf --bin .\Objects\1_Wire.axf -o .\Objects\1_Wire.bin + fromelf -cvf .\Objects\1_Wire.axf -o .\Objects\disasm.txt + 0 + 0 + 0 + 0 + + 0 + + + + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 3 + + + 1 + + + SARMCM3.DLL + + DARMCM1.DLL + -pCM0 + SARMCM3.DLL + + TARMCM1.DLL + -pCM0 + + + + 1 + 0 + 0 + 0 + 16 + + + + + 1 + 0 + 0 + 1 + 1 + 4096 + + 1 + BIN\UL2CM3.DLL + "" () + + + + + 0 + + + + 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 1 + 1 + 0 + 1 + 1 + 0 + 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + "Cortex-M0" + + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 8 + 0 + 1 + 0 + 0 + 3 + 3 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 1 + 0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x20000000 + 0x20000 + + + 1 + 0x0 + 0x40000 + + + 0 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x0 + 0x10000 + + + 1 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x20000000 + 0x10000 + + + 0 + 0x0 + 0x0 + + + + + + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 2 + 0 + 0 + 1 + 0 + 0 + 3 + 3 + 1 + 1 + 0 + 0 + 0 + + + + + ../../ + + + + 1 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 1 + + + + + + + + + 1 + 0 + 0 + 0 + 1 + 0 + 0x00000000 + 0x20000000 + + + + + + + + + + + + + app + + + 1_wire_test.c + 1 + .\1_wire_test.c + + + + + drv + + + cm0dsasm.s + 2 + ..\..\cm0dsasm.s + + + memory_map.h + 5 + ..\..\memory_map.h + + + retarget.c + 1 + ..\..\retarget.c + + + optical.h + 5 + ..\..\optical.h + + + optical.c + 1 + ..\..\optical.c + + + adc.c + 1 + ..\..\adc.c + + + adc.h + 5 + ..\..\adc.h + + + uart.c + 1 + ..\..\uart.c + + + uart.h + 5 + ..\..\uart.h + + + gpio.c + 1 + ..\..\gpio.c + + + gpio.h + 5 + ..\..\gpio.h + + + rftimer.c + 1 + ..\..\rftimer.c + + + rftimer.h + 5 + ..\..\rftimer.h + + + scm3c_hw_interface.c + 1 + ..\..\scm3c_hw_interface.c + + + scm3c_hw_interface.h + 5 + ..\..\scm3c_hw_interface.h + + + radio.c + 1 + ..\..\radio.c + + + radio.h + 5 + ..\..\radio.h + + + scum_defs.h + 5 + ..\..\scum_defs.h + + + 1_wire.c + 1 + ..\..\1_wire.c + + + 1_wire.h + 5 + ..\..\1_wire.h + + + + + + + + + + + + + + + + + oneWire + 1 + + + + +
diff --git a/scm_v3c/applications/1_wire/1_wire_test.c b/scm_v3c/applications/1_wire/1_wire_test.c new file mode 100644 index 00000000..f41f12c6 --- /dev/null +++ b/scm_v3c/applications/1_wire/1_wire_test.c @@ -0,0 +1,230 @@ +#include +#include +#include +#include + +#include "memory_map.h" +#include "optical.h" +#include "scm3c_hw_interface.h" +#include "gpio.h" +#include "rftimer.h" +#include "1_wire.h" + +//=========================== defines ========================================= +// SCuM +#define CRC_VALUE (*((unsigned int*)0x0000FFFC)) +#define CODE_LENGTH (*((unsigned int*)0x0000FFF8)) + +// DS18B20 +#define DS18B20 +#define READ_SCRATCH 0xBE +#define WRITE_SCRATCH 0x4E +#define COPY_SCRATCH 0x48 +#define RECALL_E2 0xB8 +#define READ_POWER_SUPPLY 0xB4 +#define CONVERT_T 0x44 +#define T_CONV_9 94 +#define T_CONV_10 188 +#define T_CONV_11 375 +#define T_CONV_12 750 +//=========================== variables ======================================= +// SCuM +typedef struct { + uint8_t count; +} app_vars_t; + +app_vars_t app_vars; + +// DS18B20 - valid values are only {9,10,11,12} +int resolution = 9; +//=========================== prototypes ====================================== +// 1-Wire +void OW_gpio_config(void); + +// DS18B20 +void init_DS18B20(uint64_t device_rom, uint8_t T_h, uint8_t T_l, uint8_t config); +uint16_t get_temp(uint64_t device_rom); +void silence_callback(void); +//=========================== main ============================================ + +int main(void) { + // ===================init the mote============================================ + memset(&app_vars, 0, sizeof(app_vars_t)); + printf("Initializing...\r\n"); + initialize_mote(); + crc_check(); + perform_calibration(); + printf("Initialization Complete.\r\n"); + rftimer_set_callback_by_id(silence_callback, 1); +// Start -- 1-wire init.=========================================================== + /* + GPIO 0, 1 are configured. "OW_gpio_config();" + Declare Tree root. + Declare array with root member "item_count" elements. + Pass both the root and the array to tranfer the lists contents to the array. + */ + OW_gpio_config(); // config for GPIO_0 = input GPIO_1 = input + bus_roms_root_prt_t rom_list_root; //Declaring Tree root. + if((rom_list_root = OWSearch_bus()) == NULL)// Try to get the ROMs of all devices + { + printf("No ROMs Found.\r\n"); + } + else //OWSearch_bus() found >= 1 ROM + { + uint64_t ROMS[rom_list_root->item_count]; // Declare array with root member "item_count" elements. + OWGet_rom_array(ROMS, rom_list_root); // Store ROMs in array, frees linked list + // END -- 1-wire init.============================================================= + for(int i = 0; i < sizeof(ROMS) / sizeof(uint64_t); i++)// Print all ROMs + { + printf("0x%llX\r\n", ROMS[i]); + } + + /* + After 1-wire init. success all roms discovered on bus are stored in ROMS[]. + Individual devices can be isolated using OWIsolate_device(ROMS[n]); + */ + #ifdef DS18B20 + init_DS18B20(ROMS[1], 100, 10, 12); + int count = 1; + + while(1) + { + printf("Sample #%d\r\n", count); + OWIsolate_device(ROMS[0]); + printf("Sensor 1 temp = %dC\r\n", get_temp(ROMS[0])); + OWIsolate_device(ROMS[1]); + printf("Sensor 2 temp = %dC\r\n", get_temp(ROMS[1])); + count++; + delay_milliseconds_synchronous(5000 , 1); + } + #endif + } +} + +// Routine to configure gpio after mote_init. +// Taken from the mote_init routine just changed the GPI value +void OW_gpio_config(void) +{ + // Select banks for GPIO inputs + GPI_control(0, 0, 1, 0); // 1 in 3rd arg connects GPI8 to EXT_INTERRUPT<1> + // Select banks for GPIO outputs + GPO_control(6, 6, 0, 6); // 0 in 3rd arg connects clk_3wb to GPO8 for 3WB cal + // Set GPI enables + GPI_enables(0x0102); // enable GPIO 1 as RX for 1-wire + // Set GPO enables + GPO_enables(0x0FFD); // GPIO 0 is 1-wire TX + // scan chain + analog_scan_chain_write(); + analog_scan_chain_load(); + // Initialize all pins to be low. + GPIO_REG__OUTPUT &= ~0xFFFF; +} + +#ifdef DS18B20 +// DS18B20 ======================================================================= +// Isolates then configures the DS18B20's alarms and resolution registers. +// Write all changes to EEPROM +// config = 9 | 10 | 11 | 12, for resolution bits +void init_DS18B20(uint64_t device_rom, uint8_t T_h, uint8_t T_l, uint8_t resolution) +{ + int config; // resolution sets config + switch(resolution) + { + case 9: + config = 0x00; + break; + case 10: + config = 0x20; + break; + case 11: + config = 0x40; + break; + case 12: + config = 0x60; + break; + default: + config = 0x00; + break; + } + + uint8_t scratch_mem[9] = {0}; // Read the scratch + int cnt = 0; + do + { + // Write the config registers. + OWIsolate_device(device_rom); + OWWriteByte(WRITE_SCRATCH); + OWWriteByte(T_h); // T_high alarm + OWWriteByte(T_l); // T_low alarm + OWWriteByte(config); // resolution config. + + // Copy scatch memory into device EEPROM + OWIsolate_device(device_rom); + OWWriteByte(COPY_SCRATCH); // Copies to EEPROM + delay_milliseconds_synchronous(10 , 1); + + // Read scatch memory for CRC check. + OWIsolate_device(device_rom); + OWWriteByte(READ_SCRATCH); + OWRead_bytes(scratch_mem, sizeof(scratch_mem)); + cnt++; + if(cnt > 10) + { + printf("\r\nCRC fail\r\n"); + } + }while(((OWCRC_bytes(scratch_mem, sizeof(scratch_mem) + 1)) != 0) && (cnt <= 10)); +} + + +uint16_t get_temp(uint64_t device_rom) +{ + int delay; // Resolution selects delay + switch(resolution) + { + case 9: + delay = T_CONV_9; + break; + case 10: + delay = T_CONV_10; + break; + case 11: + delay = T_CONV_11; + break; + case 12: + delay = T_CONV_12; + break; + default: + delay = T_CONV_9; + break; + } + uint8_t scratch_bytes[9] = {0}; + uint16_t temp_val = 0x00; + int cnt = 0; + do{ + OWIsolate_device(device_rom); + OWWriteByte(CONVERT_T);// take a temperature reading + delay_milliseconds_synchronous(delay, 1); // Let the sensor math + + OWIsolate_device(device_rom); + OWWriteByte(READ_SCRATCH); + OWRead_bytes(scratch_bytes, sizeof(scratch_bytes)); + temp_val |= scratch_bytes[1]; // MSB + temp_val = (temp_val << 8); + temp_val |= scratch_bytes[0]; // LSB + temp_val = (temp_val >> 4); // Truncate the decimal for now. + cnt++; + if(cnt > 10) + { + printf("\r\nCRC fail\r\n"); + } + //temp_val = (int)((temp_val * (9/5)) + 32); // convert to Freedom units ;) + }while(((OWCRC_bytes(scratch_bytes, sizeof(scratch_bytes) + 1)) != 0) && (cnt <= 10)); + return temp_val; +} + +void silence_callback(void) +{ + //SHHHHH +} +#endif + diff --git a/scm_v3c/gpio.c b/scm_v3c/gpio.c index 3f79c8c0..359d4cb9 100644 --- a/scm_v3c/gpio.c +++ b/scm_v3c/gpio.c @@ -11,6 +11,8 @@ void gpio_set_low(const gpio_e gpio) { GPIO_REG__OUTPUT &= ~(1 << gpio); } void gpio_toggle(const gpio_e gpio) { GPIO_REG__OUTPUT ^= (1 << gpio); } +int gpio_read(const gpio_e gpio) { return (GPIO_REG__INPUT &= (1 << gpio)) ? 1 : 0; } + void gpio_init(void) { // Initialize all pins to be low. GPIO_REG__OUTPUT &= ~0xFFFF; @@ -19,66 +21,82 @@ void gpio_init(void) { void gpio_0_set(void) { gpio_set_high(GPIO_0); } void gpio_0_clr(void) { gpio_set_low(GPIO_0); } void gpio_0_toggle(void) { gpio_toggle(GPIO_0); } +int gpio_0_read(void) { return gpio_read(GPIO_0); } void gpio_1_set(void) { gpio_set_high(GPIO_1); } void gpio_1_clr(void) { gpio_set_low(GPIO_1); } void gpio_1_toggle(void) { gpio_toggle(GPIO_1); } +int gpio_1_read(void) { return gpio_read(GPIO_1); } void gpio_2_set(void) { gpio_set_high(GPIO_2); } void gpio_2_clr(void) { gpio_set_low(GPIO_2); } void gpio_2_toggle(void) { gpio_toggle(GPIO_2); } +int gpio_2_read(void) { return gpio_read(GPIO_2); } void gpio_3_set(void) { gpio_set_high(GPIO_3); } void gpio_3_clr(void) { gpio_set_low(GPIO_3); } void gpio_3_toggle(void) { gpio_toggle(GPIO_3); } +int gpio_3_read(void) { return gpio_read(GPIO_3); } void gpio_4_set(void) { gpio_set_high(GPIO_4); } void gpio_4_clr(void) { gpio_set_low(GPIO_4); } void gpio_4_toggle(void) { gpio_toggle(GPIO_4); } +int gpio_4_read(void) { return gpio_read(GPIO_4); } void gpio_5_set(void) { gpio_set_high(GPIO_5); } void gpio_5_clr(void) { gpio_set_low(GPIO_5); } void gpio_5_toggle(void) { gpio_toggle(GPIO_5); } +int gpio_5_read(void) { return gpio_read(GPIO_5); } void gpio_6_set(void) { gpio_set_high(GPIO_6); } void gpio_6_clr(void) { gpio_set_low(GPIO_6); } void gpio_6_toggle(void) { gpio_toggle(GPIO_6); } +int gpio_6_read(void) { return gpio_read(GPIO_6); } void gpio_7_set(void) { gpio_set_high(GPIO_7); } void gpio_7_clr(void) { gpio_set_low(GPIO_7); } void gpio_7_toggle(void) { gpio_toggle(GPIO_7); } +int gpio_7_read(void) { return gpio_read(GPIO_7); } void gpio_8_set(void) { gpio_set_high(GPIO_8); } void gpio_8_clr(void) { gpio_set_low(GPIO_8); } void gpio_8_toggle(void) { gpio_toggle(GPIO_8); } +int gpio_8_read(void) { return gpio_read(GPIO_8); } void gpio_9_set(void) { gpio_set_high(GPIO_9); } void gpio_9_clr(void) { gpio_set_low(GPIO_9); } void gpio_9_toggle(void) { gpio_toggle(GPIO_9); } +int gpio_9_read(void) { return gpio_read(GPIO_9); } void gpio_10_set(void) { gpio_set_high(GPIO_10); } void gpio_10_clr(void) { gpio_set_low(GPIO_10); } void gpio_10_toggle(void) { gpio_toggle(GPIO_10); } +int gpio_10_read(void) { return gpio_read(GPIO_10); } void gpio_11_set(void) { gpio_set_high(GPIO_11); } void gpio_11_clr(void) { gpio_set_low(GPIO_11); } void gpio_11_toggle(void) { gpio_toggle(GPIO_11); } +int gpio_11_read(void) { return gpio_read(GPIO_11); } void gpio_12_set(void) { gpio_set_high(GPIO_12); } void gpio_12_clr(void) { gpio_set_low(GPIO_12); } void gpio_12_toggle(void) { gpio_toggle(GPIO_12); } +int gpio_12_read(void) { return gpio_read(GPIO_12); } void gpio_13_set(void) { gpio_set_high(GPIO_13); } void gpio_13_clr(void) { gpio_set_low(GPIO_13); } void gpio_13_toggle(void) { gpio_toggle(GPIO_13); } +int gpio_13_read(void) { return gpio_read(GPIO_13); } void gpio_14_set(void) { gpio_set_high(GPIO_14); } void gpio_14_clr(void) { gpio_set_low(GPIO_14); } void gpio_14_toggle(void) { gpio_toggle(GPIO_14); } +int gpio_14_read(void) { return gpio_read(GPIO_14); } void gpio_15_set(void) { gpio_set_high(GPIO_15); } void gpio_15_clr(void) { gpio_set_low(GPIO_15); } void gpio_15_toggle(void) { gpio_toggle(GPIO_15); } +int gpio_15_read(void) { return gpio_read(GPIO_15); } // ISRs for external interrupts. void ext_gpio3_activehigh_debounced_isr() { diff --git a/scm_v3c/gpio.h b/scm_v3c/gpio.h index ce876307..49c53900 100644 --- a/scm_v3c/gpio.h +++ b/scm_v3c/gpio.h @@ -31,76 +31,95 @@ void gpio_set_low(gpio_e gpio); // Toggle the GPIO. void gpio_toggle(gpio_e gpio); +// Read the GPIO +int gpio_read(const gpio_e gpio); + void gpio_init(void); void gpio_0_set(void); void gpio_0_clr(void); void gpio_0_toggle(void); +int gpio_0_read(void); // Frame. void gpio_1_set(void); void gpio_1_clr(void); void gpio_1_toggle(void); +int gpio_1_read(void); // ISR. void gpio_2_set(void); void gpio_2_clr(void); void gpio_2_toggle(void); +int gpio_2_read(void); // Slot. void gpio_3_set(void); void gpio_3_clr(void); void gpio_3_toggle(void); +int gpio_3_read(void); // FSM. void gpio_4_set(void); void gpio_4_clr(void); void gpio_4_toggle(void); +int gpio_4_read(void); // Task. void gpio_5_set(void); void gpio_5_clr(void); void gpio_5_toggle(void); +int gpio_5_read(void); // Radio. void gpio_6_set(void); void gpio_6_clr(void); void gpio_6_toggle(void); +int gpio_6_read(void); void gpio_7_set(void); void gpio_7_clr(void); void gpio_7_toggle(void); +int gpio_7_read(void); void gpio_8_set(void); void gpio_8_clr(void); void gpio_8_toggle(void); +int gpio_8_read(void); void gpio_9_set(void); void gpio_9_clr(void); void gpio_9_toggle(void); +int gpio_9_read(void); void gpio_10_set(void); void gpio_10_clr(void); void gpio_10_toggle(void); +int gpio_10_read(void); void gpio_11_set(void); void gpio_11_clr(void); void gpio_11_toggle(void); +int gpio_11_read(void); void gpio_12_set(void); void gpio_12_clr(void); void gpio_12_toggle(void); +int gpio_12_read(void); void gpio_13_set(void); void gpio_13_clr(void); void gpio_13_toggle(void); +int gpio_13_read(void); void gpio_14_set(void); void gpio_14_clr(void); void gpio_14_toggle(void); +int gpio_14_read(void); void gpio_15_set(void); void gpio_15_clr(void); void gpio_15_toggle(void); +int gpio_15_read(void); #endif // __GPIO_H From 8c626db626630f42470cc06635450befda15308c Mon Sep 17 00:00:00 2001 From: mmontee <34085121+mmontee@users.noreply.github.com> Date: Fri, 12 Jul 2024 12:24:54 -0700 Subject: [PATCH 2/8] Changed Comments --- scm_v3c/applications/1_wire/1_wire_test.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scm_v3c/applications/1_wire/1_wire_test.c b/scm_v3c/applications/1_wire/1_wire_test.c index f41f12c6..58fed72b 100644 --- a/scm_v3c/applications/1_wire/1_wire_test.c +++ b/scm_v3c/applications/1_wire/1_wire_test.c @@ -56,12 +56,14 @@ int main(void) { perform_calibration(); printf("Initialization Complete.\r\n"); rftimer_set_callback_by_id(silence_callback, 1); + // Start -- 1-wire init.=========================================================== /* - GPIO 0, 1 are configured. "OW_gpio_config();" + GPIO 0, 1 are configured for bus using "OW_gpio_config();" Declare Tree root. - Declare array with root member "item_count" elements. - Pass both the root and the array to tranfer the lists contents to the array. + Assign OWSearch_bus()'s output to the Tree root. Returns NULL on fail. + Declare array with Tree root member "item_count" elements. + Pass both the root and the array to OWGet_rom_array tranfer() moves the list into an array. */ OW_gpio_config(); // config for GPIO_0 = input GPIO_1 = input bus_roms_root_prt_t rom_list_root; //Declaring Tree root. @@ -73,7 +75,7 @@ int main(void) { { uint64_t ROMS[rom_list_root->item_count]; // Declare array with root member "item_count" elements. OWGet_rom_array(ROMS, rom_list_root); // Store ROMs in array, frees linked list - // END -- 1-wire init.============================================================= +// END -- 1-wire init.============================================================= for(int i = 0; i < sizeof(ROMS) / sizeof(uint64_t); i++)// Print all ROMs { printf("0x%llX\r\n", ROMS[i]); From 94d75af7782f40a50c80df54acfff1fa461cb9e8 Mon Sep 17 00:00:00 2001 From: mmontee <34085121+mmontee@users.noreply.github.com> Date: Fri, 12 Jul 2024 14:52:32 -0700 Subject: [PATCH 3/8] Comments/Clean up --- scm_v3c/1_wire.c | 83 +---------------------- scm_v3c/1_wire.h | 24 ++++--- scm_v3c/applications/1_wire/1_wire_test.c | 21 +++--- 3 files changed, 27 insertions(+), 101 deletions(-) diff --git a/scm_v3c/1_wire.c b/scm_v3c/1_wire.c index 755148b8..f6fa5675 100644 --- a/scm_v3c/1_wire.c +++ b/scm_v3c/1_wire.c @@ -139,10 +139,10 @@ void OWRead_bytes(uint8_t* buffer, int size) } -// CRC bytes, read elements 0 - size through crc generator. +// CRC bytes, read elements [0 to size] through crc generator. // crc8 value should match the crc produced my device if only the data is // used to generate the crc value. -// Alternitivly, pass the data and the give crc, the function should +// Alternitivly, pass the data and the crc from the device, the function should // return 0. int OWCRC_bytes(uint8_t* byte_array, int size) { @@ -420,82 +420,3 @@ int OWSearch(void) } return search_result; } -//-------------------------------------------------------------------------- -// Verify the device with the ROM number in ROM_NO buffer is present. -// Return TRUE : device verified present -// FALSE : device not present -// -int OWVerify(void) -{ - unsigned char rom_backup[8]; - int i,rslt,ld_backup,ldf_backup,lfd_backup; - // keep a backup copy of the current state - for (i = 0; i < 8; i++) - { - rom_backup[i] = ROM_NO[i]; - } - ld_backup = LastDiscrepancy; - ldf_backup = LastDeviceFlag; - lfd_backup = LastFamilyDiscrepancy; - // set search to find the same device - LastDiscrepancy = 64; - LastDeviceFlag = FALSE; - if (OWSearch()) - { - // check if same device found - rslt = TRUE; - for (i = 0; i < 8; i++) - { - if (rom_backup[i] != ROM_NO[i]) - { - rslt = FALSE; - break; - } - } - } - else - - rslt = FALSE; - // restore the search state - for (i = 0; i < 8; i++) - { - ROM_NO[i] = rom_backup[i]; - } - LastDiscrepancy = ld_backup; - LastDeviceFlag = ldf_backup; - LastFamilyDiscrepancy = lfd_backup; - // return the result of the verify - return rslt; -} -//-------------------------------------------------------------------------- -// Setup the search to find the device type 'family_code' on the next call -// to OWNext() if it is present. -// -void OWTargetSetup(unsigned char family_code) -{ - int i; - // set the search state to find SearchFamily type devices - ROM_NO[0] = family_code; - for (i = 1; i < 8; i++) - { - ROM_NO[i] = 0; - } - LastDiscrepancy = 64; - LastFamilyDiscrepancy = 0; - LastDeviceFlag = FALSE; -} -//-------------------------------------------------------------------------- -// Setup the search to skip the current device type on the next call -// to OWNext(). -// -void OWFamilySkipSetup(void) -{ - // set the Last discrepancy to last family discrepancy - LastDiscrepancy = LastFamilyDiscrepancy; - LastFamilyDiscrepancy = 0; - // check for end of list - if (LastDiscrepancy == 0) - { - LastDeviceFlag = TRUE; - } -} diff --git a/scm_v3c/1_wire.h b/scm_v3c/1_wire.h index 6d3eb8aa..320e0ead 100644 --- a/scm_v3c/1_wire.h +++ b/scm_v3c/1_wire.h @@ -8,12 +8,16 @@ #include //=========================== defines ========================================= - +// ROM commands. #define READROM 0x33 #define SKIPROM 0xCC #define MATCHROM 0x55 #define SEARCHROM 0xF0 +// Delay values are not exact and do not reflect micros. +// The plan was to use these initial values then tune them +// individually, however tuning resulted in breaking the +// driver. So they are left as the working defaults. #define DELAY_A 6 #define DELAY_B 64 #define DELAY_C 60 @@ -25,13 +29,14 @@ #define DELAY_I 70 #define DELAY_J 410 +// GPIO pins are specified here. #define TX_PIN 0 #define RX_PIN 1 // Function Macros #define BUS_LOW() GPIO_REG__OUTPUT |= (1 << TX_PIN); // Pulls bus LOW #define BUS_RELEASE() GPIO_REG__OUTPUT &= ~(1 << TX_PIN); // Release bus to Pull-up -#define BUS_READ() (GPIO_REG__INPUT &= (1 << RX_PIN)) ? 1 : 0; +#define BUS_READ() (GPIO_REG__INPUT &= (1 << RX_PIN)) ? 1 : 0; // Read the logic level of the bus. //=================================== // 1-wire search rom, code taken from; // https://www.analog.com/en/resources/app-notes/1wire-search-algorithm.html @@ -39,7 +44,9 @@ #define TRUE 1 //=========================== variables ======================================= -// ROMs found on the bus are stored in a linked list structure +// ROMs found on the bus are stored in a linked list structure. Where the root contains the +// pointed to the first node(next) and the total number of nodes(item_count). Each node stores +// an individual ROM(rom) and the pointer to the next(next). typedef struct rom_list_root{ uint32_t item_count; struct rom_list* next; @@ -52,12 +59,12 @@ typedef struct rom_list{ //=========================== prototypes ====================================== // User -uint8_t OWRead_byte(void);// Reads a byte from bus -void OWRead_bytes(uint8_t* buffer, int size);// Reads n bytes from bus -int OWIsolate_device(uint64_t device_rom); // Isolate a device -void OWWrite_rom(uint64_t device_rom); // Write rom to bus bus_roms_root_prt_t OWSearch_bus(void);// Read all roms on bus, returns base to linked list void OWGet_rom_array(uint64_t* array, bus_roms_root_prt_t root);// Create array of ROMs from linked list +void OWWrite_rom(uint64_t device_rom); // Write rom to bus +int OWIsolate_device(uint64_t device_rom); // Isolate a device +uint8_t OWRead_byte(void);// Reads a byte from bus +void OWRead_bytes(uint8_t* buffer, int size);// Reads n bytes from bus int OWCRC_bytes(uint8_t* byte_array, int size); // Driver @@ -72,9 +79,6 @@ void OWWriteByte(uint8_t byte); // https://www.analog.com/en/resources/app-notes/1wire-search-algorithm.html int OWFirst(void); int OWNext(void); -int OWVerify(void); -void OWTargetSetup(unsigned char family_code); -void OWFamilySkipSetup(void); int OWSearch(void); unsigned char docrc8(unsigned char value); #endif diff --git a/scm_v3c/applications/1_wire/1_wire_test.c b/scm_v3c/applications/1_wire/1_wire_test.c index 58fed72b..abd53e52 100644 --- a/scm_v3c/applications/1_wire/1_wire_test.c +++ b/scm_v3c/applications/1_wire/1_wire_test.c @@ -60,13 +60,13 @@ int main(void) { // Start -- 1-wire init.=========================================================== /* GPIO 0, 1 are configured for bus using "OW_gpio_config();" - Declare Tree root. - Assign OWSearch_bus()'s output to the Tree root. Returns NULL on fail. - Declare array with Tree root member "item_count" elements. + Declare List root. + Assign OWSearch_bus()'s output to the root. Returns NULL on fail. + Declare array with List root member "item_count" elements. Pass both the root and the array to OWGet_rom_array tranfer() moves the list into an array. */ OW_gpio_config(); // config for GPIO_0 = input GPIO_1 = input - bus_roms_root_prt_t rom_list_root; //Declaring Tree root. + bus_roms_root_prt_t rom_list_root; //Declaring List root. if((rom_list_root = OWSearch_bus()) == NULL)// Try to get the ROMs of all devices { printf("No ROMs Found.\r\n"); @@ -86,6 +86,7 @@ int main(void) { Individual devices can be isolated using OWIsolate_device(ROMS[n]); */ #ifdef DS18B20 + init_DS18B20(ROMS[0], 100, 10, 12); init_DS18B20(ROMS[1], 100, 10, 12); int count = 1; @@ -97,14 +98,14 @@ int main(void) { OWIsolate_device(ROMS[1]); printf("Sensor 2 temp = %dC\r\n", get_temp(ROMS[1])); count++; - delay_milliseconds_synchronous(5000 , 1); + delay_milliseconds_synchronous(2000 , 1); } #endif } } // Routine to configure gpio after mote_init. -// Taken from the mote_init routine just changed the GPI value +// Taken from the mote_init routine just changed the GPI/O_enable values void OW_gpio_config(void) { // Select banks for GPIO inputs @@ -124,7 +125,7 @@ void OW_gpio_config(void) #ifdef DS18B20 // DS18B20 ======================================================================= -// Isolates then configures the DS18B20's alarms and resolution registers. +// Isolates then configures a DS18B20's alarms and resolution registers. // Write all changes to EEPROM // config = 9 | 10 | 11 | 12, for resolution bits void init_DS18B20(uint64_t device_rom, uint8_t T_h, uint8_t T_l, uint8_t resolution) @@ -177,7 +178,7 @@ void init_DS18B20(uint64_t device_rom, uint8_t T_h, uint8_t T_l, uint8_t resolut }while(((OWCRC_bytes(scratch_mem, sizeof(scratch_mem) + 1)) != 0) && (cnt <= 10)); } - +// Returns temperature data from a given ROM uint16_t get_temp(uint64_t device_rom) { int delay; // Resolution selects delay @@ -205,7 +206,7 @@ uint16_t get_temp(uint64_t device_rom) do{ OWIsolate_device(device_rom); OWWriteByte(CONVERT_T);// take a temperature reading - delay_milliseconds_synchronous(delay, 1); // Let the sensor math + delay_milliseconds_synchronous(delay, 1); // Let the sensor think OWIsolate_device(device_rom); OWWriteByte(READ_SCRATCH); @@ -226,7 +227,7 @@ uint16_t get_temp(uint64_t device_rom) void silence_callback(void) { - //SHHHHH + //SHHHHH!!!! } #endif From 0ed52c9882f39d43dda8a1a83af22bab35684bb8 Mon Sep 17 00:00:00 2001 From: mmontee <34085121+mmontee@users.noreply.github.com> Date: Fri, 19 Jul 2024 09:31:32 -0700 Subject: [PATCH 4/8] All at once now. Driver confirmer working on LF and HF clock sources. --- scm_v3c/1_wire.c | 113 +++++++++--- scm_v3c/1_wire.h | 77 +++++--- .../applications/1_wire/1_wire_project.uvoptx | 24 +++ .../1_wire/1_wire_project.uvprojx | 10 ++ scm_v3c/applications/1_wire/1_wire_test.c | 169 ++---------------- scm_v3c/applications/1_wire/DS18B20.c | 123 +++++++++++++ scm_v3c/applications/1_wire/DS18B20.h | 27 +++ scm_v3c/scm3c_hw_interface.c | 8 +- 8 files changed, 344 insertions(+), 207 deletions(-) create mode 100644 scm_v3c/applications/1_wire/DS18B20.c create mode 100644 scm_v3c/applications/1_wire/DS18B20.h diff --git a/scm_v3c/1_wire.c b/scm_v3c/1_wire.c index f6fa5675..dfc20eb5 100644 --- a/scm_v3c/1_wire.c +++ b/scm_v3c/1_wire.c @@ -9,14 +9,76 @@ #include "memory_map.h" #include "1_wire.h" -// 1-wire search rom, code taken from; =====================================* + +// 1wire-search-algorithm, code below taken from; // https://www.analog.com/en/resources/app-notes/1wire-search-algorithm.html unsigned char ROM_NO[8]; // Rom bytes are put here on search int LastDiscrepancy; int LastFamilyDiscrepancy; int LastDeviceFlag; unsigned char crc8; -//==========================================================================* + +// Routine to configure gpio after mote_init. +// Taken from the mote_init routine just changed the GPI/O_enable values +// GPO are set default, just turn on the RX GPI and turn off the GPO for the RX pin. +inline void OW_gpio_config(int rx) +{ + uint16_t gpi_mask = (0x01 << rx); + uint16_t gpo_mask = (0x01 << rx); + + // Select banks for GPIO inputs + GPI_control(0, 0, 0, 0); // 1 in 3rd arg connects GPI8 to EXT_INTERRUPT<1> + // Select banks for GPIO outputs + GPO_control(6, 6, 6, 6); // 0 in 3rd arg connects clk_3wb to GPO8 for 3WB cal + // Set GPI enables + GPI_enables(gpi_mask |= 0x0000); // enable GPIO 1 as RX for 1-wire + printf("gpi mask = 0x%X\r\n", gpi_mask); + // Set GPO enables + GPO_enables(~(gpo_mask &= 0xFFFF)); // GPIO 0 is 1-wire TX, turn off output on RX + printf("gpo mask = 0x%X\r\n", gpi_mask); + #ifdef HF_CLOCK + // Set HCLK source as HF_CLOCK + set_asc_bit(1147);//**** + // Set RFTimer source as HF_CLOCK + set_asc_bit(1151);//**** + // Disable LF_CLOCK + set_asc_bit(553);//***** + // HF_clock div + set_asc_bit(49); + set_asc_bit(48); + clear_asc_bit(47); + set_asc_bit(46); + clear_asc_bit(45); + set_asc_bit(44); + set_asc_bit(43); + set_asc_bit(42); + #endif + #ifdef LF_CLOCK + // Let HCLK source be LF_CLOCK + clear_asc_bit(1147);//**** + // Let RFTimer source be LF_CLOCK + clear_asc_bit(1151);//**** + // Enable LF_CLOCK + clear_asc_bit(553);//***** + // LF_clock div + set_asc_bit(49); + set_asc_bit(48); + set_asc_bit(47); + clear_asc_bit(46); + clear_asc_bit(45); + set_asc_bit(44); + clear_asc_bit(43); + clear_asc_bit(42); + #endif + + // scan chain + analog_scan_chain_write(); + analog_scan_chain_load(); + // Initialize all pins to be low. + GPIO_REG__OUTPUT &= ~0xFFFF; + STRONG_PULL_UP_OFF(); +} + // Returns all roms in linked list bus_roms_root_prt_t OWSearch_bus(void) { @@ -91,23 +153,14 @@ void OWGet_rom_array(uint64_t* array, bus_roms_root_prt_t root) { bus_roms_list_prt_t prev_rom = rom_n; array[i] = rom_n->rom; + printf("Freeing element 0x%llX\r\n", rom_n->rom); rom_n = rom_n->next; + free(prev_rom); } } - -// NOP for short delays. Exact timing needs work. -// For now it is working -void OWDelay_us(int us) -{ - for(int i = us; i > 0; i--) - { - __asm("NOP"); - } -} - // Reads single byte, use in OWRead_bytes -uint8_t OWRead_byte(void) +inline uint8_t OWRead_byte(void) { int count = 8; uint8_t data = 0x00; @@ -172,52 +225,52 @@ int OWIsolate_device(uint64_t device_rom) // Platform specific function used in 1wire-search-algorithm================= // Write a bit to the bus -void OWWriteBit(uint8_t bit) +inline void OWWriteBit(uint8_t bit) { switch(bit) { case 0x00: BUS_LOW(); - OWDelay_us(DELAY_C); + DELAY_C BUS_RELEASE(); - OWDelay_us(DELAY_D); + DELAY_D break; default: BUS_LOW(); - OWDelay_us(DELAY_A); + DELAY_A BUS_RELEASE(); - OWDelay_us(DELAY_B); + DELAY_B break; } } // Reads a byte from the bus. -uint8_t OWReadBit(void) +inline uint8_t OWReadBit(void) { BUS_LOW(); - OWDelay_us(DELAY_A); + DELAY_A BUS_RELEASE(); - OWDelay_us(DELAY_E); + DELAY_E uint8_t bit = BUS_READ(); - OWDelay_us(DELAY_F); + DELAY_F return bit; } // Return is 1 for presence is detected. -int OWReset(void) +inline int OWReset(void) { BUS_LOW(); - OWDelay_us(DELAY_H); + DELAY_H BUS_RELEASE(); - OWDelay_us(DELAY_I); + DELAY_I int state = BUS_READ(); - OWDelay_us(DELAY_J); + DELAY_J return !state; } // Sends a byte -void OWWriteByte(uint8_t byte) +inline void OWWriteByte(uint8_t byte) { int count = 8; while(count > 0) @@ -237,8 +290,10 @@ void OWWriteByte(uint8_t byte) } //========================================================================== -// 1wire-search-algorithm, code taken from; +// 1wire-search-algorithm, code below taken from; // https://www.analog.com/en/resources/app-notes/1wire-search-algorithm.html + + static unsigned char dscrc_table[] = { 0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65, 157,195, 33,127,252,162, 64, 30, 95, 1,227,189, 62, 96,130,220, diff --git a/scm_v3c/1_wire.h b/scm_v3c/1_wire.h index 320e0ead..db6a8564 100644 --- a/scm_v3c/1_wire.h +++ b/scm_v3c/1_wire.h @@ -8,41 +8,68 @@ #include //=========================== defines ========================================= +// *** Congfig. defines +// Define the HCLK source. +#define LF_CLOCK // HF_CLOCK or LF_CLOCK +// Enable strong pull-up +#define USE_STRONG_PULL 1 // 1 = true, 0 = false +// GPIO pins are specified here. +#define TX_PIN 0 +#define RX_PIN 1 +#define STRONG_PULL_UP_PIN 2 +// End Congfig. defines *** + // ROM commands. #define READROM 0x33 #define SKIPROM 0xCC #define MATCHROM 0x55 #define SEARCHROM 0xF0 - -// Delay values are not exact and do not reflect micros. -// The plan was to use these initial values then tune them -// individually, however tuning resulted in breaking the -// driver. So they are left as the working defaults. -#define DELAY_A 6 -#define DELAY_B 64 -#define DELAY_C 60 -#define DELAY_D 10 -#define DELAY_E 9 -#define DELAY_F 55 -#define DELAY_G 0 -#define DELAY_H 480 -#define DELAY_I 70 -#define DELAY_J 410 - -// GPIO pins are specified here. -#define TX_PIN 0 -#define RX_PIN 1 - -// Function Macros -#define BUS_LOW() GPIO_REG__OUTPUT |= (1 << TX_PIN); // Pulls bus LOW -#define BUS_RELEASE() GPIO_REG__OUTPUT &= ~(1 << TX_PIN); // Release bus to Pull-up -#define BUS_READ() (GPIO_REG__INPUT &= (1 << RX_PIN)) ? 1 : 0; // Read the logic level of the bus. //=================================== // 1-wire search rom, code taken from; // https://www.analog.com/en/resources/app-notes/1wire-search-algorithm.html #define FALSE 0 #define TRUE 1 +//=================================== +// Function Macros +#define BUS_LOW() GPIO_REG__OUTPUT |= (1 << TX_PIN); // Pulls bus LOW +#define BUS_RELEASE() GPIO_REG__OUTPUT &= ~(1 << TX_PIN); // Release bus to Pull-up +#define BUS_READ() (GPIO_REG__INPUT &= (1 << RX_PIN)) ? 1 : 0; // Read the logic level of the bus. +#define STRONG_PULL_UP_OFF() GPIO_REG__OUTPUT |= (1 << STRONG_PULL_UP_PIN); +#define STRONG_PULL_UP_ON() GPIO_REG__OUTPUT &= ~(1 << STRONG_PULL_UP_PIN); +// NOP Macros for us timing +#define NOP_5() __asm("NOP\n\t" "NOP\n\t" "NOP\n\t" "NOP\n\t" "NOP\n\t"); //~1us +#define NOP_10() {NOP_5(); NOP_5();} // 2us +#define NOP_50() {NOP_10(); NOP_10(); NOP_10(); NOP_10(); NOP_10();} // 10us +#define NOP_100() {NOP_50(); NOP_50()} // 20 us +#define NOP_500() {NOP_100(); NOP_100(); NOP_100(); NOP_100(); NOP_100();} // 100us + +#ifdef HF_CLOCK +//Delay Macros for 1-wire timing HF_clock +#define DELAY_A {NOP_10(); NOP_10(); NOP_10();}//6us +#define DELAY_B {NOP_100(); NOP_100(); NOP_100(); NOP_10(); NOP_10();}//64 +#define DELAY_C {NOP_100(); NOP_100(); NOP_100();}//60 +#define DELAY_D {NOP_50();}//10 +#define DELAY_E {NOP_10(); NOP_10(); NOP_10(); NOP_10(); NOP_5();}//9 +#define DELAY_F {NOP_100(); NOP_100(); NOP_50();}//55 +#define DELAY_G {}//0 +#define DELAY_H {NOP_500(); NOP_500(); NOP_500(); NOP_500(); NOP_100(); NOP_100(); NOP_100(); NOP_100();}//480 +#define DELAY_I {NOP_100(); NOP_100(); NOP_100(); NOP_50();}//70 +#define DELAY_J {NOP_500(); NOP_500(); NOP_500(); NOP_500(); NOP_50();}//410 +#endif +#ifdef LF_CLOCK +// Delay Macros for 1-wire timing LF_clock LF = HF * 2/3 +#define DELAY_A { NOP_10(); NOP_10();}//6us * 0.7 = 4.2 = 4 +#define DELAY_B {NOP_100(); NOP_100(); NOP_10(); NOP_10(); NOP_5();}//64 * 0.7 = 44.8 = 45 +#define DELAY_C {NOP_100(); NOP_100(); NOP_10();}//60 * 0.7 = 42 +#define DELAY_D {NOP_10(); NOP_10(); NOP_10(); NOP_5();}//10 * 0.7 = 7 +#define DELAY_E {NOP_10(); NOP_10(); NOP_10();}//9 * 0.7 = 6.3 = 6 +#define DELAY_F {NOP_100(); NOP_50(); NOP_10(); NOP_10(); NOP_10(); NOP_10(); NOP_5();}//55 * 0.7 = 38.5 = 39 +#define DELAY_G {}//0 +#define DELAY_H {NOP_500(); NOP_500(); NOP_500(); NOP_100(); NOP_50(); NOP_10(); NOP_10(); NOP_10();}//480 * 0.7 = 336 +#define DELAY_I {NOP_100(); NOP_100(); NOP_10(); NOP_10(); NOP_10(); NOP_10(); NOP_5();}//70 * 0.7 = 49 +#define DELAY_J {NOP_500(); NOP_500(); NOP_100(); NOP_100(); NOP_100(); NOP_100(); NOP_10(); NOP_10(); NOP_10(); NOP_5();}//410 * 0.7 = 287 +#endif //=========================== variables ======================================= // ROMs found on the bus are stored in a linked list structure. Where the root contains the // pointed to the first node(next) and the total number of nodes(item_count). Each node stores @@ -68,7 +95,7 @@ void OWRead_bytes(uint8_t* buffer, int size);// Reads n bytes from bus int OWCRC_bytes(uint8_t* byte_array, int size); // Driver -void OWDelay_us(int us); +void OW_gpio_config(int tx); void OWStore_rom(unsigned char* rom_bytes, bus_roms_list_prt_t base); void OWWriteBit(uint8_t bit); uint8_t OWReadBit(void); diff --git a/scm_v3c/applications/1_wire/1_wire_project.uvoptx b/scm_v3c/applications/1_wire/1_wire_project.uvoptx index eb12b389..8a793242 100644 --- a/scm_v3c/applications/1_wire/1_wire_project.uvoptx +++ b/scm_v3c/applications/1_wire/1_wire_project.uvoptx @@ -435,6 +435,30 @@ 0 0 + + 2 + 22 + 1 + 0 + 0 + 0 + .\DS18B20.c + DS18B20.c + 0 + 0 + + + 2 + 23 + 5 + 0 + 0 + 0 + .\DS18B20.h + DS18B20.h + 0 + 0 + diff --git a/scm_v3c/applications/1_wire/1_wire_project.uvprojx b/scm_v3c/applications/1_wire/1_wire_project.uvprojx index e20d4f8c..1166f7a1 100644 --- a/scm_v3c/applications/1_wire/1_wire_project.uvprojx +++ b/scm_v3c/applications/1_wire/1_wire_project.uvprojx @@ -493,6 +493,16 @@ 5 ..\..\1_wire.h + + DS18B20.c + 1 + .\DS18B20.c + + + DS18B20.h + 5 + .\DS18B20.h + diff --git a/scm_v3c/applications/1_wire/1_wire_test.c b/scm_v3c/applications/1_wire/1_wire_test.c index abd53e52..3c29b9ad 100644 --- a/scm_v3c/applications/1_wire/1_wire_test.c +++ b/scm_v3c/applications/1_wire/1_wire_test.c @@ -9,24 +9,14 @@ #include "gpio.h" #include "rftimer.h" #include "1_wire.h" +#include "DS18B20.h" //=========================== defines ========================================= // SCuM #define CRC_VALUE (*((unsigned int*)0x0000FFFC)) #define CODE_LENGTH (*((unsigned int*)0x0000FFF8)) -// DS18B20 #define DS18B20 -#define READ_SCRATCH 0xBE -#define WRITE_SCRATCH 0x4E -#define COPY_SCRATCH 0x48 -#define RECALL_E2 0xB8 -#define READ_POWER_SUPPLY 0xB4 -#define CONVERT_T 0x44 -#define T_CONV_9 94 -#define T_CONV_10 188 -#define T_CONV_11 375 -#define T_CONV_12 750 //=========================== variables ======================================= // SCuM typedef struct { @@ -35,15 +25,9 @@ typedef struct { app_vars_t app_vars; -// DS18B20 - valid values are only {9,10,11,12} -int resolution = 9; //=========================== prototypes ====================================== // 1-Wire -void OW_gpio_config(void); -// DS18B20 -void init_DS18B20(uint64_t device_rom, uint8_t T_h, uint8_t T_l, uint8_t config); -uint16_t get_temp(uint64_t device_rom); void silence_callback(void); //=========================== main ============================================ @@ -65,7 +49,7 @@ int main(void) { Declare array with List root member "item_count" elements. Pass both the root and the array to OWGet_rom_array tranfer() moves the list into an array. */ - OW_gpio_config(); // config for GPIO_0 = input GPIO_1 = input + OW_gpio_config(RX_PIN); // config for GPIO_0 = TX, GPIO_1 = RX, and GPIO_2 = pull-up bus_roms_root_prt_t rom_list_root; //Declaring List root. if((rom_list_root = OWSearch_bus()) == NULL)// Try to get the ROMs of all devices { @@ -80,154 +64,39 @@ int main(void) { { printf("0x%llX\r\n", ROMS[i]); } - + /* After 1-wire init. success all roms discovered on bus are stored in ROMS[]. Individual devices can be isolated using OWIsolate_device(ROMS[n]); */ #ifdef DS18B20 - init_DS18B20(ROMS[0], 100, 10, 12); - init_DS18B20(ROMS[1], 100, 10, 12); + for(int i = 0; i < sizeof(ROMS) / sizeof(uint64_t); i++) + { + if((ROMS[i] & 0x28) == 0x28) + { + init_DS18B20(ROMS[i], 100, 10, RESOLUTION); // resolution is defined in DS18B20.h + } + } int count = 1; - while(1) { printf("Sample #%d\r\n", count); - OWIsolate_device(ROMS[0]); - printf("Sensor 1 temp = %dC\r\n", get_temp(ROMS[0])); - OWIsolate_device(ROMS[1]); - printf("Sensor 2 temp = %dC\r\n", get_temp(ROMS[1])); + for(int i = 0; i < sizeof(ROMS) / sizeof(uint64_t); i++) + { + if((ROMS[i] & 0x28) == 0x28) + { + OWIsolate_device(ROMS[i]); + printf("Sensor %d temp = %dC\r\n", i, get_temp(ROMS[i], USE_STRONG_PULL)); + } + } count++; - delay_milliseconds_synchronous(2000 , 1); + delay_milliseconds_synchronous(1000 , 1); } #endif } } -// Routine to configure gpio after mote_init. -// Taken from the mote_init routine just changed the GPI/O_enable values -void OW_gpio_config(void) -{ - // Select banks for GPIO inputs - GPI_control(0, 0, 1, 0); // 1 in 3rd arg connects GPI8 to EXT_INTERRUPT<1> - // Select banks for GPIO outputs - GPO_control(6, 6, 0, 6); // 0 in 3rd arg connects clk_3wb to GPO8 for 3WB cal - // Set GPI enables - GPI_enables(0x0102); // enable GPIO 1 as RX for 1-wire - // Set GPO enables - GPO_enables(0x0FFD); // GPIO 0 is 1-wire TX - // scan chain - analog_scan_chain_write(); - analog_scan_chain_load(); - // Initialize all pins to be low. - GPIO_REG__OUTPUT &= ~0xFFFF; -} - -#ifdef DS18B20 -// DS18B20 ======================================================================= -// Isolates then configures a DS18B20's alarms and resolution registers. -// Write all changes to EEPROM -// config = 9 | 10 | 11 | 12, for resolution bits -void init_DS18B20(uint64_t device_rom, uint8_t T_h, uint8_t T_l, uint8_t resolution) -{ - int config; // resolution sets config - switch(resolution) - { - case 9: - config = 0x00; - break; - case 10: - config = 0x20; - break; - case 11: - config = 0x40; - break; - case 12: - config = 0x60; - break; - default: - config = 0x00; - break; - } - - uint8_t scratch_mem[9] = {0}; // Read the scratch - int cnt = 0; - do - { - // Write the config registers. - OWIsolate_device(device_rom); - OWWriteByte(WRITE_SCRATCH); - OWWriteByte(T_h); // T_high alarm - OWWriteByte(T_l); // T_low alarm - OWWriteByte(config); // resolution config. - - // Copy scatch memory into device EEPROM - OWIsolate_device(device_rom); - OWWriteByte(COPY_SCRATCH); // Copies to EEPROM - delay_milliseconds_synchronous(10 , 1); - - // Read scatch memory for CRC check. - OWIsolate_device(device_rom); - OWWriteByte(READ_SCRATCH); - OWRead_bytes(scratch_mem, sizeof(scratch_mem)); - cnt++; - if(cnt > 10) - { - printf("\r\nCRC fail\r\n"); - } - }while(((OWCRC_bytes(scratch_mem, sizeof(scratch_mem) + 1)) != 0) && (cnt <= 10)); -} - -// Returns temperature data from a given ROM -uint16_t get_temp(uint64_t device_rom) -{ - int delay; // Resolution selects delay - switch(resolution) - { - case 9: - delay = T_CONV_9; - break; - case 10: - delay = T_CONV_10; - break; - case 11: - delay = T_CONV_11; - break; - case 12: - delay = T_CONV_12; - break; - default: - delay = T_CONV_9; - break; - } - uint8_t scratch_bytes[9] = {0}; - uint16_t temp_val = 0x00; - int cnt = 0; - do{ - OWIsolate_device(device_rom); - OWWriteByte(CONVERT_T);// take a temperature reading - delay_milliseconds_synchronous(delay, 1); // Let the sensor think - - OWIsolate_device(device_rom); - OWWriteByte(READ_SCRATCH); - OWRead_bytes(scratch_bytes, sizeof(scratch_bytes)); - temp_val |= scratch_bytes[1]; // MSB - temp_val = (temp_val << 8); - temp_val |= scratch_bytes[0]; // LSB - temp_val = (temp_val >> 4); // Truncate the decimal for now. - cnt++; - if(cnt > 10) - { - printf("\r\nCRC fail\r\n"); - } - //temp_val = (int)((temp_val * (9/5)) + 32); // convert to Freedom units ;) - }while(((OWCRC_bytes(scratch_bytes, sizeof(scratch_bytes) + 1)) != 0) && (cnt <= 10)); - return temp_val; -} - void silence_callback(void) { //SHHHHH!!!! } -#endif - diff --git a/scm_v3c/applications/1_wire/DS18B20.c b/scm_v3c/applications/1_wire/DS18B20.c new file mode 100644 index 00000000..62e6d1ef --- /dev/null +++ b/scm_v3c/applications/1_wire/DS18B20.c @@ -0,0 +1,123 @@ +#include +#include + +#include "DS18B20.h" +#include "1_wire.h" +#include "rftimer.h" + +// DS18B20 ======================================================================= +// Isolates then configures a DS18B20's alarms and resolution registers. +// Write all changes to EEPROM +// config = 9 | 10 | 11 | 12, for resolution bits +void init_DS18B20(uint64_t device_rom, uint8_t T_h, uint8_t T_l, uint8_t resolution) +{ + int config; // resolution sets config + switch(RESOLUTION) + { + case 9: + config = 0x00; + break; + case 10: + config = 0x20; + break; + case 11: + config = 0x40; + break; + case 12: + config = 0x60; + break; + default: + config = 0x00; + break; + } + + uint8_t scratch_mem[9] = {0}; // Read the scratch + int cnt = 0; + do + { + // Write the config registers. + OWIsolate_device(device_rom); + OWWriteByte(WRITE_SCRATCH); + OWWriteByte(T_h); // T_high alarm + OWWriteByte(T_l); // T_low alarm + OWWriteByte(config); // resolution config. + + // Copy scatch memory into device EEPROM + OWIsolate_device(device_rom); + OWWriteByte(COPY_SCRATCH); // Copies to EEPROM + STRONG_PULL_UP_ON(); + delay_milliseconds_synchronous(15, 1); + STRONG_PULL_UP_OFF(); + + // Read scatch memory for CRC check. + OWIsolate_device(device_rom); + OWWriteByte(READ_SCRATCH); + OWRead_bytes(scratch_mem, sizeof(scratch_mem)); + cnt++; + if(cnt > 10) + { + printf("\r\nCRC fail\r\n"); + } + }while(((OWCRC_bytes(scratch_mem, sizeof(scratch_mem) + 1)) != 0) && (cnt <= 10)); +} + +// Returns temperature data from a given ROM +uint16_t get_temp(uint64_t device_rom, int use_strong_pull_up) +{ + int delay; // Resolution selects delay + switch(RESOLUTION) + { + case 9: + delay = T_CONV_9; + break; + case 10: + delay = T_CONV_10; + break; + case 11: + delay = T_CONV_11; + break; + case 12: + delay = T_CONV_12; + break; + default: + delay = T_CONV_9; + break; + } + uint8_t scratch_bytes[9] = {0}; + uint16_t temp_val = 0x00; + int cnt = 0; + do{ + OWIsolate_device(device_rom); + if(use_strong_pull_up > 0) + { + + OWWriteByte(CONVERT_T);// take a temperature reading + STRONG_PULL_UP_ON(); + delay_milliseconds_synchronous(delay + 10, 1); // Let the sensor think + STRONG_PULL_UP_OFF(); + NOP_500(); + } + else + { + OWWriteByte(CONVERT_T);// take a temperature reading + delay_milliseconds_synchronous(delay, 1); // Let the sensor think + } + + + OWIsolate_device(device_rom); + OWWriteByte(READ_SCRATCH); + OWRead_bytes(scratch_bytes, sizeof(scratch_bytes)); + temp_val |= scratch_bytes[1]; // MSB + temp_val = (temp_val << 8); + temp_val |= scratch_bytes[0]; // LSB + temp_val = (temp_val >> 4); // Truncate the decimal for now. + cnt++; + if(cnt > 10) + { + printf("\r\nCRC fail\r\n"); + } + //temp_val = (int)((temp_val * (9/5)) + 32); // convert to Freedom units ;) + }while(((OWCRC_bytes(scratch_bytes, sizeof(scratch_bytes) + 1)) != 0) && (cnt <= 10)); + return temp_val; +} + diff --git a/scm_v3c/applications/1_wire/DS18B20.h b/scm_v3c/applications/1_wire/DS18B20.h new file mode 100644 index 00000000..a6ad94f7 --- /dev/null +++ b/scm_v3c/applications/1_wire/DS18B20.h @@ -0,0 +1,27 @@ +#ifndef __DS18B20_H +#define __DS18B20_H + +#include +#include +#include +#include "memory_map.h" + +//=========================== defines ========================================= +#define READ_SCRATCH 0xBE +#define WRITE_SCRATCH 0x4E +#define COPY_SCRATCH 0x48 +#define RECALL_E2 0xB8 +#define READ_POWER_SUPPLY 0xB4 +#define CONVERT_T 0x44 +#define T_CONV_9 94 +#define T_CONV_10 188 +#define T_CONV_11 375 +#define T_CONV_12 750 +// DS18B20 resolution - valid values are only {9,10,11,12} +#define RESOLUTION 12 + +//=========================== prototypes ====================================== +// DS18B20 +void init_DS18B20(uint64_t device_rom, uint8_t T_h, uint8_t T_l, uint8_t config); +uint16_t get_temp(uint64_t device_rom, int use_strong_pull_up); +#endif diff --git a/scm_v3c/scm3c_hw_interface.c b/scm_v3c/scm3c_hw_interface.c index 0f0863aa..8564d573 100644 --- a/scm_v3c/scm3c_hw_interface.c +++ b/scm_v3c/scm3c_hw_interface.c @@ -1165,7 +1165,7 @@ void initialize_mote() { GPO_enables(0xFFFF); // Set HCLK source as HF_CLOCK - set_asc_bit(1147); + set_asc_bit(1147);//**** // Set initial coarse/fine on HF_CLOCK // coarse 0:4 = 860 861 875b 876b 877b @@ -1174,13 +1174,15 @@ void initialize_mote() { scm3c_hw_interface_vars.HF_CLOCK_fine); // Set RFTimer source as HF_CLOCK - set_asc_bit(1151); + set_asc_bit(1151);//**** // Disable LF_CLOCK - set_asc_bit(553); + set_asc_bit(553);//***** // HF_CLOCK will be trimmed to 20MHz, so set RFTimer div value to 40 to get // 500kHz (inverted, so 1101 0111) + +// HF_clock div set_asc_bit(49); set_asc_bit(48); clear_asc_bit(47); From 8f2d154f34f34613c08bdbd5fcaeb13d6022ca68 Mon Sep 17 00:00:00 2001 From: mmontee <34085121+mmontee@users.noreply.github.com> Date: Fri, 19 Jul 2024 10:48:58 -0700 Subject: [PATCH 5/8] add GPIO config/Clean up Cleaned up debug and comments. Finished gpio config function --- scm_v3c/1_wire.c | 128 ++++++++++++++++++++-- scm_v3c/1_wire.h | 32 ++++-- scm_v3c/applications/1_wire/1_wire_test.c | 2 +- scm_v3c/applications/1_wire/DS18B20.h | 8 +- 4 files changed, 145 insertions(+), 25 deletions(-) diff --git a/scm_v3c/1_wire.c b/scm_v3c/1_wire.c index dfc20eb5..f4e24c3c 100644 --- a/scm_v3c/1_wire.c +++ b/scm_v3c/1_wire.c @@ -21,15 +21,127 @@ unsigned char crc8; // Routine to configure gpio after mote_init. // Taken from the mote_init routine just changed the GPI/O_enable values // GPO are set default, just turn on the RX GPI and turn off the GPO for the RX pin. -inline void OW_gpio_config(int rx) +void OW_gpio_config(int rx, int tx, int pull_up) { + // Set GP0(tx, pull_up) pin set to bank6 + int bank_n = 6; + if(tx < 4 || pull_up < 4) + { + for (int j = 0; j <= 3; j++) + { + if ((bank_n >> j) & 0x1) { + set_asc_bit(245 + j); + } else { + clear_asc_bit(245 + j); + } + } + } + else if((tx >= 4 && tx < 8) || (pull_up >= 4 && pull_up < 8)) + { + for (int j = 0; j <= 3; j++) + { + if ((bank_n >> j) & 0x1) { + set_asc_bit(249 + j); + } else { + clear_asc_bit(249 + j); + } + } + } + else if((tx >= 8 && tx < 12) || (pull_up >= 8 && pull_up < 12)) + { + for (int j = 0; j <= 3; j++) + { + if ((bank_n >> j) & 0x1) { + set_asc_bit(253 + j); + } else { + clear_asc_bit(253 + j); + } + } + } + else if((tx >= 12 && tx < 16) || (pull_up >= 12 && pull_up < 16)) + { + for (int j = 0; j <= 3; j++) + { + if ((bank_n >> j) & 0x1) { + set_asc_bit(257 + j); + } else { + clear_asc_bit(257 + j); + } + } + } + else if(tx >= 16 || pull_up >= 16) // out of bounds defaults to bank0 + { + for (int j = 0; j <= 3; j++) + { + if ((bank_n >> j) & 0x1) { + set_asc_bit(245 + j); + } else { + clear_asc_bit(245 + j); + } + } + } + + // Set GPI(rx) pin set to bank0 + bank_n = 0; + if(rx < 4) + { + for (int j = 0; j <= 3; j++) + { + if ((bank_n >> j) & 0x1) { + set_asc_bit(261 + j); + } else { + clear_asc_bit(261 + j); + } + } + } + else if(rx >= 4 && rx < 8) + { + for (int j = 0; j <= 3; j++) + { + if ((bank_n >> j) & 0x1) { + set_asc_bit(263 + j); + } else { + clear_asc_bit(263 + j); + } + } + } + else if(rx >= 8 && rx < 12) + { + for (int j = 0; j <= 3; j++) + { + if ((bank_n >> j) & 0x1) { + set_asc_bit(265 + j); + } else { + clear_asc_bit(265 + j); + } + } + } + else if(rx >= 12 && rx < 16) + { + for (int j = 0; j <= 3; j++) + { + if ((bank_n >> j) & 0x1) { + set_asc_bit(267 + j); + } else { + clear_asc_bit(267 + j); + } + } + } + else if(rx >= 16) // out of bounds defaults to bank0 + { + for (int j = 0; j <= 3; j++) + { + if ((bank_n >> j) & 0x1) { + set_asc_bit(261 + j); + } else { + clear_asc_bit(261 + j); + } + } + } + uint16_t gpi_mask = (0x01 << rx); uint16_t gpo_mask = (0x01 << rx); - // Select banks for GPIO inputs - GPI_control(0, 0, 0, 0); // 1 in 3rd arg connects GPI8 to EXT_INTERRUPT<1> - // Select banks for GPIO outputs - GPO_control(6, 6, 6, 6); // 0 in 3rd arg connects clk_3wb to GPO8 for 3WB cal // Set GPI enables GPI_enables(gpi_mask |= 0x0000); // enable GPIO 1 as RX for 1-wire printf("gpi mask = 0x%X\r\n", gpi_mask); @@ -143,7 +255,7 @@ void OWStore_rom(unsigned char* rom_bytes, bus_roms_list_prt_t base) } } -// Moves roms from linked list to array. +// Moves roms from linked list to array and frees the list. void OWGet_rom_array(uint64_t* array, bus_roms_root_prt_t root) { int cnt = root->item_count; @@ -153,9 +265,7 @@ void OWGet_rom_array(uint64_t* array, bus_roms_root_prt_t root) { bus_roms_list_prt_t prev_rom = rom_n; array[i] = rom_n->rom; - printf("Freeing element 0x%llX\r\n", rom_n->rom); - rom_n = rom_n->next; - + rom_n = rom_n->next; free(prev_rom); } } diff --git a/scm_v3c/1_wire.h b/scm_v3c/1_wire.h index db6a8564..14e87044 100644 --- a/scm_v3c/1_wire.h +++ b/scm_v3c/1_wire.h @@ -14,9 +14,9 @@ // Enable strong pull-up #define USE_STRONG_PULL 1 // 1 = true, 0 = false // GPIO pins are specified here. -#define TX_PIN 0 -#define RX_PIN 1 -#define STRONG_PULL_UP_PIN 2 +#define TX_PIN 5 +#define RX_PIN 6 +#define STRONG_PULL_UP_PIN 7 // End Congfig. defines *** // ROM commands. @@ -85,17 +85,25 @@ typedef struct rom_list{ } rom_list_t, *bus_roms_list_prt_t; //=========================== prototypes ====================================== -// User -bus_roms_root_prt_t OWSearch_bus(void);// Read all roms on bus, returns base to linked list -void OWGet_rom_array(uint64_t* array, bus_roms_root_prt_t root);// Create array of ROMs from linked list -void OWWrite_rom(uint64_t device_rom); // Write rom to bus -int OWIsolate_device(uint64_t device_rom); // Isolate a device -uint8_t OWRead_byte(void);// Reads a byte from bus -void OWRead_bytes(uint8_t* buffer, int size);// Reads n bytes from bus -int OWCRC_bytes(uint8_t* byte_array, int size); +// User function +// Configures SCuM I/O for bus, set up banks, leaves all pins except RX at outputs. +void OW_gpio_config(int rx, int tx, int pull_up); +// Read all roms on bus, returns base to linked list. +bus_roms_root_prt_t OWSearch_bus(void); +// Create an array of ROMs from linked list. +void OWGet_rom_array(uint64_t* array, bus_roms_root_prt_t root); + // Write a rom to bus. +void OWWrite_rom(uint64_t device_rom); +// Isolate a device by reseting, geting presence and writing the ROM. +int OWIsolate_device(uint64_t device_rom); +// Reads a byte from bus. +uint8_t OWRead_byte(void); +// Reads n bytes from bus. +void OWRead_bytes(uint8_t* buffer, int size); +// Returns the CRC8 of a series of bytes. +int OWCRC_bytes(uint8_t* byte_array, int size); // Driver -void OW_gpio_config(int tx); void OWStore_rom(unsigned char* rom_bytes, bus_roms_list_prt_t base); void OWWriteBit(uint8_t bit); uint8_t OWReadBit(void); diff --git a/scm_v3c/applications/1_wire/1_wire_test.c b/scm_v3c/applications/1_wire/1_wire_test.c index 3c29b9ad..aab262f1 100644 --- a/scm_v3c/applications/1_wire/1_wire_test.c +++ b/scm_v3c/applications/1_wire/1_wire_test.c @@ -49,7 +49,7 @@ int main(void) { Declare array with List root member "item_count" elements. Pass both the root and the array to OWGet_rom_array tranfer() moves the list into an array. */ - OW_gpio_config(RX_PIN); // config for GPIO_0 = TX, GPIO_1 = RX, and GPIO_2 = pull-up + OW_gpio_config(RX_PIN, TX_PIN, STRONG_PULL_UP_PIN); // config for GPIO_0 = TX, GPIO_1 = RX, and GPIO_2 = pull-up bus_roms_root_prt_t rom_list_root; //Declaring List root. if((rom_list_root = OWSearch_bus()) == NULL)// Try to get the ROMs of all devices { diff --git a/scm_v3c/applications/1_wire/DS18B20.h b/scm_v3c/applications/1_wire/DS18B20.h index a6ad94f7..2a15bd22 100644 --- a/scm_v3c/applications/1_wire/DS18B20.h +++ b/scm_v3c/applications/1_wire/DS18B20.h @@ -7,6 +7,11 @@ #include "memory_map.h" //=========================== defines ========================================= +// *** Congfig. defines +// DS18B20 resolution - valid values are only {9,10,11,12} +#define RESOLUTION 12 +// End Congfig. defines *** + #define READ_SCRATCH 0xBE #define WRITE_SCRATCH 0x4E #define COPY_SCRATCH 0x48 @@ -17,9 +22,6 @@ #define T_CONV_10 188 #define T_CONV_11 375 #define T_CONV_12 750 -// DS18B20 resolution - valid values are only {9,10,11,12} -#define RESOLUTION 12 - //=========================== prototypes ====================================== // DS18B20 void init_DS18B20(uint64_t device_rom, uint8_t T_h, uint8_t T_l, uint8_t config); From 71fa0ed837f57d0c15f1f14346b2a5287d051a59 Mon Sep 17 00:00:00 2001 From: mmontee <34085121+mmontee@users.noreply.github.com> Date: Fri, 19 Jul 2024 12:20:52 -0700 Subject: [PATCH 6/8] add bus schematic --- scm_v3c/applications/1_wire/Bus schematic.png | Bin 0 -> 31301 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 scm_v3c/applications/1_wire/Bus schematic.png diff --git a/scm_v3c/applications/1_wire/Bus schematic.png b/scm_v3c/applications/1_wire/Bus schematic.png new file mode 100644 index 0000000000000000000000000000000000000000..8319a42385f8cdb7b9cda552abbc2bcc639fde10 GIT binary patch literal 31301 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 literal 0 HcmV?d00001 From bfe796587a4e0a77c29443dd56b03a439d3b65c4 Mon Sep 17 00:00:00 2001 From: mmontee <34085121+mmontee@users.noreply.github.com> Date: Fri, 19 Jul 2024 23:47:28 -0700 Subject: [PATCH 7/8] Fix schematic --- scm_v3c/applications/1_wire/Bus schematic.png | Bin 31301 -> 19431 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/scm_v3c/applications/1_wire/Bus schematic.png b/scm_v3c/applications/1_wire/Bus schematic.png index 8319a42385f8cdb7b9cda552abbc2bcc639fde10..75fd899b7e26ee6efc459ff8f22d30a84acf1748 100644 GIT binary patch literal 19431 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 Tm*4@vN65=4KP;7c_U3;8zRW#{ literal 31301 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 From 87a3d7d50813557eeca11d85c0632d15137bad90 Mon Sep 17 00:00:00 2001 From: mmontee <34085121+mmontee@users.noreply.github.com> Date: Mon, 9 Sep 2024 15:19:06 -0700 Subject: [PATCH 8/8] HF_CLOCK timing changes --- 1_wire.h | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 1_wire.h diff --git a/1_wire.h b/1_wire.h new file mode 100644 index 00000000..ae29de0b --- /dev/null +++ b/1_wire.h @@ -0,0 +1,119 @@ +#ifndef __1_WIRE_H +#define __1_WIRE_H + +#include +#include +#include +#include +#include + +//=========================== defines ========================================= +// *** Congfig. defines +// Define the HCLK source. +#define HF_CLOCK // HF_CLOCK or LF_CLOCK +// Enable strong pull-up +#define USE_STRONG_PULL 1 // 1 = true, 0 = false +// GPIO pins are specified here. +#define TX_PIN 0 +#define RX_PIN 1 +#define STRONG_PULL_UP_PIN 7 +// End Congfig. defines *** + +// ROM commands. +#define READROM 0x33 +#define SKIPROM 0xCC +#define MATCHROM 0x55 +#define SEARCHROM 0xF0 +//=================================== +// 1-wire search rom, code taken from; +// https://www.analog.com/en/resources/app-notes/1wire-search-algorithm.html +#define FALSE 0 +#define TRUE 1 +//=================================== + +// Function Macros +#define BUS_LOW() GPIO_REG__OUTPUT |= (1 << TX_PIN); // Pulls bus LOW +#define BUS_RELEASE() GPIO_REG__OUTPUT &= ~(1 << TX_PIN); // Release bus to Pull-up +#define BUS_READ() (GPIO_REG__INPUT &= (1 << RX_PIN)) ? 1 : 0; // Read the logic level of the bus. +#define STRONG_PULL_UP_OFF() GPIO_REG__OUTPUT |= (1 << STRONG_PULL_UP_PIN); +#define STRONG_PULL_UP_ON() GPIO_REG__OUTPUT &= ~(1 << STRONG_PULL_UP_PIN); +// NOP Macros for us timing +#define NOP_5() __asm("NOP\n\t" "NOP\n\t" "NOP\n\t" "NOP\n\t" "NOP\n\t"); //~1us +#define NOP_10() {NOP_5(); NOP_5();} // 2us +#define NOP_50() {NOP_10(); NOP_10(); NOP_10(); NOP_10(); NOP_10();} // 10us +#define NOP_100() {NOP_50(); NOP_50()} // 20 us +#define NOP_500() {NOP_100(); NOP_100(); NOP_100(); NOP_100(); NOP_100();} // 100us + +#ifdef HF_CLOCK +//Delay Macros for 1-wire timing HF_clock +#define DELAY_A {NOP_10(); NOP_10(); NOP_10();}//6us +#define DELAY_B {NOP_100(); NOP_100(); NOP_100(); NOP_10(); NOP_10();}//64 +#define DELAY_C {NOP_100(); NOP_100(); NOP_100();}//60 +#define DELAY_D {NOP_50();}//10 +#define DELAY_E {NOP_10(); NOP_10(); NOP_10(); NOP_10(); NOP_5();}//9 +#define DELAY_F {NOP_100(); NOP_100(); NOP_50();}//55 +#define DELAY_G {}//0 +#define DELAY_H {NOP_500(); NOP_500(); NOP_500(); NOP_500(); NOP_500(); NOP_100(); NOP_100(); NOP_100(); NOP_100();}//480 +#define DELAY_I {NOP_100(); NOP_100(); NOP_100(); NOP_50();}//70 +#define DELAY_J {NOP_500(); NOP_500(); NOP_500(); NOP_500(); NOP_50();}//410 +#endif +#ifdef LF_CLOCK +// Delay Macros for 1-wire timing LF_clock LF = HF * 2/3 +#define DELAY_A { NOP_10(); NOP_10();}//6us * 0.7 = 4.2 = 4 +#define DELAY_B {NOP_100(); NOP_100(); NOP_10(); NOP_10(); NOP_5();}//64 * 0.7 = 44.8 = 45 +#define DELAY_C {NOP_100(); NOP_100(); NOP_10();}//60 * 0.7 = 42 +#define DELAY_D {NOP_10(); NOP_10(); NOP_10(); NOP_5();}//10 * 0.7 = 7 +#define DELAY_E {NOP_10(); NOP_10(); NOP_10();}//9 * 0.7 = 6.3 = 6 +#define DELAY_F {NOP_100(); NOP_50(); NOP_10(); NOP_10(); NOP_10(); NOP_10(); NOP_5();}//55 * 0.7 = 38.5 = 39 +#define DELAY_G {}//0 +#define DELAY_H {NOP_500(); NOP_500(); NOP_500(); NOP_100(); NOP_50(); NOP_10(); NOP_10(); NOP_10();}//480 * 0.7 = 336 +#define DELAY_I {NOP_100(); NOP_100(); NOP_10(); NOP_10(); NOP_10(); NOP_10(); NOP_5();}//70 * 0.7 = 49 +#define DELAY_J {NOP_500(); NOP_500(); NOP_100(); NOP_100(); NOP_100(); NOP_100(); NOP_10(); NOP_10(); NOP_10(); NOP_5();}//410 * 0.7 = 287 +#endif +//=========================== variables ======================================= +// ROMs found on the bus are stored in a linked list structure. Where the root contains the +// pointed to the first node(next) and the total number of nodes(item_count). Each node stores +// an individual ROM(rom) and the pointer to the next(next). +typedef struct rom_list_root{ + uint32_t item_count; + struct rom_list* next; +} rom_list_root_t, *bus_roms_root_prt_t; + +typedef struct rom_list{ + uint64_t rom; + struct rom_list* next; +} rom_list_t, *bus_roms_list_prt_t; + +//=========================== prototypes ====================================== +// User function +// Configures SCuM I/O for bus, set up banks, leaves all pins except RX at outputs. +void OW_gpio_config(int rx, int tx, int pull_up); +// Read all roms on bus, returns base to linked list. +bus_roms_root_prt_t OWSearch_bus(void); +// Create an array of ROMs from linked list. +void OWGet_rom_array(uint64_t* array, bus_roms_root_prt_t root); + // Write a rom to bus. +void OWWrite_rom(uint64_t device_rom); +// Isolate a device by reseting, geting presence and writing the ROM. +int OWIsolate_device(uint64_t device_rom); +// Reads a byte from bus. +uint8_t OWRead_byte(void); +// Reads n bytes from bus. +void OWRead_bytes(uint8_t* buffer, int size); +// Returns the CRC8 of a series of bytes. +int OWCRC_bytes(uint8_t* byte_array, int size); + +// Driver +void OWStore_rom(unsigned char* rom_bytes, bus_roms_list_prt_t base); +void OWWriteBit(uint8_t bit); +uint8_t OWReadBit(void); +int OWReset(void); +void OWWriteByte(uint8_t byte); +//=================================== +// 1-wire search rom, code taken from; +// https://www.analog.com/en/resources/app-notes/1wire-search-algorithm.html +int OWFirst(void); +int OWNext(void); +int OWSearch(void); +unsigned char docrc8(unsigned char value); +#endif