Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8

В първата част Опитах се да кажа на хоби инженерите по електроника, които са израснали от панталоните на Arduino, как и защо трябва да четат таблици с данни и друга документация за микроконтролери. Текстът се оказа голям, затова обещах да покажа практически примери в отделна статия. Е, той се нарече товарач ...

Днес ще ви покажа как да използвате таблици с данни за решаване на доста прости, но необходими задачи за много проекти на STM32 (Blue Pill) и STM8 контролери. Всички демо проекти са посветени на любимите ми светодиоди, ще ги запалим в големи количества, за които ще трябва да използваме всякакви интересни периферни устройства.

Текстът отново се оказа огромен, така че за удобство правя съдържанието:

STM32 Blue Pill: 16 светодиода с драйвер DM634
STM8: Настройка на шест PWM пина
STM8: 8 RGB светодиода на три пина, прекъсвания

Disclaimer: Не съм инженер, не претендирам да имам задълбочени познания в електрониката, статията е предназначена за аматьори като мен. Всъщност аз се смятах преди две години за целевата аудитория. Ако някой ми беше казал тогава, че не е страшно да чета таблици с данни за непознат чип, нямаше да прекарам много време в търсене на някои парчета код в Интернет и да измислям патерици с ножици и лейкопласт.

Фокусът на тази статия са таблици с данни, а не чернови, така че кодът може да не е твърде изпипан и често патерица. Самите проекти са много прости, въпреки че са подходящи за първото запознаване с нов чип.

Надявам се, че моята статия ще помогне на някой на подобен етап на гмуркане в хоби.

STM32

16 светодиода с DM634 и SPI

Малък проект, използващ Blue Pill (STM32F103C8T6) и DM634 LED драйвер. С помощта на таблици с данни ще се справим с драйвера, STM IO портовете и ще конфигурираме SPI.

DM634

Тайвански чип с 16 x 16-битови PWM изходи, може да бъде свързан във верига. По-младият 12-битов модел е известен от домашен проект лек пакет. По едно време, избирайки между DM63x и добре познатия TLC5940, се спрях на DM по няколко причини: 1) TLC на Aliexpress определено е фалшив, но този не е; 2) DM има автономен ШИМ със собствен честотен генератор; 3) може да се купи евтино в Москва и да не се чака пратка от Али. И, разбира се, беше интересно да научите как сами да управлявате чипа, а не да използвате готова библиотека. Чиповете вече са представени главно в пакета SSOP24, те са лесни за запояване на адаптера.

Тъй като производителят е тайвански, фиш към чипа е написано на китайски английски, което означава, че ще бъде забавно. Първо погледнете pinout-аPin връзка), за да разберете кой крак какво да свържете, и описание на щифтовете (Описание на щифта). 16 пина:

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
Мивка DC източници (отворен дренаж)

мивка / Изход с отворен дренаж - наличност; източник на входящ ток; изход, свързан към маса в активно състояние - светодиодите са свързани към драйвера чрез катоди. Електрически това, разбира се, не е "отворен дренаж" (отворен дренаж), но в таблиците с данни често се среща такова обозначение за изходи в режим на източване.

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
Външни резистори между REXT и GND за задаване на стойността на изходния ток

Между щифта REXT и земята е инсталиран референтен резистор, който контролира вътрешното съпротивление на изходите, вижте графиката на страница 9 от листа с данни. В DM634 това съпротивление може също да се контролира от софтуер чрез настройка на общата яркост (глобална яркост); Няма да навлизам в подробности в тази статия, просто ще сложа тук резистор от 2.2 - 3 kOhm.

За да разберете как да управлявате чипа, нека да разгледаме описанието на интерфейса на устройството:

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8

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

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
… За въвеждане на данни в устройството са необходими само три пина. Нарастващият ръб на сигнала SCLK премества данните от щифта SIN към вътрешния регистър. След като всички данни бъдат заредени, кратък висок XLAT сигнал заключва серийно прехвърлените данни във вътрешни регистри. Вътрешните регистри са врати, задействани от нивото на сигнала XLAT. Всички данни се предават първо MSB.

резе - резе / резе / резе.
Възходящ ръб е водещият фронт на импулса
Първо MSB – най-значимият (най-ляв) бит напред.
за часовникови данни – предаване на данни последователно (бит по бит).

Дума резе често се среща в документацията за чипове и се превежда по различни начини, така че за разбиране ще си позволя

малка образователна програмаLED драйверът е по същество регистър за смяна. "Shift" (превключване) в името - движение бит по бит на данните вътре в устройството: всеки нов бит, пъхнат вътре, избутва цялата верига напред пред себе си. Тъй като никой не иска да наблюдава хаотичното мигане на светодиоди по време на смяна, процесът се извършва в буферни регистри, отделени от работниците с капак (резе) е вид съблекалня, където битовете се подреждат в желаната последователност. Когато всичко е готово, затворът се отваря и битовете започват работа, замествайки предишната партида. Слово резе в документацията за микросхеми почти винаги предполага такъв амортисьор, в каквито и комбинации да се използва.

И така, прехвърлянето на данни към DM634 се извършва по следния начин: задайте DAI входа на стойността на високия бит на далечния светодиод, издърпайте DCK нагоре и надолу; задайте DAI входа на стойността на следващия бит, издърпайте DCK; и така нататък, докато всички битове бъдат предадени (часовник), след което издърпваме LAT. Това може да стане ръчномалко взрив), но е по-добре да използвате специално изострения SPI интерфейс за това, тъй като той е представен на нашия STM32 в две копия.

Син таблет STM32F103

Въведение: Контролерите STM32 са много по-сложни от Atmega328, отколкото могат да бъдат плашещи. В същото време, от съображения за пестене на енергия, почти всички периферни устройства са деактивирани в началото, а тактовата честота е 8 MHz от вътрешен източник. За щастие програмистите на STM написаха код, който довежда чипа до „изчислените“ 72 MHz, и авторите на всички IDE, които познавам, го включиха в процедурата за инициализация, така че не е необходимо да часовник (но можеш, ако наистина искаш). Но трябва да включите периферията.

