Интернет-лаборатория роботов ZiZiBOT.RU

Проектирование и разработки в области робототехники и автоматизации технологических процессов. Производство готовых роботов и конструкторов для творчества. Консультации и обучение по электронике и программированию.

г. Юрга,
ул.Ленинградская 38/83

+7 923-503-6074

Реанимация гибочного станка, делаем новый электронный съемник данных по повороту вала

К нам обратился предприниматель с просьбой помочь в восстановлении гибочного станка. Он приобрел б/у станок, но на нем сломано устройство для съема данных с вала по углу, на который требуется гнуть металл (в центре).

 

Ниже фото без данного устройства.

Фирменный немецкий съемник имел емкостный счетчик поворота, и фирменный контроллер, подобные схемы применяются в электронный штангенциркулях. Конечно разбираться в чужой схеме довольно сложно и не всегда для этого есть основания (где достать испорченные компоненты?). Поэтому решили заменить устройство на то, что можем сделать самостоятельно. Для этого возможно применить двунаправленный инкрементальный энкодер, который будет снимать информацию с вала.

Инкрементальные энкодеры, независимо от типа (у нас оптический), бывают с одним фотоэлементом и с двумя. Если у энкодера один фотоэлемент, то нет возможности определить, в какую сторону присходит движение/вращение, подобные энкодеры можно применять в тех случаях, когда вращение/движение однонаправленное. В нашем случае вращение может осуществляться в обе стороны, следовательно используется двунаправленный энкодер (с двумя фотоэлементами, разнесенными на половину ширины щели диска). С него, используя "хитрую" программу, можно получить данные о направлении вращения, скорости, величине перемещения.

