Перейти к содержанию

parovoZZ

Members
  • Постов

    169
  • Зарегистрирован

  • Посещение

Весь контент parovoZZ

  1. А зачем 2 сдвигать влево на один бит? Двойка превратиться в 4.
  2. Совсем не понимаю, что здесь происходит. Если нужно логическое сравнение, то пишется &&.
  3. не могу понять - в какой строчке реализован выход из функции? единственный return и тот закомменчен. Просьба - Си код вставлять с подсветкой Си кода. Так тяжело читать
  4. А код точно заливается в МК? Может, просто создает видимость? Я бы делал так - стер память, слил память. Убедился, что там FF везде. Если МК все равно мигает - летит в помойку.
  5. Надо очень внимательно читать даташит на предмет изменения регистров. Впрочем, даже чтение даташита иногда не гарантирует результат. Я с новенькими Attiny встрял - выяснилось, что ошибка в даташите. https://www.avrfreaks.net/forum/attiny817-spi-interrupt-flags-if-not-cleared В обработчик прерывания симулятор не заходит. Надо ставить бряку.
  6. У меня студия с такими настройками джае не компилирует) Ставлю минимальную оптимизацию. Дебаг внутрисхемный или симулятор?
  7. Ага, вместо того, чтобы взять готовый датчик за 400 рублей в корпусе, мы будем накручивать схему и в результате выйдем на ту же сумму. Двор на улице? На улице в солнечный день эти датчики могут "ослепнуть".
  8. Если нога привязана к компаратору таймера, то с регистра вывода она уже не доступна. Надо использовать принудительное сравнение в регистре компаратора. Почему 12 Гц вместо ожидаемого мигания - ну, где-то напортачено....
  9. parovoZZ

    Записываем данные во FRAM

    Как часто у вас бывало так, что необходимо хранить данные в ЭСППЗУ, но в текущем МК места не хватало? Приходится брать следующий по уровню МК, хотя во всем остальном он избыточен. В MSP430 на основе FRAM памяти это уже может быть вовсе и не проблемой. Изначально FRAM память поделена на сегменты. В старших моделях таких сегментов 3 с произвольной защитой каждого из них, а в кристалле 2433 всего два сегмента: под код программы и под информационный сегмент. Но вся прелесть в том, что мы можем сохранять свои данные не только в информационном сегменте (у нас он всего 512 байт), но и в сегменте программ. От слов к делу. Сколько будем хранить? У меня IAR с лицензионным ограничением линковщика на 8кБ. Вот давайте столько и запишем =) Подключаем обязательные файлы #include "io430.h" #include "stdint.h" Определяем константу с сохраняемым объемом: #define Write_Size 4000 Создадим массив с вышеприведенным количеством элементов. У нас же архитектура 16 бит? Вот столько и запишем в элемент массива: __persistent uint16_t EEPROM[Write_Size]; директива __persistent - это аналог __no_init с той разницей, что __no_init указывает компилятору не инициализировать переменную в области SRAM, а __persistent - во FRAM области. Инициализируем счетчик, отключаем вачдог и переводим пин со светодиодом (для визуального наблюдения) на выход и обнуляем регистр вывода: uint16_t count = 0; WDTCTL = WDTPW + WDTHOLD; P1DIR |= BIT0; P1OUT = 0; Далее идет очень хитрая штука. Называется она регистр PM5CTL0. В этом регистре есть бит LOCKLPM5, назначение которого блокировать регистры порта ввода-вывода. А такое необходимо, т.к. выход из режимов x.5 возможен только через ресет. По умолчанию этот бит установлен и вся работа с пинами заблокирована. Чтобы разблокировать, необходимо в него записать 0: PM5CTL0 &= ~LOCKLPM5; Дальше пишем цикл: while (1) { if (EEPROM[Write_Size - 1] == Write_Size - 1) { P1OUT |= BIT0; } else { SYSCFG0 = FRWPPW; for (uint16_t i = 0; i < Write_Size; i++) { EEPROM[i] = count++; } SYSCFG0 = FRWPPW | PFWP; } } Если в последнем элементе массива ожидаемое значение, то загорается светодиод, если нет, то уходим в подпрограмму записи элементов массива. Но прежде, чем его писать, необходимо разрешить запись в область FRAM памяти. Это делается с помощью записи разрешающей сигнатуры, которая определена в константе FRWPPW, в регистр SYSCFG0. По окончании записи и для защиты FRAM памяти от перезаписи необходимо поднять бит PFWP. В заголовочном файле вся эта кухня записана так: /* SYSCFG0 Control Bits */ #define PFWP_L (0x0001u) /* Program FRAM Write Protection */ #define DFWP_L (0x0002u) /* Data FRAM Write Protection */ #define FRWPPW (0xA500u) /* FRAM protection password */ Запускаем IAR, прошиваемся. Передергиваем питание и снова заходим в дебаггер с помощью кнопочки Запускаем окно Symbolic Memory, где переходим по адресу 0хС400 (можно подсмотреть в окне Watch по начальному адресу массива) или с помощью меню выбираем FRAM. Листаем область памяти до конца и видим такую картину Все наши 8000 байт записаны и лежат в недрах МК. Enjoy!
  10. parovoZZ

    То потухнет, то погаснет.

    Приехала ко мне прямо от ковбоев плата под звучным названием LAUNCHPAD. Что на ней имеется? Прежде всего, это целевой МК на основе FRAM памяти - MSP430FR2433. Также на плате присутствует программатор/отладчик. И самое вкусное - это аппаратная обвязка технологии EnergyTrace™ Technology. В какой среде писать программу? TI предлагает две "каропки" - это полностью бесплатная CCS на основе ECLIPSE, и второй вариант - IAR. Также присутствует онлайн-редактор с минимальными возможностями (необходим агент на компьютере и расширение в браузере). CCS - это крайне тормознутый монстр (по сравнению с MSVS), при этом в нем есть всё для комфортной работы, хотя несколько и непривычно после MSVS (необходимо нажимать Ctrl + Space, чтобы появились подсказки). Первая мысль после запуска IAR - да поотрубать всем головы, кто ЭТО придумал. Редактор кода убогий настолько, насколько это возможно в принципе. Это самый обыкновенный блокнот с минимальной подсветкой. Правда, можно прикрутить внешний редактор типа notepad++, но тогда все "фенечки" (которых и так нет) исчезнут (нельзя перейти к декларации переменной/функции, нет подсказок и прочее). Тем не менее, IAR очень быстро запускается и очень шустро работает. Лично я пока его и использую, несмотря на всю убогость редактора. Лицензионная политика у IAR такая - либо бесплатно всегда, но не более 8кБ, либо без ограничений, но 30 дней. MISRA в обоих случаях не доступна. Мало кто знает, но у MSP430 тоже есть свой фреймворк, который полностью повторяет "arduino". Называется он Energia. Но, т.к. я изучаю МК, а не фреймворки, пользоваться я ей не буду. Литература у TI для своих МК организована так (это не правило, но в большинстве случаев так) - есть даташит на целое семейство МК (в нашем случае это MSP430FR4xx and MSP430FR2xx family User's Guide) с общим описанием всевозможных модулей, а для отдельных МК из этого семейства прилагается ещё один даташит уже с подробным описанием (что есть и чего нет в МК описывается именно в этом даташите). Для MSP430FR2433 документ так и называется: MSP430FR2433 Mixed-Signal Microcontroller datasheet. Давайте уже поморгаем. Моргать в стиле "Hello, world" уже не интересно, поэтому будем моргать в стиле "Привет, ЗЕМЛЯНЕ!". Тем более, что у нас для этого есть аж пять таймеров со счетчиками в 16 бит. Первое, что делаем, выключаем вачдог (по-умолчанию он включен): WDTCTL = WDTPW | WDTHOLD; Затем необходимо определить как выход тот пин, который мы можем подключить к блоку сравнения таймера и на котором у нас сидит светодиод. Это первая нога первого порта: P1DIR |= BIT1; Бит BIT1 в заголовочнике определен как: #define BIT1 (0x0002) Затем нам необходимо подключить выход блока сравнения к пину, чтобы на нем генерировать ШИМ. MSP430FR2433 здесь особого простора не предоставляет - всё жестко привязано аппаратно внутри. Поэтому открываем даташит на МК и смотрим картинку: К пину 1 порта 1 (P1.1) можно подключить первый компаратор нулевого таймера А (TA0). Первое услови е мы уже выполнили - пин переключили на выход. Теперь необходимо определить альтернативную функцию для выбранного пина. Это делается с помощью пары регистров P1SEL0 и P1SEL1. В них, согласно таблицы, необходимо записать число 10 на то место в регистре, пин которого должен обладать альтернативной функцией (да, мозгодробительно))): P1SEL1 = BIT1; Если бы мы подключали выход второго компаратора таймера А к пину (а это второй пин первого порта), то писали бы так: P1SEL1 = BIT2; Теперь запускаем таймер. ШИМ-ить будем, как уже сказали, нулевым таймером Timer_A (это экземпляр типового таймера A3). Чтобы таймер заработал, ему необходимо подключить источник тактирования. Источников тактирования в MSP430 несколько: внутренние ACLK и MSCLK, а также внешние - TACLK и INCLK. Для разнообразия, мы будем использовать сразу два: ACLK и MSCLK. Первый генерирует частоту 32768 Гц, второй 1 МГц (по-умолчанию. Но возможно и перестроить). Для нулевого таймера Timer_A бы будем использовать частоту 1 МГц, поэтому подключим MSCLK. Счетчик в этом типе таймера может считать до определенного значения как вверх, так и вверх и вниз, а также только вверх до максимального совего значения (0xFFFFFFFF). Мы определим, что счетчик считает только вверх и сбрасывается при достижении значения 256. Также перед включением таймера необходимо сбросить его самого и его предделитель. За это отвечает бит TACLR. Теперь всё сказанное запишем в коде: TA0CCR0 = 256; TA0CTL = TASSEL__SMCLK | MC__UP | TACLR; Перед запуском таймера необходимо настроить компаратор. У компаратора есть несколько режимов работы. Их можно посмотреть в даташите на семейство: Среди них я выбрал режим Toggle/Reset. Это режим Mode2: TA0CCTL1 = OUTMOD_2; И определим значение регистра сравнения сразу после инициализации: TA0CCR1 = 255; Всё, таймер настроен и уже работает. Для модуляции ШИМ -а будем использовать другой таймер этого же типа - TA1. Чтобы промодулировать наш ШИМ генератор (создание эффекта плавного затухания и загорания светодиода), я буду использовать такую конструкцию, вызываемую в прерывании от первого таймера Timer_A: #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__) #pragma vector=TIMER1_A1_VECTOR __interrupt void TIMER1_A1_ISR(void) { switch(__even_in_range(TA1IV, TA1IV_TAIFG)) { case TA1IV_TAIFG: if (dir) { TA0CCR1 = count; } else { TA0CCR1 = 256 - count; } if (!count) { dir = !dir; } count --; break; default: break; } } где: volatile uint8_t count; volatile uint8_t dir; Ну и запуск таймера TA1. Отличие здесь в разрешении прерывания, источнике тактирования и отсутствие работы компараторов: TA1CCR0 = 255; TA1CTL = TASSEL__ACLK | MC__UP | TACLR | TAIE; Чтобы зря не гонять процессор между прерываниями, я его буду останавливать. Исторически так сложилось, что MSP430 предлагает богатый набор режимов энергосбережения. Я буду использовать режим остановки процессора - LPM3: __bis_SR_register(LPM3_bits | GIE); __no_operation(); // Для внутрисхемной отладки Выход из режима энергосбережения или его смена в МК MSP430 устроено несколько иначе, чем в Atmel. Суть в том, что периферийное устройство будит процессор, запускает необходимый осциллятор для своей работы, после завершения своей работы останавливается осциллятор (если он больше никому не нужен), а процессор уходит в тот режим, из которого его разбудили. И здесь каких-то телодвижений со стороны программиста совершать не требуется. Другое дело, если мы хотим совершить какие-то действия вне прерывания после пробуждения. Тогда в обработчике прерывания нам необходимо вручную сменить режим работы. Ну и вишенка: EnergyTrace™ Technology. На плате установлен ШИМ стабилизатор на основе МК из семейства MSP430. Как оно устроено и как работает - всё есть в документах от TI. Суть в том, что такая технология позволяет измерять токи вплоть до сотни наноампер с приемлемой точностью. Из минусов - работает только в режиме дебага. Впрочем, запускаем наш код и смотрим на токопотребление: Мы видим, что с ростом скважности ток падает и наоборот. Ну оно и логично. Enjoy!
  11. Поясню. 1-ый вариант. Кладем данные в буфер и ждем в цикле поднятия соответствующего флага. Как только флаг поднялся, читаем буфер и уходим из функции (или выполняем следующую инструкцию, если функция встроена). 2-ой вариант. Кладем данные в буфер, каким-либо образом отмечаем отправленную команду и уходим из функции (-//-). Далее уходим на прерывание, сбрасываем флаг, читаем буфер, каким-либо образом помечаем, что получены данные, выходим из прерывания. В основном цикле производим проверку новых данных. 2-ой вариант подлиннее будет. Разумеется, что если у нас дисплей и нам с него взять нечего(!), то 2-ой вариант в самый раз, особенно если буфер "длинный".
  12. В данном примере блокирующие, но ни что не мешает их такими не делать.) Про таймер не понял - с его прерываниями не работаем.
  13. Я запрашиваю значение регистра. Функция выглядит так: //... Передать и принять байт данных uint8_t SPI_ReadByte(uint8_t data) { uint8_t temp; while(!(SPI0.INTFLAGS & SPI_DREIF_bm)); // проверка готовности буфера SPI0.DATA = data; // помещаем данные в буфер. Флаг DREIF очищается SPI0.INTCTRL = SPI_RXCIE_bm; // разрешаем прерывание после приема байта (и только его) Sleep(SLPCTRL_SMODE_IDLE_gc); // while(!(SPI0.INTFLAGS & SPI_RXCIF_bm)); // удостоверимся, что есть непрочитанный байт в буфере приема temp = SPI0.DATA; // читаем приемный буфер. Флаг RXCIF очищается return temp; } Или предлагается работать через указатели? В главном цикле проверяем, не сдвинулся ли указатель RX данных и если да, то читаем? Но тогда программа усложниться в разы - сперва запрашиваем статус буфера трансивера, затем запрашиваем длину принятых данных и уже потом их читаем. И, кроме как ждать принятия данных на текущий запрос, других способов общения с трансивером я здесь не вижу. о потере времени между сеансами общения.
  14. У меня свободно 32х8 тактов...
  15. У меня за один рабочий цикл SPI через LUT выдаёт сразу байт цвета. Здесь же мы имеем всего один бит (нолик или единичка). Т.е мне на передачу цвета для одного светодиода необходимо обратиться к SPI всего три раза, здесь же - 24.
  16. Всем привет! В этом топике я покажу, как с помощью новейшей Attiny от Atmel/Microchip управлять светодиодами WS2812. Для начала давайте вспомним, а лучше подсмотрим в даташите на временные интервалы нуля и единицы. Где видим, что для передачи нуля нам необходим импульс длительностью от 200 и до 500 наносекунд, для передачи единицы - от 550 и до 850 наносекунд. Период же так и вообще может изменяться в широких пределах. Управлять светодиодами мы будем с помощью SPI. Но не напрямую, а через CCL. Почему? Всё просто - SPI содержит 8-ми битный регистр и как его не крути, целочисленная передача не получится - исходный бит надо будет "разбавлять" нулями либо единицами. А что если поступить наоборот - длительность исходного бита будет задавать период сигнала, а разбивать бит будем тактовой частотой SCK того же интерфейса SPI. Так мы сформируем "1", которую поймет драйвер светодиода. Да, T1H и T1L по длительности будут равны, но в параметры даташита мы укладываемся. Как же быть с нулем? Для этого мы воспользуемся помощью зрительного зала таймера TCA0. Пусть он нам разобьет тактовую частоту SCK ещё раз напополам и мы получим длительность T0H в четыре раза меньшую, чем период. Но всё равно укладываемся в даташит)) В результате манипуляций мы получим такую картину: Звиняюсь, но диаграммы отображают (не то, что там подписано....) желтая диаграмма - это RGB код для светодиодов, зеленая диаграмма - тактовая частота SCK, синий - меандр с таймера TCA0, фиолетовый - подготовленные импульсы для подачи на WS2812. Работать мы будем на тактовой частоте в 20 МГц, поэтому первое, что сделаем, отключим прескалер: CCP = CCP_IOREG_gc; CLKCTRL.MCLKCTRLB = 0; Затем подготовим блок SPI: //... Инициализация SPI inline void SPI_Init(void) { SPI_PORT.DIR |= (1<<MOSI_PIN) | (1<<SCK_PIN) | (0<<MISO_PIN) | (1<< PIN4_bp); // Все выводы, кроме MISO, выходы SPI0.CTRLA = (0<<SPI_DORD_bp) // Старший бит вперёд | (1<<SPI_MASTER_bp) // SPI мастер | (1<<SPI_CLK2X_bp) // Удвоенная скорость тактирования | SPI_PRESC_DIV64_gc // Делитель = 64 | (1<<SPI_ENABLE_bp); // Разрешаем работу SPI SPI0.CTRLB = (0<<SPI_BUFEN_bp) | (0<<SPI_BUFWR_bp) | (1<<SPI_SSD_bp) | SPI_MODE_0_gc; SPI0.DATA = 0; } //... Передать байт данных inline void SPI_WriteByte(uint8_t data) { SPI0.DATA = data; while(!(SPI0.INTFLAGS & SPI_IF_bm)); } Выставляем прескалер на 64 такта и взводим бит SPI_CLK2X, что в итоге нам даст частоту тактирования 625 кГц. Период тактирования при такой частоте будет равен 1.6 мкс - в даташит укладываемся. Далее настроим таймер: void TCA_Init(void) { TCA0.SINGLE.CTRLB = 1<<TCA_SINGLE_ALUPD_bp | // Установим бит автоматической блокировки обновления регистров компаратора (0 << TCA_SINGLE_CMP0EN_bp) | (0 << TCA_SINGLE_CMP1EN_bp) | (0 << TCA_SINGLE_CMP2EN_bp) | // пины выводов компараторов не включаем TCA_SINGLE_WGMODE_SINGLESLOPE_gc; // Режим работы таймера - односкатный ШИМ с прерыванием OVF в вершине счета } Режим работы таймера следующий - счетчик таймера считает от нуля и до значения в регистре PER, после чего самостоятельно обнуляется и цикл повторяется. Значение прескалера таймера и его режим работы я определю в отдельной функции: inline void TCA0_Mode(uint8_t mode) { TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc | // Определяем значение прескалера - 1 mode << TCA_SINGLE_ENABLE_bp; // mode = 1 - таймер включен, mode = 0 - таймер выключен } Уже в основной функции настрою регистры: TCA_Init(); TCA0.SINGLE.PER = TCA_period; TCA0.SINGLE.CMP2 = TCA_cmp2; Компаратор использую CMP2 - далее будет понятно почему. Выход компаратора устанавливается в "1" при сбросе счетчика, а сбрасывается при равенстве значений в счетном регистре и регистре компаратора. Для того, чтобы получить частоту 625 * 2 = 1250 кГц, я определю следующие значений регистров: #define TCA_period 15 #define TCA_cmp2 8 Давайте подключать таблицу LUT CCL. Для этого открываем даташит и внимательно читаем, на какой вход что можно подключить. Напомню, что нам надо завести сигналы от: -MOSI SPI -SCK SPI -WO2 Смотрим - на нулевой канал LUT мы можем подключить от SPI только сигнал SCK (скопирую из файла определений iotn817.h): /* LUT Input 0 Source Selection select */ typedef enum CCL_INSEL0_enum { CCL_INSEL0_MASK_gc = (0x00<<0), /* Masked input */ CCL_INSEL0_FEEDBACK_gc = (0x01<<0), /* Feedback input source */ CCL_INSEL0_LINK_gc = (0x02<<0), /* Linked LUT input source */ CCL_INSEL0_EVENT0_gc = (0x03<<0), /* Event input source 0 */ CCL_INSEL0_EVENT1_gc = (0x04<<0), /* Event input source 1 */ CCL_INSEL0_IO_gc = (0x05<<0), /* IO pin LUTn-IN0 input source */ CCL_INSEL0_AC0_gc = (0x06<<0), /* AC0 OUT input source */ CCL_INSEL0_TCB0_gc = (0x07<<0), /* TCB0 WO input source */ CCL_INSEL0_TCA0_gc = (0x08<<0), /* TCA0 WO0 input source */ CCL_INSEL0_TCD0_gc = (0x09<<0), /* TCD0 WOA input source */ CCL_INSEL0_USART0_gc = (0x0A<<0), /* USART0 XCK input source */ CCL_INSEL0_SPI0_gc = (0x0B<<0), /* SPI0 SCK source */ } CCL_INSEL0_t; На первый канал подключается только MOSI: /* LUT Input 1 Source Selection select */ typedef enum CCL_INSEL1_enum { CCL_INSEL1_MASK_gc = (0x00<<4), /* Masked input */ CCL_INSEL1_FEEDBACK_gc = (0x01<<4), /* Feedback input source */ CCL_INSEL1_LINK_gc = (0x02<<4), /* Linked LUT input source */ CCL_INSEL1_EVENT0_gc = (0x03<<4), /* Event input source 0 */ CCL_INSEL1_EVENT1_gc = (0x04<<4), /* Event input source 1 */ CCL_INSEL1_IO_gc = (0x05<<4), /* IO pin LUTn-N1 input source */ CCL_INSEL1_AC0_gc = (0x06<<4), /* AC0 OUT input source */ CCL_INSEL1_TCB0_gc = (0x07<<4), /* TCB0 WO input source */ CCL_INSEL1_TCA0_gc = (0x08<<4), /* TCA0 WO1 input source */ CCL_INSEL1_TCD0_gc = (0x09<<4), /* TCD0 WOB input source */ CCL_INSEL1_USART0_gc = (0x0A<<4), /* USART0 TXD input source */ CCL_INSEL1_SPI0_gc = (0x0B<<4), /* SPI0 MOSI input source */ } CCL_INSEL1_t; И на третьем канале от таймера TCA0 возможно подключить только WO2: /* LUT Input 2 Source Selection select */ typedef enum CCL_INSEL2_enum { CCL_INSEL2_MASK_gc = (0x00<<0), /* Masked input */ CCL_INSEL2_FEEDBACK_gc = (0x01<<0), /* Feedback input source */ CCL_INSEL2_LINK_gc = (0x02<<0), /* Linked LUT input source */ CCL_INSEL2_EVENT0_gc = (0x03<<0), /* Event input source 0 */ CCL_INSEL2_EVENT1_gc = (0x04<<0), /* Event input source 1 */ CCL_INSEL2_IO_gc = (0x05<<0), /* IO pin LUTn-IN2 input source */ CCL_INSEL2_AC0_gc = (0x06<<0), /* AC0 OUT input source */ CCL_INSEL2_TCB0_gc = (0x07<<0), /* TCB0 WO input source */ CCL_INSEL2_TCA0_gc = (0x08<<0), /* TCA0 WO2 input source */ CCL_INSEL2_TCD0_gc = (0x09<<0), /* TCD0 WOA input source */ CCL_INSEL2_SPI0_gc = (0x0B<<0), /* SPI0 MISO source */ } CCL_INSEL2_t; Пишем: CCL.LUT0CTRLB = CCL_INSEL0_SPI0_gc // Выбор канала SPI SCK | CCL_INSEL1_SPI0_gc; // Выбор канала SPI MOSI CCL.LUT0CTRLC = CCL_INSEL2_TCA0_gc; // Выбор канала W02 таймера TCA Ну и общие настройки, про которые уже писал: CCL.LUT0CTRLA = 0 << CCL_CLKSRC_bp // Блок CCL не тактируем | 1 << CCL_ENABLE_bp // Разрешение работы LUT0 | 1 << CCL_OUTEN_bp; // Разрешение выхода LUT0 CCL.CTRLA = 1 << CCL_ENABLE_bp // Разрешение работы CCL | 0 << CCL_RUNSTDBY_bp; // В стендбае не работаем Теперь необходимо запрограммировать таблицу истинности. Для этого смотрим на диаграмму выше и отмечаем входные состояния, при которых на выходе "1". В остальных случаях "0". У меня получилось следующее: CCL.TRUTH0 = 0xA8; // Таблица истинности Теперь запишем функцию трансляции байта на выход MOSI модуля SPI. Также не забываем, что нам необходимо останавливать таймер TCA0 по окончании пересылки байта и запускать его (со сбросом) в начале посылки, иначе он нам тут натикает совсем не то))) void SPI_out8bit(uint8_t data) { TCA0.SINGLE.CNT = 0; TCA0_Mode(start); SPI_WriteByte(data); TCA0_Mode(stop); } Всё, что нам осталось - это заслать байты в порядке очереди: SPI_out8bit(green[0]); SPI_out8bit(red[0]); SPI_out8bit(blue[0]); Где: uint8_t blue[1]; uint8_t red[1]; uint8_t green[1]; Как видите - никакого ассемблера, куча времени для расчета эффектов. Если подключить буферы в SPI (а мы это обязательно сделаем), ну или высший пилотаж - работать на прерываниях, то формировать узоры можно параллельно с пересылкой байтов в линию Также ещё можно поработать и с внешними интерфейсами. Но это уже совсем другая история. ENJOY!
  17. В новой аттине не всё так однозначно. SPI флаги (впрочем, как и АЦП-ные) при входе в прерывание сами не сбрасываются. Необходимо выполнить какое-то условие или вручную записать "1". Флаг DREIF (пустой буфер на передачу) так и вообще сбрасывается только в момент записи данных в буфер. И больше никак. Вот и как в таких условиях работать? На повестке дня - трансивер. Это значит, что будет передача всего одного байта и прием трех байт. Период передачи - пара раз в секунду. Поэтому принято решение ничего не трогать и работать "по старинке". Любой другой вариант порождает накладные расходы и увеличивает интервал общения с трансивером.
  18. Но не будем забывать про CS. Также науке известны случаи, когда на шине SPI более двух устройств. Поэтому тут без массива структур никак.
  19. RS - это что? Ну если говорить про радиотрансивер, то основной поток не структурированных данных идет при инициализации. Правда, у тех же СС1101 можно часть регистров записать одним куском данных. Вот и думай... А далее уже цикличный алгоритм - заправка FIFO, команда на посылку данных, сон, принятие квитанции и т.д. Ну то бишь создал массив с командами/данными, а потом только инкрементируешь указатель на его элементы.
  20. я такого не говорил) Наоборот - под каждый МК и свое устройство свой драйвер. Универсальность - имеется ввиду универсальность к методу передачи данных. Ничто не мешает смешать в одну кучу и команды, и данные. Вопрос в другом - такое смешение не будет являться катализатором к появлению ошибок?
  21. Ая вот тут кумекаю, кумекаю - никак не кумекается. На UART проще - закинул данные и поехал дальше. К тому же скорости на UART, как правило, в разы меньше тактовой. Для SPI думается создать структуру вида: struct SPI_data_t { uint8_t cmd; uint8_t *data; } но как быть со счетчиком байтов в пакете данных? Ведь по нему надо будет дергать пин CS. Создать двумерный массив? Первый уровень - наша структура (указатель на неё просто скользит без разбора), а второй уровень - количество байт. Сам массив будет неким кольцевым буфером. Многовато кода для прерывания. Впрочем, в новых тинях можно одно из прерываний (критичное) сделать с наивысшим приоритетом, если есть такая нужда. А SPI - он же USART - подождет.
  22. Дешевле 100 рублей в розницу со всеми налогами. А так и у MSP430 за 150 рублей DMA есть. Так быть не может. Прерывание надо чем-то спровоцировать. Скорее всего - да.
×
×
  • Создать...