Документация: Популярният чип STM32F103C8T6 е инсталиран на Blue Pill, има два полезни документа за него:

В листа с данни може да се интересуваме от:

  • Pinouts - чипове пинаути - в случай, че решим сами да си правим платки;
  • Карта на паметта - карта на паметта за определен чип. Справочникът има карта за цялата линия, споменава регистри, които не са на нашия.
  • Таблица с дефиниции на щифтове - списък на основните и алтернативни функции на щифта; за „синьото хапче“ в интернет можете да намерите по-удобни снимки със списък на щифтовете и техните функции. Затова незабавно търсим в Google Pinout на Blue Pill и държим тази снимка под ръка:

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
NB: имаше грешка в снимката от интернет, забелязана в коментарите, за което благодаря. Картината е сменена, но това е урок - по-добре е да проверявате информацията, а не от таблиците с данни.

Премахваме листа с данни, отваряме Справочното ръководство, оттук нататък използваме само него.
Процедура: работа със стандартен вход / изход, конфигуриране на SPI, включване на необходимите периферни устройства.

Вход изход

При Atmega328 I/O е изключително прост, поради което изобилието от STM32 опции може да бъде объркващо. Сега имаме нужда само от изводи, но дори има четири варианта:

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
Изход с отворен дренаж, изход Push-Pull, алтернативен Push-Pull, алтернативен отворен дренаж

"Дърпам бутам" (Натисни Дръпни) - обичайният изход от Arduino, щифтът може да бъде HIGH или LOW. Но с "отворен дренаж" възникват трудности, въпреки че всъщност всичко е просто тук:

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
Конфигурация на изхода / когато портът е присвоен на изхода: / изходният буфер е активиран: / – отворен режим на източване: "0" в изходния регистър разрешава N-MOS, "1" в изходния регистър оставя порта в режим Hi-Z (P -MOS не е активиран ) / - режим на натискане и изтегляне: "0" в изходния регистър активира N-MOS, "1" в изходния регистър активира P-MOS.

Цялата разлика в отворения дренаж (отворен дренаж) от "бутане-дърпане" (Натисни Дръпни) е, че в първия щифт не може да приеме състояние HIGH: когато единица бъде записана в изходния регистър, тя преминава в режим на високо съпротивление (висок импеданс, Здравей-Z). При писане на нула щифтът и в двата режима се държи еднакво, както логически, така и електрически.

В нормален изходен режим щифтът просто превежда съдържанието на изходния регистър. В "алтернатива" се управлява от съответната периферия (вижте 9.1.4):

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
Ако битът на порта е конфигуриран като изход за алтернативна функция, изходният регистър е деактивиран и щифтът е свързан към изходния сигнал на периферното устройство.

Алтернативната функционалност на всеки щифт е описана в Определения на ПИН Листът с данни е на изтеглената снимка. На въпроса какво да направите, ако щифтът има няколко алтернативни функции, отговорът е даден от бележка под линия в листа с данни:

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
Ако множество периферни устройства използват един и същ щифт, за да се избегне конфликт между алтернативни функции, трябва да се използва само едно периферно устройство в даден момент, като се превключва с помощта на бита за активиране на часовника на периферията (в съответния RCC регистър).

И накрая, щифтовете в изходен режим също имат тактова честота. Това е друга функция за пестене на енергия, в нашия случай просто я настройваме на максимум и я забравяме.

И така: ние използваме SPI, което означава, че два пина (с данни и с тактов сигнал) трябва да бъдат „алтернативна функция за натискане и издърпване“, а още един (LAT) трябва да бъде „нормална натискане и издърпване“. Но преди да ги присвоите, нека се заемем със SPI.

SPI

Още един малък хак

SPI или Serial Peripheral Interface (сериен периферен интерфейс) е прост и много ефективен интерфейс за комуникация на MK с други MK и външния свят като цяло. Принципът на неговата работа вече е описан по-горе, където за китайския LED драйвер (вижте раздел 25 в справочното ръководство). SPI може да работи в главен ("master") и подчинен ("slave") режими. SPI има четири основни канала, от които не всички може да са включени:

  • MOSI, главен изход / подчинен вход: този щифт изпраща данни в главен режим и получава данни в подчинен режим;
  • MISO, Master Input / Slave Output: напротив, в главния получава, в подчинения дава;
  • SCK, сериен часовник: задава честотата на предаване на данни в главния или получава часовников сигнал в подчинения. По същество бие бийтовете;
  • SS, Slave Select: с този канал робът знае, че искат нещо от него. На STM32 се нарича NSS, където N = отрицателно, т.е. контролерът става подчинен, ако този канал има заземяване. Комбинира се добре с режима Open Drain Output, но това е друга история.

Както всичко останало, SPI на STM32 е богат на функционалност, което го прави малко труден за разбиране. Например, той може да работи не само със SPI, но и с I2S интерфейс и техните описания са смесени в документацията, трябва да отрежете излишъка своевременно. Нашата задача е изключително проста: просто трябва да предоставите данни, като използвате само MOSI и SCK. Отиваме на раздел 25.3.4 (полудуплексна комуникация, полудуплексна комуникация), където намираме 1 часовник и 1 еднопосочен проводник за данни (1 часовник и 1 еднопосочен поток от данни):

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
В този режим приложението използва SPI в режим само за предаване или само за получаване. / Режимът само за предаване е подобен на дуплексния режим: данните се предават на щифта за предаване (MOSI в главен режим или MISO в подчинен режим), докато щифтът за получаване (съответно MISO или MOSI) може да се използва като нормален I/O карфица. В този случай е достатъчно приложението да игнорира Rx буфера (ако е прочетен, няма да има предавани данни).

