Задание 2. Примеры для ATtiny13

Задание 2. Примеры для ATtiny13

Сообщение ZuykovAV MEPhI » 06 ноя 2011, 14:19

========================
Акция - Собери программатор
========================

Пример 1 приводится в теме WinAVR. Там также есть пояснения по электрической схеме, для которой и последующие примеры.

Схема:

Изображение

Схема, собранная на макетке:

Изображение

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

Пример 2. Работа с разрядом PB0.

После подачи питания светодиод выключен.
Код: выделить все
//мк ATtiny13
//F_CPU = 9600000/8=1200000

#include <avr/io.h>  // Подключение системного файла для работы с регистрами

int main(void)  // Начало основной программы
{           
  // Инициализация порта ввода/вывода
  PORTB = 0b11111111;  // Для PB0 - 1
  DDRB  = 0b00000001;  // 0 - вход, 1 - выход, PB0 - выход
  while (1) {}         // Бесконечный цикл 
}


Задания для практической работы :

2-1. Нарисуйте в тетрадке схему, в которой четыре светодиода подключены к выводам PB0; PB2-PB4, аналогично светодиоду на VD1. Покажите схему преподавателю !
2-2. Соберите схему на макетке.
2-3. По программам :
- включите все четыре светодиода;
- выключите все светодиоды;
- два светодиода светятся, а два нет.
ZuykovAV MEPhI
 
Сообщений: 110
Зарегистрирован: 06 мар 2011, 15:52

Re: Задание 1. Примеры для ATtiny13

Сообщение ZuykovAV MEPhI » 06 ноя 2011, 14:49

Пример 3. PORTB |= 0b00000001; PORTB &= 0b11111110.

К выводу 6 (разряд PB1) подключена кнопка. При нажатии на кнопку включается светодиод.
Код: выделить все
//мк ATtiny13
//F_CPU = 9600000/8=1200000

#include <avr/io.h>

int main(void)
{           
  PORTB = 0b11111111; 
  DDRB  = 0b00000001; 
  while (1)
  {
    if (bit_is_set(PINB,PB1))
      PORTB |= 0b00000001;
    else
      PORTB &= 0b11111110;
  }
}


Пример 4. С использованием #define.

Код: выделить все
//мк ATtiny13
//F_CPU = 9600000/8=1200000

#include <avr/io.h>

#define VD_OFF   PORTB |= 0b00000001  // Выключение светодиода
#define VD_ON    PORTB &= 0b11111110  // Включение светодиода
             
int main(void)
{                     
  PORTB = 0b11111111; 
  DDRB  = 0b00000001; 
  while (1)
  { 
  if (bit_is_set(PINB,PB1))
    VD_OFF;
  else
    VD_ON;
  }
}

Пример 5. Вместо процедуры bit_is_set используется процедура bit_is_clear.

Код: выделить все
//мк ATtiny13
//F_CPU = 9600000/8=1200000

#include <avr/io.h>
             
#define VD_OFF   PORTB |= 0b00000001  // Выключение светодиода
#define VD_ON    PORTB &= 0b11111110  // Включение светодиода
           
int main(void)     
{
  PORTB = 0b11111111; 
  DDRB  = 0b00000001; 
  while (1)
  {
    if (bit_is_clear(PINB,PB1))
      VD_ON;
    else
      VD_OFF;
  }
}
ZuykovAV MEPhI
 
Сообщений: 110
Зарегистрирован: 06 мар 2011, 15:52

Re: Задание 1. Примеры для ATtiny13

Сообщение ZuykovAV MEPhI » 06 ноя 2011, 15:04

Пример 6. Системные задержки.

Включение/выключение (мигание) светодиода по системной задержке 1 секунда.
Код: выделить все
//мк ATtiny13
//F_CPU = 9600000/8=1200000

#include <avr/io.h>
#include <util/delay.h>  // Подключение системного файла для работы с задержками
                   
#define VD_OFF   PORTB |= 0b00000001
#define VD_ON    PORTB &= 0b11111110
#define DEL     _delay_ms(1000)  // Задержка 1 секунда (1000 миллисекунд)
                 
int main(void)               

  PORTB = 0b11111111; 
  DDRB  = 0b00000001; 
  while (1)
  {       
    VD_ON;
    DEL;   
    VD_OFF;
    DEL;   
  }
}

Пример 7. Используется инвертирование: PORTB ^= 0b00000001.

Код: выделить все
//мк ATtiny13
//F_CPU = 9600000/8=1200000

