Zimbra lets encrypt

Давным-давно выкладывал скрипт для обновления SSL у зимбры. С тех пор прошло много времени, поэтому капельку обновил

#!/bin/bash
cp -f /etc/letsencrypt/live/mail.ka12.co/* /opt/zimbra/ssl/letsencrypt/
wget -O /tmp/ISRG-X1.pem https://letsencrypt.org/certs/isrgrootx1.pem.txt
cat /tmp/ISRG-X1.pem > /opt/zimbra/ssl/letsencrypt/fullchain.pem 
cat /etc/letsencrypt/live/mail.ka12.co/fullchain.pem >> /opt/zimbra/ssl/letsencrypt/fullchain.pem 

chown zimbra:zimbra /opt/zimbra/ssl/letsencrypt/*
runuser -u zimbra /opt/zimbra/bin/zmcertmgr verifycrt comm /opt/zimbra/ssl/letsencrypt/privkey.pem /opt/zimbra/ssl/letsencrypt/cert.pem /opt/zimbra/ssl/letsencrypt/fullchain.pem
runuser -l zimbra -c 'zmcontrol stop'
cp -a /opt/zimbra/ssl/zimbra /opt/zimbra/ssl/zimbra.`date "+%Y%m%d"`

runuser -u zimbra cp /opt/zimbra/ssl/letsencrypt/privkey.pem /opt/zimbra/ssl/zimbra/commercial/commercial.key
runuser -u zimbra /opt/zimbra/bin/zmcertmgr deploycrt comm /opt/zimbra/ssl/letsencrypt/cert.pem /opt/zimbra/ssl/letsencrypt/fullchain.pem
runuser -l zimbra -c '/opt/zimbra/bin/zmcertmgr deploycrt comm /opt/zimbra/ssl/letsencrypt/cert.pem /opt/zimbra/ssl/letsencrypt/fullchain.pem'
runuser -l zimbra -c 'zmcontrol start'

mail.ka12.co замените на свой домен. Принцип простой – сначала любым привычным способом получаете сертификат через LE, а потом запуская скрипт выше засовываете сертификат в zimbra

Скорость работы VCP у STM32

Одним из самых распространенных вариантов обмена информации с внешним устройством это последовательный порт. Давно известная технология, куча примеров для любых языков программирования и все ошибки давно уже найдены и описаны. Сейчас обычно используется COM-over-USB, так как переписывать ничего не надо.

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

Для начала сгенерировал в STM32CubeMX пустой проект. В котором есть только USB и он определен как CDC.

Потом прямо в коде приема блока тут же его отправляю его назад. Кусок из usbd_cdc_if.c

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 6 */
  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
  CDC_Transmit_FS(&Buf[0], *Len);
  USBD_CDC_ReceivePacket(&hUsbDeviceFS);
  return (USBD_OK);
  /* USER CODE END 6 */
}

И написал маленькую программку на питоне, которая тупо спамит в порт увеличивающимися блоками и замеряет скорость. Можно взять тут https://github.com/kiltum/usb-rs485/blob/master/test/test/test.py

import time
import threading
import serial
# f042
#ser = serial.Serial(port='/dev/cu.usbmodem2058335047481')
# f303
ser = serial.Serial(port='/dev/cu.usbmodem2057385756311')

ser.isOpen()
# For windows
#ser.set_buffer_size(rx_size=262144, tx_size=262144)

bytesReceived = 0
minimalSpeed = 10000000
maximumSpeed = 0
counterStep = 0
blockSize = 1
shallExit = 0


def res():
    global bytesReceived
    global minimalSpeed
    global maximumSpeed
    global counterStep
    global blockSize
    global ser
    global shallExit

    if minimalSpeed > bytesReceived:
        if bytesReceived > 0:
            minimalSpeed = bytesReceived
    if maximumSpeed < bytesReceived:
        maximumSpeed = bytesReceived
    bytesReceived = 0

    counterStep = counterStep + 1
    if counterStep > 60:
        print("BlockSize:", blockSize, "Minimal:", minimalSpeed, "Maximum:", maximumSpeed,
              "Average:", round((minimalSpeed+maximumSpeed)/2048), "kb/s")
        with open("result.csv", "a") as myfile:
            myfile.write(str(blockSize) + "," + str(minimalSpeed) + "," + str(maximumSpeed) + "\n")
        ser.read(ser.inWaiting())
        counterStep = 0
        minimalSpeed = 100000000
        maximumSpeed = 0
        blockSize = blockSize * 2

    if shallExit == 0:
        threading.Timer(1, res).start()


with open("result.csv", "w") as myfile:
    myfile.close()

res()


while 1:
    if blockSize > 65536:
        shallExit = 1
        exit(0)
    s = "A" * blockSize
    b = s.encode()
    ser.write(b)
    bytesReceived = bytesReceived + ser.inWaiting()
    ser.read(ser.inWaiting())

Сильно я не заморачивался, поэтому указать нужный порт придется вам самим прямо в коде. “Человекочитаемые” программа пишет в консоль и попутно генерирует result.csv для импорта в excel или другую подобную программу

Под рукой у меня оказалось только два stm32 с usb: F042 и F303. Оранжевая линия это максимальная скорость, синяя – минимальная. Такие прыжки максимальной скорости вызваны буферизацией у всех участников процесса. Ну по крайней мере я сейчас так думаю.

Результаты довольно показательные. Как найду еще процессоров – попробую повторить. Но пока можно сказать, что не стоит использовать блоки больше 128-256 байт и можно надеяться на скорость не менее 200 килобайт в секунду.

UPDATE1: Добавил график от F779. Суть та же. Видимо, где-то прямо в коде CDC у stm большие проблемы

Игродельство

Чего-то зачесалось у меня в старые игрушки поиграть. Но быстрый поиск показал, что фиг вам там. Либо надо ставить эмуляторы и искать образы, либо идти и платить непонятно за что с каким качеством (у меня до сих пор в голове свежи воспоминания, как угробили pac-man, mario и boulder dash).

Ну если оно не получается, то почему бы не взять и не написать самому? В тех играх не ни графики, ни звука, ни работ на 100500 мульонов денег. А интерес-то тот же …

В общем, отряхнул я руки и за пол-дня написал первую игру: Пинг-Понг.

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

Брать тут https://github.com/kiltum/games/tree/master/pingpong

Свой gitlab на centos 7

Получив очередной счет от gitlab и github одновременно, я как-то задумался: а нафига я им плачу, когда я все это могу поднять на своем сервере? “Все это” – это кучку приватных git и простую ci/cd систему. Поставил и решил написать напоминалку, что бы в следующий раз не гуглить.

Процесс установки не вильно отличается от описанного на сайте, но есть несколько НО:

Во-первых, в centos7 nginx идет без поддержки passenger. Поэтому обновляем на версию из “пассажирской” репы.

curl --fail -sSLo /etc/yum.repos.d/passenger.repo https://oss-binaries.phusionpassenger.com/yum/definitions/el-passenger.repo
yum-config-manager --enable cr
yum install -y passenger
rpm -e nginx-mod-http-perl --nodeps
yum update nginx

