МК-управление микрошаговым драйвером LB1847 из старого принтера

Данная статья родилась в помыслах изготовить себе трёх-координатный микростанок с ЧПУ для выполнения некоторых минимальных задач по сверлению, фрезерованию и вырезке печатных плат.
В течение некоторого времени мне очень часто на запчасти отдают старые матричные и струйные принтеры по причинам того, что хозяева решили купить себе новый и более совершенный принтер или МФУ, ибо старенький свой принтер уже морально устарел или его ремонт будет стоить соизмеримо с приобретением нового принтера, а старый попросту выкинули.
После разборки и выброса ненужных пластмассовых деталей и внутренней механики, я себе оставлял только печатные платы, шаговые двигатели с пасиками и стальные направляющие, по которым когда-то бегала печатающая головка. Давным-давно я посматривал в них на интересную микросхему, которая питает тамошние шаговые двигатели.
Просто запросив в поиске даташит на данную микросхему, я увидел в ней не просто драйвер с четырьмя парами ключей, а полноценный микрошаговый ШИМ контроллер.

Итак, микросхема LB1847 (даташит PDF) - это драйвер для шаговых двигателей с широтно-импульсным управлением током обмоток биполярного двигателя.
Особенностью данной микросхемы является возможность установки тока на обмотки двигателя в 15 шагов в любой полуфазе.
С возможностью установки медленного затухания тока, быстрого спада тока или смешанного режима, тем самым повышая частотные характеристики, которыми можно добиться высокоточного управления и получить наименьшие вибрации двигателя.

Не буду заниматься комментированием оригинального даташита, вы просто можете запросить его в поиске, найти во вложении к статье и самостоятельно изучить характеристики. Я же двинусь далее.

Схема подключения довольно простая. 

lb1847.jpg

 В считанные минуты была нарисована печатка и при помощи ЛУТа сделана плата, обвязку из резисторов, конденсаторов и диодов Шоттки, тоже снял со старой платы:

plat.jpg

Далее это все было подключено к одной из моих самодельных тестовых плат с микроконтроллером Atmega32. Конечно, можно использовать любую доступную вам, но нужно учитывать, что должно хватать выводов для подключения, так как на управление используется аж 12 линий.

В моём случае изначально тестировался максимально возможный режим работы драйвера в режиме Phase 4W1-2, но потом посчитал что это уже чересчур и настолько уже сверх-точность мне не нужна, потому и перевел на режим Phase 2W1-2, тем самым просмотрев данную таблицу выявил закономерность по первым входам каждого плеча драйвера - на них постоянно присутствует логическая "1" в нужном для меня режиме.

PWM_port_medium.jpg

Ну и пусть, просто подключим их на питание микросхемы и забудем про них.

Так мы сократили количество используемых выводов до 10. При дальнейшем исследовании этой таблицы явно заметно, что вывод ENABLE получая логическую "1" (просто обесточивает канал, давая возможность другому каналу притянуть к себе магнитный ротор на максимальном токе), и при этом не имеет значения в какой фазе этот канал находится.

Сразу заметно, что та самая единица появляется тогда как на входах 2-3-4 каждого канала присутствует логический "0". Тут просто вспомним о справочнике микросхем логики и найдем нужный для нас логический элемент. Нам понадобится два элемента 3ИЛИ-НЕ, выбираем микросхему, импортная 7427, отечественная К155ЛЕ4 или подобные.

Принципиальная схема:

Схема

Обвязку полного подключения LB1847 не изображал, так как она ничем не отличается от той, что в даташите. На Vref временно поставил проволочный подстроечный резистор.

Как видно, теперь мы используем только 8 выводов для управления, чего вполне достаточно для использования одного полного порта микроконтроллера.

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

Тут я просто использовал программу Excell, где создал таблицу и встроенными формулами рассчитал значение PORTB для режима Phase 2W1-2 (учтите, что пример в даташите указан только для одной фазы, необходимо продублировать его для второй с изменением направления тока через обмотки).

excell.jpg

Сразу забегу вперед. Я изначально вдоволь наигрался как заданием статических величин по значениям перемещения вала, так и с вводом значения перемещения через порт RS232, но потом всё же захотелось вручную лицезреть сие детище и подключил энкодер, дабы насладиться механическим управлением с визуальным вращением вала шагового двигателя.

mc210_sch2.png

Программировал на CodeVisionAVR.

Микроконтроллер Atmega32, Кварц 16МГц, PORTB весь на выход, PC0 и PC1 на вход с подтяжкой к шине питания, настраиваем таймер на обработку прерывания каждые 10 микросекунд.
 
Сначала объявим переменные
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
unsigned int timerCNC=0;                // Счетчик-Таймер ожидания готовности к смене шага
unsigned long int CNC1required=25000;   // Начальное требуемое значение шага
char flagCNC1;                          // Флаг разрешения обработки шага
#define CNC1steps 32                    // Количество шагов для 1/32 шага в обоих полюсах двух фаз
unsigned long int CNC1point=25000;      // Начальная/текущая точка положения шага
unsigned char CNC1counter=0;            // Счетчик смещения. В дальнейшем приведем к значению маски в 32 варианта смещения
unsigned long EncState;                 // Состояние энкодера.
unsigned char step[CNC1steps] =         // Массив значений для шагания
{
0b01110000, //0
0b01110010, //1
0b00110100, //2
0b01010110, //3
0b00011000, //4
0b01101010, //5
0b00101100, //6
0b01001110, //7
0b00001110, //8
0b11001110, //9
0b10101100, //10
0b11101010, //11
0b10011000, //12
0b11010110, //13
0b10110100, //14
0b11110010, //15
0b11110000, //16
0b11110011, //17
0b10110101, //18
0b11010111, //19
0b10011001, //20
0b11101011, //21
0b10101101, //22
0b11001111, //23
0b00001111, //24
0b01001111, //25
0b00101101, //26
0b01101011, //27
0b00011001, //28
0b01010111, //29
0b00110101, //30
0b01110011};//31

 

Функция опроса энкодера

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
void EncoderScan (void){
 
unsigned char New;
 
New = PINC & 0x03;    // Берем текущее значение
 
// И сравниваем со старым
 
switch(EncState)
    {
    case 2:
        {
            if(New == 3){
            CNC1required++;
            };
 
            if(New == 0){
            CNC1required--;
            };
        break;
        }
 
    case 0:
        {
            if(New == 2){
            CNC1required++;
            };
 
            if(New == 1){
            CNC1required--;
            };
        break;
        }
    case 1:
        {
            if(New == 0){
            CNC1required++;
            };
 
            if(New == 3){
            CNC1required--;
            };
        break;
        }
    case 3:
        {
            if(New == 1){
            CNC1required++;
            };
                 
            if(New == 2){
            CNC1required--;
            };
        break;
        }
    }
  
EncState = New;        // Записываем новое значение предыдущего состояния
}

Функция опроса энкодера не имеет никаких особенностей, банально читает значения с выводов и по их изменению добавляет или отнимает значение счетчика, тем самым диктуя главной программе направление на вращения. Единственное что опрос у меня сейчас проходит на частоте 100кГц, и мне было лень добавлять отдельный счетчик (три строчки программы), чтобы отсчитывать только полные щелчки оборота энкодера, да это и совсем не нужно на данной стадии тестовых испытаний. 

 

Обработчик прерывания таймера

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
// Reinitialize Timer 0 value
TCNT0=0xEC;
// Place your code here
 
EncoderScan(); // Производим опрос энкодера
 
//Счетчик временной задержки между шагами
//Чтоб не пропустить шаг на высокой скорости
timerCNC++;
if(timerCNC == 100) {       //Если значение достигло
flagCNC1=1;                 //разрешаем шагнуть
timerCNC=0;                 //перезапускаем счетчик 
}