#include <avr/io.h>
#include <util/delay.h>  // Подключение системного файла для работы с задержками
                   
#define VD_OFF   PORTB |= 0b00000001
#define VD_ON    PORTB &= 0b11111110
#define DEL     _delay_ms(1000)  // Задержка 1 секунда (1000 миллисекунд)
                 
int main(void)               

  PORTB = 0b11111111; 
  DDRB  = 0b00000001; 
  while (1)
  {       
    PORTB ^= 0b00000001; // Инвертирование на выходе PB0
    DEL;     
  }
}

Вместо 0b00000001 можно написать 0x01.
ZuykovAV MEPhI
 
Сообщений: 110
Зарегистрирован: 06 мар 2011, 15:52

Re: Задание 1. Примеры для ATtiny13

Сообщение ZuykovAV MEPhI » 06 ноя 2011, 15:31

Пример 8. Прерывание INT0. При перепаде из 1 в 0.

Изменение состояния светодиода в момент нажатия на кнопку:
Код: выделить все
//мк ATtiny13 //a
//F_CPU = 9600000/8=1200000

#include <avr/io.h>
#include <avr/interrupt.h> // Подключение системного файла для работы с прерываниями

//SIGNAL(INT0_vect)
SIGNAL(SIG_INTERRUPT0)  // Обработчик прерываний
{           
  PORTB ^= 0x01;
}       

int main(void)                   
{                                     
  PORTB = 0b11111111; 
  DDRB  = 0b00000001; 

  GIMSK = 0b01000000; // Разрешение прерываний INT0 на входе PB1
  MCUCR = 0b00000010; // при перепаде из 1 в 0

  sei(); // Общее разрешение прерываний
  while (1) {}
}

При замыкании кнопки обратите внимание на включение/выключение светодиода.
Если часто нажимать, то иногда происходит лишнее переключение - это из-за "дребезга" контакта кнопки.

Пример 9. Первая попытка устранить "дребезг":
Код: выделить все
//мк ATtiny13 //a
//F_CPU = 9600000/8=1200000

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>   

//SIGNAL(INT0_vect)
SIGNAL(SIG_INTERRUPT0)
{
  _delay_ms(200);  // Задержка 200 миллисекунд
  if (bit_is_clear(PINB,PB1))  // Опрос PB1
    PORTB ^= 0x01;
}
int main(void)                   
{                                     
  PORTB = 0b11111111; 
  DDRB  = 0b00000001; 

  GIMSK = 0b01000000; // Разрешение прерываний INT0 на входе PB1
  MCUCR = 0b00000010; // при перепаде из 1 в 0

  sei();
  while (1) {}
}

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

Почему светодиод не переключается ?

Чтобы это исправить, нужно уменьшить время задержи: _delay_ms(200) замените на _delay_ms(10).

Внимание ! Вариантов устранения дребезга контактов много и этот несколько упрощённый.

Пример 10. Регистр GIFR (прерывание INT0 при перепаде из 1 в 0).

GIFR – общий регистр флагов внешних прерываний. Из восьми его разрядов используется только два. Бит D6 - для INT0, а бит D5 - для PCINTx.

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

Код примера 9 (но задержка 1 секунда):
Код: выделить все
//мк ATtiny13 //a
//F_CPU = 9600000/8=1200000

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>   

//SIGNAL(INT0_vect)
SIGNAL(SIG_INTERRUPT0)
{
  _delay_ms(1000);  // Задержка 1 секунда
  if (bit_is_clear(PINB,PB1))  // Опрос PB1
    PORTB ^= 0x01;
}
int main(void)                   
{                                     
  PORTB = 0b11111111; 
  DDRB  = 0b00000001; 

  GIMSK = 0b01000000; // Разрешение прерываний INT0 на входе PB1
  MCUCR = 0b00000010; // при перепаде из 1 в 0

  sei();
  while (1) {}
}

Здесь для замыкания вместо кнопки предлагается использовать отрезок провода (тем самым увеличивая влияние дребезга). Один его вывод подключите к выводу 6 микроконтроллера, а вторым выводом провода будете эмитировать нажатие кнопки, соединять с одним из контактов шины GND (земля) на макетке (кнопку из макетки можно не убирать).

Внимание ! При записи в контроллер второй вывод провода не должен быть отсоединён с землёй (то есть, кнопка не нажата).

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

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

Чтобы исключить не нужное (лишнее) переключение светодиода необходимо перед выходом из прерывания программно сбрасывать флаг прерывания записью 1 в соответствующий разряд регистра GIFR.

