Обсуждаем контроллеры компании Atmel.
Ответить

Re: Нескольно простых вопросов о программировании AVR на Си.

Чт апр 18, 2019 23:08:12

Зачем тупо копипастить куски кода? Зачем Вам мой делитель 64? Почему не 1024, лучше подходящий для задачи? Для шестичасовых интервалов - чем больше делитель, тем реже прерывания, тем лучше для Вас. Откройте даташит на МК, почитайте про таймера, про их регистры. Сделайте осознанный выбор таймера - возможно, 16-битный Timer1 с соответственно подобранным делителем будет дучшим выбором.
Я Вам потому и давал общее описание решения, чтобы понимание сути появилось. А Вы просто пытаетесь куски кода без малейших правок утащить, даже в документацию не заглянув.

Re: Нескольно простых вопросов о программировании AVR на Си.

Пт апр 19, 2019 04:32:49

Для осознанного выбора хорошо иметь понимание. Присматриваюсь, отсюда и куски кода, как обозначение проблемы, а не ее решение. Направление Вы указали, спасибо, но перейти с основательно подзабытых пятнадцатилетней давности "плюсов" на си для мк за несколько часов мне не удалось. Засим, мечтаю о подарке в виде рабочего актуального кода :) .

Re: Нескольно простых вопросов о программировании AVR на Си.

Сб апр 27, 2019 10:04:44

Подскажите в чём косяк может быть? Если кнопка (включаем к питанию) на 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

Prod писал(а):if ((PINC &(1<<PC1))==1) // Если нажата кнопка
Неверное условие. PINC & (1<<PC1) никогда не будет равно 1. Либо 0, либо 2

Проверяйте либо:
Код:
if ((PINC & (1<<PC1)) == (1<<PC1))

либо
Код:
if ((PINC & (1<<PC1))

Re: Нескольно простых вопросов о программировании AVR на Си.

Сб апр 27, 2019 11:52:14

WiseLord
Спасибо, а как надёжней исходя из опыта?
Или if ((PINC & 0x2) == 0x2) // Если нажата кнопка на PC1
Последний раз редактировалось Prod Сб апр 27, 2019 12:13:08, всего редактировалось 1 раз.

Re: Нескольно простых вопросов о программировании AVR на Си.

Сб апр 27, 2019 12:01:50

Без разницы

Re: Нескольно простых вопросов о программировании AVR на Си.

Вс апр 28, 2019 15:23:38

Prod, надежно тогда, когда знаешь как работает код.

Re: Нескольно простых вопросов о программировании AVR на Си.

Ср май 01, 2019 00:39:17

WiseLord
Спасибо, а как надёжней исходя из опыта?


Никакой разницы в "надежности" тут нет.

Проверка

Код:
if ((data & MASK) != 0)


применяется для того, чтобы проверить, установлен ли в 1 хотя бы один бит, описываемый маской `MASK`.

Проверка

Код:
if ((data & MASK) == BITS)


применяется для того, чтобы проверить, установлены ли биты, описываемый маской `MASK`, именно в значения, указанные в наборе `BITS`.

Понятно, что когда речь идет о проверке одного-единственного бита, разница между этими вариантами стирается полностью. Используйте тот, который вам больше нравится.

Re: Нескольно простых вопросов о программировании AVR на Си.

Вт июн 04, 2019 19:19:21

Доброго времени суток
Делаю преобразователь из 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

Немножко смущает (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

а зачем 12 бит, если они потом обрезаются до 10 бит?

Re: Нескольно простых вопросов о программировании AVR на Си.

Вт июн 04, 2019 21:29:27

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

Starichok51 писал(а):а зачем 12 бит, если они потом обрезаются до 10 бит?

Потому что исходный сигнал 12 бит, но точность, пока, особо не нужна - может сделаю и 12-бит ШИМ потом.

Немножко смущает (data_in_B << 4). Хотя.. обнуляться не должно, ведь 4 - вроде как 16-битная переменная, так что и data_in_B должно привестись к 16бит перед сдвигом.
И, кстати, не пробовали работать отдельно с OCR1AH и OCR1AL. AVR-libc, конечно, позволяет сразу с 16-битным работать, но кто знает, как это протеус воспринимает... В даташите на ATmega8A (17.3 - Accessing 16-bit Registers) про это немножко написано.


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

Я бы всё-таки попробовал, спокойствия ради:
Код:
OCR1AH = (data_input >> 8);
OCR1AL = (data_input & 0xFF);

Но всё-таки, скорее всего, очередной глюк Proteus.

Re: Нескольно простых вопросов о программировании AVR на Си.

Ср июн 05, 2019 08:42:22

Я бы всё-таки попробовал, спокойствия ради:
Код:
OCR1AH = (data_input >> 8);
OCR1AL = (data_input & 0xFF);

Но всё-таки, скорее всего, очередной глюк Proteus.

Ничего не изменилось. Взял процедуру из датащита, но она тоже не помогла:
Код:
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

Немножко смущает (data_in_B << 4). Хотя.. обнуляться не должно, ведь 4 - вроде как 16-битная переменная, так что и data_in_B должно привестись к 16бит перед сдвигом.


Нет, это так не работает. В операторах побитового сдвига тип второго операнда никак не влияет на преобразования первого операнда. Так что не имеет никакого значения, какой тип имеет `4`.

А вот обыкновенные integral promotions тут никто не отменял, то есть `data_in_B` будет неявно преобразовано к типу `int` само по себе.

Re: Нескольно простых вопросов о программировании AVR на Си.

Ср июн 05, 2019 18:36:11

baron_P писал(а):Как посмотреть в Протеусе состояние переменных не разобрался сходу,
Нужно подгружать исполняемый код не hex, а elf. Тогда можно будет видеть переменные в отладке на паузе. И еще желательно кинуть исходники в папку с проектом в протеусе, тогда можно и по шагам шагать будет. Я правда очень давно в протеусе симуляцию не делал, может чего не точно сказал. А так да, модели МК для протеуса не без глюков, что-то там в режиме СТС было не так, например.

Re: Нескольно простых вопросов о программировании AVR на Си.

Чт июн 06, 2019 14:09:41

Нужно подгружать исполняемый код не hex, а elf. Тогда можно будет видеть переменные в отладке на паузе. И еще желательно кинуть исходники в папку с проектом в протеусе, тогда можно и по шагам шагать будет. Я правда очень давно в протеусе симуляцию не делал, может чего не точно сказал. А так да, модели МК для протеуса не без глюков, что-то там в режиме СТС было не так, например.

Спасибо, попробую при случае, пока же необходимость отпала) Вот, что получилось в итоге (входы типа "открытый коллектор", 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

Код:
//Функция получения среднего значения из N_MAX полученных
unsigned int data_conversation(unsigned int data_in, unsigned int data_ref)


Во-первых, среднее у вас вычисляется не из `N_MAX` значений, как написано в комментарии, а из `2^N_MAX` значений.

Во-вторых, вышеприведенная функция возвращает новое среднее только на каждой `2^N_MAX` итерации. На остальных итерациях возвращается последнее вычисленное среднее. Это так и задумано?
Последний раз редактировалось KorbenDallas Чт июн 06, 2019 20:02:46, всего редактировалось 1 раз.

Re: Нескольно простых вопросов о программировании AVR на Си.

Чт июн 06, 2019 19:53:28

baron_P писал(а)://Процедура атомарной записи в 16-битный регистр OCR1A (пример из датащита на Atmega8)

атомарные операции в AVR-GCC делаются не так
Код:
#include <util/atomic.h> /* вот в этом заголовке все нужное */

void WriteToOCRA1(unsigned int i){
   ATOMIC_BLOCK(ATOMIC_RESTORSETATE){
      OCR1A= i;
   }
}
хотя я так и не понял, зачем в этом коде атомарность для доступа к OCR1A...
Ответить