From: Manfred Steiner Date: Sat, 3 Aug 2024 09:22:22 +0000 (+0200) Subject: ... X-Git-Url: https://git.htl-mechatronik.at/public/?a=commitdiff_plain;h=070fc42ad57d23ab87979463af5665c7ba93d1b4;p=nano-x-base.git ... --- diff --git a/software/arduino-nano-5v/test_2024-07-23/.gdb_history b/software/arduino-nano-5v/test_2024-07-23/.gdb_history new file mode 100644 index 0000000..3339046 --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/.gdb_history @@ -0,0 +1,9 @@ +target remote :1234 +layout split +stepi +quit +target remote :1234 +layout split +stepi +b *main+9 +quit diff --git a/software/arduino-nano-5v/test_2024-07-23/.gdbinit b/software/arduino-nano-5v/test_2024-07-23/.gdbinit new file mode 100644 index 0000000..139597f --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/.gdbinit @@ -0,0 +1,2 @@ + + diff --git a/software/arduino-nano-5v/test_2024-07-23/.gitignore b/software/arduino-nano-5v/test_2024-07-23/.gitignore new file mode 100644 index 0000000..a959910 --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/.gitignore @@ -0,0 +1,4 @@ +.depend +**/build +**/dist +**/sim diff --git a/software/arduino-nano-5v/test_2024-07-23/.vscode/c_cpp_properties.json b/software/arduino-nano-5v/test_2024-07-23/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..93c17af --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "Linux AVR", + "includePath": [ + "/usr/lib/avr/include/**", + "/usr/lib/gcc/avr/**" + ], + "defines": [], + "compilerPath": "/usr/bin/avr-gcc", + "compilerArgs": [ "-mmcu=atmega328p", "-DF_CPU=16000000", "-Os" ], + "cStandard": "gnu11", + "cppStandard": "gnu++11", + "intelliSenseMode": "linux-gcc-x64" + } + ], + "version": 4 +} diff --git a/software/arduino-nano-5v/test_2024-07-23/.vscode/launch.json b/software/arduino-nano-5v/test_2024-07-23/.vscode/launch.json new file mode 100644 index 0000000..f29cf2e --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/.vscode/launch.json @@ -0,0 +1,37 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Build", + // "request": "launch", + "type": "node-terminal", + "preLaunchTask": "build" + },{ + "name": "Flash", + // "request": "launch", + "type": "node-terminal", + "preLaunchTask": "flash" + },{ + "name": "Clean", + // "request": "launch", + "type": "node-terminal", + "preLaunchTask": "clean" + },{ + // es muss mit simuc --board arduino dist/programm.elf der Simulator + // gestartet werden. Dessen gdb-stub öffnet auf localhost:1234 einen Port + "name": "Debug (simuc)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/sim/atmega328p.elf", + "cwd": "${workspaceFolder}", + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/avr-gdb", + "miDebuggerServerAddress": ":1234", + "preLaunchTask": "build" + } + ] +} diff --git a/software/arduino-nano-5v/test_2024-07-23/.vscode/settings.json b/software/arduino-nano-5v/test_2024-07-23/.vscode/settings.json new file mode 100644 index 0000000..b2e94c9 --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/.vscode/settings.json @@ -0,0 +1,26 @@ +{ + "[c]": { + "editor.insertSpaces": true, + "editor.tabSize": 3, + "editor.detectIndentation": false + }, + "[cpp]": { + "editor.insertSpaces": true, + "editor.tabSize": 3, + "editor.detectIndentation": false + }, + "[h]": { + "editor.insertSpaces": true, + "editor.tabSize": 3, + "editor.detectIndentation": false + }, + "[hpp]": { + "editor.insertSpaces": true, + "editor.tabSize": 3, + "editor.detectIndentation": false + }, + "cSpell.words": [], + "cSpell.ignorePaths": [ + "**/*.json", "**/*.c", "**/*.h", "**/*.cpp", "**/*.hpp", "**/Makefile" + ] +} diff --git a/software/arduino-nano-5v/test_2024-07-23/.vscode/tasks.json b/software/arduino-nano-5v/test_2024-07-23/.vscode/tasks.json new file mode 100644 index 0000000..74fb1c7 --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/.vscode/tasks.json @@ -0,0 +1,23 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [{ + "label": "build", + "type": "shell", + "command": "make", + "problemMatcher":[ + "$gcc" + ] + },{ + "label": "clean", + "type": "shell", + "command": "make", + "args": [ "clean" ], + },{ + "label": "flash", + "type": "shell", + "command": "make", + "args": [ "flash" ], + }] +} \ No newline at end of file diff --git a/software/arduino-nano-5v/test_2024-07-23/Makefile b/software/arduino-nano-5v/test_2024-07-23/Makefile new file mode 100644 index 0000000..aa7ba13 --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/Makefile @@ -0,0 +1,82 @@ +.PHONY: all info flash picocom clean +$(shell mkdir -p dist >/dev/null) +$(shell mkdir -p build >/dev/null) +$(shell mkdir -p sim >/dev/null) + +NAME="test_2024-07-23_nano-5v" +SRC= $(wildcard src/*.c src/*.cpp src/*/*.cpp) +OBJ = $(SRC:src/%.c=build/%.o) +OBJ_SIM = $(SRC:src/%.c=sim/%.o) + +DEVICE=atmega328p + +CC= avr-g++ +CFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=16000000 -c +LFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=16000000 -Wl,-u,vfprintf -lprintf_flt -lm + +CFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=16000000 -g -c +LFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=16000000 -g -Wl,-u,vfprintf -lprintf_flt -lm + + +all: dist/$(NAME).elf dist/$(NAME).s dist/$(NAME).hex sim/$(NAME).elf sim/$(NAME).s info + +info: + @echo + @avr-size --mcu=$(DEVICE) --format=avr dist/$(NAME).elf + +.depend: $(SRC) + $(CC) -MM $(SRC) > .depend + +-include $(DEPENDFILE) + +dist/$(NAME).elf: .depend $(OBJ) + $(CC) $(LFLAGS) -o $@ $(OBJ) + +dist/%.s: dist/%.elf + avr-objdump -d $< > $@ + +dist/%.hex: dist/%.elf + avr-objcopy -O ihex $(HEX_FLASH_FLAGS) $< $@ + +sim/$(NAME).elf: .depend $(OBJ_SIM) + $(CC) $(LFLAGS_SIM) -o $@ $(OBJ_SIM) + + +build/%.o: src/%.c + $(CC) $(CFLAGS) -o $@ $< + +sim/%.o: src/%.c + $(CC) $(CFLAGS_SIM) -o $@ $< + +sim/%.s: sim/%.elf + avr-objdump -d $< > $@ + +simuc: sim/$(NAME).elf + simuc --board arduino $< + +gdb: sim/$(NAME).elf + avr-gdb $< + +isp-328p: + avrdude -c usbasp -p m328p + +isp-flash-328p: dist/$(NAME).elf all + avrdude -c usbasp -p m328p -e -U flash:w:$< + +flash-328p: dist/$(NAME).elf all + avrdude -c arduino -p m328p -P /dev/ttyUSB0 -b 115200 -e -U flash:w:$< + + +picocom: + # picocom sends CR for ENTER -> convert cr (\r) to lf (\n) + picocom -b 115200 --omap crlf /dev/ttyUSB0 + +isp-fuse-328p: + avrdude -c usbasp -p m328p -U lfuse:w:0x62:m -U hfuse:w:0xD9:m -U efuse:w:0xFF:m -U lock:w:0xFF:m + +clean: + @rm -r dist + @rm -r build + @rm -r sim + @find . -type f -name ".depend" -exec rm {} \; + @echo "clean done" diff --git a/software/arduino-nano-5v/test_2024-07-23/README.md b/software/arduino-nano-5v/test_2024-07-23/README.md new file mode 100644 index 0000000..c4d5e14 --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/README.md @@ -0,0 +1 @@ +# Testprogramm diff --git a/software/arduino-nano-5v/test_2024-07-23/src/adafruit/bme280.cpp b/software/arduino-nano-5v/test_2024-07-23/src/adafruit/bme280.cpp new file mode 100644 index 0000000..95a0f49 --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/src/adafruit/bme280.cpp @@ -0,0 +1,512 @@ +#include "bme280.h" +#include +#include + +Adafruit_BME280 theBME280; +Adafruit_BME280_Temp bm280TempSensor; +Adafruit_BME280_Pressure bm280PressureSensor; +Adafruit_BME280_Humidity bm280HumiditySensor; + +Adafruit_BME280::Adafruit_BME280() { + static I2cMaster i2cDevice; + t_fine_adjust = 0; + temp_sensor = &bm280TempSensor; + pressure_sensor = &bm280PressureSensor; + humidity_sensor = &bm280HumiditySensor; + i2c_dev = &i2cDevice; +} + +bool Adafruit_BME280::begin (uint8_t addr) { + if (!i2c_dev->begin(addr)) { + return false; + } + return init(); +} + +bool Adafruit_BME280::init() { + _sensorID = read8(BME280_REGISTER_CHIPID); + if (_sensorID != 0x60) { + return false; + } + write8(BME280_REGISTER_SOFTRESET, 0xB6); + _delay_ms(10); // wait for chip to wake up. + + // if chip is still reading calibration, delay + while (isReadingCalibration()) { + _delay_ms(10); + } + + readCoefficients(); // read trimming parameters, see DS 4.2.2 + setSampling(); // use defaults + _delay_ms(100); + + return true; +} + +/*! + * @brief setup sensor with given parameters / settings + * + * This is simply a overload to the normal begin()-function, so SPI users + * don't get confused about the library requiring an address. + * @param mode the power mode to use for the sensor + * @param tempSampling the temp samping rate to use + * @param pressSampling the pressure sampling rate to use + * @param humSampling the humidity sampling rate to use + * @param filter the filter mode to use + * @param duration the standby duration to use + */ +void Adafruit_BME280::setSampling(sensor_mode mode, + sensor_sampling tempSampling, + sensor_sampling pressSampling, + sensor_sampling humSampling, + sensor_filter filter, + standby_duration duration) { + _measReg.mode = mode; + _measReg.osrs_t = tempSampling; + _measReg.osrs_p = pressSampling; + + _humReg.osrs_h = humSampling; + _configReg.filter = filter; + _configReg.t_sb = duration; + _configReg.spi3w_en = 0; + + // making sure sensor is in sleep mode before setting configuration + // as it otherwise may be ignored + write8(BME280_REGISTER_CONTROL, MODE_SLEEP); + + // you must make sure to also set REGISTER_CONTROL after setting the + // CONTROLHUMID register, otherwise the values won't be applied (see + // DS 5.4.3) + write8(BME280_REGISTER_CONTROLHUMID, _humReg.get()); + write8(BME280_REGISTER_CONFIG, _configReg.get()); + write8(BME280_REGISTER_CONTROL, _measReg.get()); +} + +/*! + * @brief Writes an 8 bit value over I2C or SPI + * @param reg the register address to write to + * @param value the value to write to the register + */ +void Adafruit_BME280::write8(byte reg, byte value) { + byte buffer[2]; + buffer[1] = value; + if (i2c_dev) { + buffer[0] = reg; + i2c_dev->write(buffer, 2); + } +} + +/*! + * @brief Reads an 8 bit value over I2C or SPI + * @param reg the register address to read from + * @returns the data byte read from the device + */ +uint8_t Adafruit_BME280::read8(byte reg) { + uint8_t buffer[1]; + if (i2c_dev) { + buffer[0] = uint8_t(reg); + i2c_dev->write_then_read(buffer, 1, buffer, 1); + } + return buffer[0]; +} + +/*! + * @brief Reads a 16 bit value over I2C or SPI + * @param reg the register address to read from + * @returns the 16 bit data value read from the device + */ +uint16_t Adafruit_BME280::read16(byte reg) { + uint8_t buffer[2]; + + if (i2c_dev) { + buffer[0] = uint8_t(reg); + i2c_dev->write_then_read(buffer, 1, buffer, 2); + } + return uint16_t(buffer[0]) << 8 | uint16_t(buffer[1]); +} + +/*! + * @brief Reads a signed 16 bit little endian value over I2C or SPI + * @param reg the register address to read from + * @returns the 16 bit data value read from the device + */ +uint16_t Adafruit_BME280::read16_LE(byte reg) { + uint16_t temp = read16(reg); + return (temp >> 8) | (temp << 8); +} + +/*! + * @brief Reads a signed 16 bit value over I2C or SPI + * @param reg the register address to read from + * @returns the 16 bit data value read from the device + */ +int16_t Adafruit_BME280::readS16(byte reg) { return (int16_t)read16(reg); } + +/*! + * @brief Reads a signed little endian 16 bit value over I2C or SPI + * @param reg the register address to read from + * @returns the 16 bit data value read from the device + */ +int16_t Adafruit_BME280::readS16_LE(byte reg) { + return (int16_t)read16_LE(reg); +} + +/*! + * @brief Reads a 24 bit value over I2C + * @param reg the register address to read from + * @returns the 24 bit data value read from the device + */ +uint32_t Adafruit_BME280::read24(byte reg) { + uint8_t buffer[3]; + + if (i2c_dev) { + buffer[0] = uint8_t(reg); + i2c_dev->write_then_read(buffer, 1, buffer, 3); + } + return uint32_t(buffer[0]) << 16 | uint32_t(buffer[1]) << 8 | + uint32_t(buffer[2]); +} + +/*! + * @brief Take a new measurement (only possible in forced mode) + @returns true in case of success else false + */ +bool Adafruit_BME280::takeForcedMeasurement(void) { + bool return_value = false; + // If we are in forced mode, the BME sensor goes back to sleep after each + // measurement and we need to set it to forced mode once at this point, so + // it will take the next measurement and then return to sleep again. + // In normal mode simply does new measurements periodically. + if (_measReg.mode == MODE_FORCED) { + return_value = true; + // set to forced mode, i.e. "take next measurement" + write8(BME280_REGISTER_CONTROL, _measReg.get()); + // Store current time to measure the timeout + uint32_t timeout_start = millis(); + // wait until measurement has been completed, otherwise we would read the + // the values from the last measurement or the timeout occurred after 2 sec. + while (read8(BME280_REGISTER_STATUS) & 0x08) { + // In case of a timeout, stop the while loop + if ((millis() - timeout_start) > 2000) { + return_value = false; + break; + } + _delay_ms(1); + } + } + return return_value; +} + +/*! + * @brief Reads the factory-set coefficients + */ +void Adafruit_BME280::readCoefficients(void) { + _bme280_calib.dig_T1 = read16_LE(BME280_REGISTER_DIG_T1); + _bme280_calib.dig_T2 = readS16_LE(BME280_REGISTER_DIG_T2); + _bme280_calib.dig_T3 = readS16_LE(BME280_REGISTER_DIG_T3); + + _bme280_calib.dig_P1 = read16_LE(BME280_REGISTER_DIG_P1); + _bme280_calib.dig_P2 = readS16_LE(BME280_REGISTER_DIG_P2); + _bme280_calib.dig_P3 = readS16_LE(BME280_REGISTER_DIG_P3); + _bme280_calib.dig_P4 = readS16_LE(BME280_REGISTER_DIG_P4); + _bme280_calib.dig_P5 = readS16_LE(BME280_REGISTER_DIG_P5); + _bme280_calib.dig_P6 = readS16_LE(BME280_REGISTER_DIG_P6); + _bme280_calib.dig_P7 = readS16_LE(BME280_REGISTER_DIG_P7); + _bme280_calib.dig_P8 = readS16_LE(BME280_REGISTER_DIG_P8); + _bme280_calib.dig_P9 = readS16_LE(BME280_REGISTER_DIG_P9); + + _bme280_calib.dig_H1 = read8(BME280_REGISTER_DIG_H1); + _bme280_calib.dig_H2 = readS16_LE(BME280_REGISTER_DIG_H2); + _bme280_calib.dig_H3 = read8(BME280_REGISTER_DIG_H3); + _bme280_calib.dig_H4 = ((int8_t)read8(BME280_REGISTER_DIG_H4) << 4) | + (read8(BME280_REGISTER_DIG_H4 + 1) & 0xF); + _bme280_calib.dig_H5 = ((int8_t)read8(BME280_REGISTER_DIG_H5 + 1) << 4) | + (read8(BME280_REGISTER_DIG_H5) >> 4); + _bme280_calib.dig_H6 = (int8_t)read8(BME280_REGISTER_DIG_H6); +} + +/*! + * @brief return true if chip is busy reading cal data + * @returns true if reading calibration, false otherwise + */ +bool Adafruit_BME280::isReadingCalibration(void) { + uint8_t const rStatus = read8(BME280_REGISTER_STATUS); + + return (rStatus & (1 << 0)) != 0; +} + +/*! + * @brief Returns the temperature from the sensor + * @returns the temperature read from the device + */ +float Adafruit_BME280::readTemperature(void) { + int32_t var1, var2; + + int32_t adc_T = read24(BME280_REGISTER_TEMPDATA); + if (adc_T == 0x800000) // value in case temp measurement was disabled + return NAN; + adc_T >>= 4; + + var1 = (int32_t)((adc_T / 8) - ((int32_t)_bme280_calib.dig_T1 * 2)); + var1 = (var1 * ((int32_t)_bme280_calib.dig_T2)) / 2048; + var2 = (int32_t)((adc_T / 16) - ((int32_t)_bme280_calib.dig_T1)); + var2 = (((var2 * var2) / 4096) * ((int32_t)_bme280_calib.dig_T3)) / 16384; + + t_fine = var1 + var2 + t_fine_adjust; + + int32_t T = (t_fine * 5 + 128) / 256; + + return (float)T / 100; +} + +/*! + * @brief Returns the pressure from the sensor + * @returns the pressure value (in Pascal) read from the device + */ +float Adafruit_BME280::readPressure(void) { + int64_t var1, var2, var3, var4; + + readTemperature(); // must be done first to get t_fine + + int32_t adc_P = read24(BME280_REGISTER_PRESSUREDATA); + if (adc_P == 0x800000) // value in case pressure measurement was disabled + return NAN; + adc_P >>= 4; + + var1 = ((int64_t)t_fine) - 128000; + var2 = var1 * var1 * (int64_t)_bme280_calib.dig_P6; + var2 = var2 + ((var1 * (int64_t)_bme280_calib.dig_P5) * 131072); + var2 = var2 + (((int64_t)_bme280_calib.dig_P4) * 34359738368); + var1 = ((var1 * var1 * (int64_t)_bme280_calib.dig_P3) / 256) + + ((var1 * ((int64_t)_bme280_calib.dig_P2) * 4096)); + var3 = ((int64_t)1) * 140737488355328; + var1 = (var3 + var1) * ((int64_t)_bme280_calib.dig_P1) / 8589934592; + + if (var1 == 0) { + return 0; // avoid exception caused by division by zero + } + + var4 = 1048576 - adc_P; + var4 = (((var4 * 2147483648UL) - var2) * 3125) / var1; + var1 = (((int64_t)_bme280_calib.dig_P9) * (var4 / 8192) * (var4 / 8192)) / + 33554432; + var2 = (((int64_t)_bme280_calib.dig_P8) * var4) / 524288; + var4 = ((var4 + var1 + var2) / 256) + (((int64_t)_bme280_calib.dig_P7) * 16); + + float P = var4 / 256.0; + + return P; +} + +/*! + * @brief Returns the humidity from the sensor + * @returns the humidity value read from the device + */ +float Adafruit_BME280::readHumidity(void) { + int32_t var1, var2, var3, var4, var5; + + readTemperature(); // must be done first to get t_fine + + int32_t adc_H = read16(BME280_REGISTER_HUMIDDATA); + if (adc_H == 0x8000) // value in case humidity measurement was disabled + return NAN; + + var1 = t_fine - ((int32_t)76800); + var2 = (int32_t)(adc_H * 16384); + var3 = (int32_t)(((int32_t)_bme280_calib.dig_H4) * 1048576); + var4 = ((int32_t)_bme280_calib.dig_H5) * var1; + var5 = (((var2 - var3) - var4) + (int32_t)16384) / 32768; + var2 = (var1 * ((int32_t)_bme280_calib.dig_H6)) / 1024; + var3 = (var1 * ((int32_t)_bme280_calib.dig_H3)) / 2048; + var4 = ((var2 * (var3 + (int32_t)32768)) / 1024) + (int32_t)2097152; + var2 = ((var4 * ((int32_t)_bme280_calib.dig_H2)) + 8192) / 16384; + var3 = var5 * var2; + var4 = ((var3 / 32768) * (var3 / 32768)) / 128; + var5 = var3 - ((var4 * ((int32_t)_bme280_calib.dig_H1)) / 16); + var5 = (var5 < 0 ? 0 : var5); + var5 = (var5 > 419430400 ? 419430400 : var5); + uint32_t H = (uint32_t)(var5 / 4096); + + return (float)H / 1024.0; +} + +/*! + * Calculates the altitude (in meters) from the specified atmospheric + * pressure (in hPa), and sea-level pressure (in hPa). + * @param seaLevel Sea-level pressure in hPa + * @returns the altitude value read from the device + */ +float Adafruit_BME280::readAltitude(float seaLevel) { + // Equation taken from BMP180 datasheet (page 16): + // http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf + + // Note that using the equation from wikipedia can give bad results + // at high altitude. See this thread for more information: + // http://forums.adafruit.com/viewtopic.php?f=22&t=58064 + + float atmospheric = readPressure() / 100.0F; + return 44330.0 * (1.0 - pow(atmospheric / seaLevel, 0.1903)); +} + +/*! + * Calculates the pressure at sea level (in hPa) from the specified + * altitude (in meters), and atmospheric pressure (in hPa). + * @param altitude Altitude in meters + * @param atmospheric Atmospheric pressure in hPa + * @returns the pressure at sea level (in hPa) from the specified altitude + */ +float Adafruit_BME280::seaLevelForAltitude(float altitude, float atmospheric) { + // Equation taken from BMP180 datasheet (page 17): + // http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf + + // Note that using the equation from wikipedia can give bad results + // at high altitude. See this thread for more information: + // http://forums.adafruit.com/viewtopic.php?f=22&t=58064 + + return atmospheric / pow(1.0 - (altitude / 44330.0), 5.255); +} + +/*! + * Returns Sensor ID found by init() for diagnostics + * @returns Sensor ID 0x60 for BME280, 0x56, 0x57, 0x58 BMP280 + */ +uint32_t Adafruit_BME280::sensorID(void) { return _sensorID; } + +/*! + * Returns the current temperature compensation value in degrees Celsius + * @returns the current temperature compensation value in degrees Celsius + */ +float Adafruit_BME280::getTemperatureCompensation(void) { + return float((t_fine_adjust * 5) >> 8) / 100.0; +}; + +/*! + * Sets a value to be added to each temperature reading. This adjusted + * temperature is used in pressure and humidity readings. + * @param adjustment Value to be added to each temperature reading in Celsius + */ +void Adafruit_BME280::setTemperatureCompensation(float adjustment) { + // convert the value in C into and adjustment to t_fine + t_fine_adjust = ((int32_t(adjustment * 100) << 8)) / 5; +}; + + +/**************************************************************************/ +/*! + @brief Gets the sensor_t data for the BME280's temperature sensor +*/ +/**************************************************************************/ +void Adafruit_BME280_Temp::getSensor(sensor_t *sensor) { + /* Clear the sensor_t object */ + memset(sensor, 0, sizeof(sensor_t)); + + /* Insert the sensor name in the fixed length char array */ + strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1); + sensor->name[sizeof(sensor->name) - 1] = 0; + sensor->version = 1; + sensor->sensor_id = _sensorID; + sensor->type = SENSOR_TYPE_AMBIENT_TEMPERATURE; + sensor->min_delay = 0; + sensor->min_value = -40.0; /* Temperature range -40 ~ +85 C */ + sensor->max_value = +85.0; + sensor->resolution = 0.01; /* 0.01 C */ +} + +/**************************************************************************/ +/*! + @brief Gets the temperature as a standard sensor event + @param event Sensor event object that will be populated + @returns True +*/ +/**************************************************************************/ +bool Adafruit_BME280_Temp::getEvent(sensors_event_t *event) { + /* Clear the event */ + memset(event, 0, sizeof(sensors_event_t)); + + event->version = sizeof(sensors_event_t); + event->sensor_id = _sensorID; + event->type = SENSOR_TYPE_AMBIENT_TEMPERATURE; + event->timestamp = millis(); + event->temperature = theBME280.readTemperature(); + return true; +} + +/**************************************************************************/ +/*! + @brief Gets the sensor_t data for the BME280's pressure sensor +*/ +/**************************************************************************/ +void Adafruit_BME280_Pressure::getSensor(sensor_t *sensor) { + /* Clear the sensor_t object */ + memset(sensor, 0, sizeof(sensor_t)); + + /* Insert the sensor name in the fixed length char array */ + strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1); + sensor->name[sizeof(sensor->name) - 1] = 0; + sensor->version = 1; + sensor->sensor_id = _sensorID; + sensor->type = SENSOR_TYPE_PRESSURE; + sensor->min_delay = 0; + sensor->min_value = 300.0; /* 300 ~ 1100 hPa */ + sensor->max_value = 1100.0; + sensor->resolution = 0.012; /* 0.12 hPa relative */ +} + +/**************************************************************************/ +/*! + @brief Gets the pressure as a standard sensor event + @param event Sensor event object that will be populated + @returns True +*/ +/**************************************************************************/ +bool Adafruit_BME280_Pressure::getEvent(sensors_event_t *event) { + /* Clear the event */ + memset(event, 0, sizeof(sensors_event_t)); + + event->version = sizeof(sensors_event_t); + event->sensor_id = _sensorID; + event->type = SENSOR_TYPE_PRESSURE; + event->timestamp = millis(); + event->pressure = theBME280.readPressure() / 100; // convert Pa to hPa + return true; +} + +/**************************************************************************/ +/*! + @brief Gets the sensor_t data for the BME280's humidity sensor +*/ +/**************************************************************************/ +void Adafruit_BME280_Humidity::getSensor(sensor_t *sensor) { + /* Clear the sensor_t object */ + memset(sensor, 0, sizeof(sensor_t)); + + /* Insert the sensor name in the fixed length char array */ + strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1); + sensor->name[sizeof(sensor->name) - 1] = 0; + sensor->version = 1; + sensor->sensor_id = _sensorID; + sensor->type = SENSOR_TYPE_RELATIVE_HUMIDITY; + sensor->min_delay = 0; + sensor->min_value = 0; + sensor->max_value = 100; /* 0 - 100 % */ + sensor->resolution = 3; /* 3% accuracy */ +} + +/**************************************************************************/ +/*! + @brief Gets the humidity as a standard sensor event + @param event Sensor event object that will be populated + @returns True +*/ +/**************************************************************************/ +bool Adafruit_BME280_Humidity::getEvent(sensors_event_t *event) { + /* Clear the event */ + memset(event, 0, sizeof(sensors_event_t)); + + event->version = sizeof(sensors_event_t); + event->sensor_id = _sensorID; + event->type = SENSOR_TYPE_RELATIVE_HUMIDITY; + event->timestamp = millis(); + event->relative_humidity = theBME280.readHumidity(); + return true; +} diff --git a/software/arduino-nano-5v/test_2024-07-23/src/adafruit/bme280.h b/software/arduino-nano-5v/test_2024-07-23/src/adafruit/bme280.h new file mode 100644 index 0000000..b842782 --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/src/adafruit/bme280.h @@ -0,0 +1,373 @@ +// https://github.com/adafruit/Adafruit_BME280_Library + +/*! + * @file Adafruit_BME280.h + * + * Designed specifically to work with the Adafruit BME280 Breakout + * ----> http://www.adafruit.com/products/2650 + * + * These sensors use I2C or SPI to communicate, 2 or 4 pins are required + * to interface. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Written by Kevin "KTOWN" Townsend for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * See the LICENSE file for details. + * + */ + +#ifndef __BME280_H__ +#define __BME280_H__ + +// #include "Arduino.h" + +// #include +// #include +// #include + + +#include "../i2cmaster.hpp" +#include "../main.hpp" +#define byte uint8_t + + + +#include +#include +#include "sensor.h" + +/*! + * @brief default I2C address + */ +#define BME280_ADDRESS (0x77) // Primary I2C Address + /*! + * @brief alternate I2C address + */ +#define BME280_ADDRESS_ALTERNATE (0x76) // Alternate Address + +/*! + * @brief Register addresses + */ +enum { + BME280_REGISTER_DIG_T1 = 0x88, + BME280_REGISTER_DIG_T2 = 0x8A, + BME280_REGISTER_DIG_T3 = 0x8C, + + BME280_REGISTER_DIG_P1 = 0x8E, + BME280_REGISTER_DIG_P2 = 0x90, + BME280_REGISTER_DIG_P3 = 0x92, + BME280_REGISTER_DIG_P4 = 0x94, + BME280_REGISTER_DIG_P5 = 0x96, + BME280_REGISTER_DIG_P6 = 0x98, + BME280_REGISTER_DIG_P7 = 0x9A, + BME280_REGISTER_DIG_P8 = 0x9C, + BME280_REGISTER_DIG_P9 = 0x9E, + + BME280_REGISTER_DIG_H1 = 0xA1, + BME280_REGISTER_DIG_H2 = 0xE1, + BME280_REGISTER_DIG_H3 = 0xE3, + BME280_REGISTER_DIG_H4 = 0xE4, + BME280_REGISTER_DIG_H5 = 0xE5, + BME280_REGISTER_DIG_H6 = 0xE7, + + BME280_REGISTER_CHIPID = 0xD0, + BME280_REGISTER_VERSION = 0xD1, + BME280_REGISTER_SOFTRESET = 0xE0, + + BME280_REGISTER_CAL26 = 0xE1, // R calibration stored in 0xE1-0xF0 + + BME280_REGISTER_CONTROLHUMID = 0xF2, + BME280_REGISTER_STATUS = 0XF3, + BME280_REGISTER_CONTROL = 0xF4, + BME280_REGISTER_CONFIG = 0xF5, + BME280_REGISTER_PRESSUREDATA = 0xF7, + BME280_REGISTER_TEMPDATA = 0xFA, + BME280_REGISTER_HUMIDDATA = 0xFD +}; + +/**************************************************************************/ +/*! + @brief calibration data +*/ +/**************************************************************************/ +typedef struct { + uint16_t dig_T1; ///< temperature compensation value + int16_t dig_T2; ///< temperature compensation value + int16_t dig_T3; ///< temperature compensation value + + uint16_t dig_P1; ///< pressure compensation value + int16_t dig_P2; ///< pressure compensation value + int16_t dig_P3; ///< pressure compensation value + int16_t dig_P4; ///< pressure compensation value + int16_t dig_P5; ///< pressure compensation value + int16_t dig_P6; ///< pressure compensation value + int16_t dig_P7; ///< pressure compensation value + int16_t dig_P8; ///< pressure compensation value + int16_t dig_P9; ///< pressure compensation value + + uint8_t dig_H1; ///< humidity compensation value + int16_t dig_H2; ///< humidity compensation value + uint8_t dig_H3; ///< humidity compensation value + int16_t dig_H4; ///< humidity compensation value + int16_t dig_H5; ///< humidity compensation value + int8_t dig_H6; ///< humidity compensation value +} bme280_calib_data; +/*=========================================================================*/ + +class Adafruit_BME280; + +/** Adafruit Unified Sensor interface for temperature component of BME280 */ +class Adafruit_BME280_Temp : public Adafruit_Sensor { +public: + /** @brief Create an Adafruit_Sensor compatible object for the temp sensor + @param parent A pointer to the BME280 class */ + Adafruit_BME280_Temp() { _sensorID = 280; } + bool getEvent(sensors_event_t *); + void getSensor(sensor_t *); + +private: + int _sensorID; +}; + +/** Adafruit Unified Sensor interface for pressure component of BME280 */ +class Adafruit_BME280_Pressure : public Adafruit_Sensor { +public: + /** @brief Create an Adafruit_Sensor compatible object for the pressure sensor + @param parent A pointer to the BME280 class */ + Adafruit_BME280_Pressure() { _sensorID = 280; } + bool getEvent(sensors_event_t *); + void getSensor(sensor_t *); + +private: + int _sensorID; +}; + +/** Adafruit Unified Sensor interface for humidity component of BME280 */ +class Adafruit_BME280_Humidity : public Adafruit_Sensor { +public: + /** @brief Create an Adafruit_Sensor compatible object for the humidity sensor + @param parent A pointer to the BME280 class */ + Adafruit_BME280_Humidity() { _sensorID = 280;} + bool getEvent(sensors_event_t *); + void getSensor(sensor_t *); + +private: + int _sensorID; +}; + +/**************************************************************************/ +/*! + @brief Class that stores state and functions for interacting with BME280 IC +*/ +/**************************************************************************/ +class Adafruit_BME280 { +public: + /**************************************************************************/ + /*! + @brief sampling rates + */ + /**************************************************************************/ + enum sensor_sampling { + SAMPLING_NONE = 0b000, + SAMPLING_X1 = 0b001, + SAMPLING_X2 = 0b010, + SAMPLING_X4 = 0b011, + SAMPLING_X8 = 0b100, + SAMPLING_X16 = 0b101 + }; + + /**************************************************************************/ + /*! + @brief power modes + */ + /**************************************************************************/ + enum sensor_mode { + MODE_SLEEP = 0b00, + MODE_FORCED = 0b01, + MODE_NORMAL = 0b11 + }; + + /**************************************************************************/ + /*! + @brief filter values + */ + /**************************************************************************/ + enum sensor_filter { + FILTER_OFF = 0b000, + FILTER_X2 = 0b001, + FILTER_X4 = 0b010, + FILTER_X8 = 0b011, + FILTER_X16 = 0b100 + }; + + /**************************************************************************/ + /*! + @brief standby duration in ms + */ + /**************************************************************************/ + enum standby_duration { + STANDBY_MS_0_5 = 0b000, + STANDBY_MS_10 = 0b110, + STANDBY_MS_20 = 0b111, + STANDBY_MS_62_5 = 0b001, + STANDBY_MS_125 = 0b010, + STANDBY_MS_250 = 0b011, + STANDBY_MS_500 = 0b100, + STANDBY_MS_1000 = 0b101 + }; + + // constructors + Adafruit_BME280(); + + bool begin(uint8_t addr = BME280_ADDRESS); + bool init(); + + void setSampling(sensor_mode mode = MODE_NORMAL, + sensor_sampling tempSampling = SAMPLING_X16, + sensor_sampling pressSampling = SAMPLING_X16, + sensor_sampling humSampling = SAMPLING_X16, + sensor_filter filter = FILTER_OFF, + standby_duration duration = STANDBY_MS_0_5); + + bool takeForcedMeasurement(void); + float readTemperature(void); + float readPressure(void); + float readHumidity(void); + + float readAltitude(float seaLevel); + float seaLevelForAltitude(float altitude, float pressure); + uint32_t sensorID(void); + + float getTemperatureCompensation(void); + void setTemperatureCompensation(float); + +protected: + I2cMaster *i2c_dev = NULL; ///< Pointer to I2C bus interface + // Adafruit_SPIDevice *spi_dev = NULL; ///< Pointer to SPI bus interface + + Adafruit_BME280_Temp *temp_sensor; + Adafruit_BME280_Pressure *pressure_sensor; + Adafruit_BME280_Humidity *humidity_sensor; + + void readCoefficients(void); + bool isReadingCalibration(void); + + void write8(byte reg, byte value); + uint8_t read8(byte reg); + uint16_t read16(byte reg); + uint32_t read24(byte reg); + int16_t readS16(byte reg); + uint16_t read16_LE(byte reg); // little endian + int16_t readS16_LE(byte reg); // little endian + + uint8_t _i2caddr; //!< I2C addr for the TwoWire interface + int32_t _sensorID; //!< ID of the BME Sensor + int32_t t_fine; //!< temperature with high resolution, stored as an attribute + //!< as this is used for temperature compensation reading + //!< humidity and pressure + + int32_t t_fine_adjust; //!< add to compensate temp readings and in turn + //!< to pressure and humidity readings + + bme280_calib_data _bme280_calib; //!< here calibration data is stored + + /**************************************************************************/ + /*! + @brief config register + */ + /**************************************************************************/ + struct config { + // inactive duration (standby time) in normal mode + // 000 = 0.5 ms + // 001 = 62.5 ms + // 010 = 125 ms + // 011 = 250 ms + // 100 = 500 ms + // 101 = 1000 ms + // 110 = 10 ms + // 111 = 20 ms + unsigned int t_sb : 3; ///< inactive duration (standby time) in normal mode + + // filter settings + // 000 = filter off + // 001 = 2x filter + // 010 = 4x filter + // 011 = 8x filter + // 100 and above = 16x filter + unsigned int filter : 3; ///< filter settings + + // unused - don't set + unsigned int none : 1; ///< unused - don't set + unsigned int spi3w_en : 1; ///< unused - don't set + + /// @return combined config register + unsigned int get() { return (t_sb << 5) | (filter << 2) | spi3w_en; } + }; + config _configReg; //!< config register object + + /**************************************************************************/ + /*! + @brief ctrl_meas register + */ + /**************************************************************************/ + struct ctrl_meas { + // temperature oversampling + // 000 = skipped + // 001 = x1 + // 010 = x2 + // 011 = x4 + // 100 = x8 + // 101 and above = x16 + unsigned int osrs_t : 3; ///< temperature oversampling + + // pressure oversampling + // 000 = skipped + // 001 = x1 + // 010 = x2 + // 011 = x4 + // 100 = x8 + // 101 and above = x16 + unsigned int osrs_p : 3; ///< pressure oversampling + + // device mode + // 00 = sleep + // 01 or 10 = forced + // 11 = normal + unsigned int mode : 2; ///< device mode + + /// @return combined ctrl register + unsigned int get() { return (osrs_t << 5) | (osrs_p << 2) | mode; } + }; + ctrl_meas _measReg; //!< measurement register object + + /**************************************************************************/ + /*! + @brief ctrl_hum register + */ + /**************************************************************************/ + struct ctrl_hum { + /// unused - don't set + unsigned int none : 5; + + // pressure oversampling + // 000 = skipped + // 001 = x1 + // 010 = x2 + // 011 = x4 + // 100 = x8 + // 101 and above = x16 + unsigned int osrs_h : 3; ///< pressure oversampling + + /// @return combined ctrl hum register + unsigned int get() { return (osrs_h); } + }; + ctrl_hum _humReg; //!< hum register object +}; + +extern Adafruit_BME280 theBME280; + +#endif diff --git a/software/arduino-nano-5v/test_2024-07-23/src/adafruit/ens160.cpp b/software/arduino-nano-5v/test_2024-07-23/src/adafruit/ens160.cpp new file mode 100644 index 0000000..ef23404 --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/src/adafruit/ens160.cpp @@ -0,0 +1,374 @@ +/* + ScioSense_ENS160.h - Library for the ENS160 sensor with I2C interface from ScioSense + 2023 Mar 23 v6 Christoph Friese Bugfix measurement routine, prepare next release + 2021 Nov 25 v5 Martin Herold Custom mode timing fixed + 2021 Feb 04 v4 Giuseppe de Pinto Custom mode fixed + 2020 Apr 06 v3 Christoph Friese Changed nomenclature to ScioSense as product shifted from ams + 2020 Feb 15 v2 Giuseppe Pasetti Corrected firmware flash option + 2019 May 05 v1 Christoph Friese Created + based on application note "ENS160 Software Integration.pdf" rev 0.01 +*/ + +#include "ens160.h" +#include "math.h" +#include +#include +#include + +ScioSense_ENS160::ScioSense_ENS160 () { + _revENS16x = 0; + + //Isotherm, HP0 252°C / HP1 350°C / HP2 250°C / HP3 324°C / measure every 1008ms + _seq_steps[0][0] = 0x7c; + _seq_steps[0][1] = 0x0a; + _seq_steps[0][2] = 0x7e; + _seq_steps[0][3] = 0xaf; + _seq_steps[0][4] = 0xaf; + _seq_steps[0][5] = 0xa2; + _seq_steps[0][6] = 0x00; + _seq_steps[0][7] = 0x80; +} + +bool ScioSense_ENS160::begin () { + i2cDevice.begin(ENS160_I2CADDR_1); + _delay_ms(ENS160_BOOTING); + if (reset()) { + if (checkPartID()) { + if (setMode(ENS160_OPMODE_IDLE)) { + if (clearCommand()) { + if (getFirmware()) { + return true; + } + } + } + } + } + return false; + +} + +bool ScioSense_ENS160::write8 (byte reg, byte value) { + byte buffer[2]; + buffer[1] = value; + buffer[0] = reg; + return i2cDevice.write(buffer, 2); +} + +bool ScioSense_ENS160::read8 (byte reg, byte *value) { + uint8_t buffer[1]; + buffer[0] = uint8_t(reg); + return i2cDevice.write_then_read(buffer, 1, value, 1); +} + +bool ScioSense_ENS160::read16 (byte reg, uint16_t *value) { + uint8_t buffer[1]; + buffer[0] = uint8_t(reg); + return i2cDevice.write_then_read(buffer, 1, (uint8_t *)value, 2); +} + +bool ScioSense_ENS160::read16LE (byte reg, uint16_t *value) { + uint16_t tmp; + if (read16(reg, &tmp)) { + *value = ((tmp & 0xff) << 8) | (tmp >> 8); + return true; + } + return false; +} + +bool ScioSense_ENS160::readBytes (byte reg, uint8_t *bytes, uint8_t len) { + uint8_t buffer[1]; + buffer[0] = uint8_t(reg); + return i2cDevice.write_then_read(buffer, 1, buffer, len); +} + + +// Sends a reset to the ENS160. Returns false on I2C problems. +bool ScioSense_ENS160::reset () { + if (write8(ENS160_REG_OPMODE, ENS160_OPMODE_RESET)) { + _delay_ms(ENS160_BOOTING); + return true; + } + _delay_ms(ENS160_BOOTING); + return false; +} + +// Reads the part ID and confirms valid sensor +bool ScioSense_ENS160::checkPartID () { + uint16_t part_id; + + read16(ENS160_REG_PART_ID, &part_id); + _delay_ms(ENS160_BOOTING); + + if (part_id == ENS160_PARTID) { + _revENS16x = 0; + return true; + + } else if (part_id == ENS161_PARTID) { + _revENS16x = 1; + return true; + } + + return false; +} + +// Initialize idle mode and confirms +bool ScioSense_ENS160::clearCommand () { + uint8_t status; + + if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_NOP)) { + if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_CLRGPR)) { + _delay_ms(ENS160_BOOTING); + if (read8(ENS160_REG_DATA_STATUS, &status)) { + return true; + } + } + } + _delay_ms(ENS160_BOOTING); + return false; +} + +// Read firmware revisions +bool ScioSense_ENS160::getFirmware () { + uint8_t i2cbuf[3]; + + if (clearCommand()) { + _delay_ms(ENS160_BOOTING); + if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_GET_APPVER)) { + if (readBytes(ENS160_REG_GPR_READ_4, i2cbuf, 3)) { + _fw_ver_major = i2cbuf[0]; + _fw_ver_minor = i2cbuf[1]; + _fw_ver_build = i2cbuf[2]; + _revENS16x = this->_fw_ver_major > 6 ? 1 : 0; + _delay_ms(ENS160_BOOTING); + return true; + } + } + } + _delay_ms(ENS160_BOOTING); + return false; +} + +// Set operation mode of sensor +bool ScioSense_ENS160::setMode (uint8_t mode) { + //LP only valid for rev>0 + if ((mode == ENS160_OPMODE_LP) and (_revENS16x == 0)) { + return false; + } + if (write8(ENS160_REG_OPMODE, mode)) { + _delay_ms(ENS160_BOOTING); + return true; + } + _delay_ms(ENS160_BOOTING); + return false; +} + +// Initialize definition of custom mode with steps +bool ScioSense_ENS160::initCustomMode (uint16_t stepNum) { + if (stepNum > 0) { + _stepCount = stepNum; + if (setMode(ENS160_OPMODE_IDLE)) { + if (clearCommand()) { + if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_SETSEQ)) { + _delay_ms(ENS160_BOOTING); + return true; + } + } + } + } + _delay_ms(ENS160_BOOTING); + return false; +} + +// Add a step to custom measurement profile with definition of duration, enabled data acquisition and temperature for each hotplate +bool ScioSense_ENS160::addCustomStep (uint16_t time, bool measureHP0, bool measureHP1, bool measureHP2, bool measureHP3, uint16_t tempHP0, uint16_t tempHP1, uint16_t tempHP2, uint16_t tempHP3) { + uint8_t seq_ack; + uint8_t temp; + + _delay_ms(ENS160_BOOTING); + + temp = (uint8_t)(((time / 24) - 1) << 6); + if (measureHP0) { + temp = temp | 0x20; + } + if (measureHP1) { + temp = temp | 0x10; + } + if (measureHP2) { + temp = temp | 0x08; + } + if (measureHP3) { + temp = temp | 0x04; + } + if (!write8(ENS160_REG_GPR_WRITE_0, temp)) { + return false; + } + temp = (uint8_t)(((time / 24) - 1) >> 2); + if (!write8(ENS160_REG_GPR_WRITE_1, temp)) { + return false; + } + if (!write8(ENS160_REG_GPR_WRITE_2, (uint8_t)(tempHP0 / 2))) { + return false; + } + if (!write8(ENS160_REG_GPR_WRITE_3, (uint8_t)(tempHP1 / 2))) { + return false; + } + if (write8(ENS160_REG_GPR_WRITE_4, (uint8_t)(tempHP2 / 2))) { + return false; + } + if (write8(ENS160_REG_GPR_WRITE_5, (uint8_t)(tempHP3 / 2))) { + return false; + } + + if (write8(ENS160_REG_GPR_WRITE_6, (uint8_t)(_stepCount - 1))) { + return false; + } + + if (_stepCount == 1) { + if (!write8(ENS160_REG_GPR_WRITE_7, 128)) { + return false; + } + } else { + if (!write8(ENS160_REG_GPR_WRITE_7, 0)) { + return false; + } + } + _delay_ms(ENS160_BOOTING); + + if (!read8(ENS160_REG_GPR_READ_7, &seq_ack)) { + return false; + } + _delay_ms(ENS160_BOOTING); + + if ((ENS160_SEQ_ACK_COMPLETE | _stepCount) != seq_ack) { + _stepCount++; + return false; + } + + return true; +} + +bool ScioSense_ENS160::readStatus (uint8_t *status) { + return read8(ENS160_REG_DATA_STATUS, status); +} + +bool ScioSense_ENS160::readData (ENS160_DATA *data) { + uint8_t buffer[1] = { 0x21 }; + return i2cDevice.write_then_read(buffer, 1, (uint8_t *)data, sizeof(ENS160_DATA)); +} + +// Perform prediction measurement and stores result in internal variables +bool ScioSense_ENS160::measure (bool waitForNew) { + uint8_t i2cbuf[8]; + uint8_t status; + + // Set default status for early bail out + if (waitForNew) { + do { + if (!read8(ENS160_REG_DATA_STATUS, &status)) { + return false; + } + _delay_ms(1); + } while (!IS_NEWDAT(status)); + } else { + if (!read8(ENS160_REG_DATA_STATUS, &status)) { + return false; + } + } + + + // Read predictions + if (IS_NEWDAT(status)) { + if (!readBytes(ENS160_REG_DATA_AQI, i2cbuf, 7)) { + return false; + } + return false; + _data_aqi = i2cbuf[0]; + _data_tvoc = i2cbuf[1] | ((uint16_t)i2cbuf[2] << 8); + _data_eco2 = i2cbuf[3] | ((uint16_t)i2cbuf[4] << 8); + if (_revENS16x > 0) { + _data_aqi500 = ((uint16_t)i2cbuf[5]) | ((uint16_t)i2cbuf[6] << 8); + } else { + _data_aqi500 = 0; + } + return true; + } + + return false; +} + +// Perfrom raw measurement and stores result in internal variables +bool ScioSense_ENS160::measureRaw (bool waitForNew) { + uint8_t i2cbuf[8]; + uint8_t status; + + // Set default status for early bail out + if (waitForNew) { + do { + _delay_ms(1); + if (!read8(ENS160_REG_DATA_STATUS, &status)) { + return false; + } + } while (!IS_NEWGPR(status)); + } else { + if (!read8(ENS160_REG_DATA_STATUS, &status)) { + return false; + } + } + + if (IS_NEWGPR(status)) { + + // Read raw resistance values + if (!readBytes(ENS160_REG_GPR_READ_0, i2cbuf, 8)) { + return false; + } + _hp0_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[0] | ((uint16_t)i2cbuf[1] << 8))); + _hp1_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[2] | ((uint16_t)i2cbuf[3] << 8))); + _hp2_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[4] | ((uint16_t)i2cbuf[5] << 8))); + _hp3_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[6] | ((uint16_t)i2cbuf[7] << 8))); + + // Read baselines + if (!readBytes(ENS160_REG_DATA_BL, i2cbuf, 8)) { + return false; + } + _hp0_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[0] | ((uint16_t)i2cbuf[1] << 8))); + _hp1_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[2] | ((uint16_t)i2cbuf[3] << 8))); + _hp2_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[4] | ((uint16_t)i2cbuf[5] << 8))); + _hp3_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[6] | ((uint16_t)i2cbuf[7] << 8))); + + if (!read8(ENS160_REG_DATA_MISR, i2cbuf)) { + return false; + } + _misr = i2cbuf[0]; + return true; + } + + return false; +} + + +// Writes t (degC) and h (%rh) to ENV_DATA. Returns false on I2C problems. +bool ScioSense_ENS160::set_envdata (float t, float h) { + uint16_t t_data = (uint16_t)((t + 273.15f) * 64.0f); + uint16_t rh_data = (uint16_t)(h * 512.0f); + return this->set_envdata210(t_data, rh_data); +} + +// Writes t and h (in ENS210 format) to ENV_DATA. Returns false on I2C problems. +bool ScioSense_ENS160::set_envdata210 (uint16_t t, uint16_t h) { + //uint16_t temp; + uint8_t trh_in[4]; + + //temp = (uint16_t)((t + 273.15f) * 64.0f); + trh_in[0] = t & 0xff; + trh_in[1] = (t >> 8) & 0xff; + + //temp = (uint16_t)(h * 512.0f); + trh_in[2] = h & 0xff; + trh_in[3] = (h >> 8) & 0xff; + + if (!i2cDevice.writeByteAndBuffer(ENS160_REG_TEMP_IN, trh_in, 4)) { + return false; + } + + return true; +} diff --git a/software/arduino-nano-5v/test_2024-07-23/src/adafruit/ens160.h b/software/arduino-nano-5v/test_2024-07-23/src/adafruit/ens160.h new file mode 100644 index 0000000..fb6925d --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/src/adafruit/ens160.h @@ -0,0 +1,188 @@ +/* + ScioSense_ENS160.h - Library for the ENS160 sensor with I2C interface from ScioSense + 2023 Mar 23 v6 Christoph Friese Bugfix measurement routine, prepare next release + 2021 July 29 v4 Christoph Friese Changed nomenclature to ScioSense as product shifted from ams + 2020 Apr 06 v3 Christoph Friese Changed nomenclature to ScioSense as product shifted from ams + 2020 Feb 15 v2 Giuseppe Pasetti Corrected firmware flash option + 2019 May 05 v1 Christoph Friese Created + based on application note "ENS160 Software Integration.pdf" rev 0.01 +*/ + +#ifndef __SCIOSENSE_ENS160_H_ +#define __SCIOSENSE_ENS160_H_ + +#include "../i2cmaster.hpp" +#include +#define byte uint8_t + +// #if (ARDUINO >= 100) +// #include "Arduino.h" +// #else +// #include "WProgram.h" +// #endif + +// #include + +// Chip constants +#define ENS160_PARTID 0x0160 +#define ENS161_PARTID 0x0161 +#define ENS160_BOOTING 10 + +// 7-bit I2C slave address of the ENS160 +#define ENS160_I2CADDR_0 0x52 //ADDR low +#define ENS160_I2CADDR_1 0x53 //ADDR high + +// ENS160 registers for version V0 +#define ENS160_REG_PART_ID 0x00 // 2 byte register +#define ENS160_REG_OPMODE 0x10 +#define ENS160_REG_CONFIG 0x11 +#define ENS160_REG_COMMAND 0x12 +#define ENS160_REG_TEMP_IN 0x13 +#define ENS160_REG_RH_IN 0x15 +#define ENS160_REG_DATA_STATUS 0x20 +#define ENS160_REG_DATA_AQI 0x21 +#define ENS160_REG_DATA_TVOC 0x22 +#define ENS160_REG_DATA_ECO2 0x24 +#define ENS160_REG_DATA_BL 0x28 +#define ENS160_REG_DATA_T 0x30 +#define ENS160_REG_DATA_RH 0x32 +#define ENS160_REG_DATA_MISR 0x38 +#define ENS160_REG_GPR_WRITE_0 0x40 +#define ENS160_REG_GPR_WRITE_1 ENS160_REG_GPR_WRITE_0 + 1 +#define ENS160_REG_GPR_WRITE_2 ENS160_REG_GPR_WRITE_0 + 2 +#define ENS160_REG_GPR_WRITE_3 ENS160_REG_GPR_WRITE_0 + 3 +#define ENS160_REG_GPR_WRITE_4 ENS160_REG_GPR_WRITE_0 + 4 +#define ENS160_REG_GPR_WRITE_5 ENS160_REG_GPR_WRITE_0 + 5 +#define ENS160_REG_GPR_WRITE_6 ENS160_REG_GPR_WRITE_0 + 6 +#define ENS160_REG_GPR_WRITE_7 ENS160_REG_GPR_WRITE_0 + 7 +#define ENS160_REG_GPR_READ_0 0x48 +#define ENS160_REG_GPR_READ_4 ENS160_REG_GPR_READ_0 + 4 +#define ENS160_REG_GPR_READ_6 ENS160_REG_GPR_READ_0 + 6 +#define ENS160_REG_GPR_READ_7 ENS160_REG_GPR_READ_0 + 7 + +//ENS160 data register fields +#define ENS160_COMMAND_NOP 0x00 +#define ENS160_COMMAND_CLRGPR 0xCC +#define ENS160_COMMAND_GET_APPVER 0x0E +#define ENS160_COMMAND_SETTH 0x02 +#define ENS160_COMMAND_SETSEQ 0xC2 + +#define ENS160_OPMODE_RESET 0xF0 +#define ENS160_OPMODE_DEP_SLEEP 0x00 +#define ENS160_OPMODE_IDLE 0x01 +#define ENS160_OPMODE_STD 0x02 +#define ENS160_OPMODE_LP 0x03 +#define ENS160_OPMODE_CUSTOM 0xC0 + +#define ENS160_BL_CMD_START 0x02 +#define ENS160_BL_CMD_ERASE_APP 0x04 +#define ENS160_BL_CMD_ERASE_BLINE 0x06 +#define ENS160_BL_CMD_WRITE 0x08 +#define ENS160_BL_CMD_VERIFY 0x0A +#define ENS160_BL_CMD_GET_BLVER 0x0C +#define ENS160_BL_CMD_GET_APPVER 0x0E +#define ENS160_BL_CMD_EXITBL 0x12 + +#define ENS160_SEQ_ACK_NOTCOMPLETE 0x80 +#define ENS160_SEQ_ACK_COMPLETE 0xC0 + +#define IS_ENS160_SEQ_ACK_NOT_COMPLETE(x) (ENS160_SEQ_ACK_NOTCOMPLETE == (ENS160_SEQ_ACK_NOTCOMPLETE & (x))) +#define IS_ENS160_SEQ_ACK_COMPLETE(x) (ENS160_SEQ_ACK_COMPLETE == (ENS160_SEQ_ACK_COMPLETE & (x))) + +#define ENS160_DATA_STATUS_NEWDAT 0x02 +#define ENS160_DATA_STATUS_NEWGPR 0x01 + +#define IS_NEWDAT(x) (ENS160_DATA_STATUS_NEWDAT == (ENS160_DATA_STATUS_NEWDAT & (x))) +#define IS_NEWGPR(x) (ENS160_DATA_STATUS_NEWGPR == (ENS160_DATA_STATUS_NEWGPR & (x))) +#define IS_NEW_DATA_AVAILABLE(x) (0 != ((ENS160_DATA_STATUS_NEWDAT | ENS160_DATA_STATUS_NEWGPR ) & (x))) + +#define CONVERT_RS_RAW2OHMS_I(x) (1 << ((x) >> 11)) +#define CONVERT_RS_RAW2OHMS_F(x) (pow (2, (float)(x) / 2048)) + +typedef struct { + uint8_t aqi; + uint16_t tvoc; + uint16_t eco2; +} ENS160_DATA; + +class ScioSense_ENS160 { + + public: + ScioSense_ENS160(); + + void setI2C(uint8_t sda, uint8_t scl); // Function to redefine I2C pins + + bool begin(); // Init I2C communication, resets ENS160 and checks its PART_ID. Returns false on I2C problems or wrong PART_ID. + uint8_t revENS16x() { return this->_revENS16x; } // Report version of sensor (0: ENS160, 1: ENS161) + bool setMode(uint8_t mode); // Set operation mode of sensor + + bool initCustomMode(uint16_t stepNum); // Initialize definition of custom mode with steps + bool addCustomStep(uint16_t time, bool measureHP0, bool measureHP1, bool measureHP2, bool measureHP3, uint16_t tempHP0, uint16_t tempHP1, uint16_t tempHP2, uint16_t tempHP3); + // Add a step to custom measurement profile with definition of duration, enabled data acquisition and temperature for each hotplate + + bool readData (ENS160_DATA *data); + bool readStatus(uint8_t *status); + bool measure(bool waitForNew); // Perform measurement and stores result in internal variables + bool measureRaw(bool waitForNew); // Perform raw measurement and stores result in internal variables + bool set_envdata(float t, float h); // Writes t (degC) and h (%rh) to ENV_DATA. Returns "0" if I2C transmission is successful + bool set_envdata210(uint16_t t, uint16_t h); // Writes t and h (in ENS210 format) to ENV_DATA. Returns "0" if I2C transmission is successful + uint8_t getMajorRev() { return this->_fw_ver_major; } // Get major revision number of used firmware + uint8_t getMinorRev() { return this->_fw_ver_minor; } // Get minor revision number of used firmware + uint8_t getBuild() { return this->_fw_ver_build; } // Get build revision number of used firmware + + uint8_t getAQI() { return this->_data_aqi; } // Get AQI value of last measurement + uint16_t getTVOC() { return this->_data_tvoc; } // Get TVOC value of last measurement + uint16_t geteCO2() { return this->_data_eco2; } // Get eCO2 value of last measurement + uint16_t getAQI500() { return this->_data_aqi500; } // Get AQI500 value of last measurement + uint32_t getHP0() { return this->_hp0_rs; } // Get resistance of HP0 of last measurement + uint32_t getHP1() { return this->_hp1_rs; } // Get resistance of HP1 of last measurement + uint32_t getHP2() { return this->_hp2_rs; } // Get resistance of HP2 of last measurement + uint32_t getHP3() { return this->_hp3_rs; } // Get resistance of HP3 of last measurement + uint32_t getHP0BL() { return this->_hp0_bl; } // Get baseline resistance of HP0 of last measurement + uint32_t getHP1BL() { return this->_hp1_bl; } // Get baseline resistance of HP1 of last measurement + uint32_t getHP2BL() { return this->_hp2_bl; } // Get baseline resistance of HP2 of last measurement + uint32_t getHP3BL() { return this->_hp3_bl; } // Get baseline resistance of HP3 of last measurement + uint8_t getMISR() { return this->_misr; } // Return status code of sensor + + private: + I2cMaster i2cDevice; + bool reset(); // Sends a reset to the ENS160. Returns false on I2C problems. + bool checkPartID(); // Reads the part ID and confirms valid sensor + bool clearCommand(); // Initialize idle mode and confirms + bool getFirmware(); // Read firmware revisions + + uint8_t _revENS16x; // ENS160 or ENS161 connected? (FW >7) + + uint8_t _fw_ver_major; + uint8_t _fw_ver_minor; + uint8_t _fw_ver_build; + + uint16_t _stepCount; // Counter for custom sequence + + uint8_t _data_aqi; + uint16_t _data_tvoc; + uint16_t _data_eco2; + uint16_t _data_aqi500; + uint32_t _hp0_rs; + uint32_t _hp0_bl; + uint32_t _hp1_rs; + uint32_t _hp1_bl; + uint32_t _hp2_rs; + uint32_t _hp2_bl; + uint32_t _hp3_rs; + uint32_t _hp3_bl; + uint16_t _temp; + int _slaveaddr; // Slave address of the ENS160 + uint8_t _misr; + + uint8_t _seq_steps[1][8]; + + bool write8(byte reg, byte value); + bool read8 (byte reg, byte *value); + bool read16 (byte reg, uint16_t *value); + bool read16LE (byte reg, uint16_t *value); + bool readBytes (byte reg, uint8_t *bytes, uint8_t len); +}; + + +#endif diff --git a/software/arduino-nano-5v/test_2024-07-23/src/adafruit/sensor.h b/software/arduino-nano-5v/test_2024-07-23/src/adafruit/sensor.h new file mode 100644 index 0000000..ac7e454 --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/src/adafruit/sensor.h @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software< /span> + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Update by K. Townsend (Adafruit Industries) for lighter typedefs, and + * extended sensor support to include color, voltage and current */ + +#ifndef _ADAFRUIT_SENSOR_H +#define _ADAFRUIT_SENSOR_H + +#ifndef ARDUINO +#include +#elif ARDUINO >= 100 +#include "Arduino.h" +#include "Print.h" +#else +#include "WProgram.h" +#endif + +/* Constants */ +#define SENSORS_GRAVITY_EARTH (9.80665F) /**< Earth's gravity in m/s^2 */ +#define SENSORS_GRAVITY_MOON (1.6F) /**< The moon's gravity in m/s^2 */ +#define SENSORS_GRAVITY_SUN (275.0F) /**< The sun's gravity in m/s^2 */ +#define SENSORS_GRAVITY_STANDARD (SENSORS_GRAVITY_EARTH) +#define SENSORS_MAGFIELD_EARTH_MAX \ + (60.0F) /**< Maximum magnetic field on Earth's surface */ +#define SENSORS_MAGFIELD_EARTH_MIN \ + (30.0F) /**< Minimum magnetic field on Earth's surface */ +#define SENSORS_PRESSURE_SEALEVELHPA \ + (1013.25F) /**< Average sea level pressure is 1013.25 hPa */ +#define SENSORS_DPS_TO_RADS \ + (0.017453293F) /**< Degrees/s to rad/s multiplier \ + */ +#define SENSORS_RADS_TO_DPS \ + (57.29577793F) /**< Rad/s to degrees/s multiplier */ +#define SENSORS_GAUSS_TO_MICROTESLA \ + (100) /**< Gauss to micro-Tesla multiplier */ + +/** Sensor types */ +typedef enum { + SENSOR_TYPE_ACCELEROMETER = (1), /**< Gravity + linear acceleration */ + SENSOR_TYPE_MAGNETIC_FIELD = (2), + SENSOR_TYPE_ORIENTATION = (3), + SENSOR_TYPE_GYROSCOPE = (4), + SENSOR_TYPE_LIGHT = (5), + SENSOR_TYPE_PRESSURE = (6), + SENSOR_TYPE_PROXIMITY = (8), + SENSOR_TYPE_GRAVITY = (9), + SENSOR_TYPE_LINEAR_ACCELERATION = + (10), /**< Acceleration not including gravity */ + SENSOR_TYPE_ROTATION_VECTOR = (11), + SENSOR_TYPE_RELATIVE_HUMIDITY = (12), + SENSOR_TYPE_AMBIENT_TEMPERATURE = (13), + SENSOR_TYPE_OBJECT_TEMPERATURE = (14), + SENSOR_TYPE_VOLTAGE = (15), + SENSOR_TYPE_CURRENT = (16), + SENSOR_TYPE_COLOR = (17), + SENSOR_TYPE_TVOC = (18), + SENSOR_TYPE_VOC_INDEX = (19), + SENSOR_TYPE_NOX_INDEX = (20), + SENSOR_TYPE_CO2 = (21), + SENSOR_TYPE_ECO2 = (22), + SENSOR_TYPE_PM10_STD = (23), + SENSOR_TYPE_PM25_STD = (24), + SENSOR_TYPE_PM100_STD = (25), + SENSOR_TYPE_PM10_ENV = (26), + SENSOR_TYPE_PM25_ENV = (27), + SENSOR_TYPE_PM100_ENV = (28), + SENSOR_TYPE_GAS_RESISTANCE = (29), + SENSOR_TYPE_UNITLESS_PERCENT = (30), + SENSOR_TYPE_ALTITUDE = (31) +} sensors_type_t; + +/** struct sensors_vec_s is used to return a vector in a common format. */ +typedef struct { + union { + float v[3]; ///< 3D vector elements + struct { + float x; ///< X component of vector + float y; ///< Y component of vector + float z; ///< Z component of vector + }; ///< Struct for holding XYZ component + /* Orientation sensors */ + struct { + float roll; /**< Rotation around the longitudinal axis (the plane body, 'X + axis'). Roll is positive and increasing when moving + downward. -90 degrees <= roll <= 90 degrees */ + float pitch; /**< Rotation around the lateral axis (the wing span, 'Y + axis'). Pitch is positive and increasing when moving + upwards. -180 degrees <= pitch <= 180 degrees) */ + float heading; /**< Angle between the longitudinal axis (the plane body) + and magnetic north, measured clockwise when viewing from + the top of the device. 0-359 degrees */ + }; ///< Struct for holding roll/pitch/heading + }; ///< Union that can hold 3D vector array, XYZ components or + ///< roll/pitch/heading + int8_t status; ///< Status byte + uint8_t reserved[3]; ///< Reserved +} sensors_vec_t; + +/** struct sensors_color_s is used to return color data in a common format. */ +typedef struct { + union { + float c[3]; ///< Raw 3-element data + /* RGB color space */ + struct { + float r; /**< Red component */ + float g; /**< Green component */ + float b; /**< Blue component */ + }; ///< RGB data in floating point notation + }; ///< Union of various ways to describe RGB colorspace + uint32_t rgba; /**< 24-bit RGBA value */ +} sensors_color_t; + +/* Sensor event (36 bytes) */ +/** struct sensor_event_s is used to provide a single sensor event in a common + * format. */ +typedef struct { + int32_t version; /**< must be sizeof(struct sensors_event_t) */ + int32_t sensor_id; /**< unique sensor identifier */ + int32_t type; /**< sensor type */ + int32_t reserved0; /**< reserved */ + int32_t timestamp; /**< time is in milliseconds */ + union { + float data[4]; ///< Raw data */ + sensors_vec_t acceleration; /**< acceleration values are in meter per second + per second (m/s^2) */ + sensors_vec_t + magnetic; /**< magnetic vector values are in micro-Tesla (uT) */ + sensors_vec_t orientation; /**< orientation values are in degrees */ + sensors_vec_t gyro; /**< gyroscope values are in rad/s */ + float temperature; /**< temperature is in degrees centigrade (Celsius) */ + float distance; /**< distance in centimeters */ + float light; /**< light in SI lux units */ + float pressure; /**< pressure in hectopascal (hPa) */ + float relative_humidity; /**< relative humidity in percent */ + float current; /**< current in milliamps (mA) */ + float voltage; /**< voltage in volts (V) */ + float tvoc; /**< Total Volatile Organic Compounds, in ppb */ + float voc_index; /**< VOC (Volatile Organic Compound) index where 100 is + normal (unitless) */ + float nox_index; /**< NOx (Nitrogen Oxides) index where 100 is normal + (unitless) */ + float CO2; /**< Measured CO2 in parts per million (ppm) */ + float eCO2; /**< equivalent/estimated CO2 in parts per million (ppm + estimated from some other measurement) */ + float pm10_std; /**< Standard Particulate Matter <=1.0 in parts per million + (ppm) */ + float pm25_std; /**< Standard Particulate Matter <=2.5 in parts per million + (ppm) */ + float pm100_std; /**< Standard Particulate Matter <=10.0 in parts per + million (ppm) */ + float pm10_env; /**< Environmental Particulate Matter <=1.0 in parts per + million (ppm) */ + float pm25_env; /**< Environmental Particulate Matter <=2.5 in parts per + million (ppm) */ + float pm100_env; /**< Environmental Particulate Matter <=10.0 in parts per + million (ppm) */ + float gas_resistance; /**< Proportional to the amount of VOC particles in + the air (Ohms) */ + float unitless_percent; /** +#include + +#include "i2cmaster.hpp" + +I2cMaster::I2cMaster () { + address = 0; + timer = 0; +} + +void I2cMaster::tick1ms () { + if (timer > 0) { + timer--; + } +} + +bool I2cMaster::begin (uint8_t addr) { + this->address = addr; + // TWBR = 13; // 100kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 100000) / (2 * 100000 * 4); + TWBR = 100; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4); + TWCR = (1 << TWEN); + return true; +} + +void I2cMaster::end () { + TWCR = (1 << TWEN); + TWBR = 0; +} + +bool I2cMaster::read (uint8_t *buffer, uint8_t len) { + if (start(true)) { + if (readBytes(buffer, len)) { + if (stop()) { + return true; + } + } + } + return false; +} + + +bool I2cMaster::write (const uint8_t *buffer, uint8_t len) { + if (start(false)) { + if (writeBytes(buffer, len)) { + if (stop()) { + return true; + } + } + } + return false; +} + +bool I2cMaster::writeByteAndBuffer (uint8_t byte, const uint8_t *buffer, uint8_t len) { + if (start(false)) { + do { + TWDR = byte; + TWCR = (1 << TWINT) | (1 << TWEN); // send byte + while (!(TWCR & (1 << TWINT))) {}; // wait until last action done + if ((TWSR & 0xf8) != 0x28) { + return false; + } + byte = *buffer++; + } while (len-- > 0); + return true; + } + return false; +} + + +bool I2cMaster::write_then_read (const uint8_t *write_buffer, uint8_t write_len, uint8_t *read_buffer, uint8_t read_len) { + if (start(false)) { + if (writeBytes(write_buffer, write_len)) { + if (start(true)) { + if (readBytes(read_buffer, read_len)) { + if (stop()) { + return true; + } + } + } + } + } + return false; +} + +// ------------------------------------------------------------- + +bool I2cMaster::readBytes (uint8_t *buffer, uint8_t len) { + while (len-- > 0) { + if (len > 0) { + TWCR = (1 << TWEA) | (1 << TWINT) | (1 << TWEN); // read data byte with ACK enabled + } else { + TWCR = (1 << TWINT) | (1 << TWEN); // read data byte with ACK disabled + } + while (!(TWCR & (1 << TWINT))) {}; // wait until last action done + uint8_t sr = TWSR & 0xf8; + if ((len > 0 && sr != 0x50) || (len == 0 && sr != 0x58)) { + return false; + } + *buffer++ = TWDR; + } + return true; +} + +bool I2cMaster::writeBytes (const uint8_t *buffer, uint8_t len) { + while (len-- > 0) { + // printf_P(PSTR("[wB:len=%d, byte=%02x]"), len + 1, *buffer); + TWDR = *buffer++; + TWCR = (1 << TWINT) | (1 << TWEN); // send data byte + timer = 5; + while (timer > 0 && !(TWCR & (1 << TWINT))) {}; // wait until last action done + if (!timer || (TWSR & 0xf8) != 0x28) { + return false; + } + } + return true; +} + + +bool I2cMaster::start (bool read) { + TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // send START condition + timer = 5; + while (timer > 0 && !(TWCR & (1 << TWINT))) {}; // wait until last action done + uint8_t sr = TWSR & 0xf8; + if (!timer || (sr != 0x08 && sr != 0x10)) { + return false; + } + TWDR = (address << 1) | (read ? 1 : 0); // address + R/nW + TWCR = (1 << TWINT) | (1 << TWEN); // send address/RW + timer = 5; + while (timer > 0 && !(TWCR & (1 << TWINT))) {}; // wait until last action done + sr = TWSR & 0xf8; + if (!timer || (!read && sr != 0x18) || (read && sr != 0x40)) { + return false; + } + return true; +} + +bool I2cMaster::stop () { + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); + while (TWCR & ( 1 << TWSTO)); + return true; +} + diff --git a/software/arduino-nano-5v/test_2024-07-23/src/i2cmaster.hpp b/software/arduino-nano-5v/test_2024-07-23/src/i2cmaster.hpp new file mode 100644 index 0000000..e70326c --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/src/i2cmaster.hpp @@ -0,0 +1,28 @@ +#ifndef I2C_MASTER +#define I2C_MASTER + +#include + +class I2cMaster { + public: + static void end (); + + public: + I2cMaster (); + void tick1ms (); + bool begin (uint8_t addr); + bool read (uint8_t *buffer, uint8_t len); + bool write(const uint8_t *buffer, uint8_t len); + bool write_then_read(const uint8_t *write_buffer, uint8_t write_len, uint8_t *read_buffer, uint8_t read_len); + bool writeByteAndBuffer (uint8_t byte, const uint8_t *buffer, uint8_t len); + + private: + uint8_t address; + uint8_t timer; + bool start (bool read); + bool stop (); + bool writeBytes (const uint8_t *buffer, uint8_t len); + bool readBytes (uint8_t *buffer, uint8_t len); +}; + +#endif \ No newline at end of file diff --git a/software/arduino-nano-5v/test_2024-07-23/src/i2cslave.cpp b/software/arduino-nano-5v/test_2024-07-23/src/i2cslave.cpp new file mode 100644 index 0000000..2bf6ac2 --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/src/i2cslave.cpp @@ -0,0 +1,92 @@ +#include +#include +#include + +#include "i2cslave.hpp" + +I2cSlave::I2cSlave () { + timer = 0; + fromMaster.rIndex = 0; + fromMaster.wIndex = 0; + toMaster.rIndex = 0; + toMaster.wIndex = 0; +} + +void I2cSlave::tick1ms () { + if (timer > 0) { + timer--; + } +} + +bool I2cSlave::begin (uint8_t addr, bool acceptGeneralCalls) { + if (addr > 127) { + return false; + } + TWAR = addr << 1 | (acceptGeneralCalls ? 1 : 0); + TWBR = 100; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4); + TWCR = (1 << TWEA) | (1 << TWEN) | (1 << TWIE); + return true; +} + +void I2cSlave::end () { + TWCR = (1 << TWEN); + TWBR = 0; +} + +int I2cSlave::read () { + return getByte(fromMaster); +} + +void I2cSlave::write (uint8_t byte) { + putByte(toMaster, byte); +} + + +void I2cSlave::putByte (RingBuffer& buffer, uint8_t byte) { + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + buffer.data[buffer.wIndex++] = byte; + if (buffer.wIndex >= sizeof(buffer.data)) { + buffer.wIndex = 0; + } + if (buffer.wIndex == buffer.rIndex) { + buffer.rIndex++; + if (buffer.rIndex >= sizeof(buffer.data)) { + buffer.rIndex = 0; + } + } + } +} + +int I2cSlave::getByte (RingBuffer& buffer) { + uint8_t b; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + if (buffer.rIndex == buffer.wIndex) { + return EOF; + } + b = buffer.data[buffer.rIndex++]; + if (buffer.rIndex >= sizeof(buffer.data)) { + buffer.rIndex = 0; + } + } + return b; +} + +void I2cSlave::handleTWIIsr () { + uint8_t sr = TWSR & 0xf8; + switch (sr) { + case 0x80: { // Previously addressed with own SLA+W; data has been received; ACK has been returned + putByte(fromMaster, TWDR); + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE); // no TWEA -> only one byte accepted + break; + } + case 0xa8: { // Own SLA+R has been received; ACK has been returned + int response = getByte(toMaster);; + TWDR = response < 0 ? 0x00 : (uint8_t)response; + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE); // no TWEA -> only one byte accepted + break; + } + default: TWCR = (1 << TWINT) | (1 << TWEA) | (1 << TWEN) | (1 << TWIE); break; + } + +} + diff --git a/software/arduino-nano-5v/test_2024-07-23/src/i2cslave.hpp b/software/arduino-nano-5v/test_2024-07-23/src/i2cslave.hpp new file mode 100644 index 0000000..2fe2dc7 --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/src/i2cslave.hpp @@ -0,0 +1,32 @@ +#ifndef I2C_SLAVE +#define I2C_SLAVE + +#include + +class I2cSlave { + private: + typedef struct { + uint8_t rIndex; + uint8_t wIndex; + uint8_t data[8]; + } RingBuffer; + + public: + I2cSlave (); + void tick1ms (); + bool begin (uint8_t addr, bool acceptGeneralCalls); + void end (); + void handleTWIIsr (); + int read (); + void write (uint8_t byte); + + private: + uint8_t timer; + RingBuffer fromMaster; + RingBuffer toMaster; + void putByte (RingBuffer& buffer, uint8_t byte); + int getByte (RingBuffer& buffer); + +}; + +#endif \ No newline at end of file diff --git a/software/arduino-nano-5v/test_2024-07-23/src/main.cpp b/software/arduino-nano-5v/test_2024-07-23/src/main.cpp new file mode 100644 index 0000000..f5b0f3e --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/src/main.cpp @@ -0,0 +1,277 @@ +#include +#include +#include +#include + +#include +#include + +#include "main.hpp" +#include "units/encoder.hpp" +#include "units/i2c.hpp" +#include "units/led.hpp" +#include "units/ieee485.hpp" +#include "units/led.hpp" +#include "units/lcd.hpp" + + +// #include "units/switch.hpp" +// #include "units/rgb.hpp" +// #include "units/seg7.hpp" +// #include "units/poti.hpp" +// #include "units/r2r.hpp" +// #include "units/motor.hpp" +// #include "units/portexp.hpp" +// #include "units/uart1.hpp" +// #include "units/modbus.hpp" + + + + +extern "C" { + void __cxa_pure_virtual () { + } + + int __cxa_guard_acquire(uint8_t *g) { + return 0; + } + + void __cxa_guard_release(uint8_t *g) {} + + void __cxa_guard_abort(uint8_t *g) {} + + + int uart_putchar(char c, FILE *stream) { + if (c == '\n') { + uart_putchar('\r', stream); + } + if (stream == stdout) { + loop_until_bit_is_set(UCSR0A, UDRE0); + UDR0 = c; + } + return 0; + } + + + uint64_t volatile systemMillis = 0; + uint8_t volatile uartBuffer[32]; + uint8_t volatile rIndex = 0; + uint8_t volatile wIndex = 0; + + int uart_getchar (FILE *stream) { + // if (rIndex == wIndex) { + // // nothing in buffer + // return EOF; + // } + // printf_P(PSTR(" r%d"), rIndex); + while (rIndex == wIndex) { + // wait for character + } + + // don't use "char c" because german special characters would lead to negative return -> stream error + // char c = uartBuffer[rIndex++]; + + uint8_t c = uartBuffer[rIndex++]; + // printf_P(PSTR("(%02x) "), c); + if (c == '\r') { + c = '\n'; + } + putchar(c); // echo on terminal + return c; + } + + static FILE mystdout = { 0, 0, _FDEV_SETUP_WRITE , 0, 0, uart_putchar, NULL, 0 }; + static FILE mystdin = { 0, 0, _FDEV_SETUP_READ , 0, 0, NULL, uart_getchar, 0 }; + + static volatile uint32_t timer1ms = 0; + static volatile int keyUart0 = EOF; + + Led led; + // Switch sw; + // Rgb rgb; + // Seg7 seg7; + // Poti poti; + Encoder encoder; + // R2r r2r; + // Motor motor; + // PortExp portExp; + Lcd lcd; + // Uart1 uart1; + // Modbus modbus; + Ieee485 ieee485; + I2c i2cSparkfun(I2c::SparkFunEnvCombo); + I2c i2cMaster(I2c::Master); + I2c i2cSlave(I2c::Slave); + +} + +void setTimer (uint32_t ms) { + ATOMIC_BLOCK(ATOMIC_FORCEON) { + timer1ms = ms; + } +} + +int wait (uint32_t ms) { + setTimer(ms); + do { + ATOMIC_BLOCK(ATOMIC_FORCEON) { + ms = timer1ms; + } + } while (ms > 0 && keyUart0 == EOF); + return keyUart0; +} + +int main () { + + // Nano-644 LEDs (Green, Orange, Red) + DDRC |= (1 << DDC4) | (1 << DDC3) | (1 << DDC2); + PORTC &= ~((1 << PORT4) | (1 << PORT3) | (1 << PORT2)); + + // Nano-644 push button SW2 + DDRC &= ~(1 << DDC5); + PORTC |= (1 << PORT5); // enable internal pullup resistor + + // UART0 interface on Nano-644 + UCSR0A = (1 << U2X0); + UCSR0B = (1 << RXCIE0) | (1 << RXEN0) | (1 <getName()); + printf_P(PSTR("\n")); + } + printf_P(PSTR("\nSelect unit: ")); + rIndex = 0; wIndex = 0; + fgets(s, sizeof(s), stdin); + } while (sscanf(s, "%x", &i) != 1 || i < 0 || i >= sizeof(unit) / sizeof(unit[0]) ); + + TestUnit *pu = unit[i]; + printf_P(PSTR("\n\n[%s]: "), pu->getName()); + keyUart0 = EOF; + + for (uint8_t subtest = 0; subtest < 0xff; subtest++) { + printf_P(PSTR("\n%4d: "), subtest); + if (pu->run(subtest) < 0) { + break; + } + if (keyUart0 == 27) { + keyUart0 = EOF; + break; + } + keyUart0 = EOF; + } + pu->cleanup(); + } +} + +uint64_t millis () { + volatile uint64_t millis = systemMillis; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + millis = systemMillis; + } + return millis; +} + +#ifndef USART0_RX_vect + #define USART0_RX_vect USART_RX_vect +#endif +ISR (USART0_RX_vect) { + uint8_t b = UDR0; + keyUart0 = b; + uartBuffer[wIndex++] = b; + // printf_P(PSTR(" w%d(%02x)"), wIndex, b); + if (wIndex == rIndex) { + // buffer overflow, kick out oldest byte + rIndex++; + } +} + +#ifdef USART1_RX_vect + ISR (USART1_RX_vect) { + uint8_t b = UDR1; + if (modbus.enabled) { + modbus.handleRxByte(b); + } + if (uart1.enabled) { + uart1.handleRxByte(b); + } + if (ieee485.enabled) { + ieee485.handleRxByte(b); + } + } +#endif + +ISR (TWI_vect) { + if (i2cMaster.enabled) { + i2cMaster.handleTwiIrq(); + } else if (i2cSlave.enabled) { + i2cSlave.handleTwiIrq(); + } else if (i2cSparkfun.enabled) { + i2cSparkfun.handleTwiIrq(); + } else { + TWCR = (1 << TWINT); // clear interrupt request bit and disable TWI + } +} + +ISR (TIMER2_COMPA_vect) { // every 100us + static uint16_t timer500ms = 0; + static uint8_t timer100us = 0; + + if (encoder.enabled) { + encoder.tick100us(); + } + #ifdef MOTOR_HPP + if (motor.enabled) { + motor.tick100us(); + } + #endif + + timer100us++; + if (timer100us >= 10) { + timer100us = 0; + if (timer1ms > 0) { + timer1ms--; + } + systemMillis++; + i2cMaster.tick1ms(); + i2cSlave.tick1ms(); + i2cSparkfun.tick1ms(); + } + + timer500ms++; + if (timer500ms >= 5000) { + PORTC ^= (1 << PORTC3); // orange LED blinking + timer500ms = 0; + } +} + diff --git a/software/arduino-nano-5v/test_2024-07-23/src/main.hpp b/software/arduino-nano-5v/test_2024-07-23/src/main.hpp new file mode 100644 index 0000000..f60ec89 --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/src/main.hpp @@ -0,0 +1,20 @@ +#ifndef MAIN_HPP +#define MAIN_HPP + +#include +#include + +#define ENTER '\r' +#define CTRLC '\003' + +extern int wait (uint32_t ms); +extern uint64_t millis (); + +class TestUnit { + public: + virtual int8_t run (uint8_t subtest) = 0; + virtual void cleanup () = 0; + virtual PGM_P getName () = 0; +}; + +#endif \ No newline at end of file diff --git a/software/arduino-nano-5v/test_2024-07-23/src/units/encoder.cpp b/software/arduino-nano-5v/test_2024-07-23/src/units/encoder.cpp new file mode 100644 index 0000000..bc84f48 --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/src/units/encoder.cpp @@ -0,0 +1,86 @@ +#include +#include + +#include "encoder.hpp" +#include "../main.hpp" + +// PB0/T0 ... Encoder A +// PB1/T1 ... Encoder B +// PB2/INT2 ... push switch of encoder (pushed = 0) + +// Encoder signals on rotation clockwise 1 step: +// A -----____------ one char app. 1ms..2ms (rotation speed) +// B -------___----- +// one step when: A = 0, B= 1->0 + +// Encoder signals on rotation counterclockwise 1 step: +// A -----____------ one char app. 1ms..2ms (rotation speed) +// B --______----- +// one step when: A = 0, B= 0->1 + + +void Encoder::cleanup () { + DDRB &= ~((1 << PB2) | (1 << PB1) | (1 << PB0)); + PORTB &= ~((1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0)); + enabled = 0; +} + +int8_t Encoder::run (uint8_t subtest) { + switch (subtest) { + case 0: { + printf_P(PSTR("init")); + DDRB &= ~((1 << PB2) | (1 << PB1) | (1 << PB0)); + PORTB |= (1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0); // enable pullup + enabled = 1; + return 0; + } + + case 1: { + while (wait(10) == EOF) { + printf_P(PSTR("\r => Encoder (push to clear): ")); + printf_P(PSTR("%5d (0x%02x) "), count, (uint8_t)count); + if ((PINB & (1 << PINB2)) == 0) { + reset(); + } + } + return 0; + } + + case 2: { + printf_P(PSTR("end")); + break; + } + } + + return -1; +} + +struct EncoderState { + uint8_t a:1; // signal A + uint8_t b:1; // signal B +}; + +void Encoder::tick100us () { + static EncoderState lastState = { 1, 1 }; + static EncoderState lastStableState = { 1, 1 }; + + if ((DDRB & 0x03) || (PORTB & 0x07) != 0x07) { + count = 0; + return; // Enocder pins not configured + } + EncoderState nextState = { (PINB & 0x01) == 0x01, (PINB & 0x02) == 0x02 }; + if (nextState.a == lastState.a && nextState.b == lastState.b) { + if (lastStableState.a == 0 && nextState.b != lastStableState.b) { + if (nextState.b == 0) { + count = count < 127 ? count + 1 : 127; + } else { + count = count > -128 ? count - 1 : -128; + } + } + lastStableState.a = nextState.a; + lastStableState.b = nextState.b; + } + lastState.a = nextState.a; + lastState.b = nextState.b; +} + diff --git a/software/arduino-nano-5v/test_2024-07-23/src/units/encoder.hpp b/software/arduino-nano-5v/test_2024-07-23/src/units/encoder.hpp new file mode 100644 index 0000000..10bcc0f --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/src/units/encoder.hpp @@ -0,0 +1,22 @@ +#ifndef ENCODER_HPP +#define ENCODER_PP + +#include +#include "../main.hpp" +#include + +class Encoder : public TestUnit { + public: + uint8_t enabled; + int8_t count; + + public: + Encoder () { reset(); enabled = 0; }; + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Encoder"); } + void reset () { count = 0; } + void tick100us (); +}; + +#endif \ No newline at end of file diff --git a/software/arduino-nano-5v/test_2024-07-23/src/units/i2c.cpp b/software/arduino-nano-5v/test_2024-07-23/src/units/i2c.cpp new file mode 100644 index 0000000..4c5934f --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/src/units/i2c.cpp @@ -0,0 +1,300 @@ +#include +#include +#include +#include +#include + +#include "i2c.hpp" +#include "../adafruit/bme280.h" +#include "../main.hpp" + +// Sparkfun https://www.sparkfun.com/products/22858 +// ENS160 address 0x53 (air quality sensor) +// BME280 address 0x77 /humidity/temperature sensor) +// register 0xfe: humidity_7:0 +// register 0xfd: humidity_15:8 + +PGM_P I2c::getName () { + switch (mode) { + case SparkFunEnvCombo: return PSTR("I2C-Sparkfun Env-Combo"); + case Master: return PSTR("I2C-Master"); + case Slave: return PSTR("I2C-Slave"); + } + return "?"; +} + +void I2c::cleanup () { + enabled = false; + TWCR = (1 << TWEN); + TWBR = 0; + ADMUX = 0; + ADCSRA = 0; +} + +int8_t I2c::run (uint8_t subtest) { + if (subtest == 0) { + TWBR = 13; // 100kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 100000) / (2 * 100000 * 4); + TWBR = 28; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4); + TWBR = 100; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4); + TWCR = (1 << TWEN); + ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V + ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128 + enabled = true; + printf_P(PSTR("init")); + + } else if (subtest == 1 && mode == I2c::SparkFunEnvCombo) { + printf_P(PSTR(" BM280 ... ")); + if (!bm280.begin()) { + printf_P(PSTR("E1")); + return -1; + } + printf_P(PSTR("OK, ENS160 ... ")); + if (!ens160.begin()) { + printf_P(PSTR("E2")); + return -1; + } + if (!ens160.setMode(ENS160_OPMODE_STD)) { + printf_P(PSTR("E3")); + return -1; + } + if (!ens160.set_envdata(25.0, 65)) { + printf_P(PSTR("E4")); + return -1; + } + + printf_P(PSTR("OK")); + float accTemp = 0, accHumidity = 0; + int8_t accCount = -1; + + do { + // BME280 + float p = bm280.readPressure(); + printf_P(PSTR("\n => BM280: P= %.3fbar"), (double)p / 100000.0); + float t = bm280.readTemperature(); + printf_P(PSTR(", T= %.2f°C"), (double)t); + float h = bm280.readHumidity(); + printf_P(PSTR(", H= %.2f%%"), (double)h); + + if (accCount >= 0 && !isnanf(h) && !isnan(t)) { + accTemp += t; + accHumidity += h; + accCount++; + } + + bm280.setSampling( + Adafruit_BME280::MODE_NORMAL, + Adafruit_BME280::SAMPLING_X16, + Adafruit_BME280::SAMPLING_X16, + Adafruit_BME280::SAMPLING_X16, + Adafruit_BME280::FILTER_OFF, + Adafruit_BME280::STANDBY_MS_1000 + ); + + // ENS160 only activated every 32s to avoid wrong temperature measuerment + // if ES160 would be continously active, the temperature would be 4°C higher + // -> ES160 has not enough distance to BM280 + // This solution causes only a +0.3°C higher temperatur value + if (accCount < 0 || accCount >= 32) { + printf_P(PSTR(" | ENS160 (")); + if (accCount > 0) { + h = accHumidity / accCount; + t = accTemp / accCount; + accTemp = 0; + accHumidity = 0; + } + accCount = 0; + if (!ens160.set_envdata(t, h)) { + printf_P(PSTR("E1)")); + } else { + printf_P(PSTR("%.1f°C/%.1f%%): "), (double)t, (double)h); + if (!ens160.setMode(ENS160_OPMODE_STD)) { + printf_P(PSTR("E2")); + } else { + for (uint8_t i = 0; i < 100; i++) { + _delay_ms(15); + uint8_t status; + if (ens160.readStatus(&status)) { + if (status & ENS160_DATA_STATUS_NEWDAT) { + ENS160_DATA data; + if (ens160.readData(&data)) { + printf_P(PSTR(" aqi=%d("), data.aqi); + switch(data.aqi) { + case 1: printf_P(PSTR("excellent")); break; + case 2: printf_P(PSTR("good")); break; + case 3: printf_P(PSTR("moderate")); break; + case 4: printf_P(PSTR("poor")); break; + case 5: printf_P(PSTR("unhealthy")); break; + default: printf_P(PSTR("?")); break; + } + printf_P(PSTR("), tvoc=%dppb"), data.tvoc); + printf_P(PSTR(", eco2=%d("), data.eco2); + if (data.eco2 < 400) { + printf_P(PSTR("?")); + } else if (data.eco2 < 600) { + printf_P(PSTR("excellent")); + } else if (data.eco2 < 800) { + printf_P(PSTR("good")); + } else if (data.eco2 < 1000) { + printf_P(PSTR("fair")); + } else if (data.eco2 < 1500) { + printf_P(PSTR("poor")); + } else { + printf_P(PSTR("bad")); + } + printf_P(PSTR(")")); + } + break; + } + } + } + } + if (!ens160.setMode(ENS160_OPMODE_IDLE)) { + printf_P(PSTR("E3")); + } + } + } + + } while (wait(1000) == EOF); + + } else if (subtest == 1 && mode == I2c::Master) { + if (!master.begin(0x01)) { + printf_P(PSTR("E1")); + return -1; + } + do { + uint8_t buffer[1]; + // read poti + ADCSRA |= (1 << ADSC); // start ADC + while (ADCSRA & (1 << ADSC)) {} // wait for result + buffer[0] = ADCH; + printf_P(PSTR("\n write 0x%02x"), buffer[0]); + if (!master.write(buffer, 1)) { + printf_P(PSTR(" -> ERROR")); + } + printf_P(PSTR(", read ")); + if (master.read(buffer, 1)) { + printf_P(PSTR("0x%02x"), buffer[0]); + } else { + printf_P(PSTR(" -> ERROR")); + } + } while (wait(1000) == EOF); + master.end(); + + } else if (subtest == 1 && mode == I2c::Slave) { + if (!slave.begin(0x01, false)) { + printf_P(PSTR("E1")); + return -1; + } + do { + int fromMaster = slave.read(); + if (fromMaster != EOF) { + ADCSRA |= (1 << ADSC); // start ADC + while (ADCSRA & (1 << ADSC)) {} // wait for result + slave.write(ADCH); + printf_P(PSTR("\n => from master: 0x%02x -> to master: 0x%02x"), fromMaster, ADCH); + } + } while (wait(0) == EOF); + slave.end(); + + } else { + printf_P(PSTR("end")); + return -1; + } + wait(500); + return 0; +} + +void I2c::handleTwiIrq () { + if (mode == I2c::Slave) { + DDRD |= (1 << PD7); + PORTD |= (1 << PD7); + slave.handleTWIIsr(); + PORTD &= ~(1 << PD7); + } else { + TWCR |= (1 << TWINT); // clear Interrupt Request + } +} + +// uint16_t I2c::startRead (uint8_t address) { +// TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // send START condition +// while (!(TWCR & (1 << TWINT))) {}; // wait until last action done +// uint8_t sr = TWSR & 0xf8; +// if (sr != 0x08 && sr != 0x10) { +// return 0x0100 | sr; +// } + +// TWDR = (address << 1) | 0x01; // address + READ (R/W = 1) +// TWCR = (1 << TWINT) | (1 << TWEN); // send address/RW +// while (!(TWCR & (1 << TWINT))) {}; // wait until last action done +// sr = TWSR & 0xf8; +// if (sr != 0x40) { +// return 0x0200 | sr; +// } +// return 0; +// } + +// uint16_t I2c::startWrite (uint8_t address) { +// TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // send START condition +// while (!(TWCR & (1 << TWINT))) {}; // wait until last action done +// uint8_t sr = TWSR & 0xf8; +// if (sr != 0x08 && sr != 0x10) { +// return 0x0300 | sr; +// } + +// TWDR = (address << 1) | 0x00; // address + WRITE (R/W = 0) +// TWCR = (1 << TWINT) | (1 << TWEN); // send address/RW +// while (!(TWCR & (1 << TWINT))) {}; // wait until last action done +// sr = TWSR & 0xf8; +// if (sr != 0x18) { +// return 0x0400 | sr; +// } +// return 0; +// } + +// void I2c::stop () { +// TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); +// } + +// uint16_t I2c::writeData (uint8_t size, const uint8_t *data) { +// while (size-- > 0) { +// TWDR = *data++; +// TWCR = (1 << TWINT) | (1 << TWEN); // send data byte +// while (!(TWCR & (1 << TWINT))) {}; // wait until last action done +// uint8_t sr = TWSR & 0xf8; +// if (sr != 0x28) { +// return 0x0500 | sr; +// } +// } +// return 0; +// } + +// uint16_t I2c::writeByte (uint8_t data) { +// return writeData(1, &data); +// } + +// uint16_t I2c::readData (uint8_t size, uint8_t *data) { +// while (size-- > 0) { +// if (size > 0) { +// TWCR = (1 << TWEA) | (1 << TWINT) | (1 << TWEN); // read data byte with ACK enabled +// } else { +// TWCR = (1 << TWINT) | (1 << TWEN); // read data byte with ACK disabled +// } +// while (!(TWCR & (1 << TWINT))) {}; // wait until last action done +// uint8_t sr = TWSR & 0xf8; +// if ((size > 0 && sr != 0x50) || (size == 0 && sr != 0x58)) { +// return 0x0600 | sr; +// } +// *data++ = TWDR; +// } +// return 0; +// } + +// int32_t I2c::compensateBm280T (int32_t adcT) { +// // int32_t var1, var2, t; +// // var1 = ((((adcT >> 3) - ((int32_t)bm280.digT[0] << 1))) * ((int32_t)bm280.digT[1])) >> 11; +// // var2 = (((((adcT >> 4) - ((int32_t)bm280.digT[0])) * ((adcT >> 4) - ((int32_t)bm280.digT[0]))) >> 12) * ((int32_t)bm280.digT[2])) >> 14; +// // bm280.tFine = var1 + var2; +// // t = (bm280.tFine * 5 + 128) >> 8; +// // return t; +// return -1; +// } diff --git a/software/arduino-nano-5v/test_2024-07-23/src/units/i2c.hpp b/software/arduino-nano-5v/test_2024-07-23/src/units/i2c.hpp new file mode 100644 index 0000000..e1f1802 --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/src/units/i2c.hpp @@ -0,0 +1,42 @@ +#ifndef I2C_HPP +#define I2C_HPP + +#include +#include "../main.hpp" +#include "../adafruit/bme280.h" +#include "../adafruit/ens160.h" +#include "../i2cmaster.hpp" +#include "../i2cslave.hpp" + + +class I2c : public TestUnit { + public: + typedef enum I2cMode { SparkFunEnvCombo, Master, Slave } I2cMode; + + private: + I2cMode mode; + Adafruit_BME280 bm280; + ScioSense_ENS160 ens160; + I2cMaster master; + I2cSlave slave; + + public: + bool enabled; + + public: + I2c (I2cMode mode) { enabled = false; this->mode = mode; } + void tick1ms () { master.tick1ms(); slave.tick1ms(); } + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName (); + void handleTwiIrq (); + uint16_t startRead (uint8_t address); + uint16_t startWrite (uint8_t address); + void stop (); + uint16_t writeByte (uint8_t data); + uint16_t writeData (uint8_t size, const uint8_t *data); + uint16_t readData (uint8_t size, uint8_t *data); + int32_t compensateBm280T (int32_t adcT); +}; + +#endif \ No newline at end of file diff --git a/software/arduino-nano-5v/test_2024-07-23/src/units/ieee485.cpp b/software/arduino-nano-5v/test_2024-07-23/src/units/ieee485.cpp new file mode 100644 index 0000000..414efb2 --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/src/units/ieee485.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include "ieee485.hpp" +#include "../main.hpp" + +#ifdef __AVR_ATmega328P__ + +// Nano-328P +// ------------------------------------ +// IEE485 not supported (no UART1) + +void Ieee485::cleanup () {} +int8_t Ieee485::run (uint8_t subtest) { + return -1; +} + +#endif + + +#ifdef __AVR_ATmega644P__ + +// Nano-644 +// ------------------------------------ +// PB0 ... nRE .. Read enable +// PB1 ... DE .. Data enable + +#define SET_nRE (PORTB |= (1 << PB0)) +#define CLR_nRE (PORTB &= ~(1 << PB0)) +#define SET_DE (PORTB |= (1 << PB1)) +#define CLR_DE (PORTB &= ~(1 << PB1)) + +void Ieee485::cleanup () { + enabled = 0; + + ADMUX = 0; + ADCSRA = 0; + + UCSR1A = 0; + UCSR1B = 0; + UCSR1C = 0; + UBRR1H = 0; + UBRR1L = 0; + PORTD &= ~(1 << PD2); + DDRB &= ~((1 << PB1) | (1 << PB0)); + PORTB &= ~((1 << PB1) | (1 << PB0)); +} + +int8_t Ieee485::run (uint8_t subtest) { + if (subtest == 0) { + // Poti + ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V + ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128 + + // Modbus + SET_nRE; + CLR_DE; + DDRB |= (1 << PB1) | (1 << PB0); + + // UART1 interface on Nano-644 + PORTD |= (1 << PD2); // enable RxD1 pullup + UCSR1A = (1 << U2X1); + UCSR1B = (1 << RXCIE1) | (1 << RXEN1) | (1 < send Byte 0x%02x"), ADCH); + int b; + ATOMIC_BLOCK(ATOMIC_FORCEON) { + b = receivedByte; + receivedByte = -1; + } + if (b >= 0) { + printf_P(PSTR("\n => receive Byte: 0x%02x"), b); + } + } + + } else { + printf_P(PSTR("end")); + return -1; + } + + return 0; +} + +void Ieee485::handleRxByte (uint8_t b) { + receivedByte = b; +} + +#endif \ No newline at end of file diff --git a/software/arduino-nano-5v/test_2024-07-23/src/units/ieee485.hpp b/software/arduino-nano-5v/test_2024-07-23/src/units/ieee485.hpp new file mode 100644 index 0000000..677963f --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/src/units/ieee485.hpp @@ -0,0 +1,21 @@ +#ifndef IEEE485_HPP +#define IEEE485_HPP + +#include +#include "../main.hpp" +#include + +class Ieee485 : public TestUnit { + public: + uint8_t enabled; + int16_t receivedByte; + + public: + Ieee485 () { enabled = 0; receivedByte = -1; } + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual const char *getName () { return PSTR("IEEE485"); } + void handleRxByte (uint8_t); +}; + +#endif \ No newline at end of file diff --git a/software/arduino-nano-5v/test_2024-07-23/src/units/lcd.cpp b/software/arduino-nano-5v/test_2024-07-23/src/units/lcd.cpp new file mode 100644 index 0000000..fa24879 --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/src/units/lcd.cpp @@ -0,0 +1,257 @@ +#include +#include +#include + +#include "lcd.hpp" +#include "../main.hpp" + +#ifdef __AVR_ATmega644P__ + + // Nano-644 + // --------------------------------------------------------------- + // PA3 ..... E --> LCD Enable (Verbindung via J25 erforderlich) + // PD6 ..... R/W --> Read/Write: Read=1, Write=0 + // PD7 ..... RS --> Register Select: Command=0, Data=1 + // PB7:0 ... Data --> Achtung von 5V LCD nicht lesen! + + + // #define LCD_3V3 + #ifdef LCD_3V3 + #define DATA_PIN PINB + #endif + + void Lcd::initDDR () { + DDRA |= (1 << PA3); + DDRB = 0xff; + DDRD |= (1 << PD7) | (1 << PD6); + } + + void Lcd::cleanup () { + DDRA &= ~(1 << PA3); + DDRB = 0; + DDRD &= ~((1 << PD7) | (1 << PD6)); + } + + void Lcd::setRS () { PORTD |= (1 << PD7); } + void Lcd::clrRS () { PORTD &= ~(1 << PD7); } + void Lcd::setRW () { PORTD |= (1 << PD6); } + void Lcd::clrRW () { PORTD &= ~(1 << PD6); } + void Lcd::setE () { PORTA |= (1 << PA3); } + void Lcd::clrE () { PORTA &= ~(1 << PA3); } + + void Lcd::setData (uint8_t data) { + PORTB = data; + } +#endif + +#ifdef __AVR_ATmega328P__ + + // Arduino Nano (5V) + // --------------------------------------------------------------- + // PC3 ..... E --> LCD Enable (Verbindung via J25 erforderlich) + // PD3 ..... R/W --> Read/Write: Read=1, Write=0 + // PD2 ..... RS --> Register Select: Command=0, Data=1 + // PD4 ..... Data0 + // PB0 ..... Data1 + // PD7 ..... Data2 + // PD6 ..... Data3 + // PB2 ..... Data4 + // PB3 ..... Data5 + // PB4 ..... Data6 + // PB5 ..... Data7 + + void Lcd::initDDR () { + DDRB |= (1 << PB5) | (1 << PB4) | (1 << PB3) | (1 << PB2) | (1 << PB0); + DDRD |= (1 << PD7) | (1 << PD6) | (1 << PD4) | (1 << PD3) | (1 << PD2); + } + + void Lcd::cleanup () { + DDRB &= ~((1 << PB5) | (1 << PB4) | (1 << PB3) | (1 << PB2) | (1 << PB0)); + DDRD &= ~((1 << PD7) | (1 << PD6) | (1 << PD4) | (1 << PD3) | (1 << PD2)); + } + + void Lcd::setRS () { PORTD |= (1 << PD2); } + void Lcd::clrRS () { PORTD &= ~(1 << PD2); } + void Lcd::setRW () { PORTD |= (1 << PD3); } + void Lcd::clrRW () { PORTD &= ~(1 << PD3); } + void Lcd::setE () { PORTC |= (1 << PC3); } + void Lcd::clrE () { PORTC &= ~(1 << PC3); } + + void Lcd::setData (uint8_t data) { + if (data & 0x01) PORTD |= (1 << PD4); else PORTD &= ~((1 << PD4)); + if (data & 0x02) PORTB |= (1 << PB0); else PORTB &= ~((1 << PB0)); + if (data & 0x04) PORTD |= (1 << PD7); else PORTD &= ~((1 << PD7)); + if (data & 0x08) PORTD |= (1 << PD6); else PORTD &= ~((1 << PD6)); + if (data & 0x10) PORTB |= (1 << PB2); else PORTB &= ~((1 << PB2)); + if (data & 0x20) PORTB |= (1 << PB3); else PORTB &= ~((1 << PB3)); + if (data & 0x40) PORTB |= (1 << PB4); else PORTB &= ~((1 << PB4)); + if (data & 0x80) PORTB |= (1 << PB5); else PORTB &= ~((1 << PB5)); + } +#endif + +// Befehle für das Display + +#define DISP_CLEAR 0b00000001 // Display clear +#define DISP_ON 0b00001111 // Display on +#define DISP_OFF 0b00001011 // Display off +#define CURSOR_ON 0b00001111 // Cursor on +#define CURSOR_OFF 0b00001101 // Cursor off +#define BLINK_ON 0b00001111 // Cursor Blink +#define BLINK_OFF 0b00001110 // Cursor No Blink + +int8_t Lcd::run (uint8_t subtest) { + if (subtest == 0) { + init(); + #ifdef LCD_3V3 + printf_P(PSTR("init 3.3V LCD")); + #else + printf_P(PSTR("init 5V LCD")); + #endif + + } else if (subtest == 1) { + for (uint8_t i = 0; i < 20 * 4; i++) { + char c = (char)(i + 32); + if (i % 20 == 0) { + setCursor(i / 20 + 1, 1); + } + writeData(c); + while (isBusy()) {}; + } + // setCursor(1, 1); + // writeString(" 1234567890<>,;.:-_#+"); + // setCursor(2, 1); + // writeString("abcdefghijklmnopqrst"); + // setCursor(3, 1); + // writeString("uvwxyzABCDEFGHIJKLMN"); + // setCursor(4, 1); + // writeString("OPQRSTUVWXYZ "); + printf_P(PSTR("LCD beschrieben")); + while (wait(1) == EOF) { + } + + } else { + printf_P(PSTR("end")); + return -1; + } + wait(500); + return 0; +} + +void Lcd::init () { + clrRW(); + clrRS(); + clrE(); + setData(0); + initDDR(); + _delay_ms(16); // min 15ms warten für Reset des Displays + + setData( 0b00111011 ); // 8bit Modus, 5x7 Zeichen, Mehrzeilen Display + clrRW(); // write + clrRS(); // command + setE(); // E = 1 (transfer start) + _delay_us(10); // min. 10us + clrE(); // E = 0 (transfer end) + _delay_ms(5); // min. 4.1ms + + setData( 0b00111011 ); // 8bit Modus, 5x7 Zeichen, Mehrzeilen Display + clrRW(); // write + clrRS(); // command + setE(); // E = 1 (transfer start) + _delay_us(10); // min. 10us + clrE(); // E = 0 (transfer end) + _delay_us(100); // min. 100us + + setData( 0b00111011 ); // 8bit Modus, 5x7 Zeichen, Mehrzeilen Display + clrRW(); // write + clrRS(); // command + setE(); // E = 1 (transfer start) + _delay_us(10); // min. 10us + clrE(); // E = 0 (transfer end) + _delay_us(100); // min. 100us + + writeCommand(DISP_OFF); // Display aus + while(isBusy()) {}; + writeCommand(DISP_ON); // Display ein + while(isBusy()) {}; + writeCommand( BLINK_OFF & CURSOR_OFF); // Blink aus und Cursor aus + while(isBusy()) {}; + writeCommand(DISP_CLEAR);// Clear display + while(isBusy()) {}; +} + +uint8_t Lcd::isBusy () { + #ifdef LCD_3V3 + // DIR_DATA_PORT = 0; + // SET_RW_PIN; // read + // CLR_RS_PIN; // command + // SET_E_PIN; // E = 1 (transfer start) + // _delay_us(10); + // uint8_t busy = DATA_PIN & 0x80; // read bit 7 (busy bit) + // CLR_E_PIN; // E = 0 (transfer end) + // CLR_RW_PIN; + // DIR_DATA_PORT = 0xff; + // return busy != 0; + _delay_us(200); + return 0; + #else + _delay_us(200); + return 0; + #endif + +} + +void Lcd::writeCommand (uint8_t cmd) { + setData(cmd); + clrRW(); // write + clrRS(); // command + setE(); // E = 1 (transfer start) + _delay_us(10); // min. 10us + clrE(); // E = 0 (transfer end) + writeData(0); +} + +void Lcd::setDDRamAddr (uint8_t address) { + setData(address | 0x80); + clrRW(); // write + clrRS(); // command + setE(); // E = 1 (transfer start) + _delay_us(10); // min. 10us + clrE(); // E = 0 (transfer end) + _delay_us(10); // min. 10us + writeData(0); +} + +void Lcd::writeString (const char *s) { + while (*s) { + writeData(*s++); + while (isBusy()) {}; + } +} + +void Lcd::writeData (uint8_t data) { + setData(data); + setRS(); // data + clrRW(); // write + setE(); // E = 1 (transfer start) + _delay_us(10); // min. 10us + clrE(); // E = 0 (transfer end) + _delay_us(10); // min. 10us + clrRS(); + writeData(0); +} + +void Lcd::setCursor (uint8_t row, uint8_t column) { + uint8_t b; + if (column > 20) { + return; + } + switch (row) { + case 1: b = 0x00 + column - 1; break; + case 2: b = 0x40 + column - 1; break; + case 3: b = 0x14 + column - 1; break; + case 4: b = 0x54 + column - 1; break; + default: return; + } + setDDRamAddr(b); + while (isBusy()) {}; +} diff --git a/software/arduino-nano-5v/test_2024-07-23/src/units/lcd.hpp b/software/arduino-nano-5v/test_2024-07-23/src/units/lcd.hpp new file mode 100644 index 0000000..142a13f --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/src/units/lcd.hpp @@ -0,0 +1,34 @@ +#ifndef LCD_HPP +#define LCD_HPP + +#include +#include "../main.hpp" +#include + +class Lcd : public TestUnit { + public: + Lcd () {}; + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Lcd"); } + + private: + void init (); + void initDDR (); + uint8_t isBusy (); + void writeCommand (uint8_t); + void setDDRamAddr (uint8_t); + void writeString (const char *s); + void writeData (uint8_t); + void setCursor (uint8_t row, uint8_t column); + void setRS (); + void clrRS (); + void setRW (); + void clrRW (); + void setE (); + void clrE (); + void setData (uint8_t data); + +}; + +#endif \ No newline at end of file diff --git a/software/arduino-nano-5v/test_2024-07-23/src/units/led.cpp b/software/arduino-nano-5v/test_2024-07-23/src/units/led.cpp new file mode 100644 index 0000000..6dc6253 --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/src/units/led.cpp @@ -0,0 +1,134 @@ +#include +#include +#include + +#include "led.hpp" +#include "../main.hpp" + +#ifdef __AVR_ATmega644P__ + + // Nano-644 + // --------------------------------------------------------------- + // PD4 ..... Red + // PD5 ..... Orange/Yellow + // PD6 ..... Green + // PD7 ..... Blue + + void Led::initDDR () { + DDRD |= (1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4); + } + + void Led::cleanup () { + DDRD &= ~((1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4)); + PORTD &= ~((1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4)); + } + + void Led::setLed (LED led, bool on) { + if (on) { + switch(led) { + case RED: PORTD |= (1 << PD4); break; + case ORANGE: PORTD |= (1 << PD5); break; + case GREEN: PORTD |= (1 << PD6); break; + case BLUE: PORTD |= (1 << PD7); break; + } + } else { + switch(led) { + case RED: PORTD &= ~(1 << PD4); break; + case ORANGE: PORTD &= ~(1 << PD5); break; + case GREEN: PORTD &= ~(1 << PD6); break; + case BLUE: PORTD &= ~(1 << PD7); break; + } + } + } + + void Led::ledToggle (LED led) { + case RED: PORTD ^= (1 << PD4); break; + case ORANGE: PORTD ^= (1 << PD5); break; + case GREEN: PORTD ^= (1 << PD6); break; + case BLUE: PORTD ^= (1 << PD7); break; + } + +#endif + +#ifdef __AVR_ATmega328P__ + + // Arduino-Nano-5V + // --------------------------------------------------------------- + // PD5 ..... Red + // PB1 ..... Orange/Yellow + // PD3 ..... Green + // PD2 ..... Blue + + void Led::initDDR () { + DDRD |= (1 << PD5) | (1 << PD3) | (1 << PD2); + DDRB |= (1 << PB1); + } + + void Led::cleanup () { + DDRD &= ~((1 << PD5) | (1 << PD3) | (1 << PD2)); + DDRB &= ~(1 << PB1); + PORTD &= ~((1 << PD5) | (1 << PD3) | (1 << PD2)); + PORTB &= ~(1 << PB1); + } + + void Led::setLed (LED led, bool on) { + if (on) { + switch(led) { + case RED: PORTD |= (1 << PD5); break; + case ORANGE: PORTB |= (1 << PB1); break; + case GREEN: PORTD |= (1 << PD3); break; + case BLUE: PORTD |= (1 << PD2); break; + } + } else { + switch(led) { + case RED: PORTD &= ~(1 << PD5); break; + case ORANGE: PORTB &= ~(1 << PB1); break; + case GREEN: PORTD &= ~(1 << PD3); break; + case BLUE: PORTD &= ~(1 << PD2); break; + } + } + } + + void Led::ledToggle (LED led) { + switch(led) { + case RED: PORTD ^= (1 << PD5); break; + case ORANGE: PORTB ^= (1 << PB1); break; + case GREEN: PORTD ^= (1 << PD3); break; + case BLUE: PORTD ^= (1 << PD2); break; + } + } + +#endif + +void Led::ledOn (LED led) { + setLed(led, true); +} + +void Led::ledOff (LED led) { + setLed(led, false); +} + + +int8_t Led::run (uint8_t subtest) { + if (subtest == 0) { + initDDR(); + printf_P(PSTR("init")); + + } else if (subtest <= 16) { + subtest = (subtest - 1) % 4; + switch (subtest) { + case 0: ledOff(BLUE); ledOn(RED); break; + case 1: ledOff(RED); ledOn(ORANGE); break; + case 2: ledOff(ORANGE); ledOn(GREEN); break; + case 3: ledOff(GREEN); ledOn(BLUE); break; + } + printf_P(PSTR("Test LED PD%d"), subtest + 4); + + } else { + printf_P(PSTR("end")); + return -1; + } + wait(500); + return 0; +} + diff --git a/software/arduino-nano-5v/test_2024-07-23/src/units/led.hpp b/software/arduino-nano-5v/test_2024-07-23/src/units/led.hpp new file mode 100644 index 0000000..0a28f2d --- /dev/null +++ b/software/arduino-nano-5v/test_2024-07-23/src/units/led.hpp @@ -0,0 +1,28 @@ +#ifndef LED_HPP +#define LED_HPP + +#include +#include "../main.hpp" +#include + +class Led : public TestUnit { + public: + enum LED { RED, ORANGE, GREEN, BLUE }; + + + public: + Led () {}; + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Led"); } + + void setLed (LED led, bool on); + void ledOn (LED led); + void ledOff (LED led); + void ledToggle (LED led); + + private: + void initDDR (); +}; + +#endif \ No newline at end of file diff --git a/software/nano-644/test_2024-07-23/.gdb_history b/software/nano-644/test_2024-07-23/.gdb_history new file mode 100644 index 0000000..3339046 --- /dev/null +++ b/software/nano-644/test_2024-07-23/.gdb_history @@ -0,0 +1,9 @@ +target remote :1234 +layout split +stepi +quit +target remote :1234 +layout split +stepi +b *main+9 +quit diff --git a/software/nano-644/test_2024-07-23/.gdbinit b/software/nano-644/test_2024-07-23/.gdbinit new file mode 100644 index 0000000..139597f --- /dev/null +++ b/software/nano-644/test_2024-07-23/.gdbinit @@ -0,0 +1,2 @@ + + diff --git a/software/nano-644/test_2024-07-23/.gitignore b/software/nano-644/test_2024-07-23/.gitignore new file mode 100644 index 0000000..a959910 --- /dev/null +++ b/software/nano-644/test_2024-07-23/.gitignore @@ -0,0 +1,4 @@ +.depend +**/build +**/dist +**/sim diff --git a/software/nano-644/test_2024-07-23/.vscode/c_cpp_properties.json b/software/nano-644/test_2024-07-23/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..3a57c79 --- /dev/null +++ b/software/nano-644/test_2024-07-23/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "Linux AVR", + "includePath": [ + "/usr/lib/avr/include/**", + "/usr/lib/gcc/avr/**" + ], + "defines": [], + "compilerPath": "/usr/bin/avr-gcc", + "compilerArgs": [ "-mmcu=atmega644p", "-DF_CPU=12000000", "-Os" ], + "cStandard": "gnu11", + "cppStandard": "gnu++11", + "intelliSenseMode": "linux-gcc-x64" + } + ], + "version": 4 +} diff --git a/software/nano-644/test_2024-07-23/.vscode/launch.json b/software/nano-644/test_2024-07-23/.vscode/launch.json new file mode 100644 index 0000000..f29cf2e --- /dev/null +++ b/software/nano-644/test_2024-07-23/.vscode/launch.json @@ -0,0 +1,37 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Build", + // "request": "launch", + "type": "node-terminal", + "preLaunchTask": "build" + },{ + "name": "Flash", + // "request": "launch", + "type": "node-terminal", + "preLaunchTask": "flash" + },{ + "name": "Clean", + // "request": "launch", + "type": "node-terminal", + "preLaunchTask": "clean" + },{ + // es muss mit simuc --board arduino dist/programm.elf der Simulator + // gestartet werden. Dessen gdb-stub öffnet auf localhost:1234 einen Port + "name": "Debug (simuc)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/sim/atmega328p.elf", + "cwd": "${workspaceFolder}", + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/avr-gdb", + "miDebuggerServerAddress": ":1234", + "preLaunchTask": "build" + } + ] +} diff --git a/software/nano-644/test_2024-07-23/.vscode/settings.json b/software/nano-644/test_2024-07-23/.vscode/settings.json new file mode 100644 index 0000000..b2e94c9 --- /dev/null +++ b/software/nano-644/test_2024-07-23/.vscode/settings.json @@ -0,0 +1,26 @@ +{ + "[c]": { + "editor.insertSpaces": true, + "editor.tabSize": 3, + "editor.detectIndentation": false + }, + "[cpp]": { + "editor.insertSpaces": true, + "editor.tabSize": 3, + "editor.detectIndentation": false + }, + "[h]": { + "editor.insertSpaces": true, + "editor.tabSize": 3, + "editor.detectIndentation": false + }, + "[hpp]": { + "editor.insertSpaces": true, + "editor.tabSize": 3, + "editor.detectIndentation": false + }, + "cSpell.words": [], + "cSpell.ignorePaths": [ + "**/*.json", "**/*.c", "**/*.h", "**/*.cpp", "**/*.hpp", "**/Makefile" + ] +} diff --git a/software/nano-644/test_2024-07-23/.vscode/tasks.json b/software/nano-644/test_2024-07-23/.vscode/tasks.json new file mode 100644 index 0000000..74fb1c7 --- /dev/null +++ b/software/nano-644/test_2024-07-23/.vscode/tasks.json @@ -0,0 +1,23 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [{ + "label": "build", + "type": "shell", + "command": "make", + "problemMatcher":[ + "$gcc" + ] + },{ + "label": "clean", + "type": "shell", + "command": "make", + "args": [ "clean" ], + },{ + "label": "flash", + "type": "shell", + "command": "make", + "args": [ "flash" ], + }] +} \ No newline at end of file diff --git a/software/nano-644/test_2024-07-23/Makefile b/software/nano-644/test_2024-07-23/Makefile new file mode 100644 index 0000000..12a996a --- /dev/null +++ b/software/nano-644/test_2024-07-23/Makefile @@ -0,0 +1,82 @@ +.PHONY: all info flash picocom clean +$(shell mkdir -p dist >/dev/null) +$(shell mkdir -p build >/dev/null) +$(shell mkdir -p sim >/dev/null) + +NAME="test_2024-07-23_nano-644" +SRC= $(wildcard src/*.c src/*.cpp src/*/*.cpp) +OBJ = $(SRC:src/%.c=build/%.o) +OBJ_SIM = $(SRC:src/%.c=sim/%.o) + +DEVICE=atmega644p + +CC= avr-g++ +CFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=12000000 -c +LFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=12000000 -Wl,-u,vfprintf -lprintf_flt -lm + +CFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=12000000 -g -c +LFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=12000000 -g -Wl,-u,vfprintf -lprintf_flt -lm + + +all: dist/$(NAME).elf dist/$(NAME).s dist/$(NAME).hex sim/$(NAME).elf sim/$(NAME).s info + +info: + @echo + @avr-size --mcu=$(DEVICE) --format=avr dist/$(NAME).elf + +.depend: $(SRC) + $(CC) -MM $(SRC) > .depend + +-include $(DEPENDFILE) + +dist/$(NAME).elf: .depend $(OBJ) + $(CC) $(LFLAGS) -o $@ $(OBJ) + +dist/%.s: dist/%.elf + avr-objdump -d $< > $@ + +dist/%.hex: dist/%.elf + avr-objcopy -O ihex $(HEX_FLASH_FLAGS) $< $@ + +sim/$(NAME).elf: .depend $(OBJ_SIM) + $(CC) $(LFLAGS_SIM) -o $@ $(OBJ_SIM) + + +build/%.o: src/%.c + $(CC) $(CFLAGS) -o $@ $< + +sim/%.o: src/%.c + $(CC) $(CFLAGS_SIM) -o $@ $< + +sim/%.s: sim/%.elf + avr-objdump -d $< > $@ + +simuc: sim/$(NAME).elf + simuc --board arduino $< + +gdb: sim/$(NAME).elf + avr-gdb $< + +isp: + avrdude -c usbasp -p m644p + +isp-flash: dist/$(NAME).elf all + avrdude -c usbasp -p m644p -e -U flash:w:$< + +flash: dist/$(NAME).elf all + avrdude -c arduino -p m644p -P /dev/ttyUSB0 -b 115200 -e -U flash:w:$< + + +picocom: + # picocom sends CR for ENTER -> convert cr (\r) to lf (\n) + picocom -b 115200 --omap crlf /dev/ttyUSB0 + +isp-fuse: + avrdude -c usbasp -p m644p -U lfuse:w:0xFF:m -U hfuse:w:0xD9:m -U efuse:w:0xFF:m -U lock:w:0xFF:m + +clean: + @rm -r dist + @rm -r build + @rm -r sim + @find . -type f -name ".depend" -exec rm {} \; + @echo "clean done" diff --git a/software/nano-644/test_2024-07-23/README.md b/software/nano-644/test_2024-07-23/README.md new file mode 100644 index 0000000..c4d5e14 --- /dev/null +++ b/software/nano-644/test_2024-07-23/README.md @@ -0,0 +1 @@ +# Testprogramm diff --git a/software/nano-644/test_2024-07-23/src/adafruit/bme280.cpp b/software/nano-644/test_2024-07-23/src/adafruit/bme280.cpp new file mode 100644 index 0000000..95a0f49 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/adafruit/bme280.cpp @@ -0,0 +1,512 @@ +#include "bme280.h" +#include +#include + +Adafruit_BME280 theBME280; +Adafruit_BME280_Temp bm280TempSensor; +Adafruit_BME280_Pressure bm280PressureSensor; +Adafruit_BME280_Humidity bm280HumiditySensor; + +Adafruit_BME280::Adafruit_BME280() { + static I2cMaster i2cDevice; + t_fine_adjust = 0; + temp_sensor = &bm280TempSensor; + pressure_sensor = &bm280PressureSensor; + humidity_sensor = &bm280HumiditySensor; + i2c_dev = &i2cDevice; +} + +bool Adafruit_BME280::begin (uint8_t addr) { + if (!i2c_dev->begin(addr)) { + return false; + } + return init(); +} + +bool Adafruit_BME280::init() { + _sensorID = read8(BME280_REGISTER_CHIPID); + if (_sensorID != 0x60) { + return false; + } + write8(BME280_REGISTER_SOFTRESET, 0xB6); + _delay_ms(10); // wait for chip to wake up. + + // if chip is still reading calibration, delay + while (isReadingCalibration()) { + _delay_ms(10); + } + + readCoefficients(); // read trimming parameters, see DS 4.2.2 + setSampling(); // use defaults + _delay_ms(100); + + return true; +} + +/*! + * @brief setup sensor with given parameters / settings + * + * This is simply a overload to the normal begin()-function, so SPI users + * don't get confused about the library requiring an address. + * @param mode the power mode to use for the sensor + * @param tempSampling the temp samping rate to use + * @param pressSampling the pressure sampling rate to use + * @param humSampling the humidity sampling rate to use + * @param filter the filter mode to use + * @param duration the standby duration to use + */ +void Adafruit_BME280::setSampling(sensor_mode mode, + sensor_sampling tempSampling, + sensor_sampling pressSampling, + sensor_sampling humSampling, + sensor_filter filter, + standby_duration duration) { + _measReg.mode = mode; + _measReg.osrs_t = tempSampling; + _measReg.osrs_p = pressSampling; + + _humReg.osrs_h = humSampling; + _configReg.filter = filter; + _configReg.t_sb = duration; + _configReg.spi3w_en = 0; + + // making sure sensor is in sleep mode before setting configuration + // as it otherwise may be ignored + write8(BME280_REGISTER_CONTROL, MODE_SLEEP); + + // you must make sure to also set REGISTER_CONTROL after setting the + // CONTROLHUMID register, otherwise the values won't be applied (see + // DS 5.4.3) + write8(BME280_REGISTER_CONTROLHUMID, _humReg.get()); + write8(BME280_REGISTER_CONFIG, _configReg.get()); + write8(BME280_REGISTER_CONTROL, _measReg.get()); +} + +/*! + * @brief Writes an 8 bit value over I2C or SPI + * @param reg the register address to write to + * @param value the value to write to the register + */ +void Adafruit_BME280::write8(byte reg, byte value) { + byte buffer[2]; + buffer[1] = value; + if (i2c_dev) { + buffer[0] = reg; + i2c_dev->write(buffer, 2); + } +} + +/*! + * @brief Reads an 8 bit value over I2C or SPI + * @param reg the register address to read from + * @returns the data byte read from the device + */ +uint8_t Adafruit_BME280::read8(byte reg) { + uint8_t buffer[1]; + if (i2c_dev) { + buffer[0] = uint8_t(reg); + i2c_dev->write_then_read(buffer, 1, buffer, 1); + } + return buffer[0]; +} + +/*! + * @brief Reads a 16 bit value over I2C or SPI + * @param reg the register address to read from + * @returns the 16 bit data value read from the device + */ +uint16_t Adafruit_BME280::read16(byte reg) { + uint8_t buffer[2]; + + if (i2c_dev) { + buffer[0] = uint8_t(reg); + i2c_dev->write_then_read(buffer, 1, buffer, 2); + } + return uint16_t(buffer[0]) << 8 | uint16_t(buffer[1]); +} + +/*! + * @brief Reads a signed 16 bit little endian value over I2C or SPI + * @param reg the register address to read from + * @returns the 16 bit data value read from the device + */ +uint16_t Adafruit_BME280::read16_LE(byte reg) { + uint16_t temp = read16(reg); + return (temp >> 8) | (temp << 8); +} + +/*! + * @brief Reads a signed 16 bit value over I2C or SPI + * @param reg the register address to read from + * @returns the 16 bit data value read from the device + */ +int16_t Adafruit_BME280::readS16(byte reg) { return (int16_t)read16(reg); } + +/*! + * @brief Reads a signed little endian 16 bit value over I2C or SPI + * @param reg the register address to read from + * @returns the 16 bit data value read from the device + */ +int16_t Adafruit_BME280::readS16_LE(byte reg) { + return (int16_t)read16_LE(reg); +} + +/*! + * @brief Reads a 24 bit value over I2C + * @param reg the register address to read from + * @returns the 24 bit data value read from the device + */ +uint32_t Adafruit_BME280::read24(byte reg) { + uint8_t buffer[3]; + + if (i2c_dev) { + buffer[0] = uint8_t(reg); + i2c_dev->write_then_read(buffer, 1, buffer, 3); + } + return uint32_t(buffer[0]) << 16 | uint32_t(buffer[1]) << 8 | + uint32_t(buffer[2]); +} + +/*! + * @brief Take a new measurement (only possible in forced mode) + @returns true in case of success else false + */ +bool Adafruit_BME280::takeForcedMeasurement(void) { + bool return_value = false; + // If we are in forced mode, the BME sensor goes back to sleep after each + // measurement and we need to set it to forced mode once at this point, so + // it will take the next measurement and then return to sleep again. + // In normal mode simply does new measurements periodically. + if (_measReg.mode == MODE_FORCED) { + return_value = true; + // set to forced mode, i.e. "take next measurement" + write8(BME280_REGISTER_CONTROL, _measReg.get()); + // Store current time to measure the timeout + uint32_t timeout_start = millis(); + // wait until measurement has been completed, otherwise we would read the + // the values from the last measurement or the timeout occurred after 2 sec. + while (read8(BME280_REGISTER_STATUS) & 0x08) { + // In case of a timeout, stop the while loop + if ((millis() - timeout_start) > 2000) { + return_value = false; + break; + } + _delay_ms(1); + } + } + return return_value; +} + +/*! + * @brief Reads the factory-set coefficients + */ +void Adafruit_BME280::readCoefficients(void) { + _bme280_calib.dig_T1 = read16_LE(BME280_REGISTER_DIG_T1); + _bme280_calib.dig_T2 = readS16_LE(BME280_REGISTER_DIG_T2); + _bme280_calib.dig_T3 = readS16_LE(BME280_REGISTER_DIG_T3); + + _bme280_calib.dig_P1 = read16_LE(BME280_REGISTER_DIG_P1); + _bme280_calib.dig_P2 = readS16_LE(BME280_REGISTER_DIG_P2); + _bme280_calib.dig_P3 = readS16_LE(BME280_REGISTER_DIG_P3); + _bme280_calib.dig_P4 = readS16_LE(BME280_REGISTER_DIG_P4); + _bme280_calib.dig_P5 = readS16_LE(BME280_REGISTER_DIG_P5); + _bme280_calib.dig_P6 = readS16_LE(BME280_REGISTER_DIG_P6); + _bme280_calib.dig_P7 = readS16_LE(BME280_REGISTER_DIG_P7); + _bme280_calib.dig_P8 = readS16_LE(BME280_REGISTER_DIG_P8); + _bme280_calib.dig_P9 = readS16_LE(BME280_REGISTER_DIG_P9); + + _bme280_calib.dig_H1 = read8(BME280_REGISTER_DIG_H1); + _bme280_calib.dig_H2 = readS16_LE(BME280_REGISTER_DIG_H2); + _bme280_calib.dig_H3 = read8(BME280_REGISTER_DIG_H3); + _bme280_calib.dig_H4 = ((int8_t)read8(BME280_REGISTER_DIG_H4) << 4) | + (read8(BME280_REGISTER_DIG_H4 + 1) & 0xF); + _bme280_calib.dig_H5 = ((int8_t)read8(BME280_REGISTER_DIG_H5 + 1) << 4) | + (read8(BME280_REGISTER_DIG_H5) >> 4); + _bme280_calib.dig_H6 = (int8_t)read8(BME280_REGISTER_DIG_H6); +} + +/*! + * @brief return true if chip is busy reading cal data + * @returns true if reading calibration, false otherwise + */ +bool Adafruit_BME280::isReadingCalibration(void) { + uint8_t const rStatus = read8(BME280_REGISTER_STATUS); + + return (rStatus & (1 << 0)) != 0; +} + +/*! + * @brief Returns the temperature from the sensor + * @returns the temperature read from the device + */ +float Adafruit_BME280::readTemperature(void) { + int32_t var1, var2; + + int32_t adc_T = read24(BME280_REGISTER_TEMPDATA); + if (adc_T == 0x800000) // value in case temp measurement was disabled + return NAN; + adc_T >>= 4; + + var1 = (int32_t)((adc_T / 8) - ((int32_t)_bme280_calib.dig_T1 * 2)); + var1 = (var1 * ((int32_t)_bme280_calib.dig_T2)) / 2048; + var2 = (int32_t)((adc_T / 16) - ((int32_t)_bme280_calib.dig_T1)); + var2 = (((var2 * var2) / 4096) * ((int32_t)_bme280_calib.dig_T3)) / 16384; + + t_fine = var1 + var2 + t_fine_adjust; + + int32_t T = (t_fine * 5 + 128) / 256; + + return (float)T / 100; +} + +/*! + * @brief Returns the pressure from the sensor + * @returns the pressure value (in Pascal) read from the device + */ +float Adafruit_BME280::readPressure(void) { + int64_t var1, var2, var3, var4; + + readTemperature(); // must be done first to get t_fine + + int32_t adc_P = read24(BME280_REGISTER_PRESSUREDATA); + if (adc_P == 0x800000) // value in case pressure measurement was disabled + return NAN; + adc_P >>= 4; + + var1 = ((int64_t)t_fine) - 128000; + var2 = var1 * var1 * (int64_t)_bme280_calib.dig_P6; + var2 = var2 + ((var1 * (int64_t)_bme280_calib.dig_P5) * 131072); + var2 = var2 + (((int64_t)_bme280_calib.dig_P4) * 34359738368); + var1 = ((var1 * var1 * (int64_t)_bme280_calib.dig_P3) / 256) + + ((var1 * ((int64_t)_bme280_calib.dig_P2) * 4096)); + var3 = ((int64_t)1) * 140737488355328; + var1 = (var3 + var1) * ((int64_t)_bme280_calib.dig_P1) / 8589934592; + + if (var1 == 0) { + return 0; // avoid exception caused by division by zero + } + + var4 = 1048576 - adc_P; + var4 = (((var4 * 2147483648UL) - var2) * 3125) / var1; + var1 = (((int64_t)_bme280_calib.dig_P9) * (var4 / 8192) * (var4 / 8192)) / + 33554432; + var2 = (((int64_t)_bme280_calib.dig_P8) * var4) / 524288; + var4 = ((var4 + var1 + var2) / 256) + (((int64_t)_bme280_calib.dig_P7) * 16); + + float P = var4 / 256.0; + + return P; +} + +/*! + * @brief Returns the humidity from the sensor + * @returns the humidity value read from the device + */ +float Adafruit_BME280::readHumidity(void) { + int32_t var1, var2, var3, var4, var5; + + readTemperature(); // must be done first to get t_fine + + int32_t adc_H = read16(BME280_REGISTER_HUMIDDATA); + if (adc_H == 0x8000) // value in case humidity measurement was disabled + return NAN; + + var1 = t_fine - ((int32_t)76800); + var2 = (int32_t)(adc_H * 16384); + var3 = (int32_t)(((int32_t)_bme280_calib.dig_H4) * 1048576); + var4 = ((int32_t)_bme280_calib.dig_H5) * var1; + var5 = (((var2 - var3) - var4) + (int32_t)16384) / 32768; + var2 = (var1 * ((int32_t)_bme280_calib.dig_H6)) / 1024; + var3 = (var1 * ((int32_t)_bme280_calib.dig_H3)) / 2048; + var4 = ((var2 * (var3 + (int32_t)32768)) / 1024) + (int32_t)2097152; + var2 = ((var4 * ((int32_t)_bme280_calib.dig_H2)) + 8192) / 16384; + var3 = var5 * var2; + var4 = ((var3 / 32768) * (var3 / 32768)) / 128; + var5 = var3 - ((var4 * ((int32_t)_bme280_calib.dig_H1)) / 16); + var5 = (var5 < 0 ? 0 : var5); + var5 = (var5 > 419430400 ? 419430400 : var5); + uint32_t H = (uint32_t)(var5 / 4096); + + return (float)H / 1024.0; +} + +/*! + * Calculates the altitude (in meters) from the specified atmospheric + * pressure (in hPa), and sea-level pressure (in hPa). + * @param seaLevel Sea-level pressure in hPa + * @returns the altitude value read from the device + */ +float Adafruit_BME280::readAltitude(float seaLevel) { + // Equation taken from BMP180 datasheet (page 16): + // http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf + + // Note that using the equation from wikipedia can give bad results + // at high altitude. See this thread for more information: + // http://forums.adafruit.com/viewtopic.php?f=22&t=58064 + + float atmospheric = readPressure() / 100.0F; + return 44330.0 * (1.0 - pow(atmospheric / seaLevel, 0.1903)); +} + +/*! + * Calculates the pressure at sea level (in hPa) from the specified + * altitude (in meters), and atmospheric pressure (in hPa). + * @param altitude Altitude in meters + * @param atmospheric Atmospheric pressure in hPa + * @returns the pressure at sea level (in hPa) from the specified altitude + */ +float Adafruit_BME280::seaLevelForAltitude(float altitude, float atmospheric) { + // Equation taken from BMP180 datasheet (page 17): + // http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf + + // Note that using the equation from wikipedia can give bad results + // at high altitude. See this thread for more information: + // http://forums.adafruit.com/viewtopic.php?f=22&t=58064 + + return atmospheric / pow(1.0 - (altitude / 44330.0), 5.255); +} + +/*! + * Returns Sensor ID found by init() for diagnostics + * @returns Sensor ID 0x60 for BME280, 0x56, 0x57, 0x58 BMP280 + */ +uint32_t Adafruit_BME280::sensorID(void) { return _sensorID; } + +/*! + * Returns the current temperature compensation value in degrees Celsius + * @returns the current temperature compensation value in degrees Celsius + */ +float Adafruit_BME280::getTemperatureCompensation(void) { + return float((t_fine_adjust * 5) >> 8) / 100.0; +}; + +/*! + * Sets a value to be added to each temperature reading. This adjusted + * temperature is used in pressure and humidity readings. + * @param adjustment Value to be added to each temperature reading in Celsius + */ +void Adafruit_BME280::setTemperatureCompensation(float adjustment) { + // convert the value in C into and adjustment to t_fine + t_fine_adjust = ((int32_t(adjustment * 100) << 8)) / 5; +}; + + +/**************************************************************************/ +/*! + @brief Gets the sensor_t data for the BME280's temperature sensor +*/ +/**************************************************************************/ +void Adafruit_BME280_Temp::getSensor(sensor_t *sensor) { + /* Clear the sensor_t object */ + memset(sensor, 0, sizeof(sensor_t)); + + /* Insert the sensor name in the fixed length char array */ + strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1); + sensor->name[sizeof(sensor->name) - 1] = 0; + sensor->version = 1; + sensor->sensor_id = _sensorID; + sensor->type = SENSOR_TYPE_AMBIENT_TEMPERATURE; + sensor->min_delay = 0; + sensor->min_value = -40.0; /* Temperature range -40 ~ +85 C */ + sensor->max_value = +85.0; + sensor->resolution = 0.01; /* 0.01 C */ +} + +/**************************************************************************/ +/*! + @brief Gets the temperature as a standard sensor event + @param event Sensor event object that will be populated + @returns True +*/ +/**************************************************************************/ +bool Adafruit_BME280_Temp::getEvent(sensors_event_t *event) { + /* Clear the event */ + memset(event, 0, sizeof(sensors_event_t)); + + event->version = sizeof(sensors_event_t); + event->sensor_id = _sensorID; + event->type = SENSOR_TYPE_AMBIENT_TEMPERATURE; + event->timestamp = millis(); + event->temperature = theBME280.readTemperature(); + return true; +} + +/**************************************************************************/ +/*! + @brief Gets the sensor_t data for the BME280's pressure sensor +*/ +/**************************************************************************/ +void Adafruit_BME280_Pressure::getSensor(sensor_t *sensor) { + /* Clear the sensor_t object */ + memset(sensor, 0, sizeof(sensor_t)); + + /* Insert the sensor name in the fixed length char array */ + strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1); + sensor->name[sizeof(sensor->name) - 1] = 0; + sensor->version = 1; + sensor->sensor_id = _sensorID; + sensor->type = SENSOR_TYPE_PRESSURE; + sensor->min_delay = 0; + sensor->min_value = 300.0; /* 300 ~ 1100 hPa */ + sensor->max_value = 1100.0; + sensor->resolution = 0.012; /* 0.12 hPa relative */ +} + +/**************************************************************************/ +/*! + @brief Gets the pressure as a standard sensor event + @param event Sensor event object that will be populated + @returns True +*/ +/**************************************************************************/ +bool Adafruit_BME280_Pressure::getEvent(sensors_event_t *event) { + /* Clear the event */ + memset(event, 0, sizeof(sensors_event_t)); + + event->version = sizeof(sensors_event_t); + event->sensor_id = _sensorID; + event->type = SENSOR_TYPE_PRESSURE; + event->timestamp = millis(); + event->pressure = theBME280.readPressure() / 100; // convert Pa to hPa + return true; +} + +/**************************************************************************/ +/*! + @brief Gets the sensor_t data for the BME280's humidity sensor +*/ +/**************************************************************************/ +void Adafruit_BME280_Humidity::getSensor(sensor_t *sensor) { + /* Clear the sensor_t object */ + memset(sensor, 0, sizeof(sensor_t)); + + /* Insert the sensor name in the fixed length char array */ + strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1); + sensor->name[sizeof(sensor->name) - 1] = 0; + sensor->version = 1; + sensor->sensor_id = _sensorID; + sensor->type = SENSOR_TYPE_RELATIVE_HUMIDITY; + sensor->min_delay = 0; + sensor->min_value = 0; + sensor->max_value = 100; /* 0 - 100 % */ + sensor->resolution = 3; /* 3% accuracy */ +} + +/**************************************************************************/ +/*! + @brief Gets the humidity as a standard sensor event + @param event Sensor event object that will be populated + @returns True +*/ +/**************************************************************************/ +bool Adafruit_BME280_Humidity::getEvent(sensors_event_t *event) { + /* Clear the event */ + memset(event, 0, sizeof(sensors_event_t)); + + event->version = sizeof(sensors_event_t); + event->sensor_id = _sensorID; + event->type = SENSOR_TYPE_RELATIVE_HUMIDITY; + event->timestamp = millis(); + event->relative_humidity = theBME280.readHumidity(); + return true; +} diff --git a/software/nano-644/test_2024-07-23/src/adafruit/bme280.h b/software/nano-644/test_2024-07-23/src/adafruit/bme280.h new file mode 100644 index 0000000..b842782 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/adafruit/bme280.h @@ -0,0 +1,373 @@ +// https://github.com/adafruit/Adafruit_BME280_Library + +/*! + * @file Adafruit_BME280.h + * + * Designed specifically to work with the Adafruit BME280 Breakout + * ----> http://www.adafruit.com/products/2650 + * + * These sensors use I2C or SPI to communicate, 2 or 4 pins are required + * to interface. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Written by Kevin "KTOWN" Townsend for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * See the LICENSE file for details. + * + */ + +#ifndef __BME280_H__ +#define __BME280_H__ + +// #include "Arduino.h" + +// #include +// #include +// #include + + +#include "../i2cmaster.hpp" +#include "../main.hpp" +#define byte uint8_t + + + +#include +#include +#include "sensor.h" + +/*! + * @brief default I2C address + */ +#define BME280_ADDRESS (0x77) // Primary I2C Address + /*! + * @brief alternate I2C address + */ +#define BME280_ADDRESS_ALTERNATE (0x76) // Alternate Address + +/*! + * @brief Register addresses + */ +enum { + BME280_REGISTER_DIG_T1 = 0x88, + BME280_REGISTER_DIG_T2 = 0x8A, + BME280_REGISTER_DIG_T3 = 0x8C, + + BME280_REGISTER_DIG_P1 = 0x8E, + BME280_REGISTER_DIG_P2 = 0x90, + BME280_REGISTER_DIG_P3 = 0x92, + BME280_REGISTER_DIG_P4 = 0x94, + BME280_REGISTER_DIG_P5 = 0x96, + BME280_REGISTER_DIG_P6 = 0x98, + BME280_REGISTER_DIG_P7 = 0x9A, + BME280_REGISTER_DIG_P8 = 0x9C, + BME280_REGISTER_DIG_P9 = 0x9E, + + BME280_REGISTER_DIG_H1 = 0xA1, + BME280_REGISTER_DIG_H2 = 0xE1, + BME280_REGISTER_DIG_H3 = 0xE3, + BME280_REGISTER_DIG_H4 = 0xE4, + BME280_REGISTER_DIG_H5 = 0xE5, + BME280_REGISTER_DIG_H6 = 0xE7, + + BME280_REGISTER_CHIPID = 0xD0, + BME280_REGISTER_VERSION = 0xD1, + BME280_REGISTER_SOFTRESET = 0xE0, + + BME280_REGISTER_CAL26 = 0xE1, // R calibration stored in 0xE1-0xF0 + + BME280_REGISTER_CONTROLHUMID = 0xF2, + BME280_REGISTER_STATUS = 0XF3, + BME280_REGISTER_CONTROL = 0xF4, + BME280_REGISTER_CONFIG = 0xF5, + BME280_REGISTER_PRESSUREDATA = 0xF7, + BME280_REGISTER_TEMPDATA = 0xFA, + BME280_REGISTER_HUMIDDATA = 0xFD +}; + +/**************************************************************************/ +/*! + @brief calibration data +*/ +/**************************************************************************/ +typedef struct { + uint16_t dig_T1; ///< temperature compensation value + int16_t dig_T2; ///< temperature compensation value + int16_t dig_T3; ///< temperature compensation value + + uint16_t dig_P1; ///< pressure compensation value + int16_t dig_P2; ///< pressure compensation value + int16_t dig_P3; ///< pressure compensation value + int16_t dig_P4; ///< pressure compensation value + int16_t dig_P5; ///< pressure compensation value + int16_t dig_P6; ///< pressure compensation value + int16_t dig_P7; ///< pressure compensation value + int16_t dig_P8; ///< pressure compensation value + int16_t dig_P9; ///< pressure compensation value + + uint8_t dig_H1; ///< humidity compensation value + int16_t dig_H2; ///< humidity compensation value + uint8_t dig_H3; ///< humidity compensation value + int16_t dig_H4; ///< humidity compensation value + int16_t dig_H5; ///< humidity compensation value + int8_t dig_H6; ///< humidity compensation value +} bme280_calib_data; +/*=========================================================================*/ + +class Adafruit_BME280; + +/** Adafruit Unified Sensor interface for temperature component of BME280 */ +class Adafruit_BME280_Temp : public Adafruit_Sensor { +public: + /** @brief Create an Adafruit_Sensor compatible object for the temp sensor + @param parent A pointer to the BME280 class */ + Adafruit_BME280_Temp() { _sensorID = 280; } + bool getEvent(sensors_event_t *); + void getSensor(sensor_t *); + +private: + int _sensorID; +}; + +/** Adafruit Unified Sensor interface for pressure component of BME280 */ +class Adafruit_BME280_Pressure : public Adafruit_Sensor { +public: + /** @brief Create an Adafruit_Sensor compatible object for the pressure sensor + @param parent A pointer to the BME280 class */ + Adafruit_BME280_Pressure() { _sensorID = 280; } + bool getEvent(sensors_event_t *); + void getSensor(sensor_t *); + +private: + int _sensorID; +}; + +/** Adafruit Unified Sensor interface for humidity component of BME280 */ +class Adafruit_BME280_Humidity : public Adafruit_Sensor { +public: + /** @brief Create an Adafruit_Sensor compatible object for the humidity sensor + @param parent A pointer to the BME280 class */ + Adafruit_BME280_Humidity() { _sensorID = 280;} + bool getEvent(sensors_event_t *); + void getSensor(sensor_t *); + +private: + int _sensorID; +}; + +/**************************************************************************/ +/*! + @brief Class that stores state and functions for interacting with BME280 IC +*/ +/**************************************************************************/ +class Adafruit_BME280 { +public: + /**************************************************************************/ + /*! + @brief sampling rates + */ + /**************************************************************************/ + enum sensor_sampling { + SAMPLING_NONE = 0b000, + SAMPLING_X1 = 0b001, + SAMPLING_X2 = 0b010, + SAMPLING_X4 = 0b011, + SAMPLING_X8 = 0b100, + SAMPLING_X16 = 0b101 + }; + + /**************************************************************************/ + /*! + @brief power modes + */ + /**************************************************************************/ + enum sensor_mode { + MODE_SLEEP = 0b00, + MODE_FORCED = 0b01, + MODE_NORMAL = 0b11 + }; + + /**************************************************************************/ + /*! + @brief filter values + */ + /**************************************************************************/ + enum sensor_filter { + FILTER_OFF = 0b000, + FILTER_X2 = 0b001, + FILTER_X4 = 0b010, + FILTER_X8 = 0b011, + FILTER_X16 = 0b100 + }; + + /**************************************************************************/ + /*! + @brief standby duration in ms + */ + /**************************************************************************/ + enum standby_duration { + STANDBY_MS_0_5 = 0b000, + STANDBY_MS_10 = 0b110, + STANDBY_MS_20 = 0b111, + STANDBY_MS_62_5 = 0b001, + STANDBY_MS_125 = 0b010, + STANDBY_MS_250 = 0b011, + STANDBY_MS_500 = 0b100, + STANDBY_MS_1000 = 0b101 + }; + + // constructors + Adafruit_BME280(); + + bool begin(uint8_t addr = BME280_ADDRESS); + bool init(); + + void setSampling(sensor_mode mode = MODE_NORMAL, + sensor_sampling tempSampling = SAMPLING_X16, + sensor_sampling pressSampling = SAMPLING_X16, + sensor_sampling humSampling = SAMPLING_X16, + sensor_filter filter = FILTER_OFF, + standby_duration duration = STANDBY_MS_0_5); + + bool takeForcedMeasurement(void); + float readTemperature(void); + float readPressure(void); + float readHumidity(void); + + float readAltitude(float seaLevel); + float seaLevelForAltitude(float altitude, float pressure); + uint32_t sensorID(void); + + float getTemperatureCompensation(void); + void setTemperatureCompensation(float); + +protected: + I2cMaster *i2c_dev = NULL; ///< Pointer to I2C bus interface + // Adafruit_SPIDevice *spi_dev = NULL; ///< Pointer to SPI bus interface + + Adafruit_BME280_Temp *temp_sensor; + Adafruit_BME280_Pressure *pressure_sensor; + Adafruit_BME280_Humidity *humidity_sensor; + + void readCoefficients(void); + bool isReadingCalibration(void); + + void write8(byte reg, byte value); + uint8_t read8(byte reg); + uint16_t read16(byte reg); + uint32_t read24(byte reg); + int16_t readS16(byte reg); + uint16_t read16_LE(byte reg); // little endian + int16_t readS16_LE(byte reg); // little endian + + uint8_t _i2caddr; //!< I2C addr for the TwoWire interface + int32_t _sensorID; //!< ID of the BME Sensor + int32_t t_fine; //!< temperature with high resolution, stored as an attribute + //!< as this is used for temperature compensation reading + //!< humidity and pressure + + int32_t t_fine_adjust; //!< add to compensate temp readings and in turn + //!< to pressure and humidity readings + + bme280_calib_data _bme280_calib; //!< here calibration data is stored + + /**************************************************************************/ + /*! + @brief config register + */ + /**************************************************************************/ + struct config { + // inactive duration (standby time) in normal mode + // 000 = 0.5 ms + // 001 = 62.5 ms + // 010 = 125 ms + // 011 = 250 ms + // 100 = 500 ms + // 101 = 1000 ms + // 110 = 10 ms + // 111 = 20 ms + unsigned int t_sb : 3; ///< inactive duration (standby time) in normal mode + + // filter settings + // 000 = filter off + // 001 = 2x filter + // 010 = 4x filter + // 011 = 8x filter + // 100 and above = 16x filter + unsigned int filter : 3; ///< filter settings + + // unused - don't set + unsigned int none : 1; ///< unused - don't set + unsigned int spi3w_en : 1; ///< unused - don't set + + /// @return combined config register + unsigned int get() { return (t_sb << 5) | (filter << 2) | spi3w_en; } + }; + config _configReg; //!< config register object + + /**************************************************************************/ + /*! + @brief ctrl_meas register + */ + /**************************************************************************/ + struct ctrl_meas { + // temperature oversampling + // 000 = skipped + // 001 = x1 + // 010 = x2 + // 011 = x4 + // 100 = x8 + // 101 and above = x16 + unsigned int osrs_t : 3; ///< temperature oversampling + + // pressure oversampling + // 000 = skipped + // 001 = x1 + // 010 = x2 + // 011 = x4 + // 100 = x8 + // 101 and above = x16 + unsigned int osrs_p : 3; ///< pressure oversampling + + // device mode + // 00 = sleep + // 01 or 10 = forced + // 11 = normal + unsigned int mode : 2; ///< device mode + + /// @return combined ctrl register + unsigned int get() { return (osrs_t << 5) | (osrs_p << 2) | mode; } + }; + ctrl_meas _measReg; //!< measurement register object + + /**************************************************************************/ + /*! + @brief ctrl_hum register + */ + /**************************************************************************/ + struct ctrl_hum { + /// unused - don't set + unsigned int none : 5; + + // pressure oversampling + // 000 = skipped + // 001 = x1 + // 010 = x2 + // 011 = x4 + // 100 = x8 + // 101 and above = x16 + unsigned int osrs_h : 3; ///< pressure oversampling + + /// @return combined ctrl hum register + unsigned int get() { return (osrs_h); } + }; + ctrl_hum _humReg; //!< hum register object +}; + +extern Adafruit_BME280 theBME280; + +#endif diff --git a/software/nano-644/test_2024-07-23/src/adafruit/ens160.cpp b/software/nano-644/test_2024-07-23/src/adafruit/ens160.cpp new file mode 100644 index 0000000..ef23404 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/adafruit/ens160.cpp @@ -0,0 +1,374 @@ +/* + ScioSense_ENS160.h - Library for the ENS160 sensor with I2C interface from ScioSense + 2023 Mar 23 v6 Christoph Friese Bugfix measurement routine, prepare next release + 2021 Nov 25 v5 Martin Herold Custom mode timing fixed + 2021 Feb 04 v4 Giuseppe de Pinto Custom mode fixed + 2020 Apr 06 v3 Christoph Friese Changed nomenclature to ScioSense as product shifted from ams + 2020 Feb 15 v2 Giuseppe Pasetti Corrected firmware flash option + 2019 May 05 v1 Christoph Friese Created + based on application note "ENS160 Software Integration.pdf" rev 0.01 +*/ + +#include "ens160.h" +#include "math.h" +#include +#include +#include + +ScioSense_ENS160::ScioSense_ENS160 () { + _revENS16x = 0; + + //Isotherm, HP0 252°C / HP1 350°C / HP2 250°C / HP3 324°C / measure every 1008ms + _seq_steps[0][0] = 0x7c; + _seq_steps[0][1] = 0x0a; + _seq_steps[0][2] = 0x7e; + _seq_steps[0][3] = 0xaf; + _seq_steps[0][4] = 0xaf; + _seq_steps[0][5] = 0xa2; + _seq_steps[0][6] = 0x00; + _seq_steps[0][7] = 0x80; +} + +bool ScioSense_ENS160::begin () { + i2cDevice.begin(ENS160_I2CADDR_1); + _delay_ms(ENS160_BOOTING); + if (reset()) { + if (checkPartID()) { + if (setMode(ENS160_OPMODE_IDLE)) { + if (clearCommand()) { + if (getFirmware()) { + return true; + } + } + } + } + } + return false; + +} + +bool ScioSense_ENS160::write8 (byte reg, byte value) { + byte buffer[2]; + buffer[1] = value; + buffer[0] = reg; + return i2cDevice.write(buffer, 2); +} + +bool ScioSense_ENS160::read8 (byte reg, byte *value) { + uint8_t buffer[1]; + buffer[0] = uint8_t(reg); + return i2cDevice.write_then_read(buffer, 1, value, 1); +} + +bool ScioSense_ENS160::read16 (byte reg, uint16_t *value) { + uint8_t buffer[1]; + buffer[0] = uint8_t(reg); + return i2cDevice.write_then_read(buffer, 1, (uint8_t *)value, 2); +} + +bool ScioSense_ENS160::read16LE (byte reg, uint16_t *value) { + uint16_t tmp; + if (read16(reg, &tmp)) { + *value = ((tmp & 0xff) << 8) | (tmp >> 8); + return true; + } + return false; +} + +bool ScioSense_ENS160::readBytes (byte reg, uint8_t *bytes, uint8_t len) { + uint8_t buffer[1]; + buffer[0] = uint8_t(reg); + return i2cDevice.write_then_read(buffer, 1, buffer, len); +} + + +// Sends a reset to the ENS160. Returns false on I2C problems. +bool ScioSense_ENS160::reset () { + if (write8(ENS160_REG_OPMODE, ENS160_OPMODE_RESET)) { + _delay_ms(ENS160_BOOTING); + return true; + } + _delay_ms(ENS160_BOOTING); + return false; +} + +// Reads the part ID and confirms valid sensor +bool ScioSense_ENS160::checkPartID () { + uint16_t part_id; + + read16(ENS160_REG_PART_ID, &part_id); + _delay_ms(ENS160_BOOTING); + + if (part_id == ENS160_PARTID) { + _revENS16x = 0; + return true; + + } else if (part_id == ENS161_PARTID) { + _revENS16x = 1; + return true; + } + + return false; +} + +// Initialize idle mode and confirms +bool ScioSense_ENS160::clearCommand () { + uint8_t status; + + if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_NOP)) { + if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_CLRGPR)) { + _delay_ms(ENS160_BOOTING); + if (read8(ENS160_REG_DATA_STATUS, &status)) { + return true; + } + } + } + _delay_ms(ENS160_BOOTING); + return false; +} + +// Read firmware revisions +bool ScioSense_ENS160::getFirmware () { + uint8_t i2cbuf[3]; + + if (clearCommand()) { + _delay_ms(ENS160_BOOTING); + if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_GET_APPVER)) { + if (readBytes(ENS160_REG_GPR_READ_4, i2cbuf, 3)) { + _fw_ver_major = i2cbuf[0]; + _fw_ver_minor = i2cbuf[1]; + _fw_ver_build = i2cbuf[2]; + _revENS16x = this->_fw_ver_major > 6 ? 1 : 0; + _delay_ms(ENS160_BOOTING); + return true; + } + } + } + _delay_ms(ENS160_BOOTING); + return false; +} + +// Set operation mode of sensor +bool ScioSense_ENS160::setMode (uint8_t mode) { + //LP only valid for rev>0 + if ((mode == ENS160_OPMODE_LP) and (_revENS16x == 0)) { + return false; + } + if (write8(ENS160_REG_OPMODE, mode)) { + _delay_ms(ENS160_BOOTING); + return true; + } + _delay_ms(ENS160_BOOTING); + return false; +} + +// Initialize definition of custom mode with steps +bool ScioSense_ENS160::initCustomMode (uint16_t stepNum) { + if (stepNum > 0) { + _stepCount = stepNum; + if (setMode(ENS160_OPMODE_IDLE)) { + if (clearCommand()) { + if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_SETSEQ)) { + _delay_ms(ENS160_BOOTING); + return true; + } + } + } + } + _delay_ms(ENS160_BOOTING); + return false; +} + +// Add a step to custom measurement profile with definition of duration, enabled data acquisition and temperature for each hotplate +bool ScioSense_ENS160::addCustomStep (uint16_t time, bool measureHP0, bool measureHP1, bool measureHP2, bool measureHP3, uint16_t tempHP0, uint16_t tempHP1, uint16_t tempHP2, uint16_t tempHP3) { + uint8_t seq_ack; + uint8_t temp; + + _delay_ms(ENS160_BOOTING); + + temp = (uint8_t)(((time / 24) - 1) << 6); + if (measureHP0) { + temp = temp | 0x20; + } + if (measureHP1) { + temp = temp | 0x10; + } + if (measureHP2) { + temp = temp | 0x08; + } + if (measureHP3) { + temp = temp | 0x04; + } + if (!write8(ENS160_REG_GPR_WRITE_0, temp)) { + return false; + } + temp = (uint8_t)(((time / 24) - 1) >> 2); + if (!write8(ENS160_REG_GPR_WRITE_1, temp)) { + return false; + } + if (!write8(ENS160_REG_GPR_WRITE_2, (uint8_t)(tempHP0 / 2))) { + return false; + } + if (!write8(ENS160_REG_GPR_WRITE_3, (uint8_t)(tempHP1 / 2))) { + return false; + } + if (write8(ENS160_REG_GPR_WRITE_4, (uint8_t)(tempHP2 / 2))) { + return false; + } + if (write8(ENS160_REG_GPR_WRITE_5, (uint8_t)(tempHP3 / 2))) { + return false; + } + + if (write8(ENS160_REG_GPR_WRITE_6, (uint8_t)(_stepCount - 1))) { + return false; + } + + if (_stepCount == 1) { + if (!write8(ENS160_REG_GPR_WRITE_7, 128)) { + return false; + } + } else { + if (!write8(ENS160_REG_GPR_WRITE_7, 0)) { + return false; + } + } + _delay_ms(ENS160_BOOTING); + + if (!read8(ENS160_REG_GPR_READ_7, &seq_ack)) { + return false; + } + _delay_ms(ENS160_BOOTING); + + if ((ENS160_SEQ_ACK_COMPLETE | _stepCount) != seq_ack) { + _stepCount++; + return false; + } + + return true; +} + +bool ScioSense_ENS160::readStatus (uint8_t *status) { + return read8(ENS160_REG_DATA_STATUS, status); +} + +bool ScioSense_ENS160::readData (ENS160_DATA *data) { + uint8_t buffer[1] = { 0x21 }; + return i2cDevice.write_then_read(buffer, 1, (uint8_t *)data, sizeof(ENS160_DATA)); +} + +// Perform prediction measurement and stores result in internal variables +bool ScioSense_ENS160::measure (bool waitForNew) { + uint8_t i2cbuf[8]; + uint8_t status; + + // Set default status for early bail out + if (waitForNew) { + do { + if (!read8(ENS160_REG_DATA_STATUS, &status)) { + return false; + } + _delay_ms(1); + } while (!IS_NEWDAT(status)); + } else { + if (!read8(ENS160_REG_DATA_STATUS, &status)) { + return false; + } + } + + + // Read predictions + if (IS_NEWDAT(status)) { + if (!readBytes(ENS160_REG_DATA_AQI, i2cbuf, 7)) { + return false; + } + return false; + _data_aqi = i2cbuf[0]; + _data_tvoc = i2cbuf[1] | ((uint16_t)i2cbuf[2] << 8); + _data_eco2 = i2cbuf[3] | ((uint16_t)i2cbuf[4] << 8); + if (_revENS16x > 0) { + _data_aqi500 = ((uint16_t)i2cbuf[5]) | ((uint16_t)i2cbuf[6] << 8); + } else { + _data_aqi500 = 0; + } + return true; + } + + return false; +} + +// Perfrom raw measurement and stores result in internal variables +bool ScioSense_ENS160::measureRaw (bool waitForNew) { + uint8_t i2cbuf[8]; + uint8_t status; + + // Set default status for early bail out + if (waitForNew) { + do { + _delay_ms(1); + if (!read8(ENS160_REG_DATA_STATUS, &status)) { + return false; + } + } while (!IS_NEWGPR(status)); + } else { + if (!read8(ENS160_REG_DATA_STATUS, &status)) { + return false; + } + } + + if (IS_NEWGPR(status)) { + + // Read raw resistance values + if (!readBytes(ENS160_REG_GPR_READ_0, i2cbuf, 8)) { + return false; + } + _hp0_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[0] | ((uint16_t)i2cbuf[1] << 8))); + _hp1_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[2] | ((uint16_t)i2cbuf[3] << 8))); + _hp2_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[4] | ((uint16_t)i2cbuf[5] << 8))); + _hp3_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[6] | ((uint16_t)i2cbuf[7] << 8))); + + // Read baselines + if (!readBytes(ENS160_REG_DATA_BL, i2cbuf, 8)) { + return false; + } + _hp0_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[0] | ((uint16_t)i2cbuf[1] << 8))); + _hp1_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[2] | ((uint16_t)i2cbuf[3] << 8))); + _hp2_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[4] | ((uint16_t)i2cbuf[5] << 8))); + _hp3_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[6] | ((uint16_t)i2cbuf[7] << 8))); + + if (!read8(ENS160_REG_DATA_MISR, i2cbuf)) { + return false; + } + _misr = i2cbuf[0]; + return true; + } + + return false; +} + + +// Writes t (degC) and h (%rh) to ENV_DATA. Returns false on I2C problems. +bool ScioSense_ENS160::set_envdata (float t, float h) { + uint16_t t_data = (uint16_t)((t + 273.15f) * 64.0f); + uint16_t rh_data = (uint16_t)(h * 512.0f); + return this->set_envdata210(t_data, rh_data); +} + +// Writes t and h (in ENS210 format) to ENV_DATA. Returns false on I2C problems. +bool ScioSense_ENS160::set_envdata210 (uint16_t t, uint16_t h) { + //uint16_t temp; + uint8_t trh_in[4]; + + //temp = (uint16_t)((t + 273.15f) * 64.0f); + trh_in[0] = t & 0xff; + trh_in[1] = (t >> 8) & 0xff; + + //temp = (uint16_t)(h * 512.0f); + trh_in[2] = h & 0xff; + trh_in[3] = (h >> 8) & 0xff; + + if (!i2cDevice.writeByteAndBuffer(ENS160_REG_TEMP_IN, trh_in, 4)) { + return false; + } + + return true; +} diff --git a/software/nano-644/test_2024-07-23/src/adafruit/ens160.h b/software/nano-644/test_2024-07-23/src/adafruit/ens160.h new file mode 100644 index 0000000..fb6925d --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/adafruit/ens160.h @@ -0,0 +1,188 @@ +/* + ScioSense_ENS160.h - Library for the ENS160 sensor with I2C interface from ScioSense + 2023 Mar 23 v6 Christoph Friese Bugfix measurement routine, prepare next release + 2021 July 29 v4 Christoph Friese Changed nomenclature to ScioSense as product shifted from ams + 2020 Apr 06 v3 Christoph Friese Changed nomenclature to ScioSense as product shifted from ams + 2020 Feb 15 v2 Giuseppe Pasetti Corrected firmware flash option + 2019 May 05 v1 Christoph Friese Created + based on application note "ENS160 Software Integration.pdf" rev 0.01 +*/ + +#ifndef __SCIOSENSE_ENS160_H_ +#define __SCIOSENSE_ENS160_H_ + +#include "../i2cmaster.hpp" +#include +#define byte uint8_t + +// #if (ARDUINO >= 100) +// #include "Arduino.h" +// #else +// #include "WProgram.h" +// #endif + +// #include + +// Chip constants +#define ENS160_PARTID 0x0160 +#define ENS161_PARTID 0x0161 +#define ENS160_BOOTING 10 + +// 7-bit I2C slave address of the ENS160 +#define ENS160_I2CADDR_0 0x52 //ADDR low +#define ENS160_I2CADDR_1 0x53 //ADDR high + +// ENS160 registers for version V0 +#define ENS160_REG_PART_ID 0x00 // 2 byte register +#define ENS160_REG_OPMODE 0x10 +#define ENS160_REG_CONFIG 0x11 +#define ENS160_REG_COMMAND 0x12 +#define ENS160_REG_TEMP_IN 0x13 +#define ENS160_REG_RH_IN 0x15 +#define ENS160_REG_DATA_STATUS 0x20 +#define ENS160_REG_DATA_AQI 0x21 +#define ENS160_REG_DATA_TVOC 0x22 +#define ENS160_REG_DATA_ECO2 0x24 +#define ENS160_REG_DATA_BL 0x28 +#define ENS160_REG_DATA_T 0x30 +#define ENS160_REG_DATA_RH 0x32 +#define ENS160_REG_DATA_MISR 0x38 +#define ENS160_REG_GPR_WRITE_0 0x40 +#define ENS160_REG_GPR_WRITE_1 ENS160_REG_GPR_WRITE_0 + 1 +#define ENS160_REG_GPR_WRITE_2 ENS160_REG_GPR_WRITE_0 + 2 +#define ENS160_REG_GPR_WRITE_3 ENS160_REG_GPR_WRITE_0 + 3 +#define ENS160_REG_GPR_WRITE_4 ENS160_REG_GPR_WRITE_0 + 4 +#define ENS160_REG_GPR_WRITE_5 ENS160_REG_GPR_WRITE_0 + 5 +#define ENS160_REG_GPR_WRITE_6 ENS160_REG_GPR_WRITE_0 + 6 +#define ENS160_REG_GPR_WRITE_7 ENS160_REG_GPR_WRITE_0 + 7 +#define ENS160_REG_GPR_READ_0 0x48 +#define ENS160_REG_GPR_READ_4 ENS160_REG_GPR_READ_0 + 4 +#define ENS160_REG_GPR_READ_6 ENS160_REG_GPR_READ_0 + 6 +#define ENS160_REG_GPR_READ_7 ENS160_REG_GPR_READ_0 + 7 + +//ENS160 data register fields +#define ENS160_COMMAND_NOP 0x00 +#define ENS160_COMMAND_CLRGPR 0xCC +#define ENS160_COMMAND_GET_APPVER 0x0E +#define ENS160_COMMAND_SETTH 0x02 +#define ENS160_COMMAND_SETSEQ 0xC2 + +#define ENS160_OPMODE_RESET 0xF0 +#define ENS160_OPMODE_DEP_SLEEP 0x00 +#define ENS160_OPMODE_IDLE 0x01 +#define ENS160_OPMODE_STD 0x02 +#define ENS160_OPMODE_LP 0x03 +#define ENS160_OPMODE_CUSTOM 0xC0 + +#define ENS160_BL_CMD_START 0x02 +#define ENS160_BL_CMD_ERASE_APP 0x04 +#define ENS160_BL_CMD_ERASE_BLINE 0x06 +#define ENS160_BL_CMD_WRITE 0x08 +#define ENS160_BL_CMD_VERIFY 0x0A +#define ENS160_BL_CMD_GET_BLVER 0x0C +#define ENS160_BL_CMD_GET_APPVER 0x0E +#define ENS160_BL_CMD_EXITBL 0x12 + +#define ENS160_SEQ_ACK_NOTCOMPLETE 0x80 +#define ENS160_SEQ_ACK_COMPLETE 0xC0 + +#define IS_ENS160_SEQ_ACK_NOT_COMPLETE(x) (ENS160_SEQ_ACK_NOTCOMPLETE == (ENS160_SEQ_ACK_NOTCOMPLETE & (x))) +#define IS_ENS160_SEQ_ACK_COMPLETE(x) (ENS160_SEQ_ACK_COMPLETE == (ENS160_SEQ_ACK_COMPLETE & (x))) + +#define ENS160_DATA_STATUS_NEWDAT 0x02 +#define ENS160_DATA_STATUS_NEWGPR 0x01 + +#define IS_NEWDAT(x) (ENS160_DATA_STATUS_NEWDAT == (ENS160_DATA_STATUS_NEWDAT & (x))) +#define IS_NEWGPR(x) (ENS160_DATA_STATUS_NEWGPR == (ENS160_DATA_STATUS_NEWGPR & (x))) +#define IS_NEW_DATA_AVAILABLE(x) (0 != ((ENS160_DATA_STATUS_NEWDAT | ENS160_DATA_STATUS_NEWGPR ) & (x))) + +#define CONVERT_RS_RAW2OHMS_I(x) (1 << ((x) >> 11)) +#define CONVERT_RS_RAW2OHMS_F(x) (pow (2, (float)(x) / 2048)) + +typedef struct { + uint8_t aqi; + uint16_t tvoc; + uint16_t eco2; +} ENS160_DATA; + +class ScioSense_ENS160 { + + public: + ScioSense_ENS160(); + + void setI2C(uint8_t sda, uint8_t scl); // Function to redefine I2C pins + + bool begin(); // Init I2C communication, resets ENS160 and checks its PART_ID. Returns false on I2C problems or wrong PART_ID. + uint8_t revENS16x() { return this->_revENS16x; } // Report version of sensor (0: ENS160, 1: ENS161) + bool setMode(uint8_t mode); // Set operation mode of sensor + + bool initCustomMode(uint16_t stepNum); // Initialize definition of custom mode with steps + bool addCustomStep(uint16_t time, bool measureHP0, bool measureHP1, bool measureHP2, bool measureHP3, uint16_t tempHP0, uint16_t tempHP1, uint16_t tempHP2, uint16_t tempHP3); + // Add a step to custom measurement profile with definition of duration, enabled data acquisition and temperature for each hotplate + + bool readData (ENS160_DATA *data); + bool readStatus(uint8_t *status); + bool measure(bool waitForNew); // Perform measurement and stores result in internal variables + bool measureRaw(bool waitForNew); // Perform raw measurement and stores result in internal variables + bool set_envdata(float t, float h); // Writes t (degC) and h (%rh) to ENV_DATA. Returns "0" if I2C transmission is successful + bool set_envdata210(uint16_t t, uint16_t h); // Writes t and h (in ENS210 format) to ENV_DATA. Returns "0" if I2C transmission is successful + uint8_t getMajorRev() { return this->_fw_ver_major; } // Get major revision number of used firmware + uint8_t getMinorRev() { return this->_fw_ver_minor; } // Get minor revision number of used firmware + uint8_t getBuild() { return this->_fw_ver_build; } // Get build revision number of used firmware + + uint8_t getAQI() { return this->_data_aqi; } // Get AQI value of last measurement + uint16_t getTVOC() { return this->_data_tvoc; } // Get TVOC value of last measurement + uint16_t geteCO2() { return this->_data_eco2; } // Get eCO2 value of last measurement + uint16_t getAQI500() { return this->_data_aqi500; } // Get AQI500 value of last measurement + uint32_t getHP0() { return this->_hp0_rs; } // Get resistance of HP0 of last measurement + uint32_t getHP1() { return this->_hp1_rs; } // Get resistance of HP1 of last measurement + uint32_t getHP2() { return this->_hp2_rs; } // Get resistance of HP2 of last measurement + uint32_t getHP3() { return this->_hp3_rs; } // Get resistance of HP3 of last measurement + uint32_t getHP0BL() { return this->_hp0_bl; } // Get baseline resistance of HP0 of last measurement + uint32_t getHP1BL() { return this->_hp1_bl; } // Get baseline resistance of HP1 of last measurement + uint32_t getHP2BL() { return this->_hp2_bl; } // Get baseline resistance of HP2 of last measurement + uint32_t getHP3BL() { return this->_hp3_bl; } // Get baseline resistance of HP3 of last measurement + uint8_t getMISR() { return this->_misr; } // Return status code of sensor + + private: + I2cMaster i2cDevice; + bool reset(); // Sends a reset to the ENS160. Returns false on I2C problems. + bool checkPartID(); // Reads the part ID and confirms valid sensor + bool clearCommand(); // Initialize idle mode and confirms + bool getFirmware(); // Read firmware revisions + + uint8_t _revENS16x; // ENS160 or ENS161 connected? (FW >7) + + uint8_t _fw_ver_major; + uint8_t _fw_ver_minor; + uint8_t _fw_ver_build; + + uint16_t _stepCount; // Counter for custom sequence + + uint8_t _data_aqi; + uint16_t _data_tvoc; + uint16_t _data_eco2; + uint16_t _data_aqi500; + uint32_t _hp0_rs; + uint32_t _hp0_bl; + uint32_t _hp1_rs; + uint32_t _hp1_bl; + uint32_t _hp2_rs; + uint32_t _hp2_bl; + uint32_t _hp3_rs; + uint32_t _hp3_bl; + uint16_t _temp; + int _slaveaddr; // Slave address of the ENS160 + uint8_t _misr; + + uint8_t _seq_steps[1][8]; + + bool write8(byte reg, byte value); + bool read8 (byte reg, byte *value); + bool read16 (byte reg, uint16_t *value); + bool read16LE (byte reg, uint16_t *value); + bool readBytes (byte reg, uint8_t *bytes, uint8_t len); +}; + + +#endif diff --git a/software/nano-644/test_2024-07-23/src/adafruit/sensor.h b/software/nano-644/test_2024-07-23/src/adafruit/sensor.h new file mode 100644 index 0000000..ac7e454 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/adafruit/sensor.h @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software< /span> + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Update by K. Townsend (Adafruit Industries) for lighter typedefs, and + * extended sensor support to include color, voltage and current */ + +#ifndef _ADAFRUIT_SENSOR_H +#define _ADAFRUIT_SENSOR_H + +#ifndef ARDUINO +#include +#elif ARDUINO >= 100 +#include "Arduino.h" +#include "Print.h" +#else +#include "WProgram.h" +#endif + +/* Constants */ +#define SENSORS_GRAVITY_EARTH (9.80665F) /**< Earth's gravity in m/s^2 */ +#define SENSORS_GRAVITY_MOON (1.6F) /**< The moon's gravity in m/s^2 */ +#define SENSORS_GRAVITY_SUN (275.0F) /**< The sun's gravity in m/s^2 */ +#define SENSORS_GRAVITY_STANDARD (SENSORS_GRAVITY_EARTH) +#define SENSORS_MAGFIELD_EARTH_MAX \ + (60.0F) /**< Maximum magnetic field on Earth's surface */ +#define SENSORS_MAGFIELD_EARTH_MIN \ + (30.0F) /**< Minimum magnetic field on Earth's surface */ +#define SENSORS_PRESSURE_SEALEVELHPA \ + (1013.25F) /**< Average sea level pressure is 1013.25 hPa */ +#define SENSORS_DPS_TO_RADS \ + (0.017453293F) /**< Degrees/s to rad/s multiplier \ + */ +#define SENSORS_RADS_TO_DPS \ + (57.29577793F) /**< Rad/s to degrees/s multiplier */ +#define SENSORS_GAUSS_TO_MICROTESLA \ + (100) /**< Gauss to micro-Tesla multiplier */ + +/** Sensor types */ +typedef enum { + SENSOR_TYPE_ACCELEROMETER = (1), /**< Gravity + linear acceleration */ + SENSOR_TYPE_MAGNETIC_FIELD = (2), + SENSOR_TYPE_ORIENTATION = (3), + SENSOR_TYPE_GYROSCOPE = (4), + SENSOR_TYPE_LIGHT = (5), + SENSOR_TYPE_PRESSURE = (6), + SENSOR_TYPE_PROXIMITY = (8), + SENSOR_TYPE_GRAVITY = (9), + SENSOR_TYPE_LINEAR_ACCELERATION = + (10), /**< Acceleration not including gravity */ + SENSOR_TYPE_ROTATION_VECTOR = (11), + SENSOR_TYPE_RELATIVE_HUMIDITY = (12), + SENSOR_TYPE_AMBIENT_TEMPERATURE = (13), + SENSOR_TYPE_OBJECT_TEMPERATURE = (14), + SENSOR_TYPE_VOLTAGE = (15), + SENSOR_TYPE_CURRENT = (16), + SENSOR_TYPE_COLOR = (17), + SENSOR_TYPE_TVOC = (18), + SENSOR_TYPE_VOC_INDEX = (19), + SENSOR_TYPE_NOX_INDEX = (20), + SENSOR_TYPE_CO2 = (21), + SENSOR_TYPE_ECO2 = (22), + SENSOR_TYPE_PM10_STD = (23), + SENSOR_TYPE_PM25_STD = (24), + SENSOR_TYPE_PM100_STD = (25), + SENSOR_TYPE_PM10_ENV = (26), + SENSOR_TYPE_PM25_ENV = (27), + SENSOR_TYPE_PM100_ENV = (28), + SENSOR_TYPE_GAS_RESISTANCE = (29), + SENSOR_TYPE_UNITLESS_PERCENT = (30), + SENSOR_TYPE_ALTITUDE = (31) +} sensors_type_t; + +/** struct sensors_vec_s is used to return a vector in a common format. */ +typedef struct { + union { + float v[3]; ///< 3D vector elements + struct { + float x; ///< X component of vector + float y; ///< Y component of vector + float z; ///< Z component of vector + }; ///< Struct for holding XYZ component + /* Orientation sensors */ + struct { + float roll; /**< Rotation around the longitudinal axis (the plane body, 'X + axis'). Roll is positive and increasing when moving + downward. -90 degrees <= roll <= 90 degrees */ + float pitch; /**< Rotation around the lateral axis (the wing span, 'Y + axis'). Pitch is positive and increasing when moving + upwards. -180 degrees <= pitch <= 180 degrees) */ + float heading; /**< Angle between the longitudinal axis (the plane body) + and magnetic north, measured clockwise when viewing from + the top of the device. 0-359 degrees */ + }; ///< Struct for holding roll/pitch/heading + }; ///< Union that can hold 3D vector array, XYZ components or + ///< roll/pitch/heading + int8_t status; ///< Status byte + uint8_t reserved[3]; ///< Reserved +} sensors_vec_t; + +/** struct sensors_color_s is used to return color data in a common format. */ +typedef struct { + union { + float c[3]; ///< Raw 3-element data + /* RGB color space */ + struct { + float r; /**< Red component */ + float g; /**< Green component */ + float b; /**< Blue component */ + }; ///< RGB data in floating point notation + }; ///< Union of various ways to describe RGB colorspace + uint32_t rgba; /**< 24-bit RGBA value */ +} sensors_color_t; + +/* Sensor event (36 bytes) */ +/** struct sensor_event_s is used to provide a single sensor event in a common + * format. */ +typedef struct { + int32_t version; /**< must be sizeof(struct sensors_event_t) */ + int32_t sensor_id; /**< unique sensor identifier */ + int32_t type; /**< sensor type */ + int32_t reserved0; /**< reserved */ + int32_t timestamp; /**< time is in milliseconds */ + union { + float data[4]; ///< Raw data */ + sensors_vec_t acceleration; /**< acceleration values are in meter per second + per second (m/s^2) */ + sensors_vec_t + magnetic; /**< magnetic vector values are in micro-Tesla (uT) */ + sensors_vec_t orientation; /**< orientation values are in degrees */ + sensors_vec_t gyro; /**< gyroscope values are in rad/s */ + float temperature; /**< temperature is in degrees centigrade (Celsius) */ + float distance; /**< distance in centimeters */ + float light; /**< light in SI lux units */ + float pressure; /**< pressure in hectopascal (hPa) */ + float relative_humidity; /**< relative humidity in percent */ + float current; /**< current in milliamps (mA) */ + float voltage; /**< voltage in volts (V) */ + float tvoc; /**< Total Volatile Organic Compounds, in ppb */ + float voc_index; /**< VOC (Volatile Organic Compound) index where 100 is + normal (unitless) */ + float nox_index; /**< NOx (Nitrogen Oxides) index where 100 is normal + (unitless) */ + float CO2; /**< Measured CO2 in parts per million (ppm) */ + float eCO2; /**< equivalent/estimated CO2 in parts per million (ppm + estimated from some other measurement) */ + float pm10_std; /**< Standard Particulate Matter <=1.0 in parts per million + (ppm) */ + float pm25_std; /**< Standard Particulate Matter <=2.5 in parts per million + (ppm) */ + float pm100_std; /**< Standard Particulate Matter <=10.0 in parts per + million (ppm) */ + float pm10_env; /**< Environmental Particulate Matter <=1.0 in parts per + million (ppm) */ + float pm25_env; /**< Environmental Particulate Matter <=2.5 in parts per + million (ppm) */ + float pm100_env; /**< Environmental Particulate Matter <=10.0 in parts per + million (ppm) */ + float gas_resistance; /**< Proportional to the amount of VOC particles in + the air (Ohms) */ + float unitless_percent; /** +#include + +#include "i2cmaster.hpp" + +I2cMaster::I2cMaster () { + address = 0; + timer = 0; +} + +void I2cMaster::tick1ms () { + if (timer > 0) { + timer--; + } +} + +bool I2cMaster::begin (uint8_t addr) { + this->address = addr; + // TWBR = 13; // 100kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 100000) / (2 * 100000 * 4); + TWBR = 100; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4); + TWCR = (1 << TWEN); + return true; +} + +void I2cMaster::end () { + TWCR = (1 << TWEN); + TWBR = 0; +} + +bool I2cMaster::read (uint8_t *buffer, uint8_t len) { + if (start(true)) { + if (readBytes(buffer, len)) { + if (stop()) { + return true; + } + } + } + return false; +} + + +bool I2cMaster::write (const uint8_t *buffer, uint8_t len) { + if (start(false)) { + if (writeBytes(buffer, len)) { + if (stop()) { + return true; + } + } + } + return false; +} + +bool I2cMaster::writeByteAndBuffer (uint8_t byte, const uint8_t *buffer, uint8_t len) { + if (start(false)) { + do { + TWDR = byte; + TWCR = (1 << TWINT) | (1 << TWEN); // send byte + while (!(TWCR & (1 << TWINT))) {}; // wait until last action done + if ((TWSR & 0xf8) != 0x28) { + return false; + } + byte = *buffer++; + } while (len-- > 0); + return true; + } + return false; +} + + +bool I2cMaster::write_then_read (const uint8_t *write_buffer, uint8_t write_len, uint8_t *read_buffer, uint8_t read_len) { + if (start(false)) { + if (writeBytes(write_buffer, write_len)) { + if (start(true)) { + if (readBytes(read_buffer, read_len)) { + if (stop()) { + return true; + } + } + } + } + } + return false; +} + +// ------------------------------------------------------------- + +bool I2cMaster::readBytes (uint8_t *buffer, uint8_t len) { + while (len-- > 0) { + if (len > 0) { + TWCR = (1 << TWEA) | (1 << TWINT) | (1 << TWEN); // read data byte with ACK enabled + } else { + TWCR = (1 << TWINT) | (1 << TWEN); // read data byte with ACK disabled + } + while (!(TWCR & (1 << TWINT))) {}; // wait until last action done + uint8_t sr = TWSR & 0xf8; + if ((len > 0 && sr != 0x50) || (len == 0 && sr != 0x58)) { + return false; + } + *buffer++ = TWDR; + } + return true; +} + +bool I2cMaster::writeBytes (const uint8_t *buffer, uint8_t len) { + while (len-- > 0) { + // printf_P(PSTR("[wB:len=%d, byte=%02x]"), len + 1, *buffer); + TWDR = *buffer++; + TWCR = (1 << TWINT) | (1 << TWEN); // send data byte + timer = 5; + while (timer > 0 && !(TWCR & (1 << TWINT))) {}; // wait until last action done + if (!timer || (TWSR & 0xf8) != 0x28) { + return false; + } + } + return true; +} + + +bool I2cMaster::start (bool read) { + TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // send START condition + timer = 5; + while (timer > 0 && !(TWCR & (1 << TWINT))) {}; // wait until last action done + uint8_t sr = TWSR & 0xf8; + if (!timer || (sr != 0x08 && sr != 0x10)) { + return false; + } + TWDR = (address << 1) | (read ? 1 : 0); // address + R/nW + TWCR = (1 << TWINT) | (1 << TWEN); // send address/RW + timer = 5; + while (timer > 0 && !(TWCR & (1 << TWINT))) {}; // wait until last action done + sr = TWSR & 0xf8; + if (!timer || (!read && sr != 0x18) || (read && sr != 0x40)) { + return false; + } + return true; +} + +bool I2cMaster::stop () { + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); + while (TWCR & ( 1 << TWSTO)); + return true; +} + diff --git a/software/nano-644/test_2024-07-23/src/i2cmaster.hpp b/software/nano-644/test_2024-07-23/src/i2cmaster.hpp new file mode 100644 index 0000000..e70326c --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/i2cmaster.hpp @@ -0,0 +1,28 @@ +#ifndef I2C_MASTER +#define I2C_MASTER + +#include + +class I2cMaster { + public: + static void end (); + + public: + I2cMaster (); + void tick1ms (); + bool begin (uint8_t addr); + bool read (uint8_t *buffer, uint8_t len); + bool write(const uint8_t *buffer, uint8_t len); + bool write_then_read(const uint8_t *write_buffer, uint8_t write_len, uint8_t *read_buffer, uint8_t read_len); + bool writeByteAndBuffer (uint8_t byte, const uint8_t *buffer, uint8_t len); + + private: + uint8_t address; + uint8_t timer; + bool start (bool read); + bool stop (); + bool writeBytes (const uint8_t *buffer, uint8_t len); + bool readBytes (uint8_t *buffer, uint8_t len); +}; + +#endif \ No newline at end of file diff --git a/software/nano-644/test_2024-07-23/src/i2cslave.cpp b/software/nano-644/test_2024-07-23/src/i2cslave.cpp new file mode 100644 index 0000000..2bf6ac2 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/i2cslave.cpp @@ -0,0 +1,92 @@ +#include +#include +#include + +#include "i2cslave.hpp" + +I2cSlave::I2cSlave () { + timer = 0; + fromMaster.rIndex = 0; + fromMaster.wIndex = 0; + toMaster.rIndex = 0; + toMaster.wIndex = 0; +} + +void I2cSlave::tick1ms () { + if (timer > 0) { + timer--; + } +} + +bool I2cSlave::begin (uint8_t addr, bool acceptGeneralCalls) { + if (addr > 127) { + return false; + } + TWAR = addr << 1 | (acceptGeneralCalls ? 1 : 0); + TWBR = 100; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4); + TWCR = (1 << TWEA) | (1 << TWEN) | (1 << TWIE); + return true; +} + +void I2cSlave::end () { + TWCR = (1 << TWEN); + TWBR = 0; +} + +int I2cSlave::read () { + return getByte(fromMaster); +} + +void I2cSlave::write (uint8_t byte) { + putByte(toMaster, byte); +} + + +void I2cSlave::putByte (RingBuffer& buffer, uint8_t byte) { + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + buffer.data[buffer.wIndex++] = byte; + if (buffer.wIndex >= sizeof(buffer.data)) { + buffer.wIndex = 0; + } + if (buffer.wIndex == buffer.rIndex) { + buffer.rIndex++; + if (buffer.rIndex >= sizeof(buffer.data)) { + buffer.rIndex = 0; + } + } + } +} + +int I2cSlave::getByte (RingBuffer& buffer) { + uint8_t b; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + if (buffer.rIndex == buffer.wIndex) { + return EOF; + } + b = buffer.data[buffer.rIndex++]; + if (buffer.rIndex >= sizeof(buffer.data)) { + buffer.rIndex = 0; + } + } + return b; +} + +void I2cSlave::handleTWIIsr () { + uint8_t sr = TWSR & 0xf8; + switch (sr) { + case 0x80: { // Previously addressed with own SLA+W; data has been received; ACK has been returned + putByte(fromMaster, TWDR); + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE); // no TWEA -> only one byte accepted + break; + } + case 0xa8: { // Own SLA+R has been received; ACK has been returned + int response = getByte(toMaster);; + TWDR = response < 0 ? 0x00 : (uint8_t)response; + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE); // no TWEA -> only one byte accepted + break; + } + default: TWCR = (1 << TWINT) | (1 << TWEA) | (1 << TWEN) | (1 << TWIE); break; + } + +} + diff --git a/software/nano-644/test_2024-07-23/src/i2cslave.hpp b/software/nano-644/test_2024-07-23/src/i2cslave.hpp new file mode 100644 index 0000000..2fe2dc7 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/i2cslave.hpp @@ -0,0 +1,32 @@ +#ifndef I2C_SLAVE +#define I2C_SLAVE + +#include + +class I2cSlave { + private: + typedef struct { + uint8_t rIndex; + uint8_t wIndex; + uint8_t data[8]; + } RingBuffer; + + public: + I2cSlave (); + void tick1ms (); + bool begin (uint8_t addr, bool acceptGeneralCalls); + void end (); + void handleTWIIsr (); + int read (); + void write (uint8_t byte); + + private: + uint8_t timer; + RingBuffer fromMaster; + RingBuffer toMaster; + void putByte (RingBuffer& buffer, uint8_t byte); + int getByte (RingBuffer& buffer); + +}; + +#endif \ No newline at end of file diff --git a/software/nano-644/test_2024-07-23/src/main.cpp b/software/nano-644/test_2024-07-23/src/main.cpp new file mode 100644 index 0000000..0223927 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/main.cpp @@ -0,0 +1,259 @@ +#include +#include +#include +#include + +#include +#include + +#include "main.hpp" +#include "units/led.hpp" +#include "units/switch.hpp" +#include "units/rgb.hpp" +#include "units/seg7.hpp" +#include "units/poti.hpp" +#include "units/encoder.hpp" +#include "units/r2r.hpp" +#include "units/motor.hpp" +#include "units/portexp.hpp" +#include "units/lcd.hpp" +#include "units/uart1.hpp" +#include "units/modbus.hpp" +#include "units/ieee485.hpp" +#include "units/i2c.hpp" + + +extern "C" { + void __cxa_pure_virtual () { + } + + int __cxa_guard_acquire(uint8_t *g) { + return 0; + } + + void __cxa_guard_release(uint8_t *g) {} + + void __cxa_guard_abort(uint8_t *g) {} + + + int uart_putchar(char c, FILE *stream) { + if (c == '\n') { + uart_putchar('\r', stream); + } + if (stream == stdout) { + loop_until_bit_is_set(UCSR0A, UDRE0); + UDR0 = c; + } + return 0; + } + + + uint64_t volatile systemMillis = 0; + uint8_t volatile uartBuffer[32]; + uint8_t volatile rIndex = 0; + uint8_t volatile wIndex = 0; + + int uart_getchar (FILE *stream) { + // if (rIndex == wIndex) { + // // nothing in buffer + // return EOF; + // } + // printf_P(PSTR(" r%d"), rIndex); + while (rIndex == wIndex) { + // wait for character + } + + // don't use "char c" because german special characters would lead to negative return -> stream error + // char c = uartBuffer[rIndex++]; + + uint8_t c = uartBuffer[rIndex++]; + // printf_P(PSTR("(%02x) "), c); + if (c == '\r') { + c = '\n'; + } + putchar(c); // echo on terminal + return c; + } + + static FILE mystdout = { 0, 0, _FDEV_SETUP_WRITE , 0, 0, uart_putchar, NULL, 0 }; + static FILE mystdin = { 0, 0, _FDEV_SETUP_READ , 0, 0, NULL, uart_getchar, 0 }; + + static volatile uint32_t timer1ms = 0; + static volatile int keyUart0 = EOF; + + Led led; + Switch sw; + Rgb rgb; + Seg7 seg7; + Poti poti; + Encoder encoder; + R2r r2r; + Motor motor; + PortExp portExp; + Lcd lcd; + Uart1 uart1; + Modbus modbus; + Ieee485 ieee485; + I2c i2cSparkfun(I2c::SparkFunEnvCombo); + I2c i2cMaster(I2c::Master); + I2c i2cSlave(I2c::Slave); + +} + +void setTimer (uint32_t ms) { + ATOMIC_BLOCK(ATOMIC_FORCEON) { + timer1ms = ms; + } +} + +int wait (uint32_t ms) { + setTimer(ms); + do { + ATOMIC_BLOCK(ATOMIC_FORCEON) { + ms = timer1ms; + } + } while (ms > 0 && keyUart0 == EOF); + return keyUart0; +} + +int main () { + + // Nano-644 LEDs (Green, Orange, Red) + DDRC |= (1 << DDC4) | (1 << DDC3) | (1 << DDC2); + PORTC &= ~((1 << PORT4) | (1 << PORT3) | (1 << PORT2)); + + // Nano-644 push button SW2 + DDRC &= ~(1 << DDC5); + PORTC |= (1 << PORT5); // enable internal pullup resistor + + // UART0 interface on Nano-644 + UCSR0A = (1 << U2X0); + UCSR0B = (1 << RXCIE0) | (1 << RXEN0) | (1 <getName()); + } + printf_P(PSTR("\nSelect unit: ")); + rIndex = 0; wIndex = 0; + fgets(s, sizeof(s), stdin); + } while (sscanf(s, "%x", &i) != 1 || i < 0 || i >= sizeof(unit) / sizeof(unit[0]) ); + + TestUnit *pu = unit[i]; + printf_P(PSTR("\n\n[%s]: "), pu->getName()); + keyUart0 = EOF; + + for (uint8_t subtest = 0; subtest < 0xff; subtest++) { + printf_P(PSTR("\n%4d: "), subtest); + if (pu->run(subtest) < 0) { + break; + } + if (keyUart0 == 27) { + keyUart0 = EOF; + break; + } + keyUart0 = EOF; + } + pu->cleanup(); + } +} + +uint64_t millis () { + volatile uint64_t millis = systemMillis; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + millis = systemMillis; + } + return millis; +} + +ISR (USART0_RX_vect) { + uint8_t b = UDR0; + keyUart0 = b; + uartBuffer[wIndex++] = b; + // printf_P(PSTR(" w%d(%02x)"), wIndex, b); + if (wIndex == rIndex) { + // buffer overflow, kick out oldest byte + rIndex++; + } +} + +ISR (USART1_RX_vect) { + uint8_t b = UDR1; + if (modbus.enabled) { + modbus.handleRxByte(b); + } + if (uart1.enabled) { + uart1.handleRxByte(b); + } + if (ieee485.enabled) { + ieee485.handleRxByte(b); + } +} + +ISR (TWI_vect) { + if (i2cMaster.enabled) { + i2cMaster.handleTwiIrq(); + } else if (i2cSlave.enabled) { + i2cSlave.handleTwiIrq(); + } else if (i2cSparkfun.enabled) { + i2cSparkfun.handleTwiIrq(); + } else { + TWCR = (1 << TWINT); // clear interrupt request bit and disable TWI + } +} + +ISR (TIMER2_COMPA_vect) { // every 100us + static uint16_t timer500ms = 0; + static uint8_t timer100us = 0; + + if (encoder.enabled) { + encoder.tick100us(); + } + if (motor.enabled) { + motor.tick100us(); + } + + timer100us++; + if (timer100us >= 10) { + timer100us = 0; + if (timer1ms > 0) { + timer1ms--; + } + systemMillis++; + i2cMaster.tick1ms(); + i2cSlave.tick1ms(); + i2cSparkfun.tick1ms(); + } + + timer500ms++; + if (timer500ms >= 5000) { + PORTC ^= (1 << PORTC3); // orange LED blinking + timer500ms = 0; + } +} + diff --git a/software/nano-644/test_2024-07-23/src/main.hpp b/software/nano-644/test_2024-07-23/src/main.hpp new file mode 100644 index 0000000..f60ec89 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/main.hpp @@ -0,0 +1,20 @@ +#ifndef MAIN_HPP +#define MAIN_HPP + +#include +#include + +#define ENTER '\r' +#define CTRLC '\003' + +extern int wait (uint32_t ms); +extern uint64_t millis (); + +class TestUnit { + public: + virtual int8_t run (uint8_t subtest) = 0; + virtual void cleanup () = 0; + virtual PGM_P getName () = 0; +}; + +#endif \ No newline at end of file diff --git a/software/nano-644/test_2024-07-23/src/units/encoder.cpp b/software/nano-644/test_2024-07-23/src/units/encoder.cpp new file mode 100644 index 0000000..bc84f48 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/encoder.cpp @@ -0,0 +1,86 @@ +#include +#include + +#include "encoder.hpp" +#include "../main.hpp" + +// PB0/T0 ... Encoder A +// PB1/T1 ... Encoder B +// PB2/INT2 ... push switch of encoder (pushed = 0) + +// Encoder signals on rotation clockwise 1 step: +// A -----____------ one char app. 1ms..2ms (rotation speed) +// B -------___----- +// one step when: A = 0, B= 1->0 + +// Encoder signals on rotation counterclockwise 1 step: +// A -----____------ one char app. 1ms..2ms (rotation speed) +// B --______----- +// one step when: A = 0, B= 0->1 + + +void Encoder::cleanup () { + DDRB &= ~((1 << PB2) | (1 << PB1) | (1 << PB0)); + PORTB &= ~((1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0)); + enabled = 0; +} + +int8_t Encoder::run (uint8_t subtest) { + switch (subtest) { + case 0: { + printf_P(PSTR("init")); + DDRB &= ~((1 << PB2) | (1 << PB1) | (1 << PB0)); + PORTB |= (1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0); // enable pullup + enabled = 1; + return 0; + } + + case 1: { + while (wait(10) == EOF) { + printf_P(PSTR("\r => Encoder (push to clear): ")); + printf_P(PSTR("%5d (0x%02x) "), count, (uint8_t)count); + if ((PINB & (1 << PINB2)) == 0) { + reset(); + } + } + return 0; + } + + case 2: { + printf_P(PSTR("end")); + break; + } + } + + return -1; +} + +struct EncoderState { + uint8_t a:1; // signal A + uint8_t b:1; // signal B +}; + +void Encoder::tick100us () { + static EncoderState lastState = { 1, 1 }; + static EncoderState lastStableState = { 1, 1 }; + + if ((DDRB & 0x03) || (PORTB & 0x07) != 0x07) { + count = 0; + return; // Enocder pins not configured + } + EncoderState nextState = { (PINB & 0x01) == 0x01, (PINB & 0x02) == 0x02 }; + if (nextState.a == lastState.a && nextState.b == lastState.b) { + if (lastStableState.a == 0 && nextState.b != lastStableState.b) { + if (nextState.b == 0) { + count = count < 127 ? count + 1 : 127; + } else { + count = count > -128 ? count - 1 : -128; + } + } + lastStableState.a = nextState.a; + lastStableState.b = nextState.b; + } + lastState.a = nextState.a; + lastState.b = nextState.b; +} + diff --git a/software/nano-644/test_2024-07-23/src/units/encoder.hpp b/software/nano-644/test_2024-07-23/src/units/encoder.hpp new file mode 100644 index 0000000..10bcc0f --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/encoder.hpp @@ -0,0 +1,22 @@ +#ifndef ENCODER_HPP +#define ENCODER_PP + +#include +#include "../main.hpp" +#include + +class Encoder : public TestUnit { + public: + uint8_t enabled; + int8_t count; + + public: + Encoder () { reset(); enabled = 0; }; + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Encoder"); } + void reset () { count = 0; } + void tick100us (); +}; + +#endif \ No newline at end of file diff --git a/software/nano-644/test_2024-07-23/src/units/i2c.cpp b/software/nano-644/test_2024-07-23/src/units/i2c.cpp new file mode 100644 index 0000000..4c5934f --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/i2c.cpp @@ -0,0 +1,300 @@ +#include +#include +#include +#include +#include + +#include "i2c.hpp" +#include "../adafruit/bme280.h" +#include "../main.hpp" + +// Sparkfun https://www.sparkfun.com/products/22858 +// ENS160 address 0x53 (air quality sensor) +// BME280 address 0x77 /humidity/temperature sensor) +// register 0xfe: humidity_7:0 +// register 0xfd: humidity_15:8 + +PGM_P I2c::getName () { + switch (mode) { + case SparkFunEnvCombo: return PSTR("I2C-Sparkfun Env-Combo"); + case Master: return PSTR("I2C-Master"); + case Slave: return PSTR("I2C-Slave"); + } + return "?"; +} + +void I2c::cleanup () { + enabled = false; + TWCR = (1 << TWEN); + TWBR = 0; + ADMUX = 0; + ADCSRA = 0; +} + +int8_t I2c::run (uint8_t subtest) { + if (subtest == 0) { + TWBR = 13; // 100kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 100000) / (2 * 100000 * 4); + TWBR = 28; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4); + TWBR = 100; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4); + TWCR = (1 << TWEN); + ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V + ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128 + enabled = true; + printf_P(PSTR("init")); + + } else if (subtest == 1 && mode == I2c::SparkFunEnvCombo) { + printf_P(PSTR(" BM280 ... ")); + if (!bm280.begin()) { + printf_P(PSTR("E1")); + return -1; + } + printf_P(PSTR("OK, ENS160 ... ")); + if (!ens160.begin()) { + printf_P(PSTR("E2")); + return -1; + } + if (!ens160.setMode(ENS160_OPMODE_STD)) { + printf_P(PSTR("E3")); + return -1; + } + if (!ens160.set_envdata(25.0, 65)) { + printf_P(PSTR("E4")); + return -1; + } + + printf_P(PSTR("OK")); + float accTemp = 0, accHumidity = 0; + int8_t accCount = -1; + + do { + // BME280 + float p = bm280.readPressure(); + printf_P(PSTR("\n => BM280: P= %.3fbar"), (double)p / 100000.0); + float t = bm280.readTemperature(); + printf_P(PSTR(", T= %.2f°C"), (double)t); + float h = bm280.readHumidity(); + printf_P(PSTR(", H= %.2f%%"), (double)h); + + if (accCount >= 0 && !isnanf(h) && !isnan(t)) { + accTemp += t; + accHumidity += h; + accCount++; + } + + bm280.setSampling( + Adafruit_BME280::MODE_NORMAL, + Adafruit_BME280::SAMPLING_X16, + Adafruit_BME280::SAMPLING_X16, + Adafruit_BME280::SAMPLING_X16, + Adafruit_BME280::FILTER_OFF, + Adafruit_BME280::STANDBY_MS_1000 + ); + + // ENS160 only activated every 32s to avoid wrong temperature measuerment + // if ES160 would be continously active, the temperature would be 4°C higher + // -> ES160 has not enough distance to BM280 + // This solution causes only a +0.3°C higher temperatur value + if (accCount < 0 || accCount >= 32) { + printf_P(PSTR(" | ENS160 (")); + if (accCount > 0) { + h = accHumidity / accCount; + t = accTemp / accCount; + accTemp = 0; + accHumidity = 0; + } + accCount = 0; + if (!ens160.set_envdata(t, h)) { + printf_P(PSTR("E1)")); + } else { + printf_P(PSTR("%.1f°C/%.1f%%): "), (double)t, (double)h); + if (!ens160.setMode(ENS160_OPMODE_STD)) { + printf_P(PSTR("E2")); + } else { + for (uint8_t i = 0; i < 100; i++) { + _delay_ms(15); + uint8_t status; + if (ens160.readStatus(&status)) { + if (status & ENS160_DATA_STATUS_NEWDAT) { + ENS160_DATA data; + if (ens160.readData(&data)) { + printf_P(PSTR(" aqi=%d("), data.aqi); + switch(data.aqi) { + case 1: printf_P(PSTR("excellent")); break; + case 2: printf_P(PSTR("good")); break; + case 3: printf_P(PSTR("moderate")); break; + case 4: printf_P(PSTR("poor")); break; + case 5: printf_P(PSTR("unhealthy")); break; + default: printf_P(PSTR("?")); break; + } + printf_P(PSTR("), tvoc=%dppb"), data.tvoc); + printf_P(PSTR(", eco2=%d("), data.eco2); + if (data.eco2 < 400) { + printf_P(PSTR("?")); + } else if (data.eco2 < 600) { + printf_P(PSTR("excellent")); + } else if (data.eco2 < 800) { + printf_P(PSTR("good")); + } else if (data.eco2 < 1000) { + printf_P(PSTR("fair")); + } else if (data.eco2 < 1500) { + printf_P(PSTR("poor")); + } else { + printf_P(PSTR("bad")); + } + printf_P(PSTR(")")); + } + break; + } + } + } + } + if (!ens160.setMode(ENS160_OPMODE_IDLE)) { + printf_P(PSTR("E3")); + } + } + } + + } while (wait(1000) == EOF); + + } else if (subtest == 1 && mode == I2c::Master) { + if (!master.begin(0x01)) { + printf_P(PSTR("E1")); + return -1; + } + do { + uint8_t buffer[1]; + // read poti + ADCSRA |= (1 << ADSC); // start ADC + while (ADCSRA & (1 << ADSC)) {} // wait for result + buffer[0] = ADCH; + printf_P(PSTR("\n write 0x%02x"), buffer[0]); + if (!master.write(buffer, 1)) { + printf_P(PSTR(" -> ERROR")); + } + printf_P(PSTR(", read ")); + if (master.read(buffer, 1)) { + printf_P(PSTR("0x%02x"), buffer[0]); + } else { + printf_P(PSTR(" -> ERROR")); + } + } while (wait(1000) == EOF); + master.end(); + + } else if (subtest == 1 && mode == I2c::Slave) { + if (!slave.begin(0x01, false)) { + printf_P(PSTR("E1")); + return -1; + } + do { + int fromMaster = slave.read(); + if (fromMaster != EOF) { + ADCSRA |= (1 << ADSC); // start ADC + while (ADCSRA & (1 << ADSC)) {} // wait for result + slave.write(ADCH); + printf_P(PSTR("\n => from master: 0x%02x -> to master: 0x%02x"), fromMaster, ADCH); + } + } while (wait(0) == EOF); + slave.end(); + + } else { + printf_P(PSTR("end")); + return -1; + } + wait(500); + return 0; +} + +void I2c::handleTwiIrq () { + if (mode == I2c::Slave) { + DDRD |= (1 << PD7); + PORTD |= (1 << PD7); + slave.handleTWIIsr(); + PORTD &= ~(1 << PD7); + } else { + TWCR |= (1 << TWINT); // clear Interrupt Request + } +} + +// uint16_t I2c::startRead (uint8_t address) { +// TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // send START condition +// while (!(TWCR & (1 << TWINT))) {}; // wait until last action done +// uint8_t sr = TWSR & 0xf8; +// if (sr != 0x08 && sr != 0x10) { +// return 0x0100 | sr; +// } + +// TWDR = (address << 1) | 0x01; // address + READ (R/W = 1) +// TWCR = (1 << TWINT) | (1 << TWEN); // send address/RW +// while (!(TWCR & (1 << TWINT))) {}; // wait until last action done +// sr = TWSR & 0xf8; +// if (sr != 0x40) { +// return 0x0200 | sr; +// } +// return 0; +// } + +// uint16_t I2c::startWrite (uint8_t address) { +// TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // send START condition +// while (!(TWCR & (1 << TWINT))) {}; // wait until last action done +// uint8_t sr = TWSR & 0xf8; +// if (sr != 0x08 && sr != 0x10) { +// return 0x0300 | sr; +// } + +// TWDR = (address << 1) | 0x00; // address + WRITE (R/W = 0) +// TWCR = (1 << TWINT) | (1 << TWEN); // send address/RW +// while (!(TWCR & (1 << TWINT))) {}; // wait until last action done +// sr = TWSR & 0xf8; +// if (sr != 0x18) { +// return 0x0400 | sr; +// } +// return 0; +// } + +// void I2c::stop () { +// TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); +// } + +// uint16_t I2c::writeData (uint8_t size, const uint8_t *data) { +// while (size-- > 0) { +// TWDR = *data++; +// TWCR = (1 << TWINT) | (1 << TWEN); // send data byte +// while (!(TWCR & (1 << TWINT))) {}; // wait until last action done +// uint8_t sr = TWSR & 0xf8; +// if (sr != 0x28) { +// return 0x0500 | sr; +// } +// } +// return 0; +// } + +// uint16_t I2c::writeByte (uint8_t data) { +// return writeData(1, &data); +// } + +// uint16_t I2c::readData (uint8_t size, uint8_t *data) { +// while (size-- > 0) { +// if (size > 0) { +// TWCR = (1 << TWEA) | (1 << TWINT) | (1 << TWEN); // read data byte with ACK enabled +// } else { +// TWCR = (1 << TWINT) | (1 << TWEN); // read data byte with ACK disabled +// } +// while (!(TWCR & (1 << TWINT))) {}; // wait until last action done +// uint8_t sr = TWSR & 0xf8; +// if ((size > 0 && sr != 0x50) || (size == 0 && sr != 0x58)) { +// return 0x0600 | sr; +// } +// *data++ = TWDR; +// } +// return 0; +// } + +// int32_t I2c::compensateBm280T (int32_t adcT) { +// // int32_t var1, var2, t; +// // var1 = ((((adcT >> 3) - ((int32_t)bm280.digT[0] << 1))) * ((int32_t)bm280.digT[1])) >> 11; +// // var2 = (((((adcT >> 4) - ((int32_t)bm280.digT[0])) * ((adcT >> 4) - ((int32_t)bm280.digT[0]))) >> 12) * ((int32_t)bm280.digT[2])) >> 14; +// // bm280.tFine = var1 + var2; +// // t = (bm280.tFine * 5 + 128) >> 8; +// // return t; +// return -1; +// } diff --git a/software/nano-644/test_2024-07-23/src/units/i2c.hpp b/software/nano-644/test_2024-07-23/src/units/i2c.hpp new file mode 100644 index 0000000..e1f1802 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/i2c.hpp @@ -0,0 +1,42 @@ +#ifndef I2C_HPP +#define I2C_HPP + +#include +#include "../main.hpp" +#include "../adafruit/bme280.h" +#include "../adafruit/ens160.h" +#include "../i2cmaster.hpp" +#include "../i2cslave.hpp" + + +class I2c : public TestUnit { + public: + typedef enum I2cMode { SparkFunEnvCombo, Master, Slave } I2cMode; + + private: + I2cMode mode; + Adafruit_BME280 bm280; + ScioSense_ENS160 ens160; + I2cMaster master; + I2cSlave slave; + + public: + bool enabled; + + public: + I2c (I2cMode mode) { enabled = false; this->mode = mode; } + void tick1ms () { master.tick1ms(); slave.tick1ms(); } + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName (); + void handleTwiIrq (); + uint16_t startRead (uint8_t address); + uint16_t startWrite (uint8_t address); + void stop (); + uint16_t writeByte (uint8_t data); + uint16_t writeData (uint8_t size, const uint8_t *data); + uint16_t readData (uint8_t size, uint8_t *data); + int32_t compensateBm280T (int32_t adcT); +}; + +#endif \ No newline at end of file diff --git a/software/nano-644/test_2024-07-23/src/units/ieee485.cpp b/software/nano-644/test_2024-07-23/src/units/ieee485.cpp new file mode 100644 index 0000000..89e25ec --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/ieee485.cpp @@ -0,0 +1,91 @@ +#include +#include +#include +#include + +#include "ieee485.hpp" +#include "../main.hpp" + +// PB0 ... nRE .. Read enable +// PB1 ... DE .. Data enable + +#define SET_nRE (PORTB |= (1 << PB0)) +#define CLR_nRE (PORTB &= ~(1 << PB0)) +#define SET_DE (PORTB |= (1 << PB1)) +#define CLR_DE (PORTB &= ~(1 << PB1)) + + +void Ieee485::cleanup () { + enabled = 0; + + ADMUX = 0; + ADCSRA = 0; + + UCSR1A = 0; + UCSR1B = 0; + UCSR1C = 0; + UBRR1H = 0; + UBRR1L = 0; + PORTD &= ~(1 << PD2); + DDRB &= ~((1 << PB1) | (1 << PB0)); + PORTB &= ~((1 << PB1) | (1 << PB0)); +} + +int8_t Ieee485::run (uint8_t subtest) { + if (subtest == 0) { + // Poti + ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V + ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128 + + // Modbus + SET_nRE; + CLR_DE; + DDRB |= (1 << PB1) | (1 << PB0); + + // UART1 interface on Nano-644 + PORTD |= (1 << PD2); // enable RxD1 pullup + UCSR1A = (1 << U2X1); + UCSR1B = (1 << RXCIE1) | (1 << RXEN1) | (1 < send Byte 0x%02x"), ADCH); + int b; + ATOMIC_BLOCK(ATOMIC_FORCEON) { + b = receivedByte; + receivedByte = -1; + } + if (b >= 0) { + printf_P(PSTR("\n => receive Byte: 0x%02x"), b); + } + } + + } else { + printf_P(PSTR("end")); + return -1; + } + + return 0; +} + +void Ieee485::handleRxByte (uint8_t b) { + receivedByte = b; +} diff --git a/software/nano-644/test_2024-07-23/src/units/ieee485.hpp b/software/nano-644/test_2024-07-23/src/units/ieee485.hpp new file mode 100644 index 0000000..677963f --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/ieee485.hpp @@ -0,0 +1,21 @@ +#ifndef IEEE485_HPP +#define IEEE485_HPP + +#include +#include "../main.hpp" +#include + +class Ieee485 : public TestUnit { + public: + uint8_t enabled; + int16_t receivedByte; + + public: + Ieee485 () { enabled = 0; receivedByte = -1; } + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual const char *getName () { return PSTR("IEEE485"); } + void handleRxByte (uint8_t); +}; + +#endif \ No newline at end of file diff --git a/software/nano-644/test_2024-07-23/src/units/lcd.cpp b/software/nano-644/test_2024-07-23/src/units/lcd.cpp new file mode 100644 index 0000000..91d0845 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/lcd.cpp @@ -0,0 +1,224 @@ +#include +#include +#include + +#include "lcd.hpp" +#include "../main.hpp" + +// PA3 ..... E --> LCD Enable (Verbindung via J25 erforderlich) +// PD6 ..... R/W --> Read/Write: Read=1, Write=0 +// PD7 ..... RS --> Register Select: Command=0, Data=1 +// PB7:0 ... Data --> Achtung von 5V LCD nicht lesen! + +// #define LCD_3V3 +#ifdef LCD_3V3 + #define DATA_PIN PINB +#endif + +#define DATA_PORT PORTB +#define DIR_DATA_PORT DDRB + +#define PORT_RS PD7 +#define SET_RS_PIN (PORTD |= (1 << PORT_RS)) +#define CLR_RS_PIN (PORTD &= ~(1 << PORT_RS)) +#define SET_DIR_RS (DDRD |= (1 << PORT_RS)) +#define CLR_DIR_RS (DDRD &= ~(1 << PORT_RS)) + +#define PORT_RW PD6 +#define SET_RW_PIN (PORTD |= (1 << PORT_RW)) +#define CLR_RW_PIN (PORTD &= ~(1 << PORT_RW)) +#define SET_DIR_RW (DDRD |= (1 << PORT_RW)) +#define CLR_DIR_RW (DDRD &= ~(1 << PORT_RW)) + +#define SET_E_PIN (PORTA |= (1 << PA3)) +#define CLR_E_PIN (PORTA &= ~(1 << PA3)) +#define SET_DIR_E (DDRA |= (1 << PA3)) +#define CLR_DIR_E (DDRA &= ~(1 << PA3)) + +// Befehle für das Display + +#define DISP_CLEAR 0b00000001 // Display clear +#define DISP_ON 0b00001111 // Display on +#define DISP_OFF 0b00001011 // Display off +#define CURSOR_ON 0b00001111 // Cursor on +#define CURSOR_OFF 0b00001101 // Cursor off +#define BLINK_ON 0b00001111 // Cursor Blink +#define BLINK_OFF 0b00001110 // Cursor No Blink + + +void Lcd::cleanup () { + DDRA &= ~(1 << PA3); + PORTA &= ~(1 << PA3); + DDRD &= ~((1 << DDD7) | (1 << DDD6)); + PORTD &= ~((1 << PORTD7) | (1 << PORTD6)); + PORTB = 0; + DDRB = 0; +} + +int8_t Lcd::run (uint8_t subtest) { + if (subtest == 0) { + // DDRA |= (1 << PA3); + // PORTA &= ~(1 << PA3); + // DDRD |= (1 << DDD7) | (1 << DDD6); + // PORTD &= ~((1 << PORTD7) | (1 << PORTD6)); + // PORTB = 0; + // DDRB = 0xff; + init(); + #ifdef LCD_3V3 + printf_P(PSTR("init 3.3V LCD")); + #else + printf_P(PSTR("init 5V LCD")); + #endif + + } else if (subtest == 1) { + for (uint8_t i = 0; i < 20 * 4; i++) { + char c = (char)(i + 32); + if (i % 20 == 0) { + setCursor(i / 20 + 1, 1); + } + writeData(c); + while (isBusy()) {}; + } + // setCursor(1, 1); + // writeString(" 1234567890<>,;.:-_#+"); + // setCursor(2, 1); + // writeString("abcdefghijklmnopqrst"); + // setCursor(3, 1); + // writeString("uvwxyzABCDEFGHIJKLMN"); + // setCursor(4, 1); + // writeString("OPQRSTUVWXYZ "); + printf_P(PSTR("LCD beschrieben")); + while (wait(1) == EOF) { + } + + } else { + printf_P(PSTR("end")); + return -1; + } + wait(500); + return 0; +} + +void Lcd::init () { + CLR_RW_PIN; // write + CLR_RS_PIN; // command + CLR_E_PIN; // E = 0 + + SET_DIR_RW; + SET_DIR_RS; + SET_DIR_E; + + DATA_PORT = 0; + DIR_DATA_PORT = 0xff; + _delay_ms(16); // min 15ms warten für Reset des Displays + + DATA_PORT = 0b00111011; // 8bit Modus, 5x7 Zeichen, Mehrzeilen Display + CLR_RW_PIN; // write + CLR_RS_PIN; // command + SET_E_PIN; // E = 1 (transfer start) + _delay_us(10); // min. 10us + CLR_E_PIN; // E = 0 (transfer end) + _delay_ms(5); // min. 4.1ms + + DATA_PORT = 0b00111011; // 8bit Modus, 5x7 Zeichen, Mehrzeilen Display + CLR_RW_PIN; // write + CLR_RS_PIN; // command + SET_E_PIN; // E = 1 (transfer start) + _delay_us(10); // min. 10us + CLR_E_PIN; // E = 0 (transfer end) + _delay_us(100); // min. 100us + + DATA_PORT = 0b00111011; // 8bit Modus, 5x7 Zeichen, Mehrzeilen Display + CLR_RW_PIN; // write + CLR_RS_PIN; // command + SET_E_PIN; // E = 1 (transfer start) + _delay_us(10); // min. 10us + CLR_E_PIN; // E = 0 (transfer end) + _delay_us(100); // min. 100us + + writeCommand(DISP_OFF); // Display aus + while(isBusy()) {}; + writeCommand(DISP_ON); // Display ein + while(isBusy()) {}; + writeCommand( BLINK_OFF & CURSOR_OFF); // Blink aus und Cursor aus + while(isBusy()) {}; + writeCommand(DISP_CLEAR);// Clear display + while(isBusy()) {}; +} + +uint8_t Lcd::isBusy () { + #ifdef LCD_3V3 + DIR_DATA_PORT = 0; + SET_RW_PIN; // read + CLR_RS_PIN; // command + SET_E_PIN; // E = 1 (transfer start) + _delay_us(10); + uint8_t busy = DATA_PIN & 0x80; // read bit 7 (busy bit) + CLR_E_PIN; // E = 0 (transfer end) + CLR_RW_PIN; + DIR_DATA_PORT = 0xff; + return busy != 0; + #else + _delay_us(200); + return 0; + #endif + +} + +void Lcd::writeCommand (uint8_t cmd) { + DATA_PORT = cmd; + CLR_RW_PIN; // write + CLR_RS_PIN; // command + SET_E_PIN; // E = 1 (transfer start) + _delay_us(10); // min. 10us + CLR_E_PIN; // E = 0 (transfer end) + _delay_us(10); // min. 10us + DATA_PORT = 0; +} + +void Lcd::setDDRamAddr (uint8_t address) { + DATA_PORT = address | 0x80; + CLR_RW_PIN; // write + CLR_RS_PIN; // command + SET_E_PIN; // E = 1 (transfer start) + _delay_us(10); // min. 10us + CLR_E_PIN; // E = 0 (transfer end) + _delay_us(10); // min. 10us + DATA_PORT = 0; +} + +void Lcd::writeString (const char *s) { + while (*s) { + writeData(*s++); + while (isBusy()) {}; + } +} + +void Lcd::writeData (uint8_t data) { + DATA_PORT = data; // Write data to port + SET_RS_PIN; // data + CLR_RW_PIN; // command + SET_E_PIN; // E = 1 (transfer start) + _delay_us(10); // min. 10us + CLR_E_PIN; // E = 0 (transfer end) + _delay_us(10); // min. 10us + CLR_RS_PIN; + DATA_PORT = 0; +} + +void Lcd::setCursor (uint8_t row, uint8_t column) { + uint8_t b; + if (column > 20) { + return; + } + switch (row) { + case 1: b = 0x00 + column - 1; break; + case 2: b = 0x40 + column - 1; break; + case 3: b = 0x14 + column - 1; break; + case 4: b = 0x54 + column - 1; break; + default: return; + } + setDDRamAddr(b); + while (isBusy()) {}; +} + diff --git a/software/nano-644/test_2024-07-23/src/units/lcd.hpp b/software/nano-644/test_2024-07-23/src/units/lcd.hpp new file mode 100644 index 0000000..14561b0 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/lcd.hpp @@ -0,0 +1,24 @@ +#ifndef LCD_HPP +#define LCD_HPP + +#include +#include "../main.hpp" +#include + +class Lcd : public TestUnit { + public: + Lcd () {}; + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Lcd"); } + + void init (); + uint8_t isBusy (); + void writeCommand (uint8_t); + void setDDRamAddr (uint8_t); + void writeString (const char *s); + void writeData (uint8_t); + void setCursor (uint8_t row, uint8_t column); +}; + +#endif \ No newline at end of file diff --git a/software/nano-644/test_2024-07-23/src/units/led.cpp b/software/nano-644/test_2024-07-23/src/units/led.cpp new file mode 100644 index 0000000..a35ca81 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/led.cpp @@ -0,0 +1,32 @@ +#include +#include +#include + +#include "led.hpp" +#include "../main.hpp" + +void Led::cleanup () { + DDRD &= ~((1 << DDD7) | (1 << DDD6) | (1 << DDD5) | (1 << DDD4)); + PORTD &= ~((1 << PORTD7) | (1 << PORTD6) | (1 << PORTD5) | (1 << PORTD4)); +} + +int8_t Led::run (uint8_t subtest) { + if (subtest == 0) { + DDRD |= (1 << DDD7) | (1 << DDD6) | (1 << DDD5) | (1 << DDD4); + PORTD &= ~((1 << PORTD7) | (1 << PORTD6) | (1 << PORTD5) | (1 << PORTD4)); + printf_P(PSTR("init")); + + } else if (subtest <= 16) { + subtest = (subtest - 1) % 4; + PORTD &= ~((1 << PORTD7) | (1 << PORTD6) | (1 << PORTD5) | (1 << PORTD4)); + PORTD |= (1 << (subtest + 4)); + printf_P(PSTR("Test LED PD%d"), subtest + 4); + + } else { + printf_P(PSTR("end")); + return -1; + } + wait(500); + return 0; +} + diff --git a/software/nano-644/test_2024-07-23/src/units/led.hpp b/software/nano-644/test_2024-07-23/src/units/led.hpp new file mode 100644 index 0000000..acff045 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/led.hpp @@ -0,0 +1,16 @@ +#ifndef LED_HPP +#define LED_HPP + +#include +#include "../main.hpp" +#include + +class Led : public TestUnit { + public: + Led () {}; + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Led"); } +}; + +#endif \ No newline at end of file diff --git a/software/nano-644/test_2024-07-23/src/units/modbus.cpp b/software/nano-644/test_2024-07-23/src/units/modbus.cpp new file mode 100644 index 0000000..ec74d50 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/modbus.cpp @@ -0,0 +1,145 @@ +#include +#include +#include + +#include "modbus.hpp" +#include "../main.hpp" + +// PB0 ... nRE .. Read enable +// PB1 ... DE .. Data enable + +#define SET_nRE (PORTB |= (1 << PB0)) +#define CLR_nRE (PORTB &= ~(1 << PB0)) +#define SET_DE (PORTB |= (1 << PB1)) +#define CLR_DE (PORTB &= ~(1 << PB1)) + + +void Modbus::cleanup () { + enabled = 0; + UCSR1A = 0; + UCSR1B = 0; + UCSR1C = 0; + UBRR1H = 0; + UBRR1L = 0; + PORTD &= ~(1 << PD2); + DDRB &= ~((1 << PB1) | (1 << PB0)); + PORTB &= ~((1 << PB1) | (1 << PB0)); +} + +int8_t Modbus::run (uint8_t subtest) { + if (subtest == 0) { + SET_nRE; + CLR_DE; + DDRB |= (1 << PB1) | (1 << PB0); + + // UART1 interface on Nano-644 + PORTD |= (1 << PD2); // enable RxD1 pullup + UCSR1A = (1 << U2X1); + UCSR1B = (1 << RXCIE1) | (1 << RXEN1) | (1 <= 1 && subtest <= 4) { + uint8_t nre, de, b; + switch (subtest) { + case 1: nre = 1; de = 0; b = 0x01; break; + case 2: nre = 1; de = 1; b = 0x8e; break; + case 3: nre = 0; de = 0; b = 0x55; break; + case 4: nre = 0; de = 1; b = 0xaa; break; + default: return -1; + } + printf_P(PSTR(" DE=%u, nRE=%u send 0x%02x... "), de, nre, b); + if (nre) { + SET_nRE; + } else { + CLR_nRE; + } + if (de) { + SET_DE; + } else { + CLR_DE; + } + _delay_us(100); + receivedBytes = 0; + UDR1 = b; + _delay_ms(1); + if (receivedBytes > 0) { + printf_P(PSTR("0x%02x received"), received[0]); + receivedBytes = 0; + } else { + printf_P(PSTR("no byte received")); + } + printf_P(PSTR(" ... press key to proceed")); + while (wait(0xffffffff) == EOF) {} + CLR_DE; + SET_nRE; + + } else if (subtest == 5) { + static uint8_t frame[] = { 0x01, 0x04, 0x00, 0x00, 0x00, 0x02, 0x71, 0xcb }; + printf_P(PSTR("Modbus: lese Spannung von Eastron SDM-230 (Einphasenzähler)")); + SET_DE; + CLR_nRE; + _delay_us(100); + do { + SET_DE; + receivedBytes = 0; + for (uint8_t i = 0; i < sizeof(frame); i++) { + UCSR1A |= (1 << TXC1); + UDR1 = frame[i]; + while ((UCSR1A & (1 < Sending:")); + for (uint8_t i = 0; i < sizeof(frame); i++) { + printf_P(PSTR(" 0x%02x"), frame[i]); + } + int k = wait(100); + + printf_P(PSTR("\n RxD1:")); + if (receivedBytes == 0) { + printf_P(PSTR("?")); + } else { + for (uint8_t i = 0; i < receivedBytes; i++) { + if (i == sizeof(frame)) { + printf_P(PSTR(" ")); + } + printf_P(PSTR(" 0x%02x"), received[i]); + } + } + if (receivedBytes >= 16) { + union { + uint8_t b[4]; + float value; + } f; + f.b[0] = received[14]; + f.b[1] = received[13]; + f.b[2] = received[12]; + f.b[3] = received[11]; + printf_P(PSTR(" -> %4.8fV\n"), (double)f.value); + } + if (k != EOF) { + break; + } + + } while (wait(1000) == EOF); + + } else { + printf_P(PSTR("end")); + return -1; + } + wait(500); + return 0; +} + +void Modbus::handleRxByte (uint8_t b) { + if (receivedBytes < sizeof(received)) { + received[receivedBytes++] = b; + } +} diff --git a/software/nano-644/test_2024-07-23/src/units/modbus.hpp b/software/nano-644/test_2024-07-23/src/units/modbus.hpp new file mode 100644 index 0000000..d0f6c48 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/modbus.hpp @@ -0,0 +1,23 @@ +#ifndef MODBUS_HPP +#define MODBUS_HPP + +#include +#include "../main.hpp" +#include + +class Modbus : public TestUnit { + public: + uint8_t enabled; + uint8_t receivedBytes; + uint8_t received[16]; + + + public: + Modbus () { enabled = 0; receivedBytes = 0; } + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Modbus"); } + void handleRxByte (uint8_t); +}; + +#endif \ No newline at end of file diff --git a/software/nano-644/test_2024-07-23/src/units/motor.cpp b/software/nano-644/test_2024-07-23/src/units/motor.cpp new file mode 100644 index 0000000..429d39a --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/motor.cpp @@ -0,0 +1,107 @@ +#include +#include +#include + +#include "motor.hpp" +#include "../main.hpp" + +void Motor::cleanup () { + ADMUX = 0; + ADCSRA = 0; + TCCR0A = 0; + TCCR0B = 0; + DDRB &= ~((1 << PB4) | (1 << PB3)); + PORTA &= ~(1 << PORTA3); + enabled = 0; +} + +int8_t Motor::run (uint8_t subtest) { + switch (subtest) { + case 0: { + printf_P(PSTR("init")); + ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V + ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128 + TCCR0A = (1 << COM0A1) | (1 << WGM01) | (1 << WGM00); // Fast PWM on OC0A + // TCCR0B = (1 << CS02) | ( 1 << CS00); // f = 12 MHz / 1024 = 11,71875 kHz -> fPWM=45Hz + TCCR0B = (1 << CS02); // f = 12 MHz / 256 = 46,875 kHz -> fPWM=183,1Hz + DDRB |= (1 << PB4) | (1 << PB3); // Motor enable + PORTA |= (1 << PORTA3); // push button for Motor enable control + enabled = 1; + return 0; + } + + case 1: { + printf_P(PSTR("\n")); + while (wait(10) == EOF) { + + ADCSRA |= (1 << ADSC); // start ADC + while (ADCSRA & (1 << ADSC)) {} // wait for result + printf_P(PSTR("\r => ADC0=%3d"), ADCH); + + ADMUX = (1 << ADLAR) | (1 << REFS1) | (1 << REFS0) | 2; // ADC2, VREF=2.5V + + uint8_t dutyCycle = 0xff - ADCH; + if (dutyCycle <= 1) { + dutyCycle = 0; + } else if (dutyCycle > 254) { + dutyCycle = 255; + } + OCR0A = dutyCycle; + printf_P(PSTR(" PWM/OC0A=%3d"), dutyCycle); + + ADCSRA |= (1 << ADSC); // start ADC + while (ADCSRA & (1 << ADSC)) {} // wait for result + printf_P(PSTR(" ADC2=%3d"), ADCH); + ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V + + if ((PINA & (1<< PA3)) == 0) { + PORTB &= ~(1 << PORTB4); + } else { + PORTB |= (1 << PORTB4); + } + + uint16_t timer; + ATOMIC_BLOCK(ATOMIC_FORCEON) { + timer = rpmTimer; + } + float rpm = 60.0 / (float)timer / 0.0001; + if (timer > 0) { + printf_P(PSTR(" n= %4d U/min"), (int)rpm); + } else { + printf_P(PSTR(" no rotation")); + } + + } + return 0; + } + + case 2: { + printf_P(PSTR("end")); + break; + } + } + + return -1; +} + +void Motor::tick100us () { + static uint16_t timerH = 0; + static uint16_t timerL = 0; + static uint8_t pinb = 0; + if ((PINB & 0x01) && PINB != pinb && timerL > 10) { + rpmTimer = timerL + timerH; + timerL = 0; + timerH = 0; + } + if (PINB & 0x01) { + timerH = timerH < 0x4000 ? timerH + 1 : 0x4000; + } else { + timerL = timerL < 0x4000 ? timerL + 1 : 0x4000; + } + if (timerH >= 0x4000 || timerL >= 0x4000) { + rpmTimer = 0; // no ratation detected + } + pinb = PINB; +} + + diff --git a/software/nano-644/test_2024-07-23/src/units/motor.hpp b/software/nano-644/test_2024-07-23/src/units/motor.hpp new file mode 100644 index 0000000..74623ca --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/motor.hpp @@ -0,0 +1,21 @@ +#ifndef MOTOR_HPP +#define MOTOR_HPP + +#include +#include "../main.hpp" +#include + +class Motor : public TestUnit { + public: + uint8_t enabled; + uint16_t rpmTimer; + + public: + Motor () { enabled = 0; rpmTimer = 0; }; + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Motor"); } + void tick100us (); +}; + +#endif \ No newline at end of file diff --git a/software/nano-644/test_2024-07-23/src/units/portexp.cpp b/software/nano-644/test_2024-07-23/src/units/portexp.cpp new file mode 100644 index 0000000..bcc2052 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/portexp.cpp @@ -0,0 +1,113 @@ +#include +#include +#include + +#include "portexp.hpp" +#include "../main.hpp" + +// Port-Expander MCP23S17 + +// SN-Print Stecker IO16 +// MCP23S17 | IO16 (MEGA2560) | Ampel-Print | | MCP23S17 | IO16 (MEGA2560) | Ampel-Print | +// ------------------------------------------ -------------------------------------------- +// GPA0 | IO16O7 (PA7) | Taster RU | | GPB0 | IO16U7 (PC7) | Taster LU | +// GPA1 | IO16O6 (PA6) | Taster RO | | GPB1 | IO16U6 (PC6) | Taster LO | +// GPA2 | IO16O5 (PA5) | U-Gruen | | GPB2 | IO16U5 (PC5) | R-Gruen | +// GPA3 | IO16O4 (PA4) | U-Gelb | | GPB3 | IO16U4 (PC4) | R-Gelb | +// GPA4 | IO16O3 (PA3) | U-Rot | | GPB4 | IO16U3 (PC3) | R-Rot | +// GPA5 | IO16O2 (PA2) | L-Gruen | | GPB5 | IO16U2 (PC2) | O-Gruen | +// GPA6 | IO16O1 (PA1) | L-Gelb | | GPB6 | IO16U1 (PC1) | O-Gelb | +// GPA7 | IO16O0 (PA0) | L-Rot | | GPB7 | IO16U0 (PC0) | O-Rot | + + +void PortExp::cleanup () { + DDRB &= ~(1 << PB6); // // SPI MISO (=PB6) + DDRB &= ~((1 << PB7) | (1 << PB5)); // SPI SCK (=PB7) and SPI MOSI (=PB5) + DDRA &= ~(1 << PA7); + PORTA &= ~(1 << PA7); // SPI nCS + SPCR = 0; + +} + +int8_t writeByte (uint8_t addr, uint8_t b) { + + PORTA &= ~(1 << PA7); // SPI nCS 1 -> 0 + + SPDR = 0x40; // WRITE BYTE + while ((SPSR & (1 << SPIF)) == 0) {} + if (SPDR != 0) { + printf_P(PSTR("E1")); + PORTA |= (1 << PA7); // SPI nCS 0 -> 1 + return -1; + } + + SPDR = addr; // register address + while ((SPSR & (1 << SPIF)) == 0) {} + if (SPDR != 0) { + printf_P(PSTR("E2")); + PORTA |= (1 << PA7); // SPI nCS 0 -> 1 + return -1; + } + + SPDR = b; // value + while ((SPSR & (1 << SPIF)) == 0) {} + if (SPDR != 0) { + printf_P(PSTR("E3")); + PORTA |= (1 << PA7); // SPI nCS 0 -> 1 + return -1; + } + + PORTA |= (1 << PA7); // SPI nCS 0 -> 1 + + _delay_us(5); + return 0; +} + +int8_t PortExp::run (uint8_t subtest) { + if (subtest == 0) { + PRR &= (1 << PRSPI); + PORTA |= (1 << PA7); + DDRA |= (1 << PA7); // SPI nCS + // PORTB/DDRB must be configured before SPCR !! + PORTB |= (1 << PB4); // nSS must be HIGH, otherwise SPI master will not become active!! + DDRB |= (1 << PB7) | (1 << PB5) | (1 << PB4); // SPI SCK (=PB7) and SPI MOSI (=PB5) + + // SPCR |= (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0); // SPI enable , Master, f=12MHz/128=93,75kHz + SPCR = (1 << SPE) | (1 << MSTR); // SPI enable , Master, f=12MHz/4 = 3MHz + printf_P(PSTR("init")); + + } else if (subtest == 1) { + while (wait(500) == EOF) { + printf_P(PSTR("\n => start ...")); + for (uint8_t i = 0; i < 8; i++) { + writeByte(0, ~(1 << i)); // IODIRA (Bank = 0) + // writeByte(0, 0x00); // IODIRA (Bank = 0) - all output + writeByte(0x12, (1 << i)); // GPIOA (Bank = 0) + printf_P(PSTR("\n Bank0 - GPA%d = 1"), i); + wait(200); + writeByte(0x12, 0); // GPIOA (Bank = 0) + printf_P(PSTR("\n Bank0 - GPA%d = 0"), i); + writeByte(0, 0xff); // IODIRA (Bank = 0) + wait(200); + } + for (uint8_t i = 0; i < 8; i++) { + writeByte(1, ~(1 << i)); // IODIRB (Bank = 0) + // writeByte(1, 0x00); // IODIRB (Bank = 0) - all output + writeByte(0x13, (1 << i)); // GPIOB (Bank = 0) + printf_P(PSTR("\n Bank0 - GPB%d = 1"), i); + wait(200); + writeByte(0x13, 0); // GPIOB (Bank = 0) + printf_P(PSTR("\n Bank0 - GPB%d = 0"), i); + writeByte(1, 0xff); // IODIRB (Bank = 0) + wait(200); + } + } + + } else { + printf_P(PSTR("end")); + return -1; + } + wait(500); + return 0; +} + diff --git a/software/nano-644/test_2024-07-23/src/units/portexp.hpp b/software/nano-644/test_2024-07-23/src/units/portexp.hpp new file mode 100644 index 0000000..cacea83 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/portexp.hpp @@ -0,0 +1,16 @@ +#ifndef PORTEXP_HPP +#define PORTEXP_HPP + +#include +#include "../main.hpp" +#include + +class PortExp : public TestUnit { + public: + PortExp () {}; + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("PortExp"); } +}; + +#endif \ No newline at end of file diff --git a/software/nano-644/test_2024-07-23/src/units/poti.cpp b/software/nano-644/test_2024-07-23/src/units/poti.cpp new file mode 100644 index 0000000..d316a0b --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/poti.cpp @@ -0,0 +1,41 @@ +#include +#include + +#include "poti.hpp" +#include "../main.hpp" + +void Poti::cleanup () { + ADMUX = 0; + ADCSRA = 0; +} + +int8_t Poti::run (uint8_t subtest) { + switch (subtest) { + case 0: { + printf_P(PSTR("init")); + ADMUX = (1 << REFS0); // ADC0, VREF=AVCC=3.3V + ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128 + return 0; + } + + case 1: { + printf_P(PSTR("\n")); + while (wait(10) == EOF) { + printf_P(PSTR("\r => Measure ADC0: ")); + ADCSRA |= (1 << ADSC); // start ADC + while (ADCSRA & (1 << ADSC)) {} // wait for result + printf_P(PSTR("%4d (0x%03x)"), ADC, ADC); + } + return 0; + } + + case 2: { + printf_P(PSTR("end")); + break; + } + } + + return -1; +} + + diff --git a/software/nano-644/test_2024-07-23/src/units/poti.hpp b/software/nano-644/test_2024-07-23/src/units/poti.hpp new file mode 100644 index 0000000..2ca46e3 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/poti.hpp @@ -0,0 +1,16 @@ +#ifndef POTI_HPP +#define POTI_PP + +#include +#include "../main.hpp" +#include + +class Poti : public TestUnit { + public: + Poti () {}; + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Poti"); } +}; + +#endif \ No newline at end of file diff --git a/software/nano-644/test_2024-07-23/src/units/r2r.cpp b/software/nano-644/test_2024-07-23/src/units/r2r.cpp new file mode 100644 index 0000000..60f9ab1 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/r2r.cpp @@ -0,0 +1,43 @@ +#include +#include + +#include "r2r.hpp" +#include "../main.hpp" + +void R2r::cleanup () { + ADMUX = 0; + ADCSRA = 0; +} + +int8_t R2r::run (uint8_t subtest) { + switch (subtest) { + case 0: { + printf_P(PSTR("init")); + ADMUX = (1 << REFS0) | 2; // ADC2, VREF=AVCC=3.3V + ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128 + return 0; + } + + case 1: { + printf_P(PSTR("\n")); + while (wait(10) == EOF) { + printf_P(PSTR("\r => Measure ADC2: ")); + ADCSRA |= (1 << ADSC); // start ADC + while (ADCSRA & (1 << ADSC)) {} // wait for result + printf_P(PSTR("%4d (0x%03x)"), ADC, ADC); + uint8_t sw = (ADC + 32) / 64; + printf_P(PSTR(" SW9:6 = %d %d% d %d "), sw >> 3, (sw >> 2) & 0x01, (sw >> 1) & 0x01, sw & 0x01 ); + } + return 0; + } + + case 2: { + printf_P(PSTR("end")); + break; + } + } + + return -1; +} + + diff --git a/software/nano-644/test_2024-07-23/src/units/r2r.hpp b/software/nano-644/test_2024-07-23/src/units/r2r.hpp new file mode 100644 index 0000000..6088ce5 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/r2r.hpp @@ -0,0 +1,16 @@ +#ifndef R2R_HPP +#define R2R_PP + +#include +#include "../main.hpp" +#include + +class R2r : public TestUnit { + public: + R2r () {}; + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("R2R"); } +}; + +#endif \ No newline at end of file diff --git a/software/nano-644/test_2024-07-23/src/units/rgb.cpp b/software/nano-644/test_2024-07-23/src/units/rgb.cpp new file mode 100644 index 0000000..6f08cb5 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/rgb.cpp @@ -0,0 +1,60 @@ +#include +#include + +#include "rgb.hpp" +#include "../main.hpp" + +void Rgb::cleanup () { + PORTB &= ~((1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0)); + DDRB &= ~((1 << DDB2) | (1 << DDB1) | (1 << DDB0)); +} + +int8_t Rgb::run (uint8_t subtest) { + switch (subtest) { + case 0: { + DDRB |= (1 << DDB2) | (1 << DDB1) | (1 << DDB0); + PORTB |= (1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0); // all OFF + printf_P(PSTR("init")); + return 0; + } + + case 1: { + PORTB &= ~(1 << PORTB0); // ON + printf_P(PSTR("Red")); + wait(2000); + return 0; + } + + case 2: { + PORTB |= (1 << PORTB0); + PORTB &= ~(1 << PORTB1); // ON + printf_P(PSTR("Green")); + wait(2000); + return 0; + } + + case 3: { + PORTB |= (1 << PORTB1); + PORTB &= ~(1 << PORTB2); // ON + printf_P(PSTR("Blue")); + wait(2000); + return 0; + } + + case 4: { + PORTB &= ~((1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0)); + printf_P(PSTR("All")); + wait(2000); + return 0; + } + + case 5: { + printf_P(PSTR("end")); + break; + } + } + + return -1; +} + + diff --git a/software/nano-644/test_2024-07-23/src/units/rgb.hpp b/software/nano-644/test_2024-07-23/src/units/rgb.hpp new file mode 100644 index 0000000..cb5641d --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/rgb.hpp @@ -0,0 +1,16 @@ +#ifndef RGB_HPP +#define RGB_PP + +#include +#include "../main.hpp" +#include + +class Rgb : public TestUnit { + public: + Rgb () {}; + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Rgb"); } +}; + +#endif \ No newline at end of file diff --git a/software/nano-644/test_2024-07-23/src/units/seg7.cpp b/software/nano-644/test_2024-07-23/src/units/seg7.cpp new file mode 100644 index 0000000..6ca477e --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/seg7.cpp @@ -0,0 +1,95 @@ +#include +#include + +#include "seg7.hpp" +#include "../main.hpp" + +// PA0 Cathode Char 1 +// PA1 Cathode Char 2 +// PA2 Cathode Char 3 +// PA3 Cathode Char 4 + +// PB0 Anode Segment A +// PB1 Anode Segment B +// PB2 Anode Segment C +// PB3 Anode Segment D +// PB4 Anode Segment E +// PB5 Anode Segment F +// PB6 Anode Segment G +// PB7 Anode DP + +// PD5 nOE (Output Enable) for all LEDs +// PD6 Anode L1:2 +// PD7 Anode L3 + +const char *segName[] = { "A", "B", "C", "D", "E", "F", "G", "DP" }; + +void Seg7::cleanup () { + PORTA &= ~((1 < +#include "../main.hpp" +#include + +class Seg7 : public TestUnit { + public: + Seg7 () {}; + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Seg7"); } +}; + +#endif \ No newline at end of file diff --git a/software/nano-644/test_2024-07-23/src/units/switch.cpp b/software/nano-644/test_2024-07-23/src/units/switch.cpp new file mode 100644 index 0000000..9f738e5 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/switch.cpp @@ -0,0 +1,51 @@ +#include +#include +#include + +#include "switch.hpp" +#include "../main.hpp" + +void Switch::cleanup () { + PORTA &= ~((1 << PORTA3) | (1 << PORTA2) | (1 << PORTA1) | (1 << PORTA0)); +} + + +int8_t Switch::run (uint8_t subtest) { + if (subtest == 0) { + DDRA &= ~((1 << DDA3) | (1 << DDA2) | (1 << DDA1) | (1 << DDA0)); + // enable internal pullup resistor + PORTA |= (1 << PORTA3) | (1 << PORTA2) | (1 << PORTA1) | (1 << PORTA0); + wait(10); + return 0; + + } else if (subtest <= 17) { + uint8_t bit = (subtest - 1) / 4; + switch ((subtest - 1) % 4) { + case 0: case 2: { + if ((PINA & (1 << bit)) == 0) { + printf_P(PSTR("Release SW%d (PA%d) "), bit + 1, bit); + while ((PINA & (1 << bit)) == 0 && wait(0) == EOF) {} + wait(10); + } + return 0; + } + + case 1: { + if ((PINA & (1 << bit)) != 0) { + printf_P(PSTR("Press SW%d (PA%d) "), bit + 1, bit); + while ((PINA & (1 << bit)) != 0 && wait(0) == EOF) {} + wait(10); + } + return 0; + } + + case 3: { + printf_P(PSTR("end")); + return 0; + } + } + + } + + return -1; +} diff --git a/software/nano-644/test_2024-07-23/src/units/switch.hpp b/software/nano-644/test_2024-07-23/src/units/switch.hpp new file mode 100644 index 0000000..a4caf2f --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/switch.hpp @@ -0,0 +1,16 @@ +#ifndef SWITCH_HPP +#define SWITCH_HPP + +#include +#include "../main.hpp" +#include + +class Switch : public TestUnit { + public: + Switch () {}; + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Switch"); } +}; + +#endif \ No newline at end of file diff --git a/software/nano-644/test_2024-07-23/src/units/uart1.cpp b/software/nano-644/test_2024-07-23/src/units/uart1.cpp new file mode 100644 index 0000000..02f411e --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/uart1.cpp @@ -0,0 +1,58 @@ +#include +#include +#include + +#include "uart1.hpp" +#include "../main.hpp" + +int uart1_putchar(char c, FILE *stream) { + if (c == '\n') { + uart1_putchar('\r', stream); + } + loop_until_bit_is_set(UCSR1A, UDRE1); + UDR1 = c; + return 0; +} + +static FILE mystderr = { 0, 0, _FDEV_SETUP_WRITE , 0, 0, uart1_putchar, NULL, 0 }; + +void Uart1::cleanup () { + enabled = 0; + UCSR1A = 0; + UCSR1B = 0; + UCSR1C = 0; + UBRR1H = 0; + UBRR1L = 0; + stderr = NULL; +} + +int8_t Uart1::run (uint8_t subtest) { + if (subtest == 0) { + // UART1 interface on Nano-644 + PORTD |= (1 << PD2); // enable RxD1 pullup + UCSR1A = (1 << U2X1); + UCSR1B = (1 << RXCIE1) | (1 << RXEN1) | (1 < send text via UART1 now...")); + fprintf_P(stderr, PSTR("Hello UART1, ECHO-Modus active\n")); + } while (wait(5000) == EOF); + + } else { + printf_P(PSTR("end")); + return -1; + } + wait(500); + return 0; +} + +void Uart1::handleRxByte (uint8_t b) { + uart1_putchar(b, stderr); +} diff --git a/software/nano-644/test_2024-07-23/src/units/uart1.hpp b/software/nano-644/test_2024-07-23/src/units/uart1.hpp new file mode 100644 index 0000000..16b6425 --- /dev/null +++ b/software/nano-644/test_2024-07-23/src/units/uart1.hpp @@ -0,0 +1,20 @@ +#ifndef UART1_HPP +#define UART1_HPP + +#include +#include "../main.hpp" +#include + +class Uart1 : public TestUnit { + public: + uint8_t enabled; + + public: + Uart1 () { enabled = 0; } + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Uart1"); } + void handleRxByte (uint8_t); +}; + +#endif \ No newline at end of file diff --git a/software/test_2024-07-23/.gdb_history b/software/test_2024-07-23/.gdb_history deleted file mode 100644 index 3339046..0000000 --- a/software/test_2024-07-23/.gdb_history +++ /dev/null @@ -1,9 +0,0 @@ -target remote :1234 -layout split -stepi -quit -target remote :1234 -layout split -stepi -b *main+9 -quit diff --git a/software/test_2024-07-23/.gdbinit b/software/test_2024-07-23/.gdbinit deleted file mode 100644 index 139597f..0000000 --- a/software/test_2024-07-23/.gdbinit +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/software/test_2024-07-23/.gitignore b/software/test_2024-07-23/.gitignore deleted file mode 100644 index a959910..0000000 --- a/software/test_2024-07-23/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -.depend -**/build -**/dist -**/sim diff --git a/software/test_2024-07-23/.vscode/c_cpp_properties.json b/software/test_2024-07-23/.vscode/c_cpp_properties.json deleted file mode 100644 index 3a57c79..0000000 --- a/software/test_2024-07-23/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "configurations": [ - { - "name": "Linux AVR", - "includePath": [ - "/usr/lib/avr/include/**", - "/usr/lib/gcc/avr/**" - ], - "defines": [], - "compilerPath": "/usr/bin/avr-gcc", - "compilerArgs": [ "-mmcu=atmega644p", "-DF_CPU=12000000", "-Os" ], - "cStandard": "gnu11", - "cppStandard": "gnu++11", - "intelliSenseMode": "linux-gcc-x64" - } - ], - "version": 4 -} diff --git a/software/test_2024-07-23/.vscode/launch.json b/software/test_2024-07-23/.vscode/launch.json deleted file mode 100644 index f29cf2e..0000000 --- a/software/test_2024-07-23/.vscode/launch.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Build", - // "request": "launch", - "type": "node-terminal", - "preLaunchTask": "build" - },{ - "name": "Flash", - // "request": "launch", - "type": "node-terminal", - "preLaunchTask": "flash" - },{ - "name": "Clean", - // "request": "launch", - "type": "node-terminal", - "preLaunchTask": "clean" - },{ - // es muss mit simuc --board arduino dist/programm.elf der Simulator - // gestartet werden. Dessen gdb-stub öffnet auf localhost:1234 einen Port - "name": "Debug (simuc)", - "type": "cppdbg", - "request": "launch", - "program": "${workspaceFolder}/sim/atmega328p.elf", - "cwd": "${workspaceFolder}", - "externalConsole": false, - "MIMode": "gdb", - "miDebuggerPath": "/usr/bin/avr-gdb", - "miDebuggerServerAddress": ":1234", - "preLaunchTask": "build" - } - ] -} diff --git a/software/test_2024-07-23/.vscode/settings.json b/software/test_2024-07-23/.vscode/settings.json deleted file mode 100644 index b2e94c9..0000000 --- a/software/test_2024-07-23/.vscode/settings.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "[c]": { - "editor.insertSpaces": true, - "editor.tabSize": 3, - "editor.detectIndentation": false - }, - "[cpp]": { - "editor.insertSpaces": true, - "editor.tabSize": 3, - "editor.detectIndentation": false - }, - "[h]": { - "editor.insertSpaces": true, - "editor.tabSize": 3, - "editor.detectIndentation": false - }, - "[hpp]": { - "editor.insertSpaces": true, - "editor.tabSize": 3, - "editor.detectIndentation": false - }, - "cSpell.words": [], - "cSpell.ignorePaths": [ - "**/*.json", "**/*.c", "**/*.h", "**/*.cpp", "**/*.hpp", "**/Makefile" - ] -} diff --git a/software/test_2024-07-23/.vscode/tasks.json b/software/test_2024-07-23/.vscode/tasks.json deleted file mode 100644 index 74fb1c7..0000000 --- a/software/test_2024-07-23/.vscode/tasks.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "2.0.0", - "tasks": [{ - "label": "build", - "type": "shell", - "command": "make", - "problemMatcher":[ - "$gcc" - ] - },{ - "label": "clean", - "type": "shell", - "command": "make", - "args": [ "clean" ], - },{ - "label": "flash", - "type": "shell", - "command": "make", - "args": [ "flash" ], - }] -} \ No newline at end of file diff --git a/software/test_2024-07-23/Makefile b/software/test_2024-07-23/Makefile deleted file mode 100644 index 12a996a..0000000 --- a/software/test_2024-07-23/Makefile +++ /dev/null @@ -1,82 +0,0 @@ -.PHONY: all info flash picocom clean -$(shell mkdir -p dist >/dev/null) -$(shell mkdir -p build >/dev/null) -$(shell mkdir -p sim >/dev/null) - -NAME="test_2024-07-23_nano-644" -SRC= $(wildcard src/*.c src/*.cpp src/*/*.cpp) -OBJ = $(SRC:src/%.c=build/%.o) -OBJ_SIM = $(SRC:src/%.c=sim/%.o) - -DEVICE=atmega644p - -CC= avr-g++ -CFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=12000000 -c -LFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=12000000 -Wl,-u,vfprintf -lprintf_flt -lm - -CFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=12000000 -g -c -LFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=12000000 -g -Wl,-u,vfprintf -lprintf_flt -lm - - -all: dist/$(NAME).elf dist/$(NAME).s dist/$(NAME).hex sim/$(NAME).elf sim/$(NAME).s info - -info: - @echo - @avr-size --mcu=$(DEVICE) --format=avr dist/$(NAME).elf - -.depend: $(SRC) - $(CC) -MM $(SRC) > .depend - --include $(DEPENDFILE) - -dist/$(NAME).elf: .depend $(OBJ) - $(CC) $(LFLAGS) -o $@ $(OBJ) - -dist/%.s: dist/%.elf - avr-objdump -d $< > $@ - -dist/%.hex: dist/%.elf - avr-objcopy -O ihex $(HEX_FLASH_FLAGS) $< $@ - -sim/$(NAME).elf: .depend $(OBJ_SIM) - $(CC) $(LFLAGS_SIM) -o $@ $(OBJ_SIM) - - -build/%.o: src/%.c - $(CC) $(CFLAGS) -o $@ $< - -sim/%.o: src/%.c - $(CC) $(CFLAGS_SIM) -o $@ $< - -sim/%.s: sim/%.elf - avr-objdump -d $< > $@ - -simuc: sim/$(NAME).elf - simuc --board arduino $< - -gdb: sim/$(NAME).elf - avr-gdb $< - -isp: - avrdude -c usbasp -p m644p - -isp-flash: dist/$(NAME).elf all - avrdude -c usbasp -p m644p -e -U flash:w:$< - -flash: dist/$(NAME).elf all - avrdude -c arduino -p m644p -P /dev/ttyUSB0 -b 115200 -e -U flash:w:$< - - -picocom: - # picocom sends CR for ENTER -> convert cr (\r) to lf (\n) - picocom -b 115200 --omap crlf /dev/ttyUSB0 - -isp-fuse: - avrdude -c usbasp -p m644p -U lfuse:w:0xFF:m -U hfuse:w:0xD9:m -U efuse:w:0xFF:m -U lock:w:0xFF:m - -clean: - @rm -r dist - @rm -r build - @rm -r sim - @find . -type f -name ".depend" -exec rm {} \; - @echo "clean done" diff --git a/software/test_2024-07-23/README.md b/software/test_2024-07-23/README.md deleted file mode 100644 index c4d5e14..0000000 --- a/software/test_2024-07-23/README.md +++ /dev/null @@ -1 +0,0 @@ -# Testprogramm diff --git a/software/test_2024-07-23/src/adafruit/bme280.cpp b/software/test_2024-07-23/src/adafruit/bme280.cpp deleted file mode 100644 index 95a0f49..0000000 --- a/software/test_2024-07-23/src/adafruit/bme280.cpp +++ /dev/null @@ -1,512 +0,0 @@ -#include "bme280.h" -#include -#include - -Adafruit_BME280 theBME280; -Adafruit_BME280_Temp bm280TempSensor; -Adafruit_BME280_Pressure bm280PressureSensor; -Adafruit_BME280_Humidity bm280HumiditySensor; - -Adafruit_BME280::Adafruit_BME280() { - static I2cMaster i2cDevice; - t_fine_adjust = 0; - temp_sensor = &bm280TempSensor; - pressure_sensor = &bm280PressureSensor; - humidity_sensor = &bm280HumiditySensor; - i2c_dev = &i2cDevice; -} - -bool Adafruit_BME280::begin (uint8_t addr) { - if (!i2c_dev->begin(addr)) { - return false; - } - return init(); -} - -bool Adafruit_BME280::init() { - _sensorID = read8(BME280_REGISTER_CHIPID); - if (_sensorID != 0x60) { - return false; - } - write8(BME280_REGISTER_SOFTRESET, 0xB6); - _delay_ms(10); // wait for chip to wake up. - - // if chip is still reading calibration, delay - while (isReadingCalibration()) { - _delay_ms(10); - } - - readCoefficients(); // read trimming parameters, see DS 4.2.2 - setSampling(); // use defaults - _delay_ms(100); - - return true; -} - -/*! - * @brief setup sensor with given parameters / settings - * - * This is simply a overload to the normal begin()-function, so SPI users - * don't get confused about the library requiring an address. - * @param mode the power mode to use for the sensor - * @param tempSampling the temp samping rate to use - * @param pressSampling the pressure sampling rate to use - * @param humSampling the humidity sampling rate to use - * @param filter the filter mode to use - * @param duration the standby duration to use - */ -void Adafruit_BME280::setSampling(sensor_mode mode, - sensor_sampling tempSampling, - sensor_sampling pressSampling, - sensor_sampling humSampling, - sensor_filter filter, - standby_duration duration) { - _measReg.mode = mode; - _measReg.osrs_t = tempSampling; - _measReg.osrs_p = pressSampling; - - _humReg.osrs_h = humSampling; - _configReg.filter = filter; - _configReg.t_sb = duration; - _configReg.spi3w_en = 0; - - // making sure sensor is in sleep mode before setting configuration - // as it otherwise may be ignored - write8(BME280_REGISTER_CONTROL, MODE_SLEEP); - - // you must make sure to also set REGISTER_CONTROL after setting the - // CONTROLHUMID register, otherwise the values won't be applied (see - // DS 5.4.3) - write8(BME280_REGISTER_CONTROLHUMID, _humReg.get()); - write8(BME280_REGISTER_CONFIG, _configReg.get()); - write8(BME280_REGISTER_CONTROL, _measReg.get()); -} - -/*! - * @brief Writes an 8 bit value over I2C or SPI - * @param reg the register address to write to - * @param value the value to write to the register - */ -void Adafruit_BME280::write8(byte reg, byte value) { - byte buffer[2]; - buffer[1] = value; - if (i2c_dev) { - buffer[0] = reg; - i2c_dev->write(buffer, 2); - } -} - -/*! - * @brief Reads an 8 bit value over I2C or SPI - * @param reg the register address to read from - * @returns the data byte read from the device - */ -uint8_t Adafruit_BME280::read8(byte reg) { - uint8_t buffer[1]; - if (i2c_dev) { - buffer[0] = uint8_t(reg); - i2c_dev->write_then_read(buffer, 1, buffer, 1); - } - return buffer[0]; -} - -/*! - * @brief Reads a 16 bit value over I2C or SPI - * @param reg the register address to read from - * @returns the 16 bit data value read from the device - */ -uint16_t Adafruit_BME280::read16(byte reg) { - uint8_t buffer[2]; - - if (i2c_dev) { - buffer[0] = uint8_t(reg); - i2c_dev->write_then_read(buffer, 1, buffer, 2); - } - return uint16_t(buffer[0]) << 8 | uint16_t(buffer[1]); -} - -/*! - * @brief Reads a signed 16 bit little endian value over I2C or SPI - * @param reg the register address to read from - * @returns the 16 bit data value read from the device - */ -uint16_t Adafruit_BME280::read16_LE(byte reg) { - uint16_t temp = read16(reg); - return (temp >> 8) | (temp << 8); -} - -/*! - * @brief Reads a signed 16 bit value over I2C or SPI - * @param reg the register address to read from - * @returns the 16 bit data value read from the device - */ -int16_t Adafruit_BME280::readS16(byte reg) { return (int16_t)read16(reg); } - -/*! - * @brief Reads a signed little endian 16 bit value over I2C or SPI - * @param reg the register address to read from - * @returns the 16 bit data value read from the device - */ -int16_t Adafruit_BME280::readS16_LE(byte reg) { - return (int16_t)read16_LE(reg); -} - -/*! - * @brief Reads a 24 bit value over I2C - * @param reg the register address to read from - * @returns the 24 bit data value read from the device - */ -uint32_t Adafruit_BME280::read24(byte reg) { - uint8_t buffer[3]; - - if (i2c_dev) { - buffer[0] = uint8_t(reg); - i2c_dev->write_then_read(buffer, 1, buffer, 3); - } - return uint32_t(buffer[0]) << 16 | uint32_t(buffer[1]) << 8 | - uint32_t(buffer[2]); -} - -/*! - * @brief Take a new measurement (only possible in forced mode) - @returns true in case of success else false - */ -bool Adafruit_BME280::takeForcedMeasurement(void) { - bool return_value = false; - // If we are in forced mode, the BME sensor goes back to sleep after each - // measurement and we need to set it to forced mode once at this point, so - // it will take the next measurement and then return to sleep again. - // In normal mode simply does new measurements periodically. - if (_measReg.mode == MODE_FORCED) { - return_value = true; - // set to forced mode, i.e. "take next measurement" - write8(BME280_REGISTER_CONTROL, _measReg.get()); - // Store current time to measure the timeout - uint32_t timeout_start = millis(); - // wait until measurement has been completed, otherwise we would read the - // the values from the last measurement or the timeout occurred after 2 sec. - while (read8(BME280_REGISTER_STATUS) & 0x08) { - // In case of a timeout, stop the while loop - if ((millis() - timeout_start) > 2000) { - return_value = false; - break; - } - _delay_ms(1); - } - } - return return_value; -} - -/*! - * @brief Reads the factory-set coefficients - */ -void Adafruit_BME280::readCoefficients(void) { - _bme280_calib.dig_T1 = read16_LE(BME280_REGISTER_DIG_T1); - _bme280_calib.dig_T2 = readS16_LE(BME280_REGISTER_DIG_T2); - _bme280_calib.dig_T3 = readS16_LE(BME280_REGISTER_DIG_T3); - - _bme280_calib.dig_P1 = read16_LE(BME280_REGISTER_DIG_P1); - _bme280_calib.dig_P2 = readS16_LE(BME280_REGISTER_DIG_P2); - _bme280_calib.dig_P3 = readS16_LE(BME280_REGISTER_DIG_P3); - _bme280_calib.dig_P4 = readS16_LE(BME280_REGISTER_DIG_P4); - _bme280_calib.dig_P5 = readS16_LE(BME280_REGISTER_DIG_P5); - _bme280_calib.dig_P6 = readS16_LE(BME280_REGISTER_DIG_P6); - _bme280_calib.dig_P7 = readS16_LE(BME280_REGISTER_DIG_P7); - _bme280_calib.dig_P8 = readS16_LE(BME280_REGISTER_DIG_P8); - _bme280_calib.dig_P9 = readS16_LE(BME280_REGISTER_DIG_P9); - - _bme280_calib.dig_H1 = read8(BME280_REGISTER_DIG_H1); - _bme280_calib.dig_H2 = readS16_LE(BME280_REGISTER_DIG_H2); - _bme280_calib.dig_H3 = read8(BME280_REGISTER_DIG_H3); - _bme280_calib.dig_H4 = ((int8_t)read8(BME280_REGISTER_DIG_H4) << 4) | - (read8(BME280_REGISTER_DIG_H4 + 1) & 0xF); - _bme280_calib.dig_H5 = ((int8_t)read8(BME280_REGISTER_DIG_H5 + 1) << 4) | - (read8(BME280_REGISTER_DIG_H5) >> 4); - _bme280_calib.dig_H6 = (int8_t)read8(BME280_REGISTER_DIG_H6); -} - -/*! - * @brief return true if chip is busy reading cal data - * @returns true if reading calibration, false otherwise - */ -bool Adafruit_BME280::isReadingCalibration(void) { - uint8_t const rStatus = read8(BME280_REGISTER_STATUS); - - return (rStatus & (1 << 0)) != 0; -} - -/*! - * @brief Returns the temperature from the sensor - * @returns the temperature read from the device - */ -float Adafruit_BME280::readTemperature(void) { - int32_t var1, var2; - - int32_t adc_T = read24(BME280_REGISTER_TEMPDATA); - if (adc_T == 0x800000) // value in case temp measurement was disabled - return NAN; - adc_T >>= 4; - - var1 = (int32_t)((adc_T / 8) - ((int32_t)_bme280_calib.dig_T1 * 2)); - var1 = (var1 * ((int32_t)_bme280_calib.dig_T2)) / 2048; - var2 = (int32_t)((adc_T / 16) - ((int32_t)_bme280_calib.dig_T1)); - var2 = (((var2 * var2) / 4096) * ((int32_t)_bme280_calib.dig_T3)) / 16384; - - t_fine = var1 + var2 + t_fine_adjust; - - int32_t T = (t_fine * 5 + 128) / 256; - - return (float)T / 100; -} - -/*! - * @brief Returns the pressure from the sensor - * @returns the pressure value (in Pascal) read from the device - */ -float Adafruit_BME280::readPressure(void) { - int64_t var1, var2, var3, var4; - - readTemperature(); // must be done first to get t_fine - - int32_t adc_P = read24(BME280_REGISTER_PRESSUREDATA); - if (adc_P == 0x800000) // value in case pressure measurement was disabled - return NAN; - adc_P >>= 4; - - var1 = ((int64_t)t_fine) - 128000; - var2 = var1 * var1 * (int64_t)_bme280_calib.dig_P6; - var2 = var2 + ((var1 * (int64_t)_bme280_calib.dig_P5) * 131072); - var2 = var2 + (((int64_t)_bme280_calib.dig_P4) * 34359738368); - var1 = ((var1 * var1 * (int64_t)_bme280_calib.dig_P3) / 256) + - ((var1 * ((int64_t)_bme280_calib.dig_P2) * 4096)); - var3 = ((int64_t)1) * 140737488355328; - var1 = (var3 + var1) * ((int64_t)_bme280_calib.dig_P1) / 8589934592; - - if (var1 == 0) { - return 0; // avoid exception caused by division by zero - } - - var4 = 1048576 - adc_P; - var4 = (((var4 * 2147483648UL) - var2) * 3125) / var1; - var1 = (((int64_t)_bme280_calib.dig_P9) * (var4 / 8192) * (var4 / 8192)) / - 33554432; - var2 = (((int64_t)_bme280_calib.dig_P8) * var4) / 524288; - var4 = ((var4 + var1 + var2) / 256) + (((int64_t)_bme280_calib.dig_P7) * 16); - - float P = var4 / 256.0; - - return P; -} - -/*! - * @brief Returns the humidity from the sensor - * @returns the humidity value read from the device - */ -float Adafruit_BME280::readHumidity(void) { - int32_t var1, var2, var3, var4, var5; - - readTemperature(); // must be done first to get t_fine - - int32_t adc_H = read16(BME280_REGISTER_HUMIDDATA); - if (adc_H == 0x8000) // value in case humidity measurement was disabled - return NAN; - - var1 = t_fine - ((int32_t)76800); - var2 = (int32_t)(adc_H * 16384); - var3 = (int32_t)(((int32_t)_bme280_calib.dig_H4) * 1048576); - var4 = ((int32_t)_bme280_calib.dig_H5) * var1; - var5 = (((var2 - var3) - var4) + (int32_t)16384) / 32768; - var2 = (var1 * ((int32_t)_bme280_calib.dig_H6)) / 1024; - var3 = (var1 * ((int32_t)_bme280_calib.dig_H3)) / 2048; - var4 = ((var2 * (var3 + (int32_t)32768)) / 1024) + (int32_t)2097152; - var2 = ((var4 * ((int32_t)_bme280_calib.dig_H2)) + 8192) / 16384; - var3 = var5 * var2; - var4 = ((var3 / 32768) * (var3 / 32768)) / 128; - var5 = var3 - ((var4 * ((int32_t)_bme280_calib.dig_H1)) / 16); - var5 = (var5 < 0 ? 0 : var5); - var5 = (var5 > 419430400 ? 419430400 : var5); - uint32_t H = (uint32_t)(var5 / 4096); - - return (float)H / 1024.0; -} - -/*! - * Calculates the altitude (in meters) from the specified atmospheric - * pressure (in hPa), and sea-level pressure (in hPa). - * @param seaLevel Sea-level pressure in hPa - * @returns the altitude value read from the device - */ -float Adafruit_BME280::readAltitude(float seaLevel) { - // Equation taken from BMP180 datasheet (page 16): - // http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf - - // Note that using the equation from wikipedia can give bad results - // at high altitude. See this thread for more information: - // http://forums.adafruit.com/viewtopic.php?f=22&t=58064 - - float atmospheric = readPressure() / 100.0F; - return 44330.0 * (1.0 - pow(atmospheric / seaLevel, 0.1903)); -} - -/*! - * Calculates the pressure at sea level (in hPa) from the specified - * altitude (in meters), and atmospheric pressure (in hPa). - * @param altitude Altitude in meters - * @param atmospheric Atmospheric pressure in hPa - * @returns the pressure at sea level (in hPa) from the specified altitude - */ -float Adafruit_BME280::seaLevelForAltitude(float altitude, float atmospheric) { - // Equation taken from BMP180 datasheet (page 17): - // http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf - - // Note that using the equation from wikipedia can give bad results - // at high altitude. See this thread for more information: - // http://forums.adafruit.com/viewtopic.php?f=22&t=58064 - - return atmospheric / pow(1.0 - (altitude / 44330.0), 5.255); -} - -/*! - * Returns Sensor ID found by init() for diagnostics - * @returns Sensor ID 0x60 for BME280, 0x56, 0x57, 0x58 BMP280 - */ -uint32_t Adafruit_BME280::sensorID(void) { return _sensorID; } - -/*! - * Returns the current temperature compensation value in degrees Celsius - * @returns the current temperature compensation value in degrees Celsius - */ -float Adafruit_BME280::getTemperatureCompensation(void) { - return float((t_fine_adjust * 5) >> 8) / 100.0; -}; - -/*! - * Sets a value to be added to each temperature reading. This adjusted - * temperature is used in pressure and humidity readings. - * @param adjustment Value to be added to each temperature reading in Celsius - */ -void Adafruit_BME280::setTemperatureCompensation(float adjustment) { - // convert the value in C into and adjustment to t_fine - t_fine_adjust = ((int32_t(adjustment * 100) << 8)) / 5; -}; - - -/**************************************************************************/ -/*! - @brief Gets the sensor_t data for the BME280's temperature sensor -*/ -/**************************************************************************/ -void Adafruit_BME280_Temp::getSensor(sensor_t *sensor) { - /* Clear the sensor_t object */ - memset(sensor, 0, sizeof(sensor_t)); - - /* Insert the sensor name in the fixed length char array */ - strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1); - sensor->name[sizeof(sensor->name) - 1] = 0; - sensor->version = 1; - sensor->sensor_id = _sensorID; - sensor->type = SENSOR_TYPE_AMBIENT_TEMPERATURE; - sensor->min_delay = 0; - sensor->min_value = -40.0; /* Temperature range -40 ~ +85 C */ - sensor->max_value = +85.0; - sensor->resolution = 0.01; /* 0.01 C */ -} - -/**************************************************************************/ -/*! - @brief Gets the temperature as a standard sensor event - @param event Sensor event object that will be populated - @returns True -*/ -/**************************************************************************/ -bool Adafruit_BME280_Temp::getEvent(sensors_event_t *event) { - /* Clear the event */ - memset(event, 0, sizeof(sensors_event_t)); - - event->version = sizeof(sensors_event_t); - event->sensor_id = _sensorID; - event->type = SENSOR_TYPE_AMBIENT_TEMPERATURE; - event->timestamp = millis(); - event->temperature = theBME280.readTemperature(); - return true; -} - -/**************************************************************************/ -/*! - @brief Gets the sensor_t data for the BME280's pressure sensor -*/ -/**************************************************************************/ -void Adafruit_BME280_Pressure::getSensor(sensor_t *sensor) { - /* Clear the sensor_t object */ - memset(sensor, 0, sizeof(sensor_t)); - - /* Insert the sensor name in the fixed length char array */ - strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1); - sensor->name[sizeof(sensor->name) - 1] = 0; - sensor->version = 1; - sensor->sensor_id = _sensorID; - sensor->type = SENSOR_TYPE_PRESSURE; - sensor->min_delay = 0; - sensor->min_value = 300.0; /* 300 ~ 1100 hPa */ - sensor->max_value = 1100.0; - sensor->resolution = 0.012; /* 0.12 hPa relative */ -} - -/**************************************************************************/ -/*! - @brief Gets the pressure as a standard sensor event - @param event Sensor event object that will be populated - @returns True -*/ -/**************************************************************************/ -bool Adafruit_BME280_Pressure::getEvent(sensors_event_t *event) { - /* Clear the event */ - memset(event, 0, sizeof(sensors_event_t)); - - event->version = sizeof(sensors_event_t); - event->sensor_id = _sensorID; - event->type = SENSOR_TYPE_PRESSURE; - event->timestamp = millis(); - event->pressure = theBME280.readPressure() / 100; // convert Pa to hPa - return true; -} - -/**************************************************************************/ -/*! - @brief Gets the sensor_t data for the BME280's humidity sensor -*/ -/**************************************************************************/ -void Adafruit_BME280_Humidity::getSensor(sensor_t *sensor) { - /* Clear the sensor_t object */ - memset(sensor, 0, sizeof(sensor_t)); - - /* Insert the sensor name in the fixed length char array */ - strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1); - sensor->name[sizeof(sensor->name) - 1] = 0; - sensor->version = 1; - sensor->sensor_id = _sensorID; - sensor->type = SENSOR_TYPE_RELATIVE_HUMIDITY; - sensor->min_delay = 0; - sensor->min_value = 0; - sensor->max_value = 100; /* 0 - 100 % */ - sensor->resolution = 3; /* 3% accuracy */ -} - -/**************************************************************************/ -/*! - @brief Gets the humidity as a standard sensor event - @param event Sensor event object that will be populated - @returns True -*/ -/**************************************************************************/ -bool Adafruit_BME280_Humidity::getEvent(sensors_event_t *event) { - /* Clear the event */ - memset(event, 0, sizeof(sensors_event_t)); - - event->version = sizeof(sensors_event_t); - event->sensor_id = _sensorID; - event->type = SENSOR_TYPE_RELATIVE_HUMIDITY; - event->timestamp = millis(); - event->relative_humidity = theBME280.readHumidity(); - return true; -} diff --git a/software/test_2024-07-23/src/adafruit/bme280.h b/software/test_2024-07-23/src/adafruit/bme280.h deleted file mode 100644 index b842782..0000000 --- a/software/test_2024-07-23/src/adafruit/bme280.h +++ /dev/null @@ -1,373 +0,0 @@ -// https://github.com/adafruit/Adafruit_BME280_Library - -/*! - * @file Adafruit_BME280.h - * - * Designed specifically to work with the Adafruit BME280 Breakout - * ----> http://www.adafruit.com/products/2650 - * - * These sensors use I2C or SPI to communicate, 2 or 4 pins are required - * to interface. - * - * Adafruit invests time and resources providing this open source code, - * please support Adafruit and open-source hardware by purchasing - * products from Adafruit! - * - * Written by Kevin "KTOWN" Townsend for Adafruit Industries. - * - * BSD license, all text here must be included in any redistribution. - * See the LICENSE file for details. - * - */ - -#ifndef __BME280_H__ -#define __BME280_H__ - -// #include "Arduino.h" - -// #include -// #include -// #include - - -#include "../i2cmaster.hpp" -#include "../main.hpp" -#define byte uint8_t - - - -#include -#include -#include "sensor.h" - -/*! - * @brief default I2C address - */ -#define BME280_ADDRESS (0x77) // Primary I2C Address - /*! - * @brief alternate I2C address - */ -#define BME280_ADDRESS_ALTERNATE (0x76) // Alternate Address - -/*! - * @brief Register addresses - */ -enum { - BME280_REGISTER_DIG_T1 = 0x88, - BME280_REGISTER_DIG_T2 = 0x8A, - BME280_REGISTER_DIG_T3 = 0x8C, - - BME280_REGISTER_DIG_P1 = 0x8E, - BME280_REGISTER_DIG_P2 = 0x90, - BME280_REGISTER_DIG_P3 = 0x92, - BME280_REGISTER_DIG_P4 = 0x94, - BME280_REGISTER_DIG_P5 = 0x96, - BME280_REGISTER_DIG_P6 = 0x98, - BME280_REGISTER_DIG_P7 = 0x9A, - BME280_REGISTER_DIG_P8 = 0x9C, - BME280_REGISTER_DIG_P9 = 0x9E, - - BME280_REGISTER_DIG_H1 = 0xA1, - BME280_REGISTER_DIG_H2 = 0xE1, - BME280_REGISTER_DIG_H3 = 0xE3, - BME280_REGISTER_DIG_H4 = 0xE4, - BME280_REGISTER_DIG_H5 = 0xE5, - BME280_REGISTER_DIG_H6 = 0xE7, - - BME280_REGISTER_CHIPID = 0xD0, - BME280_REGISTER_VERSION = 0xD1, - BME280_REGISTER_SOFTRESET = 0xE0, - - BME280_REGISTER_CAL26 = 0xE1, // R calibration stored in 0xE1-0xF0 - - BME280_REGISTER_CONTROLHUMID = 0xF2, - BME280_REGISTER_STATUS = 0XF3, - BME280_REGISTER_CONTROL = 0xF4, - BME280_REGISTER_CONFIG = 0xF5, - BME280_REGISTER_PRESSUREDATA = 0xF7, - BME280_REGISTER_TEMPDATA = 0xFA, - BME280_REGISTER_HUMIDDATA = 0xFD -}; - -/**************************************************************************/ -/*! - @brief calibration data -*/ -/**************************************************************************/ -typedef struct { - uint16_t dig_T1; ///< temperature compensation value - int16_t dig_T2; ///< temperature compensation value - int16_t dig_T3; ///< temperature compensation value - - uint16_t dig_P1; ///< pressure compensation value - int16_t dig_P2; ///< pressure compensation value - int16_t dig_P3; ///< pressure compensation value - int16_t dig_P4; ///< pressure compensation value - int16_t dig_P5; ///< pressure compensation value - int16_t dig_P6; ///< pressure compensation value - int16_t dig_P7; ///< pressure compensation value - int16_t dig_P8; ///< pressure compensation value - int16_t dig_P9; ///< pressure compensation value - - uint8_t dig_H1; ///< humidity compensation value - int16_t dig_H2; ///< humidity compensation value - uint8_t dig_H3; ///< humidity compensation value - int16_t dig_H4; ///< humidity compensation value - int16_t dig_H5; ///< humidity compensation value - int8_t dig_H6; ///< humidity compensation value -} bme280_calib_data; -/*=========================================================================*/ - -class Adafruit_BME280; - -/** Adafruit Unified Sensor interface for temperature component of BME280 */ -class Adafruit_BME280_Temp : public Adafruit_Sensor { -public: - /** @brief Create an Adafruit_Sensor compatible object for the temp sensor - @param parent A pointer to the BME280 class */ - Adafruit_BME280_Temp() { _sensorID = 280; } - bool getEvent(sensors_event_t *); - void getSensor(sensor_t *); - -private: - int _sensorID; -}; - -/** Adafruit Unified Sensor interface for pressure component of BME280 */ -class Adafruit_BME280_Pressure : public Adafruit_Sensor { -public: - /** @brief Create an Adafruit_Sensor compatible object for the pressure sensor - @param parent A pointer to the BME280 class */ - Adafruit_BME280_Pressure() { _sensorID = 280; } - bool getEvent(sensors_event_t *); - void getSensor(sensor_t *); - -private: - int _sensorID; -}; - -/** Adafruit Unified Sensor interface for humidity component of BME280 */ -class Adafruit_BME280_Humidity : public Adafruit_Sensor { -public: - /** @brief Create an Adafruit_Sensor compatible object for the humidity sensor - @param parent A pointer to the BME280 class */ - Adafruit_BME280_Humidity() { _sensorID = 280;} - bool getEvent(sensors_event_t *); - void getSensor(sensor_t *); - -private: - int _sensorID; -}; - -/**************************************************************************/ -/*! - @brief Class that stores state and functions for interacting with BME280 IC -*/ -/**************************************************************************/ -class Adafruit_BME280 { -public: - /**************************************************************************/ - /*! - @brief sampling rates - */ - /**************************************************************************/ - enum sensor_sampling { - SAMPLING_NONE = 0b000, - SAMPLING_X1 = 0b001, - SAMPLING_X2 = 0b010, - SAMPLING_X4 = 0b011, - SAMPLING_X8 = 0b100, - SAMPLING_X16 = 0b101 - }; - - /**************************************************************************/ - /*! - @brief power modes - */ - /**************************************************************************/ - enum sensor_mode { - MODE_SLEEP = 0b00, - MODE_FORCED = 0b01, - MODE_NORMAL = 0b11 - }; - - /**************************************************************************/ - /*! - @brief filter values - */ - /**************************************************************************/ - enum sensor_filter { - FILTER_OFF = 0b000, - FILTER_X2 = 0b001, - FILTER_X4 = 0b010, - FILTER_X8 = 0b011, - FILTER_X16 = 0b100 - }; - - /**************************************************************************/ - /*! - @brief standby duration in ms - */ - /**************************************************************************/ - enum standby_duration { - STANDBY_MS_0_5 = 0b000, - STANDBY_MS_10 = 0b110, - STANDBY_MS_20 = 0b111, - STANDBY_MS_62_5 = 0b001, - STANDBY_MS_125 = 0b010, - STANDBY_MS_250 = 0b011, - STANDBY_MS_500 = 0b100, - STANDBY_MS_1000 = 0b101 - }; - - // constructors - Adafruit_BME280(); - - bool begin(uint8_t addr = BME280_ADDRESS); - bool init(); - - void setSampling(sensor_mode mode = MODE_NORMAL, - sensor_sampling tempSampling = SAMPLING_X16, - sensor_sampling pressSampling = SAMPLING_X16, - sensor_sampling humSampling = SAMPLING_X16, - sensor_filter filter = FILTER_OFF, - standby_duration duration = STANDBY_MS_0_5); - - bool takeForcedMeasurement(void); - float readTemperature(void); - float readPressure(void); - float readHumidity(void); - - float readAltitude(float seaLevel); - float seaLevelForAltitude(float altitude, float pressure); - uint32_t sensorID(void); - - float getTemperatureCompensation(void); - void setTemperatureCompensation(float); - -protected: - I2cMaster *i2c_dev = NULL; ///< Pointer to I2C bus interface - // Adafruit_SPIDevice *spi_dev = NULL; ///< Pointer to SPI bus interface - - Adafruit_BME280_Temp *temp_sensor; - Adafruit_BME280_Pressure *pressure_sensor; - Adafruit_BME280_Humidity *humidity_sensor; - - void readCoefficients(void); - bool isReadingCalibration(void); - - void write8(byte reg, byte value); - uint8_t read8(byte reg); - uint16_t read16(byte reg); - uint32_t read24(byte reg); - int16_t readS16(byte reg); - uint16_t read16_LE(byte reg); // little endian - int16_t readS16_LE(byte reg); // little endian - - uint8_t _i2caddr; //!< I2C addr for the TwoWire interface - int32_t _sensorID; //!< ID of the BME Sensor - int32_t t_fine; //!< temperature with high resolution, stored as an attribute - //!< as this is used for temperature compensation reading - //!< humidity and pressure - - int32_t t_fine_adjust; //!< add to compensate temp readings and in turn - //!< to pressure and humidity readings - - bme280_calib_data _bme280_calib; //!< here calibration data is stored - - /**************************************************************************/ - /*! - @brief config register - */ - /**************************************************************************/ - struct config { - // inactive duration (standby time) in normal mode - // 000 = 0.5 ms - // 001 = 62.5 ms - // 010 = 125 ms - // 011 = 250 ms - // 100 = 500 ms - // 101 = 1000 ms - // 110 = 10 ms - // 111 = 20 ms - unsigned int t_sb : 3; ///< inactive duration (standby time) in normal mode - - // filter settings - // 000 = filter off - // 001 = 2x filter - // 010 = 4x filter - // 011 = 8x filter - // 100 and above = 16x filter - unsigned int filter : 3; ///< filter settings - - // unused - don't set - unsigned int none : 1; ///< unused - don't set - unsigned int spi3w_en : 1; ///< unused - don't set - - /// @return combined config register - unsigned int get() { return (t_sb << 5) | (filter << 2) | spi3w_en; } - }; - config _configReg; //!< config register object - - /**************************************************************************/ - /*! - @brief ctrl_meas register - */ - /**************************************************************************/ - struct ctrl_meas { - // temperature oversampling - // 000 = skipped - // 001 = x1 - // 010 = x2 - // 011 = x4 - // 100 = x8 - // 101 and above = x16 - unsigned int osrs_t : 3; ///< temperature oversampling - - // pressure oversampling - // 000 = skipped - // 001 = x1 - // 010 = x2 - // 011 = x4 - // 100 = x8 - // 101 and above = x16 - unsigned int osrs_p : 3; ///< pressure oversampling - - // device mode - // 00 = sleep - // 01 or 10 = forced - // 11 = normal - unsigned int mode : 2; ///< device mode - - /// @return combined ctrl register - unsigned int get() { return (osrs_t << 5) | (osrs_p << 2) | mode; } - }; - ctrl_meas _measReg; //!< measurement register object - - /**************************************************************************/ - /*! - @brief ctrl_hum register - */ - /**************************************************************************/ - struct ctrl_hum { - /// unused - don't set - unsigned int none : 5; - - // pressure oversampling - // 000 = skipped - // 001 = x1 - // 010 = x2 - // 011 = x4 - // 100 = x8 - // 101 and above = x16 - unsigned int osrs_h : 3; ///< pressure oversampling - - /// @return combined ctrl hum register - unsigned int get() { return (osrs_h); } - }; - ctrl_hum _humReg; //!< hum register object -}; - -extern Adafruit_BME280 theBME280; - -#endif diff --git a/software/test_2024-07-23/src/adafruit/ens160.cpp b/software/test_2024-07-23/src/adafruit/ens160.cpp deleted file mode 100644 index ef23404..0000000 --- a/software/test_2024-07-23/src/adafruit/ens160.cpp +++ /dev/null @@ -1,374 +0,0 @@ -/* - ScioSense_ENS160.h - Library for the ENS160 sensor with I2C interface from ScioSense - 2023 Mar 23 v6 Christoph Friese Bugfix measurement routine, prepare next release - 2021 Nov 25 v5 Martin Herold Custom mode timing fixed - 2021 Feb 04 v4 Giuseppe de Pinto Custom mode fixed - 2020 Apr 06 v3 Christoph Friese Changed nomenclature to ScioSense as product shifted from ams - 2020 Feb 15 v2 Giuseppe Pasetti Corrected firmware flash option - 2019 May 05 v1 Christoph Friese Created - based on application note "ENS160 Software Integration.pdf" rev 0.01 -*/ - -#include "ens160.h" -#include "math.h" -#include -#include -#include - -ScioSense_ENS160::ScioSense_ENS160 () { - _revENS16x = 0; - - //Isotherm, HP0 252°C / HP1 350°C / HP2 250°C / HP3 324°C / measure every 1008ms - _seq_steps[0][0] = 0x7c; - _seq_steps[0][1] = 0x0a; - _seq_steps[0][2] = 0x7e; - _seq_steps[0][3] = 0xaf; - _seq_steps[0][4] = 0xaf; - _seq_steps[0][5] = 0xa2; - _seq_steps[0][6] = 0x00; - _seq_steps[0][7] = 0x80; -} - -bool ScioSense_ENS160::begin () { - i2cDevice.begin(ENS160_I2CADDR_1); - _delay_ms(ENS160_BOOTING); - if (reset()) { - if (checkPartID()) { - if (setMode(ENS160_OPMODE_IDLE)) { - if (clearCommand()) { - if (getFirmware()) { - return true; - } - } - } - } - } - return false; - -} - -bool ScioSense_ENS160::write8 (byte reg, byte value) { - byte buffer[2]; - buffer[1] = value; - buffer[0] = reg; - return i2cDevice.write(buffer, 2); -} - -bool ScioSense_ENS160::read8 (byte reg, byte *value) { - uint8_t buffer[1]; - buffer[0] = uint8_t(reg); - return i2cDevice.write_then_read(buffer, 1, value, 1); -} - -bool ScioSense_ENS160::read16 (byte reg, uint16_t *value) { - uint8_t buffer[1]; - buffer[0] = uint8_t(reg); - return i2cDevice.write_then_read(buffer, 1, (uint8_t *)value, 2); -} - -bool ScioSense_ENS160::read16LE (byte reg, uint16_t *value) { - uint16_t tmp; - if (read16(reg, &tmp)) { - *value = ((tmp & 0xff) << 8) | (tmp >> 8); - return true; - } - return false; -} - -bool ScioSense_ENS160::readBytes (byte reg, uint8_t *bytes, uint8_t len) { - uint8_t buffer[1]; - buffer[0] = uint8_t(reg); - return i2cDevice.write_then_read(buffer, 1, buffer, len); -} - - -// Sends a reset to the ENS160. Returns false on I2C problems. -bool ScioSense_ENS160::reset () { - if (write8(ENS160_REG_OPMODE, ENS160_OPMODE_RESET)) { - _delay_ms(ENS160_BOOTING); - return true; - } - _delay_ms(ENS160_BOOTING); - return false; -} - -// Reads the part ID and confirms valid sensor -bool ScioSense_ENS160::checkPartID () { - uint16_t part_id; - - read16(ENS160_REG_PART_ID, &part_id); - _delay_ms(ENS160_BOOTING); - - if (part_id == ENS160_PARTID) { - _revENS16x = 0; - return true; - - } else if (part_id == ENS161_PARTID) { - _revENS16x = 1; - return true; - } - - return false; -} - -// Initialize idle mode and confirms -bool ScioSense_ENS160::clearCommand () { - uint8_t status; - - if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_NOP)) { - if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_CLRGPR)) { - _delay_ms(ENS160_BOOTING); - if (read8(ENS160_REG_DATA_STATUS, &status)) { - return true; - } - } - } - _delay_ms(ENS160_BOOTING); - return false; -} - -// Read firmware revisions -bool ScioSense_ENS160::getFirmware () { - uint8_t i2cbuf[3]; - - if (clearCommand()) { - _delay_ms(ENS160_BOOTING); - if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_GET_APPVER)) { - if (readBytes(ENS160_REG_GPR_READ_4, i2cbuf, 3)) { - _fw_ver_major = i2cbuf[0]; - _fw_ver_minor = i2cbuf[1]; - _fw_ver_build = i2cbuf[2]; - _revENS16x = this->_fw_ver_major > 6 ? 1 : 0; - _delay_ms(ENS160_BOOTING); - return true; - } - } - } - _delay_ms(ENS160_BOOTING); - return false; -} - -// Set operation mode of sensor -bool ScioSense_ENS160::setMode (uint8_t mode) { - //LP only valid for rev>0 - if ((mode == ENS160_OPMODE_LP) and (_revENS16x == 0)) { - return false; - } - if (write8(ENS160_REG_OPMODE, mode)) { - _delay_ms(ENS160_BOOTING); - return true; - } - _delay_ms(ENS160_BOOTING); - return false; -} - -// Initialize definition of custom mode with steps -bool ScioSense_ENS160::initCustomMode (uint16_t stepNum) { - if (stepNum > 0) { - _stepCount = stepNum; - if (setMode(ENS160_OPMODE_IDLE)) { - if (clearCommand()) { - if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_SETSEQ)) { - _delay_ms(ENS160_BOOTING); - return true; - } - } - } - } - _delay_ms(ENS160_BOOTING); - return false; -} - -// Add a step to custom measurement profile with definition of duration, enabled data acquisition and temperature for each hotplate -bool ScioSense_ENS160::addCustomStep (uint16_t time, bool measureHP0, bool measureHP1, bool measureHP2, bool measureHP3, uint16_t tempHP0, uint16_t tempHP1, uint16_t tempHP2, uint16_t tempHP3) { - uint8_t seq_ack; - uint8_t temp; - - _delay_ms(ENS160_BOOTING); - - temp = (uint8_t)(((time / 24) - 1) << 6); - if (measureHP0) { - temp = temp | 0x20; - } - if (measureHP1) { - temp = temp | 0x10; - } - if (measureHP2) { - temp = temp | 0x08; - } - if (measureHP3) { - temp = temp | 0x04; - } - if (!write8(ENS160_REG_GPR_WRITE_0, temp)) { - return false; - } - temp = (uint8_t)(((time / 24) - 1) >> 2); - if (!write8(ENS160_REG_GPR_WRITE_1, temp)) { - return false; - } - if (!write8(ENS160_REG_GPR_WRITE_2, (uint8_t)(tempHP0 / 2))) { - return false; - } - if (!write8(ENS160_REG_GPR_WRITE_3, (uint8_t)(tempHP1 / 2))) { - return false; - } - if (write8(ENS160_REG_GPR_WRITE_4, (uint8_t)(tempHP2 / 2))) { - return false; - } - if (write8(ENS160_REG_GPR_WRITE_5, (uint8_t)(tempHP3 / 2))) { - return false; - } - - if (write8(ENS160_REG_GPR_WRITE_6, (uint8_t)(_stepCount - 1))) { - return false; - } - - if (_stepCount == 1) { - if (!write8(ENS160_REG_GPR_WRITE_7, 128)) { - return false; - } - } else { - if (!write8(ENS160_REG_GPR_WRITE_7, 0)) { - return false; - } - } - _delay_ms(ENS160_BOOTING); - - if (!read8(ENS160_REG_GPR_READ_7, &seq_ack)) { - return false; - } - _delay_ms(ENS160_BOOTING); - - if ((ENS160_SEQ_ACK_COMPLETE | _stepCount) != seq_ack) { - _stepCount++; - return false; - } - - return true; -} - -bool ScioSense_ENS160::readStatus (uint8_t *status) { - return read8(ENS160_REG_DATA_STATUS, status); -} - -bool ScioSense_ENS160::readData (ENS160_DATA *data) { - uint8_t buffer[1] = { 0x21 }; - return i2cDevice.write_then_read(buffer, 1, (uint8_t *)data, sizeof(ENS160_DATA)); -} - -// Perform prediction measurement and stores result in internal variables -bool ScioSense_ENS160::measure (bool waitForNew) { - uint8_t i2cbuf[8]; - uint8_t status; - - // Set default status for early bail out - if (waitForNew) { - do { - if (!read8(ENS160_REG_DATA_STATUS, &status)) { - return false; - } - _delay_ms(1); - } while (!IS_NEWDAT(status)); - } else { - if (!read8(ENS160_REG_DATA_STATUS, &status)) { - return false; - } - } - - - // Read predictions - if (IS_NEWDAT(status)) { - if (!readBytes(ENS160_REG_DATA_AQI, i2cbuf, 7)) { - return false; - } - return false; - _data_aqi = i2cbuf[0]; - _data_tvoc = i2cbuf[1] | ((uint16_t)i2cbuf[2] << 8); - _data_eco2 = i2cbuf[3] | ((uint16_t)i2cbuf[4] << 8); - if (_revENS16x > 0) { - _data_aqi500 = ((uint16_t)i2cbuf[5]) | ((uint16_t)i2cbuf[6] << 8); - } else { - _data_aqi500 = 0; - } - return true; - } - - return false; -} - -// Perfrom raw measurement and stores result in internal variables -bool ScioSense_ENS160::measureRaw (bool waitForNew) { - uint8_t i2cbuf[8]; - uint8_t status; - - // Set default status for early bail out - if (waitForNew) { - do { - _delay_ms(1); - if (!read8(ENS160_REG_DATA_STATUS, &status)) { - return false; - } - } while (!IS_NEWGPR(status)); - } else { - if (!read8(ENS160_REG_DATA_STATUS, &status)) { - return false; - } - } - - if (IS_NEWGPR(status)) { - - // Read raw resistance values - if (!readBytes(ENS160_REG_GPR_READ_0, i2cbuf, 8)) { - return false; - } - _hp0_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[0] | ((uint16_t)i2cbuf[1] << 8))); - _hp1_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[2] | ((uint16_t)i2cbuf[3] << 8))); - _hp2_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[4] | ((uint16_t)i2cbuf[5] << 8))); - _hp3_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[6] | ((uint16_t)i2cbuf[7] << 8))); - - // Read baselines - if (!readBytes(ENS160_REG_DATA_BL, i2cbuf, 8)) { - return false; - } - _hp0_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[0] | ((uint16_t)i2cbuf[1] << 8))); - _hp1_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[2] | ((uint16_t)i2cbuf[3] << 8))); - _hp2_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[4] | ((uint16_t)i2cbuf[5] << 8))); - _hp3_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[6] | ((uint16_t)i2cbuf[7] << 8))); - - if (!read8(ENS160_REG_DATA_MISR, i2cbuf)) { - return false; - } - _misr = i2cbuf[0]; - return true; - } - - return false; -} - - -// Writes t (degC) and h (%rh) to ENV_DATA. Returns false on I2C problems. -bool ScioSense_ENS160::set_envdata (float t, float h) { - uint16_t t_data = (uint16_t)((t + 273.15f) * 64.0f); - uint16_t rh_data = (uint16_t)(h * 512.0f); - return this->set_envdata210(t_data, rh_data); -} - -// Writes t and h (in ENS210 format) to ENV_DATA. Returns false on I2C problems. -bool ScioSense_ENS160::set_envdata210 (uint16_t t, uint16_t h) { - //uint16_t temp; - uint8_t trh_in[4]; - - //temp = (uint16_t)((t + 273.15f) * 64.0f); - trh_in[0] = t & 0xff; - trh_in[1] = (t >> 8) & 0xff; - - //temp = (uint16_t)(h * 512.0f); - trh_in[2] = h & 0xff; - trh_in[3] = (h >> 8) & 0xff; - - if (!i2cDevice.writeByteAndBuffer(ENS160_REG_TEMP_IN, trh_in, 4)) { - return false; - } - - return true; -} diff --git a/software/test_2024-07-23/src/adafruit/ens160.h b/software/test_2024-07-23/src/adafruit/ens160.h deleted file mode 100644 index fb6925d..0000000 --- a/software/test_2024-07-23/src/adafruit/ens160.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - ScioSense_ENS160.h - Library for the ENS160 sensor with I2C interface from ScioSense - 2023 Mar 23 v6 Christoph Friese Bugfix measurement routine, prepare next release - 2021 July 29 v4 Christoph Friese Changed nomenclature to ScioSense as product shifted from ams - 2020 Apr 06 v3 Christoph Friese Changed nomenclature to ScioSense as product shifted from ams - 2020 Feb 15 v2 Giuseppe Pasetti Corrected firmware flash option - 2019 May 05 v1 Christoph Friese Created - based on application note "ENS160 Software Integration.pdf" rev 0.01 -*/ - -#ifndef __SCIOSENSE_ENS160_H_ -#define __SCIOSENSE_ENS160_H_ - -#include "../i2cmaster.hpp" -#include -#define byte uint8_t - -// #if (ARDUINO >= 100) -// #include "Arduino.h" -// #else -// #include "WProgram.h" -// #endif - -// #include - -// Chip constants -#define ENS160_PARTID 0x0160 -#define ENS161_PARTID 0x0161 -#define ENS160_BOOTING 10 - -// 7-bit I2C slave address of the ENS160 -#define ENS160_I2CADDR_0 0x52 //ADDR low -#define ENS160_I2CADDR_1 0x53 //ADDR high - -// ENS160 registers for version V0 -#define ENS160_REG_PART_ID 0x00 // 2 byte register -#define ENS160_REG_OPMODE 0x10 -#define ENS160_REG_CONFIG 0x11 -#define ENS160_REG_COMMAND 0x12 -#define ENS160_REG_TEMP_IN 0x13 -#define ENS160_REG_RH_IN 0x15 -#define ENS160_REG_DATA_STATUS 0x20 -#define ENS160_REG_DATA_AQI 0x21 -#define ENS160_REG_DATA_TVOC 0x22 -#define ENS160_REG_DATA_ECO2 0x24 -#define ENS160_REG_DATA_BL 0x28 -#define ENS160_REG_DATA_T 0x30 -#define ENS160_REG_DATA_RH 0x32 -#define ENS160_REG_DATA_MISR 0x38 -#define ENS160_REG_GPR_WRITE_0 0x40 -#define ENS160_REG_GPR_WRITE_1 ENS160_REG_GPR_WRITE_0 + 1 -#define ENS160_REG_GPR_WRITE_2 ENS160_REG_GPR_WRITE_0 + 2 -#define ENS160_REG_GPR_WRITE_3 ENS160_REG_GPR_WRITE_0 + 3 -#define ENS160_REG_GPR_WRITE_4 ENS160_REG_GPR_WRITE_0 + 4 -#define ENS160_REG_GPR_WRITE_5 ENS160_REG_GPR_WRITE_0 + 5 -#define ENS160_REG_GPR_WRITE_6 ENS160_REG_GPR_WRITE_0 + 6 -#define ENS160_REG_GPR_WRITE_7 ENS160_REG_GPR_WRITE_0 + 7 -#define ENS160_REG_GPR_READ_0 0x48 -#define ENS160_REG_GPR_READ_4 ENS160_REG_GPR_READ_0 + 4 -#define ENS160_REG_GPR_READ_6 ENS160_REG_GPR_READ_0 + 6 -#define ENS160_REG_GPR_READ_7 ENS160_REG_GPR_READ_0 + 7 - -//ENS160 data register fields -#define ENS160_COMMAND_NOP 0x00 -#define ENS160_COMMAND_CLRGPR 0xCC -#define ENS160_COMMAND_GET_APPVER 0x0E -#define ENS160_COMMAND_SETTH 0x02 -#define ENS160_COMMAND_SETSEQ 0xC2 - -#define ENS160_OPMODE_RESET 0xF0 -#define ENS160_OPMODE_DEP_SLEEP 0x00 -#define ENS160_OPMODE_IDLE 0x01 -#define ENS160_OPMODE_STD 0x02 -#define ENS160_OPMODE_LP 0x03 -#define ENS160_OPMODE_CUSTOM 0xC0 - -#define ENS160_BL_CMD_START 0x02 -#define ENS160_BL_CMD_ERASE_APP 0x04 -#define ENS160_BL_CMD_ERASE_BLINE 0x06 -#define ENS160_BL_CMD_WRITE 0x08 -#define ENS160_BL_CMD_VERIFY 0x0A -#define ENS160_BL_CMD_GET_BLVER 0x0C -#define ENS160_BL_CMD_GET_APPVER 0x0E -#define ENS160_BL_CMD_EXITBL 0x12 - -#define ENS160_SEQ_ACK_NOTCOMPLETE 0x80 -#define ENS160_SEQ_ACK_COMPLETE 0xC0 - -#define IS_ENS160_SEQ_ACK_NOT_COMPLETE(x) (ENS160_SEQ_ACK_NOTCOMPLETE == (ENS160_SEQ_ACK_NOTCOMPLETE & (x))) -#define IS_ENS160_SEQ_ACK_COMPLETE(x) (ENS160_SEQ_ACK_COMPLETE == (ENS160_SEQ_ACK_COMPLETE & (x))) - -#define ENS160_DATA_STATUS_NEWDAT 0x02 -#define ENS160_DATA_STATUS_NEWGPR 0x01 - -#define IS_NEWDAT(x) (ENS160_DATA_STATUS_NEWDAT == (ENS160_DATA_STATUS_NEWDAT & (x))) -#define IS_NEWGPR(x) (ENS160_DATA_STATUS_NEWGPR == (ENS160_DATA_STATUS_NEWGPR & (x))) -#define IS_NEW_DATA_AVAILABLE(x) (0 != ((ENS160_DATA_STATUS_NEWDAT | ENS160_DATA_STATUS_NEWGPR ) & (x))) - -#define CONVERT_RS_RAW2OHMS_I(x) (1 << ((x) >> 11)) -#define CONVERT_RS_RAW2OHMS_F(x) (pow (2, (float)(x) / 2048)) - -typedef struct { - uint8_t aqi; - uint16_t tvoc; - uint16_t eco2; -} ENS160_DATA; - -class ScioSense_ENS160 { - - public: - ScioSense_ENS160(); - - void setI2C(uint8_t sda, uint8_t scl); // Function to redefine I2C pins - - bool begin(); // Init I2C communication, resets ENS160 and checks its PART_ID. Returns false on I2C problems or wrong PART_ID. - uint8_t revENS16x() { return this->_revENS16x; } // Report version of sensor (0: ENS160, 1: ENS161) - bool setMode(uint8_t mode); // Set operation mode of sensor - - bool initCustomMode(uint16_t stepNum); // Initialize definition of custom mode with steps - bool addCustomStep(uint16_t time, bool measureHP0, bool measureHP1, bool measureHP2, bool measureHP3, uint16_t tempHP0, uint16_t tempHP1, uint16_t tempHP2, uint16_t tempHP3); - // Add a step to custom measurement profile with definition of duration, enabled data acquisition and temperature for each hotplate - - bool readData (ENS160_DATA *data); - bool readStatus(uint8_t *status); - bool measure(bool waitForNew); // Perform measurement and stores result in internal variables - bool measureRaw(bool waitForNew); // Perform raw measurement and stores result in internal variables - bool set_envdata(float t, float h); // Writes t (degC) and h (%rh) to ENV_DATA. Returns "0" if I2C transmission is successful - bool set_envdata210(uint16_t t, uint16_t h); // Writes t and h (in ENS210 format) to ENV_DATA. Returns "0" if I2C transmission is successful - uint8_t getMajorRev() { return this->_fw_ver_major; } // Get major revision number of used firmware - uint8_t getMinorRev() { return this->_fw_ver_minor; } // Get minor revision number of used firmware - uint8_t getBuild() { return this->_fw_ver_build; } // Get build revision number of used firmware - - uint8_t getAQI() { return this->_data_aqi; } // Get AQI value of last measurement - uint16_t getTVOC() { return this->_data_tvoc; } // Get TVOC value of last measurement - uint16_t geteCO2() { return this->_data_eco2; } // Get eCO2 value of last measurement - uint16_t getAQI500() { return this->_data_aqi500; } // Get AQI500 value of last measurement - uint32_t getHP0() { return this->_hp0_rs; } // Get resistance of HP0 of last measurement - uint32_t getHP1() { return this->_hp1_rs; } // Get resistance of HP1 of last measurement - uint32_t getHP2() { return this->_hp2_rs; } // Get resistance of HP2 of last measurement - uint32_t getHP3() { return this->_hp3_rs; } // Get resistance of HP3 of last measurement - uint32_t getHP0BL() { return this->_hp0_bl; } // Get baseline resistance of HP0 of last measurement - uint32_t getHP1BL() { return this->_hp1_bl; } // Get baseline resistance of HP1 of last measurement - uint32_t getHP2BL() { return this->_hp2_bl; } // Get baseline resistance of HP2 of last measurement - uint32_t getHP3BL() { return this->_hp3_bl; } // Get baseline resistance of HP3 of last measurement - uint8_t getMISR() { return this->_misr; } // Return status code of sensor - - private: - I2cMaster i2cDevice; - bool reset(); // Sends a reset to the ENS160. Returns false on I2C problems. - bool checkPartID(); // Reads the part ID and confirms valid sensor - bool clearCommand(); // Initialize idle mode and confirms - bool getFirmware(); // Read firmware revisions - - uint8_t _revENS16x; // ENS160 or ENS161 connected? (FW >7) - - uint8_t _fw_ver_major; - uint8_t _fw_ver_minor; - uint8_t _fw_ver_build; - - uint16_t _stepCount; // Counter for custom sequence - - uint8_t _data_aqi; - uint16_t _data_tvoc; - uint16_t _data_eco2; - uint16_t _data_aqi500; - uint32_t _hp0_rs; - uint32_t _hp0_bl; - uint32_t _hp1_rs; - uint32_t _hp1_bl; - uint32_t _hp2_rs; - uint32_t _hp2_bl; - uint32_t _hp3_rs; - uint32_t _hp3_bl; - uint16_t _temp; - int _slaveaddr; // Slave address of the ENS160 - uint8_t _misr; - - uint8_t _seq_steps[1][8]; - - bool write8(byte reg, byte value); - bool read8 (byte reg, byte *value); - bool read16 (byte reg, uint16_t *value); - bool read16LE (byte reg, uint16_t *value); - bool readBytes (byte reg, uint8_t *bytes, uint8_t len); -}; - - -#endif diff --git a/software/test_2024-07-23/src/adafruit/sensor.h b/software/test_2024-07-23/src/adafruit/sensor.h deleted file mode 100644 index ac7e454..0000000 --- a/software/test_2024-07-23/src/adafruit/sensor.h +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software< /span> - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Update by K. Townsend (Adafruit Industries) for lighter typedefs, and - * extended sensor support to include color, voltage and current */ - -#ifndef _ADAFRUIT_SENSOR_H -#define _ADAFRUIT_SENSOR_H - -#ifndef ARDUINO -#include -#elif ARDUINO >= 100 -#include "Arduino.h" -#include "Print.h" -#else -#include "WProgram.h" -#endif - -/* Constants */ -#define SENSORS_GRAVITY_EARTH (9.80665F) /**< Earth's gravity in m/s^2 */ -#define SENSORS_GRAVITY_MOON (1.6F) /**< The moon's gravity in m/s^2 */ -#define SENSORS_GRAVITY_SUN (275.0F) /**< The sun's gravity in m/s^2 */ -#define SENSORS_GRAVITY_STANDARD (SENSORS_GRAVITY_EARTH) -#define SENSORS_MAGFIELD_EARTH_MAX \ - (60.0F) /**< Maximum magnetic field on Earth's surface */ -#define SENSORS_MAGFIELD_EARTH_MIN \ - (30.0F) /**< Minimum magnetic field on Earth's surface */ -#define SENSORS_PRESSURE_SEALEVELHPA \ - (1013.25F) /**< Average sea level pressure is 1013.25 hPa */ -#define SENSORS_DPS_TO_RADS \ - (0.017453293F) /**< Degrees/s to rad/s multiplier \ - */ -#define SENSORS_RADS_TO_DPS \ - (57.29577793F) /**< Rad/s to degrees/s multiplier */ -#define SENSORS_GAUSS_TO_MICROTESLA \ - (100) /**< Gauss to micro-Tesla multiplier */ - -/** Sensor types */ -typedef enum { - SENSOR_TYPE_ACCELEROMETER = (1), /**< Gravity + linear acceleration */ - SENSOR_TYPE_MAGNETIC_FIELD = (2), - SENSOR_TYPE_ORIENTATION = (3), - SENSOR_TYPE_GYROSCOPE = (4), - SENSOR_TYPE_LIGHT = (5), - SENSOR_TYPE_PRESSURE = (6), - SENSOR_TYPE_PROXIMITY = (8), - SENSOR_TYPE_GRAVITY = (9), - SENSOR_TYPE_LINEAR_ACCELERATION = - (10), /**< Acceleration not including gravity */ - SENSOR_TYPE_ROTATION_VECTOR = (11), - SENSOR_TYPE_RELATIVE_HUMIDITY = (12), - SENSOR_TYPE_AMBIENT_TEMPERATURE = (13), - SENSOR_TYPE_OBJECT_TEMPERATURE = (14), - SENSOR_TYPE_VOLTAGE = (15), - SENSOR_TYPE_CURRENT = (16), - SENSOR_TYPE_COLOR = (17), - SENSOR_TYPE_TVOC = (18), - SENSOR_TYPE_VOC_INDEX = (19), - SENSOR_TYPE_NOX_INDEX = (20), - SENSOR_TYPE_CO2 = (21), - SENSOR_TYPE_ECO2 = (22), - SENSOR_TYPE_PM10_STD = (23), - SENSOR_TYPE_PM25_STD = (24), - SENSOR_TYPE_PM100_STD = (25), - SENSOR_TYPE_PM10_ENV = (26), - SENSOR_TYPE_PM25_ENV = (27), - SENSOR_TYPE_PM100_ENV = (28), - SENSOR_TYPE_GAS_RESISTANCE = (29), - SENSOR_TYPE_UNITLESS_PERCENT = (30), - SENSOR_TYPE_ALTITUDE = (31) -} sensors_type_t; - -/** struct sensors_vec_s is used to return a vector in a common format. */ -typedef struct { - union { - float v[3]; ///< 3D vector elements - struct { - float x; ///< X component of vector - float y; ///< Y component of vector - float z; ///< Z component of vector - }; ///< Struct for holding XYZ component - /* Orientation sensors */ - struct { - float roll; /**< Rotation around the longitudinal axis (the plane body, 'X - axis'). Roll is positive and increasing when moving - downward. -90 degrees <= roll <= 90 degrees */ - float pitch; /**< Rotation around the lateral axis (the wing span, 'Y - axis'). Pitch is positive and increasing when moving - upwards. -180 degrees <= pitch <= 180 degrees) */ - float heading; /**< Angle between the longitudinal axis (the plane body) - and magnetic north, measured clockwise when viewing from - the top of the device. 0-359 degrees */ - }; ///< Struct for holding roll/pitch/heading - }; ///< Union that can hold 3D vector array, XYZ components or - ///< roll/pitch/heading - int8_t status; ///< Status byte - uint8_t reserved[3]; ///< Reserved -} sensors_vec_t; - -/** struct sensors_color_s is used to return color data in a common format. */ -typedef struct { - union { - float c[3]; ///< Raw 3-element data - /* RGB color space */ - struct { - float r; /**< Red component */ - float g; /**< Green component */ - float b; /**< Blue component */ - }; ///< RGB data in floating point notation - }; ///< Union of various ways to describe RGB colorspace - uint32_t rgba; /**< 24-bit RGBA value */ -} sensors_color_t; - -/* Sensor event (36 bytes) */ -/** struct sensor_event_s is used to provide a single sensor event in a common - * format. */ -typedef struct { - int32_t version; /**< must be sizeof(struct sensors_event_t) */ - int32_t sensor_id; /**< unique sensor identifier */ - int32_t type; /**< sensor type */ - int32_t reserved0; /**< reserved */ - int32_t timestamp; /**< time is in milliseconds */ - union { - float data[4]; ///< Raw data */ - sensors_vec_t acceleration; /**< acceleration values are in meter per second - per second (m/s^2) */ - sensors_vec_t - magnetic; /**< magnetic vector values are in micro-Tesla (uT) */ - sensors_vec_t orientation; /**< orientation values are in degrees */ - sensors_vec_t gyro; /**< gyroscope values are in rad/s */ - float temperature; /**< temperature is in degrees centigrade (Celsius) */ - float distance; /**< distance in centimeters */ - float light; /**< light in SI lux units */ - float pressure; /**< pressure in hectopascal (hPa) */ - float relative_humidity; /**< relative humidity in percent */ - float current; /**< current in milliamps (mA) */ - float voltage; /**< voltage in volts (V) */ - float tvoc; /**< Total Volatile Organic Compounds, in ppb */ - float voc_index; /**< VOC (Volatile Organic Compound) index where 100 is - normal (unitless) */ - float nox_index; /**< NOx (Nitrogen Oxides) index where 100 is normal - (unitless) */ - float CO2; /**< Measured CO2 in parts per million (ppm) */ - float eCO2; /**< equivalent/estimated CO2 in parts per million (ppm - estimated from some other measurement) */ - float pm10_std; /**< Standard Particulate Matter <=1.0 in parts per million - (ppm) */ - float pm25_std; /**< Standard Particulate Matter <=2.5 in parts per million - (ppm) */ - float pm100_std; /**< Standard Particulate Matter <=10.0 in parts per - million (ppm) */ - float pm10_env; /**< Environmental Particulate Matter <=1.0 in parts per - million (ppm) */ - float pm25_env; /**< Environmental Particulate Matter <=2.5 in parts per - million (ppm) */ - float pm100_env; /**< Environmental Particulate Matter <=10.0 in parts per - million (ppm) */ - float gas_resistance; /**< Proportional to the amount of VOC particles in - the air (Ohms) */ - float unitless_percent; /** -#include - -#include "i2cmaster.hpp" - -I2cMaster::I2cMaster () { - address = 0; - timer = 0; -} - -void I2cMaster::tick1ms () { - if (timer > 0) { - timer--; - } -} - -bool I2cMaster::begin (uint8_t addr) { - this->address = addr; - // TWBR = 13; // 100kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 100000) / (2 * 100000 * 4); - TWBR = 100; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4); - TWCR = (1 << TWEN); - return true; -} - -void I2cMaster::end () { - TWCR = (1 << TWEN); - TWBR = 0; -} - -bool I2cMaster::read (uint8_t *buffer, uint8_t len) { - if (start(true)) { - if (readBytes(buffer, len)) { - if (stop()) { - return true; - } - } - } - return false; -} - - -bool I2cMaster::write (const uint8_t *buffer, uint8_t len) { - if (start(false)) { - if (writeBytes(buffer, len)) { - if (stop()) { - return true; - } - } - } - return false; -} - -bool I2cMaster::writeByteAndBuffer (uint8_t byte, const uint8_t *buffer, uint8_t len) { - if (start(false)) { - do { - TWDR = byte; - TWCR = (1 << TWINT) | (1 << TWEN); // send byte - while (!(TWCR & (1 << TWINT))) {}; // wait until last action done - if ((TWSR & 0xf8) != 0x28) { - return false; - } - byte = *buffer++; - } while (len-- > 0); - return true; - } - return false; -} - - -bool I2cMaster::write_then_read (const uint8_t *write_buffer, uint8_t write_len, uint8_t *read_buffer, uint8_t read_len) { - if (start(false)) { - if (writeBytes(write_buffer, write_len)) { - if (start(true)) { - if (readBytes(read_buffer, read_len)) { - if (stop()) { - return true; - } - } - } - } - } - return false; -} - -// ------------------------------------------------------------- - -bool I2cMaster::readBytes (uint8_t *buffer, uint8_t len) { - while (len-- > 0) { - if (len > 0) { - TWCR = (1 << TWEA) | (1 << TWINT) | (1 << TWEN); // read data byte with ACK enabled - } else { - TWCR = (1 << TWINT) | (1 << TWEN); // read data byte with ACK disabled - } - while (!(TWCR & (1 << TWINT))) {}; // wait until last action done - uint8_t sr = TWSR & 0xf8; - if ((len > 0 && sr != 0x50) || (len == 0 && sr != 0x58)) { - return false; - } - *buffer++ = TWDR; - } - return true; -} - -bool I2cMaster::writeBytes (const uint8_t *buffer, uint8_t len) { - while (len-- > 0) { - // printf("[wB:len=%d, byte=%02x]", len + 1, *buffer); - TWDR = *buffer++; - TWCR = (1 << TWINT) | (1 << TWEN); // send data byte - timer = 5; - while (timer > 0 && !(TWCR & (1 << TWINT))) {}; // wait until last action done - if (!timer || (TWSR & 0xf8) != 0x28) { - return false; - } - } - return true; -} - - -bool I2cMaster::start (bool read) { - TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // send START condition - timer = 5; - while (timer > 0 && !(TWCR & (1 << TWINT))) {}; // wait until last action done - uint8_t sr = TWSR & 0xf8; - if (!timer || (sr != 0x08 && sr != 0x10)) { - return false; - } - TWDR = (address << 1) | (read ? 1 : 0); // address + R/nW - TWCR = (1 << TWINT) | (1 << TWEN); // send address/RW - timer = 5; - while (timer > 0 && !(TWCR & (1 << TWINT))) {}; // wait until last action done - sr = TWSR & 0xf8; - if (!timer || (!read && sr != 0x18) || (read && sr != 0x40)) { - return false; - } - return true; -} - -bool I2cMaster::stop () { - TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); - while (TWCR & ( 1 << TWSTO)); - return true; -} - diff --git a/software/test_2024-07-23/src/i2cmaster.hpp b/software/test_2024-07-23/src/i2cmaster.hpp deleted file mode 100644 index e70326c..0000000 --- a/software/test_2024-07-23/src/i2cmaster.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef I2C_MASTER -#define I2C_MASTER - -#include - -class I2cMaster { - public: - static void end (); - - public: - I2cMaster (); - void tick1ms (); - bool begin (uint8_t addr); - bool read (uint8_t *buffer, uint8_t len); - bool write(const uint8_t *buffer, uint8_t len); - bool write_then_read(const uint8_t *write_buffer, uint8_t write_len, uint8_t *read_buffer, uint8_t read_len); - bool writeByteAndBuffer (uint8_t byte, const uint8_t *buffer, uint8_t len); - - private: - uint8_t address; - uint8_t timer; - bool start (bool read); - bool stop (); - bool writeBytes (const uint8_t *buffer, uint8_t len); - bool readBytes (uint8_t *buffer, uint8_t len); -}; - -#endif \ No newline at end of file diff --git a/software/test_2024-07-23/src/i2cslave.cpp b/software/test_2024-07-23/src/i2cslave.cpp deleted file mode 100644 index 2bf6ac2..0000000 --- a/software/test_2024-07-23/src/i2cslave.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include -#include -#include - -#include "i2cslave.hpp" - -I2cSlave::I2cSlave () { - timer = 0; - fromMaster.rIndex = 0; - fromMaster.wIndex = 0; - toMaster.rIndex = 0; - toMaster.wIndex = 0; -} - -void I2cSlave::tick1ms () { - if (timer > 0) { - timer--; - } -} - -bool I2cSlave::begin (uint8_t addr, bool acceptGeneralCalls) { - if (addr > 127) { - return false; - } - TWAR = addr << 1 | (acceptGeneralCalls ? 1 : 0); - TWBR = 100; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4); - TWCR = (1 << TWEA) | (1 << TWEN) | (1 << TWIE); - return true; -} - -void I2cSlave::end () { - TWCR = (1 << TWEN); - TWBR = 0; -} - -int I2cSlave::read () { - return getByte(fromMaster); -} - -void I2cSlave::write (uint8_t byte) { - putByte(toMaster, byte); -} - - -void I2cSlave::putByte (RingBuffer& buffer, uint8_t byte) { - ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { - buffer.data[buffer.wIndex++] = byte; - if (buffer.wIndex >= sizeof(buffer.data)) { - buffer.wIndex = 0; - } - if (buffer.wIndex == buffer.rIndex) { - buffer.rIndex++; - if (buffer.rIndex >= sizeof(buffer.data)) { - buffer.rIndex = 0; - } - } - } -} - -int I2cSlave::getByte (RingBuffer& buffer) { - uint8_t b; - ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { - if (buffer.rIndex == buffer.wIndex) { - return EOF; - } - b = buffer.data[buffer.rIndex++]; - if (buffer.rIndex >= sizeof(buffer.data)) { - buffer.rIndex = 0; - } - } - return b; -} - -void I2cSlave::handleTWIIsr () { - uint8_t sr = TWSR & 0xf8; - switch (sr) { - case 0x80: { // Previously addressed with own SLA+W; data has been received; ACK has been returned - putByte(fromMaster, TWDR); - TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE); // no TWEA -> only one byte accepted - break; - } - case 0xa8: { // Own SLA+R has been received; ACK has been returned - int response = getByte(toMaster);; - TWDR = response < 0 ? 0x00 : (uint8_t)response; - TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE); // no TWEA -> only one byte accepted - break; - } - default: TWCR = (1 << TWINT) | (1 << TWEA) | (1 << TWEN) | (1 << TWIE); break; - } - -} - diff --git a/software/test_2024-07-23/src/i2cslave.hpp b/software/test_2024-07-23/src/i2cslave.hpp deleted file mode 100644 index 2fe2dc7..0000000 --- a/software/test_2024-07-23/src/i2cslave.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef I2C_SLAVE -#define I2C_SLAVE - -#include - -class I2cSlave { - private: - typedef struct { - uint8_t rIndex; - uint8_t wIndex; - uint8_t data[8]; - } RingBuffer; - - public: - I2cSlave (); - void tick1ms (); - bool begin (uint8_t addr, bool acceptGeneralCalls); - void end (); - void handleTWIIsr (); - int read (); - void write (uint8_t byte); - - private: - uint8_t timer; - RingBuffer fromMaster; - RingBuffer toMaster; - void putByte (RingBuffer& buffer, uint8_t byte); - int getByte (RingBuffer& buffer); - -}; - -#endif \ No newline at end of file diff --git a/software/test_2024-07-23/src/main.cpp b/software/test_2024-07-23/src/main.cpp deleted file mode 100644 index c363d60..0000000 --- a/software/test_2024-07-23/src/main.cpp +++ /dev/null @@ -1,256 +0,0 @@ -#include -#include -#include -#include -#include - -#include "main.hpp" -#include "units/led.hpp" -#include "units/switch.hpp" -#include "units/rgb.hpp" -#include "units/seg7.hpp" -#include "units/poti.hpp" -#include "units/encoder.hpp" -#include "units/r2r.hpp" -#include "units/motor.hpp" -#include "units/portexp.hpp" -#include "units/lcd.hpp" -#include "units/uart1.hpp" -#include "units/modbus.hpp" -#include "units/ieee485.hpp" -#include "units/i2c.hpp" - - -extern "C" { - void __cxa_pure_virtual () { - } - - int __cxa_guard_acquire(uint8_t *g) { - return 0; - } - - void __cxa_guard_release(uint8_t *g) {} - - void __cxa_guard_abort(uint8_t *g) {} - - - int uart_putchar(char c, FILE *stream) { - if (c == '\n') { - uart_putchar('\r', stream); - } - if (stream == stdout) { - loop_until_bit_is_set(UCSR0A, UDRE0); - UDR0 = c; - } - return 0; - } - - - uint64_t volatile systemMillis = 0; - uint8_t volatile uartBuffer[32]; - uint8_t volatile rIndex = 0; - uint8_t volatile wIndex = 0; - - int uart_getchar (FILE *stream) { - // if (rIndex == wIndex) { - // // nothing in buffer - // return EOF; - // } - // printf(" r%d", rIndex); - while (rIndex == wIndex) { - // wait for character - } - - // don't use "char c" because german special characters would lead to negative return -> stream error - // char c = uartBuffer[rIndex++]; - - uint8_t c = uartBuffer[rIndex++]; - // printf("(%02x) ", c); - if (c == '\r') { - c = '\n'; - } - putchar(c); // echo on terminal - return c; - } - - static FILE mystdout = { 0, 0, _FDEV_SETUP_WRITE , 0, 0, uart_putchar, NULL, 0 }; - static FILE mystdin = { 0, 0, _FDEV_SETUP_READ , 0, 0, NULL, uart_getchar, 0 }; - - static volatile uint32_t timer1ms = 0; - static volatile int keyUart0 = EOF; - - Led led; - Switch sw; - Rgb rgb; - Seg7 seg7; - Poti poti; - Encoder encoder; - R2r r2r; - Motor motor; - PortExp portExp; - Lcd lcd; - Uart1 uart1; - Modbus modbus; - Ieee485 ieee485; - I2c i2cSparkfun(I2c::SparkFunEnvCombo); - I2c i2cMaster(I2c::Master); - I2c i2cSlave(I2c::Slave); - -} - -void setTimer (uint32_t ms) { - ATOMIC_BLOCK(ATOMIC_FORCEON) { - timer1ms = ms; - } -} - -int wait (uint32_t ms) { - setTimer(ms); - do { - ATOMIC_BLOCK(ATOMIC_FORCEON) { - ms = timer1ms; - } - } while (ms > 0 && keyUart0 == EOF); - return keyUart0; -} - -int main () { - - // Nano-644 LEDs (Green, Orange, Red) - DDRC |= (1 << DDC4) | (1 << DDC3) | (1 << DDC2); - PORTC &= ~((1 << PORT4) | (1 << PORT3) | (1 << PORT2)); - - // Nano-644 push button SW2 - DDRC &= ~(1 << DDC5); - PORTC |= (1 << PORT5); // enable internal pullup resistor - - // UART0 interface on Nano-644 - UCSR0A = (1 << U2X0); - UCSR0B = (1 << RXCIE0) | (1 << RXEN0) | (1 <getName()); - } - printf("\nSelect unit: "); - rIndex = 0; wIndex = 0; - fgets(s, sizeof(s), stdin); - } while (sscanf(s, "%x", &i) != 1 || i < 0 || i >= sizeof(unit) / sizeof(unit[0]) ); - - TestUnit *pu = unit[i]; - printf("\n\n[%s]: ", pu->getName()); - keyUart0 = EOF; - - for (uint8_t subtest = 0; subtest < 0xff; subtest++) { - printf("\n%4d: ", subtest); - if (pu->run(subtest) < 0) { - break; - } - if (keyUart0 == 27) { - keyUart0 = EOF; - break; - } - keyUart0 = EOF; - } - pu->cleanup(); - } -} - -uint64_t millis () { - volatile uint64_t millis = systemMillis; - ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { - millis = systemMillis; - } - return millis; -} - -ISR (USART0_RX_vect) { - uint8_t b = UDR0; - keyUart0 = b; - uartBuffer[wIndex++] = b; - // printf(" w%d(%02x)", wIndex, b); - if (wIndex == rIndex) { - // buffer overflow, kick out oldest byte - rIndex++; - } -} - -ISR (USART1_RX_vect) { - uint8_t b = UDR1; - if (modbus.enabled) { - modbus.handleRxByte(b); - } - if (uart1.enabled) { - uart1.handleRxByte(b); - } - if (ieee485.enabled) { - ieee485.handleRxByte(b); - } -} - -ISR (TWI_vect) { - if (i2cMaster.enabled) { - i2cMaster.handleTwiIrq(); - } else if (i2cSlave.enabled) { - i2cSlave.handleTwiIrq(); - } else if (i2cSparkfun.enabled) { - i2cSparkfun.handleTwiIrq(); - } else { - TWCR = (1 << TWINT); // clear interrupt request bit and disable TWI - } -} - -ISR (TIMER2_COMPA_vect) { // every 100us - static uint16_t timer500ms = 0; - static uint8_t timer100us = 0; - - if (encoder.enabled) { - encoder.tick100us(); - } - if (motor.enabled) { - motor.tick100us(); - } - - timer100us++; - if (timer100us >= 10) { - timer100us = 0; - if (timer1ms > 0) { - timer1ms--; - } - systemMillis++; - i2cMaster.tick1ms(); - i2cSlave.tick1ms(); - i2cSparkfun.tick1ms(); - } - - timer500ms++; - if (timer500ms >= 5000) { - PORTC ^= (1 << PORTC3); // orange LED blinking - timer500ms = 0; - } -} - diff --git a/software/test_2024-07-23/src/main.hpp b/software/test_2024-07-23/src/main.hpp deleted file mode 100644 index de2c1e1..0000000 --- a/software/test_2024-07-23/src/main.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef MAIN_HPP -#define MAIN_HPP - -#include - -#define ENTER '\r' -#define CTRLC '\003' - -extern int wait (uint32_t ms); -extern uint64_t millis (); - -class TestUnit { - public: - virtual int8_t run (uint8_t subtest) = 0; - virtual void cleanup () = 0; - virtual const char *getName () = 0; -}; - -#endif \ No newline at end of file diff --git a/software/test_2024-07-23/src/units/encoder.cpp b/software/test_2024-07-23/src/units/encoder.cpp deleted file mode 100644 index 3b8ce8f..0000000 --- a/software/test_2024-07-23/src/units/encoder.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include -#include - -#include "encoder.hpp" -#include "../main.hpp" - -// PB0/T0 ... Encoder A -// PB1/T1 ... Encoder B -// PB2/INT2 ... push switch of encoder (pushed = 0) - -// Encoder signals on rotation clockwise 1 step: -// A -----____------ one char app. 1ms..2ms (rotation speed) -// B -------___----- -// one step when: A = 0, B= 1->0 - -// Encoder signals on rotation counterclockwise 1 step: -// A -----____------ one char app. 1ms..2ms (rotation speed) -// B --______----- -// one step when: A = 0, B= 0->1 - - -void Encoder::cleanup () { - DDRB &= ~((1 << PB2) | (1 << PB1) | (1 << PB0)); - PORTB &= ~((1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0)); - enabled = 0; -} - -int8_t Encoder::run (uint8_t subtest) { - switch (subtest) { - case 0: { - printf("init"); - DDRB &= ~((1 << PB2) | (1 << PB1) | (1 << PB0)); - PORTB |= (1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0); // enable pullup - enabled = 1; - return 0; - } - - case 1: { - while (wait(10) == EOF) { - printf("\r => Encoder (push to clear): "); - printf("%5d (0x%02x) ", count, (uint8_t)count); - if ((PINB & (1 << PINB2)) == 0) { - reset(); - } - } - return 0; - } - - case 2: { - printf("\r => Encoder end%30s", " "); - break; - } - } - - return -1; -} - -struct EncoderState { - uint8_t a:1; // signal A - uint8_t b:1; // signal B -}; - -void Encoder::tick100us () { - static EncoderState lastState = { 1, 1 }; - static EncoderState lastStableState = { 1, 1 }; - - if ((DDRB & 0x03) || (PORTB & 0x07) != 0x07) { - count = 0; - return; // Enocder pins not configured - } - EncoderState nextState = { (PINB & 0x01) == 0x01, (PINB & 0x02) == 0x02 }; - if (nextState.a == lastState.a && nextState.b == lastState.b) { - if (lastStableState.a == 0 && nextState.b != lastStableState.b) { - if (nextState.b == 0) { - count = count < 127 ? count + 1 : 127; - } else { - count = count > -128 ? count - 1 : -128; - } - } - lastStableState.a = nextState.a; - lastStableState.b = nextState.b; - } - lastState.a = nextState.a; - lastState.b = nextState.b; -} - diff --git a/software/test_2024-07-23/src/units/encoder.hpp b/software/test_2024-07-23/src/units/encoder.hpp deleted file mode 100644 index f28584c..0000000 --- a/software/test_2024-07-23/src/units/encoder.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef ENCODER_HPP -#define ENCODER_PP - -#include -#include "../main.hpp" - - -class Encoder : public TestUnit { - public: - uint8_t enabled; - int8_t count; - - public: - Encoder () { reset(); enabled = 0; }; - virtual void cleanup (); - virtual int8_t run (uint8_t subtest); - virtual const char *getName () { return "Encoder"; } - void reset () { count = 0; } - void tick100us (); -}; - -#endif \ No newline at end of file diff --git a/software/test_2024-07-23/src/units/i2c.cpp b/software/test_2024-07-23/src/units/i2c.cpp deleted file mode 100644 index 64454cd..0000000 --- a/software/test_2024-07-23/src/units/i2c.cpp +++ /dev/null @@ -1,300 +0,0 @@ -#include -#include -#include -#include - -#include "i2c.hpp" -#include "../adafruit/bme280.h" -#include "../main.hpp" - - -// Sparkfun https://www.sparkfun.com/products/22858 -// ENS160 address 0x53 (air quality sensor) -// BME280 address 0x77 /humidity/temperature sensor) -// register 0xfe: humidity_7:0 -// register 0xfd: humidity_15:8 - -const char *I2c::getName () { - switch (mode) { - case SparkFunEnvCombo: return "I2C-Sparkfun Env-Combo"; - case Master: return "I2C-Master"; - case Slave: return "I2C-Slave"; - } - return "?"; -} - -void I2c::cleanup () { - enabled = false; - TWCR = (1 << TWEN); - TWBR = 0; - ADMUX = 0; - ADCSRA = 0; -} - -int8_t I2c::run (uint8_t subtest) { - if (subtest == 0) { - TWBR = 13; // 100kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 100000) / (2 * 100000 * 4); - TWBR = 28; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4); - TWBR = 100; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4); - TWCR = (1 << TWEN); - ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V - ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128 - enabled = true; - printf("init"); - - } else if (subtest == 1 && mode == I2c::SparkFunEnvCombo) { - printf(" BM280 ... "); - if (!bm280.begin()) { - printf("ERROR"); - return -1; - } - printf("OK, ENS160 ... "); - if (!ens160.begin()) { - printf("ERROR"); - return -1; - } - if (!ens160.setMode(ENS160_OPMODE_STD)) { - printf("ERROR"); - return -1; - } - if (!ens160.set_envdata(25.0, 65)) { - printf("ERROR"); - return -1; - } - - printf("OK"); - float accTemp = 0, accHumidity = 0; - int8_t accCount = -1; - - do { - // BME280 - float p = bm280.readPressure(); - printf("\n => BM280: P= %.3fbar", (double)p / 100000.0); - float t = bm280.readTemperature(); - printf(", T= %.2f°C", (double)t); - float h = bm280.readHumidity(); - printf(", H= %.2f%%", (double)h); - - if (accCount >= 0 && !isnanf(h) && !isnan(t)) { - accTemp += t; - accHumidity += h; - accCount++; - } - - bm280.setSampling( - Adafruit_BME280::MODE_NORMAL, - Adafruit_BME280::SAMPLING_X16, - Adafruit_BME280::SAMPLING_X16, - Adafruit_BME280::SAMPLING_X16, - Adafruit_BME280::FILTER_OFF, - Adafruit_BME280::STANDBY_MS_1000 - ); - - // ENS160 only activated every 32s to avoid wrong temperature measuerment - // if ES160 would be continously active, the temperature would be 4°C higher - // -> ES160 has not enough distance to BM280 - // This solution causes only a +0.3°C higher temperatur value - if (accCount < 0 || accCount >= 32) { - printf(" | ENS160 ("); - if (accCount > 0) { - h = accHumidity / accCount; - t = accTemp / accCount; - accTemp = 0; - accHumidity = 0; - } - accCount = 0; - if (!ens160.set_envdata(t, h)) { - printf("E1)"); - } else { - printf("%.1f°C/%.1f%%): ", (double)t, (double)h); - if (!ens160.setMode(ENS160_OPMODE_STD)) { - printf("E2"); - } else { - for (uint8_t i = 0; i < 100; i++) { - _delay_ms(15); - uint8_t status; - if (ens160.readStatus(&status)) { - if (status & ENS160_DATA_STATUS_NEWDAT) { - ENS160_DATA data; - if (ens160.readData(&data)) { - printf(" aqi=%d(", data.aqi); - switch(data.aqi) { - case 1: printf("excellent"); break; - case 2: printf("good"); break; - case 3: printf("moderate"); break; - case 4: printf("poor"); break; - case 5: printf("unhealthy"); break; - default: printf("?"); break; - } - printf("), tvoc=%dppb", data.tvoc); - printf(", eco2=%d(", data.eco2); - if (data.eco2 < 400) { - printf("?"); - } else if (data.eco2 < 600) { - printf("excellent"); - } else if (data.eco2 < 800) { - printf("good"); - } else if (data.eco2 < 1000) { - printf("fair"); - } else if (data.eco2 < 1500) { - printf("poor"); - } else { - printf("dad"); - } - printf(")"); - } - break; - } - } - } - } - if (!ens160.setMode(ENS160_OPMODE_IDLE)) { - printf("E3"); - } - } - } - - } while (wait(1000) == EOF); - - } else if (subtest == 1 && mode == I2c::Master) { - if (!master.begin(0x01)) { - printf("E1"); - return -1; - } - do { - uint8_t buffer[1]; - // read poti - ADCSRA |= (1 << ADSC); // start ADC - while (ADCSRA & (1 << ADSC)) {} // wait for result - buffer[0] = ADCH; - printf("\n write 0x%02x", buffer[0]); - if (!master.write(buffer, 1)) { - printf(" -> ERROR"); - } - printf(", read "); - if (master.read(buffer, 1)) { - printf("0x%02x", buffer[0]); - } else { - printf(" -> ERROR"); - } - } while (wait(1000) == EOF); - master.end(); - - } else if (subtest == 1 && mode == I2c::Slave) { - if (!slave.begin(0x01, false)) { - printf("E1"); - return -1; - } - do { - int fromMaster = slave.read(); - if (fromMaster != EOF) { - ADCSRA |= (1 << ADSC); // start ADC - while (ADCSRA & (1 << ADSC)) {} // wait for result - slave.write(ADCH); - printf("\n => from master: 0x%02x -> to master: 0x%02x", fromMaster, ADCH); - } - } while (wait(0) == EOF); - slave.end(); - - } else { - printf("end"); - return -1; - } - wait(500); - return 0; -} - -void I2c::handleTwiIrq () { - if (mode == I2c::Slave) { - DDRD |= (1 << PD7); - PORTD |= (1 << PD7); - slave.handleTWIIsr(); - PORTD &= ~(1 << PD7); - } else { - TWCR |= (1 << TWINT); // clear Interrupt Request - } -} - -uint16_t I2c::startRead (uint8_t address) { - TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // send START condition - while (!(TWCR & (1 << TWINT))) {}; // wait until last action done - uint8_t sr = TWSR & 0xf8; - if (sr != 0x08 && sr != 0x10) { - return 0x0100 | sr; - } - - TWDR = (address << 1) | 0x01; // address + READ (R/W = 1) - TWCR = (1 << TWINT) | (1 << TWEN); // send address/RW - while (!(TWCR & (1 << TWINT))) {}; // wait until last action done - sr = TWSR & 0xf8; - if (sr != 0x40) { - return 0x0200 | sr; - } - return 0; -} - -uint16_t I2c::startWrite (uint8_t address) { - TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // send START condition - while (!(TWCR & (1 << TWINT))) {}; // wait until last action done - uint8_t sr = TWSR & 0xf8; - if (sr != 0x08 && sr != 0x10) { - return 0x0300 | sr; - } - - TWDR = (address << 1) | 0x00; // address + WRITE (R/W = 0) - TWCR = (1 << TWINT) | (1 << TWEN); // send address/RW - while (!(TWCR & (1 << TWINT))) {}; // wait until last action done - sr = TWSR & 0xf8; - if (sr != 0x18) { - return 0x0400 | sr; - } - return 0; -} - -void I2c::stop () { - TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); -} - -uint16_t I2c::writeData (uint8_t size, const uint8_t *data) { - while (size-- > 0) { - TWDR = *data++; - TWCR = (1 << TWINT) | (1 << TWEN); // send data byte - while (!(TWCR & (1 << TWINT))) {}; // wait until last action done - uint8_t sr = TWSR & 0xf8; - if (sr != 0x28) { - return 0x0500 | sr; - } - } - return 0; -} - -uint16_t I2c::writeByte (uint8_t data) { - return writeData(1, &data); -} - -uint16_t I2c::readData (uint8_t size, uint8_t *data) { - while (size-- > 0) { - if (size > 0) { - TWCR = (1 << TWEA) | (1 << TWINT) | (1 << TWEN); // read data byte with ACK enabled - } else { - TWCR = (1 << TWINT) | (1 << TWEN); // read data byte with ACK disabled - } - while (!(TWCR & (1 << TWINT))) {}; // wait until last action done - uint8_t sr = TWSR & 0xf8; - if ((size > 0 && sr != 0x50) || (size == 0 && sr != 0x58)) { - return 0x0600 | sr; - } - *data++ = TWDR; - } - return 0; -} - -int32_t I2c::compensateBm280T (int32_t adcT) { - // int32_t var1, var2, t; - // var1 = ((((adcT >> 3) - ((int32_t)bm280.digT[0] << 1))) * ((int32_t)bm280.digT[1])) >> 11; - // var2 = (((((adcT >> 4) - ((int32_t)bm280.digT[0])) * ((adcT >> 4) - ((int32_t)bm280.digT[0]))) >> 12) * ((int32_t)bm280.digT[2])) >> 14; - // bm280.tFine = var1 + var2; - // t = (bm280.tFine * 5 + 128) >> 8; - // return t; - return -1; -} \ No newline at end of file diff --git a/software/test_2024-07-23/src/units/i2c.hpp b/software/test_2024-07-23/src/units/i2c.hpp deleted file mode 100644 index b439038..0000000 --- a/software/test_2024-07-23/src/units/i2c.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef I2C_HPP -#define I2C_HPP - -#include -#include "../main.hpp" -#include "../adafruit/bme280.h" -#include "../adafruit/ens160.h" -#include "../i2cmaster.hpp" -#include "../i2cslave.hpp" - - -class I2c : public TestUnit { - public: - typedef enum I2cMode { SparkFunEnvCombo, Master, Slave } I2cMode; - - private: - I2cMode mode; - Adafruit_BME280 bm280; - ScioSense_ENS160 ens160; - I2cMaster master; - I2cSlave slave; - - public: - bool enabled; - - public: - I2c (I2cMode mode) { enabled = false; this->mode = mode; } - void tick1ms () { master.tick1ms(); slave.tick1ms(); } - virtual void cleanup (); - virtual int8_t run (uint8_t subtest); - virtual const char *getName (); - void handleTwiIrq (); - uint16_t startRead (uint8_t address); - uint16_t startWrite (uint8_t address); - void stop (); - uint16_t writeByte (uint8_t data); - uint16_t writeData (uint8_t size, const uint8_t *data); - uint16_t readData (uint8_t size, uint8_t *data); - int32_t compensateBm280T (int32_t adcT); -}; - -#endif \ No newline at end of file diff --git a/software/test_2024-07-23/src/units/ieee485.cpp b/software/test_2024-07-23/src/units/ieee485.cpp deleted file mode 100644 index 4fba45f..0000000 --- a/software/test_2024-07-23/src/units/ieee485.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include -#include -#include -#include - -#include "ieee485.hpp" -#include "../main.hpp" - -// PB0 ... nRE .. Read enable -// PB1 ... DE .. Data enable - -#define SET_nRE (PORTB |= (1 << PB0)) -#define CLR_nRE (PORTB &= ~(1 << PB0)) -#define SET_DE (PORTB |= (1 << PB1)) -#define CLR_DE (PORTB &= ~(1 << PB1)) - - -void Ieee485::cleanup () { - enabled = 0; - - ADMUX = 0; - ADCSRA = 0; - - UCSR1A = 0; - UCSR1B = 0; - UCSR1C = 0; - UBRR1H = 0; - UBRR1L = 0; - PORTD &= ~(1 << PD2); - DDRB &= ~((1 << PB1) | (1 << PB0)); - PORTB &= ~((1 << PB1) | (1 << PB0)); -} - -int8_t Ieee485::run (uint8_t subtest) { - if (subtest == 0) { - // Poti - ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V - ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128 - - // Modbus - SET_nRE; - CLR_DE; - DDRB |= (1 << PB1) | (1 << PB0); - - // UART1 interface on Nano-644 - PORTD |= (1 << PD2); // enable RxD1 pullup - UCSR1A = (1 << U2X1); - UCSR1B = (1 << RXCIE1) | (1 << RXEN1) | (1 < send Byte 0x%02x", ADCH); - int b; - ATOMIC_BLOCK(ATOMIC_FORCEON) { - b = receivedByte; - receivedByte = -1; - } - if (b >= 0) { - printf("\n => receive Byte: 0x%02x", b); - } - } - - } else { - printf("end"); - return -1; - } - - return 0; -} - -void Ieee485::handleRxByte (uint8_t b) { - receivedByte = b; -} diff --git a/software/test_2024-07-23/src/units/ieee485.hpp b/software/test_2024-07-23/src/units/ieee485.hpp deleted file mode 100644 index d72f337..0000000 --- a/software/test_2024-07-23/src/units/ieee485.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef IEEE485_HPP -#define IEEE485_HPP - -#include -#include "../main.hpp" - -class Ieee485 : public TestUnit { - public: - uint8_t enabled; - int16_t receivedByte; - - public: - Ieee485 () { enabled = 0; receivedByte = -1; } - virtual void cleanup (); - virtual int8_t run (uint8_t subtest); - virtual const char *getName () { return "IEEE485"; } - void handleRxByte (uint8_t); -}; - -#endif \ No newline at end of file diff --git a/software/test_2024-07-23/src/units/lcd.cpp b/software/test_2024-07-23/src/units/lcd.cpp deleted file mode 100644 index 0781b6a..0000000 --- a/software/test_2024-07-23/src/units/lcd.cpp +++ /dev/null @@ -1,224 +0,0 @@ -#include -#include -#include - -#include "lcd.hpp" -#include "../main.hpp" - -// PA3 ..... E --> LCD Enable (Verbindung via J25 erforderlich) -// PD6 ..... R/W --> Read/Write: Read=1, Write=0 -// PD7 ..... RS --> Register Select: Command=0, Data=1 -// PB7:0 ... Data --> Achtung von 5V LCD nicht lesen! - -// #define LCD_3V3 -#ifdef LCD_3V3 - #define DATA_PIN PINB -#endif - -#define DATA_PORT PORTB -#define DIR_DATA_PORT DDRB - -#define PORT_RS PD7 -#define SET_RS_PIN (PORTD |= (1 << PORT_RS)) -#define CLR_RS_PIN (PORTD &= ~(1 << PORT_RS)) -#define SET_DIR_RS (DDRD |= (1 << PORT_RS)) -#define CLR_DIR_RS (DDRD &= ~(1 << PORT_RS)) - -#define PORT_RW PD6 -#define SET_RW_PIN (PORTD |= (1 << PORT_RW)) -#define CLR_RW_PIN (PORTD &= ~(1 << PORT_RW)) -#define SET_DIR_RW (DDRD |= (1 << PORT_RW)) -#define CLR_DIR_RW (DDRD &= ~(1 << PORT_RW)) - -#define SET_E_PIN (PORTA |= (1 << PA3)) -#define CLR_E_PIN (PORTA &= ~(1 << PA3)) -#define SET_DIR_E (DDRA |= (1 << PA3)) -#define CLR_DIR_E (DDRA &= ~(1 << PA3)) - -// Befehle für das Display - -#define DISP_CLEAR 0b00000001 // Display clear -#define DISP_ON 0b00001111 // Display on -#define DISP_OFF 0b00001011 // Display off -#define CURSOR_ON 0b00001111 // Cursor on -#define CURSOR_OFF 0b00001101 // Cursor off -#define BLINK_ON 0b00001111 // Cursor Blink -#define BLINK_OFF 0b00001110 // Cursor No Blink - - -void Lcd::cleanup () { - DDRA &= ~(1 << PA3); - PORTA &= ~(1 << PA3); - DDRD &= ~((1 << DDD7) | (1 << DDD6)); - PORTD &= ~((1 << PORTD7) | (1 << PORTD6)); - PORTB = 0; - DDRB = 0; -} - -int8_t Lcd::run (uint8_t subtest) { - if (subtest == 0) { - // DDRA |= (1 << PA3); - // PORTA &= ~(1 << PA3); - // DDRD |= (1 << DDD7) | (1 << DDD6); - // PORTD &= ~((1 << PORTD7) | (1 << PORTD6)); - // PORTB = 0; - // DDRB = 0xff; - init(); - #ifdef LCD_3V3 - printf("init 3.3V LCD"); - #else - printf("init 5V LCD"); - #endif - - } else if (subtest == 1) { - for (uint8_t i = 0; i < 20 * 4; i++) { - char c = (char)(i + 32); - if (i % 20 == 0) { - setCursor(i / 20 + 1, 1); - } - writeData(c); - while (isBusy()) {}; - } - // setCursor(1, 1); - // writeString(" 1234567890<>,;.:-_#+"); - // setCursor(2, 1); - // writeString("abcdefghijklmnopqrst"); - // setCursor(3, 1); - // writeString("uvwxyzABCDEFGHIJKLMN"); - // setCursor(4, 1); - // writeString("OPQRSTUVWXYZ "); - printf("LCD beschrieben"); - while (wait(1) == EOF) { - } - - } else { - printf("end"); - return -1; - } - wait(500); - return 0; -} - -void Lcd::init () { - CLR_RW_PIN; // write - CLR_RS_PIN; // command - CLR_E_PIN; // E = 0 - - SET_DIR_RW; - SET_DIR_RS; - SET_DIR_E; - - DATA_PORT = 0; - DIR_DATA_PORT = 0xff; - _delay_ms(16); // min 15ms warten für Reset des Displays - - DATA_PORT = 0b00111011; // 8bit Modus, 5x7 Zeichen, Mehrzeilen Display - CLR_RW_PIN; // write - CLR_RS_PIN; // command - SET_E_PIN; // E = 1 (transfer start) - _delay_us(10); // min. 10us - CLR_E_PIN; // E = 0 (transfer end) - _delay_ms(5); // min. 4.1ms - - DATA_PORT = 0b00111011; // 8bit Modus, 5x7 Zeichen, Mehrzeilen Display - CLR_RW_PIN; // write - CLR_RS_PIN; // command - SET_E_PIN; // E = 1 (transfer start) - _delay_us(10); // min. 10us - CLR_E_PIN; // E = 0 (transfer end) - _delay_us(100); // min. 100us - - DATA_PORT = 0b00111011; // 8bit Modus, 5x7 Zeichen, Mehrzeilen Display - CLR_RW_PIN; // write - CLR_RS_PIN; // command - SET_E_PIN; // E = 1 (transfer start) - _delay_us(10); // min. 10us - CLR_E_PIN; // E = 0 (transfer end) - _delay_us(100); // min. 100us - - writeCommand(DISP_OFF); // Display aus - while(isBusy()) {}; - writeCommand(DISP_ON); // Display ein - while(isBusy()) {}; - writeCommand( BLINK_OFF & CURSOR_OFF); // Blink aus und Cursor aus - while(isBusy()) {}; - writeCommand(DISP_CLEAR);// Clear display - while(isBusy()) {}; -} - -uint8_t Lcd::isBusy () { - #ifdef LCD_3V3 - DIR_DATA_PORT = 0; - SET_RW_PIN; // read - CLR_RS_PIN; // command - SET_E_PIN; // E = 1 (transfer start) - _delay_us(10); - uint8_t busy = DATA_PIN & 0x80; // read bit 7 (busy bit) - CLR_E_PIN; // E = 0 (transfer end) - CLR_RW_PIN; - DIR_DATA_PORT = 0xff; - return busy != 0; - #else - _delay_us(200); - return 0; - #endif - -} - -void Lcd::writeCommand (uint8_t cmd) { - DATA_PORT = cmd; - CLR_RW_PIN; // write - CLR_RS_PIN; // command - SET_E_PIN; // E = 1 (transfer start) - _delay_us(10); // min. 10us - CLR_E_PIN; // E = 0 (transfer end) - _delay_us(10); // min. 10us - DATA_PORT = 0; -} - -void Lcd::setDDRamAddr (uint8_t address) { - DATA_PORT = address | 0x80; - CLR_RW_PIN; // write - CLR_RS_PIN; // command - SET_E_PIN; // E = 1 (transfer start) - _delay_us(10); // min. 10us - CLR_E_PIN; // E = 0 (transfer end) - _delay_us(10); // min. 10us - DATA_PORT = 0; -} - -void Lcd::writeString (const char *s) { - while (*s) { - writeData(*s++); - while (isBusy()) {}; - } -} - -void Lcd::writeData (uint8_t data) { - DATA_PORT = data; // Write data to port - SET_RS_PIN; // data - CLR_RW_PIN; // command - SET_E_PIN; // E = 1 (transfer start) - _delay_us(10); // min. 10us - CLR_E_PIN; // E = 0 (transfer end) - _delay_us(10); // min. 10us - CLR_RS_PIN; - DATA_PORT = 0; -} - -void Lcd::setCursor (uint8_t row, uint8_t column) { - uint8_t b; - if (column > 20) { - return; - } - switch (row) { - case 1: b = 0x00 + column - 1; break; - case 2: b = 0x40 + column - 1; break; - case 3: b = 0x14 + column - 1; break; - case 4: b = 0x54 + column - 1; break; - default: return; - } - setDDRamAddr(b); - while (isBusy()) {}; -} - diff --git a/software/test_2024-07-23/src/units/lcd.hpp b/software/test_2024-07-23/src/units/lcd.hpp deleted file mode 100644 index 11e7749..0000000 --- a/software/test_2024-07-23/src/units/lcd.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef LCD_HPP -#define LCD_HPP - -#include -#include "../main.hpp" - -class Lcd : public TestUnit { - public: - Lcd () {}; - virtual void cleanup (); - virtual int8_t run (uint8_t subtest); - virtual const char *getName () { return "Lcd"; } - - void init (); - uint8_t isBusy (); - void writeCommand (uint8_t); - void setDDRamAddr (uint8_t); - void writeString (const char *s); - void writeData (uint8_t); - void setCursor (uint8_t row, uint8_t column); -}; - -#endif \ No newline at end of file diff --git a/software/test_2024-07-23/src/units/led.cpp b/software/test_2024-07-23/src/units/led.cpp deleted file mode 100644 index fe48a40..0000000 --- a/software/test_2024-07-23/src/units/led.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include -#include -#include - -#include "led.hpp" -#include "../main.hpp" - -void Led::cleanup () { - DDRD &= ~((1 << DDD7) | (1 << DDD6) | (1 << DDD5) | (1 << DDD4)); - PORTD &= ~((1 << PORTD7) | (1 << PORTD6) | (1 << PORTD5) | (1 << PORTD4)); -} - -int8_t Led::run (uint8_t subtest) { - if (subtest == 0) { - DDRD |= (1 << DDD7) | (1 << DDD6) | (1 << DDD5) | (1 << DDD4); - PORTD &= ~((1 << PORTD7) | (1 << PORTD6) | (1 << PORTD5) | (1 << PORTD4)); - printf("init"); - - } else if (subtest <= 16) { - subtest = (subtest - 1) % 4; - PORTD &= ~((1 << PORTD7) | (1 << PORTD6) | (1 << PORTD5) | (1 << PORTD4)); - PORTD |= (1 << (subtest + 4)); - printf("Test LED PD%d", subtest + 4); - - } else { - printf("end"); - return -1; - } - wait(500); - return 0; -} - diff --git a/software/test_2024-07-23/src/units/led.hpp b/software/test_2024-07-23/src/units/led.hpp deleted file mode 100644 index a76983b..0000000 --- a/software/test_2024-07-23/src/units/led.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef LED_HPP -#define LED_HPP - -#include -#include "../main.hpp" - -class Led : public TestUnit { - public: - Led () {}; - virtual void cleanup (); - virtual int8_t run (uint8_t subtest); - virtual const char *getName () { return "Led"; } -}; - -#endif \ No newline at end of file diff --git a/software/test_2024-07-23/src/units/modbus.cpp b/software/test_2024-07-23/src/units/modbus.cpp deleted file mode 100644 index 28b1bca..0000000 --- a/software/test_2024-07-23/src/units/modbus.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include -#include -#include - -#include "modbus.hpp" -#include "../main.hpp" - -// PB0 ... nRE .. Read enable -// PB1 ... DE .. Data enable - -#define SET_nRE (PORTB |= (1 << PB0)) -#define CLR_nRE (PORTB &= ~(1 << PB0)) -#define SET_DE (PORTB |= (1 << PB1)) -#define CLR_DE (PORTB &= ~(1 << PB1)) - - -void Modbus::cleanup () { - enabled = 0; - UCSR1A = 0; - UCSR1B = 0; - UCSR1C = 0; - UBRR1H = 0; - UBRR1L = 0; - PORTD &= ~(1 << PD2); - DDRB &= ~((1 << PB1) | (1 << PB0)); - PORTB &= ~((1 << PB1) | (1 << PB0)); -} - -int8_t Modbus::run (uint8_t subtest) { - if (subtest == 0) { - SET_nRE; - CLR_DE; - DDRB |= (1 << PB1) | (1 << PB0); - - // UART1 interface on Nano-644 - PORTD |= (1 << PD2); // enable RxD1 pullup - UCSR1A = (1 << U2X1); - UCSR1B = (1 << RXCIE1) | (1 << RXEN1) | (1 <= 1 && subtest <= 4) { - uint8_t nre, de, b; - switch (subtest) { - case 1: nre = 1; de = 0; b = 0x01; break; - case 2: nre = 1; de = 1; b = 0x8e; break; - case 3: nre = 0; de = 0; b = 0x55; break; - case 4: nre = 0; de = 1; b = 0xaa; break; - default: return -1; - } - printf(" DE=%u, nRE=%u send 0x%02x... ", de, nre, b); - if (nre) { - SET_nRE; - } else { - CLR_nRE; - } - if (de) { - SET_DE; - } else { - CLR_DE; - } - _delay_us(100); - receivedBytes = 0; - UDR1 = b; - _delay_ms(1); - if (receivedBytes > 0) { - printf("0x%02x received", received[0]); - receivedBytes = 0; - } else { - printf("no byte received"); - } - printf(" ... press key to proceed"); - while (wait(0xffffffff) == EOF) {} - CLR_DE; - SET_nRE; - - } else if (subtest == 5) { - static uint8_t frame[] = { 0x01, 0x04, 0x00, 0x00, 0x00, 0x02, 0x71, 0xcb }; - printf("Modbus: lese Spannung von Eastron SDM-230 (Einphasenzähler)"); - SET_DE; - CLR_nRE; - _delay_us(100); - do { - SET_DE; - receivedBytes = 0; - for (uint8_t i = 0; i < sizeof(frame); i++) { - UCSR1A |= (1 << TXC1); - UDR1 = frame[i]; - while ((UCSR1A & (1 < Sending:"); - for (uint8_t i = 0; i < sizeof(frame); i++) { - printf(" 0x%02x", frame[i]); - } - int k = wait(100); - - printf("\n RxD1:"); - if (receivedBytes == 0) { - printf("?"); - } else { - for (uint8_t i = 0; i < receivedBytes; i++) { - if (i == sizeof(frame)) { - printf(" "); - } - printf(" 0x%02x", received[i]); - } - } - if (receivedBytes >= 16) { - union { - uint8_t b[4]; - float value; - } f; - f.b[0] = received[14]; - f.b[1] = received[13]; - f.b[2] = received[12]; - f.b[3] = received[11]; - printf(" -> %4.8fV\n", (double)f.value); - } - if (k != EOF) { - break; - } - - } while (wait(1000) == EOF); - - } else { - printf("end"); - return -1; - } - wait(500); - return 0; -} - -void Modbus::handleRxByte (uint8_t b) { - if (receivedBytes < sizeof(received)) { - received[receivedBytes++] = b; - } -} diff --git a/software/test_2024-07-23/src/units/modbus.hpp b/software/test_2024-07-23/src/units/modbus.hpp deleted file mode 100644 index 14580f8..0000000 --- a/software/test_2024-07-23/src/units/modbus.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef MODBUS_HPP -#define MODBUS_HPP - -#include -#include "../main.hpp" - -class Modbus : public TestUnit { - public: - uint8_t enabled; - uint8_t receivedBytes; - uint8_t received[16]; - - - public: - Modbus () { enabled = 0; receivedBytes = 0; } - virtual void cleanup (); - virtual int8_t run (uint8_t subtest); - virtual const char *getName () { return "Modbus"; } - void handleRxByte (uint8_t); -}; - -#endif \ No newline at end of file diff --git a/software/test_2024-07-23/src/units/motor.cpp b/software/test_2024-07-23/src/units/motor.cpp deleted file mode 100644 index be6abd8..0000000 --- a/software/test_2024-07-23/src/units/motor.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include -#include -#include - -#include "motor.hpp" -#include "../main.hpp" - -void Motor::cleanup () { - ADMUX = 0; - ADCSRA = 0; - TCCR0A = 0; - TCCR0B = 0; - DDRB &= ~((1 << PB4) | (1 << PB3)); - PORTA &= ~(1 << PORTA3); - enabled = 0; -} - -int8_t Motor::run (uint8_t subtest) { - switch (subtest) { - case 0: { - printf("init"); - ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V - ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128 - TCCR0A = (1 << COM0A1) | (1 << WGM01) | (1 << WGM00); // Fast PWM on OC0A - // TCCR0B = (1 << CS02) | ( 1 << CS00); // f = 12 MHz / 1024 = 11,71875 kHz -> fPWM=45Hz - TCCR0B = (1 << CS02); // f = 12 MHz / 256 = 46,875 kHz -> fPWM=183,1Hz - DDRB |= (1 << PB4) | (1 << PB3); // Motor enable - PORTA |= (1 << PORTA3); // push button for Motor enable control - enabled = 1; - return 0; - } - - case 1: { - printf("\n"); - while (wait(10) == EOF) { - - ADCSRA |= (1 << ADSC); // start ADC - while (ADCSRA & (1 << ADSC)) {} // wait for result - printf("\r => ADC0=%3d", ADCH); - - ADMUX = (1 << ADLAR) | (1 << REFS1) | (1 << REFS0) | 2; // ADC2, VREF=2.5V - - uint8_t dutyCycle = 0xff - ADCH; - if (dutyCycle <= 1) { - dutyCycle = 0; - } else if (dutyCycle > 254) { - dutyCycle = 255; - } - OCR0A = dutyCycle; - printf(" PWM/OC0A=%3d", dutyCycle); - - ADCSRA |= (1 << ADSC); // start ADC - while (ADCSRA & (1 << ADSC)) {} // wait for result - printf(" ADC2=%3d", ADCH); - ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V - - if ((PINA & (1<< PA3)) == 0) { - PORTB &= ~(1 << PORTB4); - } else { - PORTB |= (1 << PORTB4); - } - - uint16_t timer; - ATOMIC_BLOCK(ATOMIC_FORCEON) { - timer = rpmTimer; - } - float rpm = 60.0 / (float)timer / 0.0001; - if (timer > 0) { - printf(" n= %4d U/min", (int)rpm); - } else { - printf(" no rotation"); - } - - } - return 0; - } - - case 2: { - printf("\r => end"); - break; - } - } - - return -1; -} - -void Motor::tick100us () { - static uint16_t timerH = 0; - static uint16_t timerL = 0; - static uint8_t pinb = 0; - if ((PINB & 0x01) && PINB != pinb && timerL > 10) { - rpmTimer = timerL + timerH; - timerL = 0; - timerH = 0; - } - if (PINB & 0x01) { - timerH = timerH < 0x4000 ? timerH + 1 : 0x4000; - } else { - timerL = timerL < 0x4000 ? timerL + 1 : 0x4000; - } - if (timerH >= 0x4000 || timerL >= 0x4000) { - rpmTimer = 0; // no ratation detected - } - pinb = PINB; -} - - diff --git a/software/test_2024-07-23/src/units/motor.hpp b/software/test_2024-07-23/src/units/motor.hpp deleted file mode 100644 index 35fcf9c..0000000 --- a/software/test_2024-07-23/src/units/motor.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef MOTOR_HPP -#define MOTOR_HPP - -#include -#include "../main.hpp" - -class Motor : public TestUnit { - public: - uint8_t enabled; - uint16_t rpmTimer; - - public: - Motor () { enabled = 0; rpmTimer = 0; }; - virtual void cleanup (); - virtual int8_t run (uint8_t subtest); - virtual const char *getName () { return "Motor"; } - void tick100us (); -}; - -#endif \ No newline at end of file diff --git a/software/test_2024-07-23/src/units/portexp.cpp b/software/test_2024-07-23/src/units/portexp.cpp deleted file mode 100644 index 5ed192e..0000000 --- a/software/test_2024-07-23/src/units/portexp.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include -#include -#include - -#include "portexp.hpp" -#include "../main.hpp" - -// Port-Expander MCP23S17 - -// SN-Print Stecker IO16 -// MCP23S17 | IO16 (MEGA2560) | Ampel-Print | | MCP23S17 | IO16 (MEGA2560) | Ampel-Print | -// ------------------------------------------ -------------------------------------------- -// GPA0 | IO16O7 (PA7) | Taster RU | | GPB0 | IO16U7 (PC7) | Taster LU | -// GPA1 | IO16O6 (PA6) | Taster RO | | GPB1 | IO16U6 (PC6) | Taster LO | -// GPA2 | IO16O5 (PA5) | U-Gruen | | GPB2 | IO16U5 (PC5) | R-Gruen | -// GPA3 | IO16O4 (PA4) | U-Gelb | | GPB3 | IO16U4 (PC4) | R-Gelb | -// GPA4 | IO16O3 (PA3) | U-Rot | | GPB4 | IO16U3 (PC3) | R-Rot | -// GPA5 | IO16O2 (PA2) | L-Gruen | | GPB5 | IO16U2 (PC2) | O-Gruen | -// GPA6 | IO16O1 (PA1) | L-Gelb | | GPB6 | IO16U1 (PC1) | O-Gelb | -// GPA7 | IO16O0 (PA0) | L-Rot | | GPB7 | IO16U0 (PC0) | O-Rot | - - -void PortExp::cleanup () { - DDRB &= ~(1 << PB6); // // SPI MISO (=PB6) - DDRB &= ~((1 << PB7) | (1 << PB5)); // SPI SCK (=PB7) and SPI MOSI (=PB5) - DDRA &= ~(1 << PA7); - PORTA &= ~(1 << PA7); // SPI nCS - SPCR = 0; - -} - -int8_t writeByte (uint8_t addr, uint8_t b) { - - PORTA &= ~(1 << PA7); // SPI nCS 1 -> 0 - - SPDR = 0x40; // WRITE BYTE - while ((SPSR & (1 << SPIF)) == 0) {} - if (SPDR != 0) { - printf("E1"); - PORTA |= (1 << PA7); // SPI nCS 0 -> 1 - return -1; - } - - SPDR = addr; // register address - while ((SPSR & (1 << SPIF)) == 0) {} - if (SPDR != 0) { - printf("E2"); - PORTA |= (1 << PA7); // SPI nCS 0 -> 1 - return -1; - } - - SPDR = b; // value - while ((SPSR & (1 << SPIF)) == 0) {} - if (SPDR != 0) { - printf("E3"); - PORTA |= (1 << PA7); // SPI nCS 0 -> 1 - return -1; - } - - PORTA |= (1 << PA7); // SPI nCS 0 -> 1 - - _delay_us(5); - return 0; -} - -int8_t PortExp::run (uint8_t subtest) { - if (subtest == 0) { - PRR &= (1 << PRSPI); - PORTA |= (1 << PA7); - DDRA |= (1 << PA7); // SPI nCS - // PORTB/DDRB must be configured before SPCR !! - PORTB |= (1 << PB4); // nSS must be HIGH, otherwise SPI master will not become active!! - DDRB |= (1 << PB7) | (1 << PB5) | (1 << PB4); // SPI SCK (=PB7) and SPI MOSI (=PB5) - - // SPCR |= (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0); // SPI enable , Master, f=12MHz/128=93,75kHz - SPCR = (1 << SPE) | (1 << MSTR); // SPI enable , Master, f=12MHz/4 = 3MHz - printf("init"); - - } else if (subtest == 1) { - while (wait(500) == EOF) { - printf("\n => start ..."); - for (uint8_t i = 0; i < 8; i++) { - writeByte(0, ~(1 << i)); // IODIRA (Bank = 0) - // writeByte(0, 0x00); // IODIRA (Bank = 0) - all output - writeByte(0x12, (1 << i)); // GPIOA (Bank = 0) - printf("\n Bank0 - GPA%d = 1", i); - wait(200); - writeByte(0x12, 0); // GPIOA (Bank = 0) - printf("\n Bank0 - GPA%d = 0", i); - writeByte(0, 0xff); // IODIRA (Bank = 0) - wait(200); - } - for (uint8_t i = 0; i < 8; i++) { - writeByte(1, ~(1 << i)); // IODIRB (Bank = 0) - // writeByte(1, 0x00); // IODIRB (Bank = 0) - all output - writeByte(0x13, (1 << i)); // GPIOB (Bank = 0) - printf("\n Bank0 - GPB%d = 1", i); - wait(200); - writeByte(0x13, 0); // GPIOB (Bank = 0) - printf("\n Bank0 - GPB%d = 0", i); - writeByte(1, 0xff); // IODIRB (Bank = 0) - wait(200); - } - } - - } else { - printf("end"); - return -1; - } - wait(500); - return 0; -} - diff --git a/software/test_2024-07-23/src/units/portexp.hpp b/software/test_2024-07-23/src/units/portexp.hpp deleted file mode 100644 index 7db858a..0000000 --- a/software/test_2024-07-23/src/units/portexp.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef PORTEXP_HPP -#define PORTEXP_HPP - -#include -#include "../main.hpp" - -class PortExp : public TestUnit { - public: - PortExp () {}; - virtual void cleanup (); - virtual int8_t run (uint8_t subtest); - virtual const char *getName () { return "PortExp"; } -}; - -#endif \ No newline at end of file diff --git a/software/test_2024-07-23/src/units/poti.cpp b/software/test_2024-07-23/src/units/poti.cpp deleted file mode 100644 index 5f8fcdb..0000000 --- a/software/test_2024-07-23/src/units/poti.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include - -#include "poti.hpp" -#include "../main.hpp" - -void Poti::cleanup () { - ADMUX = 0; - ADCSRA = 0; -} - -int8_t Poti::run (uint8_t subtest) { - switch (subtest) { - case 0: { - printf("init"); - ADMUX = (1 << REFS0); // ADC0, VREF=AVCC=3.3V - ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128 - return 0; - } - - case 1: { - printf("\n"); - while (wait(10) == EOF) { - printf("\r => Measure ADC0: "); - ADCSRA |= (1 << ADSC); // start ADC - while (ADCSRA & (1 << ADSC)) {} // wait for result - printf("%4d (0x%03x)", ADC, ADC); - } - return 0; - } - - case 2: { - printf("\r => ADC end%20s", " "); - break; - } - } - - return -1; -} - - diff --git a/software/test_2024-07-23/src/units/poti.hpp b/software/test_2024-07-23/src/units/poti.hpp deleted file mode 100644 index df368e8..0000000 --- a/software/test_2024-07-23/src/units/poti.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef POTI_HPP -#define POTI_PP - -#include -#include "../main.hpp" - -class Poti : public TestUnit { - public: - Poti () {}; - virtual void cleanup (); - virtual int8_t run (uint8_t subtest); - virtual const char *getName () { return "Poti"; } -}; - -#endif \ No newline at end of file diff --git a/software/test_2024-07-23/src/units/r2r.cpp b/software/test_2024-07-23/src/units/r2r.cpp deleted file mode 100644 index 40b24e1..0000000 --- a/software/test_2024-07-23/src/units/r2r.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include - -#include "r2r.hpp" -#include "../main.hpp" - -void R2r::cleanup () { - ADMUX = 0; - ADCSRA = 0; -} - -int8_t R2r::run (uint8_t subtest) { - switch (subtest) { - case 0: { - printf("init"); - ADMUX = (1 << REFS0) | 2; // ADC2, VREF=AVCC=3.3V - ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128 - return 0; - } - - case 1: { - printf("\n"); - while (wait(10) == EOF) { - printf("\r => Measure ADC2: "); - ADCSRA |= (1 << ADSC); // start ADC - while (ADCSRA & (1 << ADSC)) {} // wait for result - printf("%4d (0x%03x)", ADC, ADC); - uint8_t sw = (ADC + 32) / 64; - printf(" SW9:6 = %d %d% d %d ", sw >> 3, (sw >> 2) & 0x01, (sw >> 1) & 0x01, sw & 0x01 ); - } - return 0; - } - - case 2: { - printf("\r => R2R end%20s", " "); - break; - } - } - - return -1; -} - - diff --git a/software/test_2024-07-23/src/units/r2r.hpp b/software/test_2024-07-23/src/units/r2r.hpp deleted file mode 100644 index 974c7a7..0000000 --- a/software/test_2024-07-23/src/units/r2r.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef R2R_HPP -#define R2R_PP - -#include -#include "../main.hpp" - -class R2r : public TestUnit { - public: - R2r () {}; - virtual void cleanup (); - virtual int8_t run (uint8_t subtest); - virtual const char *getName () { return "R2R"; } -}; - -#endif \ No newline at end of file diff --git a/software/test_2024-07-23/src/units/rgb.cpp b/software/test_2024-07-23/src/units/rgb.cpp deleted file mode 100644 index 200bffd..0000000 --- a/software/test_2024-07-23/src/units/rgb.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include -#include - -#include "rgb.hpp" -#include "../main.hpp" - -void Rgb::cleanup () { - PORTB &= ~((1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0)); - DDRB &= ~((1 << DDB2) | (1 << DDB1) | (1 << DDB0)); -} - -int8_t Rgb::run (uint8_t subtest) { - switch (subtest) { - case 0: { - DDRB |= (1 << DDB2) | (1 << DDB1) | (1 << DDB0); - PORTB |= (1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0); // all OFF - printf("init"); - return 0; - } - - case 1: { - PORTB &= ~(1 << PORTB0); // ON - printf("Red"); - wait(2000); - return 0; - } - - case 2: { - PORTB |= (1 << PORTB0); - PORTB &= ~(1 << PORTB1); // ON - printf("Green"); - wait(2000); - return 0; - } - - case 3: { - PORTB |= (1 << PORTB1); - PORTB &= ~(1 << PORTB2); // ON - printf("Blue"); - wait(2000); - return 0; - } - - case 4: { - PORTB &= ~((1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0)); - printf("All"); - wait(2000); - return 0; - } - - case 5: { - printf("end"); - break; - } - } - - return -1; -} - - diff --git a/software/test_2024-07-23/src/units/rgb.hpp b/software/test_2024-07-23/src/units/rgb.hpp deleted file mode 100644 index 2fa8a72..0000000 --- a/software/test_2024-07-23/src/units/rgb.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef RGB_HPP -#define RGB_PP - -#include -#include "../main.hpp" - -class Rgb : public TestUnit { - public: - Rgb () {}; - virtual void cleanup (); - virtual int8_t run (uint8_t subtest); - virtual const char *getName () { return "Rgb"; } -}; - -#endif \ No newline at end of file diff --git a/software/test_2024-07-23/src/units/seg7.cpp b/software/test_2024-07-23/src/units/seg7.cpp deleted file mode 100644 index d2fdbb9..0000000 --- a/software/test_2024-07-23/src/units/seg7.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include -#include - -#include "seg7.hpp" -#include "../main.hpp" - -// PA0 Cathode Char 1 -// PA1 Cathode Char 2 -// PA2 Cathode Char 3 -// PA3 Cathode Char 4 - -// PB0 Anode Segment A -// PB1 Anode Segment B -// PB2 Anode Segment C -// PB3 Anode Segment D -// PB4 Anode Segment E -// PB5 Anode Segment F -// PB6 Anode Segment G -// PB7 Anode DP - -// PD5 nOE (Output Enable) for all LEDs -// PD6 Anode L1:2 -// PD7 Anode L3 - -const char *segName[] = { "A", "B", "C", "D", "E", "F", "G", "DP" }; - -void Seg7::cleanup () { - PORTA &= ~((1 < -#include "../main.hpp" - -class Seg7 : public TestUnit { - public: - Seg7 () {}; - virtual void cleanup (); - virtual int8_t run (uint8_t subtest); - virtual const char *getName () { return "Seg7"; } -}; - -#endif \ No newline at end of file diff --git a/software/test_2024-07-23/src/units/switch.cpp b/software/test_2024-07-23/src/units/switch.cpp deleted file mode 100644 index 33d483c..0000000 --- a/software/test_2024-07-23/src/units/switch.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include -#include - -#include "switch.hpp" -#include "../main.hpp" - -void Switch::cleanup () { - PORTA &= ~((1 << PORTA3) | (1 << PORTA2) | (1 << PORTA1) | (1 << PORTA0)); -} - - -int8_t Switch::run (uint8_t subtest) { - if (subtest == 0) { - DDRA &= ~((1 << DDA3) | (1 << DDA2) | (1 << DDA1) | (1 << DDA0)); - // enable internal pullup resistor - PORTA |= (1 << PORTA3) | (1 << PORTA2) | (1 << PORTA1) | (1 << PORTA0); - wait(10); - return 0; - - } else if (subtest <= 17) { - uint8_t bit = (subtest - 1) / 4; - switch ((subtest - 1) % 4) { - case 0: case 2: { - if ((PINA & (1 << bit)) == 0) { - printf("Release SW%d (PA%d) ", bit + 1, bit); - while ((PINA & (1 << bit)) == 0 && wait(0) == EOF) {} - wait(10); - } - return 0; - } - - case 1: { - if ((PINA & (1 << bit)) != 0) { - printf("Press SW%d (PA%d) ", bit + 1, bit); - while ((PINA & (1 << bit)) != 0 && wait(0) == EOF) {} - wait(10); - } - return 0; - } - - case 3: { - printf("end"); - return 0; - } - } - - } - - return -1; -} diff --git a/software/test_2024-07-23/src/units/switch.hpp b/software/test_2024-07-23/src/units/switch.hpp deleted file mode 100644 index 8bdcd74..0000000 --- a/software/test_2024-07-23/src/units/switch.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef SWITCH_HPP -#define SWITCH_HPP - -#include -#include "../main.hpp" - -class Switch : public TestUnit { - public: - Switch () {}; - virtual void cleanup (); - virtual int8_t run (uint8_t subtest); - virtual const char *getName () { return "Switch"; } -}; - -#endif \ No newline at end of file diff --git a/software/test_2024-07-23/src/units/uart1.cpp b/software/test_2024-07-23/src/units/uart1.cpp deleted file mode 100644 index 7ef9504..0000000 --- a/software/test_2024-07-23/src/units/uart1.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include - -#include "uart1.hpp" -#include "../main.hpp" - -int uart1_putchar(char c, FILE *stream) { - if (c == '\n') { - uart1_putchar('\r', stream); - } - loop_until_bit_is_set(UCSR1A, UDRE1); - UDR1 = c; - return 0; -} - -static FILE mystderr = { 0, 0, _FDEV_SETUP_WRITE , 0, 0, uart1_putchar, NULL, 0 }; - -void Uart1::cleanup () { - enabled = 0; - UCSR1A = 0; - UCSR1B = 0; - UCSR1C = 0; - UBRR1H = 0; - UBRR1L = 0; - stderr = NULL; -} - -int8_t Uart1::run (uint8_t subtest) { - if (subtest == 0) { - // UART1 interface on Nano-644 - PORTD |= (1 << PD2); // enable RxD1 pullup - UCSR1A = (1 << U2X1); - UCSR1B = (1 << RXCIE1) | (1 << RXEN1) | (1 < send text via UART1 now..."); - fprintf(stderr, "Hello UART1, ECHO-Modus active\n"); - } while (wait(5000) == EOF); - - } else { - printf("end"); - return -1; - } - wait(500); - return 0; -} - -void Uart1::handleRxByte (uint8_t b) { - uart1_putchar(b, stderr); -} diff --git a/software/test_2024-07-23/src/units/uart1.hpp b/software/test_2024-07-23/src/units/uart1.hpp deleted file mode 100644 index 23c9b7f..0000000 --- a/software/test_2024-07-23/src/units/uart1.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef UART1_HPP -#define UART1_HPP - -#include -#include "../main.hpp" - -class Uart1 : public TestUnit { - public: - uint8_t enabled; - - public: - Uart1 () { enabled = 0; } - virtual void cleanup (); - virtual int8_t run (uint8_t subtest); - virtual const char *getName () { return "Uart1"; } - void handleRxByte (uint8_t); -}; - -#endif \ No newline at end of file