Задание : Получите устойчивое переключение светодиода при “нажатии” проводом, используя запись в регистр GIFR.

Пример 11. Прерывание INT0. При перепаде из 0 в 1.

Изменение состояния светодиода в момент отпускания кнопки:
Код: выделить все
//мк ATtiny13 //a
//F_CPU = 9600000/8=1200000

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

//SIGNAL(INT0_vect)
SIGNAL(SIG_INTERRUPT0)
{
  _delay_ms(10);  // Задержка 10 миллисекунд
  if (bit_is_set(PINB,PB1)) 
    PORTB ^= 0x01;
}

int main(void)                   
{                                     
  PORTB = 0b11111111; 
  DDRB  = 0b00000001; 

  GIMSK = 0b01000000; // Разрешение прерываний INT0 на входе PB1
  MCUCR = 0b00000011; // при перепаде из 0 в 1

  sei();
  while (1) {}
}

Задание : Получите устойчивое переключение светодиода при “нажатии” проводом, используя регистр GIFR.

Пример 12. Прерывание INT0 при любом перепаде (из 1 в 0 и из 0 в 1).

Простой пример управления светодиодом и при нажатии на кнопку и при её отпускании:
Код: выделить все
//мк ATtiny13 //a
//F_CPU = 9600000/8=1200000

#include <avr/io.h>
#include <avr/interrupt.h>

#define VD_OFF PORTB |=  0b00000001
#define VD_ON PORTB  &=  0b11111110 

//SIGNAL(INT0_vect)
SIGNAL(SIG_INTERRUPT0)

  if (bit_is_set(PINB,PB1))
    VD_OFF;
  else
    VD_ON; 
}       

int main(void)                   
{                                     
  PORTB = 0b11111111; 
  DDRB  = 0b00000001; 

  GIMSK = 0b01000000; // Разрешение прерываний INT0 на входе PB1
  MCUCR = 0b00000001; // при любом перепаде

  sei();
  while (1) {}
}

Задание : Придумайте пример по условию НИЗКИЙ уровень на выводе INT0.

Пример 13. Прерывание PCINTn (по изменению состояния на выводе).

Простой пример управления светодиодом по изменению состояния на выводе (и при нажатии на кнопку и при её отпускании):
Код: выделить все
//мк ATtiny13 //a
//F_CPU = 9600000/8=1200000

#include <avr/io.h>
#include <avr/interrupt.h>

#define VD_OFF PORTB |=  0b00000001
#define VD_ON PORTB  &=  0b11111110

//SIGNAL(PCINT0_vect)
SIGNAL(SIG_PIN_CHANGE0)

  if (bit_is_set(PINB,PB1))
    VD_OFF;
  else
    VD_ON; 
}       

int main(void)                   
{                                     
  PORTB = 0b11111111; 
  DDRB  = 0b00000001; 

  GIMSK = 0b00100000;  // Разрешение прерываний PCINTn
  PCMSK = 0b00000010;  // прерывание по входу PCINT1 (PB1)   

  sei();
  while (1) {}
}
ZuykovAV MEPhI
 
Сообщений: 110
Зарегистрирован: 06 мар 2011, 15:52

Re: Задание 1. Примеры для ATtiny13

Сообщение ZuykovAV MEPhI » 06 ноя 2011, 16:51

Пример 14. Работа с таймером. Прерывание по переполнению.

Использовать на практике примеры 6 и 7 (с использованием системной задержки) не всегда хорошо.
В этом примере светодиод также мигает, но задержку устанавливает таймер.
Код: выделить все
//мк ATtiny13 //a
//F_CPU = 9600000/8=1200000

#include <avr/io.h>
#include <avr/interrupt.h>

//ISR(TIM0_OVF_vect)
ISR(SIG_OVERFLOW0)
{
  PORTB ^= 0x01;
  TCNT0  = (256 - 234);
}
                       
int main(void)
{
  PORTB  =  0b00111111;
  DDRB   =  0b00000001;

  // Таймер 
  TCCR0A = 0;
  TCCR0B = 0b00000101; //делитель
  TCNT0  = (256 - 234);
  TIMSK0 = 0b00000010; // Разрешение прерываний по переполнению
       
  sei();
  while (1) {}
}

