Thursday, September 18, 2014

USACO \2013\January\Gold\1 - "LINEUP_GOLD"

Farmer John's N cows (1 <= N <= 100,000) are lined up in a row.  Each cow is
identified by an integer "breed ID" in the range 0...1,000,000,000; the
breed ID of the ith cow in the lineup is B(i).  Multiple cows can share the
same breed ID.

FJ thinks that his line of cows will look much more impressive if there is
a large contiguous block of cows that all have the same breed ID.  In order
to create such a block, FJ chooses up to K breed IDs and removes from his
lineup all the cows having those IDs.  Please help FJ figure out
the length of the largest consecutive block of cows with the same breed ID
that he can create by doing this.

PROBLEM NAME: lineup

INPUT FORMAT:

* Line 1: Two space-separated integers: N and K.

* Lines 2..1+N: Line i+1 contains the breed ID B(i).

SAMPLE INPUT (file lineup.in):

9 1
2
7
3
7
7
3
7
5
7

INPUT DETAILS:

There are 9 cows in the lineup, with breed IDs 2, 7, 3, 7, 7, 3, 7, 5, 7. 
FJ would like to remove up to 1 breed ID from this lineup.

OUTPUT FORMAT:

* Line 1: The largest size of a contiguous block of cows with
        identical breed IDs that FJ can create.

SAMPLE OUTPUT (file lineup.out):

4

OUTPUT DETAILS:

By removing all cows with breed ID 3, the lineup reduces to 2, 7, 7, 7, 7,
5, 7.  In this new lineup, there is a contiguous block of 4 cows with the
same breed ID (7).



SOLUTION:

Let's consider solution using sliding window algorithm. 
N <= 1e5, hence complexity of the algorithm should be approximately N*log(N).
At each iteration, we move the right pointer and must maintain the segment satisfying the conditions that there are less or equal K+1 types of ID. If the segment is correct, then we can simply delete the least groups of types in that interval and there will be all the same elements. Otherwise, we move the left pointer.
To do this we need to count the number of types somehow. The first idea is binary tree, right? We may use priority_queue or set, or just map. Let's use map.

So, what we have:

  • We have map<int,int> that can respond how many elements within current interval with some ID.
  • We can figure out is the current segment is correct (if number of types <= K+1)
  • If the segment is correct - try to maximize the answer and keep going increasing right pointer
  • If the segment is not correct - just increment left pointer (move to the right)
The final algorithm looks as follows:
map<int,int> m;
int A[]; // input IDs
...
for (int i = 0, j = 0; i < n; i++)
{
   if (m[A[i]] == 0) types++;
   m[A[i]]++;
   for (; types > k+1; j++)
   {
       m[A[j]]--;
       if (m[A[j]] == 0)
       {
          types--;
       }
   }
   ans = max(ans, m[A[i]]);
}

Monday, August 25, 2014

Problem “Сonsumer” (Republican Programming Contest 2014, Day 1, Problem 3)

Friday, April 4, 2014

Robo-Hand (The prototype of robotic hand)

Данный программно-аппаратный комплекс, разработанный нами, является тренажёром для приобретения навыков практического программирования реальных устройств подобного типа, экспериментальный стенд, а так же устройство для проверки и демонстрации на практике математических расчётов. С его помощью можно более наглядно изучить физику движения механизмов подобного типа. В программный комплекс входят: 3D эмулятор робота-манипулятора, лёгкий и функциональный макро-язык, анализатор движения (плавные разгоны и торможения), поддержка USB-джойстика, удалённое управление со смартфона, а так же всё необходимое для реализации алгоритмов движения.

Если вас заинтересовала эта информация - пишите в комментарии.



Saturday, December 21, 2013

Split the set of numbers into subsets

 Разбить множество чисел на подмножества, разница сумм которых минимальна и вывести на экран эту разницу.
Для решения этой задачи будем использовать динамическое программирование.
Не сложно понять, что сумма каждого из двух выбранных множеств должна быть как можно "ближе" к половине суммы всего исходного множества т.е. если представить сумму всего исходного множества за C, а суммы двух выбранных подмножеств за A и B, то:
A + B = C;
A ~ C / 2
B ~ C / 2
И так, задача сводится к нахождению подмножества с суммой C/2.
Почитайте это

Перейдём к реализации.


Так как каждую вещь можно брать только один раз, то будем идти не слева направо, а справа налево.

Будем хранить 1 или 0, что значит можно ли достичь суммы M, если взять N предметов.

0 <= M <= C/2

Ответом будет ячейка таблицы отмеченная как 1 и с максимальной M (т.к. нам нужно взять набранную сумму как можно ближе к C/2)


#include <iostream> 
#include <vector> 
#include <cmath> 

#define REP(i,n) for (int i = 0; i < n; i++)

using namespace std;

bool f[100];
int main()
{
    int n;
    long sum = 0;
    cin >> n;
    vector<int> v(n);
    REP(i,n) cin >> v[i], sum += v[i];
    int m = sum/2;

    int w = 0;
    f[0] = 1;
    for (int i = n-1; i >= 0; i--)
    {
        for (int j = m; j >= 0; j--)
        {
            if (f[j] == 1)
            {
                f[j+v[i]] = 1;
            }
        }
    }
    for (int i=m;i>=0;i--) if (f[i]) {
        w=i;
        break;
    }
    cout << abs(w-(sum-w)) << endl;

    return 0;
}

