Я долго думал о чем написать следующий пост, debug, потоки (thread), прерывания и т.п для каждого поста как-то мало получится, а одним постом все в куче, тоже не красиво. Да и теории в принципе хватает, это вы и сами сможете почитать, а что непонятно — спросить в моем блоге.
Но тут подвернулась реальная задача измерения температуры нагрева катушки соленоида ЭМ-клапана в длительном промежутке времени. В дальнейшем я решил писать новые топики с практической реализацией и в каждом топике затрагивать немного теоретической части. Т.о. мы будем медленно, но верно продвигаться от более простых, к более сложным проектам.
Итак, имеем популярный цифровой датчик температуры DS18B20 (даташит). Подключил я его по трехпроводной схеме (с внешним питанием). Здесь можно посмотреть схему подключения к Arduino, у нас будет тоже самое подключение, единственное пин данных — D4.
У GHI хорошая техподдержка и сообщество, и для очень многих шилдов, сенсоров, LCD-экранчиков и т.п. уже имеются готовые библиотеки и примеры программ. Естественно, работа плат FEZ с таким популярным датчиком как DS18B20 там тоже освещена. Я не стал изобретать велосипед и воспользовался вот этим рабочим кодом.
Убрав, немного лишнего кода в итоге получилось вот что:
using System; using System.Threading; using Microsoft.SPOT; using Microsoft.SPOT.Hardware; using GHIElectronics.NETMF.FEZ; using GHIElectronics.NETMF.Hardware; namespace DS18B20 { public class Program { public static OneWire ow = new OneWire((Cpu.Pin)FEZ_Pin.Digital.Di4); public static void Main() { while (true) { int temp; int sign; float tempc = 0; byte[] romid = new byte[8]; byte[] readall = new byte[9]; ow.Reset(); ow.WriteByte(0x33); ow.Read(romid, 0, 8); ow.Reset(); ow.WriteByte(0x55); ow.Write(romid, 0, 8); ow.WriteByte(0x44); // Start temperature conversion Thread.Sleep(1000); //wait for conversion to finish ow.Reset(); ow.WriteByte(0x55); ow.Write(romid, 0, 8); ow.WriteByte(0xBE); // Read Scratchpad Thread.Sleep(1000); //wait for conversion to finish ow.Read(readall, 0, 9); temp = readall[0]; // LSB temp |= (ushort)(readall[1] << 8); // MSB //determine if the reading is < 0 sign = temp & 0x8000; if (sign != 0) { // value is < 0 temp = (temp ^ 0xFFFF) + 1; tempc = (float)(((temp >> 1) - 0.25 + ((16.00 - readall[6]) / 16.00)) * -1.00); //See datasheet - way to increase resolution } else { tempc = (float)((temp >> 1) - 0.25 + ((16.00 - readall[6]) / 16.00)); //See datasheet - way to increase resolution } Debug.Print("Temp: " + tempc.ToString("F2")); Thread.Sleep(3000); } } } }
Вверху мы видим новую строчку: using GHIElectronics.NETMF.Hardware,
Это необходимо для работы с протоколом OneWire, по которому работает датчик.
Строкой public static OneWire ow = new OneWire((Cpu.Pin)FEZ_Pin.Digital.Di4), мы показываем, что устройство OneWire подключено к 4 выводу Panda II.
Однако, это еще не все. Если сделать новый проект и скопировать туда данный код, то вы увидите следующие ошибки:
Дело в том, что данную библиотеку необходимо включить в проект в обозревателе решений (ссылки)
Для этого, нажимаем правой клавишей мыши на ‘Ссылки’ и выбираем ‘Добавить ссылку’
В открывшемся окне «AddReference» вы увидите большой список библиотек. Вам необходимо выбрать библиотеку GHIElectronics.NETMF.Hardware:
Теперь ошибок не будет, и код будет компилироваться без ошибок.
В коде есть такая строка: Thread.Sleep(1000), Как можно догадаться, это пауза. Задается в миллисекундах. Т.е. в данном случае происходит пауза при выполнении кода в 1 секунду. В нашем коде находится 3 таких строчки и т.к. код выполняется в постоянном цикле, то каждый цикл будет длиться 1+1+3=5 сек.
Еще одна полезная строчка кода: Debug.Print(«Temp: » + tempc.ToString(«F2»)), которая выводит измеренную температуру в окно отладки.
Debug.Print() очень полезная функция и она незаменима при отладке приложения.
В итоге, после заливки программы в контроллер мы увидим следующие данные в окне вывода:
Теперь немного усложним код. Для того, чтобы видеть, что измерение температуры сделано я решил задействовать встроенный LED. Для этого, я написал небольшую функцию:
public static void BlinkLED(int num) { int count = 0; num = num * 2; bool ledState = false; while (count < num) { ledState = !ledState; led.Write(ledState); count++; Thread.Sleep(50); } led.Write(false); }
Данная функция заставляет мигать встроенный на плату светодиод (объект «led») количество раз, равное «num». Пауза 50мс.
Теперь, вызвав данную функцию скажем так: BlinkLED(150), светодиод моргнет 150 раз.
Также, мы немного переделаем наш код, убрав измерение температуры из цикла в отдельную функцию. После модернизации код будет выглядеть так:
using System; using System.Threading; using Microsoft.SPOT; using Microsoft.SPOT.Hardware; using GHIElectronics.NETMF.FEZ; using GHIElectronics.NETMF.Hardware; namespace DS18B20 { public class Program { static float temp; //Температура public static OneWire ow = new OneWire((Cpu.Pin)FEZ_Pin.Digital.Di4); public static OutputPort led = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.LED, false); //Инициализация LED public static void Main() { while (true) { temp = GetTemperature(); //Считываем показания температуры с датчика DS18B20 Debug.Print("Temp: " + temp); //Вывод данных текущей температуры BlinkLED(10); //Мигаем 10 раз встроенным LED Thread.Sleep(3000); //Пауза 3 сек. } } public static float GetTemperature() { int temp2; int sign; float tempc = 0; byte[] romid = new byte[8]; byte[] readall = new byte[9]; ow.Reset(); ow.WriteByte(0x33); ow.Read(romid, 0, 8); ow.Reset(); ow.WriteByte(0x55); ow.Write(romid, 0, 8); ow.WriteByte(0x44); // Start temperature conversion Thread.Sleep(1000); //wait for conversion to finish ow.Reset(); ow.WriteByte(0x55); ow.Write(romid, 0, 8); ow.WriteByte(0xBE); // Read Scratchpad Thread.Sleep(1000); //wait for conversion to finish ow.Read(readall, 0, 9); temp2 = readall[0]; // LSB temp2 |= (ushort)(readall[1] << 8); // MSB //determine if the reading is < 0 sign = temp2 & 0x8000; if (sign != 0) { // value is < 0 temp2 = (temp2 ^ 0xFFFF) + 1; tempc = (float)(((temp2 >> 1) - 0.25 + ((16.00 - readall[6]) / 16.00)) * -1.00); //See datasheet - way to increase resolution } else { tempc = (float)((temp2 >> 1) - 0.25 + ((16.00 - readall[6]) / 16.00)); //See datasheet - way to increase resolution } return tempc; } public static void BlinkLED(int num) { int count = 0; num = num * 2; bool ledState = false; while (count < num) { ledState = !ledState; //Инвертируем предыдущее состояние led.Write(ledState); count++; Thread.Sleep(50); } led.Write(false); //Чтобы не оставался включенным } } }
В код я добавил комментарии, поэтому думаю все понятно. Как видите, в основном цикле программы теперь всего 4 строчки кода. Последней строкой в цикле стоит пауза 3 сек, но учтите, что в функции GetTemperature() находится еще 2 паузы по 1 сек. Поэтому выполнение данной функции занимает 2 секунды.
Логгирование
На плате FEZ Panda 2 присутствует встроенный слот для MicroSD карт. Его я и решил использовать для сохранение данных о температуре.
Каждые 10 секунд я буду сохранять полученную информацию о температуре с датчика, в файл temp.csv. Помимо температуры, чтобы в дальнейшем построить наглядный график, необходимо сохранять и время. В Panda, есть встроенные часы реального времени RTC. Но, для того, чтобы они сохраняли время после перезагрузки и отключения питания, необходимо подключить внешнюю батарейку. В начале я их и хотел задействовать, но честно говоря немного поэкспериментировав не стал этого делать, да и для моей задачи они не нужны. Тем более в Excel для меня стала проблема построить график, где по X-были данные формата даты (ну не силен я в Excel, мне легче в PHP график сдела
Оцените статью!