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

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

Пт июн 30, 2017 15:55:02

Уважаемые знатоки, объясните пожалуйста какая структура кода более "правильная", если так можно выразиться:
(Оба кода работают как надо)

Вариант 1:
Спойлер
Код:
#include <avr/io.h> //Подключаем библиотеку AVR
#include <avr/iotn2313.h> //Подключаем библиотеку ATtiny2313
#include <avr/interrupt.h> //Подключаем библиотеку прерываний
#define F_CPU 1000000UL
#include <util/delay.h>

//Глобальные переменные сюда:
volatile unsigned char reg=0; //Переменная reg - режим

//Обработка прерывания по спаду напряжения на INT0
ISR(INT0_vect)
{
_delay_ms(10);
reg++; //при нажатии на кнопку Увеличиваем значение переменной reg на 1
if (reg==4) reg=0; //Если досчитали до 4, то reg=0
}

void INTinit()
{
GIMSK=(1<<6); //Разрешаем прерывание INT0
MCUCR=(1<<0)|(1<<1); //Прерывыание по ниспадающему форонту (с 1 на 0); ISC01=1, ISC00=1
}

int main (void)
{
DDRB=(1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<7); //Порт В на ВЫХод

PORTD=(1<<2); //Порт PD2 Подтягивающий резистор ВКлючен

INTinit();   // вызываем функцию инициализации прерываний
sei();   //Глобальное разрешение прерываний

   while (1)
   {
      switch(reg)
         {

            case 1:
               PORTB=(1<<0);
               break;
            case 2:
               PORTB=(1<<1);
               break;
            case 3:
               PORTB=(1<<2);
               break;
            default:
               PORTB=0;
         }
            

   };
}


Вариант 2:
Спойлер
Код:
#include <avr/io.h> //Подключаем библиотеку AVR
#include <avr/iotn2313.h> //Подключаем библиотеку ATtiny2313
#include <avr/interrupt.h> //Подключаем библиотеку прерываний
#define F_CPU 1000000UL
#include <util/delay.h>

//Глобальные переменные сюда:
volatile unsigned char reg=0; //Переменная reg - режим

//Обработка прерывания по спаду напряжения на INT0
ISR(INT0_vect)
{
_delay_ms(10);
reg++; //при нажатии на кнопку Увеличиваем значение переменной reg на 1
if (reg==4) reg=0; //Если досчитали до 4, то reg=0
}

int main (void)
{
DDRB=(1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<7); //Порт В на ВЫХод

PORTD=(1<<2); //Порт PD2 Подтягивающий резистор ВКлючен

GIMSK=(1<<6); //Разрешаем прерывание INT0
MCUCR=(1<<0)|(1<<1); //Прерывыание по ниспадающему форонту (с 1 на 0); ISC01=1, ISC00=1

sei();   //Глобальное разрешение прерываний

   while (1)
   {
      switch(reg)
         {

            case 1:
               PORTB=(1<<0);
               break;
            case 2:
               PORTB=(1<<1);
               break;
            case 3:
               PORTB=(1<<2);
               break;
            default:
               PORTB=0;
         }
            

   };
}

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

Пн июл 03, 2017 22:43:35

Зря вы столько кода лишнего вставили. Тяжело анализировать
Спойлер
Код:
ПЕРВЫЙ ВАРИАНТ

void INTinit()
{
GIMSK=(1<<6); //Разрешаем прерывание INT0
MCUCR=(1<<0)|(1<<1); //Прерывыание по ниспадающему форонту (с 1 на 0); ISC01=1, ISC00=1
}
int main (void)
{
INTinit();   // вызываем функцию инициализации прерываний
sei();   //Глобальное разрешение прерываний
   while (1)
   {
   }
}

ВТОРОЙ ВАРИАНТ

int main (void)
{
GIMSK=(1<<6); //Разрешаем прерывание INT0
MCUCR=(1<<0)|(1<<1); //Прерывыание по ниспадающему форонту (с 1 на 0); ISC01=1, ISC00=1
sei();   //Глобальное разрешение прерываний
   while (1)
   {
   }
}

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

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

Пн июл 03, 2017 23:22:37

MCUCR=(1<<0)|(1<<1); //Прерывыание по ниспадающему форонту (с 1 на 0); ISC01=1, ISC00=1
Вот так писать не надо. Специально для наглядности кода существуют константы:
MCUCR = (1<<ISC01 | 1<<ISC00); // прерывание INT0 по спаду
Видел так делают, когда принимают или отправляют значение, так называемые геттеры и сеттеры, но это не тот случай.
Вроде как это делается для большей структуризации кода и некоторого увеличения его безопасности. В контроллерах, где идет прямое обращение к регистрам, это лишнее.

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

Вт июл 04, 2017 16:17:09

небольшой вопрос по языку си (правда на примере регистров stm32). Для чего применяется запись uint32_t? Это какой-то намек на 32-разрядные числа? Вот пример, нужно сбросить бит в регистре:
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); Для чего здесь первый раз применяется эта конструкция и для чего второй?

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

Вт июл 04, 2017 18:38:16

Для чего применяется запись uint32_t?
uint32_t - unsigned integer 32-битный.
Не трудно догадаться, что этот тип применяется для целочисленных беззнаковых 32-битных переменных.

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

Ср июл 05, 2017 09:49:30

uint32_t - unsigned integer 32-битный.
Не трудно догадаться, что этот тип применяется для целочисленных беззнаковых 32-битных переменных.

Т.е. uint_32t это тоже самое для программы, что unsigned int в объявлении переменных? Я всегда это подозревал. Но тогда как понять запись вида ((uint32_t)~(RCC_CFGR_SW))?

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

Ср июл 05, 2017 10:27:48

Не совсем. unsigned int не везде 32-битный (в тех же AVR - 16-битный, например). Использование типов вроде uint32_t избавляет от необходимости помнить эти размеры в разных архитектурах, упрощает код и обеспечивает лучшую переносимость кода между разными платформами.

Собственно, то же и с приведением типов. Если имеем, к примеру, #define SOME 4, то (uint32_t)(~((uint32_t)SOME) гарантировано даст 32-битную инверсию 32-битной четвёрки, т.е., 0xFFFFFFFB. Тогда как, например, просто (uint32_t)(~SOME) вполне может дать результат 0x0000FFFB на некоторых архитектурах.

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

Ср июл 05, 2017 11:12:06

Не совсем. unsigned int не везде 32-битный (в тех же AVR - 16-битный, например). Использование типов вроде uint32_t избавляет от необходимости помнить эти размеры в разных архитектурах, упрощает код и обеспечивает лучшую переносимость кода между разными платформами.

Собственно, то же и с приведением типов. Если имеем, к примеру, #define SOME 4, то (uint32_t)(~((uint32_t)SOME) гарантировано даст 32-битную инверсию 32-битной четвёрки, т.е., 0xFFFFFFFB. Тогда как, например, просто (uint32_t)(~SOME) вполне может дать результат 0x0000FFFB на некоторых архитектурах.

Разобрался, спасибо.

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

Ср июл 12, 2017 10:00:34

Здравствуйте. Подскажите по поводу использования таймера в atmega8. Есть таймер 16 бит. Сконфигурирован таким образом чтобы на выход OCR1B в режиме fast PWM 9bit был меандр со скважностью 50%. Это для пищалки. Хочу использовать этот же таймер для прерывания по совпадению с OCR1A. Но МК не входит в прерывание. Я так понимаю не до тикивает до нужного значения TCNT1.
Можно ли вообще использовать часть таймера для генерации ШИМ и отсчета времени в прерывании?
Спасибо.

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

Ср июл 12, 2017 10:16:21

Можно. Чтобы определить где в вашем коде ошибка, нужно его видеть.

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

Ср июл 12, 2017 10:26:17

Спойлер/*

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

#include "MFRC522.h"


#define RX_ENABLE PORTD |= _BV(PD2); _delay_ms(1)
#define TX_ENABLE PORTD &= ~(_BV(PD2)); _delay_ms(1)
#define GREENLED_OFF PORTC |= _BV(PC3)
#define GREENLED_ON PORTC &= ~(_BV(PC3))
#define REDLED_OFF PORTC |= _BV(PC2)
#define REDLED_ON PORTC &= ~(_BV(PC2))

#define STARTDEV 1
#define REEDOK 2

#define BEEP_ON cli(); OCR1B = 0x00FF; sei()
#define BEEP_OFF cli(); OCR1B = 0x01FF; sei()

#define RXUBRR (F_CPU/16/9600)-1
#define BUFFER_SIZE 2
//#define UART_RX0_BUFFER_SIZE 3

char buffer[BUFFER_SIZE];
volatile uint8_t delay_count = 0; //задержка для таймера 1, для бипера (30 = 1сек)

void indicate (uint8_t event){
switch(event){
case STARTDEV : BEEP_ON;
GREENLED_ON;
delay_count = 15; break;
// REEDOK :
default: break;
}


}

ISR(TIMER1_COMPA_vect){ // Прерывание отсчета времени
TCNT1 = 0;
if (delay_count > 1){
delay_count--;
} else if (delay_count) {
GREENLED_OFF;
BEEP_OFF;
delay_count = 0;
}
}

int main(void)
{
uchar card_num[5];
uchar i;
//== Init============
DDRD = 0b00000100;
PORTD = 0b11111100;
DDRC = 0b00001100;
PORTC = 0b00101100;
DDRB = 0b00000100;
PORTB = 0b00000111;
//== Init===========
TIMSK = (1<<OCIE1A); //Прерывание по пререполнению таймера 1 (30Гц)
//== 1 16bit Init====
TCCR1A = (1<<COM1B1)|(1<<COM1B0)|(1<<WGM12)|(1<<WGM11); //Fast PWM 9bit на выводе OC1B (частота 3600Гц)
OCR1B = 0x00FF; //Устанавливаем скважность 50%
OCR1A = 0xEFFF; //Значение перегрузки таймера (30гц)
TIFR = 0;
TCCR1B = (1<<CS11); //Делитель частоты на 8, запуск таймера
//======
sei();
uart_init(RXUBRR); //UART Init
MFRC522_Init(); //NFC Init
indicate(STARTDEV);

while (1)
{
_delay_ms(500);
if ( MFRC522_Request( PICC_REQIDL, card_num ) == MI_OK ) {
if ( MFRC522_Anticoll( card_num ) == MI_OK ) {
TX_ENABLE;
GREENLED_ON;
uart_puts("CN"); //Последовательность о передаче номера карты
for (i = 0; i < 5; i++ ) {
uart_putc(card_num[i]); //5 байт номера кары
}
uart_puts("\r"); //Символ конца строки
RX_ENABLE;
GREENLED_OFF;
}
}
}
}

Заметил ошибку в инициализации таймера TCCR1A = (1<<COM1B1)|(1<<COM1B0)|(1<<WGM12)|(1<<WGM11);
WGM12 находится в другом регистре. Исправил на TCCR1A = (1<<COM1B1)|(1<<COM1B0)|(1<<WGM11); TCCR1B = (1<<WGM12);
Проблема остается

ШИМ работает в 9ти битном режиме, т.е. максимальное число это 0х01FF (511). Таймер дотикивает до 511ти и сбрасывается?
Последний раз редактировалось Vergilium Ср июл 12, 2017 11:38:50, всего редактировалось 1 раз.

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

Ср июл 12, 2017 11:38:13

Код:
TIFR = 0;
Бесполезная операция, флаги сбрасываются записью 1.
Код:
OCR1A = 0xEFFF; //Значение перегрузки таймера (30гц)
В ДШ, буружуйским по белому написано, что счетчик будет считать до 0x01FF в 9битном режиме, что логично, а не до регистра OCR.
Или уменьшайте OCR1A или используйте прерывание по переполнению таймера.

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

Пт июл 14, 2017 09:19:32

Подскажите по передаче UART с помощью кольцевого буфера и на прерываниях. В принципе все работает хорошо и очень быстро. Но дело в том, что там же нужно задавать размер этого самого буфера. Это хорошо если заранее знаешь размер передаваемых данных, а если нет, то как быть? Можно как то заранее узнать количество передаваемых байт и потом задать нужный буфер?
Есть только одна мысль, сначала поместить эту строку во временный массив, пройтись циклом, пока не закончаться значения в массиве, инкрементируя при этом счетчик, вот он и будет размером буфера. Не знаю насколько это правильно.
Еще вычитал что есть функция sizeof() для массивов
Спойлер
Код:
//         UART.c

#include "usart.h"

//передающий буфер
unsigned char usartTxBuf[SIZE_BUF];
unsigned char txBufTail = 0;
unsigned char txBufHead = 0;
unsigned char txCount = 0;

void uart_init( void )            //функция инициализации UART
{
  //настройка скорости обмена
  UBRR0H = 0;
  UBRR0L = 15;  // скорость 57600 для 14 745 600
  //разрешить прием и передачу данных
  UCSR0B = (1<<TXCIE0)|(1<<TXEN0); //включаем прерывание по передаче и саму передачу
  //8 бит данных, 1 стоп бит, без контроля четности
  UCSR0C = (1<<UCSZ01) | (1<<UCSZ00);
 
}
//передача
//возвращает колличество символов передающего буфера
unsigned char USART_GetTxCount(void)
{
  return txCount; 
}

//"очищает" передающий буфер
void USART_FlushTxBuf(void)
{
  txBufTail = 0;
  txCount = 0;
  txBufHead = 0;
}

//помещает символ в буфер, инициирует начало передачи
void USART_PutChar(unsigned char sym)
{
  //если модуль usart свободен и это первый символ
  //пишем его прямо в регистр UDR
  if(((UCSR0A & (1<<UDRE0)) != 0) && (!txCount))
  {
   UDR0 = sym;
  }
  else
  {
    if (txCount < SIZE_BUF) //если в буфере еще есть место
   {   
      usartTxBuf[txBufTail] = sym; //помещаем в него символ
      txCount++;                   //инкрементируем счетчик символов
      txBufTail++;                 //и индекс хвоста буфера
      if (txBufTail == SIZE_BUF)
     {
      txBufTail = 0;
     }
    }
  }
}
 
//функция отправки строки
void USART_SendStr(char * data)
{
  unsigned char sym;
  while(*data){
    sym = *data++;
    USART_PutChar(sym);
  }
}

//обработчик прерывания по завершению передачи
ISR(USART_TX_vect)
{
  if (txCount > 0) //если буфер не пустой
  {             
    UDR0 = usartTxBuf[txBufHead]; //записываем в UDR символ из буфера
    txCount--;                   //уменьшаем счетчик символов
    txBufHead++;                 //инкрементируем индекс головы буфера
    if (txBufHead == SIZE_BUF)
   {
    USART_FlushTxBuf();
   }   
  }
}


//         USART.H


#ifndef USART_H
#define USART_H

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

//размер буфера
#define SIZE_BUF 65

void uart_init( void );
unsigned char USART_GetTxCount(void); //взять число символов передающего буфера
void USART_FlushTxBuf(void); //очистить передающий буфер
void USART_PutChar(unsigned char sym); //положить символ в буфер
void USART_SendStr(char * data); //послать строку по usart`у


#endif //USART_H

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

Пт июл 14, 2017 12:58:17

Но дело в том, что там же нужно задавать размер этого самого буфера. Это хорошо если заранее знаешь размер передаваемых данных, а если нет, то как быть?

Выбрать его больше [либо равным] длины максимально возможной посылки, и перед помещением в него новой порции данных отслеживать состояние передатчика. Но в этом случае кольцевой буфер и не нужен. Либо перед помещением новой порции данных проверять наличие там места - буфер ведь кольцевой и передатчик его "обмолачивает" пока в нём что-то есть. А дело программы - подкидывать новые данные в хвост.

Можно как то заранее узнать количество передаваемых байт и потом задать нужный буфер?

буфера разного размера -> работа с динамическим выделением памяти -> передатчик должен отслеживать работу со списком буферов - кмк, заморочно получается.

Еще вычитал что есть функция sizeof() для массивов

Это не функция - а оператор определяющий размер содержимого в момент компиляции - т.е. во время работы программы это уже будет константа.

Это теория :) была, а по коду: добавьте в USART_PutChar и USART_SendStr возвращаемое значение bool для успеха попытки помещения в буффер и проверяйте его в своей программе - если "отослалось" успешно - забыли, если нет - повторите попытку послать попозже.

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

Пт июл 14, 2017 22:13:55

Выбрать его больше [либо равным] длины максимально возможной посылки, и перед помещением в него новой порции данных отслеживать состояние передатчика. Но в этом случае кольцевой буфер и не нужен. Либо перед помещением новой порции данных проверять наличие там места - буфер ведь кольцевой и передатчик его "обмолачивает" пока в нём что-то есть. А дело программы - подкидывать новые данные в хвост.

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

Если было бы возможным задать размер буфера с запасом, то это для меня был бы отличный вариант. У меня задача для которой понадобилось два UART, нужно через определенные промежутки времени слать команды двум разным датчикам, а от одного еще и получать. Хотелось отказаться от программной реализации UART, которая довольно ресурсозатратна, вот и было решено оставить аппаратный и коммутировать одно или другое устройство . Команды разные размер их тоже разный и скорости передачи отличаются (57600 и 9600), вот в этом и сложность.

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

Сб июл 15, 2017 11:42:19

У меня видимо где-то в коде ошибка, буфер должен идеально соответствовать размеру передаваемых данных, ни больше ни меньше. Если он больше, то получается фигня, данные дублируются, теряются.

У вас буффер "хвостом" вперёд ползёт (что, впрочем, для МК безразлично). ;) А txCount - дублирующая сущность - высчитывайте количество неотправленых из разницы головы к хвосту (с учётом "заворота", конечно-же). Тем более что меняется txCount как в основном "потоке" так и в прерывании - начните с причисления его к Ордену Волатайла Приснопамятного. ;-)

У меня задача для которой понадобилось два UART, нужно через определенные промежутки времени слать команды двум разным датчикам, а от одного еще и получать. [....] вот и было решено оставить аппаратный и коммутировать одно или другое устройство.

Фишка кольцевого буфера в его асинхронной автономности - кидаем мясо в мясорубку, а котлеты с другой стороны аккуратно выпадают. Коммутация предполагает синхронизацию с тем фактом, что вся посылка к текущему адресату ушла (включая замыкающий байт!) - и не проще ли будет переключиться на обычный буффер - заряжать всегда его с нуля и не позволять преключаться пока передатчик в busy?
И да, "от одного ещё и получать" - синхронные ответы на команды? В противном случае - сами понимаете что будет. ;)

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

Сб июл 15, 2017 13:33:07

и не проще ли будет переключиться на обычный буффер

Обычный в смысле не кольцевой? А скорость будет такая же быстрая как с кольцевым?
Я просто всего пару месяцев изучаю МК так что особо не понимаю разницы.
Мне главное чтобы работало быстро. А то я сравнивал UART без прерываний (ожидание флага готовности), так отправка 60 байт занимала время МК на 8 миллисекунд, с прерываниями максимум 150 микросекунд, разница колоссальная.

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

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

Сб июл 15, 2017 14:38:30

Обычный в смысле не кольцевой? А скорость будет такая же быстрая как с кольцевым?

Если теоретизировать - то зависит от задачи. Представьте себе, что лежите вы с пулемётом на огневом рубеже и лента у пулемёта закольцована. Подполз второй номер с цинком - набил в это кольцо десяток-другой патронов, разбудил вас сапогом в бок - нажали вы на спуск и пошла стрельба пока есть чем. Это кольцевой буфер. А линейный буфер - это если у пулемёта питание магазинное - то второй номер вам боеприпасы в виде этих самых магазинов подаёт, смена которых гарантировано ведёт к задержкам в стрельбе. В кольцевом-же - набивание патронов может производиться параллельно основному процессу и не задерживать процесс истребления басурманов. Но моментальная "скорострельность" в обоих случаях одинакова. А общая "скорострельность" да - для линейного будет слегка пониже. Но задача у вас ведь не непрерывное подавление огнём, при котором такие задержки критичны, а попеременное "отоваривание" двух целей фиксированным количеством пуль - где зарядка нового "магазина" во время переноса огня с одной цели на другую, не мешает выполнению основной задачи.
А UART что из кольцевого буфера, что из линейного будет данные кушать одинаково быстро. Кольцевой - лишь способ организации безостановочной отсылки данных. А линейный проще - зарядили, запустили и ждём пока всё не уйдёт.
Я бы убрал из программы head, tail, count, заведя вместо них один index, загружал каждую посылку в буффер с нулевого индекса и с того-же нулевого индекса и начинал отсылку. Буффер размером с максимально возможную посылку. Окончание передачи - index на байте, следующем за концом отсылаемых данных && состояние передатчика не BUSY.

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

Сб июл 15, 2017 17:12:32

Спасибо большое. После такого объяснения с приведением аналогии я наконец-то понял как все работает.
Да пожалуй линейный буфер как раз для моего случая.

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

Ср авг 02, 2017 19:45:45

Да кодвижн самый лучший! Что тут говорить, по крайней мере для начинания.
Ответить