Открытый проект универсального зарядника

R2D2

Урок №5 (Языки программирования):

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

Теперь рассмотрим это утверждение с разных сторон:

  1. Программы существуют с доисторических времен. Программы зашиты на генном уровне во всех животных и в человеке.

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

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

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

Те люди которые говорят, что они свободны и поэтому имеют право сделать плохой выбор (украсть для себя, выпить стакан водки, нахамить, взять деньги в ущерб совести, власть ради безопасности и денег для своих, гордыня и впадание в прелесть) эти люди незаметно с каждым таким выбором изменяют внутреннее отношение к миру (мне позволено…) не потому что их Бог накажет, а потому что они изменили свое сознание, оно постепенно чернеет. и одновременно становятся зависимыми от удовольствий и грехов как от наркотика. Это подлинное рабство от удовольствий, рабство неправильного выбора, которое ведет к деградации, обессиливает, жестко привязывает человека к судьбе (которая определяется вещами и желаниями). Если я купил машину значит мне написано на судьбе ездить на заправку, проходить ТО, менять резину, стоять в пробках, портить воздух, переживать из-за царапин, думать о парковке, думать чтоб не украли. Купив машину, мой мозг и мое тело обеспечины занятостью и переживаниями на значительное время жизни. Ну вот меня опять понесло.

  1. Итак процессор это машина - железяка он строго выполняет программу не ошибаясь никогда, поэтому если проц работает не так, значит это ваша ошибка.

  2. Каждый процессор разговаривает только на своем языке (языке машинных кодов), поэтому программу надо дать процессору исключительно в машинных кодах и исключительно в тех машинных кодах которые он понимает. Процессоры INTEL8086 (IBM XT), Z80 (ZX SPECTRUM), MOTOROLA 6502 (APPLE или АГАТ) каждый понимают только свои машинные коды, потому что схемы процессоров разные.

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

Вот как выглядит ассемблер (не пытайтесь понять и не ищите смысла)

MOVE REGISTER, #100
ADD REGISTER, 3
IN A
OUT B,20
JMP #1000
JZ #200

Писать програму на ассемблере это всеравно что объяснять человеку какие мышцы (а их ~400 штук) надо напряч а какие расслабить для совершения прыжка.

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

  2. Я считаю, что оптимальное программирвание это знание ассемблера в общих чертах и работа на Си.

  3. Все остальные языки также разнятся как и машины, ктото более удобный, ктото более быстрый, ктото может только копать, ктото ездеет только задом. Язык Си может все. Для языка Си есть компиляторы (преобразователи) программы на Си в язык машинных кодов.

msv

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

Это уже имхо перебор… Не стоит определять свои ценности, как истинные - для других… Если, конечно, у Вас не цель создать ложу зарядникостроителей… Уж извините, но я “кормлю” семью, езжу на машине, занимаюсь кучей “хоббей” и между делом запускаю RC-самолетики и пишу программульки… Что делать- выпить 100г или сесть за комп решать мне… И в этом моя Зсвобода… Какой выбор и приоритеты “правильные”- философия явно не для этой темы…
ЗЫ Но раз даже меня, (хитрож-го прагматика) это задело, может для обывателя все и правильно иззложено.

R2D2

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

Урок №6 (Структура программы на Си)
Когда-то давно, когда компьютеры еще не были столь сильны, компиляторы (преобразователи) программы Си в язык машинных кодов работали очень медленно, а также с целью легкости обнаружения ошибок, на текст программы Си было наложено ограничение. Все программы на Си должны иметь приблизительно такой вид:

------------------------------------------------------
Команды препроцессора - команды для автоматического редактирования текста программы

Описание переменных, видных во всех подпрограммах
Подпрограмма1
{
Описание переменных, видных только внутри подпрограммы
Тело подпрограммы
}

ПодпрограммаN
{
Описание переменных, видных только внутри подпрограммы
Тело подпрограммы
}