Частота тактов мк = 1200000 Гц
TCCR0B = 0b00000101 - делитель на 1024
1200000 / 1024 = 1172 Гц - частота тактов для таймера
1 / 1172 = 853 мкс - длительность одного такта для таймера
TCNT0 = 256 - 234
Регистр TCNT0 8-ми разрядный, следовательно его максимальное значение = 255, а 256 - это значение переполнения
234 (устанавливаемое значение) - это количество тактов до переполнения, которое набегает от начального состояния регистра TCNT0 до его переполнения
234 такта * 853 мкс = 199602 мкс = 0.2 с
То есть через каждые 0.2 с будет вызываться прерывание и каждый раз будет изменяться состояние выхода PB0
Длительность включённого состояния 0.2 с
Длительность выключенного состояния 0.2 с
Период 0.2 + 0.2 = 0.4 с
1 / 0.4 = 2.5 Гц - частота мигания светодиода (заметная глазу)
ZuykovAV MEPhI
 
Сообщений: 110
Зарегистрирован: 06 мар 2011, 15:52

Re: Задание 1. Примеры для ATtiny13

Сообщение ZuykovAV MEPhI » 06 ноя 2011, 21:08

Пример 15. Работа с таймером. ШИМ.

Управление состоянием вывода 5 (PB0-OCR0A) - изменение яркости светодиода.
Код: выделить все
//мк ATtiny13
//F_CPU = 9600000/8=1200000

#include <avr/io.h>

int main(void)               
{                 
  PORTB  =  0b00111111;
  DDRB   =  0b00000001;
   
  // Таймер   
  TCCR0A = 0b11000001;  // управление выходом, ШИМ
  TCCR0B = 0b00000011;  // делитель N = 64 

  OCR0A = 21;   // Значение регистра определяет яркость светодиода
   
  while (1) {}
}

- После записи должно налюдаться слабое свечение, при этом, заметна пульсация светодиода
- Если установить OCR0A = 33; то яркость прибавится, но пульсация останется
- Если установить TCCR0B = 0x05, то пульсация станет сильно заметна
- Если установить TCCR0B = 0x02, то пульсации не будет

Разряды регистров TCCR0A, TCCR0B:
- задают режим работы таймера (здесь Phase correct PWM для ШИМ)
- подключают таймер к выводу 5 мк и задают его тип управления
- выбирают источник тактового сигнала (здесь f_cpu) и коэффициент деления предделителя (здесь N=64, 1024 или 8)

Частота сигнала на выводе 5 определяется по формуле f = f_cpu / (N * 512)
f_cpu = 1200000 Гц
TCCR0B = 0x03 (N = 64), 1200000 / (64 * 512) = 36.6 Гц
TCCR0B = 0x05 (N = 1024), 1200000 / (1024 * 512) = 2.3 Гц
TCCR0B = 0x02 (N = 8), 1200000 / (8 * 512) = 293 Гц

Объясните, почему заметна или не заметна пульсация?

Пример 16. Работа с таймером. ШИМ.

Ступенчатое изменение яркости светодиода при нажатии на кнопку.
Код: выделить все
//мк ATtiny13
//F_CPU = 9600000/8=1200000
//при выполнении примера частота меняется с 1200000 на 9600000

#include <avr/io.h>

int main(void)
{
  PORTB  =  0b00111111;
  DDRB   =  0b00000001;
  // Таймер
  TCCR0A = 0b11000001;  // управление выходом, ШИМ
  TCCR0B = 0b00000100;  // делитель N = 256 
  OCR0A = 33;   
  while (1) {}
}

1. После записи должно налюдаться слабое свечение. При этом также заметна пульсация светодиода.
TCCR0B = 0x04 (N = 256), 1200000 / (256 * 512) = 9.15 Гц

2. Внимание! Измените частоту тактирования мк на 9600000 Гц. Для этого следует выключить делитель (у fuse CKDIV8 снять галочку).
После изменения должно налюдаться слабое свечение. При этом пульсация светодиода будет не заметна.
9600000 / (256 * 512) = 73.2 Гц

3. Подбором определите 8 уровней яркости для светодиода (разные значения регистра OCR0A).

4. Задание. Напишите программу - при последовательном нажатии на кнопку должно происходить ступенчатое изменение яркости светодиода: от выключенного состояния до максимальной яркости и обратно.
ZuykovAV MEPhI
 
Сообщений: 110
Зарегистрирован: 06 мар 2011, 15:52

Re: Задание 1. Примеры для ATtiny13

Сообщение ZuykovAV MEPhI » 06 ноя 2011, 21:30

Пример 17. Работа с таймером, режим СТС.

На выходе (PB0) необходимо получить меандр с частотой около 1 Гц.
Код: выделить все
//мк ATtiny13
//F_CPU = 9600000/1=9600000
//при выполнении примера частота F_CPU меняется

#include <avr/io.h> 

int main(void)
{
  PORTB  =  0b00111111;
  DDRB   =  0b00000001;
  // Таймер
  TCCR0A = 0b01000010;  // управление выходом, режим CTC
  TCCR0B = 0b00000101;  // делитель
  OCR0A = 255;
 
  while (1) {}
}

После предыдущего примера частота тактов мк должна быть 9600000 Гц
Разряды регистров TCCR0A, TCCR0B:
- задают режим работы таймера (здесь СТС)
- подключают таймер к выводу 5 мк и задают его тип управления
- выбирают источник тактового сигнала (здесь f_cpu) и коэффициент деления предделителя (здесь N = 1024)

Частота на выводе 5 определяется по формуле f = f_cpu / (2 * N * (1+OCROA))
9600000 / (2 * 1024 * (1+255)) = 18.3 Гц

Задание: Используя изменение фьюзов, получите частоту генерации на выводе около 1 секунды.

Пример 18. Работа с таймером. Режим СТС.

При нажатии на кнопку (PB1) на выходе (PB0) необходимо получить меандр с частотой 36 КГц.
Код: выделить все
//мк ATtiny13
//F_CPU = 4800000/8=600000
//при выполнении примера частота F_CPU меняется

#include <avr/io.h> 

#define TIM0A_ON  TCCR0A |= 0b01000000 // Вкл. выхода таймера
#define TIM0A_OFF TCCR0A &= 0b00111111 // Ввыкл. выхода таймера   

int main(void)
{
  PORTB  =  0b00111111;
  DDRB   =  0b00000001;
  // Таймер
  TCCR0A = 0b00000010;  // режим CTC 
  TCCR0B = 0b00000101;  // делитель
  OCR0A = 255;
 
  while (1)
  {
    if (bit_is_clear(PINB,PB1))
      TIM0A_ON;
    else
      TIM0A_OFF; 
  }
}

После выполнения предыдущего задания частота тактов мк f_cpu = 4800000 / 8,
и при нажатии на кнопку светодиод мигает с частотой около 1 Гц.

Задание: Установите на выводе 5 мк меандр с частотой 36 КГц. При выполнении этого задания следует правильно установить значения регистров таймера TCCR0B, OCR0A и фьюзов.
ZuykovAV MEPhI
 
Сообщений: 110
Зарегистрирован: 06 мар 2011, 15:52

Re: Задание 2. Примеры для ATtiny13

Сообщение VVZ » 11 июн 2013, 00:03

Пример 18. АЦП и ШИМ таймера.

Входной сигнал в диапазоне напряжений GND - VCC подаётся на вывод 3 (ADC2) микроконтроллера. Далее, через АЦП микроконтроллера выводится на ШИМ и с выхода таймера OC0A определяет яркость светодиода VD1.

Код: выделить все
//мк ATtiny13 //a
//F_CPU = 9600000/1=9600000

#include <avr/io.h>
#include <avr/interrupt.h>     

//SIGNAL(ADC_vect)
SIGNAL(SIG_ADC)

   OCR0A = ADCH;
   PORTB ^=0b00001000; //для задания, пункт 3
}

int main(void)               
{                   
   PORTB = 0b00001111;  // PB4 - вход ADC2
   DDRB  = 0b00001001;  // PB0 - выход таймера OC0A
   
   ADMUX  = 0b00100010;
   ADCSRA = 0b11101111;
   DIDR0  = 0b00010000; 
   TCCR0A = 0b11000001;
   TCCR0B = 0b00000011;
     
   sei();
   while (1) {}           
}

Задание :
1. По справочнику разберите назначения всех разрядов регистров ADMUX; ADCSRA; DIDR0; TCCR0A; TCCR0B.
2. Проверьте работу ШИМ при всех значениях разрядов CS02; CS01; CS00. Объясните причину мигания светодиода на выходе OC0A при максимальном значении делителя.
3. Проверьте работу АЦП при всех значениях разрядов ADPS2; ADPS1; ADPS0. Для фиксации времени измерения АЦП используйте осциллограф и разряд PB3 (см. текст программы). Найдите отношения времени измерения АЦП при работе с делителями 128/64; 64/32; 32/16; 16/8; 8/4 и 4/2. Объясните существенное отличие между отношением делителей 4/2 от остальных.
VVZ
 
Сообщений: 409
Зарегистрирован: 28 апр 2011, 09:44


Вернуться в Микроконтроллеры - Задания и примеры

Кто сейчас на форуме

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1