Просмотр исходного кода

First developments on MAX31855. Added support for SPI interface

Victor 3 лет назад
Родитель
Сommit
a5028170a9
9 измененных файлов с 341 добавлено и 11 удалено
  1. 22 4
      Sensors.c
  2. 2 1
      Sensors.h
  3. 80 0
      interfaces/SPISensor.c
  4. 66 0
      interfaces/SPISensor.h
  5. 91 0
      sensors/MAX31855.c
  6. 65 0
      sensors/MAX31855.h
  7. 1 1
      views/General_view.c
  8. 12 3
      views/SensorEdit_view.c
  9. 2 2
      views/SensorsList_view.c

+ 22 - 4
Sensors.c

@@ -66,9 +66,14 @@ const Interface ONE_WIRE = {
     .allocator = unitemp_onewire_sensor_alloc,
     .mem_releaser = unitemp_onewire_sensor_free,
     .updater = unitemp_onewire_sensor_update};
+const Interface SPI = {
+    .name = "SPI",
+    .allocator = unitemp_spi_sensor_alloc,
+    .mem_releaser = unitemp_spi_sensor_free,
+    .updater = unitemp_spi_sensor_update};
 
 //Перечень интерфейсов подключения
-//static const Interface* interfaces[] = {&SINGLE_WIRE, &I2C, &ONE_WIRE};
+//static const Interface* interfaces[] = {&SINGLE_WIRE, &I2C, &ONE_WIRE, &SPI};
 //Перечень датчиков
 static const SensorType* sensorTypes[] = {
     &DHT11,
@@ -87,7 +92,8 @@ static const SensorType* sensorTypes[] = {
     &HDC1080,
     &BMP180,
     &BMP280,
-    &BME280};
+    &BME280,
+    &MAX31855};
 
 const SensorType* unitemp_sensors_getTypeFromInt(uint8_t index) {
     if(index > SENSOR_TYPES_COUNT) return NULL;
@@ -166,7 +172,7 @@ uint8_t unitemp_gpio_getAviablePortsCount(const Interface* interface, const GPIO
         }
 
         //Проверка для single wire
-        if(interface == &SINGLE_WIRE) {
+        if(interface == &SINGLE_WIRE || interface == &SPI) {
             if(gpio_interfaces_list[i] == NULL || (unitemp_gpio_getFromIndex(i) == extraport)) {
                 aviable_ports_count++;
             }
@@ -205,6 +211,13 @@ const GPIO*
             return NULL;
         }
     }
+    if(interface == &SPI) {
+        if(!((gpio_interfaces_list[0] == NULL || gpio_interfaces_list[0] == &SPI) &&
+             (gpio_interfaces_list[1] == NULL || gpio_interfaces_list[1] == &SPI) &&
+             (gpio_interfaces_list[3] == NULL || gpio_interfaces_list[3] == &SPI))) {
+            return NULL;
+        }
+    }
 
     uint8_t aviable_index = 0;
     for(uint8_t i = 0; i < GPIO_ITEMS; i++) {
@@ -222,7 +235,7 @@ const GPIO*
             }
         }
         //Проверка для single wire
-        if(interface == &SINGLE_WIRE) {
+        if(interface == &SINGLE_WIRE || interface == &SPI) {
             if(gpio_interfaces_list[i] == NULL || unitemp_gpio_getFromIndex(i) == extraport) {
                 if(aviable_index == index) {
                     return unitemp_gpio_getFromIndex(i);
@@ -435,6 +448,11 @@ bool unitemp_sensors_save(void) {
             stream_write_format(
                 app->file_stream, "%d\n", unitemp_singlewire_sensorGetGPIO(sensor)->num);
         }
+        if(sensor->type->interface == &SPI) {
+            uint8_t gpio_num = ((SPISensor*)sensor->instance)->CS_pin->num;
+            stream_write_format(app->file_stream, "%d\n", gpio_num);
+        }
+
         if(sensor->type->interface == &I2C) {
             stream_write_format(
                 app->file_stream, "%X\n", ((I2CSensor*)sensor->instance)->currentI2CAdr);

+ 2 - 1
Sensors.h

@@ -136,7 +136,7 @@ typedef struct Sensor {
 extern const Interface SINGLE_WIRE; //Собственный однопроводной протокол датчиков DHTXX и AM23XX
 extern const Interface ONE_WIRE; //Однопроводной протокол Dallas
 extern const Interface I2C; //I2C_2 (PC0, PC1)
-//extern const Interface SPI;
+extern const Interface SPI; //SPI_1 (MOSI - 2, MISO - 3, CS - 4, SCK - 5)
 
 /* ============================= Датчик(и) ============================= */
 /**
@@ -326,4 +326,5 @@ const GPIO*
 #include "./sensors/BMP180.h"
 #include "./sensors/HTU21x.h"
 #include "./sensors/HDC1080.h"
+#include "./sensors/MAX31855.h"
 #endif

+ 80 - 0
interfaces/SPISensor.c

@@ -0,0 +1,80 @@
+/*
+    Unitemp - Universal temperature reader
+    Copyright (C) 2022-2023  Victor Nikitchuk (https://github.com/quen0n)
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include <furi.h>
+#include <furi_hal.h>
+#include "SPISensor.h"
+
+static uint8_t sensors_count = 0;
+
+bool unitemp_spi_sensor_alloc(Sensor* sensor, char* args) {
+    if(args == NULL) return false;
+    SPISensor* instance = malloc(sizeof(SPISensor));
+    if(instance == NULL) {
+        FURI_LOG_E(APP_NAME, "Sensor %s instance allocation error", sensor->name);
+        return false;
+    }
+    sensor->instance = instance;
+
+    int gpio = 255;
+    sscanf(args, "%d", &gpio);
+
+    instance->CS_pin = unitemp_gpio_getFromInt(gpio);
+    instance->spi = &furi_hal_spi_bus_handle_external;
+
+    if(instance->CS_pin == NULL) {
+        FURI_LOG_E(APP_NAME, "Sensor %s GPIO setting error", sensor->name);
+        free(instance);
+        return false;
+    }
+
+    bool status = sensor->type->allocator(sensor, args);
+
+    //Блокировка портов GPIO
+    sensors_count++;
+    unitemp_gpio_lock(unitemp_gpio_getFromInt(2), &SPI);
+    unitemp_gpio_lock(unitemp_gpio_getFromInt(3), &SPI);
+    unitemp_gpio_lock(unitemp_gpio_getFromInt(5), &SPI);
+    return status;
+}
+
+bool unitemp_spi_sensor_free(Sensor* sensor) {
+    bool status = sensor->type->mem_releaser(sensor);
+    free(sensor->instance);
+    if(--sensors_count == 0) {
+        unitemp_gpio_unlock(unitemp_gpio_getFromInt(2));
+        unitemp_gpio_unlock(unitemp_gpio_getFromInt(3));
+        unitemp_gpio_unlock(unitemp_gpio_getFromInt(5));
+    }
+
+    return status;
+}
+
+bool unitemp_spi_sensor_init(Sensor* sensor) {
+    return sensor->type->initializer(sensor);
+}
+
+bool unitemp_spi_sensor_deinit(Sensor* sensor) {
+    UNUSED(sensor);
+
+    return true;
+}
+
+UnitempStatus unitemp_spi_sensor_update(Sensor* sensor) {
+    return sensor->type->updater(sensor);
+}

+ 66 - 0
interfaces/SPISensor.h

@@ -0,0 +1,66 @@
+/*
+    Unitemp - Universal temperature reader
+    Copyright (C) 2022-2023  Victor Nikitchuk (https://github.com/quen0n)
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef UNITEMP_SPI
+#define UNITEMP_SPI
+
+#include "../unitemp.h"
+#include <furi_hal_spi.h>
+
+//Структура SPI датчика
+typedef struct SPISensor {
+    //Указатель на интерфейс SPI
+    FuriHalSpiBusHandle* spi;
+    //Порт подключения CS
+    const GPIO* CS_pin;
+} SPISensor;
+
+/**
+ * @brief Выделение памяти для датчика с интерфейсом SPI
+ * @param sensor Указатель на датчик
+ * @param args Указатель на массив аргументов с параметрами датчика
+ * @return Истина если всё ок
+ */
+bool unitemp_spi_sensor_alloc(Sensor* sensor, char* args);
+
+/**
+ * @brief Высвобождение памяти инстанса датчика
+ * @param sensor Указатель на датчик
+ */
+bool unitemp_spi_sensor_free(Sensor* sensor);
+
+/**
+ * @brief Инициализации датчика с интерфейсом one wire
+ * @param sensor Указатель на датчик
+ * @return Истина если инициализация упспешная
+ */
+bool unitemp_spi_sensor_init(Sensor* sensor);
+
+/**
+ * @brief Деинициализация датчика
+ * @param sensor Указатель на датчик
+ */
+bool unitemp_spi_sensor_deinit(Sensor* sensor);
+
+/**
+ * @brief Обновить значение с датчка
+ * @param sensor Указатель на датчик
+ * @return Статус обновления
+ */
+UnitempStatus unitemp_spi_sensor_update(Sensor* sensor);
+
+#endif

+ 91 - 0
sensors/MAX31855.c

@@ -0,0 +1,91 @@
+/*
+    Unitemp - Universal temperature reader
+    Copyright (C) 2022-2023  Victor Nikitchuk (https://github.com/quen0n)
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#include "MAX31855.h"
+
+const SensorType MAX31855 = {
+    .typename = "MAX31855",
+    .altname = "MAX31855 (Thermocouple)",
+    .interface = &SPI,
+    .datatype = UT_TEMPERATURE,
+    .pollingInterval = 1000,
+    .allocator = unitemp_MAX31855_alloc,
+    .mem_releaser = unitemp_MAX31855_free,
+    .initializer = unitemp_MAX31855_init,
+    .deinitializer = unitemp_MAX31855_deinit,
+    .updater = unitemp_MAX31855_update};
+
+bool unitemp_MAX31855_alloc(Sensor* sensor, char* args) {
+    UNUSED(sensor);
+    UNUSED(args);
+    return true;
+}
+
+bool unitemp_MAX31855_free(Sensor* sensor) {
+    UNUSED(sensor);
+    return true;
+}
+
+bool unitemp_MAX31855_init(Sensor* sensor) {
+    SPISensor* instance = sensor->instance;
+    furi_hal_spi_bus_handle_init(instance->spi);
+    UNUSED(instance);
+    return true;
+}
+
+bool unitemp_MAX31855_deinit(Sensor* sensor) {
+    UNUSED(sensor);
+    return true;
+}
+
+UnitempStatus unitemp_MAX31855_update(Sensor* sensor) {
+    SPISensor* instance = sensor->instance;
+
+    furi_hal_spi_acquire(instance->spi);
+    furi_hal_gpio_write(instance->CS_pin->pin, false);
+
+    uint8_t buff[4] = {0};
+
+    furi_hal_spi_bus_rx(instance->spi, buff, 4, 0xFF);
+    furi_hal_spi_release(instance->spi);
+
+    uint32_t raw = (buff[0] << 24) | (buff[1] << 16) | (buff[2] << 8) | buff[3];
+
+    //Определение состояния термопары
+    uint8_t state = raw & 0b111;
+    //Обрыв
+    if(state == 0x01) {
+        UNITEMP_DEBUG("%s has thermocouple open circuit", sensor->name);
+        return UT_SENSORSTATUS_ERROR;
+    }
+    //Короткое замыкание к земле
+    if(state == 0x02) {
+        UNITEMP_DEBUG("%s has thermocouple short to GND", sensor->name);
+        return UT_SENSORSTATUS_ERROR;
+    }
+    //Короткое замыкание к питанию
+    if(state == 0x04) {
+        UNITEMP_DEBUG("%s has thermocouple short to VCC", sensor->name);
+        return UT_SENSORSTATUS_ERROR;
+    }
+
+    raw = (raw >> 16) & 0xFFFC;
+
+    sensor->temp = (int16_t)(raw) / 16.0f;
+
+    return UT_SENSORSTATUS_OK;
+}

+ 65 - 0
sensors/MAX31855.h

@@ -0,0 +1,65 @@
+/*
+    Unitemp - Universal temperature reader
+    Copyright (C) 2022-2023  Victor Nikitchuk (https://github.com/quen0n)
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef UNITEMP_MAX31855
+#define UNITEMP_MAX31855
+
+#include "../unitemp.h"
+#include "../Sensors.h"
+#include "../interfaces/SPISensor.h"
+
+extern const SensorType MAX31855;
+
+/**
+ * @brief Выделение памяти и установка начальных значений датчика MAX31855
+ *
+ * @param sensor Указатель на создаваемый датчик
+ * @return Истина при успехе
+ */
+bool unitemp_MAX31855_alloc(Sensor* sensor, char* args);
+
+/**
+ * @brief Инициализации датчика MAX31855
+ *
+ * @param sensor Указатель на датчик
+ * @return Истина если инициализация упспешная
+ */
+bool unitemp_MAX31855_init(Sensor* sensor);
+
+/**
+ * @brief Деинициализация датчика
+ *
+ * @param sensor Указатель на датчик
+ */
+bool unitemp_MAX31855_deinit(Sensor* sensor);
+
+/**
+ * @brief Обновление значений из датчика
+ *
+ * @param sensor Указатель на датчик
+ * @return Статус обновления
+ */
+UnitempStatus unitemp_MAX31855_update(Sensor* sensor);
+
+/**
+ * @brief Высвободить память датчика
+ *
+ * @param sensor Указатель на датчик
+ */
+bool unitemp_MAX31855_free(Sensor* sensor);
+
+#endif

+ 1 - 1
views/General_view.c

@@ -76,7 +76,7 @@ static void _draw_temperature(Canvas* canvas, Sensor* sensor, uint8_t x, uint8_t
         app->buff[0] = '-';
         offset = 1;
     }
-    snprintf((char*)(app->buff + offset), BUFF_SIZE, "%d", (int8_t)sensor->temp);
+    snprintf((char*)(app->buff + offset), BUFF_SIZE, "%d", (int16_t)sensor->temp);
     canvas_set_font(canvas, FontBigNumbers);
     canvas_draw_str_aligned(
         canvas,

+ 12 - 3
views/SensorEdit_view.c

@@ -193,6 +193,12 @@ static void _gpio_change_callback(VariableItem* item) {
             unitemp_gpio_getAviablePort(editable_sensor->type->interface, index, initial_gpio);
         variable_item_set_current_value_text(item, instance->gpio->name);
     }
+    if(editable_sensor->type->interface == &SPI) {
+        SPISensor* instance = editable_sensor->instance;
+        instance->CS_pin =
+            unitemp_gpio_getAviablePort(editable_sensor->type->interface, index, initial_gpio);
+        variable_item_set_current_value_text(item, instance->CS_pin->name);
+    }
     if(editable_sensor->type->interface == &ONE_WIRE) {
         OneWireSensor* instance = editable_sensor->instance;
         instance->bus->gpio =
@@ -296,12 +302,15 @@ void unitemp_SensorEdit_switch(Sensor* sensor) {
         offset_buff, OFFSET_BUFF_SIZE, "%+1.1f", (double)(editable_sensor->temp_offset / 10.0));
     variable_item_set_current_value_text(temp_offset_item, offset_buff);
 
-    //Порт подключения датчка (для one wire и single wire)
-    if(sensor->type->interface == &ONE_WIRE || sensor->type->interface == &SINGLE_WIRE) {
+    //Порт подключения датчка (для one wire, SPI и single wire)
+    if(sensor->type->interface == &ONE_WIRE || sensor->type->interface == &SINGLE_WIRE ||
+       sensor->type->interface == &SPI) {
         if(sensor->type->interface == &ONE_WIRE) {
             initial_gpio = ((OneWireSensor*)editable_sensor->instance)->bus->gpio;
-        } else {
+        } else if(sensor->type->interface == &SINGLE_WIRE) {
             initial_gpio = ((SingleWireSensor*)editable_sensor->instance)->gpio;
+        } else if(sensor->type->interface == &SPI) {
+            initial_gpio = ((SPISensor*)editable_sensor->instance)->CS_pin;
         }
 
         uint8_t aviable_gpio_count =

+ 2 - 2
views/SensorsList_view.c

@@ -86,8 +86,8 @@ static void _enter_callback(void* context, uint32_t index) {
         return;
     }
 
-    //Выбор первого доступного порта для датчика single wire
-    if(type->interface == &SINGLE_WIRE) {
+    //Выбор первого доступного порта для датчика single wire и SPI
+    if(type->interface == &SINGLE_WIRE || type->interface == &SPI) {
         snprintf(
             args,
             4,