[Из песочницы] Замена аналоговой регулировки на цифровую в лабораторном блоке питания HY3005D

В статье пойдет речь о замене штатных потенциометров на механические энкодеры EC11 (накапливающие датчики угла поворота) в блоке питания Mastech HY3005D с использованием микроконтроллера PIC16F1829 и сдвигового регистра 74HC595 реализующей ЦАП на резисторной матрице R2R

Предисловие

Несколько лет назад приобрел блок питания Mastech HY3005D. Не так давно возникли проблемы с регулировкой напряжения — истерлось графитовое покрытие реостатов и выставить необходимое напряжение стало сложной задачей. Подходящих реостатов не нашлось, и я решил не покупать аналогичные, а изменить способ регулировки.
Уровень выходного напряжения и тока задается опорным напряжением, подаваемым на операционные усилители. Таким образом можно полностью избавиться от потенциометров заменив их на ЦАП способный выдавать напряжение в нужном диапазоне.
В каталоге microchip я не смог подобрать подходящего микроконтроллера, имеющего два ЦАП на борту, а внешние ЦАП имеют не малый ценник и слишком много лишнего функционала. Поэтому приобрел сдвиговые регистры 74HC595 и резисторы для матрицы R2R. Микроконтроллер PIC16F1829 уже был в наличии.
Для возможности вернуться к первоначальной схеме все изменения сведены к минимуму — замене блока регулировки выполненного на отдельной плате.

Описание работы

В основе схемы лежит микроконтроллер PIC16F1829 работающий на частоте 32МГц. Тактовая частоста задается встроенным тактовым генератором, согласно даташиту он не слишком точный, но для данной схемы — это не критично. Плюсом данного МК является наличие подтягивающих резисторов на всех цифровых входах и два MSSP модуля реализующих SPI. Все 18 логических вывода микроконтроллера использованы.
На четырех сдвиговых регистрах 74HC595 и R2R матрицах реализованы два ЦАП по 16 бит. К плюсам данного регистра можно отнести наличие раздельного сдвигового регистра и регистра хранения. Это позволяет записывать данные в регистр, не сбивая текущие выходные значения. Матрица R2R собрана на резисторах с погрешностью 1%. Стоит заметить, что выборочные замеры показали погрешность не более 10 Ом. Изначально планировалось использовать 3 регистра, но при разводке платы мне показалось это не удачным решением, к тому же требовалось складывать полубайты.
Встроенные в МК подтягивающие резисторы активированы на всех входах и позволяют упростить схему. Все выходы с энкодеров подключены напрямую к выводам МК, всего 4 энкодера у каждого по два вывода для самого датчика поворота и один для встроенной кнопки. Итого 12 выводов МК используется для обработки входных данных. Дребезг контактов сглаживается емкостью 100нФ. После изменения значений 16-битных буферов тока и напряжения в соответствии с входными данными от энкодеров значения передаются в сдвиговые регистры 74HC595 по SPI. Для сокращения времени передачи данных используется два SPI-модуля что позволяет передавать данные одновременно для тока и напряжения. После того как данные переданы на регистр подается команда переноса данных из сдвигового буфера в буфер хранения. Выходы регистра подключены к матрице R2R выполняющую роль делителя для ЦАП. Выходное напряжение с матрицы передается на входы операционных усилителей.
Кнопки, встроенные в энкодеры, устанавливают значения на минимум (кнопка энкодера плавной регулировки) или максимум (кнопка энкодера грубой регулировки), соответственно, для тока или напряжения.

Схема

В интернете не нашел схему, полностью совпадающую с моей, поэтому взял по первой ссылке. Внес исправления по выявленным несоответствиям и затем добавил свои изменения. Схему блока регулировки чертил в TinyCAD — скачать файл HY3005D-regulator.dsn.

Оригинал

[Из песочницы] Замена аналоговой регулировки на цифровую в лабораторном блоке питания HY3005D

После найденных неточностей

Итоговая схема после доработки

Выносной блок с регулировкой (выделен красным) вынес в отдельную схему.

К разъему J3 подключается цифровой вольтметр с дисплеем на лицевой панели (его нет на схемах).

Использованные компоненты

Ниже приведен список использованных компонентов (в скобках указан корпус). Все приобретались в разное время в Китае. Важно использовать светодиоды с такими же характеристиками что и родные, т.к. они стоят последовательно в выходной цепи операционных усилителей (я взял те же что стояли на родной плате).

  • U1: микроконтроллер PIC16F1829I/ML (QFN)
  • U2 — U5: сдвиговый регистр 74HC595BQ (DHVQFN16 или SOT-763)
  • U6: линейный регулятор напряжения AMS1117 на 5В (SOT-223)
  • RE1 — RE4: механический накапливающий датчик угла поворота EC11
  • R1, R2 и матрицы R2R: резисторы 1 и 2 кОм (SMD 0402)
  • C1 — C12, C14-C17: керамические конденсаторы GRM21BR71E104KA01L 100нФ (SMD 0805)
  • C13: танталовый конденсатор 22мкФ 16В (тив B)
  • D1, D2: светодиоды индикации напряжения/тока на лицевой панели

Плата

Плату разводил в Sprint Layout 6 — скачать файл HY3005D-regulator.lay6. К сожалению, оригинал, на котором я сделал свой вариант, не сохранился, в формате lay6 уже с исправлениями, выявленными в ходе сборки:

  1. В разрыв подключения энкодера плавной регулировки тока добавил перемычки рядом с интерфейсом для прошивки, т.к. емкости, фильтрующие дребезг контактов, не позволяли прошивать контроллер
  2. Добавил недостающие перемычки для земли между сторонами
  3. Переместил стабилизирующую сборку на 5В на другую сторону для уменьшения сквозных перемычек
  4. Добавлены сглаживающие конденсаторы на линии питания (обсуждение)

Фото после травления и сверления (первая версия)


Изготавливал с использованием пленочного фоторезиста. Долго мучился с мелкой разводкой регистров. В последнем варианте были небольшие огрехи, которые пришлось зачищать после травления. Но в целом плата удалась. Здесь еще не хватает двух перемычек для соединения земли на лицевой и тыльной сторонах.

После внесения выявленных проблем (вторая версия)



В качестве перемычек использованы три резистора номиналом 0 Ом в корпусе SMD 0805.

Фото после распайки и установки на место

В левой части сам блок питания. В правой — лицевая панель лицом в низ. Зеленый провод из левого верхнего угла в правый нижний — дополнительное питание 12В.

Как видно, изменения минимальны, все старые разъемы остались без изменений. Пришлось добавить отдельно питание, т.к. единственное напряжение, приходящее на плату регулировки 2.5В для родного делителя не подходит. Если на основной плате блока питания убрать стабилитрон на 2.5В (V5A) и поставить перемычку в место резистора (R1A), можно обойтись и без дополнительного подведения 12В питания.

Прошивка

Код на Си для компилятора XC8. Прошивал оригинальным PICkit 3.

config.h

// PIC16F1829 Configuration Bit Settings  // 'C' source line config statements  #include   // #pragma config statements should precede project file includes. // Use project enums instead of #define for ON and OFF.  // CONFIG1 #pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin) #pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled) #pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled) #pragma config MCLRE = OFF      // MCLR Pin Function Select (MCLR/VPP pin function is digital input) #pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled) #pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled) #pragma config BOREN = OFF      // Brown-out Reset Enable (Brown-out Reset disabled) #pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin) #pragma config IESO = OFF       // Internal/External Switchover (Internal/External Switchover mode is disabled) #pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)  // CONFIG2 #pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off) #pragma config PLLEN = OFF      // PLL Enable (4x PLL disabled) #pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset) #pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.) #pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming) 

main.c

#include "config.h"  #define _XTAL_FREQ 32000000 #pragma intrinsic(_delay) extern void _delay(unsigned long); #define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0))) #define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))  #define TransferNotDone          !(SSP2STAT&0b00000001)  #define StoreAll                 LATA4 #define ResetAll                 LATC6  #define VoltageSharpCHA          RC1 #define VoltageSharpCHB          RC0 #define VoltageSharpBTN          RA2  #define VoltageSmoothCHA         RB5 #define VoltageSmoothCHB         RB4 #define VoltageSmoothBTN         RC2  #define CurrentSharpCHA          RC5 #define CurrentSharpCHB          RC4 #define CurrentSharpBTN          RC3  #define CurrentSmoothCHA         RA1 #define CurrentSmoothCHB         RA0 #define CurrentSmoothBTN         RA3  #define VoltageRateSharp         0x0100 #define VoltageRateSmooth        0x0010 #define CurrentRateSharp         0x0040 #define CurrentRateSmooth        0x0004  #define VoltageMax               0xC000 #define CurrentMax               0x5000 #define VoltageMin               0x0000 #define CurrentMin               0x0000  #define VoltageStart             0x1E00 #define CurrentStart             CurrentMax  unsigned short VoltageBuff; unsigned short CurrentBuff;  void interrupt tc_int() { };  void SendData() {     SSP1BUF = VoltageBuff>>8;     SSP2BUF = CurrentBuff>>8;     while ( TransferNotDone );     SSP1BUF = VoltageBuff;     SSP2BUF = CurrentBuff;     while ( TransferNotDone );     StoreAll = 1;     StoreAll = 0; };  void main() {     // Configure oscillator for 32MHz     //             76543210     OSCCON     = 0b11110000; //B1      // Enable individual pull-ups     //             76543210     OPTION_REG = 0b01111111; //B1      // Configure analog port (1 - enable, 0 - disable)     //             76543210     ANSELA     = 0b00000000; //B3     ANSELB     = 0b00000000; //B3     ANSELC     = 0b00000000; //B3      // Reset latch     //             76543210     LATA       = 0b00000000; //B2     LATB       = 0b00000000; //B2     LATC       = 0b00000000; //B2      // Alternate pin function (set SDO2 on RA5)     //             76543210     APFCON0    = 0b00000000; //B2     APFCON1    = 0b00100000; //B2      // Configure digital port (1 - input, 0 - output)     //             76543210     TRISA      = 0b00001111; //B1     TRISB      = 0b00110000; //B1     TRISC      = 0b00111111; //B1      // Configure input level (1 - CMOS, 0 - TTL)     INLVLA     = 0b11000000; //B7     INLVLB     = 0b00001111; //B7     INLVLC     = 0b00000000; //B7      // Configure individual pull-ups (1 - enable, 0 - disable)     //             76543210     WPUA       = 0b00111111; //B4     WPUB       = 0b11110000; //B4     WPUC       = 0b11111111; //B4      ResetAll = 0;     ResetAll = 1;      // Configure SPI in master mode     //             76543210   //SSP1ADD    = 0b00000000; //B4     SSP1STAT   = 0b01000000; //B4     SSP1CON3   = 0b00000000; //B4     SSP1CON1   = 0b00100000; //B4   //SSP1ADD    = 0b00000000; //B4     SSP2STAT   = 0b01000000; //B4     SSP2CON3   = 0b00000000; //B4     SSP2CON1   = 0b00100000; //B4      VoltageBuff = VoltageStart;     CurrentBuff = CurrentStart;     __delay_ms(50);     SendData();      while ( 1 ) {         if ( !VoltageSharpCHA ) {             if ( VoltageSharpCHB ) { VoltageBuff-=VoltageRateSharp; if ( VoltageBuff > VoltageMax ) VoltageBuff = VoltageMin; }                               else { VoltageBuff+=VoltageRateSharp; if ( VoltageBuff > VoltageMax ) VoltageBuff = VoltageMax; }             while ( !VoltageSharpCHA );             SendData();         }          if ( !VoltageSmoothCHA ) {             if ( VoltageSmoothCHB ) { VoltageBuff-=VoltageRateSmooth; if ( VoltageBuff > VoltageMax ) VoltageBuff = VoltageMin; }                                else { VoltageBuff+=VoltageRateSmooth; if ( VoltageBuff > VoltageMax ) VoltageBuff = VoltageMax; }             while ( !VoltageSmoothCHA );              SendData();         }          if ( !CurrentSharpCHA ) {             if ( CurrentSharpCHB ) { CurrentBuff-=CurrentRateSharp; if ( CurrentBuff > CurrentMax ) CurrentBuff = CurrentMin; }                               else { CurrentBuff+=CurrentRateSharp; if ( CurrentBuff > CurrentMax ) CurrentBuff = CurrentMax; }             while ( !CurrentSharpCHA );             SendData();         }          if ( !CurrentSmoothCHA ) {             if ( CurrentSmoothCHB ) { CurrentBuff-=CurrentRateSmooth; if ( CurrentBuff > CurrentMax ) CurrentBuff = CurrentMin; }                                else { CurrentBuff+=CurrentRateSmooth; if ( CurrentBuff > CurrentMax ) CurrentBuff = CurrentMax; }             while ( !CurrentSmoothCHA );             SendData();         }          if ( !VoltageSharpBTN  ) { VoltageBuff = VoltageMax; while ( !VoltageSharpBTN  ); SendData(); }         if ( !VoltageSmoothBTN ) { VoltageBuff = VoltageMin; while ( !VoltageSmoothBTN ); SendData(); }         if ( !CurrentSharpBTN  ) { CurrentBuff = CurrentMax; while ( !CurrentSharpBTN  ); SendData(); }         if ( !CurrentSmoothBTN ) { CurrentBuff = CurrentMin; while ( !CurrentSmoothBTN ); SendData(); }     }; } 

Для минимальных значений VoltageMin и CurrentMin выставлена 1, т.к. при 0 в буфере регулировка перестает работать, пока не понял где проблема. Рейты *Rate* подбирал кратные и наиболее удобные на мой взгляд. Для метода SendData не делал передачу переменных в качестве параметров для экономии машинных команд и памяти. Режим прошивки с низким напряжением (LVP) должен быть выключен, иначе RA3 не будет работать как цифровой вход. Прерывания не используются, метод tc_int присутствует в коде для того чтобы компилятор поместил основной блок в начало ППЗУ.
Для прошивки достаточно снять перемычки, подключить PICkit 3 (или другой программатор) и выполнить прошивку. В первой версии не было перемычек на CLK и DAT, поэтому мне пришлось выпаять сглаживающие конденсаторы, прошить и потом впаять их обратно.
UPD: После установки дополнительных емкостей на линии питания проблема с выходом из нулевого положения счетчика исчезла. Так же пришлось поменять направление вращения. Судя по всему, шум от выпрямителя AMS1117 мешал корректно распознавать состояние энкодеров. Дополнительно добавил установку стартовых значений, теперь напряжение по умолчанию выставляется на 5 вольт (ток по-прежнему на максимум). Перед первой отправкой данных в регистры вставлена задержка в 50мс (значение задержки взял с большим запасом) для ожидания инициализации модулей SPI.

Характеристики выходного напряжения

После окончательной сборки устройства был выполнен замер напряжений между контактами J4.1 — J4.2 (регулировка напряжения) и J4.1 — J4.7 (регулировка тока). По полученным данным построены графики (ниже под спойлером) зависимостей значение/напряжение для ЦАП.

Графики


Расчетные значения напряжений получены по формуле (U*D)/(2^K), где
U — напряжение на выходе регистра с учетом делителей в основной схеме (для ЦАП тока — 4950мВ, для ЦАП напряжения — 3550мВ);
D — десятичное значение счетчика ЦАП;
K — разрядность ЦАП (16 бит)

Что можно улучшить

  • добавить сохранение значений по таймеру
  • завязать на кнопки энкодеров переключение стандартных значений, например для напряжения: 3,3; 5,0; 7,5; 12В
  • для защиты от неизвестного значения при старте лучше подключить MR на 1, а OE подтянуть через резистор к 1 и сбрасывать в 0 после инициализации МК.
  • заменить ЦАП на ШИМ со сглаживающей цепочкой (предложил LampTester тут)

Источник

microchip, microcontrollers, pic16, блок питания, программирование микроконтроллеров

Читайте также