Главная » Микроконтроллеры
Призовой фонд
на октябрь 2020 г.
1. 1500 руб
Сайт Паяльник
2. Тестер компонентов MG328
Сайт Паяльник
3. 150 руб.
От пользователей


Модуль реле. Напряжение переключения 5В, ток 5мА

AVR на C - просто? Часть 3

3. Аппаратный ШИМ (PWM).

3.1. Таймеры/счетчики и регистры управления.

ШИМ (широтно импульсная модуляция) в микроконтроллере реализуется с использованием аппаратных таймеров. Теорию ШИМ рассматривать не будем. Рассмотрим лишь получение плавного изменения напряжения/тока на основе ШИМ. Для начала разберемся как управлять таймерами.

Имеется три таймера два по 8 бит (TC0, TC2) и один 16 бит (TC1). Настройка происходит регистрами TCCRxA, TCCRxB(x – означает выбранный таймер 0, 1 или 2).

Разберем чуточку подробнее на примере таймера TC1 в режиме 8 бит:

Регистр TCCR1A

бит

7

6

5

4

3

2

1

0

 

COM1A1

COM1A0

COM1B1

COM1B0

WGM11

WGM10

Бит 7, 6 (COM1A1, COM1A0)- настройка поведения вывода A по событию.

Бит 5, 4 (COM1B1, COM1B0)- настройка поведения вывода B по событию.

COM1A1/

COM1B1

COM1A0/

COM1B0

События

0

0

Нормальная работа порта, таймер отключен от вывода.

0

1

Переключение порта с 0 на 1 или обратно при совпадении.

1

0

Сброс на 0 при совпадении.

1

1

Установка 1 при совпадении.

Бит 3, 2 — зарезервированы.

Бит 1, 0 (WGM11, WGM10) — позволяют настроить ШИМ, подробно рассматривать не будем из-за объемности описания.

Регистр TCCR1B

бит

7

6

5

4

3

2

1

0

 

FOC1A

FOC1B

WGM12

CS12

CS11

CS10

Бит 7, 6 (FOC1A, FOC1B)- при использовании ШИМ должны быть установлены в 0.

Бит 5, 4 — зарезервированы.

Бит 3 — используется совместно с WGM11, WGM10 для настройки ШИМ.

Бит 2, 1, 0 (CS12, CS11, CS10) — выбор источника тактирования таймера.

CS12

CS11

CS10

Событие

0

0

0

Нет тактового сигнала (таймер остановлен)

0

0

1

CLK

0

1

0

CLK/8

0

1

1

CLK/64

1

0

0

CLK/256

1

0

1

CLK/1024

1

1

0

Внешний тактовый импульс по заднему фронту.

1

1

1

Внешний тактовый импульс по переднему фронту.

Регистр TCNT1

бит

7

6

5

4

3

2

1

0

 

Из регистра TCNT1 можно считать значение или записать начальное значение для таймера.

Регистры OCN1A и OCN1B

бит

7

6

5

4

3

2

1

0

 

Регистры OCN1A и OCN1B содержат значение сравнения связанные с соответствующими выводами микроконтроллера.

Есть еще два регистра TIMSK1 и TIFR1 – они предназначены для настройки маски прерываний и флагов прерываний.

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

Данный раздел не претендует на полноту описания работы с таймерами и ШИМ.

3.1. Программа с ШИМ

Для начала построим в Proteus схему и дальше применим ее для моделирования

ATmega328 PWM

Теперь программа.

#include <avr/io.h>            //Подключаем библиотеку ввода/вывода AVR
#include <util/delay.h>        //Подключаем библиотеку формирования задержки выполнения