Страхотно, щифтът MISO е свободен, нека свържем LAT сигнала към него. Нека се заемем със Slave Select, който може да се управлява програмно на STM32, което е изключително удобно. Четем параграфа със същото име в раздел 25.3.1 Общо описание на SPI:

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
Софтуерно управление на NSS (SSM = 1) / информация за избор на подчинен се съдържа в бита SSI на регистъра SPI_CR1. Външният NSS щифт е оставен свободен за други нужди на приложението.

Време е да пишете в регистрите. Реших да използвам SPI2, търсим основния му адрес в листа с данни - в раздел 3.3 Карта на паметта (Карта на паметта):

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8

Е, да започнем:

#define _SPI2_(mem_offset) (*(volatile uint32_t *)(0x40003800 + (mem_offset)))

Отваряме раздел 25.3.3 с показателното заглавие „Конфигуриране на SPI в главен режим“:

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8

1. Задайте часовника на серийния интерфейс с битовете BR[2:0] в регистъра SPI_CR1.

Регистрите са събрани в едноименния раздел на справочното ръководство. Промяна на адреса (отместване на адреса) CR1 има 0x00, по подразбиране всички битове са изчистени (Нулиране на стойността 0x0000):

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8

BR битовете задават делителя на часовника на контролера, като по този начин определят честотата, на която ще работи SPI. Честотата на STM32 ще бъде 72 MHz, светодиодният драйвер, според неговия лист с данни, работи на честота до 25 MHz, така че трябва да разделим на четири (BR[2:0] = 001).

#define _SPI_CR1 0x00

#define BR_0        0x0008
#define BR_1        0x0010
#define BR_2        0x0020

_SPI2_ (_SPI_CR1) |= BR_0;// pclk/4

2. Задайте битовете CPOL и CPHA, за да определите връзката между трансфера на данни и часовника на серийния интерфейс (вижте диаграмата на страница 240)

Тъй като тук четем лист с данни и не разглеждаме схеми, нека разгледаме по-отблизо текстовото описание на битовете CPOL и CPHA на страница 704 (Общо описание на SPI):

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
Фаза и полярност на часовника
Използвайки битовете CPOL и CPHA на регистъра SPI_CR1, можете програмно да изберете четири опции за времеви съотношения. Битът CPOL (Clock Polarity) контролира състоянието на тактовия сигнал, когато не се предават данни. Този бит контролира главния и подчинения режим. Ако CPOL е нулиран, щифтът SCK е нисък в покой. Ако битът CPOL е зададен, SCK щифтът е висок, когато е неактивен.
Ако битът CPHA (тактова фаза) е зададен, стробът на прихващане на MSB е вторият фронт на SCK сигнала (намаляващ, ако CPOL е изчистен, или нарастващ фронт, ако е зададен CPOL). Данните се фиксират при втората смяна на часовника. Ако CPHA битът е изчистен, нарастващият фронт на SCK сигнала (спадащ фронт, ако CPOL е зададен, или нарастващ фронт, ако CPOL е изчистен) служи като строб за улавяне на високи битове. Данните се заключват при първата смяна на часовника.

Вкусвайки това знание, стигаме до извода, че и двата бита трябва да останат нула, защото искаме SCK сигналът да остане нисък, когато не се използва, и данните да се предават по нарастващия фронт на импулса (вижте фиг. нарастващ ръб в лист с данни DM634).

Между другото, тук за първи път срещнахме характеристика на речника в ST таблиците с данни: в тях е написана фразата „нулиране на бита до нула“ да нулирам малкоИ не да изчистя малко, като например Atmega.

3. Задайте DFF бита, за да определите 8-битовия или 16-битовия формат на блока с данни

Специално взех 16-битовия DM634, за да не се занимавам с прехвърлянето на 12-битови PWM данни, като DM633. DFF има смисъл да се постави в единство:

#define DFF         0x0800

_SPI2_ (_SPI_CR1) |= DFF; // 16-bit mode

4. Конфигурирайте бита LSBFIRST в регистъра SPI_CR1, за да дефинирате блоковия формат

LSBFIRST, както подсказва името му, настройва предаването с най-малкия бит първи. Но DM634 иска първо да получи MSB данни. Затова го оставяме нулиран.

5. В хардуерен режим, ако се изисква вход от NSS пина, задвижете NSS пина високо по време на цялата последователност на прехвърляне на байтове. В програмен режим на NSS задайте битовете SSM и SSI в регистъра SPI_CR1. Ако щифтът на NSS трябва да бъде изведен, трябва да се зададе само битът SSOE.

Инсталирайте SSM и SSI, за да забравите за хардуерния режим на NSS:

#define SSI         0x0100
#define SSM         0x0200

_SPI2_ (_SPI_CR1) |= SSM | SSI; //enable software control of SS, SS high

6. Битовете MSTR и SPE трябва да бъдат зададени (те остават зададени само ако NSS е висок)

Всъщност с тези битове присвояваме нашия SPI като главен и го включваме:

#define MSTR        0x0004
#define SPE         0x0040

_SPI2_ (_SPI_CR1) |= MSTR; //SPI master
//когда все готово, включаем SPI
_SPI2_ (_SPI_CR1) |= SPE;

SPI е конфигуриран, нека веднага напишем функции, които изпращат байтове към драйвера. Продължете да четете 25.3.3 „Конфигуриране на SPI в главен режим“:

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
Процедура за прехвърляне на данни
Трансферът започва, когато байт бъде записан в Tx буфера.
Байтът с данни се зарежда в регистъра за смяна на паралелен режим (от вътрешната шина) по време на предаването на първия бит, след което се предава в последователен MOSI pin режим, първи или последен бит напред в зависимост от настройката на бита LSBFIRST в регистъра CPI_CR1. Флагът TXE се задава след предаване на данни от Tx буфер към преместващ регистъри се генерира прекъсване, ако битът TXEIE в регистъра CPI_CR1 е зададен.

