https://www.youtube.com/watch?v=hXBmD3VHRQo
Продолжаем проектирование паяльной станции.На данном этапе паяльная станция имеет нижний подогрев с четырьмя нагревателями и умеет подогревать плату до нужной температуры. К паяльной станции подключены три термопары. Сейчас нагрев контролируется по одной из них, вторая просто показывает температуру, не участвуя в процессе регулирования. Третяя термопара показывает температуру корпуса в отсеке с электроникой. Я установил её, так как было не понятно, будет ли перегреваться электроника. Сейчас уже понятно, что если использовать станцию по назначению, а не в качестве отопления комнаты, то станция не перегревается даже без вентиляторов. Хотя, для большей надежности я всё равно планирую установить два вентилятора в отсек с электроникой.
Изначально я планировал использовать ПИД регулятор для регулировки температуры. После многочисленных тестов я так и не смог добиться приемлемых результатов его работы для разных ситуаций. Например, когда я подбираю параметры ПИД регулятора для нагрева температуры до 150 градусов, станция уверенно выходит на заданную температуру и поддерживает её. Но как только я задаю другую температуру, например 100 градусов, регулятор уже промахивается и поднимает до 110-120 градусов. Если же подобрать параметры, которые будут более плавно выводить температуру на нужную уставку, это приведет к затягиванию процесса для более высоких температур.
Пока я решил эту проблему ограничением мощности для разных температур. Во-первых, я вычисляю разность температур (текущей и требуемой), чем больше разность, тем больше разрешенная мощность. Во вторых, ввожу дополнительный коэффициент "dop". Чем выше требуемая температура, тем больше коэффициент.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
void pid::SetTemperature(float t_) { int dop=0; if (t_>110) dop=15; if (t_>140) dop+=20; if (t_>160) dop+=20; if (t_>180) dop+=20; if (t_>200) dop+=20; requered_temp = t_; integral = 0; if (t_>current_temp) { if (t_-current_temp<=15) {setMaxPower(10+dop);Compute(this);return;} if (t_-current_temp<=25) {setMaxPower(25+dop);Compute(this);return;} if (t_-current_temp<=50) {setMaxPower(60+dop);Compute(this);return;} if (t_-current_temp<=250) {setMaxPower(100+dop);Compute(this);return;} } Compute(this); } |
Схема соединений
Изначально я начал разрабатывать паяльную станцию на базе отладочной платы Nucleo F410RB. До данного момента она справлялась со своими задачами. Я покупал её для изучения mbed, а не под конкретный проект и не мог рассчитать примерный объем нужной мне памяти. Как следствие, я уперся в ограничение 128Кб на размер программы. Этого достаточно на текущем этапе, но нет задела для будущего. Я планирую подключать флеш карту к паяльной станции, на которой будут храниться настройки, лог - файлы, профили пайки. А это накладывает повышенные требования к памяти, так как необходимо подключить библиотеки для работы с файлами, возможно с XML файлами. Подключение библиотек для работы с файловой системой сразу же увеличило размер програмы до 130Кб. Это уж не укладывается в Nucleo F410RB. Я не стал придумывать костыли и решил использовать другую отладочную плату -Nucleo F446RE.
Посмотрим, что подключено к микроконтроллеру.
Фазовый регулятор мощности
Фазовый регулятор мощности вы могли видеть в моём видеоролике.
Там же описан принцип его работы. В предыдущей статье я описывал как им управлять в mbed. Ниже вы можете видеть к каким портам он подключен в моей паяльной станции.

Термопары
Термопар много не бывает. На данном этапе я использую три термопары, подключенные через микросхемы max6675 (spi интерфейс). Все три модуля подключены к одному интерейсу SPI. Различаются только ножки CS(chip select). Изначально все CS порты установлены в единицу. Как только мы хотим получить данные с нужной термопары, мы устанавливаем ножку CS этой термопары в 0 и получаем данные о температуре.

Подключение флешкарты
На данном этапе флешка не задействована, но в будущем на ней будут храниться настройки и лог-файлы.

Связь с монитором Nextion и компьютером
Nextion - главный элемент управления станцией. Он реализует интерфейс пользователя. Подключается по uart интерфейсу. Ещё один uart интерфейс задействован по умолчанию для связи с компьютером.

