Пример №1. Управление яркостью светодиода с помощью ШИМ
Пояснения к схеме
Программа
//************************************************************
//
// Учебный курс. Программирование микроконтроллеров AVR на Си
// Управление нагрузкой с помощью
// широтно-импульсной модуляции (ШИМ, PWM)
//
//************************************************************
#include
#include
int main(void)
{
//инициализация портов
PORTB = 0;
DDRB = 0xff;
//инициализация таймера Т0
TIMSK = 0;
//реж. - fast pwm, вывод OC0 - неинверт. шим, clk/64
TCCR0 = (1<<WGM01)|(1<<WGM00)|(1<<COM01)|(0<<COM00)|(0<<CS02)|(1<<CS01)|(1<<CS00);
TCNT0 = 0;
OCR0 = 0;
//ион - напряжение питания, выравнивание влево, нулевой канал
ADMUX = (0<<REFS1)|(1<<REFS0)|(1<<ADLAR)|(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0);
//вкл. ацп, реж. непрерывн. преобр., разр. прерывания, частота преобр. = FCPU/128
ADCSRA = (1<<ADEN)|(1<<ADSC)|(1<<ADATE)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
//режим непрерывного преобразования
SFIOR = 0;
__enable_interrupt();
while(1);
return 0;
}
//********************************
//прерывание АЦП
#pragma vector=ADC_vect
__interrupt void adc_my(void)
{
Пояснения к коду
Логика работы
Инициализация периферии - таймера Т0, модуля АЦП. Разрешение прерываний. Бесконечный цикл. В прерывании АЦП напряжение считанное с переменного резистора записывается в регистр сравнения OCR0. В микроконтроллере AVR для этого регистра организован буфер , и на самом деле запись происходит сначала в него. Из буфера же значение переписывается в OCR0, только когда происходит переполнение счетного регистра TCNT0. Параллельно циклу while и модулю АЦП, работает таймер Т0 и генерит на выводе OC0(PB2) ШИМ сигнал. Когда счетный регистр TCNT0 переполняется OC0 устанавливается в 1, когда значение счетного регистра совпадает с регистром сравнения OCR0, вывод устанавливается в 0.
Инициализация таймера
Пример №2. Генерация синусоидального сигнала с помощью ШИМ
Пояснение к схемe
Резистор R2 просто забыл выкинуть из схемы. R3 и C8 интегратор.
Программа
#include
#include
#include "TableSin.h"
int main(void)
{
//инициализация портов
PORTB = 0;
DDRB = 0xff;
TIMSK = (1<<OCIE0);
//реж. - fast pwm, вывод OC0 - неинверт. шим, предделитель - на 8
TCCR0 = (1<<WGM01)|(1<<WGM00)|(1<<COM01)|(0<<COM00)|(0<<CS02)|(1<<CS01)|(0<<CS00);
//обнуляем счетный регистр и регистр сравнения
TCNT0 = 0;
OCR0 = 0;
__enable_interrupt();
while(1);
return 0;
}
Пояснения к коду
Таблица значений синуса
TableSin.h - хедер, в котором прописан константный массив, содержащий 32 значения синуса.
Инициализация таймера Т0
В программе используется прерывание таймера Т0 по событию совпадение.Для разрешения прерывания устанавливается бит OCIE0 регистра TIMSK. Остальная часть инициализации таймера, как и в предыдущей программе.
Обработчик прерывания таймера Т0
В прерывании таймера микроконтроллер считывает из массива очередное значение синуса и записывает его в регистр сравнения OCR0. Действительная перезапись этого регистра происходит когда счетный регистр таймера Т0 переполняется. Для доступа к элементам массива используется статическая переменная, значение которой увеличивается на 1, пока не дойдет до 32.
Comments
Только при моделировании в Proteus появляется провал синусоиды, из-за того что OC0=255, при i=7 и идет провал ШИМ в 0.
Поставил 254 стало намного красивее.
__enable_interrupt();
и каков ее аналог в CVAVR?
Я понимаю что это связано как то с разрешением или обьявлением прерываний но разаобратья не могу.
#asm("sei") //enable interrut
#asm("cli") //disale interrupt
Code:
.//ATmega16 Внешний кварц 12МГц;
#include <ioavr.h>
#include <intrinsics.h>
int main(void)
{
DDRB=(0<<DDB0)|(0<<DDB1);//Порти кнопок;
PORTB=(1<<PB0)|(1<<PB1);//Под резистор;
DDRD=(1<<DDD4)|(1<<DDD5);//Порти ОС1В,ОС1А;
PORTD=0;
//таймер Т1;
TCCR1A = (0<<WGM11)|(1<<WGM10)|(1<<COM1A1)|(0<<COM1A1)|(1<<COM1B1)|(0<<COM1B1);
TCCR1B = (0<<WGM13)|(0<<WGM12)|(0<<CS12)|(1<<CS11)|(1<<CS10);
TCNT1 = 0;
OCR1A = 0;
OCR1B = 0;
ADMUX = (0<<REFS1)|(1<<REFS0)|(1<<ADLAR)|(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0);
ADCSRA = (1<<ADEN)|(1<<ADSC)|(1<<ADATE)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
SFIOR = 0;
__enable_interrupt();
while(1);
return 0;
}
#pragma vector=ADC_vect
__interrupt void adc_my(void)
{
if((PINB&(1<<PB0))==1)//кнопка1
{
OCR1A = 0;
}
else
{
OCR1A = ADCH;
}
if((PINB&(1<<PB1))==1)//кнопка2
{
OCR1B = 0;
}
else
{
OCR1B = ADCH;
}
}
Когда нажимаеш на первую кнопку ШИМ на порту OCR1A а когда вторую ШИМА на порту OCR1B нету. Мне кажется я с таймером что-то нетак понял.:)
Code:
TCCR1A = (0<<WGM11)|(1<<WGM10)|(1<<COM1A1)|(0<<COM1A1)|(0<<CS12)|(1<<CS11)|(1<<CS10);
TCCR1B = (0<<WGM13)|(0<<WGM12)|(1<<COM1B1)|(0<<COM1B1)|(0<<CS12)|(1<<CS11)|(1<<CS10);
Нужно так
Code:
TCCR1A = (0<<WGM11)|(1<<WGM10)|(1<<COM1A1)|(0<<COM1A1)|(1<<COM1B1)|(0<<COM1B1);
TCCR1B = (0<<WGM13)|(0<<WGM12)|(0<<CS12)|(1<<CS11)|(1<<CS10);
Code:
if((PINB&(1<<PB0))==0){
Вывод работает в режиме входа, включен подтягивающий резистор. Нажатие на кнопку коммутирует вывод микроконтроллера на землю. Соответственно нужно проверять, что на выводе установился логический ноль. Обычно делают так.
Если делать как ты, то нужно сравнивать не с 1, а с ненулевым значением. Вот так:
Code:
if((PINB&(1<<PB0))>0){...
или так
Code:
if(PINB&(1<<PB0)){...
но там два вида подключения по SPI и USARТ думаю брать SPI но еще не решил.
Программирую на mikroC++
RSS feed for comments to this post