Подчертах няколко думи в превода, за да привлека вниманието към една особеност на внедряването на SPI в STM контролери. На Atmega флагът TXE (Tx празен, Tx е празен и готов за получаване на данни) се задава само след като целият байт е изпратен навън. И тук този флаг е зададен, след като байтът е бил пъхнат във вътрешния регистър за преместване. Тъй като се пъхва там с всички битове едновременно (паралелно) и след това данните се предават последователно, TXE се задава преди байтът да бъде напълно изпратен. Това е важно, защото в случай на нашия LED драйвер, трябва да издърпаме LAT щифта след изпращане всички данни, т.е. само флагът TXE няма да ни е достатъчен.

Което означава, че имаме нужда от друго знаме. Нека да разгледаме 25.3.7 - "Флагове за състояние":

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
<…>
Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
Флаг ЗАЕТ
Флагът BSY се задава и изчиства от хардуера (записването в него няма ефект). Флагът BSY показва състоянието на SPI комуникационния слой.
Нулира се:
когато прехвърлянето приключи (освен в главен режим, ако прехвърлянето е непрекъснато)
когато SPI е деактивиран
когато възникне грешка в главния режим (MODF=1)
Ако предаването не е непрекъснато, флагът BSY се изчиства между всяко предаване на данни.

Добре, ще ми е от полза. Разберете къде се намира Tx буферът. За да направите това, прочетете "SPI Data Register":

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
Битове 15:0 DR[15:0] регистър на данните
Получени данни или данни за предаване.
Регистърът на данните е разделен на два буфера, един за запис (буфер за предаване) и един за четене (буфер за получаване). Записът в регистъра на данните записва в Tx буфера, а четенето от регистъра на данните ще върне стойността, съдържаща се в Rx буфера.

Е, регистърът на състоянието, където има флагове TXE и BSY:

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8

Ние пишем:

#define _SPI_DR  0x0C
#define _SPI_SR  0x08

#define BSY         0x0080
#define TXE         0x0002

void dm_shift16(uint16_t value)
{
    _SPI2_(_SPI_DR) = value; //send 2 bytes
    while (!(_SPI2_(_SPI_SR) & TXE)); //wait until they're sent
}

Е, тъй като трябва да прехвърлим 16 пъти по два байта, според броя на изходите на LED драйвера, нещо подобно:

void sendLEDdata()
{
    LAT_low();
    uint8_t k = 16;
    do
    {   k--;
        dm_shift16(leds[k]);
    } while (k);

    while (_SPI2_(_SPI_SR) & BSY); // finish transmission

    LAT_pulse();
}

Но все още не знаем как да издърпаме LAT щифта, така че нека се върнем към I/O.

Задайте щифтове

В STM32F1 регистрите, отговорни за състоянието на щифтовете, са доста необичайни. Ясно е, че има повече от Atmega, но те също са различни от другите STM чипове. Раздел 9.1 Общо описание на GPIO:

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
Всеки от I/O портовете с общо предназначение (GPIO) има два 32-битови конфигурационни регистъра (GPIOx_CRL и GPIOx_CRH), два 32-битови регистъра за данни (GPIOx_IDR и GPIOx_ODR), 32-битов регистър за настройка/нулиране (GPIOx_BSRR), 16-битов регистър за нулиране (GPIOx_BRR) и 32- регистър за блокиране на битове (GPIOx_LCKR).

Необичайни, а също и доста неудобни, са първите два регистъра тук, защото 16-те пина на порта са разпръснати по тях във формат "четири бита на брат". Тези. щифтове от XNUMX до XNUMX са в CRL, а останалите са в CRH. В същото време, останалите регистри успешно пасват на битовете на всички щифтове на порта - често оставайки наполовина "запазени".

За по-лесно нека започнем от края на списъка.

Нямаме нужда от блокиращ регистър.

Регистрите за настройка и нулиране са доста забавни, тъй като те частично се дублират един друг: можете да запишете всичко само в BSRR, където горните 16 бита ще нулират щифта на нула, а долните ще бъдат зададени на 1, или можете също използвайте BRR, по-малките 16 бита от които само нулират щифта. Харесвам втория вариант. Тези регистри са важни, защото осигуряват атомен достъп до щифтовете:

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
Atomic инсталиране или нулиране
Не е необходимо да забранявате прекъсванията, когато програмирате GPIOx_ODR на битово ниво: можете да промените един или повече бита с една операция за атомно записване на APB2. Това се постига чрез записване на "1" в регистъра за настройка/нулиране (GPIOx_BSRR или, само за нулиране, GPIOx_BRR) на бита, който трябва да се промени. Останалите битове ще останат непроменени.

Регистрите на данни имат доста красноречиви имена - IDR = Вход Регистър на посоката, входен регистър; ODR= Продукция Регистър на посоката, изходен регистър. В настоящия проект нямаме нужда от тях.

И накрая контролните регистри. Тъй като се интересуваме от щифтовете на втория SPI, а именно PB13, PB14 и PB15, веднага разглеждаме CRH:

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8

И виждаме, че ще е необходимо да напишем нещо в битове от 20-ти до 31-ви.

Вече разбрахме какво искаме от щифтовете по-горе, така че тук ще направя без екранна снимка, просто кажете, че MODE задава посоката (въвежда се, ако и двата бита са зададени на 0) и скоростта на щифта (имаме нужда от 50MHz, т.е. и двата щифта на "1"), а CNF задава режима: нормален "натискане-натискане" - 00, "алтернатива" - ​​10. По подразбиране, както виждаме по-горе, всички щифтове имат третия бит отдолу ( CNF0), той ги настройва в режим плаващ вход.

Тъй като планирам да направя нещо друго с този чип, за простота, като цяло дефинирах всички възможни стойности на MODE и CNF както за долния, така и за горния контролен регистър.

Някак си така

