Attiny817 - "Hello, world!"

Тема в разделе "Микроконтроллеры AVR", создана пользователем parovoZZ, 6 апр 2019.

  1. parovoZZ

    parovoZZ Гуру

    Наконец-то дошли руки до этой няшки. Когда почитал даташит на 600 страниц, первый вопрос был - а это точно тинька? Периферия не очень богата - два обычных 16-битных таймера, один 16-ти битный RTC таймер, один 12-битный, АЦП 10 бит, ЦАП 8 бит с кучей внутренних референсных напряжений, оконный WDT. Ну и плюшки - 3-х уровневые прерывания, система событий, безумный блок CCL. Впрочем, те, кто программирует XMEGA, ничего нового не произошло - менно от этой линейки происходят новые тиньки нулевой и первой серии. Разве что порт программирования PDI превратился в UPDI.
    Ну давайте уже помОргаем светодиодиком. У меня на руках давно купленная в ЧиДе по дешману плата Xplained Mini - на ней и буду осваивать новые восьмибиточки))) На ней присутствует светодиод на порту PC0 и кнопка на порту PC5. Будем на кнопочку давить и ожидать, что светодиодик погаснет=) Давайте писать код. Определим константы:
    Код (C++):
    uint8_t const    maskLED        = 0b00000001;
    uint8_t const    maskSwitch    = 0b00100000;
    uint8_t const    Switch        = 5;
    Теперь напишем функцию, которая читает кнопку:
    Код (C++):
    uint8_t GetSwith (PORT_t volatile *switchPort, uint8_t mask)
    {
        return (switchPort->IN & mask);
    }
    Здесь все просто - читаем регистр IN порта и по маске отсекаем всё лишнее.

    Теперь функция поджига светика:
    Код (C++):
    void SetLEDPort (PORT_t volatile *ledPort, uint8_t value, uint8_t mask)
    {                  
        ledPort->OUT = ledPort->OUT &    ~mask | value;
    }
    Здесь также по маске стираем значение порта и выставляем новое.

    Ну и главная функция
    Код (C++):
    int main(void)
    {
        PORT_t volatile *ledPort = &PORTC;
        PORT_t volatile *switchPort = &PORTC;

        ledPort->DIR = maskLED;

     
        while (1)
        {
            uint8_t value = (GetSwith(switchPort, maskSwitch) >> Switch);
            SetLEDPort(ledPort, value, maskLED);
        }
    }
    Т.к. кнопка и светодиод физически висят на разных битах, то смещением вправо на 5 бит мы значение кнопки ставим на место светодиода.
    И самое важное - для программирования необходим всего один проводок, не считая нулевого. По нему же работает внутрисхемная отладка.
    xmini_deb.png
    Красный квадратик в регистре IN - это я отпустил кнопку. Размер кода - 106 байт с оптимизацией O3.

    Скоро освоим таймер! А потом и АЦП.
    До новых ВСТРЕЧ!
     
    NikitOS, Tomasina и DetSimen нравится это.
  2. SergeiL

    SergeiL Оракул Модератор

    Ой, что делается то??? Теория переходит к практике?
    @parovoZZ, на практике, кнопку без обработки дребезга, и светодиод освоил :) !

    А я в свое время, года 4-е назад, наоборот, от ATxmega, тяжелого Atmel Studio, ASF и "чистого Си" с перешел на Ардуино. Периодически что-то небольшое для МК на Си пишу, а так под Ардуино, и всем доволен на 100%.
    Особенно если что-то под ESP писать. Не профессионально, а для себя :).
    Быстро, очень надежно, и не накладно. :) И грузится намного быстрее. :)
     
    Последнее редактирование: 6 апр 2019
    Airbus нравится это.
  3. Airbus

    Airbus Радиохулиган Модератор

    И я тоже.К кодвижен обращаюсь редко если только по нужде.А Паровоз он же профессионал он серьезные вещи робит.Начинку для ЯРСов это не хвост собачий.
     
    SergeiL нравится это.
  4. parovoZZ

    parovoZZ Гуру

    Скажи мне - чем отличается
    Код (C++):
    SPI0.CTRLA
    от
    Код (C++):
    SPI0_CTRLA
    Первое понятно - это поля структуры, второе - это классический адрес регистра. Но зачем была введена структура?
     
  5. KindMan

    KindMan Гуру

    А ledPort и switchPort как получились, чего то я не догнал
     
  6. parovoZZ

    parovoZZ Гуру

    Все регистры порта ввода/вывода определены как поля структуры PORT_t
     
  7. KindMan

    KindMan Гуру

    А где они объявлены, из инклудов только io вижу. Или это прототип?
     
  8. parovoZZ

    parovoZZ Гуру

    Вот
    Код (C++):
    typedef struct PORT_struct
    {
        register8_t DIR;  /* Data Direction */
        register8_t DIRSET;  /* Data Direction Set */
        register8_t DIRCLR;  /* Data Direction Clear */
        register8_t DIRTGL;  /* Data Direction Toggle */
        register8_t OUT;  /* Output Value */
        register8_t OUTSET;  /* Output Value Set */
        register8_t OUTCLR;  /* Output Value Clear */
        register8_t OUTTGL;  /* Output Value Toggle */
        register8_t IN;  /* Input Value */
        register8_t INTFLAGS;  /* Interrupt Flags */
        register8_t reserved_0x0A;
        register8_t reserved_0x0B;
        register8_t reserved_0x0C;
        register8_t reserved_0x0D;
        register8_t reserved_0x0E;
        register8_t reserved_0x0F;
        register8_t PIN0CTRL;  /* Pin 0 Control */
        register8_t PIN1CTRL;  /* Pin 1 Control */
        register8_t PIN2CTRL;  /* Pin 2 Control */
        register8_t PIN3CTRL;  /* Pin 3 Control */
        register8_t PIN4CTRL;  /* Pin 4 Control */
        register8_t PIN5CTRL;  /* Pin 5 Control */
        register8_t PIN6CTRL;  /* Pin 6 Control */
        register8_t PIN7CTRL;  /* Pin 7 Control */
        register8_t reserved_0x18;
        register8_t reserved_0x19;
        register8_t reserved_0x1A;
        register8_t reserved_0x1B;
        register8_t reserved_0x1C;
        register8_t reserved_0x1D;
        register8_t reserved_0x1E;
        register8_t reserved_0x1F;
    } PORT_t;
    Идея в чем - структура есть указатель адреса на физический адрес первого регистра - DIR. Смещение адреса поля структуры есть адрес реального регистра. Заданы они так:
    Код (C++):
    #define PORTA                (*(PORT_t *) 0x0400) /* I/O Ports */
    #define PORTB                (*(PORT_t *) 0x0420) /* I/O Ports */
    #define PORTC                (*(PORT_t *) 0x0440) /* I/O Ports */

    Также есть виртуальные порты ввода/вывода, которые есть "отображение" реальных портов, но в ОЗУ
    Код (C++):
    typedef struct VPORT_struct
    {
        register8_t DIR;  /* Data Direction */
        register8_t OUT;  /* Output Value */
        register8_t IN;  /* Input Value */
        register8_t INTFLAGS;  /* Interrupt Flags */
    } VPORT_t;
    Даташит утверждает, что прелесть в таких портах заключается в следующем
    Но можно обращаться и напрямую, по старинке:
    Код (C++):
    #define PORTA_DIR  _SFR_MEM8(0x0400)
    #define PORTA_DIRSET  _SFR_MEM8(0x0401)
    #define PORTA_DIRCLR  _SFR_MEM8(0x0402)
    #define PORTA_DIRTGL  _SFR_MEM8(0x0403)
    #define PORTA_OUT  _SFR_MEM8(0x0404)
    #define PORTA_OUTSET  _SFR_MEM8(0x0405)
    #define PORTA_OUTCLR  _SFR_MEM8(0x0406)
    #define PORTA_OUTTGL  _SFR_MEM8(0x0407)
    #define PORTA_IN  _SFR_MEM8(0x0408)
    #define PORTA_INTFLAGS  _SFR_MEM8(0x0409)
    #define PORTA_PIN0CTRL  _SFR_MEM8(0x0410)
    #define PORTA_PIN1CTRL  _SFR_MEM8(0x0411)
    #define PORTA_PIN2CTRL  _SFR_MEM8(0x0412)
    #define PORTA_PIN3CTRL  _SFR_MEM8(0x0413)
    #define PORTA_PIN4CTRL  _SFR_MEM8(0x0414)
    #define PORTA_PIN5CTRL  _SFR_MEM8(0x0415)
    #define PORTA_PIN6CTRL  _SFR_MEM8(0x0416)
    #define PORTA_PIN7CTRL  _SFR_MEM8(0x0417)
    Вот как-то так. В заголовочниках MSP430 примерно всё тоже самое.
     
    KindMan нравится это.