КОТ++
Прикладное программирование
Системное программирование
Программирование микроконтроллеров

Если вы дадите человеку программу, то займете его на один день. Если вы научите человека программировать, то займете его на всю жизнь. (с) Васим Латиф

Поиск по тегам

Опубликовано
Комментарии 0

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

Книга содержит объяснение различных методик оптимизации, листинги C и ASM программ с примерами оптимизации (развернутые примеры представлены на CD диске, который прилагается к книге), а также методы определения слабых, с точки зрения оптимизации, мест в программе.

Теги ,
Автор

Опубликовано
Комментарии 2

Так как я в процессе программирования больше всего ценю сложность задач, то решил ознакомиться с программированием микроконтроллеров. Считаю, что программирование мк довольно не тривиальная задача и есть над чем поразмыслить. Да и не всю же жизнь знать только одну лишь Arduino. Начинаем вылезать из ардуиновских штанов. Я хотел начать с stm32, но всё-таки решил, что хочу попробовать PIC для начала. Скачал MPLAB IDE X, хочу сказать, что это очень удобная среда разработки, тем более после Arduino IDE уж точно. Далее устанавливаю компилятор XC8, вот тут я немного подгорел, что компилятор не включён в среду разработки, или же хотя бы нет скачки и установки прям через саму MPLAB IDE. Но не страшно, установка достаточно простая.

Далее я создаю проект, выбираю контроллер PIC18F4550, выбор обусловлен тем, что пока я смотрел в гугле информацию по PIC, то часто попадались примеры и туториалы именно по этой модели контроллера. Настало время поставить перед собой задачу и решить её. На мой взгляд очень интересная и полезная задача (возможно потом использую в дальнейших проектах) - это реализация некоторого протокола для связи с ПК по COM порту. То есть в программе нужно реализовать связь по UART в рамках некоторого протокола.

Протокол, который я буду реализовывать: Первый байт пакета - длина пакета. Второй байт пакета - номер пакета. Третий байт - команда. Остальные байты - какие-то данные. Последние два байта - контрольная сумма пакета, буду использовать CRC16. Пакеты должны следовать строго по нумерации, нумерацию будем считать одним байтом, после 0го пакета должен следовать 1ый, 1го - 2ой ... 254го - 255ый, 255го - 0ой. Пакеты, которые ПК пришлёт вне нумерации будут отброшены. Пакеты с неверной контрольной суммой тоже будут отброшены. Контроллер не подтверждает приём каждого пакета, заместо этого он периодически шлёт пакет, в котором содержится информация с номером последнего принятого пакета.

А теперь время приступить к написанию кода. Начать я решил с того, чтобы разобраться как работать с UART в своей программе. Оказалось, что есть два варианта: через библиотеки или напрямую работая с определёнными регистрами. Библиотечные методы довольно просты, я решил ознакомиться с ними, но потребовалось дополнительно установить библиотеку "Peripheral Library", но вот сколько я не мучался настроить работать компилятор XC8 с ней - мне не удалось. Было решено напрямую управлять регистрами из программы для работы с UART. Реализовал по примеру из инета, теперь нужно как-то это всё проверить.

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

Первый: есть замечательная программа PIC18 Simulator IDE от разработчика OshonSoft, она позволяет симулировать работу мк PIC 18-ой серии. К слову, на сайте присутствуют и симуляторы под PIC других серий, а также симулятор AVR. Также у них есть программа для мониторинга последовательного порта, отличная штука, до этого мне попадались мониторы COM порта отвратительного качества, с глюками, либо же платные, которые и попадаются в гугле. Так что Serial Port Monitor от OshonSoft очень годная штука и дальше ещё может пригодиться.

Работать в данном симуляторе достаточно просто, выбираем частоту, нужную модель контроллера, загружаем прошивку в виде .hex файла, выбираем нужный темп (скорость) симуляции, стартуем симуляцию. Программа имеет широкий спектр инструментов, которые представлены на скрине. Нам же сейчас нужен инструмент для проверки работоспособности UART. Для этого есть Hardware UART Simulation Interface и Software UART Simulation Interface. В Hardware interface всё заработало отлично, то есть видно что приходят байты нам в качестве ответа. А вот Software interface у меня так и не завёлся, к слову я считаю что это какой-то баг. В любом случае, эта программа не позволяет работать с мк через виртуальный COM порт, поэтому не смотря на многочисленные достоинства этой программы, она на данный момент не подходит.

Второй способ: симуляция работы мк в Proteus. Этот способ и придётся использовать, так как для работы с мк хочется иметь в системе COM порт, по которому можно послать данные контроллеру, или получить данные от контроллера, а Proteus это точно умеет. С Proteus я особо никогда не работал, поэтому не особо хотелось с ним знакомиться, но надо, так надо. Далее расскажу как создать виртуальный COM порт в системе и соединиться через него с симулированным мк в Proteus.

Кидаем на рабочую область нужный нам микроконтроллер, кидаем компонент COMPIM, соединяем TX с TX, RX с RX. Указываем в настройках микроконтроллера в Proteus нужную частоту и .hex файл. Далее нам нужно создать и соединить между собой два виртуальный последовательных порта. С этим нам поможет программа com0com. В ней ничего сложного, там уже есть созданные порты, выглядеть должно как на скрине. Теперь один COM порт прописываем в настройках COMPIM в Proteus, а другой можно использовать либо в своей собственной программе либо в уже готовой терминальной программе (например Putty), главное выставить верный baudrate.

Но тут не всё так просто. Потому что моя программа для PIC наотрез отказывалась верно работать в Proteus. Сначала я винил скрытые ошибки в моём коде, поэтому раз за разом пробовал всё новые примеры кода для работы с UART из гугла. Когда уже стало понятно, что в коде ошибки точно нет, то закралась мысль, что Proteus при настройке тактовой частоты в 4МГц и baudrate в 9600 что-то не так моделирует, может что-то не успевает и где-то обсчитывается. Я понизил тактовую частоту до 1МГц и baudrate до 300 - и заработало! Какое-то время я винил косяки в симуляции Proteus, но на форуме добрые люди подсказали, что при моих изначальных параметрах ошибка UART достигала 6.99% и отсюда следовали все проблемы. По даташиту выбрал другие параметры, чтобы и 9600 и 4МГц, и в итоге прошивка отлично заработала! Так как во всех туториалах, которые я прочитал по UART на PIC не было сказано про эту проблему ни слова, а промучался я с этим несколько часов, то хочу этим текстом обратить внимание на это новичков. На одном скрине параметры, которые портят работу с UART, на другом уже с ошибкой всего 0.16%, с которой можно хорошо жить.

А вот код программы:

#define _XTAL_FREQ 4000000

#include <xc.h>
#include <stdint.h>
#include <stdbool.h>

#include "fastcrc.h"
#include "commands.h"

struct Packet
{
    uint8_t length;
    uint_least8_t data[100];
};

uint8_t lastNumPacket;

void initUART(void);
void writeUART(unsigned char data);

bool dataAvailable(void);
bool overrunError(void);

void analyzePacket(struct Packet* packet);
bool checkCheckSum(struct Packet* packet);
bool checkCorrectNumPacket(uint8_t numPacket);

void main(void) 
{     
    initUART();

    uint8_t counterBytes = 0;
    lastNumPacket = 255;
    struct Packet packet;

    while (1)
    {
        if (dataAvailable() == true)
        {
            if (overrunError() == true)
            {           
                CREN = 0;
                NOP();
                CREN = 1;
            }
            if (counterBytes == 0)
            {
                packet.data[0] = RCREG;
                packet.length = packet.data[0];
                counterBytes++;
            }
            else 
            {
                packet.data[counterBytes] = RCREG;
                counterBytes++;
                if (counterBytes == packet.length)
                {
                    counterBytes = 0;
                    if (checkCorrectNumPacket(packet.data[1]) == true && checkCheckSum(&packet) == true)
                    {
                        lastNumPacket++;
                        writeUART(0x33);
                        analyzePacket(&packet);
                    }
                }
            }
        }
    }
    return;
}

void initUART()
{
    TXSTA = 0; //Reset USART registers to POR state
    RCSTA = 0;

    TRISC6 = 0; //TX as output
    TRISC7 = 1; //RX as input
    SYNC = 0; //Async operation
    TX9 = 0;  //No tx of 9th bit
    RX9 = 0;  //No rx of 9th bit
    TXEN = 1;  //Enable transmitter
    CREN = 1; //Enable receiver
    SPEN = 1; //Enable serial port

    TXIE=0; //disable tx interrupts
    RCIE=0; //disbale rx interrupts 

    BRG16 = 0; //Divisor at 8 bit
    BRGH = 1; //No high-speed baudrate
    SPBRG = 25;  //9600 baudrate

    RCIF = 0;
}

void writeUART(unsigned char data)
{
  while(!TRMT);
  TXREG = data;
}

bool dataAvailable()
{
    if (RCIF != 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool overrunError()
{
    if (RCSTAbits.OERR)
    {
        return true;
    }
    else
    {
        return false;
    }
}

void analyzePacket(struct Packet* packet)
{
    uint8_t command = packet->data[2];
    switch (command)
    {
        case COMMAND_1: break;
    }
}

bool checkCorrectNumPacket(uint8_t numPacket)
{
    uint8_t ifReceivedLastNumPacket = lastNumPacket + 1;
    if (ifReceivedLastNumPacket == numPacket)
    { 
        return true;   
    }    
    else
    {
        return false;
    }
}

bool checkCheckSum(struct Packet* packet)
{
    uint16_t packetCrc = *((uint16_t*)(&packet->data + packet->length - 2));
    uint16_t calculatedCrc = fast_crc_blk(&(packet->data), packet->length - 2);
    if (calculatedCrc == packetCrc)
    {
        return true;
    }
    else
    {
        return false;
    }
}

Логика кода такая: инициализируем UART, далее в бесконечном цикле ждём байты, если принят байт, то считаем его за длину пакета, отсчитываем длину, далее проверяем соответствие нумерации и контрольную сумму (реализацию расчёта контрольной суммы я приводить тут не стал, так как тема статьи это не алгоритмы подсчёта контрольных сумм), если проверка прошла успешно, то принимаем пакет и выполняем команду из этого пакета.

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

Теги , ,
Автор

Опубликовано
Комментарии 0

Не так давно мне поступил заказ на написание программы для windows, которая будет управлять по Ethernet ЧПУ контроллером PLCM от purelogic. Нужно было сделать не готовый софт, а все основные модули для работы с контроллером. Обязательно на Borland Delphi 7, что меня не могло не расстроить – это же старьё, ну так как человек, который после будет работать с моим кодом ничего другого не знает, то куда деваться то.

Протокол для работы с устройством был предоставлен. Связь по UDP, соответственно отслеживание потери пакетов перекладывается на мои плечи, для этого в протоколе предусмотрена нумерация каждого пакета, контроллер периодически говорит какой номер у последнего принятого пакета. Первые и последние байты – длина пакета, второй – нумерация, остальное – команда и данные.

Научив программу считать контрольную сумму (CRC16), уже удалось убедиться что контроллер принимает и понимает отправленные ему пакеты. Но, конечно, не обошлось без Wireshark, так как документация по протоколу не была полной, пришлось смотреть некоторые каверзные моменты протокола вживую, я изучал то что шлёт программа Mach3. Были реализованы команды: управление пинами, сброс (для обозначения начала связи), ручное перемещение осей, поиск базы, остановка, авария, траекторные данные, установка текущих координат, настройка ограничений перемещений, настройка портов ввода-вывода, умножитель скорости.

Команда управления пинами подразумевает управление пинами на трёх портах, по историческим причинам ЧПУ станков эти порты повторяют LPT, что изначально я не знал, поэтому были сложности. Ручное перемещение осей сделал с управлением клавиатурой, нажимая клавиши можно перемещать оси. Самое главное нужно было сделать интерпретацию G кода. И большим разочарованием для меня стало то, что контроллер этого делать оказывается не умеет. Поэтому пришлось сделать парсер G кодов, класс математических расчётов траекторий и интерпретатор (который посылал расчитанные траектории контроллеру и следил за выполнением каждой строчки кода).

Как уже можно было догадаться, самая большая сложность как раз в правильных математических расчётах траекторий. Учитывая то, что нужны разгоны и торможения осей, соответственно может быть два вида такого движения – ось успела полностью разогнаться и затормозить (график трапеция), ось не успела разогнаться и уже пора тормозить (график треугольник). Очень не хотелось изобретать велосипед, и я начал искать решение в интернете. Как только я понял, что в гугле ничего нормального не находится, то решил покопаться в arduino библиотеках, там же наверняка реализованы разгоны для шаговых двигателей, но вскоре стало ясно, что разобраться в этом arduino коде не получится. Поискал я книги по ЧПУ и тоже не нашёл нужной мне информации, всё только очень обобщённо. И было решено всё же изобрести этот велосипед. Алгоритм я разработал отталкиваясь от физического смысла движения, то есть работал по формулам с ускорениями и скоростями, мне кажется этот подход правильным. С математикой было очень много проблем, потому что с первого раза алгоритм корректно не работал, пришлось сделать unit тесты, которые очень помогли отладить алгоритм и исправить ошибки. На скрине пример работы тестовой программы, графики отображают движение осей, таким образом я синхронизировал оси.

Итог в этой публикации.

В заключении хотел сказать, что на рынке сейчас очень много различных ЧПУ контроллеров и ПО к ним, о чём я раньше не знал. Из этого можно сделать вывод, что всевозможные бесплатные решения на arduino, raspberry pi с grbl, LinuxCNC не покрывают все требования к ЧПУ.
И ещё: LPT порт пережиток прошлого (на мой взгляд), но всё равно от него есть следы в современных контроллерах ЧПУ. Как я понимаю раньше драйверы шаговых двигателей подключали к компьютеру через LPT порт, и теперь когда люди подключают драйверы уже в отдельный контроллер, то видимо всем удобней, чтобы входы-выходы на этих контроллерах повторяли LPT порты.

Теги , , ,
Автор

Опубликовано
Комментарии 0

Решил попрограммить на Wct. Для тех кто не знает, Wct – система счисления, которая полностью состоит из букв (A, B, C, D, E, F, G, H, I, J, K, L, M, N, P, O). Подробнее тут.
Такое программирование будет очень интересно для тех, кто любит поразмять мозги. Написал прогу, которая выводит I LOVE WCT PROGRAMMING. По началу было трудно, но быстро разобрался в Wct. Прога под dos, т.к. если писать под windows или linux, то после может понадобиться психотерапевт.
Код моей первой Wct программы:


LEAJLKANABMNCBLEAIMNCBMNCAEJCAEMEOFGEFCAFHEDFECAFAFCEOEHFCEBENENEJEPEHCE

PS: Замахнуться что ли на игруху на Wct :D

Теги ,
Автор