#define CNF0_0 0x00000004
#define CNF0_1 0x00000008
#define CNF1_0 0x00000040
#define CNF1_1 0x00000080
#define CNF2_0 0x00000400
#define CNF2_1 0x00000800
#define CNF3_0 0x00004000
#define CNF3_1 0x00008000
#define CNF4_0 0x00040000
#define CNF4_1 0x00080000
#define CNF5_0 0x00400000
#define CNF5_1 0x00800000
#define CNF6_0 0x04000000
#define CNF6_1 0x08000000
#define CNF7_0 0x40000000
#define CNF7_1 0x80000000
#define CNF8_0 0x00000004
#define CNF8_1 0x00000008
#define CNF9_0 0x00000040
#define CNF9_1 0x00000080
#define CNF10_0 0x00000400
#define CNF10_1 0x00000800
#define CNF11_0 0x00004000
#define CNF11_1 0x00008000
#define CNF12_0 0x00040000
#define CNF12_1 0x00080000
#define CNF13_0 0x00400000
#define CNF13_1 0x00800000
#define CNF14_0 0x04000000
#define CNF14_1 0x08000000
#define CNF15_0 0x40000000
#define CNF15_1 0x80000000

#define MODE0_0 0x00000001
#define MODE0_1 0x00000002
#define MODE1_0 0x00000010
#define MODE1_1 0x00000020
#define MODE2_0 0x00000100
#define MODE2_1 0x00000200
#define MODE3_0 0x00001000
#define MODE3_1 0x00002000
#define MODE4_0 0x00010000
#define MODE4_1 0x00020000
#define MODE5_0 0x00100000
#define MODE5_1 0x00200000
#define MODE6_0 0x01000000
#define MODE6_1 0x02000000
#define MODE7_0 0x10000000
#define MODE7_1 0x20000000
#define MODE8_0 0x00000001
#define MODE8_1 0x00000002
#define MODE9_0 0x00000010
#define MODE9_1 0x00000020
#define MODE10_0 0x00000100
#define MODE10_1 0x00000200
#define MODE11_0 0x00001000
#define MODE11_1 0x00002000
#define MODE12_0 0x00010000
#define MODE12_1 0x00020000
#define MODE13_0 0x00100000
#define MODE13_1 0x00200000
#define MODE14_0 0x01000000
#define MODE14_1 0x02000000
#define MODE15_0 0x10000000
#define MODE15_1 0x20000000

Нашите щифтове са на порт B (базов адрес - 0x40010C00), код:

#define _PORTB_(mem_offset) (*(volatile uint32_t *)(0x40010C00 + (mem_offset)))

#define _BRR  0x14
#define _BSRR 0x10
#define _CRL  0x00
#define _CRH  0x04

//используем стандартный SPI2: MOSI на B15, CLK на B13
//LAT пусть будет на неиспользуемом MISO – B14

//очищаем дефолтный бит, он нам точно не нужен
_PORTB_ (_CRH) &= ~(CNF15_0 | CNF14_0 | CNF13_0 | CNF12_0);

//альтернативные функции для MOSI и SCK
_PORTB_ (_CRH) |= CNF15_1 | CNF13_1;

//50 МГц, MODE = 11
_PORTB_ (_CRH) |= MODE15_1 | MODE15_0 | MODE14_1 | MODE14_0 | MODE13_1 | MODE13_0;

И съответно можете да напишете дефиниции за LAT, които ще потрепват регистрите BRR и BSRR:

/*** LAT pulse – high, then low */
#define LAT_pulse() _PORTB_(_BSRR) = (1<<14); _PORTB_(_BRR) = (1<<14)

#define LAT_low() _PORTB_(_BRR) = (1<<14)

(LAT_low просто по инерция, някак винаги е било така, нека си остане за вас)

Сега всичко е супер, просто не става. Тъй като това е STM32, тук пестят електричество, което означава, че трябва да включите клокването на необходимата периферия.

Включете часовника

Часовникът е отговорен за часовника, те също са часовник. И вече можехме да забележим съкращението RCC. Търсим го в документацията: това е Reset and Clock Control (Управление на нулиране и часовник).

Както споменахме по-горе, за щастие хората от STM ни направиха най-трудната част от темата за клокването, за което много им благодаря (пак ще дам линк към Уебсайтът на Di Haltза да стане ясно колко е объркан). Нуждаем се само от регистри, отговорни за активирането на периферно тактоване (Peripheral Clock Enable Registers). Първо, нека намерим основния адрес на RCC, той е в самото начало на "Карта с памет":

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8

#define _RCC_(mem_offset) (*(volatile uint32_t *)(0x40021000 + (mem_offset)))

И след това или кликнете върху връзката, където да опитате да намерите нещо в таблицата, или, много по-добре, прегледайте описанията на включващите регистри от разделите за активирайте регистрите. Къде намираме RCC_APB1ENR и RCC_APB2ENR:

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8

И в тях, съответно, битове, които включват клокването на SPI2, IOPB (I / O порт B) и алтернативни функции (AFIO).

#define _APB2ENR 0x18
#define _APB1ENR 0x1C

#define IOPBEN 0x0008
#define SPI2EN 0x4000
#define AFIOEN 0x0001

//включаем тактирование порта B и альт. функций
_RCC_(_APB2ENR) |= IOPBEN | AFIOEN;

//включаем  тактирование SPI2
_RCC_(_APB1ENR) |= SPI2EN;

Крайният код може да бъде намерен тук.

Ако има възможност и желание за тестване, тогава свързваме DM634 така: DAI към PB15, DCK към PB13, LAT към PB14. Захранваме водача от 5 волта, не забравяйте да комбинирате основанията.

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8

STM8 PWM

ШИМ на STM8

Когато току-що планирах тази статия, реших например да се опитам да овладея някои функции на непознат чип само с помощта на лист с данни, така че обущар да не излезе без ботуши. STM8 беше идеален за тази роля: първо, имах няколко китайски дъски с STM8S103, и второ, не е много популярен и следователно изкушението да четеш и да намериш решение в Интернет почива на липсата на същите тези решения.

Чипът също има фиш и справочно ръководство RM0016, в първия pinout и регистър адреси, във втория - всичко останало. Програмиране на STM8 в C в грозна IDE ST Visual Develop.

Тактиране и I/O