Friday, October 11, 2013

Broadcasting audio/video/audio & video. FFmpeg + FFserver

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

Официальный сайт проекта FFmpeg
Про то как кодировать аудио/видео можно почитать тут: http://nix-sa.blogspot.com/2012/02/ffmpeg.html

Если вкратце, то FFmpeg - берёт входные данные (как самые обычные файлы, так и удалённый поток), и отправляет в другом формате (если это надо) или в файл, или в поток.
FFserver - исходя из названия уже понятно, что это сервер на котором будет происходить получение данных с FFmpeg источников, буферизировать данные, перенаправлять поток,  и давать возможность получать данные устройствам через плеер (я использовал FFplay, а так же VLC).
Что касается плеера: искать нужно под свой формат и кодек. Я использовал G722. VLC отказался проигрывать этот формат, но на счастье, "родной" FFplay его подхватил (если в параметрах FFplay явно не указать g722, он работать не будет, о запуске - ниже).
G722 формат честно говоря, достаточно не плох. Качество звука хорошее, и не сильно забивает сеть.

В интернете видел вопросы людей о том что при трансляции аудио в g722 звук в два раза медленней: сам столкнулся с этим, решение заключается в том, чтобы просто правильно указать битрейт и сэмплрейт. :)


Чтобы не разбивать статью на много небольших, соберу всё здесь:

Начнём про связку FFmpeg + FFserver.

Сперва почитаем https://trac.ffmpeg.org/wiki/Streaming%20media%20with%20ffserver

Скачаем и установим пакет FFmpeg (apt-get install ffmpeg или opkg install ffmpeg или ...)
(How install ffmpeg)

Далее заходим в /etc/ и редактируем/создаём файл ffserver.conf
Пример конфигурационного файла http://ffmpeg.org/sample.html

Запускаем ffmpeg, который будет перенаправлять поток с микрофона в feed ffserver-а (возможно перекодирование).

Начинает работать ffmpeg, если вы не установили флаг запуска daemon-а в фоновом режиме, и вы работаете через терминал, то запустите вторую сессию и запустите ffserver (если конфигурационный файл лежит не в /etc/ffserver.conf, то ffserver -f *ваш путь*)

Подробней можно почитать на официальном сайте.

FFmpeg и сразу в сеть

Мой скрипт для запуска:

#!/bin/bash
############### Uni/Multu cast stream
#ffmpeg -d -f oss -i /dev/dsp -f g722 -ac 1 -ab 8 -ar 16000 \

#-acodec g722 rtp://192.168.80.4:1234

ffmpeg -d -f oss -i /dev/dsp -f g722 -ac 1 -ab 8 -ar 16000 \

-acodec g722 rtp://224.1.1.1:1234 /usb_flash/out.g722

Скрипт для прослушивания:

ffplay.exe -f rtp -acodec g722 -ar 8000 -ab 8k rtp://127.0.0.1:1234

Thursday, October 10, 2013

Knapsack problem

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

1. Когда каждый тип предмета можно брать неограниченное количество раз:

Рассмотрим следующую функцию. Пусть F(s, n) есть максимальная стоимости предметов, которые можно уложить в рюкзак максимальной вместимости n, если можно использовать только первые s предметов из заданных k.

Зададим краевые значения функции F(s, n).

Если s = 0, то F(0, n) = 0 для всех n (ни один предмет нельзя брать, поэтому максимальная стоимость равна 0).

Если n = 0, то F(s, 0) = 0 для всех s (можно брать любые из первых s предметов, но вместимость рюкзака равна 0).

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

Если предмет s не попал в максимальный рюкзак массы n, то максимальный рюкзак будет составлен только из предметов с номерами 1, ..., s - 1, следовательно, F(s, n) = F(s - 1, n).

Если же в максимальный рюкзак включен предмет s, то масса оставшихся предметов не превышает n - ws, а от добавления предмета s общая стоимость рюкзака увеличивается на ps. Значит, F(s, n) = F(s - 1, n - ws) + ps. Теперь из двух возможных вариантов составить рюкзак массы, не превосходящей n, из предметов 1, ..., s нужно выбрать наилучший:

F(s, n) = max ( F(s - 1, n), F(s - 1, n - ws) + ps ).

Теперь составим программу. Будем считать, что веса предметов хранятся в массиве w[1], ..., w[k], а их стоимости в массиве p[1], ..., p[k]. Значения функции F(s, n), где 0<=s<=k, 0<=n<=W, будем хранить в массиве F[k+1][W+1].

int F[k+1][W+1];
for(n=0;n<=W;++n)       // Заполняем нулевую строчку
    F[0][n]=0;
for(s=1;s<=k;++s)       // s - максимальный номер предмета, 
{                       // который можно использовать
    for(n=0;n<=W;++n)   // n - вместимости рюкзака
    {
        F[s][n]=F[s-1][n]; 
        if ( n>=w[s] && ( F[s-1][n-w[s]]+p[s] > F[s][n]) )
            F[s][n] = F[s-1][n-w[s]]+p[s];
    }
}

