From d3b43c31d20148e4e6905e237773fb4d953aac48 Mon Sep 17 00:00:00 2001 From: Dmitry Ermakov Date: Fri, 10 Jan 2025 01:02:10 +0300 Subject: [PATCH] Add w1-ds18b20 (#1659) --- general/package/w1-ds18b20/Config.in | 4 + general/package/w1-ds18b20/readme.md | 13 + general/package/w1-ds18b20/src/w1-ds18b20.c | 631 ++++++++++++++++++++ general/package/w1-ds18b20/w1-ds18b20.mk | 21 + 4 files changed, 669 insertions(+) create mode 100644 general/package/w1-ds18b20/Config.in create mode 100644 general/package/w1-ds18b20/readme.md create mode 100644 general/package/w1-ds18b20/src/w1-ds18b20.c create mode 100644 general/package/w1-ds18b20/w1-ds18b20.mk diff --git a/general/package/w1-ds18b20/Config.in b/general/package/w1-ds18b20/Config.in new file mode 100644 index 000000000..9d4e241cc --- /dev/null +++ b/general/package/w1-ds18b20/Config.in @@ -0,0 +1,4 @@ +config BR2_PACKAGE_W1-DS18B20 + bool "w1-ds18b20" + help + OneWire DS18B20 GPIO bitbang app diff --git a/general/package/w1-ds18b20/readme.md b/general/package/w1-ds18b20/readme.md new file mode 100644 index 000000000..a36dfb53b --- /dev/null +++ b/general/package/w1-ds18b20/readme.md @@ -0,0 +1,13 @@ +# w1-ds18b20 + +This app allows reading ds18b20 sensors in bitbang mode using direct GPIO memory access. +Currently supports only HiSilicon-style GPIO offsets. Pull-up resistor is required. + +## Usage + +Example: reading temperature using GPIO8_2 on Gk7205V300 (consult datasheet or ipctool output) + +``` +# ./w1-ds18b20 -base 0x120B8000 -gpio 2 +28-4A4E3C1E64FF : 22.44 C +``` diff --git a/general/package/w1-ds18b20/src/w1-ds18b20.c b/general/package/w1-ds18b20/src/w1-ds18b20.c new file mode 100644 index 000000000..79c3df729 --- /dev/null +++ b/general/package/w1-ds18b20/src/w1-ds18b20.c @@ -0,0 +1,631 @@ +// Based on https://github.com/danjperron/BitBangingDS18B20.git + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int mem_fd; +void *gpio_map; + +unsigned short DS_PIN = 7; +unsigned int GPIO_BASE = 0x120b6000; +unsigned short ArgResolution = 0; +unsigned short ArgScan = 0; +unsigned short ArgWaitTime = 750; + +// I/O access +volatile unsigned *gpio; + + +#define BLOCK_SIZE (4 * 1024) + +#define OUT_GPIO(g) *(gpio + 0x400 / sizeof(*gpio)) |= (1 << g) +#define INP_GPIO(g) *(gpio + 0x400 / sizeof(*gpio)) &= ~(1 << g) + +#define GPIO_SET(n) (*(gpio + 0x3FC / sizeof(*gpio)) |= (1 << (n))) +#define GPIO_CLR(n) (*(gpio + 0x3FC / sizeof(*gpio)) &= ~(1 << (n))) +#define GPIO_READ(g) (((*(gpio + 0x3FC / sizeof(*gpio))) & (1 << (g))) >> (g)) + +#define DS18B20_SKIP_ROM 0xCC +#define DS18B20_CONVERT_T 0x44 +#define DS18B20_MATCH_ROM 0x55 +#define DS18B20_SEARCH_ROM 0XF0 +#define DS18B20_READ_SCRATCHPAD 0xBE +#define DS18B20_WRITE_SCRATCHPAD 0x4E +#define DS18B20_COPY_SCRATCHPAD 0x48 + +unsigned char ScratchPad[9]; +double temperature; +int resolution; + +void setup_io(); +#define DELAY1US DelayMicrosecondsNoSleep(1); + +void DelayMicrosecondsNoSleep(int delay_us) { + long int start_time; + long int time_difference; + struct timespec gettime_now; + + clock_gettime(CLOCK_REALTIME, &gettime_now); + start_time = gettime_now.tv_nsec; // Get nS value + while (1) { + clock_gettime(CLOCK_REALTIME, &gettime_now); + time_difference = gettime_now.tv_nsec - start_time; + if (time_difference < 0) + time_difference += 1000000000; //(Rolls over every 1 second) + if (time_difference > (delay_us * 1000)) // Delay for # nS + break; + } +} + +int DoReset(void) { + INP_GPIO(DS_PIN); + + DelayMicrosecondsNoSleep(10); + + INP_GPIO(DS_PIN); + OUT_GPIO(DS_PIN); + + // pin low for 480 us + GPIO_CLR(DS_PIN); + usleep(480); + INP_GPIO(DS_PIN); + DelayMicrosecondsNoSleep(60); + if (GPIO_READ(DS_PIN) == 0) { + DelayMicrosecondsNoSleep(420); + return 1; + } + return 0; +} + +void WriteByte(unsigned char value) { + unsigned char Mask = 1; + int loop; + + for (loop = 0; loop < 8; loop++) { + INP_GPIO(DS_PIN); + OUT_GPIO(DS_PIN); + GPIO_CLR(DS_PIN); + + if ((value & Mask) != 0) { + DELAY1US + INP_GPIO(DS_PIN); + DelayMicrosecondsNoSleep(60); + + } else { + DelayMicrosecondsNoSleep(60); + INP_GPIO(DS_PIN); + DelayMicrosecondsNoSleep(1); + } + Mask *= 2; + DelayMicrosecondsNoSleep(60); + } + + usleep(100); +} + +void WriteBit(unsigned char value) { + INP_GPIO(DS_PIN); + OUT_GPIO(DS_PIN); + GPIO_CLR(DS_PIN); + if (value) { + DELAY1US + INP_GPIO(DS_PIN); + DelayMicrosecondsNoSleep(60); + } else { + DelayMicrosecondsNoSleep(60); + INP_GPIO(DS_PIN); + DelayMicrosecondsNoSleep(1); + } + DelayMicrosecondsNoSleep(60); +} + +unsigned char ReadBit(void) { + unsigned char rvalue = 0; + INP_GPIO(DS_PIN); + OUT_GPIO(DS_PIN); + GPIO_CLR(DS_PIN); + DELAY1US + INP_GPIO(DS_PIN); + DelayMicrosecondsNoSleep(2); + if (GPIO_READ(DS_PIN) != 0) + rvalue = 1; + DelayMicrosecondsNoSleep(60); + return rvalue; +} + +unsigned char ReadByte(void) { + + unsigned char Mask = 1; + int loop; + unsigned char data = 0; + + for (loop = 0; loop < 8; loop++) { + INP_GPIO(DS_PIN); + OUT_GPIO(DS_PIN); + GPIO_CLR(DS_PIN); + DELAY1US + INP_GPIO(DS_PIN); + // Wait 2 us + DelayMicrosecondsNoSleep(2); + if (GPIO_READ(DS_PIN) != 0) + data |= Mask; + Mask *= 2; + DelayMicrosecondsNoSleep(60); + } + + return data; +} + +int ReadScratchPad(void) { + int loop; + + WriteByte(DS18B20_READ_SCRATCHPAD); + for (loop = 0; loop < 9; loop++) { + ScratchPad[loop] = ReadByte(); + } + return 1; +} + +unsigned char CalcCRC(unsigned char *data, unsigned char byteSize) { + unsigned char shift_register = 0; + unsigned char loop, loop2; + char DataByte; + + for (loop = 0; loop < byteSize; loop++) { + DataByte = *(data + loop); + for (loop2 = 0; loop2 < 8; loop2++) { + if ((shift_register ^ DataByte) & 1) { + shift_register = shift_register >> 1; + shift_register ^= 0x8C; + } else + shift_register = shift_register >> 1; + DataByte = DataByte >> 1; + } + } + return shift_register; +} + +char IDGetBit(unsigned long long *llvalue, char bit) { + unsigned long long Mask = 1ULL << bit; + + return ((*llvalue & Mask) ? 1 : 0); +} + +unsigned long long IDSetBit(unsigned long long *llvalue, char bit, + unsigned char newValue) { + unsigned long long Mask = 1ULL << bit; + + if ((bit >= 0) && (bit < 64)) { + if (newValue == 0) + *llvalue &= ~Mask; + else + *llvalue |= Mask; + } + return *llvalue; +} + +void SelectSensor(unsigned long long ID) { + int BitIndex; + + WriteByte(DS18B20_MATCH_ROM); + + for (BitIndex = 0; BitIndex < 64; BitIndex++) + WriteBit(IDGetBit(&ID, BitIndex)); +} + +int SearchSensor(unsigned long long *ID, int *LastBitChange) { + int BitIndex; + char Bit, NoBit; + + if (*LastBitChange < 0) + return 0; + + // Set bit at LastBitChange Position to 1 + // Every bit after LastbitChange will be 0 + + if (*LastBitChange < 64) { + + IDSetBit(ID, *LastBitChange, 1); + for (BitIndex = *LastBitChange + 1; BitIndex < 64; BitIndex++) + IDSetBit(ID, BitIndex, 0); + } + + *LastBitChange = -1; + + if (!DoReset()) + return -1; + + WriteByte(DS18B20_SEARCH_ROM); + + for (BitIndex = 0; BitIndex < 64; BitIndex++) { + + NoBit = ReadBit(); + Bit = ReadBit(); + + if (Bit && NoBit) + return -2; + + if (!Bit && !NoBit) { + // ok 2 possibilities + if (IDGetBit(ID, BitIndex)) { + // Bit High already set + WriteBit(1); + } else { + // ok let's try LOW value first + *LastBitChange = BitIndex; + WriteBit(0); + } + } else if (!Bit) { + WriteBit(1); + IDSetBit(ID, BitIndex, 1); + } else { + WriteBit(0); + IDSetBit(ID, BitIndex, 0); + } + } + + return 1; +} + +int ReadSensor(unsigned long long ID) { + int RetryCount; + unsigned char CRCByte; + union { + short SHORT; + unsigned char CHAR[2]; + } IntTemp; + + temperature = -9999.9; + + for (RetryCount = 0; RetryCount < 10; RetryCount++) { + + if (!DoReset()) + continue; + + // start a conversion + SelectSensor(ID); + + if (!ReadScratchPad()) + continue; + + // OK Check sum Check; + CRCByte = CalcCRC(ScratchPad, 8); + + if (CRCByte != ScratchPad[8]) + continue; + + // Check Resolution + resolution = 0; + switch (ScratchPad[4]) { + + case 0x1f: + resolution = 9; + break; + case 0x3f: + resolution = 10; + break; + case 0x5f: + resolution = 11; + break; + case 0x7f: + resolution = 12; + break; + } + + if (resolution == 0) + continue; + // Read Temperature + IntTemp.CHAR[0] = ScratchPad[0]; + IntTemp.CHAR[1] = ScratchPad[1]; + + temperature = 0.0625 * (double)IntTemp.SHORT; + + ID &= 0x00FFFFFFFFFFFFFFULL; + printf("%02llX-%012llX : ", ID & 0xFFULL, ID >> 8); + + // printf("%02d bits Temperature: %6.2f +/- %4.2f C\n", resolution, + // temperature, 0.0625 * (double)(1 << (12 - resolution))); + printf("%6.2f C\n", temperature); + + return 1; + } + + return 0; +} + +int GlobalStartConversion(void) { + int retry = 0; + int maxloop; + + while (retry < 10) { + if (!DoReset()) + usleep(10000); + else { + WriteByte(DS18B20_SKIP_ROM); + WriteByte(DS18B20_CONVERT_T); + maxloop = 0; + +#define USE_CONSTANT_DELAY +#ifdef USE_CONSTANT_DELAY + usleep(ArgWaitTime * 1000); + return 1; +#else + // wait until ready + while (!ReadBit()) { + maxloop++; + if (maxloop > 100000) + break; + } + + if (maxloop <= 100000) + return 1; +#endif + } + retry++; + } + return 0; +} + +void WriteScratchPad(unsigned char TH, unsigned char TL, unsigned char config) { + + // First reset device + DoReset(); + + usleep(10); + // Skip ROM command + WriteByte(DS18B20_SKIP_ROM); + + // Write Scratch pad + WriteByte(DS18B20_WRITE_SCRATCHPAD); + + // Write TH + WriteByte(TH); + + // Write TL + WriteByte(TL); + + // Write config + WriteByte(config); +} + +void CopyScratchPad(void) { + + // Reset device + DoReset(); + usleep(1000); + + // Skip ROM Command + WriteByte(DS18B20_SKIP_ROM); + + // copy scratch pad + WriteByte(DS18B20_COPY_SCRATCHPAD); + usleep(100000); +} + +void ChangeSensorsResolution(int resolution) { + int config = 0; + + switch (resolution) { + case 9: + config = 0x1f; + break; + case 10: + config = 0x3f; + break; + case 11: + config = 0x5f; + break; + default: + config = 0x7f; + break; + } + WriteScratchPad(0xff, 0xff, config); + usleep(1000); + CopyScratchPad(); +} + +void ScanForSensor(void) { + unsigned long long ID = 0ULL; + int NextBit = 64; + int _NextBit; + int rcode; + int retry = 0; + unsigned long long _ID; + unsigned char _ID_CRC; + unsigned char _ID_Calc_CRC; + // unsigned char _ID_Family; + + while (retry < 10) { + _ID = ID; + _NextBit = NextBit; + rcode = SearchSensor(&_ID, &_NextBit); + if (rcode == 1) { + _ID_CRC = (unsigned char)(_ID >> 56); + _ID_Calc_CRC = CalcCRC((unsigned char *)&_ID, 7); + if (_ID_CRC == _ID_Calc_CRC) { + if (ArgScan == 0) { + if (ReadSensor(_ID)) { + ID = _ID; + NextBit = _NextBit; + retry = 0; + } else + retry = 0; + } else { + ID = _ID; + NextBit = _NextBit; + printf("%016llX\n", ID); + } + } else + retry++; + } else if (rcode == 0) + break; + else + retry++; + } +} + +void PrintUsage(char *app) { + fprintf(stderr, "usage :\n\n\t"); + fprintf(stderr, "%s -base 0x -gpio n [-xbits] [-s] [-t delay]\n\n", app); + fprintf(stderr, " -base 0xn -> n specify the GPIO group base address\n"); + fprintf(stderr, + " -gpio n -> n specify the GPIO number in group [0-7]\n"); + fprintf( + stderr, + " -xbits -> x set the number of bits -9bits,-10bits,-11bits and " + "-12bits\n"); + fprintf( + stderr, + " -t delay -> delay is the time in ms to wait after conversion\n"); + fprintf(stderr, " -s -> Scan for sensor\n"); +} + +int DecodeArg(int argc, char **argv) { + + int idx = 1; + + if (argc == 1) { + PrintUsage(argv[0]); + return 0; + } + + while (idx < argc) { + if (strstr(argv[idx], "help") != NULL) { + PrintUsage(argv[0]); + return 0; + } + if (strcmp(argv[idx], "-base") == 0) + sscanf(argv[++idx], "%x", &GPIO_BASE); + else if (strcmp(argv[idx], "-gpio") == 0) + DS_PIN = atoi(argv[++idx]); + else if (strcmp(argv[idx], "-9bits") == 0) + ArgResolution = 9; + else if (strcmp(argv[idx], "-10bits") == 0) + ArgResolution = 10; + else if (strcmp(argv[idx], "-11bits") == 0) + ArgResolution = 11; + else if (strcmp(argv[idx], "-12bits") == 0) + ArgResolution = 12; + else if (strcmp(argv[idx], "-s") == 0) + ArgScan = 1; + else if (strcmp(argv[idx], "-t") == 0) + ArgWaitTime = atoi(argv[++idx]); + else { + fprintf(stderr, "Unknown argument %s! ", argv[idx]); + exit(0); + } + idx++; + } + return 1; +} + +void set_max_priority(void) { + struct sched_param sched; + memset(&sched, 0, sizeof(sched)); + // Use FIFO scheduler with highest priority for the lowest chance of the + // kernel context switching. + sched.sched_priority = sched_get_priority_max(SCHED_FIFO); + sched_setscheduler(0, SCHED_FIFO, &sched); +} + +void set_default_priority(void) { + struct sched_param sched; + memset(&sched, 0, sizeof(sched)); + // Go back to default scheduler with default 0 priority. + sched.sched_priority = 0; + sched_setscheduler(0, SCHED_OTHER, &sched); +} + +int main(int argc, char **argv) { + int loop; + int Flag = 0; + + if (DecodeArg(argc, argv) == 0) + return 0; + + setup_io(); + + // Check for pull up resistor + // Signal input should be high + + // Set PIN to INPUT MODE + INP_GPIO(DS_PIN); + + Flag = 0; + for (loop = 0; loop < 100; loop++) { + usleep(1000); + if (GPIO_READ(DS_PIN) != 0) { + Flag = 1; + break; + } + } + + if (Flag == 0) { + fprintf(stderr, "*** Error Unable to detect HIGH level. No pull-up resistor ?\n"); + exit(-1); + } + + if (ArgResolution > 0) { + // need to change resolution + ChangeSensorsResolution(ArgResolution); + // do it twice just in case + ChangeSensorsResolution(ArgResolution); + } + + if (GlobalStartConversion() == 0) { + fprintf(stderr, "*** Error Unable to detect any DS18B20 sensor\n"); + exit(-2); + } + + set_max_priority(); + + ScanForSensor(); + + set_default_priority(); + + return 0; + +} + +void setup_io() { +#ifdef USE_GPIOLIB + +#else + /* open /dev/mem */ + if ((mem_fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) { + fprintf(stderr, "can't open /dev/mem \n"); + exit(-1); + } + /* mmap GPIO */ + gpio_map = + mmap(NULL, // Any adddress in our space will do + BLOCK_SIZE, // Map length + PROT_READ | PROT_WRITE, // Enable reading & writting to mapped memory + MAP_SHARED, // Shared with other processes + mem_fd, // File to map + GPIO_BASE // Offset to GPIO peripheral + ); +#endif + +#ifdef USE_GPIOLIB + +#else + close(mem_fd); // No need to keep mem_fd open after mmap + + if (gpio_map == MAP_FAILED) { + fprintf(stderr, "mmap error gpio_map=%p\n", gpio_map); // errno also set! + exit(-1); + } + + // Always use volatile pointer! + gpio = (volatile unsigned *)gpio_map; +#endif + +} diff --git a/general/package/w1-ds18b20/w1-ds18b20.mk b/general/package/w1-ds18b20/w1-ds18b20.mk new file mode 100644 index 000000000..4372b44e1 --- /dev/null +++ b/general/package/w1-ds18b20/w1-ds18b20.mk @@ -0,0 +1,21 @@ +################################################################################ +# +# w1-ds18b20 +# +################################################################################ + +W1-DS18B20_SITE_METHOD = local +W1-DS18B20_SITE = $(W1-DS18B20_PKGDIR)/src + +W1-DS18B20_LICENSE = MIT +W1-DS18B20_LICENSE_FILES = LICENSE + +define W1-DS18B20_BUILD_CMDS + $(TARGET_CC) $(@D)/w1-ds18b20.c -o $(@D)/w1-ds18b20 -Os -s +endef + +define W1-DS18B20_INSTALL_TARGET_CMDS + $(INSTALL) -m 0755 -t $(TARGET_DIR)/usr/bin $(@D)/w1-ds18b20 +endef + +$(eval $(generic-package))