int main(void)
{
	DDRB |= 1<<PB1;             //Устанавливаем как вывод регистр 1 порта B

	TCCR1A |= 1<<(COM1A1) | 1<<(WGM10); //В регистре TCCR1A устанавливаем биты COM1A1 и WGM10 в 1
	TCCR1B |= 1<<(CS10) | 1<<(WGM12);   //В регистре TCCR1B устанавливаем биты CS10 и WGM12 в 1

	int pwm = 0;                //Объявляем переменную для значения сравнения
	int up = 1;                 //Создаем флаг для увеличения или уменьшения скважности ШИМ

    while(1)
    {

		OCR1A = pwm;            //Заносим значение в регистр сравнения

		pwm += up ? 1 : -1;     //В зависимости от флага uo увеличиваем или уменьшаем значение сравнения pwm
		if (pwm == 255)         //Проверяем pwm на достижение 255 и переключаем флаг uo в 0
			up = 0;
		else if (pwm == 0)      //Проверяем pwm на достижение 0 и переключаем флаг uo в 1
			up = 1;

		_delay_ms(10);          //Задержка выполнения 10мс
	}

    return 0;
}

Программа подключает ШИМ к регистру 1 порта B и плавно увеличивает а затем уменьшает ток.

4. Обмен данными по SPI

Вот и добрались до обмена данными между микроконтроллером и внешним миром. Рассмотрим это на связке AVR >> SPI >> MAX7221 >> семисегментные индикаторы.

4.1. SPI

Работу с протоколом обмена SPI на ATmega328 начнем с рассмотрения регистров управления. Всего используется три регистра SPCR, SPSR и SPDR.

Регистр SPCR

бит

7

6

5

4

3

2

1

0

 

SPIE

SPE

DORD

MSTR

CPOL

CPHA

SPR1

SPR0

Бит 7 (SPIE)- разрешает прерывания.

Бит 6 (SPE)- включает аппаратный SPI.

Бит 5 (DORD) — определяет порядок передачи данных (0 — старший бит вперед, 1 — младший бит вперед).

Бит 4 (MSTR) — режим работы master/slave (1/0).

Бит 3 и 2 (CPOL, CPHA) — режим работы SPI.

Mode

CPOL

CPHA

SPI Mode 0

0

0

SPI Mode 1

0

1

SPI Mode 2

1

0

SPI Mode 3

1

1

Бит 1 и 0 (SPR1, SPR0) — частота тактирования SPI.

SPI2X

SPR1

SPR0

Частота тактирования

0

0

0

f/4

0

0

1

f/16

0

1

0

f/64

0

1

1

f/128

1

0

0

f/2

1

0

1

f/8

1

1

0

f/32

1

1

1

f/64

Регистр SPSR

бит

7

6

5

4

3

2

1

0

 

SPIF

WCOL

-

-

-

-

-

SPI2X

Бит 7 (SPIF)- флаг прерываний SPI.

Бит 6 (WCOL)- флаг ошибки записи.

Бит 5, 4, 3, 2, 1 — зарезервированы.

Бит 6 (SPI2X)- бит удвоения скорости записи.

Регистр SPDR

бит

7

6

5

4

3

2

1

0

 

MSB

-

-

-

-

-

-

LSB

Регистр SPDR предназначен для записи/чтения в сдвиговый регистр что приводит к активации передачи данных по SPI.

4.2. Использование SPI.

Рассмотрим передачу данных по SPI на примере вывода информации на семисегментные индикаторы. Для начала создадим схему в Proteus для эмуляции процесса.

ATmega328 SPI MAX7221

Программировать будем вывод данных на 5 семисегментных индикатора через микросхему MAX7221, которая принимает данные по 16 бит по протоколу SPI.

/* Использование семисегментного индикатора с общим катодом и сдвиговым регистром по протоколу SPI */

#include <avr/io.h>         //подключаем библиотеки
#include <util/delay.h>

#define SPI_DDR     DDRB    //назначаем имя для изменеия направления на порту B
#define SPI_PORT    PORTB   //назначаем имя для используемого порта
#define SPI_SS      PB2     //назначаем имя выхода SS
#define SPI_MOSI    PB3     //назначаем имя выхода MOSI
#define SPI_MISO    PB4     //назначаем имя выхода MISO
#define SPI_SCK     PB5     //назначаем имя выхода SCK

char d[18] ={               //кодирование знаков для семисегментного индикатора
    0x7E,  //0
    0x30,  //1
    0x6D,  //2
    0x79,  //3
    0x33,  //4
    0x5B,  //5
    0x5F,  //6
    0x70,  //7
    0x7F,  //8
    0x7B,  //9
    0x77,  //a
    0x1F,  //b
    0x4E,  //c
    0x3D,  //d
    0x4F,  //e
    0x47,  //f
    0x80,   //.
    0x00   //пусто
};

void spi(char cmd,char data)                //Функция передачи двух пакетов по 8 бит по протоколу SPI
{

        SPI_PORT &= ~(1<<SPI_SS);                       //сбрасываем SS в 0
        SPDR = cmd;                                     //отправляем данные по SPI адрес
        while(!(SPSR&(1<<SPIF)));                       //ждем окончания отправки
        SPDR = data;                                    //отправляем данные по SPI данные
        while(!(SPSR&(1<<SPIF)));                       //ждем окончания отправки
        SPI_PORT |= (1<<SPI_SS);                        //устанавливаем SS в 1

}

void clrdig ()          //Функция очистки индикаторов
{
    spi(0x01,d[17]);
    spi(0x02,d[17]);
    spi(0x03,d[17]);
    spi(0x04,d[17]);
    spi(0x05,d[17]);

}
int main()
{

   SPI_DDR = (1<<SPI_MOSI)|(1<<SPI_SCK)|(1<<SPI_SS);    //настраиваем MOSI, SCK, SS как выходы
   SPI_PORT |=(1<<SPI_SS);                              //устанавливаем SS в 1
   SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0);       //через регистр SPCR настраиваеи аппаратное SPI

   //Инициализация MAX7221

    spi(0x0C,0x00);     //Отключение индикаторов
    spi(0x09,0x00);     //Отключение декодирования
    spi(0x0A,0x0A);     //Интенсивность свечения индикаторов
    spi(0x0B,0x04);     //Количество индикаторов начиная с 0
    spi(0x0F,0x00);     //Отключение теста индикаторов
    spi(0x0C,0x01);     //Включение индикаторов

    clrdig();           //Очистка всех индикаторов

int i=0;                //Переменная перебора выводимых символов
int j=1;                //Переменная осчета номера индикатора

    while(1){           //Бесконечный цикл

        spi(j,d[i]);    //Вывод на индикатор j символа i

        _delay_ms(1000);    //Задержка выполнения 1000мс

        i++;
        if(i>17)i=0;
        j++;
        if(j>5)j=1;

    }

    return 0;
}

Перед использованием MAX7221 не забываем провести инициализацию.

Передача по SPI проходит по следующей схеме:

  1. Выставляем SS в 0;

  2. Заносим данные в регистр SPDR данные;

  3. Каждый бит по очереди передается по тактам SCK;

  4. Завершаем передачу установкой SS в 1.

Часть 4

Прикрепленные файлы:

Теги:

Опубликована: Изменена: 04.06.2017 0 0
Я собрал 0 0
x

Оценить статью

  • Техническая грамотность
  • Актуальность материала
  • Изложение материала
  • Полезность устройства
  • Повторяемость устройства
  • Орфография
0

Средний балл статьи: 0 Проголосовало: 0 чел.

Комментарии (2) | Я собрал (0) | Подписаться

0
Ваше имя #
Почему то ни в одном туториале по spi не упоминается одна очень важная вещь. Я по своей простоте хотел обойтись только выводом данных (MOSI PB3 ) и линией тактирования (SCK PB5),ну а зачем мне больше ?прижимал к земле устройство другим пином (я говорю про так называемый SS). И вот собственно в чем заковырка, настраиваю я значит порт DDR на выход только для MOSI и SCK, собираю код-не работает, танцую с бубном - не работает. Почему? Потому что какая особенность МК (может китайского) что если не назначить DDR SS(он же PB2) то spi не заводится. Это стоило мне очень много времени и нервов
Ответить
0

[Автор]
vavaav #
Я просто брал даташит на контроллер и там расписан порядок регистров для инициализации. Для AVR очень доходчиво в даташите расписано с примерами на си и ассемблере.
Ответить
Добавить комментарий
Имя:
E-mail:
не публикуется
Текст:
Защита от спама:
В чем измеряется электрическое сопротивление?
Файлы:
 
Для выбора нескольких файлов использйте CTRL

Программатор Pickit3
Программатор Pickit3
Осциллограф DSO138 200 Вт усилитель класса D на IRS2092
вверх