В результате исполнения такого алгоритма в элементе массива F[k][W] будет записан ответ на поставленную задачу. Легко видеть, что сложность этого алгоритма, равно как и объем используемой им памяти, являются величиной O(kW).

Рассмотрим пример работы этого алгоритма. Пусть максимальная вместимость рюкзака W = 15, количество предметов k = 5, их стоимости и массы таковы:
w1 = 6, p1 = 5, w2 = 4, p2 = 3, w3 = 3, p3 = 1,
w4 = 2, p4 = 3, w5 = 5, p5 = 6.

В приведенной ниже таблице указаны значения заполненного массива F[k+1][W+1].



2. Когда надо получить не только стоимость, но и сам набор:

Сравним значение F[k][W] со значением F[k-1][W]. Если они равны, то максимальный рюкзак можно составить без использования предмета с номером k. Если не равны, то предмет c номером k обязательно входит в максимальный рюкзак. В любом случае, задача печати рюкзака сводится к задаче печати рюкзака для меньшего числа предметов. Напишем это в виде рекурсивной функции Print(int s, int n), которая по параметрам s и n печатает номера предметов, входящих в максимальный рюкзак массой не более n и составленный из предметов 1, ..., s:
void Print(int s, int n) {
  if (F[s][n]==0) // максимальный рюкзак для параметров (s,n)
    return;        // имеет нулевую ценность, 
                   // поэтому ничего не выводим
  else if (F[s-1][n] == F[s][n]) 
    Print(s-1,n);  // можно составить рюкзак без предмета s
  else
  {                               
    Print(s-1,n-w[s]); // Предмет s должен обязательно 
    cout<<s<<endl;     // войти в рюкзак
  }
}

3. Когда каждую вещь можно брать только один раз:

Тут всё ещё проще: надо просто в каждой итерации идти по массиву не снизу вверх, а сверху вниз, то есть, от W к 0. Тогда получится, что при рассмотрении max[n-w[s]], там всё ещё будет записано, набор какой максимальной стоимости веса n — w[s] можно получить, используя только вещи 0..s-1. Следовательно, если получится, что в оптимальном наборе надо использовать s-тую вещь, то она будет использована только один раз.

4. Выбор значения, как можно более близкого к данному:

Если нет стоимости, то данный алгоритм, очевидно, помогает как можно сильнее набить рюкзак. Или же, если речь идёт не о рюкзаке, и неважно, что общий объём вещей не больше объёма рюкзака, можно из нескольких чисел с помощью сложения получить число, как можно более близкое к данному.

В всех случаях ассимптотика времени работы и используемой памяти та же.

5. Когда каждую вещь можно брать определённое количество раз:

Есть вариация задачи, в которой вещь с номером i можно брать от 0 до q[i] раз. Конечно, можно «размножить» i-ю вещь на q[i] единиц и решить задачу о 0-1 рюкзаке. Однако, сложность такого решения будет O(W * ∑q[i]), что не очень хорошо. На самом деле, можно поступить хитрее.

Пусть q[i] = 1 + 2 + 4 + 8 +… + 2^k + r[i], где r[i] < 2^(k+1).
Тогда рассмотрим k+2 предмета объёмом 1*v[i], 2*v[i], ..., 2^k*v[i], r[i]*v[i] и стоимостью 1*c[i], 2*c[i], ..., 2^k*c[i], r[i]*c[i] соответственно.
(«Названия» новых предметов — «1 i-я вещь», «2 i-ых вещи», «4 i-ых вещи» и т. д.)

Ясно, что выбирая различные подмножества этих k+2 предметов, можно получить любое количество (от 0 до q[i]) вещей типа i. Т. е. вместо «размножения» вещей i-го типа на q[i] единиц, мы сумели ограничиться k+2 = O(log q[i]) единицами.

Такой метод будет иметь сложность порядка O(W * ∑log q[i]), что куда более эффективно, чем «наивное» решение.

Tuesday, July 30, 2013

Just added syntax highlighting :)
#include <stdio.h>
int main()
{
    printf("Hello Syntax Highlighting!\n");
    return 0;
}
#include <iostream> 
int main()
{
    std::cout << "Hello Syntax Highlighting!" << endl;
    return 0;
}
public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello Syntax Highlighting!");
    }
}
public class Hello
{
   public static void Main()
   {
      System.Console.WriteLine("Hello Syntax Highlighting!");
   }
}
with Ada.Text_IO;
procedure Hello is
 use Ada.Text_IO;
begin
    Put_Line("Hello Syntax Highlighting!");
end;
         .486p
         .model  flat,STDCALL
include  win32.inc
 
extrn            MessageBoxA:PROC
extrn            ExitProcess:PROC
 
.data
 
Hello db "Hello Syntax Highlighting!",0
msgTitle db "Hello",0
 
.code
Start:
         push    MB_ICONQUESTION + MB_APPLMODAL + MB_OK
         push    offset msgTitle
         push    offset HelloWorld
         push    0
         call    MessageBoxA
 
         push 0
         call ExitProcess
ends
end Start
(Кому интересно - ссылка)

Sunday, January 27, 2013

Helper - A tool for creating control scripts (mouse) + Recursive execution



Надоело возиться с однотипными действиями. Первым делом полез в интернет. Думал найти удобную программу, в которой можно быстро и без усилий создавать скрипты управления мышкой. В итоге, остался не удовлетворён увиденным. Или слишком громоздкая, или платная, или вообще, тормозит на середине выполнения. Познакомился с AHT (Autohotkey), AutoIt. Но желание написать собственную софтину не пропало. И вот, как видите, результат моей
двухчасовой работы (:
Изобретать велосипед не пришлось, так как уже ранее были наработки с либой от AutoIt (которую предварительно нужно зарегистрировать в системе, для этого зайдите в папку "Регистрация" и запустите "HelperRegistrator.exe").
Для перехвата данных о текущем положении курсора в системе (состояний кнопок и т.д.) использовал тоже ранее реализованные наработки GlobalHook.
Можно изменять скорость перемещения (двигается плавно, с разгоном и торможением).
Если CheckBox "Нажимать автоматически" установлен, то после каждого нажатия "Нажать"/"Отпустить"/"Кликнуть" будет нажиматься клавиша "Получить координаты" и после, можно тыкать в пункт назначения. Если не выставлен, то нужно начать одну из кнопок выбора действия ("Нажать"/"Отпустить"/"Кликнуть") и далее, можно прописать вручную координаты, или начать на "Получить координаты" и как я уже писал, тыкать в пункт назначения. Всё просто. Если нажать правой клавишей мыши на список последовательных команд, по выпадет меню, в котором можно удалить текущий элемент, передвинуть его вверх/вниз или сделать его клон.

При добавлении действия в список, указывается сколько раз программа должна выполнить данную процедуру. Например, если вам необходимо кликнуть мышью 2, 3 или даже 100 раз, то достаточно просто добавить в список элемент "Кликнуть", после нажать на него в в списке, прописать справа количество, и нажать Ок.
Если вы хотите включить в содержимое скрипта другой скрипт (который так же может включать в себя целое дерево скриптов с разной вложенностью), то необходимо нажать на кнопку Загрузить скрипт. Что бы выполнить скрипт несколько раз, необходимо сделать ту же инструкцию, что и на количество кликов.
Если необходимо вставить задержку (которая указывается в миллисекундах, 1000 миллисекунд - 1 секунда), достаточно указать числовое значение и нажать кнопку "Задержка".

При запуске запускается второй поток (параллельный главному но с более низким приоритетом) и начинается процесс работы.
Естественно, скрипты можно сохранять/загружать.

Если в процессе выполнения скрипта необходимо остановить выполнение - жмите Escape ;)

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

Tuesday, January 8, 2013

Portable console on STM32F407. Part 4. Configuration Panel; User Controls




И так, что нового в новой версии моей консоли.
Все кнопки, которые раньше были прямоугольные, стали с закругленными краями (скрестил алгоритм рисования прямоугольников и алгоритм быстрого рисования окружностей).
Были реализованы высокоуровневые пользовательские элементы управления (Button, CheckBox, RadioButton, MessageBox, ValueRegulator). Пользоваться ими просто элементарно! Отняли хороший кусок времени. Каждый элемент состоит из .c и .h файла, с едиными общепринятыми функциями (т.е. все п.э.у. написаны по одному шаблону), видимые  программисту, который работает с обслуживанием данных пользовательских элементов. Это функции:
  • Add - функция добавления в коллекцию
  • Clear - очистка коллекции
  • Handle - обслуживание (на вход 3 параметра: координата X (с тачскрина), Y, и флаг нажатия)
  • Draw - прорисовка п.э.у (параметром служит флаг был ли нажат или активирован данный п.э.у)
