Реанимация гибочного станка, делаем новый электронный съемник данных по повороту вала
К нам обратился предприниматель с просьбой помочь в восстановлении гибочного станка. Он приобрел б/у станок, но на нем сломано устройство для съема данных с вала по углу, на который требуется гнуть металл (в центре).
Ниже фото без данного устройства.
Фирменный немецкий съемник имел емкостный счетчик поворота, и фирменный контроллер, подобные схемы применяются в электронный штангенциркулях. Конечно разбираться в чужой схеме довольно сложно и не всегда для этого есть основания (где достать испорченные компоненты?). Поэтому решили заменить устройство на то, что можем сделать самостоятельно. Для этого возможно применить двунаправленный инкрементальный энкодер, который будет снимать информацию с вала.
Инкрементальные энкодеры, независимо от типа (у нас оптический), бывают с одним фотоэлементом и с двумя. Если у энкодера один фотоэлемент, то нет возможности определить, в какую сторону присходит движение/вращение, подобные энкодеры можно применять в тех случаях, когда вращение/движение однонаправленное. В нашем случае вращение может осуществляться в обе стороны, следовательно используется двунаправленный энкодер (с двумя фотоэлементами, разнесенными на половину ширины щели диска). С него, используя "хитрую" программу, можно получить данные о направлении вращения, скорости, величине перемещения.
Состояние углового положения может быть выражено не в абсолютной форме, а относительно предыдущего положения (состояния) энкодера. Зная предыдущее состояние пары фотодатчиков и получив новое, можно говорить о движении на одну позицию вала в определенную сторону. Подсчет этих позиций и дает информацию о текущем положении вала. Рисунок ниже демонстрирует, как будут изменяться состояния фотодатчиков (A и B) при вращении вала с диском, допустим датчики находятся в состоянии "3" , а затем перешли в состояние "2", значит требуется отнять "1" от накопленного перемещения, а если из состояния "3" фотодатчики (A и B) перешли в состояние "4" следует прибавить "1" к накопленному перемещению. Если нужно перевести относительные единицы в абсолютные, потребуется умножение на коэффициент, который зависит, в том числе и от количества отверстий в диске.
Схематично энкодер и его подключение к контроллеру изображено на рисунке ниже. Фотоэлемены представлены в виде пары фототранзисторов Q1 и Q2, которые подсвечиваются светодиодом. Между ними и светодиодом вращается диск к прорезями, что приводит к изменению состояний фотоэлементов. Состояния фотоэлементов подаются на входы D8 и D9 контроллера, а затем программно дешифруется в состояние угла поворота.
Для визуализации результата используется дисплей на основе микросхемы HT1621, его подключение соответствует рисунку:
Программа:
Основная программа использует встроенную библиотеку EEPROM и два локальных модуля encoder.h - обработка энкодера, ht1621_m.h - работа с дисплеем.
EEPROM служит для чтения записи данных в энергонезависимую память, для этого реализованы две функции void load_from_EEPROM() - читает информацию из энергонезависимой памяти, а button2_check() - пишет при необходимости данные в энергонезависимую память.
-
#include < EEPROM.h >
-
#include "ht1621_m.h"
-
#include "encoder.h"
-
unsigned long tme;
-
void button2_check()
-
{
-
static long write_check;
-
byte temp_b;
-
if (Bu2) //Если нажата кнопка записи в энергонезависимую память
-
{
-
HT1621_all_on(16);
-
if (write_check != encoder_L)
-
{
-
write_check = encoder_L;
-
//Производим запись
-
temp_b = write_check;
-
EEPROM.write(0, temp_b);
-
temp_b = write_check >> 8;
-
EEPROM.write(1, temp_b);
-
temp_b = write_check >> 16;
-
EEPROM.write(2, temp_b);
-
temp_b = write_check >> 24;
-
EEPROM.write(3, temp_b);
-
delay(10);
-
}
-
}
-
}
-
void load_from_EEPROM()
-
{
-
long write_check;
-
write_check = 0;
-
write_check = EEPROM.read(0);
-
write_check |= EEPROM.read(1) << 8;
-
write_check |= EEPROM.read(2) << 16;
-
write_check |= EEPROM.read(3) << 24;
-
encoder_L = write_check;
-
}
-
void setup()
-
{
-
load_from_EEPROM();
-
Serial.begin(115200);
-
setup_HT1621();
-
encoder_setup();
-
tme = millis() + 100;
-
}
-
void loop() {
-
{
-
if (tme < millis())
-
{
-
write_digit(encoder_L);
-
Serial.println(Bu2);
-
tme = millis() + 100;
-
}
-
button2_check();
-
}
-
}
-
Рассмотрим модуль encoder.h , он дополнительно нужждается в библиотеке TimerOne.h , которая служить для организации прерывания по таймеру.
Один раз в 20 микросекунд срабатывает прерывание, что приводит к вызову функции обработки прерывания void i_encoder() , которая проверяет состояние пинов D8 D9 и обрабатывает их изменение по приведенному выше закону. В этом же прерывании обрабатывается нажатие кнопок подключенных к D10, D11 (сброс и запись) с учетом минимизации дребезга контактов.
-
#include "TimerOne.h"
-
-
byte encoders;
-
long encoder_L;
-
byte encoder_new_L, encoders_old, encoders_new, encoder_old_L, buttons_new;
-
byte sys_Bu1, sys_Bu2;
-
bool Bu1, Bu2;
-
-
//=================================================
-
void i_encoder() //Обработка по прерыванию энкодера
-
{
-
if (Bu1) encoder_L = 0; // Сброс нуля.
-
-
encoders_new = PINB & B00001111;
-
buttons_new = encoders_new & B1100;
-
if (encoders_new != encoders_old)
-
{
-
// number_move++;
-
encoders_old = encoders_new;
-
encoder_new_L = encoders_new & B11;
-
-
if (encoder_new_L != encoder_old_L)
-
{
-
switch (encoder_old_L)
-
{
-
case 2:
-
{
-
if (encoder_new_L == 3) encoder_L++;
-
if (encoder_new_L == 0) encoder_L--;
-
break;
-
}
-
case 0:
-
{
-
if (encoder_new_L == 2) encoder_L++;
-
if (encoder_new_L == 1) encoder_L--;
-
break;
-
}
-
case 1:
-
{
-
if (encoder_new_L == 0) encoder_L++;
-
if (encoder_new_L == 3) encoder_L--;
-
break;
-
}
-
case 3:
-
{
-
if (encoder_new_L == 1) encoder_L++;
-
if (encoder_new_L == 2) encoder_L--;
-
break;
-
}
-
}
-
encoder_old_L = encoder_new_L;
-
// Записываем новое значение
-
// Предыдущего состояния
-
}
-
}
-
//Обработка нажатич кнопок
-
switch (buttons_new) // Если кнопаки не нажаты то на выходе число 3
-
{
-
case 8: // //Нажата 1 кнопка
-
if (sys_Bu1 < 20) sys_Bu1++;
-
else Bu1 = true;
-
break;
-
case 4: // //Нажата 2 кнопка
-
if (sys_Bu2 < 20) sys_Bu2++;
-
else Bu2 = true;
-
break;
-
default:
-
Bu1 = false;
-
Bu2 = false;
-
sys_Bu2 = 0;
-
sys_Bu1 = 0;
-
break;
-
}
-
-
}
-
-
void encoder_setup()
-
{
-
//пины энкодера
-
pinMode(8, INPUT);
-
pinMode(9, INPUT);
-
pinMode(10, INPUT); //button 1
-
pinMode(11, INPUT);//button 2
-
digitalWrite(10, HIGH); //button 1
-
digitalWrite(11, HIGH); //button 2
-
-
Timer1.initialize(20); //longest pulse in PPM is usally 0.02 milliseconds,
-
-
// encoder_L = 0;
-
encoders_old = PORTB & B11;
-
Timer1.attachInterrupt(i_encoder);
-
}
Работа с дисплеем возложена на модуль ht1621_m.h. Его я нашел на просторах сети и слегка модернизировал под свои цели. Он позволяет выводить на дисплей цифры и точки. Хотя сам дисплей конечно может больше...
-
#define CS 13 //Pin 13 as chip selection output
-
#define WR 12 //Pin 12 as read clock output
-
#define DATA 7 //Pin 7 as Serial data output
-
#define CS1 digitalWrite(CS, HIGH)
-
#define CS0 digitalWrite(CS, LOW)
-
#define WR1 digitalWrite(WR, HIGH)
-
#define WR0 digitalWrite(WR, LOW)
-
#define DATA1 digitalWrite(DATA, HIGH)
-
#define DATA0 digitalWrite(DATA, LOW)
-
#define sbi(x, y) (x |= (1 << y))
-
#define cbi(x, y) (x &= ~(1 <
-
#define uchar unsigned char
-
#define uint unsigned int
-
//定义HT1621的命令
-
#define ComMode 0x52 //4COM,1/3bias 1000 010 1001 0
-
#define RCosc 0x30 //1000 0011 0000
-
#define LCD_on 0x06 //1000 0000 0 11 0
-
#define LCD_off 0x04
-
#define Sys_en 0x02 //1000 0000 0010
-
#define CTRl_cmd 0x80
-
#define Data_cmd 0xa0
-
/*0,1,2,3,4,5,6,7,8,9,A,b,C,c,d,E,F,H,h,L,n,N,o,P,r,t,U,-, ,*/
-
//const char num[]={0x7D,0x60,0x3E,0x7A,0x63,0x5B,0x5F,0x70,0x7F,0x7B,0x77,0x4F,0x1D,0x0E,0x6E,0x1F,0x17,0x67,0x47,0x0D,0x46,0x75,0x37,0x06,0x0F,0x6D,0x02,0x00,};
-
//===================0 1 2 3 4 5 6 7 8 9
-
const char num[] = {0x7D, 0x60, 0x3E, 0x7A, 0x63, 0x5B, 0x5F, 0x70, 0x7F, 0x7B, 0x02, 0x00,};
-
//================ 0 1 2 3 4 5 6 7 8 9 -
-
char dispnum[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-
-
void SendBit_1621(uchar sdata, uchar cnt)
-
{
-
//data cnt HT1621
-
uchar i;
-
for (i = 0; i < cnt; i++)
-
{
-
WR0;
-
if (sdata & 0x80) DATA1;
-
else DATA0;
-
WR1;
-
sdata <<= 1;
-
}
-
}
-
void SendCmd_1621(uchar command)
-
{
-
CS0;
-
SendBit_1621(0x80, 4);
-
SendBit_1621(command, 8);
-
CS1;
-
}
-
void Write_1621(uchar addr, uchar sdata)
-
{
-
addr <<= 2;
-
CS0;
-
SendBit_1621(0xa0, 3);
-
SendBit_1621(addr, 6);
-
SendBit_1621(sdata, 8);
-
CS1;
-
}
-
-
void HT1621_all_off(uchar num)
-
{
-
uchar i;
-
uchar addr = 0;
-
for (i = 0; i < num; i++)
-
{
-
Write_1621(addr, 0x00);
-
addr += 2;
-
}
-
}
-
void HT1621_all_on(uchar num)
-
{
-
uchar i;
-
uchar addr = 0;
-
for (i = 0; i < num; i++)
-
{
-
Write_1621(addr, 0xff);
-
addr += 2;
-
}
-
}
-
void Init_1621(void)
-
{
-
SendCmd_1621(Sys_en);
-
SendCmd_1621(RCosc);
-
SendCmd_1621(ComMode);
-
SendCmd_1621(LCD_on);
-
}
-
void setup_HT1621() {
-
pinMode(CS, OUTPUT); //
-
pinMode(WR, OUTPUT); //
-
pinMode(DATA, OUTPUT); //
-
CS1;
-
DATA1;
-
WR1;
-
delay(50);
-
Init_1621();
-
}
-
void write_digit(long X)
-
{
-
bool minus;
-
minus = false;
-
if (X < 0)
-
{
-
X = -X;
-
minus = true;
-
}
-
if (X > 99999) X = 99999;
-
int i;
-
i = 0;
-
while (true)
-
{
-
if (!i) Write_1621(i, (num[X % 10] | 128)); //Добавляем точку
-
else Write_1621(i, num[X % 10]);
-
i += 2;
-
X /= 10;
-
if (!X) break;
-
}
-
if (minus)
-
{
-
Write_1621(i, 0x02);
-
i += 2;
-
}
-
while (i < 12)
-
{
-
Write_1621(i, 0x00);
-
i += 2;
-
}
-
}
Далее рассмотрим реализацию прибора в натуральном виде. Для этого использовался пакет SolidWorks.
И фото реальной сборки прибора.
К сожалению, я не буду участвовать в испытаниях, т.к. уезжаю в отпуск..., а жаль.