Во-вторых, нигде в мануале не указано, что gitlab требует nodejs для работы

yum install nodejs

И наконец, нигде не указано, что gitlab не работает без unicorn. Во всех мануалах написано, что если у вас внешний nginx, отрубите встроенный и unicorn. Так вот, этого делать нельзя, иначе получите неработающий gitlab-workhouse

Из других неочевидных тюнингов стал вынос порта unicorn с 8080 и изменение размера буферов у постгреса. Иначе на моем загруженном сервере он отказывался запускаться.

unicorn['port'] = 8088
postgresql['shared_buffers'] = "100MB"

В остальном единственной засадой было изменение прав на сокеты, но это только из-за моей конфигурации, где куча всяких пользователей лезут в в один каталог. Так как сервер “домашний”, проще стало дать всем права на запись.

Больше никаких отступлений от официального руководства.

STM32 ADC Multiple Channels HAL

Ну или подобными вопросами заполнены тематические конференции. И большинство из них без ответов.

Суть проблемы проста: если в STM32CubeMX включить в АЦП один канал, то все работает изумительно. Примеры есть повсюду и код работает стабильно. Но если включить несколько, то все ломается. При вызове HAL_ADC_GetValue на выходе получается “мусор”, содержащий случайную выборку с каналов.

Решение: в коде HAL содержится ошибка. В функции HAL_ADC_ConfigChannel индусы заложили такую логику, которая препятствует работе более чем с одним каналом. Согласно ей, перед измерением надо каждый канал конфигурировать с Rank = ADC_RANK_NONE, а затем снова его же конфигурировать, но уже с другим rank.

Если делать по документации, то вместо конфигурирования одного канала происходит конфигурирование всех каналов.

В общем, рассказывать дольше, чем показать код

Контроллер версия 2

У меня уже была версия 1, но недавно с испытаний вернулась более новая и улучшенная версия. Теперь она выглядит так:

Это по-прежнему шилд для Arduino Nano, но функционал немного поменялся.

Во-первых, эта версия умеет “распознавать” на входе сигналы уровнем как 220В, так и 24В. При этом без разницы, постоянное или переменное напряжение. Бонусом добавил световую индикацию состояния входов.

Во-вторых, плата теперь имеет два выхода 0-10В. Мелочь, но это очень приятная мелочь.

И наконец, сделал “закладку” для универсального порта. Это маленькая плата-расширение на снимке снизу

Сам порт с испытаний не вернулся, но если все будет хорошо, он обеспечит вход/выход 0-10В, вход-выход 0-20мА и измерение сопротивлений от 100Ом до 100кОм. Разрабатывался в расчете на “закрытие” всех потребностей обычных автоматизаторов. Или говоря другими словами, для тех кому не нужны защиты в 5кВ, гальванические развязки и прочее. Благодаря этому конечная цена данной версии колеблется в районе 2000 рублей в зависимости от объемов.

Но нет пределов совершенству. Скоро будет 3я версия платы, которая закроет найденные недочеты. Например, можно и нужно выровнять реле. И добавить еще немного индикации. И … в общем, все некритично, но практично.

Кто такие DevOps?

Слово модное…это да!! Но если публиковать как «Системный администратор», то откликаются мальчики-настройщики компов, если «Программист», то жесткие программеры без фантазии © Объяснения одной дамы

Любой, кто интересовался этой темой, знают, что данное сокращение произошло от Development и Operations. Или если перевести на русский – Программисты и Сисадмины. Да, operations дословно переводится не так, но здесь я использую слово «сисадмины» в самом широком смысле.

Кто такие «сисадмины»? Если кратко, то это именно те люди, на руках которых держится вся инфраструктура. Где-то этим словом называют приходящих «аникейщиков», которые меняют картриджи в принтере и изредка чистят компьютеры от вирусов и троянов. А где-то это целые залы людей, разбитых на свои касты. Кто-то занимается сетями, кто-то базами данных, а кто-то обслуживанием датацентров и их инфраструктуры. Титаны антистрессовых методик.

Точно такое же широкое определение имеют и «программисты». И тот, кто пишет на javascript проверку ввода на сайт и тот, кто добавляет код во внутренний модуль корпоративной системы – все они «программисты». И даже тот, кто пишет на встроенном языке 1С, тоже программист, что бы об этом не думали другие.

В чем же основная причина, приведшая к появлению девопсов? Для этого необходимо немного углубиться в специфику работы каждой «секты».

Для сисадмина самое важное это то, что бы вверенная ему система работала без перебоев. Не обращая внимания на тупых пользователей, идиотов-разработчиков, отключения питания и поломок оборудования. Все, абсолютно все, рассматривается с точки зрения надежности. И венцом этой надежности является достижение времени доступности системы в 99,999% (или «5 девяток»). Поверьте, заставить современные системы работать так, чтобы их время недоступности не превышало 5 минут в год – это очень сложная задача. И когда вы добиваетесь, что максимальное время, когда подчиненная система может сказать «ой, я не могу» – это 6 секунд в сутки, то спокойно можете брать с полки пирожок.

У разработчиков же другое положение. Над ними коршунами вьются кровососы-менеджеры, которые постоянно требуют новый функционал, который должен работать быстрее или лучше. И всё это великолепие не должно поломать уже существующий функционал. И код, который прекрасно работает на твоей машине, почему-то отказывается работать как надо даже в тестовом окружении. А еще эти гамадрилы-сисадмины не дают нормально покопаться в получившейся у них системе и понять, что пошло не так даже после успешно пройденных тестов. Эти чудаки на букву «м» постоянно орут как резанные и набрасывают говен на вентилятор при любом чихе. И никто кроме коллег не способен оценить красоты получившегося кода или изящества алгоритма.

А рядом вьются … коллеги из отдела тестирования. Эти вообще постоянно нудят про то, что с говнокодом пора кончать и им опять нужна свежая копия базы данных.

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

Вот в горниле этих битв и родилось новая каста – DevOps или девопсы. Как водится, поначалу были перегибы с обоих сторон и презрение к «переметнувшимся». Плюс из-за молодости самого термина у него множество толкований и определений. Кто-то считает девопс методологией, направленной на автоматизацию и увеличение числа релизов, а кто-то – на уменьшение ошибок. Я же считаю, что девопс – это прежде всего человек, главной задачей которого является снижение головной боли у программеров, сисадминов и QAшников. А те уже сами напишут, отконфигурят и оттестируют нужное.

Ибо если поглядеть с любой стороны, девопс будет выглядеть как некий недоучка, лишь в редких случаях способный выдать что-то толковое. Бегает со своими скриптами наперевес, чего-то там курочит в коде и лишь принадлежность к «своим» не позволяет его послать в очень далекое эротическое путешествие. У меня нет ни капли сомнения, что программер переиграет девопса в программировании, сисадмин – в администрировании, а тестировщик – на своем тестировочном поле.

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

Сейчас, в самом начале жизни девопсов (с 2009 года же), самой основной их задачей является автоматизация. Объёмы того, что необходимо автоматизировать – поистине ужасающие. Ведь все (программеры и сисадмины) непрерывно трудятся над увеличением завалов не один десяток лет.

