Страница 46 из 75
Re: Нескольно простых вопросов о программировании AVR на Си.
Добавлено: Чт апр 18, 2019 23:08:12
WiseLord
Зачем тупо копипастить куски кода? Зачем Вам мой делитель 64? Почему не 1024, лучше подходящий для задачи? Для шестичасовых интервалов - чем больше делитель, тем реже прерывания, тем лучше для Вас. Откройте даташит на МК, почитайте про таймера, про их регистры. Сделайте осознанный выбор таймера - возможно, 16-битный Timer1 с соответственно подобранным делителем будет дучшим выбором.
Я Вам потому и давал общее описание решения, чтобы понимание сути появилось. А Вы просто пытаетесь куски кода без малейших правок утащить, даже в документацию не заглянув.
Re: Нескольно простых вопросов о программировании AVR на Си.
Добавлено: Пт апр 19, 2019 04:32:49
webtime
Для осознанного выбора хорошо иметь понимание. Присматриваюсь, отсюда и куски кода, как обозначение проблемы, а не ее решение. Направление Вы указали, спасибо, но перейти с основательно подзабытых пятнадцатилетней давности "плюсов" на си для мк за несколько часов мне не удалось. Засим, мечтаю о подарке в виде рабочего актуального кода

.
Re: Нескольно простых вопросов о программировании AVR на Си.
Добавлено: Сб апр 27, 2019 10:04:44
Prod
Подскажите в чём косяк может быть? Если кнопка (включаем к питанию) на PC0, то отлично всё выполняется, но если на PC1 и старше хрена лысого ..
Код: Выделить всё
/*
* GccApplication1.c
* ATMEGA48
#define F_CPU 8000000UL // 8 MHz
#include <avr/io.h>
#include <util/delay.h>
int main(void)
{
DDRB = 0xFF; // все выводы порта B сконфигурировать как выходы
DDRD = 0xFF; // все выводы порта D сконфигурировать как выходы
DDRC = 0x00; // все выводы порта C сконфигурировать как входы
while (1)
if ((PINC &(1<<PC1))==1) // Если нажата кнопка
{
while((PINC &(1<<PC1))==1){} // Ждем отпускания кнопки
PORTB =0xFF; PORTD =0xFF; _delay_ms(500); // Включение группы портов B и D
}
}
Компилирую с помощью Atmel Studio 7, и визуализирую на Proteus 8.6/8.8 один фикус.
Re: Нескольно простых вопросов о программировании AVR на Си.
Добавлено: Сб апр 27, 2019 11:07:33
WiseLord
Prod писал(а):if ((PINC &(1<<PC1))==1) // Если нажата кнопка
Неверное условие. PINC & (1<<PC1) никогда не будет равно 1. Либо 0, либо 2
Проверяйте либо:
либо
Re: Нескольно простых вопросов о программировании AVR на Си.
Добавлено: Сб апр 27, 2019 11:52:14
Prod
WiseLord
Спасибо, а как надёжней исходя из опыта?
Или if ((PINC & 0x2) == 0x2) // Если нажата кнопка на PC1
Re: Нескольно простых вопросов о программировании AVR на Си.
Добавлено: Сб апр 27, 2019 12:01:50
WiseLord
Без разницы
Re: Нескольно простых вопросов о программировании AVR на Си.
Добавлено: Вс апр 28, 2019 15:23:38
Oxford
Prod, надежно тогда, когда знаешь как работает код.
Re: Нескольно простых вопросов о программировании AVR на Си.
Добавлено: Ср май 01, 2019 00:39:17
KorbenDallas
[uquote="Prod",url="/forum/viewtopic.php?p=3624013#p3624013"]
WiseLord
Спасибо, а как надёжней исходя из опыта?[/uquote]
Никакой разницы в "надежности" тут нет.
Проверка
применяется для того, чтобы проверить, установлен ли в 1 хотя бы один бит, описываемый маской `MASK`.
Проверка
применяется для того, чтобы проверить, установлены ли биты, описываемый маской `MASK`, именно в значения, указанные в наборе `BITS`.
Понятно, что когда речь идет о проверке одного-единственного бита, разница между этими вариантами стирается полностью. Используйте тот, который вам больше нравится.
Re: Нескольно простых вопросов о программировании AVR на Си.
Добавлено: Вт июн 04, 2019 19:19:21
baron_P
Доброго времени суток
Делаю преобразователь из 12-битнго параллельного интерфейса в аналоговый на Atmega8. Вроде, просто: собираю со входов число, выдаю его как задание скважности ШИМ. Использую 10-битный FastPWM в Таймер/Счетчеке 1. ШИМ работает, задание отрабатывается, число со входов собирается. Только вот Протеус выдает что-то очень странное.
Тестовая схема:

Фьюзы:

Код:
Спойлер
Код: Выделить всё
/*
МК ATMEGA8
Частота МК = 8 МГц
Fuse High Byte
Fuse Low Byte
*/
//Подключенные файлы
#include <avr/io.h> // Стандартные функции ввода/вывода для МК
#include <avr/interrupt.h> // Стандартные функции работы с прерываниями
//Подстановки
//Прототипы функций
//Процедура инициализации входов/выходов
void init_IO(void);
//Процедура инициализации ТС1
void init_TC1(void);
//Глобальные переменные
/********** Главная фунция **********/
void main(void)
{
//Инициализация входов/выходов
init_IO();
//Процедура инициализации ТС1
init_TC1();
//Инициализация локальных переменных
//Значение числа с 12-битного входа
unsigned int data_input = 0;
//Значения с соотв. входов
unsigned char data_in_D, data_in_B = 0;
//Разрешение прерываний
sei();
//Рабочий цикл
while(1)
{
//Читаем значения со входов
//Порт D без изменений
data_in_D = PIND;
//Порт B с очисткой неиспользуемых младших битов
data_in_B = PINB & ((1 << PB7) | (1 << PB6) | (1 << PB5) | (1 << PB4));
//Преобразуем их в 10-битное число
data_input = ((data_in_B << 4) | data_in_D) >> 2;
//Выдаем заданную скважность ШИМ
OCR1A = data_input;
};
}
/********** Функции **********/
//Процедура инициализации входов/выходов
void init_IO(void)
{
//12-битный вход, собранный из входов портов D и B
//Младшие биты входа 7-0 (порт D)
DDRD &= ~((1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4) | (1 << PD3) | (1 << PD2) | (1 << PD1) | (1 << PD0));
//Старшие биты входа 11-8 (порт B)
DDRB &= ~((1 << PB7) | (1 << PB6) | (1 << PB5) | (1 << PB4));
//Подтягиваем входы к единице
PORTD |= (1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4) | (1 << PD3) | (1 << PD2) | (1 << PD1) | (1 << PD0);
PORTB |= (1 << PB7) | (1 << PB6) | (1 << PB5) | (1 << PB4);
//Тестовый выход
DDRB |= (1 << PB0);
}
//Процедура инициализации ТС1 (формирование выходного аналогового напряжения)
void init_TC1(void)
{
//Вывод PB1 (OC1A) - выход ШИМ
DDRB |= (1 << PB1);
//Сброс в ноль выхода OC1A при совпадении, режим 10-bit FastPWM
TCCR1A |= (1 << COM1A1) | (1 << WGM11) | (1 << WGM10);
//Предделитель 1
TCCR1B |= (1 << WGM12) | (1 << CS10);
//Начальное значение скважности ШИМ
OCR1A = 0;
//Вкл. прерывания по переполнению ТС
TIMSK |= (1 << TOIE1);
}
/********** Векторы прерываний **********/
//Вектор прерывания по переполнению ТС1
ISR(TIMER1_OVF_vect)
{
PORTB ^= (1 << PB0);
}
Собственно, что не так. При задании разных бит на входы порта D проблем нет - ШИМ на выходе выдает сообразную входам скважность. То самое с двумя младшими битами порта B (PB5 и PB4). А вот на изменение старших битов PB6 и PB7, ШИМ реагирует только после того, как я меняю значение на любом другом входе. Т.е. меняю на PB6 0 на 1 - на выходе ничего не изменятся. Потом меняю на PD3 0 на 1 - и тут же ШИМ на выходе становится таким, как и должно быть при выставленных в 1 PB6 и PD3.
Отчего такая муть может происходить? Два часа ковырял сегодня и ничего, идеи кончились.
Re: Нескольно простых вопросов о программировании AVR на Си.
Добавлено: Вт июн 04, 2019 19:46:29
WiseLord
Немножко смущает (data_in_B << 4). Хотя.. обнуляться не должно, ведь 4 - вроде как 16-битная переменная, так что и data_in_B должно привестись к 16бит перед сдвигом.
И, кстати, не пробовали работать отдельно с OCR1AH и OCR1AL. AVR-libc, конечно, позволяет сразу с 16-битным работать, но кто знает, как это протеус воспринимает... В даташите на ATmega8A (17.3 - Accessing 16-bit Registers) про это немножко написано.
Re: Нескольно простых вопросов о программировании AVR на Си.
Добавлено: Вт июн 04, 2019 21:00:05
Starichok51
а зачем 12 бит, если они потом обрезаются до 10 бит?
Re: Нескольно простых вопросов о программировании AVR на Си.
Добавлено: Вт июн 04, 2019 21:29:27
Z_h_e
baron_P писал(а):Два часа ковырял сегодня и ничего, идеи кончились.
Варнинги компилятор выдает?
Вот это
Код: Выделить всё
//Преобразуем их в 10-битное число
data_input = ((data_in_B << 4) | data_in_D) >> 2
Заменить например на это
Код: Выделить всё
data_input = (uint16_t) какая-то тестовая константа;
Поглядеть на реакцию системы. Сделать какие-то выводы.
Кстати в протеусе можно и точки останова использовать и смотреть значения переменных и по шагам шагать.
Естественно следует избегать неявного преобразования типов.
WiseLord писал(а):AVR-libc, конечно, позволяет сразу с 16-битным работать, но кто знает, как это протеус воспринимает...
А протеус исполняет то, что скомпиллено.
Re: Нескольно простых вопросов о программировании AVR на Си.
Добавлено: Вт июн 04, 2019 21:55:08
baron_P
Starichok51 писал(а):а зачем 12 бит, если они потом обрезаются до 10 бит?
Потому что исходный сигнал 12 бит, но точность, пока, особо не нужна - может сделаю и 12-бит ШИМ потом.
[uquote="WiseLord",url="/forum/viewtopic.php?p=3645324#p3645324"]Немножко смущает (data_in_B << 4). Хотя.. обнуляться не должно, ведь 4 - вроде как 16-битная переменная, так что и data_in_B должно привестись к 16бит перед сдвигом.
И, кстати, не пробовали работать отдельно с OCR1AH и OCR1AL. AVR-libc, конечно, позволяет сразу с 16-битным работать, но кто знает, как это протеус воспринимает... В даташите на ATmega8A (17.3 - Accessing 16-bit Registers) про это немножко написано.[/uquote]
Z_h_e писал(а):Варнинги компилятор выдает?
Вот это
Код: Выделить всё
//Преобразуем их в 10-битное число
data_input = ((data_in_B << 4) | data_in_D) >> 2
Заменить например на это
Код: Выделить всё
data_input = (uint16_t) какая-то тестовая константа;
Поглядеть на реакцию системы. Сделать какие-то выводы.
Кстати в протеусе можно и точки останова использовать и смотреть значения переменных и по шагам шагать.
Естественно следует избегать неявного преобразования типов. А протеус исполняет то, что скомпиллено.
Компилятор молчит. Проблема в том, что, если вместо PINB подставить константу, то все выводится как должно. Более того, и с PINB выводится в ШИМ как должно, но происходит это только если изменить какой-либо еще из битов, кроме PB6 и PB7. Т.е. считается то все правильно, а вот с обновлением значений этой пары битов какая-то странь. Завтра посмотрю по шагам.
Re: Нескольно простых вопросов о программировании AVR на Си.
Добавлено: Вт июн 04, 2019 22:26:42
WiseLord
Я бы всё-таки попробовал, спокойствия ради:
Код: Выделить всё
OCR1AH = (data_input >> 8);
OCR1AL = (data_input & 0xFF);
Но всё-таки, скорее всего, очередной глюк Proteus.
Re: Нескольно простых вопросов о программировании AVR на Си.
Добавлено: Ср июн 05, 2019 08:42:22
baron_P
[uquote="WiseLord",url="/forum/viewtopic.php?p=3645413#p3645413"]Я бы всё-таки попробовал, спокойствия ради:
Код: Выделить всё
OCR1AH = (data_input >> 8);
OCR1AL = (data_input & 0xFF);
Но всё-таки, скорее всего, очередной глюк Proteus.[/uquote]
Ничего не изменилось. Взял процедуру из датащита, но она тоже не помогла:
Код: Выделить всё
void TIM16_WriteOCRA1(unsigned int i)
{
unsigned char sreg;
/* Save Global Interrupt Flag*/
sreg = SREG;
/* Disable interrupts*/
cli();
/* Set OCR1A to i */
OCR1A= i;
/* Restore Global Interrupt Flag*/
SREG = sreg;
}
Как посмотреть в Протеусе состояние переменных не разобрался сходу, но поигрался с константами и выяснил, что входы портов не причем. Описанный глюк проявляется при трех значениях в OCR1A:
0b0100000000 - 256
0b1000000000 - 512
0b1100000000 - 768
Но любые другие значения - без проблем ( 255 и 257, 511 и 513, 767 и 769). Не понимаю, как состояние младших битов может влиять на старшие.
UPD: Спаял на макетке - все работает без проблем. Видимо, это и впрямь баг Протеуса.
Re: Нескольно простых вопросов о программировании AVR на Си.
Добавлено: Ср июн 05, 2019 16:36:24
KorbenDallas
[uquote="WiseLord",url="/forum/viewtopic.php?p=3645324#p3645324"]Немножко смущает (data_in_B << 4). Хотя.. обнуляться не должно, ведь 4 - вроде как 16-битная переменная, так что и data_in_B должно привестись к 16бит перед сдвигом.[/uquote]
Нет, это так не работает. В операторах побитового сдвига тип второго операнда никак не влияет на преобразования первого операнда. Так что не имеет никакого значения, какой тип имеет `4`.
А вот обыкновенные integral promotions тут никто не отменял, то есть `data_in_B` будет неявно преобразовано к типу `int` само по себе.
Re: Нескольно простых вопросов о программировании AVR на Си.
Добавлено: Ср июн 05, 2019 18:36:11
Z_h_e
baron_P писал(а):Как посмотреть в Протеусе состояние переменных не разобрался сходу,
Нужно подгружать исполняемый код не hex, а elf. Тогда можно будет видеть переменные в отладке на паузе. И еще желательно кинуть исходники в папку с проектом в протеусе, тогда можно и по шагам шагать будет. Я правда очень давно в протеусе симуляцию не делал, может чего не точно сказал. А так да, модели МК для протеуса не без глюков, что-то там в режиме СТС было не так, например.
Re: Нескольно простых вопросов о программировании AVR на Си.
Добавлено: Чт июн 06, 2019 14:09:41
baron_P
[uquote="Z_h_e",url="/forum/viewtopic.php?p=3645877#p3645877"]Нужно подгружать исполняемый код не hex, а elf. Тогда можно будет видеть переменные в отладке на паузе. И еще желательно кинуть исходники в папку с проектом в протеусе, тогда можно и по шагам шагать будет. Я правда очень давно в протеусе симуляцию не делал, может чего не точно сказал. А так да, модели МК для протеуса не без глюков, что-то там в режиме СТС было не так, например.[/uquote]
Спасибо, попробую при случае, пока же необходимость отпала) Вот, что получилось в итоге (входы типа "открытый коллектор", 12-битный ШИМ на выходе):
Спойлер
Код: Выделить всё
/*
МК ATMEGA8
Частота МК = 8 МГц
Fuse High Byte
BODLEVEL = 0 - нижний порог напряжения питания 4 В
BODEN = 0 - слежение за напряжением питания вкл.
SUT1 = 0 \ время включения 6СК + 64 ms
SUT0 = 0 /
CKSEL3 = 0 \
CKSEL2 = 1 - встроенный источник тактовых импульсов
CKSEL1 = 0 - с частотой 8 МГц
CKSEL0 = 0 /
Fuse Low Byte
RSTDISBL = 1 - переназначение вывода ~RESET выкл.
WDTON = 1 - постоянная работа сторожевого таймера выкл.
SPIEN = 0 - внутрисхемное программирование вкл.
CKOPT = 1 - опции работы с внешним кварцевым резонатором выкл.
EESAVE = 1 - защита EEPROM от стирания выкл.
BOOTSZ1 = 0 \ размер загрузочного сектора
BOOTSZ0 = 0 / 1024 слова, начальный адрес $0C00
BOOTRST = 1 - перенос стартового сектора в область загрузчика выкл.
*/
//Подключенные файлы
#include <avr/io.h> // Стандартные функции ввода/вывода для МК
#include <avr/interrupt.h> // Стандартные функции работы с прерываниями
#include <avr/wdt.h> // Стандартные функции работы cо сторожевым таймером
//Подстановки
#define PWM_MAX 4095 // Максимальное значение скважности для 12-битиного ШИМ (PWM_MAX = 2^12 - 1)
#define N_MAX 3 // Максимальное количесво значений для получения средне арифметичесого (2^N_MAX = 8)
#define U_INT_MAX 65535 // Максимальное значение типа unsigned int
//Прототипы функций
//Процедура инициализации входов/выходов
void init_IO(void);
//Процедура инициализации ТС1
void init_TC1(void);
//Процедура записи в 16-битный регистр OCR1A
void WriteToOCRA1(unsigned int);
//Функция получения среднего значения из N_MAX полученных
unsigned int data_conversation(unsigned int, unsigned int);
/********** Главная фунция **********/
void main(void)
{
//Инициализация входов/выходов
init_IO();
//Инициализация ТС1
init_TC1();
//Инициализация локальных переменных
//Значения с входов соотв. портов
unsigned char data_in_D = 0, data_in_B = 0;
//Значение числа с 12-битного входа
unsigned int data_input = 0;
//Вычисленное средне арифметическое из N_MAX полученных значений
unsigned int data_reference = 0;
//Разрешение прерываний
sei();
//Включение сторожевого таймера с периодом 1с
wdt_enable(WDTO_1S);
//Рабочий цикл
while(1)
{
//Чтение значений со входов
//Порт D без изменений
data_in_D = PIND;
//Порт B с очисткой неиспользуемых младших битов
data_in_B = PINB & ((1 << PB7) | (1 << PB6) | (1 << PB5) | (1 << PB4));
//Преобразование полученных значений в 12-битное число (0b00 4бита PortB 8 битов PortD)
data_input = (data_in_B << 4) | data_in_D;
//Получение среднего арифметического из N_MAX значений
data_reference = data_conversation(data_input, data_reference);
//Выдача заданния скважности ШИМ в регистр сравнения
WriteToOCRA1(data_reference);
//Сброс сторожевого таймера
wdt_reset();
};
}
/********** Функции **********/
//Процедура инициализации входов/выходов
void init_IO(void)
{
//12-битный вход, собранный из входов портов D и B
//Младшие биты входа 7-0 (порт D)
DDRD &= ~((1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4) | (1 << PD3) | (1 << PD2) | (1 << PD1) | (1 << PD0));
//Старшие биты входа 11-8 (порт B)
DDRB &= ~((1 << PB7) | (1 << PB6) | (1 << PB5) | (1 << PB4));
//Подтяжка входов к единице
PORTD |= (1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4) | (1 << PD3) | (1 << PD2) | (1 << PD1) | (1 << PD0);
PORTB |= (1 << PB7) | (1 << PB6) | (1 << PB5) | (1 << PB4);
//Неиспользуемые выводы назначаются входами
DDRB &= ~((1 << PB3) | (1 << PB2) | (1 << PB0));
DDRC &= ~((1 << PC5) | (1 << PC4) | (1 << PC3) | (1 << PC2) | (1 << PC1) | (1 << PC0));
//Подтяжка неиспользуемых входов к единице
PORTB |= (1 << PB3) | (1 << PB2) | (1 << PB0);
PORTC |= (1 << PC5) | (1 << PC4) | (1 << PC3) | (1 << PC2) | (1 << PC1) | (1 << PC0);
}
//Процедура инициализации ТС1 (формирование выходного аналогового напряжения)
void init_TC1(void)
{
//Вывод PB1 (OC1A) - выход ШИМ
DDRB |= (1 << PB1);
//Сброс в ноль выхода OC1A при совпадении, режим FastPWM (ICR1)
TCCR1A |= (1 << COM1A1) | (1 << WGM11);
//Предделитель 1
TCCR1B |= (1 << WGM13) | (1 << WGM12) | (1 << CS10);
//Маскимальное значение скважности
ICR1 = PWM_MAX;
//Начальное (нулевое) значение скважности ШИМ
WriteToOCRA1(0);
}
//Процедура атомарной записи в 16-битный регистр OCR1A (пример из датащита на Atmega8)
void WriteToOCRA1(unsigned int i)
{
unsigned char sreg;
/* Save Global Interrupt Flag*/
sreg = SREG;
/* Disable interrupts*/
cli();
/* Set OCR1A to i */
OCR1A= i;
/* Restore Global Interrupt Flag*/
SREG = sreg;
}
//Функция получения среднего значения из N_MAX полученных
unsigned int data_conversation(unsigned int data_in, unsigned int data_ref)
{
//Суммарное значение N_MAX полученных
static unsigned int data_sum;
//Счетчик количества просуммированых значений
static unsigned char data_n;
//Если количество просуммированных значений меньше заданного
if (data_n < (1 << N_MAX))
{
//Добавляем полученное значение к сумме предыдущих (предельное значени data_in = PWM_MAX*2^N_MAX = 32670)
data_sum += data_in;
//Инкрементируем счетчик полученных значений
data_n++;
}
else
{
//Иначе, обновляем предыдущее средне-арифметическое значение
data_ref = (data_sum >> N_MAX);
//Обнуляем сумму полученных значений
data_sum = 0;
//Обнуляем счетчик полученных значений
data_n = 0;
}
//Возвращаем среднее значение
return data_ref;
}
Re: Нескольно простых вопросов о программировании AVR на Си.
Добавлено: Чт июн 06, 2019 19:40:33
KorbenDallas
[uquote="baron_P",url="/forum/viewtopic.php?p=3646369#p3646369"]
Код: Выделить всё
//Функция получения среднего значения из N_MAX полученных
unsigned int data_conversation(unsigned int data_in, unsigned int data_ref)
[/uquote]
Во-первых, среднее у вас вычисляется не из `N_MAX` значений, как написано в комментарии, а из `2^N_MAX` значений.
Во-вторых, вышеприведенная функция возвращает новое среднее только на каждой `2^N_MAX` итерации. На остальных итерациях возвращается последнее вычисленное среднее. Это так и задумано?
Re: Нескольно простых вопросов о программировании AVR на Си.
Добавлено: Чт июн 06, 2019 19:53:28
ARV
baron_P писал(а)://Процедура атомарной записи в 16-битный регистр OCR1A (пример из датащита на Atmega8)
атомарные операции в AVR-GCC делаются не так
Код: Выделить всё
#include <util/atomic.h> /* вот в этом заголовке все нужное */
void WriteToOCRA1(unsigned int i){
ATOMIC_BLOCK(ATOMIC_RESTORSETATE){
OCR1A= i;
}
}
хотя я так и не понял, зачем в этом коде атомарность для доступа к OCR1A...