Главная программа (запускается при включении или перезапуске ЗУ)
{
Описание переменных, видных только внутри подпрограммы
Тело подпрограммы
}
---------------------------------------------------------------

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

Итак теперь мы можем написать программу зарядника Вот она (содержимое файла main.cpp):

#include <avr/io.h>_______// Подключаем файл с подпрограммами и переменными от avr
#include “util.h”_________// Подключаем файл со всеми описаниями переменных
#include “util.cpp”_______// Подключаем файл со всеми подпрограммами
int main(void)____________// Главная программа
{
#include “init.cpp”_______// Инициализация всех ног процессора
#include “zagruzka.cpp”___// Загрузка начальных переменных из ПЗУ
while(true)_______________// Бесконечно повторяем {содержимое скобок}
__{
__wdt_reset();____________// Сброс собаки (авторесета) на случай зависания
__#include “sh.cpp”_______// Прорисовка шаблонов - всех изображений ЖКИ
__#include “test.cpp”_____// Рассчитываем все переменные по каналам
__if(iK1!=iK0)____________// Если есть необработанные кнопки рисуем меню
____{
____#include “menu.cpp”___// Обработка кнопок
____}
__TestMainParam();________// Если какой нибудь канал работает или тестирование проверяем не горит ли чего
__if(Ch1.C)Go(Ch1);_______// Если запущен канал 1 обрабатываем этот канал
__if(Ch2.C)Go(Ch2);_______// Если запущен канал 2 обрабатываем этот канал
__#include “uart_in.cpp”__// Если надо чтонибудь получить с COM-порта
__#include “uart_out.cpp”_// Если надо чтонибудь послать на COM-порт
__}
}

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

#include <avr/io.h> - подключает к моему файлу необходимые мне описания и функции из комплекта поставки winavr. Это очень удобно, т.к. мне не надо писать свои соответствующие подпрограммы. Угловые скобки говорят о том что файл надо искать в том месте куда установился winavr в папке avr файл io.h

Фактически “#include” и все что начинается на “#” это команды препроцессора, т.е. перед (пре) компиляцией моей программы текст моей программы изменяется, в нее добавляются содержимое других файлов. Это удобно. Еслибы я тупо сам добавил в свой файл все эти тексты, то файл разросся бы и было бы неудобно все это листать и понимать, а так это всего лишь одна строка и мне все ясно. Эта команда является реализацией принципа - “Разбей сложное на много простых частей”.

#include “util.h”
#include “util.cpp” - это другие файлы с описателями и функциями. Кавычки указывают компилятору, что файлы надо искать в тойже папке где главный файл main.cpp

Расширение “cpp” говорит о том, что внутри файла лежат подпрограммы на языке Си. Расширение “h” говорит, что там лежат описания переменных и функций. В принципе компилятору все равно и он это не отслеживает, но это нужно людям.

Есть еще одна широко используемая команда препроцессора:
#define aaa bbb
Эта команда перед компиляцией обыскивает весь мой файл и меняет aaa на bbb.

Все команды препроцессора включая #include и #define на прямую никак не отражаются в конечной программе на машинных кодах, они просто меняют текст программы перед компиляцией, а после все возвращают не место.

Сами слова #include и #define это жестко зафиксированные слова в Си и никак иначе их использовать нельзя.
Есть и много других слов которые жестко зафиксированы.

продолжение следует…

R2D2

Продолжение.

После компиляции нашей Си программы получается файл main.hex это файл с машинными кодами. Как там все устроено нас пока не волнует. Важно только то, что у нас есть главная программа main(), котороя запустится сразу после включения ЗУ или после нажатия на кнопку RESET, если у вас такая есть.

До сего момента я все время путался с названиями: программа, подпрограмма, функция. По большому счету это одно и тоже. Попробуем сформулировать определение на простом языке. Функция это подпрограмма. Внутрь функции можно передавать параметры. После окончания функция может возвращать один параметр. Приведем несколько примеров:

Пример1:
wdt_reset(); - это вызов функции сброса сторожевой собаки. Внутри процессора АТМЕГА32 есть специальный счетчик - собака. В зависимости от настроек приблизительно раз в 2 секунды он перезапускает процессор. Если этот счетчик не сбрасывать, то процессор будет каждые 2 секунды перезапускаться и вы так и не сможете заряжать аккумы. Этот механизм предусмотрен разработчиками АТМЕЛА для предотвращения зависания машинных программ. Всякое бывает, может компилятор глючит, может сама программа с ошибками, может железо процессора глючит например от перегрева или радиации, но программы иногда зависают. В этот момент могут происходить непредвиденные события, ЗУ может само себя спалить. Если программа зависла, то она не проходит основной цикл и не сбрасывает собаку, и процессор перезапускается и все процессы в ЗУ останавливаются. Конечно при таких делах надо искать ошибку и исправлять. И все же сторожевая собака должна быть.

Возвращаемся к функциям. Данная конкретная функция не получает ни каких параметров (поэтому внутри скобок ничего нет) и не возвращает ничего, поэтому результат работы функции не присваивается никакой переменной.

Чтобы иметь право вызвать на выполнение эту функцию надо чтобы ее текст был где-то выше вызова описан. Эта функция была написана внутри библиотек функций компилятора winavr, т.е. досталась нам готовенькой, и, чтобы мы могли ее вызвать мы должны описать компилятору что это за функция. Для этого надо в начале нашего файла подключить файл описателей включающий в себя описание функций собаки #include <avr/wdt.h>.

Пример 2:
x=sin(5);
sin - это функция, которая получает параметр 5 и возвращает результат в переменную x.

Пример 3:
int main(void)
{
}

main - это функция которая не имеет параметров об этом говорит слово void или пусто внутри скобок. Main возвращает как результат целое число об этом говорит слово int. Функция main вызывается один раз прямо там где написано ее тело, поэтому больше нигде ее не вызывают.
Каждая функция, использованная в нашей программе, либо написана нами лично и помещена где-то выше или ниже вызова или заимствована из библиотеки стандартных функций. Если тело функции ниже вызова или в библиотеке, то выше вызова надо описать функцию.

Если вы в тексте программы увидели слово из английских букв и цифр (первая обязательно буква или “_”) и сразу после него круглые скобки, знайте это функция.

R2D2

Продолжение:

Процессор - исполнитель программ работает как простоватый рабочий действующий по своим записям. Выполняет все по порядку слева направо и сверху вниз. Читает одну команду и выполняет, читает и выполняет. Это относится к нашему процессору АТМЕГА32. Этот процессор не имеет никаких параллельных потоков выполнения. Существует одна точка текущего выполнения - один поток выполнения.

Только кой какие счетчики, COM-порт и АЦП работают сами по себе - аппаратно.

Вспоминаем, что АТМЕГА32 это маленький компьютер в одной микросхеме (в одном чипе). Внутри него есть сам процессор-исполнитель, ОЗУ, ПЗУ, порты ввода вывода и все это перевязано шинами данных, адреса и управления.

Вот как работает процессор (на этот механизм мы повлиять не можем он строго зашит и является личностью и характером процессора):

  1. Юзер включил питание.
  2. Процессор ожил, все засчитало, но он спит и видит сон: беспорядочно метаются биты.
  3. Процессор увидел, что питание достигло уровня 95% от стандартного.
  4. Процессор пропустил тактовый генератор на схему исполнения команд, заодно инициализировал несколько системных регистров.
  5. Процессор начал выполнять команды, вытаскивая их по одной по порядку начиная с ячейки памяти с адресом ноль (или как у него там принято)
  6. Одна команда один такт. Один такт - 1/16 000 000 секунды.
  7. Один такт:
    __а) Проц выставляет адрес на шину адреса (для адресации 64кБ необходимо 16 проводов в шине, т.е. 16ти битная)
    __б) Проц выставляет управляющую команду на шину управления (шина управления 3-6 проводов: чтение/запись, запрос ОЗУ, запрос ПЗУ, запрос FLASH, запрос портов ввода вывода, прерывания). Команда например: Память дай данные с адреса который выставлен на шине адреса.
    __в) Память дала данные на шину данных (8 проводов, т.е. 8ми битная) и дернула за шину управления “данные готовы”.
    __г) Проц взял данные. В данных лежала команда для исполнения. К этой команде возможно нужны еще несколько байтов (по 8бит считок из памяти). Если так то проц их дочитал.
    __д) Проц выполнил команду (это могла быть команда сложения или записи или перехода это тоже целая серия работ с шинами данных, управления и адреса)
    __е) Проц рассчитал адрес следующей команды и попросил ее из памяти потом выполнил.

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

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

Чтобы сыграть мелодию на динамике, надо на катушку оттягивающую мембрану подавать величину оттяга от 0-256 (1 байт) с частотой 44 000 раз в секунду.

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

У кота тоже есть мозги, он рассчитывает как ему прыгнуть и что кушать а что нет. Мозги не единственное что нас отличает от животных. У нас есть душа, сердце - особая непонятная высшая деятельность.

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

R2D2

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

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

Хотя создатели Си очень хорошо постарались для нас, полностью полагаться на них мы не будем. Вот если бы мы программировали на intel i7 920, то конечно, все изыски Си в нашем распоряжении, но т.к. мы программируем для АТМЕГА32 нам приходится искать золотую середину, что взять от Си, а что сделать самим. Мы слишком скованы ограниченной памятью и быстродействием. Мы возьмем от Си только самое необходимое, потому что это все нести нам в своем рюкзаке на самую высокую гору.

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

Компилятор оптимизирует код по быстродействию или по размеру. Не все версии компиляторов работают одинаково. Придется выбирать нужный проводя сравнения производительности.

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

А если юзер отцепит аккум во время зарядки?!
А если юзер коротнет зарядку?!
А если юзер забудет зарядку включенной и уедет в отпуск?!
А если аккум перегреется и сожгет квартиру?!

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

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

Для освоения языка Си необходимо узнать мнемонику языка (характерные слова и символы для записи программы).

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

Материалист не найдет смысла в этом бреде. Смысл специально спрятан от понимания материалистами. Следующий уровень подразумевает отсутствие материи и выгоды. И всеже каждый может понять.

Итак переходы и циклы, а между ними функции вот и все программирование.

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

Итак переходы и циклы:
----------------------------------------------------------------------------------
if(условие)
__{
__внутренность этих скобок выполняется однократно если условие выполнилось
__}
else
__{
__внутренность этих скобок выполняется однократно если условие НЕ выполнилось
__}
далее программа выполняет программу здесь
----------------------------------------------------------------------------------
while(условие)
__{
__внутренность этих скобок выполняется до тех пор пока справедливо условие
__}
далее программа выполняет программу здесь
----------------------------------------------------------------------------------
do{
__внутренность этих скобок выполняется 1 раз и до тех пор пока справедливо условие
__}while(условие);
далее программа выполняет программу здесь
----------------------------------------------------------------------------------
for(i=0; i<100; i=i+1)
__{
__внутренность этих скобок выполнится 100 раз
__первый раз i=0
__второй раз i=1
__последний раз i=99
__}
далее программа выполняет программу здесь
----------------------------------------------------------------------------------

ЭТО ВСЕ!!!
Я считаю, что я Вам Си и смысл всех языков программирования объяснил. Далее просто детали. Далее мы просто будем расширять список функций и углубляться в детали написания.

P.S. Т.к движок сайта жрет пробелы мне пришлось натыкать везде подчерков “_”. Не обращайте на них внимание. Подчерки нужны мне чтобы сохранить рисунок программы, так легче отслеживать начало и конец цикла и условия.

mega_john
R2D2:

P.S. Т.к движок сайта жрет пробелы мне пришлось натыкать везде подчерков “_”.

а вы используйте тэг CODE, он вроде как для этого создан, и ничего не жрет

msv

Выражение i=i+1 для сишника выглядит непривычно… 😃

R2D2

Спасибо за подсказки.
i++ решил пока не пугать.

R2D2

Урок №7 (Переменные)

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

Где-то выше мы отмечали, что всему в мире можно присвоить код и этот код можно хранить в памяти компьютера в виде напряжений в ячейках памяти, которые устроены как конденсатор (заряжен/незаряжен) или как триггер из 6ти транзисторов (который соединяет выход либо с землей либо с +5в питания) или это домен (маленький магнит) в магнитном слое на винчестере/дискетке/ленте или это дырка в перфокарте как это было на заре цивилизации.

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

Всего 2 состояния, поэтому и двоичная система исчисления. В двоичной системе всего две цифры 0 и 1. В двоичной системе нет цифры 2, но есть число 2 и оно записывается так: 10.

Просмакуем ситуацию с десятичной системой исчисления.
В десятичной системе исчисления 10 цифр: 0,1,2,3,4,5,6,7,8,9 и тоже нету цифры 10, но есть число 10 и записывается оно: 10.

В шестнадцатиричной системе исчисления 16 цифр: 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f и тоже нет цифры 16, но есть число 16 и записывается оно: 10

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

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

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

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

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

8 битов информации могут хранить в себе число от 00000000 до 11111111 или в десятичной от 0 до 255 или в шестнадцатиричной от 00 до ff.

Процессор АТМЕГА32 это 8 битный процессор, значит все вышесказанное относится к нам.

С данными разобрались, теперь адрес. Если данные это содержимое ящичка, то адрес это номер ящичка по порядку. И то и другое есть число. И то и другое записывается в 2,10,16 тиричном исчислении. Необходимо четко понимать в компьютере есть только два вида чисел: данные и адрес. Все остальные слова, придуманные международным сообществом (ссылка, метка, идентификатор, дата …) - это все либо данные либо адрес.

Когда я себе представляю мысленно память компьютера, я вижу следующее:

Адрес Данные
0000 15
0001 2f
0002 ff
0003 67

fff9 12
fffa 32
fffb 00
fffc ff
fffd ff
fffe aa
ffff 45

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

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

Вот мы и будем выкручиваться как хотим, но в соответствии с международными стандартами.

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

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

ТИПЫ ДАННЫХ:

-------------------------------------------------------------------------------------------
“BYTE” - тип данных один байт - маленькое целое беззнаковое число от 0 до 255.
“unsigned char” - это тоже самое по смыслу выражение и также будет понято компилятором.

ВНИМАНИЕ! Язык Си, если не указано в настройках, различает большие и маленькие буквы. Т.е. написание букв большии или маленькими важно и отслеживается.

Пример использования в Си:
BYTE x=7, y=0x45, z=b00010010;

В этой строке мы сказали компилятору выделить гденибудь где удобно в памяти 3 ячейки. В одной ячейке будет храниться переменная “x” в другой “y” в третей “z”. Компилятору абсолютно все равно как вы назовете переменные, для него важно, что везде где встретится x в программе он будет вспоминать о том месте где он выделил память и будет оттуда брать саму переменную или записывать ее туда. Название переменной не попадет в текст машинного кода, значит длинна названия переменной никак не повлияет на экономию памяти машинного кода.

R2D2

Переменные могут называться только латинскими буквами и цифрами из 32 знаков причем первая обязательно должна быть буква.

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

При описании переменной мы поставили знак “=”, этим мы сказали компилятору вписать в программу на машинных кодах небольшой код по инициализации переменных. Это значит, что после того как ЗУ включится, первым делом программа на машинных кодах впишет в ячейку с адресом переменной (которую мы называли x) число 7.

В переменную “y” мы попросили записать 0x45 (таким образом мы сказали компилятору, что 45 это 16тиричное число).
В переменную “z” мы попросили записать b00010010 - двоичное число. Мне именно необходимо двоичное число, чтобы видеть на какой ноге будет +5в. Каждый знак в двоичной записи соответствует ноге АТМЕГА32 - это очень удобно (это только если реч идет о порте ввода вывода A,B,C,D).

Разные компиляторы по разному требуют запись двоичного числа. Тот компилятор который я использовал глючил в этом вопросе, поэтому для своего удобства я поправил ситуацию самодельщиной - добавил в программу команду препроцессора “#define b00010010 0x12”, т.е. перед компиляцией заменить все b00010010 на 0x12.

BYTE будем использовать для маленьких целых для организации циклов, для работы непосредственно с памятью, для хранения всяческих маленьких настроек ЗУ.
-------------------------------------------------------------------------------------------------
“int” - это 2 байта. В памяти хранится сначала младший байт потом старший байт. Когда мы говорим об адресе в памяти для переменной int, то говорим об адресе младшего байта, т.е. первого из двух.

Два байта может вместить в себя беззнаковое число от b0000000000000000 до b1111111111111111 или от 0 до 65535 или от 0x0000 до 0xffff. Думаю уже понятно что есть что.
А если число знаковое, то вот что придумали программеры.

R2D2

они как бы перекинули половину диапазона в отрицательную часть числа. Получилось, что знаковый int может принимать значения от -32768 до 32767 это ровно такое же количество чисел как и было. Никто не пользуется отрицательными двоичными и шестнадцатиричными числами это может привести к путанице. 2ичные и 16тиричные используются в контексте реальных адресов и данных и непонятно что такое отрицательный адрес. Но заметим одну важную особенность: у отрицательного числа самый старший бит 1. Больше на эту тему распространяться пока не буду. Хотите поточнее узнать - играйтесь с калькулятором.

int - это знаковое целое (-32768 до 32767)
unsigned int - это беззнаковое целое (0-65535)

unsigned int писать очень долго поэтому сделаем #define WORD unsigned int и будем везде писать WORD

Этот тип переменных мы будем использовать для счетчиков циклов, для средних целых, для ШИМ счетчиков, для контрольных сумм, для АЦП суммирования.
--------------------------------------------------------------------------------------------------------

Заметьте уже второй тип переменных строго ограничен в своих значениях. С одной стороны мы точно знаем что потратили только 2 байта при использовании переменной, а мы по жмотски будем экономить каждый байт, но с другой стороны мы получили головную боль по переполнению переменных и жесткому отслеживанию рамок. Например если к WORD(65535) добавить 1 то получится 0. А если от WORD(0) отнять 5, то получится 65531 и компилятор вам не поможет в этом вопросе, т.к. вычисления производятся уже после того как прога с машинными кодами уже во всю заряжает и разряжает.

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

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

R2D2

Все что сказано про int (integer - целое число без дробной части) справедливо и для других двух типов: short (короткий 1 байт) и long (длинный 4 байта).

unsigned short это тоже самое что и BYTE, за одним маленьким исключением: некоторые стандартные функции требуют именно short, а не BYTE.

long - это ооочень много - это можно считать сотые доли секунды в течение всего дня и long не переполнится.
------------------------------------------------------------------------------------
Ну и наконец float это число с плавающей точкой занимает 4 байта. Как хранится в памяти мне не известно да и не надо знать.
Все точные вычисления до 4 знака после запятой будем делать в переменных типа float (токи, напряжения, сопротивления, балансировочные коэффициенты, интегралы-суммы, диференциалы-наклоны). Это самые медленные операции. Одна графическая карта ATI или nVIDEA может пережевать милионы операций с плавающей точкой, а АТМЕГА32 не более 100 операций в секунду и это сильно тормозит все остальные операции ЗУ.

Поэтому перед тем как программировать надо ооочень хорошо подумать как сократить количество вычислений как упростить все формулы, а что можно вычислить заранее и ввести в формулу в качестве коэффициента.
------------------------------------------------------------------------------------
Последний тип который нам понадобится - bool от слова boolean тип логической переменной, которая может принимать два значения true (правда) и false (лож), т.е. это и есть один бит информации. Хранить один бит информации в памяти где все хранится по 8 бит это у компилятора вызывает трудности: либо наплевать на экономию и хранить бит как байт (0-лож, >0-правда) либо заморачиваться с запоминанием в каком байте какой бит соответствует нашей переменной.
Мы то точно будем заморачиваться на эту тему, т.к. мы жадные, а bool переменных (флагов) у нас много. Но об этом потом.
------------------------------------------------------------------------------------

В заключение скажем, что меньшая банка помещается в большую легко. Например long=int компилятор спокойно преобразовывает. А вот в обратную сторону могут происходить чудеса. Нужно быть внимательными.

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

roadster

Скажите пожалуйста, подойдет ли для этого проекта ATmega32A-AU?

R2D2

28.07.2008
Выпущены новые микросхемы ATmega16A и ATmega32A - на замену микросхемам ATmega16 и ATmega32. В новых ATmega16A и ATmega32A переработана топология металлизированных слоев кристалла таким образом, чтобы уменьшить энергопотребление микросхемы, но при этом сохранить полную совместимость с популярными ATmega16 и ATmega32. Новые микросхемы имеют меньший ток потребления как в активном режиме работы микросхем, так и в режимах энергосбережения.
В отличии от ATmega16 и ATmega32 новые микросхемы не будут иметь модификаций с суффиксом “L” - новые ATmega16A и ATmega32A будут работать в диапазоне напряжений питания от 2.7В до 5.5В, обеспечивая максимальную производительность 16МГц при напряжении питания 4,5…5,5В и 8МГц - при напряжении питания 2.7В.
Ниже для сравнения приведены значения энергопотребления микросхем ATmega32 и ATmega32A

Ниже для сравнения приведены значения энергопотребления микросхем ATmega16 и ATmega16A

Да подходит.

R2D2

Забыл сказать важную вещь про вычисления с плавающей точкой: необходимо таким образом составлять формулу, чтобы компилятор на каждом этапе вычислений находился в среднем диапазоне чисел это повысит точность вычислений. На каждом этапе вычислений точность падает до велечины округления. Т.к. мы используем float (4 байта на хранение и вычисление переменной), а не double (8 байт на хранение и вычисление переменной), точность с каждым вычислением будет уплывать и уплывать, особенно если мы будем выскакивать в +бесконечность или будем приближаться к нулю.

Поэтому правило: стараться чтобы каждое вычисление внутри формулы приводило к результату от 0.001 до 1000 (по модулю) и хранилось в этом же диапазоне и только в самом конце (перед отображением) приводилось к истинному порядку величины. Такие простые правила позволят не потерять точность на стадии вычислений, хотя бы точность уже была потеряна на стадии помех в схеме ЗУ.

Урок №8 (операторы)
До сих пор я много говорил про команды процессора (управляющие ноги процессора, мостов, памяти, устройств ввода/вывода), говорил про команды машинных кодов, говорил про функции (подпрограммы и программы).

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

Как видите мы утопаем в количестве информации про которую надо говорить и употребление слова команда приводит в замешательство: в каком смысле оно используется, поэтому при описании языка Си, имея ввиду команды, мы будем использовать слово ОПЕРАТОР.

Оператор - это то, что должен исполнить процессор при выполнении программы.
Оператор это как бы логическая единица выполнения.
Оператор это одно выражение, заканчивающееся “;”.
Оператор может быть сложным, состоящим из множества функций.
Оператор может состоять из одной функции. Функция и оператор это почти одно и тоже.
Программа Си - это последовательность операторов.

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

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

Например:

x=47*y; // Здесь оператор “=”
goto qqq; // Оператор перехода goto
for(int i=0; i<45; i++) // Оператор цикла for
{
break; // Оператор выхода из цикла
if(x<y)e=4; // Оператор сравнения if

}

Русинов_Сергей
R2D2:

if(x<y)e=4; // Оператор сравнения if

Всегда вернёт true. 😉 Должно быть “==”.
Воизбежание подобных радостей надо ставить константу вперёд.
if 4=(x<y)e компилятор не пропустит. 😉

R2D2

Это я просто бессмыслицу писал чтоб про операторы сказать.

Урок №10 (Первая настоящая программа на Си для АТМЕГА32)

#include <avr/io.h>                 // Общая муть
#include <avr/iom32.h>              // Мега 32
#include <avr/wdt.h>                // Стандартные описатели собаки
#include <util/delay.h>             // Стандартные описатели задержек
#include "bit.h"                    // Обращение к битам портов по названию

//------------------------------------------------------------------------------
// Главная программа запускается по ресету
int main(void)
{
// Это магические слова, чтобы ЗУ себя не спалило
cli();          // Запрет всех прерываний
WDTCR=15;       // Инициализация сторожевого таймера
WDTCR=15;       // Инициализация сторожевого таймера
PORTA=0;        // В порту A все нули 0000 0000
PORTB=0x1c;     // В порту B все нули 0001 1100 кроме PB2 PB3 PB4 (клавиатура подтянута на +)
PORTC=0x80;     // В порту C все нули 1000 0000 кроме PС7 (клавиатура подтянута на +)
PORTD=0x02;     // В порту D все нули 0000 0010 кроме PD1 (подтяжка TXD на +)
DDRA=0;         // Задаем направление работы ног порта A 0000 0000
DDRB=0x43;      // Задаем направление работы ног порта B 0100 0011
DDRC=0x7f;      // Задаем направление работы ног порта C 0111 1111
DDRD=0xfe;      // Задаем направление работы ног порта D 1111 1110

// Этот цикл выполняется бесконечно. То что нам надо!
while(true)
  {
  wdt_reset();                   // Сброс собаки
  _delay_us(10000);              // Задержка 0.01 секунды
  PORTD_Bit7=1;                  // Подать напряжение на динамик
  _delay_us(10000);              // Задержка 0.01 секунды
  PORTD_Bit7=0;                  // Убрать напряжение с динамика
  }
// Хотя сюда программа никогда не доберется, мы обязаны закрыть все скобки и соблюсти все правила хорошего тона.
}
// Конец главной программы
//------------------------------------------------------------------------------

После запуска этой программы мы услышим тарахтение динамика.

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

Самое интересное в этой программе, то из-за чего все было затеяно, это внутренности оператора while
АТМЕГА32 будет бесконечно выполнять все операторы внутри цикла, т.к. условие while(true) всегда истинно, значит дойдя до нижней скобки цикла “}” и проверив условие (true), программа начнет цикл заново от верхней скобки “{”.
Внутри цикла выполняются последовательно оператор wdt_reset(); - сброс собаки, _delay_us(10000); ничего не делать (выполнять пустой код, растрачивая время) в течение 10000 микросекунд, т.е. 0.01 секунды, потом установить на 16 ноге проца 5в PORTD_Bit7=1;, потом опять подождать и сбросить в 0в

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

Русинов_Сергей
R2D2:

WDTCR=15; // Инициализация сторожевого таймера
WDTCR=15; // Инициализация сторожевого таймера

К логопеду пора… 😃

R2D2

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

И про
if(x<y)e=4;
здесь все верно написано. Это не Си-шный изворот это простая конструкция

if(x<y)
  {
  e=4;
  }