Что обычно автоматизируют?
– Развертывание очередного релиза кода. В тест, в стейджинг, в продакшн. Да хоть на машину разработчика. С необходимыми сервисами и данными. Разных версий.
– Обновление. Сервера, сервисы и сайты. Быстро и еще быстрей.
– Управление инфраструктурой. Сегодня 100 серверов в одном датацентре, завтра 500 в трех, а послезавтра 1000 снова в одном. Мониторинг, сбор логов и куча других мелких задач, которые раньше выполнялись вручную или не выполнялись совсем.

Это самые общие задачи. Где-то спектр для автоматизации побольше, а где-то поменьше.

Широкий характер выполняемых задач автоматически формирует и необходимые требования к девопсам:

– Необходимо знать все используемые продукты, языки и технологии. Только тогда стандартная для девопса задача «скрестить ужа с ежом и заставить их летать» автоматически становится выполнимой. Сисадмин сделал бекап? А девопс должен забрать этот бекап, выкинуть из него всякую конфиденциальную информацию и засунуть в систему (которую построил он же), которая позволит разработчикам и тестировщикам без опаски гонять свои творения на «почти продакшене».

– Наличие знаний (есть еще умное слово «компетенций») по всему процессу разработки. Причем знания должны быть реальными и боевыми. Потому что некому кроме девопса остановить программера, когда он решит напрямую засовывать json в обычную базу данных. Для разработчика в этом нет ничего крамольного (база поддерживает? поддерживает!), для сисадмина тоже (ну растет база и требует больше ресурсов – это нормально), а DBA по привычке обматерит всех и молча пойдет строить индексы и партиционировать базу. А то, что полученная конструкция могла быть быстрее и надежней – это никому не интересно. Кроме девопсов.

Нет ни того, ни другого? Тогда получаются люди, которые прекрасно характеризуются как «чуваки с накачанным ЧСВ и недержанием кода».

Требования большие? Да, большие. Зато и награда велика.

Сама возможность пощупать возведенное твоими трудами, а не какого-нибудь сферического коня в вакууме стоит дорого. Причем ощутить весь процесс возведения от «долбанное ничего» до «обалдеть какая няшка». Ни разработчики, ни сисадмины, ни тестировщики такой возможностью не обладают …

P.S. Ну и денег дают нормально.

Контроллер версия 1

Тут можно и подлиннее, чем в фейсбучике.

Как-то раз ко мне пришел заказчик. И захотел он странного для меня: шилд для ардуины. Вернее, первоначально задача была поставлена еще проще: вот есть нцать шилдов, собери их в одну кучу, что бы без проводов. И что бы arduino uno управляла всем этим. После выяснения, где и что будет этим управлять, я немного офигел. Оказывается, в вентиляции и теплоснабжении есть куча мест, которые требуют простого и тупого управления в духе “вон там сигнал появился? Ну и щелкни вот тут”. И ничего больше не надо. Вообще.

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

Поговорили, почесали макушки и ударили по рукам. Я пошел делать, заказчик пошел переводить денег.

Долго ли, коротко, но я сделал первую версию контроллера. 4 входа 220в, 4 переключающих реле, выход 0-10В, и развязанный rs-485. Плюс пара подтянутых выходов для 1-Wire и все это под управлением ардуинки, запрограммированной в fl-prog. Ну и питаться может от 7В до 40В.

photo_2017-02-03_09-44-40

photo_2017-02-03_09-44-33

В общем-то ничего особенного от слова вообще. Обычный навороченный шилд для ардуины и особенного в нем только то, что он у меня первый. Ну не делал я раньше шилдов для ардуинок 🙂

Первым же неприятным фактом стало то, что ардуинка отказывалась прошиваться, когда она стоит в контроллере. Это 100% мой косяк, ибо “сделай как там” привело к тому, что я использовал драйвер rs-485 “как у китайцев”. А этот ADM485 при отсутствии сигнала на линии тупо подтягивает линию RX в 1. Ну ладно, прошить можно и вытащив ардуинку из платы.

Второй проблемой стало то, что при “автономной” работе rs-485 не работал. То есть питаешь плату через ардуинку – все ок. Питаешь через внешний источник – rs-485 мертв. Проблема осложнялась тем, что у меня все работало, а у заказчика – нет. На одном и том же коде.

Опуская подробности, оказалось, что в мире существует две версии arduino nano. “Дешевая” версия и “дорогая”. Естественно, что “дешевой” версией забиты все магазины Москвы и алиэкспресса. Разница только в одном – чипы мостов usart-usb. На дешевой версии стоит CH340, на дорогой – FTDI.

И этот дешевый CH340 при питании “снаружи” видя, что на входе usb у него ничего нет, тупо подтягивает RX/TX в ноль. А дорогой FTDI – ставит в Z.

Вышли из положения, заиспользовав Software Serial, откусили ножки на самой ардуине и тупо бросили два проводка на плате. То, что у меня заинвертирован RE/DE сигнал, решили ручной правкой кода. Ну не умеет flprog в такое. У них все прямо …

Следующей претензией стало, что “даю 220в, датчик показывает 0, но это ок. Не ок, когда секунд через 40 он переходит в 1, и еще через 40 обратно”. Тут я офигел мрачно.

После разбирательств стало понятно, что ардуинка насколько перегружена кодом от flprog, что тупо не успевает реагировать на полуимпульсы от опторазвязки. А в прерывания оно не умеет. Мы взяли 20МГц чип и тормознули его до 50Гц. Ну почти до 50 …

Не, я и там дунул-плюнул и поправил.

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

В результате по вечерам я проектирую новую, улучшенную версию контроллера. Для безопасности добавлю опторазвязку вообще на все выходы, ибо пользователи явно его будут совать куда не попадя, наплевав на правила техники безопасности. Будет изолировано все от всего. Люблю ночью спать спокойно.

В качестве главного процессора будет STM32F072. Мощный, умеет программироваться через USB и имеет кучку ножек. А ардуинка … ну дам ей “кроватку”. Пусть используют те, кто привык 🙂

Raspberry PI 3 и QT 5.8

Рабочая инструкция. На выполнение требуется примерно 30 часов


sudo apt-get update
sudo apt-get upgrade

sudo apt-get install libfontconfig1-dev libdbus-1-dev libfreetype6-dev libudev-dev libicu-dev libsqlite3-dev libxslt1-dev libssl-dev libasound2-dev libavcodec-dev libavformat-dev libswscale-dev libgstreamer0.10-dev libgstreamer-plugins-base0.10-dev gstreamer-tools gstreamer0.10-plugins-good gstreamer0.10-plugins-bad libraspberrypi-dev libpulse-dev libx11-dev libglib2.0-dev libcups2-dev freetds-dev libsqlite0-dev libpq-dev libiodbc2-dev libmysqlclient-dev firebird-dev libpng12-dev libjpeg9-dev libgst-dev libxext-dev libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev libxcb-icccm4 libxcb-icccm4-dev libxcb-sync-dev libxcb-render-util0 libxcb-render-util0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-glx0-dev libxi-dev libdrm-dev libssl-dev

sudo apt-get install libxcb-xinerama0-dev

Install libpng16-16! Default libpng is too old.

mkdir ~/opt
cd ~/opt

git clone git://code.qt.io/qt/qt5.git

cd qt5

./init-repository

./configure -v -opengl es2 -device linux-rasp-pi-g''+ -device-option CROSS_COMPILE=/usr/bin/ -opensource -confirm-license -optimized-qmake -reduce-exports -release -qt-pcre -qt-libpng -make libs -prefix /usr/local/qt5 &> output

make -j4 &> output_make

sudo make install &> output_make_install

pi@raspberrypi:~ $ cat >> .bashrc
export LD_LIBRARY_PATH=/usr/local/qt5/lib/
export PATH=/usr/local/qt5/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
pi@raspberrypi:~ $ source .bashrc

Решил написать книгу …

… про реальную разработку устройств на stm32. Поэтому думаю обновления если и будут, то нерегулярным и в основном в виде кусочков из книги. Ну прёт меня пока.

Что бы не было скучно, вот вам малюсенький кусочек, можно сказать бета-версия про кварцевые резонаторы и вообще частоты.

Программируем микроконтроллеры в QtCreator

Это копия моей статьи, опубликованной на хабре

qbs03
Почему-то в интернете мало документации про qbs, пора немного исправить эту ситуацию.

Как-то у меня сложилось так, что практически исчезли проекты, в которых надо сделать что-то одно. Обычно надо и написать прошивку для микроконтроллера и управляющую программу для смартфона или десктопа. Можно делать все по старинке: писать код для каждого устройства в родной для него среде разработки.

Но поверьте, это довольно быстро задалбывает. Под виндовс – MSVC, под ARM – CooCox или Keil (приношу свои соболезнования вынужденным работать под IAR), под MSP – CCS, под андроид – eclipse, под ios – Xcode, под пики – MPLAB. И ладно бы, со всем этим работать можно было бы, но ведь фиг: везде свои заморочки, тонкости и неписанные правила. Все это накладывается на общую тормознутость так популярного эклипса помноженную на криворукие дополнения от производителей.

Некоторое время назад я начал сводить все свои разработки под мобильные и десктопные приложения под одну платформу. После довольно долгого чеса по интернетикам мой выбор остановился на Qt. Есть все, что надо, при необходимости можно подцепить нативный код. В общем, задача закрылась.

Но вот с микроконтроллерами ситуация не желала складываться категорически. В основном из-за того, что везде свои хотелки и желалки. Ну ладно, про это я уже жаловался. Я бы еще долго мучался, пока внезапно не наткнулся на краткое описание qbs.

Желающие могут пошариться по инету сами, но если кратко, то это заменитель всяких make и cmake, использующая нормальный (тут должен быть смаил) язык программирования. И сам QtCreator собирается с ее помощью, значит она уже вылезла из штанишек …

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

Хватит излияний, пора попробовать. Создадим очень простой проект, в котором у нас будет десктопная и микроконтроллерная составляющая.

Открываем QtCreator, выбираем создать Non-Qt Project (что бы пока не заморачиваться сильно) и далее выбираем то, где присутствует C и Qbs. Обратите внимание на приятные взгляду слова Platform independent

qbs01

В результате получаем один main.c и qbs. Можно уже нажать “build” и получить вывод Hello World.

Открываем qbs и ничего не понимаем. Поэтому все стираем, вооружаемся интернетом и начинаем писать. Яваскрипт и все такое.

import qbs

Так, тут вроде понятно. Импортируем всякое необходимое для работы самого qbs.

Project {
name: "simple"
}

Сохраняем и наблюдаем исчезновение main.c с левой панели. При попытке запустить проект QtCreator спросит: а чего пускать-то? В принципе пока все логично.

Из чего состоит проект в терминологии qbs? Из продуктов. И их может быть несколько, но пока я сделаю один.

Project {
name: "simple"
Product {
name: "desktop"
}
}

Теперь для нашего “десктопного” укажем исходник.

Project {
name: "simple"
Product {
name: "desktop"
files: "main.c"
}
}

При попытке скомпилировать не изменится ровным счетом ничего. Подсмотрим в некоторые обучалки и добавим зависимость от cpp и укажем, что вообще-то это приложение.

Project {
name: "simple"
Product {
name: "desktop"
files: "main.c"
Depends {name: "cpp"}
type: "application"
}
}

И вот теперь при попытке собрать приложение QtCreator пошуршит немного диском и в панельке Application Output появится искомое

qbs02

Ага, значит мы на верном пути. Осталось разобраться, что делают те самые две магические строки.

Depends {name: "cpp"}

Читаю документацию и понимаю, что данной командой я устанавливаю зависимость проекта от какого-то модуля с именем cpp. Понятней стало? Мне нет.

Простым поиском нахожу что-то подобное в /usr/share/qtcreator/qbs/share/qbs/modules/ (Если у вас другая операционка, то скорее всего аналогичное лежит где-то неподалеку от QtCreator). Если говорить коротко, то там куча яваскрипта, которая в зависимости от платформы подбирает компилятор под эту платформу. Полностью повторять подобное мне смысла нет, поэтому оставляю как есть.

type: "application". Из документации: The file tags matching the product’s target artifacts. Артефакт … An Artifact represents a single file produced by a Rule or Transformer. … ерр .. Rule? Creates transformers for input tags. Напоминает ситуацию про сепулькарий .. Transformer? Creates files, typically from other files.

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

Но вернемся к нашему проекту. Давайте добавим еще один продукт, только уже для микроконтроллера

Project {
name: "simple"
Product {
name: "desktop"
files: "main.c"
Depends {name: "cpp"}
type: "application"
}
Product {
name: "micro"
files: "blink.c"
}
}

При попытке что-то сделать, нам сразу выскочит сообщение, что вообще-то файла blink.c нет. Ну, ок, добавим в проект фаил blink.c. Как видно из названия, это тот же HelloWorld, только для микроконтроллеров. Я взял из примеров для микроконтроллера семейства msp430.

#include <msp430.h>

int main(void)
{
WDTCTL = WDTPW + WDTHOLD;
P1DIR |= 0x01;

while (1) {
P1OUT ^= 0x01;
__delay_cycles(1000000); // 1 second @ 1MHz
}

return 0;
}

Будучи скомпилированным и залитым, он начнет дергать ножкой P1.0 с интервалом в одну секунду. А так как на этой ножке у большинства демо- и девелоперских плат висит светодиодик, то он замигает.

Теперь QtCreator не ругается, но и в микроконтроллер ничего не заливается. Странно, да?

Добавлять Depends {name: “cpp”} смысла нет, потому что установленный в системе родной gcc не в курсе про существование такой платформы, да и в дальнейшем пригодится, например для пиковских контроллеров, где вообще все свое.

Теперь воспользуемся обрывками тех сакральных буковок, что встретились раньше.

Для начала, я предпочитаю в микроконтроллерных проектах расписывать каждый функционал в своем файле. Записывать каждый файл руками? Лень. Подсматриваем решение и переписываем блок

Product {
name: "micro"
Group {
name: "msp430 sources"
files: 'src/*.c'
fileTags: ['c']
}
}