Состояние углового положения может быть выражено не в абсолютной форме, а относительно предыдущего положения (состояния) энкодера. Зная предыдущее состояние пары фотодатчиков и получив новое, можно говорить о движении на одну позицию вала в определенную сторону. Подсчет этих позиций и дает информацию о текущем положении вала. Рисунок ниже демонстрирует, как будут изменяться состояния фотодатчиков (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() - пишет при необходимости данные в энергонезависимую память.

 
  1. #include < EEPROM.h >
  2. #include "ht1621_m.h"
  3. #include "encoder.h"
  4. unsigned long tme;
  5. void button2_check()
  6. {
  7. static long write_check;
  8. byte temp_b;
  9. if (Bu2) //Если нажата кнопка записи в энергонезависимую память
  10. {
  11. HT1621_all_on(16);
  12. if (write_check != encoder_L)
  13. {
  14. write_check = encoder_L;
  15. //Производим запись
  16. temp_b = write_check;
  17. EEPROM.write(0, temp_b);
  18. temp_b = write_check >> 8;
  19. EEPROM.write(1, temp_b);
  20. temp_b = write_check >> 16;
  21. EEPROM.write(2, temp_b);
  22. temp_b = write_check >> 24;
  23. EEPROM.write(3, temp_b);
  24. delay(10);
  25. }
  26. }
  27. }
  28. void load_from_EEPROM()
  29. {
  30. long write_check;
  31. write_check = 0;
  32. write_check = EEPROM.read(0);
  33. write_check |= EEPROM.read(1) << 8;
  34. write_check |= EEPROM.read(2) << 16;
  35. write_check |= EEPROM.read(3) << 24;
  36. encoder_L = write_check;
  37. }
  38. void setup()
  39. {
  40. load_from_EEPROM();
  41. Serial.begin(115200);
  42. setup_HT1621();
  43. encoder_setup();
  44. tme = millis() + 100;
  45. }
  46. void loop() {
  47. {
  48. if (tme < millis())
  49. {
  50. write_digit(encoder_L);
  51. Serial.println(Bu2);
  52. tme = millis() + 100;
  53. }
  54. button2_check();
  55. }
  56. }
  57.  

 

 

Рассмотрим модуль encoder.h , он дополнительно нужждается в библиотеке TimerOne.h , которая служить для организации прерывания по таймеру. 

Один раз в 20 микросекунд срабатывает прерывание, что приводит к вызову функции обработки прерывания void i_encoder() , которая проверяет состояние пинов D8 D9 и обрабатывает их изменение по приведенному выше закону. В этом же прерывании обрабатывается нажатие кнопок подключенных к D10, D11 (сброс и запись) с учетом минимизации дребезга контактов.

 
  1. #include "TimerOne.h"
  2.  
  3. byte encoders;
  4. long encoder_L;
  5. byte encoder_new_L, encoders_old, encoders_new, encoder_old_L, buttons_new;
  6. byte sys_Bu1, sys_Bu2;
  7. bool Bu1, Bu2;
  8.  
  9. //=================================================
  10. void i_encoder() //Обработка по прерыванию энкодера
  11. {
  12. if (Bu1) encoder_L = 0; // Сброс нуля.
  13.  
  14. encoders_new = PINB & B00001111;
  15. buttons_new = encoders_new & B1100;
  16. if (encoders_new != encoders_old)
  17. {
  18. // number_move++;
  19. encoders_old = encoders_new;
  20. encoder_new_L = encoders_new & B11;
  21.  
  22. if (encoder_new_L != encoder_old_L)
  23. {
  24. switch (encoder_old_L)
  25. {
  26. case 2:
  27. {
  28. if (encoder_new_L == 3) encoder_L++;
  29. if (encoder_new_L == 0) encoder_L--;
  30. break;
  31. }
  32. case 0:
  33. {
  34. if (encoder_new_L == 2) encoder_L++;
  35. if (encoder_new_L == 1) encoder_L--;
  36. break;
  37. }
  38. case 1:
  39. {
  40. if (encoder_new_L == 0) encoder_L++;
  41. if (encoder_new_L == 3) encoder_L--;
  42. break;
  43. }
  44. case 3:
  45. {
  46. if (encoder_new_L == 1) encoder_L++;
  47. if (encoder_new_L == 2) encoder_L--;
  48. break;
  49. }
  50. }
  51. encoder_old_L = encoder_new_L;
  52. // Записываем новое значение
  53. // Предыдущего состояния
  54. }
  55. }
  56. //Обработка нажатич кнопок
  57. switch (buttons_new) // Если кнопаки не нажаты то на выходе число 3
  58. {
  59. case 8: // //Нажата 1 кнопка
  60. if (sys_Bu1 < 20) sys_Bu1++;
  61. else Bu1 = true;
  62. break;
  63. case 4: // //Нажата 2 кнопка
  64. if (sys_Bu2 < 20) sys_Bu2++;
  65. else Bu2 = true;
  66. break;
  67. default:
  68. Bu1 = false;
  69. Bu2 = false;
  70. sys_Bu2 = 0;
  71. sys_Bu1 = 0;
  72. break;
  73. }
  74.  
  75. }
  76.  
  77. void encoder_setup()
  78. {
  79. //пины энкодера
  80. pinMode(8, INPUT);
  81. pinMode(9, INPUT);
  82. pinMode(10, INPUT); //button 1
  83. pinMode(11, INPUT);//button 2
  84. digitalWrite(10, HIGH); //button 1
  85. digitalWrite(11, HIGH); //button 2
  86.  
  87. Timer1.initialize(20); //longest pulse in PPM is usally 0.02 milliseconds,
  88.  
  89. // encoder_L = 0;
  90. encoders_old = PORTB & B11;
  91. Timer1.attachInterrupt(i_encoder);
  92. }

 

Работа с дисплеем возложена на модуль ht1621_m.h. Его я нашел на просторах сети и слегка модернизировал под свои цели. Он позволяет выводить на дисплей цифры и точки. Хотя сам дисплей конечно может больше...

 


 
  1. #define CS 13 //Pin 13 as chip selection output
  2. #define WR 12 //Pin 12 as read clock output
  3. #define DATA 7 //Pin 7 as Serial data output
  4. #define CS1 digitalWrite(CS, HIGH)
  5. #define CS0 digitalWrite(CS, LOW)
  6. #define WR1 digitalWrite(WR, HIGH)
  7. #define WR0 digitalWrite(WR, LOW)
  8. #define DATA1 digitalWrite(DATA, HIGH)
  9. #define DATA0 digitalWrite(DATA, LOW)
  10. #define sbi(x, y) (x |= (1 << y))
  11. #define cbi(x, y) (x &= ~(1 <
  12. #define uchar unsigned char
  13. #define uint unsigned int
  14. //定义HT1621的命令
  15. #define ComMode 0x52 //4COM,1/3bias 1000 010 1001 0
  16. #define RCosc 0x30 //1000 0011 0000
  17. #define LCD_on 0x06 //1000 0000 0 11 0
  18. #define LCD_off 0x04
  19. #define Sys_en 0x02 //1000 0000 0010
  20. #define CTRl_cmd 0x80
  21. #define Data_cmd 0xa0
  22. /*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,-, ,*/
  23. //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,};
  24. //===================0 1 2 3 4 5 6 7 8 9
  25. const char num[] = {0x7D, 0x60, 0x3E, 0x7A, 0x63, 0x5B, 0x5F, 0x70, 0x7F, 0x7B, 0x02, 0x00,};
  26. //================ 0 1 2 3 4 5 6 7 8 9 -
  27. char dispnum[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  28.  
  29. void SendBit_1621(uchar sdata, uchar cnt)
  30. {
  31. //data cnt HT1621
  32. uchar i;
  33. for (i = 0; i < cnt; i++)
  34. {
  35. WR0;
  36. if (sdata & 0x80) DATA1;
  37. else DATA0;
  38. WR1;
  39. sdata <<= 1;
  40. }
  41. }
  42. void SendCmd_1621(uchar command)
  43. {
  44. CS0;
  45. SendBit_1621(0x80, 4);
  46. SendBit_1621(command, 8);
  47. CS1;
  48. }
  49. void Write_1621(uchar addr, uchar sdata)
  50. {
  51. addr <<= 2;
  52. CS0;
  53. SendBit_1621(0xa0, 3);
  54. SendBit_1621(addr, 6);
  55. SendBit_1621(sdata, 8);
  56. CS1;
  57. }
  58.  
  59. void HT1621_all_off(uchar num)
  60. {
  61. uchar i;
  62. uchar addr = 0;
  63. for (i = 0; i < num; i++)
  64. {
  65. Write_1621(addr, 0x00);
  66. addr += 2;
  67. }
  68. }
  69. void HT1621_all_on(uchar num)
  70. {
  71. uchar i;
  72. uchar addr = 0;
  73. for (i = 0; i < num; i++)
  74. {
  75. Write_1621(addr, 0xff);
  76. addr += 2;
  77. }
  78. }
  79. void Init_1621(void)
  80. {
  81. SendCmd_1621(Sys_en);
  82. SendCmd_1621(RCosc);
  83. SendCmd_1621(ComMode);
  84. SendCmd_1621(LCD_on);
  85. }
  86. void setup_HT1621() {
  87. pinMode(CS, OUTPUT); //
  88. pinMode(WR, OUTPUT); //
  89. pinMode(DATA, OUTPUT); //
  90. CS1;
  91. DATA1;
  92. WR1;
  93. delay(50);
  94. Init_1621();
  95. }
  96. void write_digit(long X)
  97. {
  98. bool minus;
  99. minus = false;
  100. if (X < 0)
  101. {
  102. X = -X;
  103. minus = true;
  104. }
  105. if (X > 99999) X = 99999;
  106. int i;
  107. i = 0;
  108. while (true)
  109. {
  110. if (!i) Write_1621(i, (num[X % 10] | 128)); //Добавляем точку
  111. else Write_1621(i, num[X % 10]);
  112. i += 2;
  113. X /= 10;
  114. if (!X) break;
  115. }
  116. if (minus)
  117. {
  118. Write_1621(i, 0x02);
  119. i += 2;
  120. }
  121. while (i < 12)
  122. {
  123. Write_1621(i, 0x00);
  124. i += 2;
  125. }
  126. }

 

Далее рассмотрим реализацию прибора в натуральном виде. Для этого использовался пакет SolidWorks.

И фото реальной сборки прибора.

 

К сожалению, я не буду участвовать в испытаниях, т.к. уезжаю в отпуск..., а жаль. 

 

Скачать программу...

 


X

Написать сообщение:

Укажите свой номер телефона или e-mail для обратной связи
- e-mail
ИЛИ
- номер телефона

Текст сообщения: