Добавить форум Lasers.fonarevka.ru в Избранное ×
LASERS.FONAREVKA.RU - Всё о лазерах и лазерной технике  

Вернуться   Форум Lasers.fonarevka.ru > Электроника > Микроконтроллеры
Забыли пароль? Регистрация
Темы с вашим участием Новые сообщения Сообщения за день

  • Бесплатные розыгрыши призов
  • Для гостей форума
  • Пожертвования (Donate)
На форуме ежемесячно проходят бесплатные розыгрыши призов для зарегистрированных и активно общающихся форумчан. Вы можете выиграть не только различные лазерные указки и другое лазерное оборудование, но и фонарики, зарядные устройства, аккумуляторы и другие аксессуары известных брендов, а также фонари и компоненты от известных кастомщиков и мелкосерийных производителей. Также разыгрываются и другие призы, такие как мультитулы, ножи, рюкзаки и другое снаряжение. Все проходящие розыгрыши призов являются действительно абсолютно бесплатными для самих участников, т.к. все расходы берут на себя организаторы (т.е. владельцы данного форума) и сами спонсоры, которые предоставляют тот или иной приз для наших розыгрышей призов. Форумчане не несут никаких имущественных рисков, связанных с участием в данных розыгрышах (т.е. вы ничего не оплачиваете и ничем не рискуете). Вам достаточно нажать кнопку "Принять участие" и ждать результата!

На данный момент проходит 1 розыгрыш призов.

1. Зарядное устройство XTAR XP4 Panzer + 2 аккумулятора Panasonic NCR18650B 3400 мАч

Рекомендуем Вам подписаться на тему (оптимальнее с уведомлением на ваш E-mail), где публикуется информация о новых розыгрышах призов.
Подробная информация по ссылке
Искренне рады видеть Вас на нашем независимом проекте о лазерах и лазерной технике!

Что Вам даст регистрация на нашем проекте:


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

и много других приятных привилегий

Искренне надеемся, что Вам у нас понравится!
Если вы хотите финансово поддержать работу проекта LASERS.FONAREVKA.RU, будем искренне благодарны!
Даже самая малая сумма поможет дальнейшему развитию и прогрессу нашего с вами уже общего проекта.


Ответ
Просмотров в теме 114354   Ответов в теме 119   Подписчиков на тему 10   Добавили в закладки 0
Опции темы
Старый 19.10.2010, 15:59   1
Старший научный сотрудник
 
Аватар для Gall
 
Регистрация: 21.06.2010
Последняя активность: 02.08.2015 00:26
Адрес: Екатеринбург
Сообщений: 205
Сказал(а) спасибо: 0
Поблагодарили 1 раз в 1 сообщении
По умолчанию Основы программирования контроллеров AVR на Си

По просьбам посетителей форума пишу мини-учебник по началам программирования.

Предполагается, что на компьютере уже установлен компилятор Си для AVR (avr-gcc, он же в Windows называется WinAVR) с прилагаемой к нему стандартной библиотекой avr-libc. Также предполагается, что читатель умеет запускать его и делать из файла *.c файл *.hex, а потом прошивать его в микроконтроллер. Все это здесь объясняться не будет, а рассмотрим мы только написание собственно программы, то есть файла на Си.

1. Основы синтаксиса Си. Простейшие программы.

Начнем с того, что напишем на Си программу, которая ничего не делает. Вот она:
Код:
int main(void)
{
    /* не делаем ничего */
    return 0;
}
Здесь мы уже сталкиваемся с таким понятием языка Си, как функция. В языке Си функция - примерно то же самое, что функция в математике, а в других языках она может называться подпрограммой или процедурой. Функция в Си является кусочком программы, содержащим некоторую последовательность действий. В этом фрагменте кода мы объявляем функцию под названием main, тело которой состоит из единственной строки return 0;, требуемой лишь стандартом Си. Позднее мы поймем, что это значит, а пока просто запомним, что:
основное тело программы в Си пишется в функции main

Знаками /* ... */ в Си обозначаются комментарии.

Теперь следовало бы написать программу "Hello World". Но у нас нет экрана, куда можно было бы вывести такое сообщение. Поэтому мы просто включим светодиод. Подключим светодиод к выводу PORTB0 нашего микроконтроллера через резистор так, чтобы он горел при появлении единицы на этом выводе. Программа, включающая его, будет выглядеть так:
Код:
#include <avr/io.h>

int main()
{
    DDRB = 0x01;
    PORTB = 0x01;
    return 0;
}
Первая строка #include означает подключение библиотечного файла к нашей программе. В программе мы воспользовались портами ввода-вывода процессора. Это не часть языка Си, это библиотечные имена, и содержатся они в файле <avr/io.h> стандартной библиотеки.

2. Работа с портами ввода-вывода AVR.

Для каждого порта микроконтроллера в библиотеке <avr/io.h> определены три специальных имени, через которые с ним можно работать из программы. Это PORTx, PINx и DDRx, где x - какая-то буква. Например, DDRB и PORTB. Каждый из них представляет собой 1 байт (число из 8 битов, то есть от 0 до 255), каждый бит соответствует одному из проводов порта (нумерация битов справа налево от 0 до 7).

Регистр DDR отвечает за направление порта: 0 - вход, 1 - выход.
Регистр PORT в режиме выхода просто выдает 0 или 1 на соответствующий вывод.
Регистр PORT в режиме входа включает (1) или выключает (0) резистор подтяжки вывода на плюс.
Регистр PIN во всех режимах отражает реальное состояние нулей и единиц на ножках контроллера. Он только для чтения, пытаться изменять его бесполезно. У порта на выходе его содержимое просто повторяет содержимое регистра PORT (если только на выводах контроллера не КЗ), а у порта на входе это и есть регистр, через который читают входы.

Обращаются к регистрам из Си просто - подставляют их в арифметические выражения. Чтобы зажечь светодиод на PORTB0, нам надо сделать две вещи - переключить PORTB0 в режим выхода (поставить DDRB в двоичное 00000001, то есть B7-B1 входы, а B0 выход), а потом в PORTB поднять 0-1 бит (поставить тоже в 00000001, то есть на B7-B1 выключить резисторы подтяжки [они входы], а B0 сделать единицей). Оба числа в десятичной системе - просто 1. Можно было бы написать:
Код:
DDRB = 1;
PORTB = 1;
и это работало бы. Но из двоичной системы в десятичную переводить в уме трудно. Гораздо проще переводить в шестнадцатеричную - это легко научиться делать в уме. Поэтому мы в шестнадцатеричной системе и запишем: 01. Чтобы Си понял, что это не десятичная система, нужно перед числом написать специальный магический код "0x" - признак 16-ричности. И получается:
Код:
DDRB = 0x01;
PORTB = 0x01;
Подставив это в main, получим программу, которая написана выше.

Если мы хотим зажечь два светодиода сразу, на PORTB0 и на PORTB7, то мы напишем это так:
Код:
int main()
{
    DDRB = 0x81;
    PORTB = 0x81;
    return 0;
}
Упражнение. Почему эта программа зажигает PORTB0 и PORTB7?
Упражнение. Что надо написать, чтобы зажечь PORTB4, PORTB0 и PORTB7 одновременно?

[Исправлено: Gall, 19.10.2010 в 17:46].
Gall вне форума   Ответить с цитированием Вверх
Пользователь сказал cпасибо:
Жуков (26.08.2015)
Старый 19.10.2010, 17:42   2
Gall
Старший научный сотрудник
 
Аватар для Gall
 
Регистрация: 21.06.2010
Последняя активность: 02.08.2015 00:26
Адрес: Екатеринбург
Сообщений: 205
Сказал(а) спасибо: 0
Поблагодарили 1 раз в 1 сообщении
По умолчанию

3. Условия и циклы.

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

Пусть на PORTB подключены 8 светодиодов, а на PORTA - 8 кнопок на землю (при отпущенной кнопке на выводе 1 за счет резистора подтяжки, при нажатой - 0). Светодиоды подключены с выводов на плюс питания через резисторы - при 0 горят, при 1 гаснут. Задача: сделать, чтобы при нажатии кнопки соответствующий светодиод загорался, а при отпускании - гас.

Совершенно очевидно, что основная часть кода программы будет выглядеть так:
Код:
PORTB = PINA;
Эта строчка сделает именно то, что надо - прочитает 8 входных битов от 8 кнопок и подаст их на 8 выходов. Прежде чем ее выполнить, нам потребуется проинициализировать железо - сделать PORTB выходом, а PORTA входом, причем включить на PORTA подтяжку. Это просто:
Код:
DDRB = 0xFF;
DDRA = 0xFF;
PORTA = 0xFF;
Напоминаю, что 0xFF (шестнадцатеричное FF) - это 11111111 в двоичной, то есть все единицы. Теперь надо сделать, чтобы строка PORTB = PINA повторялась бесконечно. Но как? А очень просто:

Код:
#include <avr/io.h>

int main()
{

    DDRB = 0xFF;
    DDRA = 0xFF;
    PORTA = 0xFF;

    while (1)
    {
         PORTB = PINA;
    }

    return 0;
}
Оператор while - это оператор цикла. В круглых скобках указывается условие повторения, а затем в фигурных - тело цикла. (Тело цикла из единственной команды разрешается указывать без фигурных скобок, но мы так делать не будем.) Условие в данном случае - "всегда да", то есть "1". Условие "никогда" записывалось бы "0" (но зачем?), а еще можно было бы записать другие условия. Подробнее условия мы рассмотрим, когда будем разбирать оператор if.

Следующая задача. В той же схеме переделаем прошивку так, чтобы при нажатии любой кнопки все светодиоды загорались, а при отпускании - гасли. Зажечь все - это PORTB = 0xFF, а погасить - это PORTB = 0x00. Если нажата хотя бы одна кнопка, то PINA будет не равен 0xFF.

В Си условия "больше", "меньше" и т.п. пишутся так:

< меньше
> больше
>= больше или равно
<= меньше или равно
== равно
!= не равно

Обратите внимание, "равно" пишется двумя знаками равенства. Один знак равенства - это присваивание, а не сравнение! Это частая ошибка.

Если надо составить сложное условие из простых, то используются операторы:

&& и
|| или
! не

а также простые круглые скобки, как в арифметике, для указания порядка действий. Например: ! (x > 3) || (y < 4) означает "x не больше 3 или y меньше 4".

И еще одна особенность: если в качестве условия подставить просто число, без всяких сравнений, это будет то же самое, что "число не равно 0". Именно этим мы пользовались, когда мы писали while (1) - это было то же самое, что написать while (1 != 0).

Осталось понять, как пишется "если". Очень просто - if. Программа выглядит теперь так:

Код:
#include <avr/io.h>

int main()
{

    DDRB = 0xFF;
    DDRA = 0xFF;
    PORTA = 0xFF;

    while (1)
    {
        if (PINA != 0xFF)
        {
            PORTB = 0x00;
        }
        else
        {
            PORTB = 0xFF;
        }
    }

    return 0;
}
Тут все просто: "если, то, иначе". Если нам не надо "иначе", то else и скобок после него не будет.
Gall вне форума   Ответить с цитированием Вверх
Старый 19.10.2010, 18:30   3
SviMik
Администратор
 
Аватар для SviMik
 
Регистрация: 26.02.2010
Последняя активность: 18.08.2015 19:47
Адрес: Tallinn, Estonia
Сообщений: 1101
Сказал(а) спасибо: 0
Поблагодарили 0 раз(а) в 0 сообщениях
По умолчанию

Получилось немного в кучу В идеале, "синтаксис Си" и "вещи, специфичные для МК", надо написать отдельными пособиями. И то и другое интересно, но кто уже имеет представление о самом Си, тому будет немного скучно читать про циклы и условия
SviMik вне форума   Ответить с цитированием Вверх
Старый 19.10.2010, 18:36   4
Gall
Старший научный сотрудник
 
Аватар для Gall
 
Регистрация: 21.06.2010
Последняя активность: 02.08.2015 00:26
Адрес: Екатеринбург
Сообщений: 205
Сказал(а) спасибо: 0
Поблагодарили 1 раз в 1 сообщении
По умолчанию

Цитата:
Посмотреть сообщение Сообщение от SviMik :
Получилось немного в кучу В идеале, "синтаксис Си" и "вещи, специфичные для МК", надо написать отдельными пособиями. И то и другое интересно, но кто уже имеет представление о самом Си, тому будет немного скучно читать про циклы и условия
Тому, кто знаком с Си, вместо всего этого достаточно сказать лишь:

#include <avr/io.h>
Регистры периферии отбражаются на
volatile uint8_t-переменные, имена которых совпадают с именами регистров в даташите микроконтроллера.
avr-gcc -mmcu=процессор -Os -Wall -Wextra --pedantic -o file.bin file.c
avr-objcopy -O ihex file.bin file.hex


Ну и про прерывания отдельно объяснить.

Я решил специально написать одновременно про AVR и про Си, потому что многим требуется "по-быстрому" написать прошивку, не вникая в тонкости языка. Я хочу дать рецепт, как делать не содержащие сложных алгоритмов прошивки, способные мигать светодиодами, делать ШИМ, крутить шаговые двигатели и т.п. Для более сложных задач вроде взаимодействия с компьютером знания одного лишь Си все равно недостаточно, там уже алгоритмы всерьез задействуются.

[Исправлено: Gall, 19.10.2010 в 18:43].
Gall вне форума   Ответить с цитированием Вверх
Старый 07.11.2010, 18:35   5
Gall
Старший научный сотрудник
 
Аватар для Gall
 
Регистрация: 21.06.2010
Последняя активность: 02.08.2015 00:26
Адрес: Екатеринбург
Сообщений: 205
Сказал(а) спасибо: 0
Поблагодарили 1 раз в 1 сообщении
По умолчанию

4. Переменные.

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

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

Основные типы переменных:
Код:
char - целое число для хранения кода символа, обычно 1 байт (8 бит)
short - маленькое целое число, обычно 2 байта (16 бит)
int - обычное целое число, обычно 2-4 байта (16-32 бита)
long - большое целое число, обычно 32 бита
long long - очень длинное число (64 бита), поддерживается не всеми системами
Любой из названных типов может быть как знаковым (signed), так и беззнаковым (unsigned). По умолчанию тип знаковый.

Простой пример работы с переменными:
Код:
int a;
int b;
int c;
a = 3;
b = 4;
c = a + b;
Арифметические операции пишутся как обычно: + - * / (четыре действия), % (остаток от деления). Следует иметь в виду, что деление округляет целые числа вниз. Кроме этих операций, есть еще побитовые & (и), | (или), << (сдвиг влево), >> (сдвиг вправо) и др.

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

Нетрудно заметить, что стандарт Си не гарантирует точных размеров переменных. Это может быть неудобно, поэтому в стандартной библиотеке существуют предопределенные типы фиксированных размеров. Для их использования надо включить stdint.h.
Код:
#include <stdint.h>
int8_t - знаковый 8 бит
uint8_t - беззнаковый 8 бит ("просто байт")
int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t - числа от 16 до 64 бит
Там же объявлены и другие типы, но мы пока не будем их затрагивать. Еще два важных типа объявлены в stddef.h:
Код:
#include <stddef.h>
size_t - беззнаковое число достаточного размера,
        чтобы в него влез размер любого блока памяти
ssize_t - то же, знаковое
Gall вне форума   Ответить с цитированием Вверх
Старый 07.11.2010, 18:47   6
Gall
Старший научный сотрудник
 
Аватар для Gall
 
Регистрация: 21.06.2010
Последняя активность: 02.08.2015 00:26
Адрес: Екатеринбург
Сообщений: 205
Сказал(а) спасибо: 0
Поблагодарили 1 раз в 1 сообщении
По умолчанию

5. Оператор asm.

Попробуем помигать светодиодом.
Код:
#include <avr/io.h>

int main()
{
    DDRB = 0x01;
    while (1)
    {
        PORTB = 0x01;
        PORTB = 0x00;
    }
    return 0; /* программа никогда не попадет сюда */
}
Зажигаем светодиод и снова гасим. Пробуем запустить. Оп-па... светодиод горит "вполнакала"! Посмотрим осциллографом - нет, мигает, но очень быстро (около 300 кГц при тактовой частоте процессора 1 МГц). Надо поставить задержку.

Мы пока не хотим учиться работать с аппаратными таймерами AVR, предназначенными для этой задержки, поэтому просто заставим процессор ничего не делать (точнее, делать нечто бесполезное). Например, так:
Код:
#include <avr/io.h>

int main()
{
    unsigned int c;
    DDRB = 0x01;
    while (1)
    {
        PORTB = 0x01;
        c = 0;
        while (c < 50000)
        {
            c = c + 1;
        }  
        PORTB = 0x00;
        c = 0;
        while (c < 50000)
        {
            c = c + 1;
        }
    }
    return 0; /* программа никогда не попадет сюда */
}
С выключенной оптимизацией это работает. Но если оптимизацию не выключать, будет сюрприз - компилятор удалит из программы "бесполезный" код, и задержки не станет. Надо поставить в цикл что-нибудь, например ассемблерную команду "nop", чтобы они не оптимизировались. Скажем, вот так:
Код:
#include <avr/io.h>

int main()
{
    unsigned int c;
    DDRB = 0x01;
    while (1)
    {
        PORTB = 0x01;
        c = 0;
        while (c < 50000)
        {
            asm volatile ("nop");
            c = c + 1;
        }  
        PORTB = 0x00;
        c = 0;
        while (c < 50000)
        {
            asm volatile ("nop");
            c = c + 1;
        }
    }
    return 0; /* программа никогда не попадет сюда */
}
Теперь все работает как надо.

Оператор asm позволяет вставить в программу кусочек, написанный на ассемблере. Слово "volatile" здесь означает "не оптимизировать ассемблерную вставку". В данном случае мы вставили просто nop (команду ничегонеделания), но можно вставить и довольно длинный код. Ассемблерные вставки в Си достаточно хитрые - в них можно доверить компилятору самому выбрать свободные регистры и даже небольшую оптимизацию. На практике же asm используется только для вызова таких вещей, как nop, sleep, cli и sei, недоступных из Си напрямую.
Gall вне форума   Ответить с цитированием Вверх
Старый 07.11.2010, 18:54   7
Gall
Старший научный сотрудник
 
Аватар для Gall
 
Регистрация: 21.06.2010
Последняя активность: 02.08.2015 00:26
Адрес: Екатеринбург
Сообщений: 205
Сказал(а) спасибо: 0
Поблагодарили 1 раз в 1 сообщении
По умолчанию

6. Цикл for.

В нашей программе дважды встретилась конструкция:
Код:
x = 0;
while (x < 50000)
{
    ...
    x = x + 1;
}
Язык Си позволяет записать то же самое короче. Во-первых, вместо
Код:
x = x + 1;
можно написать
Код:
++x;
, что означает то же самое - прибавление единички (инкремент). Вычитание единички можно записать --x, а еще есть операторы x++ и x--, которые делают примерно то же самое (отличия разберем позже). Получаем:
Код:
x = 0;
while (x < 50000)
{
    ...
    ++x;
}
Но можно записать еще короче. В Си есть замечательный оператор for, который пишется так:
for (с чего начать ; до каких пор продолжать ; что делать после каждого прохода)
С его использованием код будет выглядеть следующим образом:
Код:
for (x = 0; x < 50000; ++x)
{
   ...
}
Знающие Бейсик или Паскаль могут считать эту запись аналогом "for x:= 0 to 49999".
Gall вне форума   Ответить с цитированием Вверх
Старый 07.11.2010, 19:01   8
SviMik
Администратор
 
Аватар для SviMik
 
Регистрация: 26.02.2010
Последняя активность: 18.08.2015 19:47
Адрес: Tallinn, Estonia
Сообщений: 1101
Сказал(а) спасибо: 0
Поблагодарили 0 раз(а) в 0 сообщениях
По умолчанию

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

Слышал (но не видел ), что он может и в регистрах хранить. Можно обьявить какую-то переменную так, чтобы она всегда была в регистре? (например, если требуется быстрый доступ к ней).
SviMik вне форума   Ответить с цитированием Вверх
Старый 07.11.2010, 19:26   9
Gall
Старший научный сотрудник
 
Аватар для Gall
 
Регистрация: 21.06.2010
Последняя активность: 02.08.2015 00:26
Адрес: Екатеринбург
Сообщений: 205
Сказал(а) спасибо: 0
Поблагодарили 1 раз в 1 сообщении
По умолчанию

Если в компиляторе включена оптимизация (флаг -O2 или -Os в командной строке gcc), то компилятор автоматически оптимизирует размещение переменных. Обычно это означает, что "нужные" переменные оказываются в регистрах, а "ненужные" не создаются вообще. ("Ненужной" становится переменная, которая используется исключительно как промежуточный результат вычислений, и от которой компилятор в состоянии избавиться - например, заменив на константу). Ячейки оперативной памяти и LDS/STS используются тогда, когда либо не хватает регистров, либо переменная по логике программы обязана находиться именно в памяти (например, если к ней применяют взятие адреса и обращение через указатели, если переменная объявлена со словом volatile, является большим массивом или структурой и т.п.).

Если в компиляторе полностью выключена оптимизация (опция -O0 ) или компилятор просто "дебильный" (gcc таковым не является), тогда действительно все переменные попадут в память.

В старых компиляторах применялось слово-подсказка register для размещения переменных в регистрах. Современные компиляторы это слово обычно игнорируют, использовать его не рекомендуется.

Я тестировал возможности gcc/g++ по размещению переменных в регистрах. Даже подобная конструкция:
Код:
char c[128];
char p;
c[32] = 12;
p = c[32];
компилируется просто в "ldi Rxx, 12", где Rxx - регистр, выделенный для p. Массив c не создается совсем, поэтому такой код компилируется даже для ATtiny без ОЗУ.

Еще очень важно при работе с переменными правильно указывать типы. А именно, использовать каждый тип строго по назначению. Неоптимальности легко возникают на конструкциях вида
Код:
int x;
unsigned char y;
uint8_t z = x + y;
особенно при сравнении чисел разного размера или беззнакового числа со знаковым. Последнее связано с особенностями сравнения дополнительного кода.
Gall вне форума   Ответить с цитированием Вверх
Старый 07.11.2010, 19:35   10
Gall
Старший научный сотрудник
 
Аватар для Gall
 
Регистрация: 21.06.2010
Последняя активность: 02.08.2015 00:26
Адрес: Екатеринбург
Сообщений: 205
Сказал(а) спасибо: 0
Поблагодарили 1 раз в 1 сообщении
По умолчанию

Реальный пример того, как работает оптимизация переменных. Исходный текст:
Код:
#include <avr/io.h>

int main()
{
    unsigned c[10000];
    unsigned y;
    c[15] = 0xFF;
    y = c[15];
    DDRA = y;
    PORTA = y;
    while (1) { }
    return 0;
}
Обратите внимание, здесь создается массив размером 20000 байт, который не влез бы в память ни одного из недорогих микроконтроллеров. Более того, здесь идет очень грязная работа с 16-битными знаковыми переменными. Компилируем в Ассемблер:
Код:
avr-gcc -mmcu=atmega16 -Os -S avr.c
и видим:
Код:
.file	"avr.c"
__SREG__ = 0x3f
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__CCP__  = 0x34
__tmp_reg__ = 0
__zero_reg__ = 1
	.text
.global	main
	.type	main, @function
main:
/* prologue: function */
/* frame size = 0 */
	ldi r24,lo8(-1)
	out 58-32,r24
	out 59-32,r24
.L2:
	rjmp .L2
	.size	main, .-main
что и требовалось доказать. Всего 4 команды, всего 1 регистр.
Gall вне форума   Ответить с цитированием Вверх
Старый 07.11.2010, 19:43   11
SviMik
Администратор
 
Аватар для SviMik
 
Регистрация: 26.02.2010
Последняя активность: 18.08.2015 19:47
Адрес: Tallinn, Estonia
Сообщений: 1101
Сказал(а) спасибо: 0
Поблагодарили 0 раз(а) в 0 сообщениях
По умолчанию

Цитата:
Ячейки оперативной памяти и LDS/STS используются тогда, когда либо не хватает регистров
А по какой логике он тогда определяет, какие следует уместить в регистрах, а какие уже пойдут в озу?

Цитата:
unsigned c[10000];
unsigned y;
Цитата:
с 16-битными знаковыми переменными
Хм, а где знаковые?
SviMik вне форума   Ответить с цитированием Вверх
Старый 08.11.2010, 21:42   12
Gall
Старший научный сотрудник
 
Аватар для Gall
 
Регистрация: 21.06.2010
Последняя активность: 02.08.2015 00:26
Адрес: Екатеринбург
Сообщений: 205
Сказал(а) спасибо: 0
Поблагодарили 1 раз в 1 сообщении
По умолчанию

Цитата:
Посмотреть сообщение Сообщение от SviMik :
А по какой логике он тогда определяет, какие следует уместить в регистрах, а какие уже пойдут в озу?
По частоте использования, по размеру и т.п. Он пробует разные варианты размещения и выбирает тот, в котором получается меньше лишних команд. Общее правило - короткоживущие переменные идут в регистры, долгоживущие - в память.

Цитата:
Посмотреть сообщение Сообщение от SviMik :
Хм, а где знаковые?
Моя ошибка - не тот код запостил. От замены unsigned на int, char и даже на long ассемблер не меняется.
Gall вне форума   Ответить с цитированием Вверх
Старый 08.11.2010, 22:08   13
SviMik
Администратор
 
Аватар для SviMik
 
Регистрация: 26.02.2010
Последняя активность: 18.08.2015 19:47
Адрес: Tallinn, Estonia
Сообщений: 1101
Сказал(а) спасибо: 0
Поблагодарили 0 раз(а) в 0 сообщениях
По умолчанию

Напиши какой-нидь пример с прерыванием
Если надо сгенерировать сигнал определённой частоты (или какое-то событие со строгим интервалом обрабатывать) - без прерываний по таймеру ну никак не обойтись.
SviMik вне форума   Ответить с цитированием Вверх
Старый 08.11.2010, 23:51   14
INFERION
Старший модератор
 
Аватар для INFERION
 
Регистрация: 15.02.2010
Адрес: Украина, Полтава
Сообщений: 1498
Сказал(а) спасибо: 0
Поблагодарили 2 раз(а) в 2 сообщениях
По умолчанию

+1. Я отказался от многих прерываний в индикаторе, но от компаратора таймера отказаться нельзя. Нужно в определённые моменты без тормозов тушить светодиоды, а в Си я пока не представляю как работать с прерываниями...
INFERION вне форума   Ответить с цитированием Вверх
Старый 10.11.2010, 19:34   15
SviMik
Администратор
 
Аватар для SviMik
 
Регистрация: 26.02.2010
Последняя активность: 18.08.2015 19:47
Адрес: Tallinn, Estonia
Сообщений: 1101
Сказал(а) спасибо: 0
Поблагодарили 0 раз(а) в 0 сообщениях
По умолчанию

Я так понимаю, в зависимости от компилятора, есть аж 3 варианта, для каждого надо писать по-своему

Для IAR
Код:
#pragma vector=name_vect
__interrupt void interruptName(void)
{
}
Для WinAvr
Код:
interrupt(VECTOR_NUMBER) interruptName(void)
{
}
Ещё вариант для WinAvr
Код:
#include <avr/io.h>
#include <avr/interrupt.h>

//обработчик прерывания таймера Т0
ISR(TIMER0_COMP_vect)
{
}
SviMik вне форума   Ответить с цитированием Вверх
Старый 10.11.2010, 22:01   16
Gall
Старший научный сотрудник
 
Аватар для Gall
 
Регистрация: 21.06.2010
Последняя активность: 02.08.2015 00:26
Адрес: Екатеринбург
Сообщений: 205
Сказал(а) спасибо: 0
Поблагодарили 1 раз в 1 сообщении
По умолчанию

Мы рассматриваем gcc (WinAVR) по очень простой причине - на других компиляторах не получается такая лихая оптимизация сишной простыни в три команды ассемблера. Вторая причина - все-таки сильно нарушать стандарты ANSI не стоит, а gcc им полностью соответствует.

Полная документация на работу с прерываниями:
http://avr-libc.nongnu.org/user-manu...nterrupts.html

Вкратце:
Код:
#include <avr/interrupt.h>

ISR(TIMER0_COMP_vect)
{
}
либо EMPTY_INTERRUPT, если нужен пустой обработчик прерывания. При генерации сигналов определенной частоты на прерываниях от таймеров очень удобно использовать именно пустые прерывания - вот так:
Код:
while(1)
{
    PORTA = 0xFF;
    asm volatile ("sleep");
    PORTA = 0x00;
    asm volatile ("sleep");
}
и тогда прерывание выполняет только одну функцию - прерывает sleep. Никакой код в обработчике не нужен.

Вообще же для повышения надежности и контролируемости программы не рекомендуется делать в прерываниях что-либо кроме установки флажков. Напоминаю, что переменные, к которым обращаются из прерываний, обязаны быть объявлены с модификатором volatile во избежание переоптимизации в неработоспособный код.
Код:
volatile uint8_t flag;
 ISR(TIMER0_COMP_vect)
{
    flag = 1;
}
Gall вне форума   Ответить с цитированием Вверх
Старый 10.11.2010, 22:25   17
SviMik
Администратор
 
Аватар для SviMik
 
Регистрация: 26.02.2010
Последняя активность: 18.08.2015 19:47
Адрес: Tallinn, Estonia
Сообщений: 1101
Сказал(а) спасибо: 0
Поблагодарили 0 раз(а) в 0 сообщениях
По умолчанию

Цитата:
и тогда прерывание выполняет только одну функцию - прерывает sleep
Ага, это справедливо, если использовать мк в чисто качестве генератора сигналов
А обычно он паралельно что-то ещё должен делать, а не только спать.

Цитата:
не рекомендуется делать в прерываниях что-либо кроме установки флажков
Опять же, как я это реализую, если помимо, допустим, пищания спикера, обрабатывается какой-то большой массив данных?
SviMik вне форума   Ответить с цитированием Вверх
Старый 10.11.2010, 23:58   18
Gall
Старший научный сотрудник
 
Аватар для Gall
 
Регистрация: 21.06.2010
Последняя активность: 02.08.2015 00:26
Адрес: Екатеринбург
Сообщений: 205
Сказал(а) спасибо: 0
Поблагодарили 1 раз в 1 сообщении
По умолчанию

Цитата:
Посмотреть сообщение Сообщение от SviMik :
Ага, это справедливо, если использовать мк в чисто качестве генератора сигналов
А обычно он паралельно что-то ещё должен делать, а не только спать.
Не проблема абсолютно. "Что-то-еще-делание" ставится непосредственно перед sleep, а вся генерация сигналов - после sleep. И будет как раз как надо.

Цитата:
Посмотреть сообщение Сообщение от SviMik :
Опять же, как я это реализую, если помимо, допустим, пищания спикера, обрабатывается какой-то большой массив данных?
У микроконтроллера нет такого объема памяти, чтобы он мог что-то обрабатывать долгое время, не общаясь с внешним миром. А внешний мир - это прерывания, ввод-вывод и sleep. Пример надуманный, на практике так не бывает.
Gall вне форума   Ответить с цитированием Вверх
Старый 11.11.2010, 00:27   19
SviMik
Администратор
 
Аватар для SviMik
 
Регистрация: 26.02.2010
Последняя активность: 18.08.2015 19:47
Адрес: Tallinn, Estonia
Сообщений: 1101
Сказал(а) спасибо: 0
Поблагодарили 0 раз(а) в 0 сообщениях
По умолчанию

Цитата:
Не проблема абсолютно. "Что-то-еще-делание" ставится непосредственно перед sleep, а вся генерация сигналов - после sleep. И будет как раз как надо.
Ты не понял. "Что-то-еще-делание" может затянуться дольше, чем период между прерываниями. Что тогда?
А если там будет общение с внешними девайсами? Оно тем более может затянуться.

Цитата:
У микроконтроллера нет такого объема памяти, чтобы он мог что-то обрабатывать долгое время, не общаясь с внешним миром. А внешний мир - это прерывания, ввод-вывод и sleep. Пример надуманный, на практике так не бывает.
Я твоей фразы не понимаю.

Хорошо, отойдём от абстракции.
Написал я недавно WAV-плеер на тиньке, прекрасно играющий с microsd карточки. На ассемблере
Теперь представь: на мк с тактовой частотой 8мгц, надо играть звук 48000гц. При этом, без джиттеров (т.е. периоды между семплами должны быть равны). Свободное процессорное время отдано на обработку других устройств ввода\вывода.
По расчётам, выходит, что у мк есть ~166 тактов на семпл. За это время надо прочитать 2-4 байта с карты памяти, при этом ещё и обработать протокол самой карты, а оставшееся время отдать на обработку устройств ввода. И всё это без джиттеров должно быть!

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

Только не говори, что взял бы для этой задачи другой мк или другую тактовую частоту
SviMik вне форума   Ответить с цитированием Вверх
Старый 11.11.2010, 03:51   20
INFERION
Старший модератор
 
Аватар для INFERION
 
Регистрация: 15.02.2010
Адрес: Украина, Полтава
Сообщений: 1498
Сказал(а) спасибо: 0
Поблагодарили 2 раз(а) в 2 сообщениях
По умолчанию

Цитата:
Посмотреть сообщение Сообщение от SviMik :
Теперь представь: на мк с тактовой частотой 8мгц, надо играть звук 48000гц. При этом, без джиттеров (т.е. периоды между семплами должны быть равны). Свободное процессорное время отдано на обработку других устройств ввода\вывода.
По расчётам, выходит, что у мк есть ~166 тактов на семпл. За это время надо прочитать 2-4 байта с карты памяти, при этом ещё и обработать протокол самой карты, а оставшееся время отдать на обработку устройств ввода. И всё это без джиттеров должно быть!

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

Тут то ладно, временные интервалы между прерываниями одинаковые. Разбросать "фоновый" код между ними не проблема. А что если прерывания могут возникать непоймикогда? Например программный ШИМ. И на которые тоже нужно реагировать незамедлительно, бросая все свои текущие дела. По-моему тут гемора с флагами гораздо больше, чем с отдельным обработчиком прерывания. Да и ты не уточнил, что WAV плеер тебе нужен не всегда. Тут возможно прерывание проще отключить, чем разбираться с ненужными слипами, и тем что за ними. Да и как быть с пробуждением от любого внешнего, или левого внутреннего прерывания? Нужно ещё и смотреть что именно тебя разбудило...

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

[Исправлено: INFERION, 11.11.2010 в 04:06].
INFERION вне форума   Ответить с цитированием Вверх
Пользователь сказал cпасибо:
tolikvoron (30.01.2016)
Ответ


Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 
Опции темы

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.



Текущее время: 06:40. Часовой пояс GMT +4.


Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2019, vBulletin Solutions, Inc. Перевод: zCarot
Template-Modifications by TMS

Copyright ©2010 - 2015, Hobbi.TV & FONAREVKA.RU.

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

При использовании материалов форума ссылка на Lasers.fonarevka.ru обязательна.

Украинский портАл CATALOG.METKA.RU
Каталог webplus.info Каталог сайтов Zabor.com