Обработчики шагов

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Обработчик прибавления шагов
// задается значение S на количество шагов перемещения вверх
void STEP_UP (unsigned char s)
    {
    if (flagCNC1){                  //Проверяем флаг разрешения смещения
 
    unsigned char q;
 
    CNC1counter +=s;                //Записываем новое значение смещения
    q = CNC1counter & 0b00011111;   //Получаем номер значения из CNC1steps по маске
    PORTB = step[q];                //Производим запись в порт нужный байт шага               
    CNC1point+=s;                   //Добавляем новое значения текущего положения двигателя
 
    flagCNC1=0;                     //Очищаем флаг, смещение завершено, шаг выполнен.
    }
}

Аналогичная функция на обработку обратного счета для движения оси шагового двигателя в обратную сторону.

1
2
3
4
5
6
7
8
9
10
11
12
13
// Обработчик вычитания шагов
// задается значение S на количество шагов перемещения вниз
void STEP_DOWN (unsigned char s)
    {
    if (flagCNC1){
    unsigned char q;
    CNC1counter -=s;
    q = CNC1counter & 0b00011111;
    PORTB = step[q];
    CNC1point-=s;
 
    flagCNC1 = 0;
    }

В данную функцию я ввел очень полезную величину, можно задать шаг работы двигателя от 1 до 8. Это я и хочу использовать в дальнейшем, чтобы можно было программно управлять скоростью перемещения.

Например: для холостого перемещения на пару тысяч шагов можно составить простой алгоритм, который может плавно начать с одного микрошага за такт разрешения таймера, и каждые 10 тактов поднимать на единицу пока не достигнет "8", так будет программно реализован четверть шаг (счетчик тоже будет добавлять или отнимать по 8 шагов), а далее за 100 шагов до окончания пути начать уменьшать значение перешагивания каждые 10 тактов и двигатель плавно остановится на нужном ему значении. Такая реализация программно обеспечит высокую скорость перемещения при максимальной точности перемещения вала двигателя даже под нагрузкой (старт-разгон-работа-торможение-остановка). Можно, конечно, поднять значение и до 16, в таком случае двигатель выйдет на режим полушага.

Главный цикл программы

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void main(void)
{
//
// Код инициализации микроконтроллера
//
 
PORTB = step[0];  // После включения записываем в порт нулевое значение шага
 
while (1)
    {
    // Обработка циклического перемещения пока CNC1required не станет равным CNC1point
 
          while (CNC1required>CNC1point) 
          {
            STEP_UP(1);
          }
          while (CNC1required < CNC1point) 
          {
            STEP_DOWN(1);
          }
    }
}

Для проверок использовался один из биполярных шаговых двигателей с тех самых разобранных принтеров.

P5113956.jpg

Он имеет шаг 7,5 градусов, что соответствует 48 шагам на полный оборот, при 32 микрошагах это выходит точность 1536 микрошагов на полный оборот вала двигателя. Если бы нам не было жалко использовать еще 2 вывода микроконтроллера, то легко можно получить 1/64 шага. А присмотревшись в конструктив этой микросхемы, думаю несложно и поболее 128 шагов сделать, только придется много расчетов произвести на усредненные значения, правда будет серьёзная нелинейность вращения, но и то что мы получили вполне достаточно, незачем нам вращение менее 0,1 градуса.

Энкодер, что я нашел у себя, имеет 24 щелчка на полный оборот, в каждом щелчке 4 импульса изменения состояния, то есть 96 импульсов на полный оборот.

Без использования энкодера программно запускал его на довольно быстрое вращение и действительно чувствовалась сила на валу при том, что я его питаю 12V вместо 24V родного питания принтера.

При необходимости можно сохранять в энерго-независимой памяти текущие значения шага, и использовать его при отключениях устройства, только заранее привести значение к нулевому, ибо после отключения-включения устройства полушаг может провернуть вал как в одну, так и в другую сторону. Или просто использовать калибровку (например, на оптопаре или концевике) при включении устройства.

Данная статья была предварительным тестом работы микросхемы LB1847, все собрано практически на коленке, только для уточнения всех нюансов её работы. Далее планируется использовать более продвинутый микроконтроллер (скорее всего STM32) и организация одновременного управления тремя (и более) двигателями.

При необходимости можно еще дополнительно вывести на МК выводы DECAY, MD и программно управлять режимом спада тока при различных условиях.

Прикрепленные файлы: