Проэкт под Attiny, "как-бы так начать програмировать"
#1

Моя попытка обьяснить что куда и как на первых порах.
Поскольку сам этим занимаюсь от случая к случаю то я очнь много чего могу не знать или не уметь, поэтому претензии, в случае чего, не принимаются :)
Это не единственая среда програмирования которую можно использовать, но как-то я всегда использовать AVR Studio. Некоторое время назад они обьеденили нескольо своих "студий", называется он теперь Atmal Studio, каччается от сюда http://www.atmel.com/tools/ATMELSTUDIO.aspx
Я так и не понял есть ли у него GCC компилятор на борту (помоему есть патаму что скомпилировалось :) ), но на всякий случай ссылка - http://sourceforge.net/projects/winavr/files/


Во вложении проэкт под ATTiny44 для защиты УМа. Там вобщем-то есть достаточно много чего. Инициализируются порты, таймеры, ADC, прерывания.
Я все эти "инициализации" раскидываю обычно по отдельным файл с с соотв названием (ADC, IOs, timers etc), но делать именно так не обязательно. Можно вобще хоть все написать в одном файле. Делаю я так сугубо для своего удобства, что бы знать где и что искать.

Вот вариант програматора который евгений нашел на ибее http://www.ebay.com/itm/MRP01-USB-AVR-IS...4897.l5661
Судя по описанию атмел студия должна его знать, так что работать с ним должно быть удобно.

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

N.B. Я понимаю что читать чужой недокументированый код это жуть полная, но постараюсь отвечать на вопросы.
Информация по регистрам, пинам контроллера и т.д. ка кобычно в DS - http://www.atmel.com/Images/doc8006.pdf


Тут много всего по AVRам - http://www.avrfreaks.net/
Тут люди задают вопросы по всевозможным языкам, думаю что С не обошли стороной - http://stackoverflow.com/


Файлы вложений
.zip amp_prot_studio_6.zip Размер: 30.21 KB  Загрузок: 7
.pdf protection_1.pdf Размер: 266.68 KB  Загрузок: 18

"Найкраще сало то ковбаса." (с)
The following 2 users say Thank You to БендеровецЪ for this post:
  • EDWARD (09-03-2014), flipper (03-14-2017)
Ответ
#2

Если начинать с задачи "зажечь светодиод", то наверное стоит начать с конфигурации портов. В данном МК есть два порта, А и В. У каждого порта есть несколько восьмибитныз конфигурационных регистров. Для начала нужно два из них, один который задает направление (вход или выход) DDRx (DDRA для порта А и DDRB для порта В) и второй PORTx (тоже А и В).
Для того что бы сконфигурировать порт как выход в соотв бит DDRx регистра надо записать единицу (нулевой бит порта DDRA соотв пину PA0, первый - PA1 и т.д.)

С одной стороны в порт можно сразу записать число, написав DDRA = 0xff (что соотв всем единицам), ну или DDRA = 0x01 (нулевой бит в единицу), но так делать нерационально, хоть и кажется что так проще всего. Это сделает код нечитаемым. Для этого я сначала присваиваю понятные мне псевдонимы битам, например вот так:

#define REL PA0
#define DC PA1
#define LED0 PA5
#define LED1 PA4
#define FAULT PA7
#define ADC_IN3 PA3
#define ADC_IN2 PA2
#define RESET PB3

В библиотеке io.h, которую надо предварительно подключить написав #include <avr\io.h> (деректива компилятора) названиям PA0, PA1 и т.д. соотв номера пинов в порту, например PA0 на самом деле имеет значение 0 (нулевая позиция).

Написав #define REL PA0 я присваиваю REL значение которое было ранее присвоено PA0, т.е. 0, и теперь компилятор, встретив в коде REL будет подставлять это присвоеное значение.

Теперь подумав какие порты будут выходами, можно "собрать" число которое будет записано в регистр DDRA, приблизительно вот так:
#define DDRA_RESET_VALUE ( (1 << REL) + (1 << LED0) + (1 << LED1) + (0 << ADC_IN3) + (0 << ADC_IN2) + (0 << FAULT) + (0 << DC))

Т.е. там где я хочу выход, я ставлю 1, если не хочу - 0 (можно опустить поскольку это ничего не меняет)
<< это сдвиг влево, т.е. я беру единицу (в двоичной записи 00000001) и сдвигаю влево на позицию которая соотв нужному мне биту. например (1 << LED0) дает 0001000, поскольку LED0 я присвоил то что было присвоено PA4 ранее, т.е. 4.

Далее число DDRA_RESET_VALUE записывается в регистр DDRA
DDRA = DDRA_RESET_VALUE; (Достаточно ожидаемо)

Далее если мы хотим установить пины LED0 и REL в единицу нам надо записать единицу в соотв бит регистра PORTA, например вот так:
PORTA |= (1 << REL) + (1 << LED0);

запись типа a |= b; эквивалентна a = a | b;

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

#include <avr\io.h>

#define REL PA0
#define DC PA1
#define LED0 PA5
#define LED1 PA4
#define FAULT PA7
#define ADC_IN3 PA3
#define ADC_IN2 PA2
#define RESET PB3

#define DDRA_RESET_VALUE ( (1 << REL) + (1 << LED0) + (1 << LED1) + (0 << ADC_IN3) + (0 << ADC_IN2) + (0 << FAULT) + (0 << DC))

int main(void)
{
DDRA = DDRA_RESET_VALUE;
PORTA |= (1 << REL) + (1 << LED0);

while(1)
{
}
}

Если ничего не заыбл то должно работать

N.B.

while(1)
{}

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

"Найкраще сало то ковбаса." (с)
The following 2 users say Thank You to БендеровецЪ for this post:
  • begemot (07-11-2014), flipper (03-14-2017)
Ответ
#3

Самый просто способ "поморгать" пинами выглядит как-то так:

#define REL PA0
#define LED0 PA5
#define LED1 PA4

#define DDRA_RESET_VALUE ( (1 << REL) + (1 << LED0) + (1 << LED1) )
#include <avr/io.h>

unsigned int counter = 0;

int main(void)
{
DDRA = DDRA_RESET_VALUE;
PORTA |= (1 << REL) + (1 << LED0);
while(1)
{
counter++;
if (!counter)
{
PINA |= (1 << REL) + (1 << LED0);
}
}
}

Что было сделано
Была создана беззнаковая переменная типа интеджер, 16 бит, максимально значение которой равно 65535
Програма "крутится" в главном цикле, и каждый оборот добавляет к переменной единицу.
Запись counter++; эквивалентна counter = counter + 1;
потом в if происходит проврека условия когда counter == 0
! является логическим отрицанием, которое вернет единицу только в одном случае когда counter равен нулю. Можно было записать иначе

if (counter == 0)
{
}

Главное помнить что сравнение это ==, а = это присвоение значения. В данном случае надо использовать ==

Далее, когда мы попадаем внутрь if мы устанавливаем две еденицы в регистре PINA
Если обратится в DS то там написано что такая операция изменяет значение соотв пина на противоположное, что и дает нам мигание. Впринципе это можно было сделать и несколькими другими способами, но так получается достатоно компактно и без лишних операций.

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

Ну а вобще надо делать это с помощью таймера, завтра попробую набросать.

"Найкраще сало то ковбаса." (с)
The following 2 users say Thank You to БендеровецЪ for this post:
  • EDWARD (09-03-2014), flipper (03-14-2017)
Ответ
#4

Вариант с таймером. Аналогичным же образом "осваиваем" конфигурационные регистры таймера "номер" 0

#define TCCR0A_RESET_VALUE ( (0 << COM0A1) + (0 << COM0A0) + (0 << COM0B1) + (0 << COM0B0) + (0 << WGM01) + (0 << WGM00)) //Timer mode
#define TCCR0B_RESET_VALUE ( (0 << WGM02) + (0 << CS02) + (0 << CS01) + (1 << CS00)) //Normal mode, prescaler 1
#define TIMSK0_RESET_VALUE ( (0 << OCIE0B) + (0 << OCIE0A) + (0 << TOIE0))

Впринципе TCCR0A и TIMSK0 останутся в исходном значении, так что их можно и не прописывать, но хорошо иметь заготовку на будущее. В CCR0B запишем единичку что соответвует отсутствию деления частоты клока перед таймером.
В этом режиме таймер будет считать ввех по кругу, и каждый раз при переполнении будет выставлятся флаг переполнения в регистре TIFR0. Для чтения этого бита зделаем заготовку:
#define TIMER0_OF() TIFR0 & 0x01
для зброса (поскольку делаем пока без прирывание, то флаг надо сбрасывать вручную записью туда еденицы):
#define TIMER0_OF_RESET() TIFR0 = 0x01

В цикл програмы добавим if который будет постоянно проверять флаг переполнения счетчика (а так же сбрасывать флаг и выполнять нужное действие):
if (TIMER0_OF())
{
TIMER0_OF_RESET();
PINA |= (1 << REL) + (1 << LED0);
}
Такой способ работы с таймером (да и со всем остальным) называется полингом, т.е. постоянным опросом.
Итого код выглядит так:

#include <avr/io.h>

#define COUNTER_RESET_VALUE 0x02
#define REL PA0
#define DC PA1
#define LED0 PA5
#define LED1 PA4
#define FAULT PA7
#define ADC_IN3 PA3
#define ADC_IN2 PA2
#define RESET PB3

#define TCCR0A_RESET_VALUE ( (0 << COM0A1) + (0 << COM0A0) + (0 << COM0B1) + (0 << COM0B0) + (0 << WGM01) + (0 << WGM00)) //Timer mode
#define TCCR0B_RESET_VALUE ( (0 << WGM02) + (0 << CS02) + (0 << CS01) + (1 << CS00)) //Normal mode, prescaler 1
#define TIMSK0_RESET_VALUE ( (0 << OCIE0B) + (0 << OCIE0A) + (0 << TOIE0))

#define TIMER0_OF() TIFR0 & 0x01
#define TIMER0_OF_RESET() TIFR0 = 0x01
#define TIMER0_MATCH() TIFR0 & 0x02
#define TIMER0_MATCH_RESET() TIFR0 = 0x02

#define ENABLE_TIMER0_INT() TIMSK0 |= (1 << TOIE0)
#define DISABLE_TIMER0_INT() TIMSK0 &= ~(1 << TOIE0)

#define DDRA_RESET_VALUE ( (1 << REL) + (1 << LED0) + (1 << LED1) + (0 << ADC_IN3) + (0 << ADC_IN2) + (0 << FAULT) + (0 << DC))

int main(void)
{
DDRA = DDRA_RESET_VALUE;
PORTA |= (1 << REL) + (1 << LED0);

TCCR0A = TCCR0A_RESET_VALUE;
TCCR0B = TCCR0B_RESET_VALUE;
TIMSK0 = TIMSK0_RESET_VALUE;

while(1)
{
if (TIMER0_OF())
{
TIMER0_OF_RESET();
PINA |= (1 << REL) + (1 << LED0);
}
}
}

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

"Найкраще сало то ковбаса." (с)
The following 2 users say Thank You to БендеровецЪ for this post:
  • EDWARD (09-03-2014), flipper (03-14-2017)
Ответ
#5

Вобще для удобства пора это безобразие с #define выносить в отдельный файл. Пока не сильно много "всего" хватит одного .h файла.
Создаем файл, например helper.h в дериктории с основным .c фалом, добавляем его в проэкт.
Переносим в .h файл все наши дефайны, плюс еще пара инструкций, что-бы придержат компилятор от черызмерных усилий:
#ifndef HELPER_H
#define HELPER_H

#include <avr\io.h>

#define COUNTER_RESET_VALUE 0x02
#define REL PA0
#define DC PA1
#define LED0 PA5
#define LED1 PA4
#define FAULT PA7
#define ADC_IN3 PA3
#define ADC_IN2 PA2
#define RESET PB3

#define TCCR0A_RESET_VALUE ( (0 << COM0A1) + (0 << COM0A0) + (0 << COM0B1) + (0 << COM0B0) + (0 << WGM01) + (0 << WGM00)) //Timer mode
#define TCCR0B_RESET_VALUE ( (0 << WGM02) + (0 << CS02) + (0 << CS01) + (1 << CS00)) //Normal mode, prescaler 1
#define TIMSK0_RESET_VALUE ( (0 << OCIE0B) + (0 << OCIE0A) + (0 << TOIE0))

#define TIMER0_OF() TIFR0 & 0x01
#define TIMER0_OF_RESET() TIFR0 = 0x01
#define TIMER0_MATCH() TIFR0 & 0x02
#define TIMER0_MATCH_RESET() TIFR0 = 0x02

#define ENABLE_TIMER0_INT() TIMSK0 |= (1 << TOIE0)
#define DISABLE_TIMER0_INT() TIMSK0 &= ~(1 << TOIE0)

#define DDRA_RESET_VALUE ( (1 << REL) + (1 << LED0) + (1 << LED1) + (0 << ADC_IN3) + (0 << ADC_IN2) + (0 << FAULT) + (0 << DC))

#endif

А в основном фале замещаем все скопирование строкой
#include "helper.h"

Готово

"Найкраще сало то ковбаса." (с)
The following 1 user says Thank You to БендеровецЪ for this post:
  • flipper (03-14-2017)
Ответ
#6

Ну тут сначала наверно надо сказать для чего сопсно нужны эти #define
Мы просто называем те штуки с которыми мы будем работать, ну всякие регистры например, удобоваримыми именами.
Нам потом надо будет проверять какой-то порт или виводить 1 или 0 на эту ножку. Мы её обзовём так чтоб было понятней
Нам самим понятней, а не дяде Васе.

Nobody Is Perfect
Ответ
#7

Ну наверное самое время попытатся считывать какой-нить пин.
Для этих дел у AVR микроконтроллеров есть регистры под названием PINx (PINA, PINB etc). Впринципе читать этот регистр можно в независимости от того сконфигурирован пин как вход или выход, но мы ж хотим таки вход.
Вобщем все те пины, для которых мы записали нули в DDRA регистр отсаются входами. Напомню:
#define DDRA_RESET_VALUE ( (1 << REL) + (1 << LED0) + (1 << LED1) + (0 << ADC_IN3) + (0 << ADC_IN2) + (0 << FAULT) + (0 << DC))
...
DDRA = DDRA_RESET_VALUE;

Далее, для каждого пина сконфигурированого как вход можно включить "слабую подтяжку", записав единице в соотв бит порта PORTA. Например возьмем пин под псевдонимом FAULT (он будет нашим входом на сегодня):
PORTA |= 1<<FAULT //так мы устанавливаем единственный бит в восьмибитно регистре.

Чтение из регистра PINA входа FAULT будет выглядить приблизитено вот так:
Что-то там = PINA & (1 << FAULT) // так мы наложили маску, и нам будет виден только интересующий нас бит

Для удобства делаем заготовку в .h фале:
#define READ_PIN_FAULT PINA & (1 << FAULT)

Читать пин можно по разному, "по полингу", по таймеру, по прирыванию.
В данном случае будет довольно просто вложить это в нашу обработку таймера таким образом:
int main(void)
{
DDRA = DDRA_RESET_VALUE;
PORTA |= (1 << REL) + (1 << LED0) + (1 << FAULT);

TCCR0A = TCCR0A_RESET_VALUE;
TCCR0B = TCCR0B_RESET_VALUE;
TIMSK0 = TIMSK0_RESET_VALUE;

while(1)
{
if (TIMER0_OF())
{
TIMER0_OF_RESET();
if (READ_PIN_FAULT)
PINA |= (1 << REL) + (1 << LED0);
}
}
}

"Найкраще сало то ковбаса." (с)
The following 1 user says Thank You to БендеровецЪ for this post:
  • flipper (03-14-2017)
Ответ
#8

Вот проэкт под Attiny13 с приемником RC-5 и програмным SPI. ВРоде бы все работает, включая распознавание toggle bit в протоколе. Остается подставить коды клавиш для конкретного пульта и отправлять по SPI осмысленые значения. В покое МК уходит в слип и потребляет порядка 300мкА.
В проэкте остались неиспользуемый заготовки для ADC - вдруг кому захочется добавить.
Клок надо сконфигурировать в 9.6МГц делить на 8 (помоему это идет по умолчанию).


Файлы вложений
.zip small_rc.zip Размер: 39.1 KB  Загрузок: 8

"Найкраще сало то ковбаса." (с)
The following 3 users say Thank You to БендеровецЪ for this post:
  • EDWARD (11-03-2014), begemot (11-12-2014), flipper (03-14-2017)
Ответ
#9

Покупал как-то вот такое :)
http://www.ebay.com/itm/Digispark-ATTINY...Swc1FXY8Db
Удобно делать какое-то мелкое управление. Программируется сразу по USB.
Вообщем удобная мелочь за смешные деньги, может кому пригодится.
Ответ
#10

Лучше так, а то затеряется на наебэй

Digispark ATTINY85 General Micro USB Development Board For Arduino


Файлы вложений Эскизы(ов)
   
The following 1 user says Thank You to EDWARD for this post:
  • Black_Jack (04-09-2017)
Ответ
#11

Ну и код бы зарелизили под эту блоху для наглядности.
Ответ
#12

Код, пардоньте, чего?
Arduino полно всего.
Сама платка - это Digispark.
Вся информация вот тут.
http://digistump.com/products/1

Ну вот, например, светодиодом по-ШИМ-ить

Код:
// Declare variables
int P1_GREEN = 1; // PIN #1 For Green LED
int cnt = 0;
unsigned long Pause = 4;
int green_value;
float x;
float green;

// The setup routine runs once after power-up/reset
void setup()
{
  // initialize the digital pin as an output.
  pinMode(P1_GREEN, OUTPUT); //Internal Digispark LED

  // Delay for 10 sec
  delay(Pause*1000);
  
}

// The loop routine runs over and over again forever:
void loop()
{
  while (cnt < 20)
  {
    for (int i = 0; i < 360; i++)
    {
      // Convert into a float to calculate green (increase amplitute by multiplying by 127 and make positive by adding 1)
      x = float(i);
      green = 127 * (sin(x / 180 * PI) + 1);
    
      // Convert float 'green' to integer 'green_value'
      green_value = int(green);

      // Write LED levels to P1_GREEN (Assign PWM values to Green LED)
      analogWrite (P1_GREEN, green_value);

      // Wait for 3 ms
      delay(3);  
    }
    cnt++;
  }
  digitalWrite (P1_GREEN, HIGH);
    
}
Ответ


Перейти к форуму:


Пользователи, просматривающие эту тему: 1 Гость(ей)