По подразбиране STM8 работи на честота от 2 MHz, това трябва да се коригира незабавно.

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
HSI часовник (висок вътрешен)
Часовникът HSI се получава от вътрешен 16 MHz RC осцилатор с програмируем делител (1 до 8). Задава се в регистъра на делителя на часовника (CLK_CKDIVR).
Забележка: HSI RC осцилатор с делител 8 е избран като източник на главен часовник при стартиране.

Намираме адреса на регистъра в листа с данни, описанието в refman и виждаме, че регистърът трябва да бъде изчистен:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Тъй като ще стартираме ШИМ и ще свържем светодиоди, нека да разгледаме разводката:

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8

Чипът е малък, много функции са окачени на едни и същи щифтове. Това, което е в квадратни скоби, е „алтернативна функционалност“, превключва се от „опционални байтове“ (опция байтове) - нещо като предпазители Atmega. Можете да промените техните стойности програмно, но не е необходимо, т.к. Новата функционалност се активира само след рестартиране. По-лесно е да използвате ST Visual Programmer (изтеглен с Visual Develop), който може да променя тези байтове. pinout показва, че изходите CH1 и CH2 на първия таймер са скрити в квадратни скоби; необходимо е да зададете битовете AFR1 и AFR0 в STVP, а вторият също ще прехвърли изхода CH1 на втория таймер от PD4 към PC5.

Така 6 пина ще управляват светодиодите: PC6, PC7 и PC3 за първия таймер, PC5, PD3 и PA3 за втория.

Настройването на самите I/O пинове на STM8 е по-просто и по-логично, отколкото на STM32:

  • Atmega-познат регистър за посока на данни DDR (Регистър за насочване на данни): 1 = изход;
  • първият контролен регистър CR1, когато е изведен, задава режима на издърпване (1) или отворен дренаж (0); тъй като свързвам светодиодите към чипа с катоди, оставям нули тук;
  • вторият управляващ регистър CR2 ​​задава тактовата честота при извеждане: 1 = 10 MHz

#define PA_DDR     *(volatile uint8_t *)0x005002
#define PA_CR2     *(volatile uint8_t *)0x005004
#define PD_DDR     *(volatile uint8_t *)0x005011
#define PD_CR2     *(volatile uint8_t *)0x005013
#define PC_DDR     *(volatile uint8_t *)0x00500C
#define PC_CR2     *(volatile uint8_t *)0x00500E

PA_DDR = (1<<3); //output
PA_CR2 |= (1<<3); //fast
PD_DDR = (1<<3); //output
PD_CR2 |= (1<<3); //fast
PC_DDR = ((1<<3) | (1<<5) | (1<<6) | (1<<7)); //output
PC_CR2 |= ((1<<3) | (1<<5) | (1<<6) | (1<<7)); //fast

Настройка на ШИМ

Първо, нека дефинираме термините:

  • PWM честота – честота, с която тиктака таймерът;
  • Автоматично презареждане, AR – автоматично заредена стойност, до която ще брои таймерът (период на импулса);
  • Събитие за актуализиране, UEV – събитие, което се случва, когато таймерът е отброил до AR;
  • Работен цикъл на ШИМ - PWM работен цикъл, често наричан "работен цикъл";
  • Улавяне/Сравняване на стойност – стойност за заснемане/сравняване, броене до която таймерът ще направи нещо (в случай на ШИМ инвертира изходния сигнал);
  • стойност на предварително натоварване – предварително заредена стойност. сравни стойност не може да се промени, докато таймерът тиктака, в противен случай ШИМ цикълът ще се прекъсне. Следователно новите предадени стойности се поставят в буфера и се изтеглят, когато таймерът достигне края на обратното броене и се нулира;
  • Подравнени по ръбовете и Централно подравнени режими – подравняване на границата и в центъра, същото като atmelovskie Бърз ШИМ и Фазово правилен ШИМ.
  • OCiREF, референтен сигнал за сравнение на изхода - референтният изходен сигнал, всъщност това, което се появява на съответния щифт в режим PWM.

Както вече става ясно от pinout, два таймера имат PWM възможности - първият и вторият. И двата са 16-битови, като първият има много допълнителни функции (по-специално може да брои нагоре и надолу). Трябва и двата да работят еднакво, затова реших да започна с очевидно по-бедния втори, за да не използвам случайно нещо, което го няма в него. Известен проблем е, че описанието на функционалността на PWM на всички таймери в справочното ръководство е в главата за първия таймер (17.5.7 Режим PWM), така че трябва да прескачате напред-назад през документа през цялото време.

PWM на STM8 има важно предимство пред Atmega PWM:

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
PWM с подравняване на ръба
Конфигуриране на акаунта отдолу нагоре
Преброяването нагоре е активно, ако битът DIR в регистъра TIM_CR1 е изчистен
Пример
Примерът използва първия ШИМ режим. Референтният PWM сигнал OCiREF се поддържа висок, докато TIM1_CNT < TIM1_CCRi. В противен случай е необходимо ниско ниво. Ако стойността за сравнение в регистъра TIM1_CCRi е по-голяма от стойността за автоматично зареждане (регистър TIM1_ARR), сигналът OCiREF се задържа на 1. Ако сравнителната стойност е 0, OCiREF се поддържа на нула....

STM8 таймер по време на събитие за актуализиране първо проверява сравни стойност, и едва след това произвежда референтен сигнал. В Atmega таймерът първо трепти и след това сравнява, в резултат на което кога compare value == 0 изходът е игла, с която трябва да се работи по някакъв начин (например чрез програмно обръщане на логиката).

И така, какво искаме да направим: 8-битова ШИМ (AR == 255), като се брои отдолу нагоре, подравняване по границата. Тъй като крушките са свързани към чипа чрез катоди, ШИМ трябва да извежда 0 (LED свети), докато сравни стойност и 1 след.

Вече сме чели за някои PWM режим, така че намираме желания регистър на втория таймер, като търсим в справочното ръководство за тази фраза (18.6.8 - TIMx_CCMR1):

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
110: Първи ШИМ режим - при броене отдолу нагоре, първият канал е активен, докато TIMx_CNT < TIMx_CCR1. В противен случай първият канал е неактивен. [по-нататък в документа, грешно копиране-поставяне от таймер 1] 111: Втори режим на ШИМ - при броене отдолу нагоре, първият канал е неактивен, докато TIMx_CNT < TIMx_CCR1. В противен случай първият канал е активен.

Тъй като светодиодите са свързани към MK с катоди, вторият режим ни подхожда (първият също, но все още не знаем това).

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
Бит 3 OC1PE: Разрешаване на предварително зареждане на изход 1
0: Регистърът за предварително зареждане на TIMx_CCR1 е деактивиран. Можете да пишете на TIMx_CCR1 по всяко време. Новата стойност работи веднага.
1: Регистърът за предварително зареждане на TIMx_CCR1 е активиран. Операциите за четене/запис имат достъп до регистъра за предварително зареждане. Предварително заредената стойност на TIMx_CCR1 се зарежда в скрития регистър по време на всяко събитие за актуализиране.
*Забележка: Регистрите за предварително зареждане трябва да са активирани, за да може режимът PWM да работи правилно. Това не е задължително в режим на единичен сигнал (битът OPM е зададен в регистъра TIMx_CR1).

Добре, включете всичко необходимо за трите канала на втория таймер:

#define TIM2_CCMR1 *(volatile uint8_t *)0x005307
#define TIM2_CCMR2 *(volatile uint8_t *)0x005308
#define TIM2_CCMR3 *(volatile uint8_t *)0x005309

#define PWM_MODE2   0x70 //PWM mode 2, 0b01110000
#define OCxPE       0x08 //preload enable

TIM2_CCMR1 = (PWM_MODE2 | OCxPE);
TIM2_CCMR2 = (PWM_MODE2 | OCxPE);
TIM2_CCMR3 = (PWM_MODE2 | OCxPE);

AR се състои от два осембитови регистъра, тук всичко е просто:

#define TIM2_ARRH  *(volatile uint8_t *)0x00530F
#define TIM2_ARRL  *(volatile uint8_t *)0x005310

TIM2_ARRH = 0;
TIM2_ARRL = 255;

Вторият таймер може да брои само отдолу нагоре, подравняване на границата, нищо не трябва да се променя. Задайте честотния делител например на 256. За втория таймер делителят е зададен в регистъра TIM2_PSCR и е степен на две:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

Остава да включите заключенията и самия втори таймер. Първата задача се решава с регистри Заснемане/Сравнение Разреши: има два от тях, три канала са разпръснати асиметрично върху тях. Тук също можем да научим, че е възможно да променим полярността на сигнала, т.е. по принцип може да се използва и PWM Mode 1. Пишем:

#define TIM2_CCER1 *(volatile uint8_t *)0x00530A
#define TIM2_CCER2 *(volatile uint8_t *)0x00530B

#define CC1E  (1<<0) // CCER1
#define CC2E  (1<<4) // CCER1
#define CC3E  (1<<0) // CCER2

TIM2_CCER1 = (CC1E | CC2E);
TIM2_CCER2 = CC3E;

И накрая, стартираме таймера в регистъра TIMx_CR1:

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Нека напишем прост аналог на AnalogWrite (), който ще предаде действителните стойности на таймера за сравнение. Регистрите са предвидимо наименувани Заснемане/Сравняване на регистри, има два от тях за всеки канал: ниските 8 бита в TIM2_CCRxL и високите бита в TIM2_CCRxH. Тъй като започнахме 8-битова ШИМ, достатъчно е да напишем само ниските битове:

#define TIM2_CCR1L *(volatile uint8_t *)0x005312
#define TIM2_CCR2L *(volatile uint8_t *)0x005314
#define TIM2_CCR3L *(volatile uint8_t *)0x005316

void setRGBled(uint8_t r, uint8_t g, uint8_t b)
{
    TIM2_CCR1L = r;
    TIM2_CCR2L = g;
    TIM2_CCR3L = b;
}

Внимателният читател ще забележи, че имаме леко дефектна ШИМ, която не може да даде 100% запълване (при максимална стойност 255, сигналът се обръща с един цикъл на таймера). За светодиодите това не играе роля и внимателният читател вече се досеща как да го поправи.

PWM на втория таймер работи, отидете на първия.

Първият таймер има абсолютно същите битове в същите регистри (просто онези битове, които са останали "запазени" във втория таймер, се използват активно за всякакви разширени неща в първия). Следователно е достатъчно да намерите адресите на същите регистри в листа с данни и да копирате кода. Е, променете стойността на делителя на честотата, защото. първият таймер иска да получи не степен на две, а точна 16-битова стойност в два регистъра Prescaler High и ниско. Правим всичко и ... първият таймер не работи. Какъв е проблема?

Единственият начин да решим проблема е като разгледаме целия раздел за контролните регистри на таймер 1, където търсим такъв, който вторият таймер няма. ще има 17.7.30 Регистър за прекъсване (TIM1_BKR), където има малко като това:

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
Разрешете главния изход

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Това е всичко за сега, кодът там.

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8

STM8 мултиплекс

Мултиплексиране на STM8

Третият мини-проект е да свържете осем RGB светодиода към втория таймер в режим PWM и да ги накарате да показват различни цветове. Тя се основава на концепцията за LED мултиплексиране, която се състои в това, че ако включите и изключите светодиодите много, много бързо, ще ни се струва, че те са постоянно включени (постоянство на зрението, инерция на зрителното възприятие). Веднъж го направих нещо подобно на ардуино.

Алгоритъмът на работа изглежда така:

  • свържете анода на първия RGB светодиод;
  • запали го, давайки необходимите сигнали на катодите;
  • изчака края на ШИМ цикъла;
  • свържете анода на втория RGB светодиод;
  • запали го...

Е и т.н. Разбира се, за красива работа е необходимо свързването на анода и „запалването“ на светодиода да се извършват едновременно. Е, почти. Във всеки случай трябва да напишем код, който ще изведе стойности в три канала на втория таймер, ще ги промени, когато се достигне UEV и в същото време ще промени текущо активния RGB LED.

Тъй като превключването на светодиода е автоматично, трябва да създадете "видео памет", откъдето манипулаторът на прекъсвания ще получава данни. Това е прост масив:

uint8_t colors[8][3];

За да промените цвета на конкретен светодиод, ще бъде достатъчно да запишете необходимите стойности в този масив. И променливата ще отговаря за номера на активния светодиод

uint8_t cnt;

Демукс

За правилното мултиплексиране се нуждаем, колкото и да е странно, демултиплексорът CD74HC238. Демултиплексор - чип, който хардуерно имплементира оператора <<. Чрез три входни пина (битове 0, 1 и 2) му подаваме трибитово число X, а в отговор той активира изходното число (1<<X). Останалите входове на чипа се използват за мащабиране на целия дизайн. Нуждаем се от този чип не само за намаляване на броя на заетите пинове на микроконтролера, но и за безопасност - за да не включим случайно повече светодиоди от възможното и да не изгорим MK. Чипът струва една стотинка, винаги трябва да се държи в аптечката у дома.

CD74HC238 ще отговаря за подаването на напрежение към анода на желания светодиод. В пълноценен мултиплекс той ще подава напрежение към колоната през P-MOSFET, но в тази демонстрация можете да го направите директно, т.к. дърпа 20mA, според абсолютни максимални оценки в листа с данни. от лист с данни CD74HC238 имаме нужда от щифтове и този измамен лист:

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
H = ниво на високо напрежение, L = ниво на ниско напрежение, X - не ме интересува

Свързваме E2 и E1 към маса, E3, A0, A1 и A3 към щифтове PD5, PC3, PC4 и PC5 на STM8. Тъй като таблицата по-горе съдържа както ниски, така и високи нива, ние настройваме тези щифтове като щифтове за натискане и издърпване.

ШИМ

ШИМ на втория таймер е конфигуриран по същия начин, както в предишната история, с две разлики:

Първо, трябва да активираме прекъсването Актуализиране на събитието (UEV), който ще извика функция за превключване на активния светодиод. Това става чрез смяна на бита Разрешаване на прекъсване на актуализиране в регистър с говорещо име

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
Регистър за разрешаване на прекъсване

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

Втората разлика е свързана с такова явление на мултиплексиране като отблясъците - паразитно светене на диоди. В нашия случай може да се появи поради факта, че таймерът, след като е причинил прекъсване на UEV, продължава да тиктака и манипулаторът на прекъсванията няма време да превключи светодиода, преди таймерът да започне да записва нещо на изходите. За да се борите с това, ще трябва да обърнете логиката (0 = максимална яркост, 255 = нищо не е включено) и да не позволявате екстремни стойности на работния цикъл. Тези. уверете се, че след UEV светодиодите са напълно изгасени за един цикъл на ШИМ.

Сменете полярността:

//set polarity 
    TIM2_CCER1 |= (CC1P | CC2P);
    TIM2_CCER2 |= CC3P;

Избягвайте да задавате r, g и b на 255 и не забравяйте да ги обърнете, когато използвате.

Прекъсва

Същността на прекъсването е, че при определени обстоятелства чипът спира да изпълнява основната програма и извиква някаква външна функция. Прекъсванията възникват поради външни или вътрешни влияния, включително от таймера.

Когато за първи път създадохме проект в ST Visual Develop, освен main.c получихме прозорец с мистериозен файл stm8_interrupt_vector.cавтоматично се включва в проекта. В този файл към всяко прекъсване е прикачена функция NonHandledInterrupt. Трябва да обвържем нашата функция с желаното прекъсване.

Листът с данни има таблица с прекъсващи вектори, където намираме тези, от които се нуждаем:

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
13 Актуализация/препълване на TIM2
14 TIM2 Улавяне/Сравняване

Трябва да сменим светодиода на UEV, така че е необходимо прекъсване #13.

Съответно, първо, във файла stm8_interrupt_vector.c променете името на функцията, отговорна за прекъсване номер 13 (IRQ13) по подразбиране на нашето собствено:

{0x82, TIM2_Overflow}, /* irq13 */

Второ, ще трябва да създадем файл main.h такова съдържание:

#ifndef __MAIN_H
#define __MAIN_H

@far @interrupt void TIM2_Overflow (void);
#endif

И накрая, напишете тази функция във вашия main.c:

@far @interrupt void TIM2_Overflow (void)
{
    PD_ODR &= ~(1<<5); // вырубаем демультиплексор
    PC_ODR = (cnt<<3); // записываем в демультиплексор новое значение
    PD_ODR |= (1<<5); // включаем демультиплексор

    TIM2_SR1 = 0; // сбрасываем флаг Update Interrupt Pending

    cnt++; 
    cnt &= 7; // двигаем счетчик LED

    TIM2_CCR1L = ~colors[cnt][0]; // передаем в буфер инвертированные значения
    TIM2_CCR2L = ~colors[cnt][1]; // для следующего цикла ШИМ
    TIM2_CCR3L = ~colors[cnt][2]; // 

    return;
}

Остава да разрешите прекъсванията. Това става с команда на асемблер. rim - ще трябва да го потърсите в Ръководство за програмиране:

//enable interrupts
_asm("rim");

Друга инструкция за асемблер - sim - Изключва прекъсванията. Те трябва да бъдат деактивирани, докато новите стойности се записват във "видео паметта", така че прекъсването, причинено в злополучен момент, да не развали масива.

Всички кодове - в Github.

Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8

Ако поне някой тази статия е полезна, тогава не съм я написал напразно. Ще се радвам на коментари и забележки, ще се опитам да отговоря на всички.

Източник: www.habr.com

Добавяне на нов коментар