Тут создаем группу файлов, которые обзываем “msp430 sources” и тупо включаем в нее все файлы, которые подходят под маску src/*.c. Для дальнейшей работы с ними тегируем их буквой С.

Что с ними делать? У qbs есть на этот случай две штуки – Rule и Transformer. По сути они близки, но немного разные. Счас попробую описать на пальцах разницу.

Rule умеет срабатывать на каждый файл, попадающий под что-то. Может срабатывать по разу на каждый фаил (например, для вызова компилятора), а может один раз на все (линкер).

Transformer предназначен для срабатывания только на один фаил, с заранее определенным именем. Например, прошивальщик или какой-нибудь хитрый скрипт.

Ок, добавляем правило, которое должно будет сработать на все наши файлы, промаркированные как “с”.

Product {
name: "micro"
Group {
name: "msp430 sources"
files: 'src/*.c'
fileTags: ['c']
}
Rule {
inputs: ["c"]
prepare: {
var cmd = new JavaScriptCommand();
cmd.description = "file passing"
cmd.silent = false;
cmd.highlight = "compiler";
cmd.sourceCode = function() {
print("Nothing to do");
};
return cmd;
}
}
}

В принципе из синтаксиса уже все понятно. Есть inputs, есть prepare, в который засовывается яваскрипт, который выполняет необходимое. В данном случае он должен в окошке Compile Output показать file passing, и куда-то вывести Nothing to do. Ну по документации вроде так.

Запускаем перекомпиляцию всего и смотрим. Не знаю, как у вас, но я ничего не вижу. Почему? Потому что qbs больно умный, а документация к нему страдает лакунами.

Правило не срабатывает, потому что qbs считает, что оно не производит никаких действий в системе и от него ничего не зависит. В принципе это соответвует реальности, но провести проверку мешает.

Ок, за это отвечают те самые артефакты. Под ними подразумеваются результаты деятельности Rule или Transformer. Лучше всего это объяснить на примере компиляции. Когда мы компилируем .с файл, то на выходе мы получим объектный файл .о. Он нам нужен для дальнейшей линковки, но с другой стороны, мы его можем удалить, так как потом спокойно сможем сгенерировать заново.

Опять копируем пример из документации и чуть-чуть модернизируем.

Rule {
inputs: ["c"]
Artifact {
fileTags: ['obj']
filePath: '.obj/' + qbs.getHash(input.baseDir) + '/' + input.fileName + '.o'
}
prepare: {
var cmd = new JavaScriptCommand();
cmd.description = "Compiling "+ input.fileName
cmd.silent = false;
cmd.highlight = "compiler";
cmd.sourceCode = function() {
print("Nothing to do");
};
return cmd;
}
}

Теперь мы говорим, что после нашей деятельности останутся артефакты в каталоге .obj (ну и я добавил вывод того, над каким файлом мы сейчас работаем). Запускаем. Опять ничего в ответ. Почему? Ответ тот же – никому не нужны файлы с тегом ‘obj’.

Хорошо, для проверки сделаем так, что они нужны нам. И вообще, наше приложение – это один сплошной obj.

Product {
name: "micro"
type: "obj"
Group {
name: "msp430 sources"
files: 'src/*.c'
fileTags: ['c']
}
Rule {
inputs: ["c"]
Artifact {
fileTags: ['obj']
filePath: '.obj/' + qbs.getHash(input.baseDir) + '/' + input.fileName + '.o'
}
prepare: {
var cmd = new JavaScriptCommand();
cmd.description = "Compiling "+ input.fileName
cmd.silent = false;
cmd.highlight = "compiler";
cmd.sourceCode = function() {
print("Nothing to do");
};
return cmd;
}
}
}

Пробуем, и удача! В окошке появился заветный “Compiling blink.c”. Теперь давайте добавим, что бы оно реально компилировало и сразу по-быдлокодерски, то есть тупо забив все необходимое в одну кучу.

prepare: {
var args = [];
args.push("-mmcu=cc430f5137")
args.push("-g")
args.push("-Os")
args.push("-Wall")
args.push("-Wunused")
args.push('-c');
args.push(input.filePath);
args.push('-o');
args.push(output.filePath);
var compilerPath = "/usr/bin/msp430-elf-gcc"
var cmd = new Command(compilerPath, args);
cmd.description = 'compiling ' + input.fileName;
cmd.highlight = 'compiler';
return cmd;
}

Перекомпилируем все с нуля и смотрим в каталог .obj

$ ls -R1
.:
f27fede2220bcd32

./f27fede2220bcd32:
blink.c.o

Ура! Файлик появился. Теперь, для проверки я делаю еще один файлик, с хитрым названием hz.с. Если я прав, то после перекомпиляции рядом появится еще один объектный файл.

В выводе появилось

compiling blink.c
compiling hz.c

а в каталоге

./f27fede2220bcd32:
blink.c.o
hz.c.o

Все вроде ок. Теперь необходимо все это дело слинковать. А значит опять правило, только теперь для линковки.

Rule {
multiplex: true
inputs: ['obj']
Artifact {
fileTags: ['elf']
filePath: project.name + '.elf'
}
prepare: {
var args = [];
args.push("-mmcu=cc430f5137")

for (i in inputs["obj"])
args.push(inputs["obj"][i].filePath);

args.push('-o');
args.push(output.filePath);
var compilerPath = "/usr/bin/msp430-elf-gcc"
var cmd = new Command(compilerPath, args);
cmd.description = 'linking ' + project.name;
cmd.highlight = 'linker';
return cmd;
}
}

Где отличия? Во-первых, добавился флаг multiplex, который говорит о том, что это правило обрабатывает сразу все файлы данного типа скопом. А во-вторых, во входных параметрах исчез input. Появился inputs, который является массивом файлов данного типа. Ну и я вопользовался именем продукта, что бы брать имя для финальной прошивки.

Ставим тип приложения elf и пробуем собрать. Через некотрое время мы в каталоге для сборки обнаружим файл simple.elf

$ file simple.elf
simple.elf: ELF 32-bit LSB executable, TI msp430, version 1, statically linked, not stripped

То, что нам и необходимо. Его можно уже заливать в плату и наслаждаться мигающим светодиодиком.

Исходная цель достигнута: мы в одной среде разработки делаем все: и редактирование и компиляцию.

На всякий случай конечный qbs

import qbs
Project {
name: "simple"
Product {
name: "desktop"
files: "main.c"
Depends {name: "cpp"}
type: "application"
}
Product {
name: "micro"
type: "elf"
Group {
name: "msp430 sources"
files: 'src/*.c'
fileTags: ['c']
}
Rule {
inputs: ["c"]
Artifact {
fileTags: ['obj']
filePath: '.obj/' + qbs.getHash(input.baseDir) + '/' + input.fileName + '.o'
}
prepare: {
var args = [];
args.push("-mmcu=cc430f5137")
args.push("-g")
args.push("-Os")
args.push("-Wall")
args.push("-Wunused")
args.push('-c');
args.push(input.filePath);
args.push('-o');
args.push(output.filePath);
var compilerPath = "/usr/bin/msp430-elf-gcc"
var cmd = new Command(compilerPath, args);
cmd.description = 'compiling ' + input.fileName;
cmd.highlight = 'compiler';
return cmd;
}
}
Rule {
multiplex: true
inputs: ['obj']
Artifact {
fileTags: ['elf']
filePath: project.name + '.elf'
}
prepare: {
var args = [];
args.push("-mmcu=cc430f5137")

for (i in inputs["obj"])
args.push(inputs["obj"][i].filePath);

args.push('-o');
args.push(output.filePath);
var compilerPath = "/usr/bin/msp430-elf-gcc"
var cmd = new Command(compilerPath, args);
cmd.description = 'linking ' + project.name;
cmd.highlight = 'linker';
return cmd;
}
}
}
}

PS Вынос “захардкоденных” переменных в более удобное место оставлю на вашей совести, ибо это уже к обучалке по яваскрипту.

MSP430 и Fedora Linux

В принципе, в программировании микроконтроллеров под Linux нет ничего сложного. Более-менее доступны средства разработки, а некоторые производители и вовсе официально поддерживают Linux как среду разработки.

TI тоже имеет свою фирменную среду, но она … скажем так, для хардкорных железячников. Старый, тормознутый эклипс и какие-то невнятные настройки. Залитие прошивки и ее отладка тоже доставляют много удовольствия привыкшим к нормальным средам.

В общем, долой разврат – будем делать все по-правильному, что бы программирование доставляло удовльствие, а не требовало героического преодоления трудностей.

Расписывать я буду под Fedora Linux (21), потому что я сейчас под ней. Под другие дистрибутивы инструкций как-то больше.

В чем проблема с федорой? Ведь они кровь и плоть от редхата, а тот официально поддерживает msp430-gcc, который использует и сама TI. Проблема в том, что это федора, а значит в ней все нестабильное и вообще может не работать.

И с MSP430 случилось ЭТО. В 20й версии он работал, а в 21й – уже нет. Подробнее можно посмотреть тут. Если кратко, то в федоре сломали линкер.

Что делать? Есть несколько вариантов, из которых самый простой это взять старую версию компилятора из CCS или Energia. Но добрый Brandon Nielsen решил эту проблему по-другому: просто сделал репозиторий со правильными версиями.

Открываем рутовую консоль и подключаем нужный репозитарий

dnf copr enable nielsenb/msp430-development-tools

И ставим все необходимое, соглашаясь с тем, что никто ни за что не отвечает.

yum install msp430-elf-gcc msp430-elf-binutils msp430-elf-gdb mspds msp430flasher msp430-gcc-support-files dos2unix srecord

Теперь скопипастим Makefile
#
# Makefile for msp430
#
# 'make' builds everything
# 'make clean' deletes everything except source files and Makefile
# 'make install' builds everything, and programs the MSP430 using MSPFLASHER
# You need to set TARGET, MCU and SOURCES for your project.
# TARGET is the name of the executable file to be produced
# $(TARGET).elf $(TARGET).hex and $(TARGET).txt nad $(TARGET).map are all generated.
# The TXT file is used for BSL loading, the ELF can be used for JTAG use
#
TARGET = project0
MCU = msp430f5529
# MSP430Flasher name
MSPFLASHER = MSP430Flasher
# List all the source files here
# eg if you have a source file foo.c then list it here
SOURCES = main.c delay.c
# Include are located in the Include directory
INCLUDES = -IInclude
# Add or subtract whatever MSPGCC flags you want. There are plenty more
#######################################################################################
CFLAGS = -mmcu=$(MCU) -g -Os -Wall -Wunused $(INCLUDES)
ASFLAGS = -mmcu=$(MCU) -x assembler-with-cpp -Wa,-gstabs
LDFLAGS = -mmcu=$(MCU) -Wl,-Map=$(TARGET).map
########################################################################################
CC = msp430-elf-gcc
LD = msp430-elf-ld
AR = msp430-elf-ar
AS = msp430-elf-gcc
NM = msp430-elf-nm
OBJCOPY = msp430-elf-objcopy
RANLIB = msp430-elf-ranlib
STRIP = msp430-elf-strip
SIZE = msp430-elf-size
READELF = msp430-elf-readelf
MAKETXT = srec_cat
CP = cp -p
RM = rm -f
MV = mv
########################################################################################
# the file which will include dependencies
DEPEND = $(SOURCES:.c=.d)
# all the object files
OBJECTS = $(SOURCES:.c=.o)
all: $(TARGET).elf $(TARGET).hex $(TARGET).txt
$(TARGET).elf: $(OBJECTS)
echo "Linking $@"
$(CC) $(OBJECTS) $(LDFLAGS) $(LIBS) -o $@
echo
echo ">>>> Size of Firmware <<<<" $(SIZE) $(TARGET).elf echo %.hex: %.elf $(OBJCOPY) -O ihex $< $@ %.txt: %.hex $(MAKETXT) -O $@ -TITXT $< -I unix2dos $(TARGET).txt # The above line is required for the DOS based TI BSL tool to be able to read the txt file generated from linux/unix systems. %.o: %.c echo "Compiling $<" $(CC) -c $(CFLAGS) -o $@ $< # rule for making assembler source listing, to see the code %.lst: %.c $(CC) -c $(ASFLAGS) -Wa,-anlhd $< > $@
# include the dependencies unless we're going to clean, then forget about them.
ifneq ($(MAKECMDGOALS), clean)
-include $(DEPEND)
endif
# dependencies file
# includes also considered, since some of these are our own
# (otherwise use -MM instead of -M)
%.d: %.c
echo "Generating dependencies $@ from $<" $(CC) -M ${CFLAGS} $< >$@
.SILENT:
.PHONY: clean
clean:
-$(RM) $(OBJECTS)
-$(RM) $(TARGET).*
-$(RM) $(SOURCES:.c=.lst)
-$(RM) $(DEPEND)
install: $(TARGET).txt
$(MSPFLASHER) -n $(MCU) -w "$(TARGET).txt" -v -z [VCC]

Если при попытке компиляции Вы получите

Makefile:48: *** missing separator. Stop.

то это означает лишь то, что вы нарвались на tab-hell. Все отступы выше – это не пробелы, а символы табуляции. Если отвлечься и открыть этот фаил в mc, то вы должны увидеть следующее:

mc_tab

Обратите внимание на символы “<---->” – именно ими mc показывает табуляцию. Поправьте где надо.

Теперь осталось поправить параметры MCU под мой процессор и SOURCES под мой проект и запустить make. Если все сделано правильно, то вы должны увидеть побежавшие строки, завершающиеся чем-то похожим.

Linking blink.elf

>>>> Size of Firmware <<<< text data bss dec hex filename 2906 214 152 3272 cc8 blink.elf unix2dos: converting file blink.txt to DOS format...

Если оно так, то можете себя поздравить. Теперь можно развиваться дальше.

PS С автором, чьи наработки я использовал, можно познакомиться тут

Дисплеи 4D …

… или не ходите дети в Австралию гулять.

Есть в далекой загранице такая компания – 4D Systems. Основной продукт – “интеллектуальные дисплеи”. По роду деятельности я с ними очень глубоко познакомился, так что устриц ел.

Задумка совершенно шикарная. Купив такую штуку, разработчик получает полностью готовый блок, где есть дисплей с тачскрином и в который можно загрузить программу, воткнуть микро-sd карточку и подцепить какие-то внешние датчики или устройства.

Причем последние версии дисплеев отличаются только разрешением или формой (да, у них есть круглые дисплеи) – все остальное у них одинаково.

Как заведено в “корпративном” мире, к этим дисплеям полагается софт для их программирования. Софт работает только под Windows и может загонять дисплеи в три режима работы.

1. Режим тупого usart терминала. Цепляем к дисплею любой внешний микроконтроллер через совершенно стандартный usart и отправляете в дисплей команды в духе “нарисуй точку”, “смонтируй флешку”, “покажи картинку с флешки”. Работает очень медленно.

2. Написание “визуальных программ”. Есть библиотечка страшненьких контролов, из которых делаются экраны, а потом в специально отведенные места записывается код. Что-то сложное написать невозможно.

3. “Полноценный”. Можно писать программы на 4DL – некой смеси паскаля и си.

Плюс прямо в IDE встроено немного утилиток типа калибровки экрана, разметки microsd карточек и терминалки. В принципе штуки полезные в плане “все свое ношу с собой”.

В общем, почти счастье любого программиста встраиваемых систем. Но как обычно, все портит реализация.

Начинается всё с редактора. Эта штука абсолютно не умеет форматировать код. Ни работы с блоками, ни нормального свертывания. Зато раздражающие комментарии – в полном объеме. Короче, этакий notepad с подсветкой. Результат – очень страшно выглядящий код.

Компилятор тоже доставляет немного удовольствия: иногда программа компилируется, но отказывается заливаться в дисплей. Добавляем где-нибудь в незначащем месте лишний пробел или что-нибудь типа i:=i; и все прекрасно компилируется и заливается. Еще больше доставляют ошибки, когда программа на дисплее просто падает на ровном месте, которое в прошлые разы не вызывало никаких нареканий. Лечится точно так же: либо лишние команды, либо просто поменять что-нибудь местами.

“Исполнятор” тоже не свободен от греха: например, если вы “примонтировали” с microsd файловую систему и с нее только читаете, то при падении программы вы с 50% вероятностью получите “убитую” флешку, которую надо снова форматировать.

Да, и большие microsd не поддерживаются. Если вам удалось найти карточку на 2Гб, то вам повезло. Если у вас больше – то будьте добры сделать раздел на 2Гб, а про остальное – забудьте.

Где еще проблемы? Перечислю кратко:

– Ошибки в документации. Неправильные аргументы, неправильные примеры, код скопипащен из примера для другой функции.
– Программисту не дают доступа ни к прерываниям, ни к какой-либо многозадачности. Вообще. Делай большой цикл на все и молись, что бы стек не переполнился.
– Сильно грузить порты ввода-вывода нельзя. Например, если включить проигрывание wav-ки с карточки и побежать исполнять код дальше, то вместо “чистого” звука услышишь его же с треском. Начнешь много и часто читать с карточки – получишь мешанину байт вместо ожидаемого. Рисуешь картинку – будь готов к потерям байт в последовательном порту.
– Отладчика нет. Вообще ни в каком виде. Отладочный вывод? Ну можно писать в usart, если он не занят. Но проще тупо на экран.

После всего этого проблемы вида “есть возможность загрузить шрифты, но нет ни описания формата, ни редактора для них” кажутся такими пустяками …

Но с другой стороны, если вы достаточно сильны духом, то за $185 долларов (сама дорогая модель) вы получите готовую уберштуку, в которой 7″ дисплей (800х480), маленький динамик и слот для карточек. Попутно будет доступно нцать выводов, на которые можно повесить всякие енкодеры, пзушки (внутри хоть и есть флеш, но писать не дают) и прочие штуковины. И после кучки оптимизаций все это будет крутиться довольно быстро и не раздражать пользователя.

В этой реальности я бы посоветовал найти отдельно 7″ дисплей и какой-нибудь контроллер. Будет и дешевле и быстрее и удобней.

Как-то так 🙂

STM32 и FreeRTOS. 5. Приносим пользу и добро!

Как-то раз попросили меня посмотреть на одно очень дорогостоящее устройство. Проблема была одна: среди использующих это устройство возникло стойкое убеждение, что 99,99% его цены происходит от того факта, что производитель этого устройства монополист в своей сфере и деваться пользователям этого устройства некуда.

Вооружившись осциллографом, я полез внутрь.

Через некоторое время поиски привели к двум проводкам, которые были в жгуте, соединявшем блоки устройства. Осциллограмма показала, что в проводках почти обычный USART. Почти — потому что «туда» данные бежали на скорости 9600, а обратно на 115200.

Продолжение тут

STM32 и FreeRTOS. 4. Шаг в сторону HAL

Одним из основных препятствий для перехода на STM32 является обилие текстов, инструкций и мануалов, описывающих работу с контроллером. Виновником этого обилия стала сама STMicroelectronics, которая поначалу планомерно запутывала своих пользователей, а затем предлагала неверные варианты выхода.

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

Но не так давно ST поняла, в какую яму она угодила и начала усиленно из нее выбираться, привлекая новые силы. И именно благодаря этому сейчас время старта сократилось до несуразно маленьких величин. Как это выглядит на практике?

Статья на хабре

Еще один способ отъема денег у …

… бедных программистов с помощью компилятора.

Маленькая программка на С.

Внутри код

uint8_t i[]="123456789012345678901234567890";

Или +30 байт.

Результат:

Program space used F9h ( 249) of 2000h words ( 3.0%)
Data space used 49h ( 73) of 200h bytes ( 14.3%)

А теперь добавим const перед i, показывая компилятору, что неплохо бы переменную перенести в “программную” часть.

Program space used D9h ( 217) of 2000h words ( 2.6%)
Data space used Bh ( 11) of 200h bytes ( 2.1%)

Хм. 249-217=32 байт освободили в программной части и 73-11=62 (!) байта в области данных

Говоря другими словами, родной компилятор XC8, если не сказать другого, при любом использовании переменной хранит 2 копии переменной в программной памяти и 2 копии в области данных. Сказать, что я офигел мрачно, значит ничего не сказать.

После этого становится понятно, откуда компилятор берет ресурсы для оптимизации и гордо сообщает при любом удобном случае Running this compiler in PRO mode, with Omniscient Code Generation enabled, produces code which is typically 40% smaller than in Free mode.

(Чуть позже) решил перепроверить, а то вдруг всякие библиотеки/типа навязались

Сделал пустой проект с одним файлом для PIC16F1503.

void main(void) {
char i[30]="123456789012345678901234567890";
int q;
q=i[2];
}

Ничего больше, ни инклюдов, ни библиотек, ни инициализаций.

Program space used 4Ah ( 74) of 800h words ( 3.6%)
Data space used 42h ( 66) of 80h bytes ( 51.6%)

добавляем const

Program space used 2Fh ( 47) of 800h words ( 2.3%)
Data space used 6h ( 6) of 80h bytes ( 4.7%)

Хорошие, добрые и отзывчивые люди!

STM32 и FreeRTOS. 3. Очереди

Это черновая версия, финальная опубликована на хабре

“Вас много, а я одна!” – классическая фраза продавщицы, которую затерроризировали покупатели с вопросами “А есть …?”. Вот и в микроконтроллерах случаются полностью аналогичные ситуации, когда несколько потоков требуют внимания от какой-либо медленной штуки, которая просто физически не способна обслужить всех разом.

Возьмем наиболее яркий и богатый проблемами пример, на котором “валятся” большинство неопытных программистов. Есть мощный и достаточно быстрый микроконтроллер. К нему подключен с одной стороны адаптер com-порта, через который пользователь подает команды и получает результаты, а с другой – шаговый двигатель, который согласно этим командам поворачивается на какой-то угол. И конечно же, прикольная кнопочка, которая тоже что-то этакое значит для пользователя. Где можно наловить проблем?

Пойдем со стороны пользователя. Com-порт, или USART (universal asynchronous receiver/transmitter) – штука очень нежная и капризная. Основной каприз заключается в том, что ее нельзя оставлять без внимания. По одной простой причине – для экономии выводов в 99% случаев выводят только сигналы приема и передачи, оставляя сигналы разрешения приема и передачи за бортом. И стоит микропроцессору хоть чуть-чуть замешкаться, как символ будет потерян. Судите сами: магические цифры 9600/8N1 говорят нам, что в секунду по линии летит 9600 бод, а один символ кодируется 9 импульсами. В итоге максимальная скорость передачи составляет 9600/9 = 1066 байт в секунду. Или чуть меньше одной миллисекунды на байт. А если по ТЗ скорость передачи 115200? Там может прилететь 128 байт за миллисекунду. А ведь микроконтроллеру надо еще обработать эти данные. Кажется, что все плохо, но в реальности куча устройств работает и не доставляет проблем. Какие же решения применяются?

Cамым распространенным (и правильным) решением является перекладка всей работы по приему или передаче символов на плечи микроконтроллера. Делать аппаратный usart порт научились сейчас все, поэтому типовое решение в большинстве случаев выглядит вот так:

void URARTInterrupt()
{
a=GetCharFromUSART();
if(a!=0x13)
buffer[count++]=a;
else
startProcessing();
}

Где проблема? Во-первых, проблема в вызове startProcessing. Стоит ей хоть чуть-чуть задержаться с работой, как очередной символ будет потерян. STM32L1 на минимальной частоте успевает за 1мс обработать 84 команды. В итоге при более-менее развесистой логике полученная конструкция будет терять символы.

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

Обычно где-то на этом месте я замечаю удивленные глаза народа и возмущенные выкрики в духе “ну такие алгоритмы же работают в куче проектов и ничего, никаких проблем”. Да, работают. Но в каких проектах? Только в тех, где можно все общение с контроллером перевести на синхронную логику. Вот пример диалога пользователя (П) и контроллера (К)

П: ATZ (Контроллер, ты живой?)
К: ОК (Ага, я тут)
П: М340,1 (Сделай чего-то)
(тут может быть пауза, иногда очень большая)
К: ОК (Сделал типа)

Где проблемы? Во-первых, нельзя послать несколько команд подряд. Значит либо надо будет менять логику или делать сложные команды с несколькими параметрами. Во-вторых, между запросом и ответом не может быть никаких других запросов и ответов. А вдруг пользователь в процессе движения нажмет кнопку? Что делать? Выход только один – использовать родную природу порта, а именно его асинхронность. В результате диалог между пользователем и контроллером выглядит примерно так

П: ATZ (жив?)
К: ОК (ага)
П: M340,1
К: К1=1 (кнопку нажали)
П: Е333,25,2
(тишина)
К: Е=1 (задачу Е выполнил)
К: М=1 (задачу М выполнил)

Конечно, логика обработки подобного потока немного усложняется, зато благодаря такой асинхронности мы сразу получаем множество преимуществ перед “классической школой”.

Во-первых, резко повышается отзывчивость интерфейса с пользователем. Ну работает там где-то моторчик или считается что-то, но это же не повод “замирать” или “тормозить”. А когда пользователь понимает, что величина тормозов практически не зависит от мощности контроллера, у него возникают резонные вопросы …
Во-вторых, легко реализовать совместное управление. Скажем, у меня на плате 8 выводов. Легко можно сделать так, что бы первые 3 управлялись через USART1, вторые 3 через USB-CDC, а последние два – совместно. И все это на одном контроллере.
И наконец, очень легко отвязать логику от аппаратного интерфейса. Нам все равно, откуда приходят команды – от соседнего процесса, от пользователя или вообще от другого контроллера.

А теперь со всеми этими задумками взглянем на противоположную сторону – на сторону исполнителя. Он достаточно медлителен, что бы попросту не успеть обработать команды по порядку. И время запуска-остановки моторчика очень большое, поэтому хорошо бы сделать примитивную оптимизацию в духе “если поступило две команды на поворот в одну сторону, то поверни за раз”.

Что делает обычный программист? Так как он прочитал предыдущие статьи и кучку книжек, то он рисует логику расставляя семафоры по необходимости, а для блокирования одновременного доступа к моторчику использует мутексы. Все хорошо, но код получается громоздкий и тяжело читаемый. Что делать? У нас в studiovsemoe.com мы используем рецепт Шарикова: “В очередь, сукины дети! В очередь!”

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

Все, всего три очереди, а дикая куча проблем решена. Во-первых, нет даже потенциальной проблемы потери или переписи буфера принятых символов. Правда результатом станет чуть больший расход памяти, но это допустимая цена. Во-вторых, нет проблем с выводом. Все процессы просто пишут в одну очередь, а как выводить, в каком формате и прочее – это уже не их забота. А значит упрощается код и понижается стоимость разработки и поддержки. И наконец благодаря такому подходу задачи очень легко разделить на более мелкие и раскидать их по потокам/ядрам микропроцессора.

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

Где-то в начале кода определим очередь

xQueueHandle q;

Код светодиодов поменяем по принципу “в очереди больше Н элементов? зажигаем, если нет, то нет”

if(uxQueueMessagesWaiting(q)>1)
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_9,GPIO_PIN_SET);
else
НAL_GPIO_WritePin(GPIOE,GPIO_PIN_9,GPIO_PIN_RESET);
osDelay(100);

Перед запуском планировщика проинициализируем очередь так, что бы она могла хранить 8 байт.

q = xQueueCreate( 8, sizeof( unsigned char ) );

Ну и код кнопки для помещения символов в очередь

if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_SET)
{
unsigned char toSend;
xQueueSend( q, ( void * ) &toSend, portMAX_DELAY );
}
osDelay(500);

Чего не хватает? Воркера, который забирает из очереди задания. Пишем.

static void WorkThread(void const * argument)
{
for(;;)
{
unsigned char rec;
xQueueReceive( q, &( rec ), portMAX_DELAY );
osDelay(1000);
}
}

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

Как обычно, использующийся код доступен по адресу http://kaloshin.ru/stm32/freertos/stage3.rar