Интерфейс пользователя
Интерфейс пользователя состоит из четырёх экранов.
Стартовый экран
На стартовом экране две кнопки. По нажатию на каждую из них происходит переход на соответствующую страницу интерфейса. Обработчики для нажатий на эти кнопки представлены ниже:
Нижний подогрев
1 2 3 4 |
print "xpage=" covx 1,va0.txt,3,0 print va0.txt print " " |
Настройки
1 2 3 4 |
print "xpage=" covx 3,va0.txt,3,0 print va0.txt print " " |
При нажатии на кнопку в uart порт передается команда "xpage=номер страницы". Номер страницы я передаю в текстовом виде. Это связано с проблемой, которую я не смог решить. Экран nextion может передавать числа в шестнадцатиричном виде по 4 байта на число. Изначально я писал:
1 2 3 |
print "xpage=" print 1 print " " |
На стороне микроконтроллера я анализировал полученные данные. Разбивал команду на то что находится до знака "=" и после. Я знал, что после знака равно идут четыре байта с числом и успешно его считывал. Но проблема в том, что дисплей отказывался передавать некоторые числа. Например 10,11,12. Вместо них не передавалось ничего. Я случайно обнаружил эту проблему, когда задавал температуру уставки 11 или 12 градусов. При этом станция начинала глючить. Хотя на всех других температурах отрабатывала нормально. Столкнувшись с этой проблемой я решил передавать все данные в текстовом виде, а уже на стороне микроконтроллера переводить нужные из них в число. Отсюда появилась строчка "covx 1,va0.txt,3,0", которая переводит цифру 1 в текстовый вид и записывает её в невидимое текстовое поле va0.txt.
Вообще для того, чтобы быстро отличать все мои команды от другой вспомогательной информации, которая передаётся по uart порту между дисплеем и микроконтроллером, я решил, что все мои команды будут начинаться с буквы "x".
Экран задания температуры
На этом экране можно задавать требуемую температуру с помощью слайдера или наэкранной клавиатуры. Кнопки назад и вперед рассматривать не будем, они не отличаются от ранее рассмотренных. Посмотрим код кнопки "Старт".
1 2 3 4 5 |
print "xsd=" cov h1.val,va0.txt,0 print va0.txt print " " page2.tempg.val=h1.val |
При нажатии кнопки "Старт" по uart микроконтроллеру передается команда вида "xsd=заданная температура". h1.val - значение слайдера, page2.tempg.val - поле на следующей странице, которое показывает, какую температуру мы хотим достичь.
Экран отображения динамики нагрева
На этом экране отображаются графики изменения температуры для двух термопар. Голубой график - "термопара нижнего подогрева". Желтый - "термопара верхнего нагрева". Зеленый - температура уставки. Таким образом видим, что задана температура 145 градусов и нижний нагреватель разогревает тестовую плату (синий график приближается к зеленому). Вторая термопара пока в процессе не учавствует, она лежит рядом с паяльной станцией. Я кратковременно прикоснулся ей к нижнему нагревателю, о чем свидетельствует всплеск на желтом графике.
Экран настроек
На этом экране пока можно включать и отключать элементы нижнего нагревателя. Приведу код кнопки для первого нагревателя. При нажатии кнопки передается команда "xtoggle=0" для первого нагревателя; "xtoggle=1" для второго и.т.д.
1 2 3 4 |
print "xtoggle=" cov 0,va0.txt,0 print va0.txt print " " |
Функция микроконтроллера для обработки команд, принятых от экрана
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
void ReadCommands() { char a[30]; // буфер для приема команд string str,command; int data = 0; int c = 0; // c>0, если пришли данные, требующие обработки. Если c=0, то пришли ненужные нам данные(служебная информация) while(1) { while (s2.readable()) { if (s2.getc()!='x') // если первый символ не x, то эти данные нам не нужны { char b; while (s2.readable()) { b=s2.getc(); } } else { s2.scanf("%s",a); // считываем данные в буффер а c++; } } if (c>0) // если пришли данные, которые нужно обработать str.append(a); c=0; if (str.length()>0) { // разбиваем строку на команду и данные. Все что перед равно - команда. После равно - данные int pos =str.find("="); if (pos!=-1) { command = str.substr(0,pos); data = atoi(a+pos+1); if (command=="sd") //sd - set down. Команда устанавливает температуру для нагрева нижним нагревателем { // по команде sd открывается страница с графиками, сейчас нужно будет передавать массивы точек graphPre = data; // зеленая линия на графике, указывающая температуру до которой нужно нагреть ThisThread::sleep_for(200); // пауза нужна для того, чтобы дисплей распознал команду(он только что передавал данные, теперь ему нужно их читать) ShowPage2(); //функция переключает дисплей на страницу с графиками и передает массивы точек для графиков reg.SetTemperature(data); // даем задание ПИД регулятору } if (command == "page") // если была нажата кнопка перехода на другую страницу дисплея { prevPage = displayPage; // запоминаем текущую страницу displayPage = data; // запоминаем страницу на которую будем переходить if (data == 2){ShowPage2();} // если это вторая страница(графики), то вызываем функцию отображения второй страницы else{ // если любая другая страница while(!s2.writable()){ThisThread::sleep_for(5);} s2.printf("page %d%c%c%c",data,255,255,255);// отправляем команду на смену страницы } } if (command == "back") // команда перехода на предыдущую страницу { displayPage = prevPage; if (prevPage==2) { ShowPage2(); } else { while(!s2.writable()){ThisThread::sleep_for(5);} s2.printf("page %d%c%c%c",prevPage,255,255,255);// отправляем команду на смену страницы } } if (command == "toggle") // команда включения/выключения элементов нижнего нагревателя { while(!s2.writable()){ThisThread::sleep_for(5);} int x=P.ToggleHeater(data); if (x==0) { if(s2.writable()) s2.printf("bt%d.bco=12678%c%c%c",data,255,255,255); // меняем цвет на серый (выключен) else P.ToggleHeater(data); } if (x==1) { if(s2.writable()) s2.printf("bt%d.bco=64520%c%c%c",data,255,255,255); // меняем цвет на оранжевый (включен) else P.ToggleHeater(data); } } } } str.clear(); command.clear(); data = 0; } } |
Кратко опишу, как происходит общение между микроконтроллером и дисплеем. Общаются они по uart. Дисплей передает команду в виде "xcommand=data", где command - команда, data - данные. Все команды я начинаю с буквы x. Так легче их отделять от остального мусора. Если команда не начинается с x, значит она нам не нужна. Дальше микроконтроллер разбивает команду на command и data и выполняет необходимые действия по той или иной команде. Если же микроконтроллер хочет передать команду дисплею, то делает о это в соответствии с инструкцией.
1 |
s2.printf("page %d%c%c%c",data,255,255,255); |
Вот так, например, выглядит команда перевода дисплея на другую страницу. Обратите внимание, что все команды дисплею должны заканциваться FF FF FF. Об этом подробнее написано в инструкции к дисплею.