У каждой функции элемента управления свой соответствующий префикс (например UserControl_Button_Add)
Реализовав всю эту систему (которая сама создает и обслуживает коллекцию п.э.у), стало намного проще проектировать пользовательский интерфейс. Добавить кнопку или любой элемент управления можно буквально несколькими строками кода. (при добавлении нового э.у. типа Button, одним из параметров служит ссылка на функцию, которая вызывается при нажатии, благодаря этому, обслуживание кнопок (а так же других п.э.у.) сводиться к тому что бы добавить и просто отправлять значения с тачскрина).
Если кого-то заинтересует - могу поделиться исходниками. (:

Главной частью любого терминала - естественно панели настроек. Я разместил всё, что требуется при работе с USART. Скорость порта, стоп биты, четность, контроль, размер пакетов. Конфигурация для перевода строки (CR, LF). Режим Echo.

Так же на плате появился многофункциональный модуль Bluetooth, который может быть как мастером, так и слэйвом. А так же может быть включен как в режим передачи данных, так и в режим конфигурации, в котором масса полезный настроек. Об этом подробно пойдет речь в 5 части.

Полным ходом идет разработка загрузчика, который является своего рода shell-ом, а так же ядром (сокращенно "кореш"). Он размещается в первом пользовательском секторе флешь. Ядро - так как включает в себя поддержку всех периферийных устройств. Начиная с дисплея, и заканчивая модулем Bluetooth. Функции управления размещены вместе с самим загрузчиком, а начиная со второго пользовательского сектора флешь, начинается таблица адресов, для косвенного обращения к функциям ядра, и сама программа (которую загружает загрузчик с карты памяти, и в последствии запускает её). Подробно об этом вы сможете почитать в 6 части.

Friday, December 28, 2012

Portable console on STM32F407. Prat 3. Coordinate reeding, temperature and battery level from ADS7846.

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

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

Я уже частично начал рассказ о работе с сенсорным экраном в прошлой части статьи (все конкретно по режимам работы).

STM32 общается с контроллером сенсорного экрана по SPI. Полный запрос вмещается в один байт. Процесс получения данных выглядит: "Открытие диалога", "Последовательная отправка байта запроса", "Получение ответного байта (12/8 бит)", "Завершение диалога".

Содержание "байта запроса"







Описание битов "байта запроса"





















Из описания следует, что седьмой бит (START) должен быть всегда выставлен в 1.

Биты 6-4 (A2-A0) - выбор канала чтения.

Описание битов "байта запроса"












Бит 3 (MODE) управляет длиной ответного пакета данный (0 - 12 бит, 1 - 8 бит).

Бит 2 (SER/DFR) выбор режима (0 - дифференциальный режим, 1 - несимметричность режим).
Говоря практическим языком, если требуется читать координаты - выбирается дифференциальный режим (выставить в 0), а если получить значение батареи или датчика температуры - несимметричный (выставить в 1).


Несимметричный режим.

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


Дифференциальный режим.

Работа АЦП в дифференциальном режиме похожа на работу в несимметричном режиме, за исключением того, что внутренние ключи должны быть включены от начала сбора данных до окончания преобразования. При преобразовании в дифференциальном режиме напряжение перпендикулярной линии используется в качестве опорного, что позволяет измерять не абсолютное значение напряжения, а коэффициент передачи резистивного делителя. Это означает, что, если напряжение на перпендикулярной линии изменится за счет изменения напряжения источника питания, изменения полного выходного сопротивлении драйвера при изменении его напряжения питания или температуры или изменения полного сопротивления сенсорного экрана из-за изменения температуры, то это изменение будет скомпенсировано за счет работы аналого-цифрового преобразователя в режиме измерения коэффициента передачи.

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

(Ссылка на источник)

Биты 1 и 0 (PD1-PD0) отвечают за введение контроллера сенсорного экрана в различные режимы, в которых у контроллера сенсорного экрана будет разное энергопотребление.

Описание битов выбора режима контроллера


















Процедура записи данных:

static void WR_CMD (uint8_t cmd)
{
   /* Wait for SPI2 Tx buffer empty */
   while (SPI_I2S_GetFlagStatus(Open_SPI, SPI_I2S_FLAG_TXE) == RESET);
   /* Send SPI2 data */
   SPI_I2S_SendData(Open_SPI,cmd);
   /* Wait for SPI2 data reception */
   while (SPI_I2S_GetFlagStatus(Open_SPI, SPI_I2S_FLAG_RXNE) == RESET);
   /* Read Open_SPI received data */
   SPI_I2S_ReceiveData(Open_SPI);
}


Процедура чтения данных:

static int RD_AD(void)

{ 

   unsigned short buf,temp;

   /* Wait for Open_SPI Tx buffer empty */

   while (SPI_I2S_GetFlagStatus(Open_SPI, SPI_I2S_FLAG_TXE) == RESET);

   /* Send Open_SPI data */

   SPI_I2S_SendData(Open_SPI,0x0000);

   /* Wait for SPI2 data reception */

   while (SPI_I2S_GetFlagStatus(Open_SPI, SPI_I2S_FLAG_RXNE) == RESET);

   /* Read Open_SPI received data */

   temp=SPI_I2S_ReceiveData(Open_SPI);

   buf=temp<<8;

   DelayUS(1);

   while (SPI_I2S_GetFlagStatus(Open_SPI, SPI_I2S_FLAG_TXE) == RESET);

   /* Send Open_SPI data */

   SPI_I2S_SendData(Open_SPI,0x0000);

   /* Wait for Open_SPI data reception */

   while (SPI_I2S_GetFlagStatus(Open_SPI, SPI_I2S_FLAG_RXNE) == RESET);

   /* Read Open_SPI received data */

   temp=SPI_I2S_ReceiveData(Open_SPI);

   buf |= temp;

   buf>>=3;

   buf&=0xfff;

   return buf;

} 



Процедура получения координаты X:

int Read_X(void)

{  

   int i;

   TP_CS(0);

   DelayUS(1);

   WR_CMD(0x90);

   DelayUS(1);

   i=RD_AD();

   TP_CS(1);

   return i;

}



Процедура получения координаты Y:

int Read_Y(void)

{

   int i;

   TP_CS(0);

   DelayUS(1);

   WR_CMD(0xD0);

   DelayUS(1);

   i=RD_AD();

   TP_CS(1);

   return i;

} 



Процедура получения текущего уровня заряда:

int Read_Bat(void)

{

   int i;

   TP_CS(0);

   DelayUS(1);

   WR_CMD(0xa4);

   DelayUS(1);

    i=RD_AD();

   TP_CS(1);

   return i;

}



Ссылки на части статьи проекта:

Портативная консоль на STM32F407. Часть 1
Портативная консоль на STM32F407. Часть 2. Питание от батареи.

Portable console on STM32F407. Part 2. Battery.

На столе система работает замечательно. Но когда дело заходит о портативном устройстве, которое должно питаться от батареи, приходиться задумываться о том, что бы батареи хватало на длительный промежуток времени. Для этого нужно организовать режим сна, в котором контроллер будет как можно более экономичным. Эта статья будет посвящена тому, как я работал над переходом моей консоли в режим низкого энергопотребления, а так же расскажу про режим низкого энергопотребления в STM32. В моем проекте эту часть можно было реализовать аппаратно, поставив банальный выключатель или сделать систему на трех транзисторах, которая задействует две ножки контроллера и к тому же не очень надежна. Но что бы не вносить изменения в схему устройства, пришлось реализовывать все это программно. К счастью в STM32 есть 3 режима низкого энергопотребления, у каждого есть свои плюсы и минусы.
  • Sleep. Первая ступень экономичной работы.
    По умолчанию, после выполнения процессором Cortex инструкции WFE или WFI, отключается внутренняя синхронизация и прекращается выполнение кода программы. В режиме SLEEP, остальная часть STM32 продолжает работу. Выход из режима SLEEP происходит, когда УВВ (Устройство ввода-вывода) генерирует прерывание. Если STM32 синхр
    онизируется блоком ФАПЧ (Фазовая автоподстройка частоты) и HSE-генератором частотой 72 МГц и использует все УВВ, то при переходе в режим SLEEP потребляемый ток снизится до приблизительно 14.4мА. Тем не менее, если выполнить специальную подготовку STM32 к переходу в экономичный режим, отключив синхронизацию всех УВВ, кроме тех, что используются для возобновления работы процессора Cortex, и переключившись на синхронизацию от HSI-генератора (частоту которого можно снизить до 1 МГц и даже менее), можно добиться снижения потребляемого тока до приблизительно 0.5 мА.
    Потребляемый ток можно так же снизить до 0.14 мА
  • Stop. Вторая ступень экономичной работы. 
    Для этого необходимо установить бит SLEEPDEEP в регистре управления энергопотреблением ядра Cortex и сбросить бит Power Down Deep Sleep (PDDS) в регистре управления энергопотреблением STM32.
    После завершения конфигурации режима STOP, выполнение инструкции WFI или WFE приведет к остановке процессора Cortex и отключению HSI- и HSE-генераторов. Флэш-память, статическое ОЗУ и УВВ остаются запитанными, поэтому, состояние STM32 сберегается. Также как и в случае с режимом SLEEP, выход из режима STOP возможен путем генерации прерывания УВВ. Однако в режиме STOP синхронизация всех УВВ отключена, за исключением контроллера внешних прерываний. Таким образом, выход из режима STOP возможен при изменении состояния любой из линии ввода-вывода. Кроме того, у контроллера внешних прерываний имеется одна линия, которая может, как запрашивать, так и генерировать прерывание по достижении заданного времени часами реального времени. Поскольку у часов реального времени имеется отдельный генератор (LSI или LSE), то они могут использоваться для генерации периодических прерываний для вывода STM32 из режима STOP.
    После перехода STM32 в режим STOP его потребляемый ток снижается с миллиампер, потребляемых в режиме RUN, до приблизительно 24 мкА. Дальнейшего снижения энергопотребления можно добиться переводом внутреннего генератора в специальный экономичный режим работы. Для этого необходимо установить бит LPDS в регистре управления энергопотреблением STM32. Если при переходе в режим STOP данный бит был установлен, то потребляемый ток снизится до 14 мкА. Если используются часы реального времени, то потребляемый ток увеличится на 1.4 мкА.
  • StandBy. Третья ступень экономичной работы. STM32 можно настроить на работу в режиме STANDBY, если установить бит SLEEPDEEP в регистре управления энергопотреблением ядра Cortex и установить бит Power Down Deep Sleep в одноименном регистре STM32. После этого, выполнение инструкции WFI или WFE приведет к переводу STM32 в режим с наименьшим энергопотреблением. В режиме STANDBY STM32 абсолютно полностью бездействует. Отключены внутренний стабилизатор напряжения и HSE- и HSI-генераторы. В этом режиме STM32 потребляет ток всего лишь 2 мкА.
    В режиме STANDBY потребляемый ток равен 2 мкА, а задержка возобновления составляет 50 мкс. Выход из режима STANDBY возможен по прерыванию часов реального времени (достижение заданного времени) точно также как и при выходе из режима STOP. Кроме того, возобновление возможно через внешний вывод сброса STM32 или с помощью независимого сторожевого таймера. Выход из режима STANDBY также возможен по нарастающему фронту на линии 0 порта А. Данный вывод можно настроить, как вывод возобновления WKUP путем установки бита EWUP в регистре управления энергопотреблением и статуса. Поскольку режим STANDBY самый маломощный, то и выход из него осуществляется дольше всего: задержка возобновления исполнения инструкций составляет около 50 мкс.
    После перехода в режим STANDBY содержимое статического ОЗУ, регистров ядра Cortex и STM32 теряется. Выход из режима STANDBY практически идентичен программному сбросу.
Так как значения SRAM хранить было не обязательно, а все параметры из настроек хранятся на карте памяти, в проекте консоли было решено использовать режим StandBy. Что бы достичь как можно более низкого энергопотребления без внесения и нагромождения аппаратной части проекта, требовалось программно отключать (переводить в режим низкого энергопотребления) всю периферию. Сперва взялся за дисплей. Подсветка была на одной из ножки GPIO контроллера, поэтому это можно было оставить, так как при переходе в экономичный режим, все значения на портах вывода сбрасывались в ноль. Контроллер дисплея (ILI9481) потреблял около 14 мА, что достаточно много. Для решения данной проблемы необходимо ввести контроллер дисплея в режим Sleep. Для того что бы реализовать это с ILI9481, достаточно отправить контроллеру команду выбора регистра 0x10. Номер регистра для перехода в режим Sleep можно найти в документации на ваш дисплей. Вторым этапом понижения энергопотребления была сенсорная панель. В моем проекте использовался контроллер ADS7846, который кроме того, что может выдавать текущее состояние о нажатии, может отправлять текущее значение уровня заряда батареи, и значение с двух датчиков температуры (о получении координат, уровня заряда и температуры в следующей части статьи).
STM32 общается с контроллером сенсорного экрана по SPI. Полный запрос вмещается в один байт. Процесс получения данных выглядит: "Открытие диалога", "Последовательная отправка байта запроса", "Получение ответного байта (12/8 бит)", "Завершение диалога".   

Содержание "байта запроса"

Описание битов "байта запроса"

Из описания следует, что седьмой бит (START) должен быть всегда выставлен в 1.
(Полное описание в следующей части статьи)
Биты 1 и 0 (PD1-PD0) отвечают за введение контроллера сенсорного экрана в различные режимы, в которых у контроллера сенсорного экрана будет разное энергопотребление.

Описание битов выбора режима контроллера
В итоге, для того что бы перевести сенсорный экран в режим низкого энергопотребления, необходимо отправиться на контроллер 10000000 в бинарном виде или 0x80 в шестнадцатеричном.

На текущий момент удалось добиться потребления в режиме StandBy - 0.4 мА


(Функции работы с контроллером сенсорного экрана описаны в следующей части статьи)

Ссылки на части статьи проекта:

Портативная консоль на STM32F407. Часть 1

RGB and Alpha

Сегодня, работая с проектом консоли, реализованной на STM32F407, столкнулся с проблемой рисования графических элементов с процентным соотношением смешивания самого элемента и фонового графического изображения под ним (фона).

Рис. 7.1. Непрозрачный чайник
Непрозрачный чайник
Рис. 7.2. Прозрачный чайник
Прозрачный чайник
  


Для реализации данной задачи, достаточно одной очень простой формулы.




В проекте консоли, цвета представляются в формате RGB. Будем брать в процентном соотношении коэффициент прозрачности (от 0.0 до 1.0) и разность 1 и коэффициента прозрачности фона. Формулу применяем и для R, и для G, и для B. Вот что получилось в итоге:
NEW.R = FIGURE.R * ALPHA + BACKGROUND.R * (1 - ALPHA)
NEW.G = FIGURE.G * ALPHA + BACKGROUND.G * (1 - ALPHA)
NEW.B = FIGURE.B * ALPHA + BACKGROUND.B * (1 - ALPHA)
где NEW - это результирующий цвет смешивания, FIGURE - цвет фигуры (которая накладывается), BACKGROUND - цвет фона, на который накладывается фигура (изображение).
Вот собственно и всё.

Tuesday, December 25, 2012

Johny - A smart friendly robot


v.3.0.0
Началось все ещё года 2 назад. Этот робот постоянно модернизировался и вот так он выглядит на сегодняшний день. В этой версии была полностью переделана главная плата (но распиновка периферии почти не изменилась). Так же были добавлены боковые оптические датчики, в следствии чего робот перестал натыкаться на стены и объекты, к которым подъезжал под углом (ультразвук отражался от стен). Ориентация робота в пространстве улучшилась в разы. Был снят ИК приемопередатчик, поэтому теперь роботом нельзя управлять с пульта от телевизора  На замену ему появился Bluetooth. Скорость общения 9600. На компьютере организован виртуальный COM-порт. Имея такую беспроводную связь, можно в последствии развить проект, что робот будет отправлять данные на компьютер, и компьютер основываясь на полученных данных  (а так же видео с камеры, полученные из ресивера) может строить карту помещения, распознавать объекты, двигаться по проложенным маршрутам (когда сформирует полную карту помещения).
 Новые мозги
                              
Боковые оптические датчики

 
                                   
Предыдущих версии:

v.2.0.0 (вид сбоку)

v.2.0.0 (вид спереди)

v.2.0.0 (вид сзади)

В версии v.2.0.0 главным изменением было то, что у робота теперь вместо "светодиодной" головы были установлены ультразвуковые датчики и аналоговая видео камера, которая работает независимо от робота (передает видео на ресивер по беспроводному соединению). Вместо двух "мозгов" (главный контроллер и "светодиодная голова") теперь один. Раньше, за звуки отвечала "светодиодная голова", теперь робот вынужден молчать (но к счастью вскоре на роботе появился звуковой модуль, который способен воспроизводить аудио-файлы в формате .wav).

Самая первая версия робота (v.1.0.0):

Управляется с помощью пульта. Нет никакой ориентации, никаких датчиков. Только весёлый робот и потешные рожицы на RGB матрице 8х8. 







Portable console on STM32F407. Part 1. Introduction









Началось все с того, что приобрел ЖК дисплей разрешением 320x480 (ILI9481) и резистивный сенсорный экран с контроллером ADS7846. Дисплей подключал по FSMC. Контроллер сенсорного экрана по SPI.
В начале работы выполняется инициализация RTC (Read Time Clock), а так же параметры для перехода в режим сна (STANDBY Mode, потребление около 2мкА).

Были разработаны следующие драйвера:
  • Драйвер управления дисплеем:
    • Инициализация и настройка FSMC
    • Инициализация и настройка контроллера дисплея
    • Заливка экрана
    • Заливка пикселя
    • Рисования текста (используя таблицу шрифтов, в данном проекте это шрифт размерами 5x8)
    • Рисование основных геометрических примитивов (линии, эллипсы, прямоугольники)
    • Рисование изображений с внутренней (flash) памяти
    • Рисование изображений с карты памяти
    • Аппаратная прокрутка содержимого дисплея
  • Драйвер управления сенсорным экраном:
    • Инициализация и настройка SPI
    • Инициализация и настройка контроллера сенсорного экрана
    • Сохранение калибровочных данных на карту памяти
    • Загрузка калибровочных данных с карты памяти
    • Считывание значения текущего напряжения батареи (в ADS7843 такой функции нет)
  • Драйвер управления картой памяти:
    • Настройка транспорта SDIO
    • Реализация технологии файловой системы (FatFs)
Драйвер для управления дисплеем разделяется на два файла. В первом по шаблону записаны основные функции, такие как: настройка транспорта, инициализация, вывод пикселя, и получение разрешения дисплея. (эти файлы носят названия с конкретным названием дисплея, по сути они являются низкоуровневыми драйверами, имея множество таких файлов заполненных по одному шаблону, можно менять устройство вывода без каких либо затруднений). А так же файлы низкоуровневого управления (текст, фигуры, графика). 
Разобравшись с работой USART в STM32, я реализовал систему общения, буферизации принятых данных, и вывода принятых данных на дисплей. При полном заполнении экрана текстом, выполняется аппаратная прокрутка со смещением содержимого рабочей области терминала на одну строку.
Размер буфера можно изменять по мере необходимости (изначально был установлен в 50 000 символов, чего вполне хватало для умеренной работы). Для моргания курсора специально завел таймер, при срабатывании которого изменяется состояние курсора, и на каждое 100 срабатывание выполняется обновление значений и прорисовка часов и батареи. 
Также ввел поддержку esc-последовательностей (которые начинаются с \e и зависят от двух параметров), которые часто используются в терминалах для выделения участков теста цветом. Функции перевода строки, перевода каретки и табуляция так же поддерживаются. 
В процессе работы, вся полученная информацию из USART записывается на карту памяти в файле LOG.txt. Все настройки сохраняются в SETTINGS.txt
Реализована система обработки пользовательских элементов (кнопки, формы) при срабатывании сенсорного экрана. 
Встроенная пользовательская клавиатура для набора текста. Имеет русскую и английскую раскладку, а так же почти все символьные знаки что и на классической клавиатуре. При открытии и закрытии изменятся высота рабочей зоны терминала и зоны аппаратной прокрутки содержимого.
На панели задач отображается текущий номер страницы и клавиши переключения страниц (неактивны при развёрнутой клавиатуре). Индикатор батареи, который может быть раскрашен 3 цветами в зависимости от уровня заряда (зелёный, жёлтый, красный). Внутри индикатора отображается текущее время в формате часы : минуты. А так же кнопки открытия меню и изменения состояния клавиатуры.
В меню выполняется настройка скорости порта (BaudRate), и текущего времени. Там же можно перейти в режим сна (для выхода из режима сна необходимо нажать боковую кнопку на устройстве, при нажатии которой происходит перелистывание страниц вверх).
Ведется обслуживание USB Host для HID устройств. Мышку обслуживает здорово, правда иногда не с первого раза подхватывает. Клавиатуру классическую (больших размеров) тоже нормально обслуживает, но тоже порой не с первого раза подхватывает, и при нажатии на Caps Lock/Num Lock/Scroll Lock не зажигаются светодиоды на клавиатуре. А вот с клавиатурой небольших размеров (так же беспроводными) ничего не выходит - при нажатии клавиш ничего не приходит. (Может кто имел дело с этим?)
Так же на данном устройстве в последствии будет установлен Bluetooth модуль, с помощью которого можно будет общаться с устройствами без проводов.