Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8

В првиот дел Се обидов да им кажам на хоби инженерите за електроника кои израснаа од панталоните Arduino како и зошто треба да читаат листови со податоци и друга документација за микроконтролери. Текстот се покажа како голем, па ветив дека ќе покажам практични примери во посебна статија. Па, се нарече млечна печурка...

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

Текстот повторно се покажа огромен, па за погодност ја правам содржината:

STM32 Blue Pill: 16 LED диоди со двигател DM634
STM8: Поставување шест PWM пинови
STM8: 8 RGB LED диоди на три пина, прекини

Одрекување: Не сум инженер, не се преправам дека имам длабоки познавања од електрониката, статијата е наменета за аматери како мене. Всушност, пред две години се сметав себеси за целна публика. Да ми кажеше некој тогаш дека листовите со податоци за непознат чип не се страшни за читање, немаше да потрошам многу време да барам некои парчиња код на Интернет и да измислувам патерици со ножици и селотејп.

Фокусот на оваа статија е на листови со податоци, а не на проекти, така што кодот можеби не е многу уреден и често тесен. Самите проекти се многу едноставни, иако погодни за прво запознавање со новиот чип.

Се надевам дека мојата статија ќе помогне некому во слична фаза на потопување во хобито.

STM32

16 LED диоди со DM634 и SPI

Мал проект кој користи Blue Pill (STM32F103C8T6) и LED драјвер DM634. Користејќи листови со податоци, ќе го откриеме двигателот, STM IO портите и ќе го конфигурираме SPI.

DM634

Тајвански чип со 16 16-битни PWM излези, може да се поврзе во синџири. Нискиот 12-битен модел е познат од домашен проект Lightpack. Едно време, избирајќи помеѓу DM63x и добро познатиот TLC5940, го избрав DM од неколку причини: 1) TLC на Aliexpress е дефинитивно лажен, но овој не е; 2) DM има автономен PWM со сопствен генератор на фреквенции; 3) може да се купи ефтино во Москва, наместо да се чека парцела од Али. И, се разбира, беше интересно да научите како сами да го контролирате чипот, наместо да користите готова библиотека. Чиповите сега главно се претставени во пакетот SSOP24; тие лесно се лемат на адаптер.

Бидејќи производителот е тајвански, податоци чипот е напишан на кинески англиски, што значи дека ќе биде забавен. Прво го гледаме пинаутот (Поврзување со пинови) да се разбере со која нога да се поврзе со што, и опис на пиновите (Опис на игла). 16 иглички:

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
Извори на мијалник со еднонасочна струја (отворен одвод)

мијалник / Излез со отворен одвод – одвод; извор на приливна струја; излезот е поврзан со земјата во активна состојба - LED диодите се поврзани со возачот со катоди. Електрично, ова, се разбира, не е „отворен одвод“ (отворен одвод), но во листовите со податоци често се наоѓа оваа ознака за иглички во режим на одвод.

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
Надворешни отпорници помеѓу REXT и GND за поставување на излезната тековна вредност

Помеѓу пинот REXT и заземјувањето е инсталиран референтен отпорник, кој го контролира внатрешниот отпор на излезите, видете го графикот на страница 9 од листот со податоци. Во DM634, овој отпор може да се контролира и со софтвер, поставувајќи ја вкупната осветленост (глобална осветленост); Нема да навлегувам во детали во оваа статија, тука само ќе ставам отпорник од 2.2 - 3 kOhm.

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

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8

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

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
... Само три пина се потребни за внесување податоци во уредот. Подигнатиот раб на сигналот SCLK ги префрла податоците од SIN пинот во внатрешниот регистар. Откако ќе се вчитаат сите податоци, краток висок XLAT сигнал ги приклучува последователно пренесените податоци во внатрешните регистри. Внатрешните регистри се порти активирани од нивото на сигналот XLAT. Сите податоци прво се пренесуваат најзначајниот бит.

Зафат – бравата/бравата/бравата.
Подигнување на работ – предниот раб на пулсот
MSB прво – најзначајниот (најлевиот) бит напред.
за такт на податоци – пренос на податоци секвенцијално (бит по бит).

Збор бравата често се наоѓа во документацијата за чипови и се преведува на разни начини, па заради разбирање ќе си дозволам

мала едукативна програмаLED драјверот во суштина е регистер за смена. "Shift" (префрлат) во името - битови движење на податоците во уредот: секој нов бит внесен внатре го турка целиот синџир напред пред него. Бидејќи никој не сака да набљудува хаотично трепкање на LED диодите за време на смената, процесот се одвива во тампон регистри одделени од работните регистри со амортизер (бравата) е еден вид чекална каде што битовите се распоредени по саканата низа. Кога сè е подготвено, блендата се отвора и битовите одат на работа, заменувајќи ја претходната серија. збор бравата во документацијата за микроциркути речиси секогаш се подразбира таков амортизер, без разлика во какви комбинации се користи.

Значи, преносот на податоци до DM634 се врши вака: поставете го влезот DAI на вредноста на најзначајниот бит од далечната LED диода, повлечете го DCK нагоре и надолу; поставете го влезот DAI на вредноста на следниот бит, повлечете го DCK; и така натаму додека не се пренесат сите битови (заклучен внатре), по што влечеме LAT. Ова може да се направи рачно (бит-бенг), но подобро е да користите SPI интерфејс специјално прилагоден за ова, бидејќи е претставен на нашиот STM32 во две копии.

Сина пилула STM32F103

Вовед: STM32 контролерите се многу посложени од Atmega328 отколку што може да изгледаат страшно. Покрај тоа, поради заштеда на енергија, речиси сите периферни уреди се исклучени на самиот почеток, а фреквенцијата на часовникот е 8 MHz од внатрешниот извор. За среќа, програмерите на STM напишаа код што го носи чипот до „пресметените“ 72 MHz, а авторите на сите IDE што ги знам го вклучија во процедурата за иницијализација, така што не треба да тактираме (но можеш ако навистина сакаш). Но, ќе мора да ги вклучите периферните уреди.

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

Во листот со податоци може да не интересира:

  • Пинаути – чип пинаути – во случај да решиме сами да ги правиме таблите;
  • Мемориска карта – мемориска карта за одреден чип. Референтниот прирачник има мапа за целата линија, и споменува регистри што нашите ги немаат.
  • Табела со дефиниции за пинови – наведување на главните и алтернативните функции на пиновите; за „сината пилула“ можете да најдете попогодни слики на Интернет со список на пинови и нивните функции. Затоа, веднаш го бараме на Google записот на Blue Pill и ја чуваме оваа слика при рака:

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
Напомена: имаше грешка на сликата од интернет, која беше забележана во коментарите, благодарам за тоа. Сликата е заменета, но ова е лекција - подобро е да се проверат информациите не од листови со податоци.

Го отстрануваме листот со податоци, го отвораме Референтниот прирачник и отсега го користиме само него.
Постапка: се занимаваме со стандарден влез/излез, го конфигурираме SPI, ги вклучуваме потребните периферни уреди.

Влез излез

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

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
отворен одвод, туркање-влечење, алтернативен туркање-влечење, алтернативен отворен одвод

„Повлечете-туркајте“ (туркање) е вообичаениот излез од Arduino, пинот може да ја земе вредноста или HIGH или LOW. Но, со „отворен одвод“ има тешкотии, иако всушност сè е едноставно овде:

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
Излезна конфигурација / кога портата е доделена на излез: / излезниот бафер е овозможен: / – отворен режим на одвод: „0“ во излезниот регистар овозможува N-MOS, „1“ во излезниот регистар ја напушта портата во режим Hi-Z ( P-MOS не е активиран ) / – режим на притискање: „0“ во излезниот регистар го активира N-MOS, „1“ во излезниот регистар го активира P-MOS.

Целата разлика помеѓу отворениот одвод (отворен одвод) од „push-pull“ (туркање(висока импеданса, Здраво-З). Кога пишувате нула, пинот се однесува исто и во двата режими, и логички и електрично.

Во нормален излезен режим, пинот едноставно ја емитува содржината на излезниот регистар. Во „алтернативата“ се контролира од соодветните периферни уреди (види 9.1.4):

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
Ако бит за порта е конфигуриран како алтернативна функционална игла, пинскиот регистар е оневозможен и пинот е поврзан со периферниот пин.

Алтернативната функционалност на секоја игла е опишана во Дефиниции за пинови Листот со податоци е на преземената слика. На прашањето што да направите ако иглата има неколку алтернативни функции, одговорот е даден со фуснота во листот со податоци:

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
Ако повеќе периферни уреди користат ист пин, за да се избегне конфликт помеѓу алтернативните функции, треба да се користи само еден периферен уред истовремено, со вклучен бит за овозможување на периферниот часовник (во соодветниот RCC регистар).

Конечно, игличките во излезниот режим имаат и брзина на часовникот. Ова е уште една карактеристика за заштеда на енергија; во нашиот случај, само ја поставивме на максимум и ја забораваме.

Значи: користиме SPI, што значи дека два пина (со податоци и со такт-сигнал) треба да бидат „алтернативна функција за притискање“, а уште еден (ЛАТ) треба да биде „редовно притискање“. Но, пред да ги доделиме, да се занимаваме со SPI.

SPI

Уште една мала едукативна програма

SPI или Сериски периферен интерфејс (сериски периферен интерфејс) е едноставен и многу ефикасен интерфејс за поврзување на MK со други MK и со надворешниот свет воопшто. Принципот на неговото функционирање е веќе опишан погоре, каде што за кинескиот LED драјвер (во упатството за упатување, видете дел 25). SPI може да работи во главен („господар“) и slave („slave“) режим. SPI има четири основни канали, од кои не може да се користат сите:

  • MOSI, Главен излез / Слав влез: овој пин пренесува податоци во главен режим и прима податоци во slave режим;
  • MISO, Master Input / Slave Output: напротив, прима во главниот, а пренесува во slave;
  • SCK, Сериски часовник: ја поставува фреквенцијата на пренос на податоци во главниот или прима сигнал за часовникот во slave. Во суштина удирање отчукувања;
  • 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; PWM, тајмери ​​и прекини на STM8
Во овој режим, апликацијата користи SPI или во режим само за пренос или само за примање. / Режимот само за пренос е сличен на дуплекс режимот: податоците се пренесуваат на иглата за пренос (MOSI во главен режим или MISO во режим на slave), а пинот за примање (MISO или MOSI соодветно) може да се користи како обичен I/O пин . Во овој случај, апликацијата треба само да го игнорира баферот Rx (ако се чита, нема да има пренесени податоци таму).

Одлично, MISO пинот е бесплатен, ајде да го поврземе ЛАТ сигналот на него. Да го погледнеме Slave Select, кој на STM32 може да се контролира програмски, што е исклучително погодно. Го читаме истоимениот пасус во делот 25.3.1 SPI Општ опис:

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
Софтверска контрола NSS (SSM = 1) / Информациите за избор на робови се содржани во битот SSI на регистарот SPI_CR1. Надворешниот NSS пин останува бесплатен за други потреби на апликацијата.

Време е за пишување во регистрите. Решив да користам SPI2, да ја побарам неговата основна адреса во листот со податоци - во делот 3.3 Мемориска карта:

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8

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

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

Отворете го делот 25.3.3 со самообјаснив наслов „Конфигурирање на SPI во главен режим“:

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8

1. Поставете ја фреквенцијата на серискиот часовник со битови BR[2:0] во регистарот SPI_CR1.

Регистрите се собрани во истоимениот дел за референтниот прирачник. Промена на адреса (Поместување на адресата) за CR1 – 0x00, стандардно сите битови се бришат (Ресетирај ја вредноста 0x0000):

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8

BR битовите го поставуваат делителот на часовникот на контролорот, со што ја одредуваат фреквенцијата на која ќе работи SPI. Нашата фреквенција STM32 ќе биде 72 MHz, LED драјверот, според неговиот лист со податоци, работи со фреквенција до 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; PWM, тајмери ​​и прекини на STM8
Фаза на часовник и поларитет
Користејќи ги битовите CPOL и CPHA на регистарот SPI_CR1, можете програмски да изберете четири временски врски. Битот CPOL (поларитет на часовникот) ја контролира состојбата на сигналот на часовникот кога не се пренесуваат податоци. Овој бит ги контролира режимите master и slave. Ако CPOL се ресетира, пинот SCK е низок во режим на мирување. Ако битот CPOL е поставен, пинот SCK е висок за време на режимот на одмор.
Кога битот CPHA (фаза на часовникот) е поставен, стробот за замка со високи битови е вториот раб на сигналот SCK (паѓа ако CPOL е јасен, се зголемува ако е поставен CPOL). Податоците се заробени со втората промена на сигналот на часовникот. Ако битот CPHA е јасен, стробот за замка со високи битови е растечкиот раб на сигналот SCK (опаѓачки раб ако е поставен CPOL, растечки раб ако CPOL е исчистен). Податоците се заробени при првата промена на сигналот на часовникот.

Откако го апсорбиравме ова знаење, доаѓаме до заклучок дека двата бита мора да останат нули, бидејќи Сакаме SCK сигналот да остане низок кога не се користи, а податоците да се пренесуваат на растечкиот раб на пулсот (види Сл. Рајзинг Еџ во листот со податоци DM634).

Патем, овде за прв пат наидовме на карактеристика на вокабуларот во листовите со податоци ST: во нив е напишана фразата „ресетирајте го битот на нула“. да се ресетира малкуи не да се расчисти малку, како, на пример, Атмега.

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 сака да прима податоци почнувајќи од најзначајниот бит. Затоа, го оставаме да се ресетира.

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; PWM, тајмери ​​и прекини на STM8
Нарачка за пренос на податоци
Преносот започнува кога бајт е запишан во баферот Tx.
Податочниот бајт се вчитува во регистарот за поместување на паралелно режим (од внатрешната магистрала) за време на преносот на првиот бит, по што се пренесува до секвенцијален Режим на пински MOSI, прв или последен бит напред во зависност од дотерувањето на битот LSBFIRST во регистарот CPI_CR1. Знамето TXE се поставува по преносот на податоци од Tx тампон до регистар за поместување, а исто така генерира прекин ако е поставен битот TXEIE во регистарот CPI_CR1.

Истакнав неколку зборови во преводот за да привлечам внимание на една карактеристика на имплементацијата на SPI во STM контролерите. На Atmega знамето TXE (Tx Празен, Tx е празен и подготвен да прима податоци) се поставува само откако ќе се испрати целиот бајт надвор. И овде ова знаменце е поставено откако бајтот е вметнат во внатрешниот регистар за поместување. Бидејќи се турка таму со сите битови во исто време (паралелно), а потоа податоците се пренесуваат последователно, TXE се поставува пред бајтот целосно да се испрати. Ова е важно затоа што во случајот со нашиот двигател за LED, треба да го повлечеме иглата LAT по испраќањето Сите податоци, т.е. Само знамето TXE нема да ни биде доволно.

Тоа значи дека ни треба уште едно знаме. Ајде да погледнеме во 25.3.7 - „Знамиња за статус“:

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
<…>
Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
Зафатено знаме
Знамето BSY е поставено и бришено со хардвер (пишувањето на него нема ефект). Знамето BSY ја означува состојбата на комуникацискиот слој SPI.
Се ресетира:
кога преносот е завршен (освен во главниот режим ако преносот е континуиран)
кога SPI е оневозможен
кога ќе се појави грешка во главниот режим (MODF=1)
Ако преносот не е континуиран, знамето BSY се брише помеѓу секој пренос на податоци

Добро, ова ќе ни се најде. Ајде да дознаеме каде се наоѓа тампонот Tx. За да го направите ова, прочитајте „Регистар на податоци SPI“:

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
Битови 15:0 DR[15:0] Регистар на податоци
Примени податоци или податоци што треба да се пренесат.
Регистарот на податоци е поделен на два бафери - еден за запишување (тампон за пренос) и еден за читање (тампон за примање). Пишувањето во регистарот на податоци запишува во баферот Tx, а читањето од регистарот на податоци ќе ја врати вредноста содржана во баферот Rx.

Па, и статусниот регистар, каде што се наоѓаат знамињата TXE и BSY:

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на 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, па ќе се вратиме на В/И.

Доделување иглички

Во STM32F1, регистрите одговорни за состојбата на пиновите се сосема невообичаени. Јасно е дека ги има повеќе од Atmega, но тие се разликуваат и од другите STM чипови. Дел 9.1 Општ опис на GPIO:

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
Секоја од влезните/излезни порти за општа намена (GPIO) има два 32-битни конфигурациски регистри (GPIOx_CRL и GPIOx_CRH), два 32-битни регистри на податоци (GPIOx_IDR и GPIOx_ODR), 32-битен регистер за поставување/ресетирање (GPIOx_BSRR), 16-битен регистар за ресетирање (GPIOx_BRR) и регистар за блокирање на битови (GPIOx_LCKR).

Првите два регистри се невообичаени, а исто така доста незгодни, бидејќи 16-те пинови на портите се расфрлани низ нив во формат „четири бита по брат“. Оние. пиновите од нула до седум се во CRL, а останатите се во CRH. Во исто време, преостанатите регистри успешно ги содржат битови од сите пинови на пристаништето - често остануваат половина „резервирани“.

За едноставност, да почнеме од крајот на листата.

Не ни треба регистар за блокирање.

Регистрите за поставување и ресетирање се прилично смешни по тоа што делумно се дуплираат еден со друг: можете да напишете сè само во BSRR, каде што повисоките 16 бита ќе го ресетираат пинот на нула, а долните ќе бидат поставени на 1, или можете исто така користете BRR, од кои долните 16 бита го ресетираат само пинот. Ми се допаѓа втората опција. Овие регистри се важни затоа што обезбедуваат атомски пристап до пиновите:

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
Атомско поставување или ресетирање
Нема потреба да се оневозможуваат прекините при програмирање на GPIOx_ODR на ниво на битови: еден или повеќе битови може да се сменат со една операција за атомско запишување APB2. Ова се постигнува со запишување „1“ во регистерот за поставување/ресетирање (GPIOx_BSRR или, само за ресетирање, GPIOx_BRR) на битот што треба да се промени. Другите битови ќе останат непроменети.

Регистрите на податоци имаат прилично самообјасниви имиња - IDR = Внесете Регистар на насоки, регистар на влезови; ODR = излез Регистар на насока, регистер за излез. Нема да ни требаат во тековниот проект.

И конечно, контролните регистри. Бидејќи сме заинтересирани за вторите SPI пинови, имено PB13, PB14 и PB15, веднаш го разгледуваме CRH:

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8

И гледаме дека ќе треба да напишеме нешто во битови од 20 до 31.

Погоре веќе сфативме што сакаме од пиновите, па тука ќе направам без слика од екранот, само ќе кажам дека MODE ја одредува насоката (влез ако двата бита се поставени на 0) и брзината на пиновите (ни требаат 50 MHz, т.е. и двете пинови на „1“), а CNF го поставува режимот: редовно „push-pull“ – 00, „alternative“ – 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

Нашите пинови се наоѓаат на портата Б (базна адреса – 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. Го бараме во документацијата: ова е Ресетирање и Контрола на часовникот.

Како што беше кажано погоре, за среќа, најтешкиот дел од темата за такт ни го направија луѓе од СТМ, за што им благодариме многу (уште еднаш ќе дадам линк до Веб-страницата на Ди Халт, за да биде јасно колку е збунувачки). Ни требаат само регистри одговорни за овозможување на периферно тактирање (Peripheral Clock Enable Registers). Прво, да ја најдеме основната адреса на RCC, таа е на самиот почеток на „Мапата за меморија“:

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8

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

И потоа или кликнете на врската каде што се обидувате да најдете нешто во плочата или, многу подобро, поминете низ описите на регистрите за овозможување од деловите за овозможи регистри. Каде ќе ги најдеме RCC_APB1ENR и RCC_APB2ENR:

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8

И тие, соодветно, содржат битови кои вклучуваат тактирање на SPI2, IOPB (I/O Port 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; PWM, тајмери ​​и прекини на STM8

STM8 PWM

PWM на STM8

Кога само ја планирав оваа статија, решив, како пример, да се обидам да совладам некоја функционалност на непознат чип користејќи само лист со податоци, за да не завршам со чевлар без чизми. STM8 беше идеален за оваа улога: прво, имав неколку кинески табли со STM8S103, и второ, не е многу популарен, и затоа искушението да се прочита и да се најде решение на Интернет почива на недостатокот на токму овие решенија.

Чипот исто така има податоци и референтен прирачник RM0016, во првата има адреси за пинаут и регистар, во втората - се останато. STM8 е програмиран во C во страшна IDE ST Visual Develop.

Клокирање и В/И

Стандардно, STM8 работи на фреквенција од 2 MHz, ова мора веднаш да се коригира.

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
HSI (Внатрешен) часовник со голема брзина
HSI такт-сигналот е изведен од внатрешен RC осцилатор од 16 MHz со програмабилен делител (1 до 8). Тој е поставен во регистарот за делител на часовникот (CLK_CKDIVR).
Забелешка: на почетокот, HSI RC осцилатор со делител 8 е избран како водечки извор на сигналот на часовникот.

Ја наоѓаме адресата на регистарот во листот со податоци, описот во refman и гледаме дека регистарот треба да се исчисти:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Бидејќи ќе извршиме PWM и ќе ги поврземе LED диодите, ајде да го погледнеме пинаутот:

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8

Чипот е мал, многу функции се суспендирани на истите пинови. Она што е во квадратни загради е „алтернативна функционалност“, се префрла со „бајти за опција“ (бајти на опции) – нешто како Atmega осигурувачи. Можете да ги менувате нивните вредности програмски, но тоа не е неопходно, бидејќи Новата функционалност се активира само по рестартирање. Полесно е да се користи ST Visual Programmer (преземен со Visual Develop), кој може да ги промени овие бајти. Приказот покажува дека пиновите CH1 и CH2 на првиот тајмер се скриени во квадратни загради; потребно е да се постават битовите AFR1 и AFR0 во STVP, а вториот ќе го пренесе и излезот CH1 на вториот тајмер од PD4 на PC5.

Така, 6 пина ќе ги контролираат LED диодите: PC6, PC7 и PC3 за првиот тајмер, PC5, PD3 и PA3 за вториот.

Поставувањето на самите I/O пинови на STM8 е поедноставно и пологично отколку на STM32:

  • познат од регистарот за насока на податоци Atmega DDR (Регистар за насока на податоци): 1 = излез;
  • првиот контролен регистер CR1, кога излегува, го поставува режимот за притискање (1) или отворен одвод (0); бидејќи ги поврзувам LED диодите со чипот со катоди, оставам нули овде;
  • вториот контролен регистар 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

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

  • Фреквенција на PWM – фреквенција со која штиклира тајмерот;
  • Автоматско повторно вчитување, AR – автоматска вредност до која ќе брои тајмерот (период на пулсот);
  • Ажурирај настан, UEV – настан што се случува кога тајмерот броел до AR;
  • PWM Duty Cycle – Циклус на работа на PWM, често наречен „фактор на должност“;
  • Снимајте/Спореди вредност – вредност за снимање/споредување, на која броел тајмерот ќе направи нешто (во случај на PWM, го превртува излезниот сигнал);
  • Вредност пред вчитување – однапред вчитана вредност. Споредете ја вредноста не може да се промени додека тајмерот отчукува, инаку циклусот PWM ќе се прекине. Затоа, новите пренесени вредности се ставаат во бафер и се извлекуваат кога тајмерот ќе го достигне крајот на своето одбројување и ќе се ресетира;
  • Порамнети на работ и Режими подредени во центарот – порамнување долж границата и во центарот, исто како и Атмел Брз PWM и Фаза-точен PWM.
  • OCiREF, Излезен спореден референтен сигнал – референтен излезен сигнал, всушност, она што се појавува на соодветниот пин во режимот PWM.

Како што е веќе јасно од пинаутот, два тајмери ​​имаат PWM можности – првиот и вториот. И двете се 16-битни, првиот има многу дополнителни функции (особено, може да брои и нагоре и надолу). Ни треба и двете за да работиме подеднакво, па решив да почнам со очигледно послабата втора, за случајно да не искористам нешто што го нема. Некој проблем е што описот на PWM функционалноста на сите тајмери ​​во упатството за повикување е во поглавјето за првиот тајмер (17.5.7 PWM режим), така што мора постојано да скокате напред-назад низ документот.

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

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
Порамнети граници PWM
Конфигурација на сметката од дното кон врвот
Броењето од долу нагоре е активно ако битот DIR во регистарот TIM_CR1 е исчистен
Пример
Примерот го користи првиот режим PWM. Референтниот сигнал PWM OCiREF се одржува високо додека TIM1_CNT < TIM1_CCRi. Во спротивно, потребно е ниско ниво. Ако споредбената вредност во регистарот TIM1_CCRi е поголема од вредноста на автоматско вчитување (регистар TIM1_ARR), сигналот OCiREF се одржува на 1. Ако споредбената вредност е 0, OCiREF се одржува на нула....

STM8 тајмер за време на ажурирање настан прво проверува споредете ја вредноста, и само тогаш произведува референтен сигнал. Тајмерот на Atmega прво се навртува, а потоа се споредува, што резултира со compare value == 0 излезот е игла, со која мора некако да се справи (на пример, со програмско инвертирање на логиката).

Значи, што сакаме да направиме: 8-битен PWM (AR == 255), броејќи од дното кон врвот, порамнување по должината на границата. Бидејќи светилките се поврзани со чипот со катоди, PWM треба да даде 0 (светло LED) додека споредете ја вредноста и 1 после.

За некои веќе прочитавме Режим PWM, така што го наоѓаме потребниот регистар на вториот тајмер со пребарување во упатството за оваа фраза (18.6.8 - TIMx_CCMR1):

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
110: Прв PWM режим – кога се брои од дното нагоре, првиот канал е активен додека TIMx_CNT < TIMx_CCR1. Во спротивно, првиот канал е неактивен. [Понатаму во документот има погрешна копија-лепење од тајмерот 1] 111: Втор режим PWM – кога се брои од дното кон врвот, првиот канал е неактивен додека TIMx_CNT < TIMx_CCR1. Во спротивно, првиот канал е активен.

Бидејќи LED диодите се поврзани со MK со катоди, вториот режим ни одговара (и првиот, но тоа сè уште не го знаеме).

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на 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 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; PWM, тајмери ​​и прекини на STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Ајде да напишеме едноставен аналог на AnalogWrite(), кој ќе ги пренесе вистинските вредности на тајмерот за споредба. Регистрите се именувани предвидливо Снимајте/Спореди регистри, има два од нив за секој канал: 8 бита од низок ред во TIM2_CCRxL и оние од висок ред во TIM2_CCRxH. Бидејќи создадовме 8-битен PWM, доволно е да ги напишете само најмалку значајните битови:

#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;
}

Внимателниот читател ќе забележи дека имаме малку дефектен PWM, кој не може да произведе 100% полнење (при максимална вредност од 255, сигналот се превртува за еден циклус на тајмер). За LED диоди ова не е важно, а внимателниот читател веќе може да погоди како да го поправи.

PWM на вториот тајмер работи, ајде да преминеме на првиот.

Првиот тајмер ги има токму истите битови во истите регистри (само оние битови што останаа „резервирани“ во вториот тајмер активно се користат во првиот за секакви напредни работи). Затоа, доволно е да ги пронајдете адресите на истите регистри во листот со податоци и да го копирате кодот. Па, сменете ја вредноста на делителот на фреквенцијата, бидејќи ... првиот тајмер сака да не прими моќност од два, туку точна 16-битна вредност во два регистри Прескалар Висок и Ниско. Правиме се и... првиот тајмер не работи. Што е проблемот?

Проблемот може да се реши само со прегледување на целиот дел за контролните регистри на тајмерот 1, каде што го бараме оној што го нема вториот тајмер. Ќе биде 17.7.30 Регистар за прекини (TIM1_BKR), каде го има овој бит:

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
Овозможете го главниот излез

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Тоа е сега сигурно, кодот таму.

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8

STM8 мултиплекс

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

Третиот мини-проект е да поврзете осум RGB LED диоди со вториот тајмер во режим PWM и да ги натерате да прикажуваат различни бои. Тој се заснова на концептот на LED мултиплексирање, а тоа е дека ако ги вклучите и исклучите LED диодите многу, многу брзо, ќе ни се чини дека тие се постојано вклучени (упорност на видот, инерција на визуелна перцепција). Јас еднаш направив вакво нешто на Arduino.

Алгоритмот за работа изгледа вака:

  • ја поврза анодата на првата RGB LED;
  • го запали, испраќајќи ги потребните сигнали до катодите;
  • чекаше до крајот на циклусот PWM;
  • ја поврза анодата на втората RGB LED;
  • запали го...

Па, итн. Се разбира, за убаво работење потребно е анодата да се поврзе и ЛЕР да се „запали“ во исто време. Па, или речиси. Во секој случај, треба да напишеме код што ќе ги исфрли вредностите во три канали од вториот тајмер, ќе ги промени кога ќе се достигне UEV и во исто време ќе ја смени моментално активната RGB LED.

Бидејќи префрлувањето на ЛЕД е автоматско, треба да создадеме „видео меморија“ од која управувачот со прекини ќе прима податоци. Ова е едноставна низа:

uint8_t colors[8][3];

За да ја смените бојата на одредена LED диода, ќе биде доволно да ги напишете потребните вредности во оваа низа. И променливата ќе биде одговорна за бројот на активната ЛЕР

uint8_t cnt;

Демукс

За правилно мултиплексирање, потребен ни е, чудно, демултиплексер CD74HC238. Демултиплексер - чип што го имплементира операторот во хардвер <<. Преку три влезни пинови (битови 0, 1 и 2) го внесуваме со трибитен број X, а како одговор го активира излезниот број (1<<X). Останатите влезови на чипот се користат за скалирање на целиот дизајн. Овој чип ни треба не само за да го намалиме бројот на зафатени пинови на микроконтролерот, туку и за безбедност - за случајно да не вклучиме повеќе LED диоди отколку што е можно и да не го запалиме MK. Чипот чини еден денар и секогаш треба да се чува во вашиот домашен кабинет со лекови.

Нашиот CD74HC238 ќе биде одговорен за напојување на напон до анодата на саканата ЛЕР. Во полноправно мултиплекс, тој би снабдувал напон до столбот преку P-MOSFET, но во оваа демо тоа е можно директно, бидејќи повлекува 20 mA, според апсолутни максимални оценки во листот со податоци. Од Лист со податоци CD74HC238 ни требаат пинаути и овој лист за измами:

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
H = високонапонско ниво, L = ниско напонско ниво, X – немај гајле

Ние ги поврзуваме E2 и E1 со земјата, E3, A0, A1 и A3 со пиновите PD5, PC3, PC4 и PC5 на STM8. Бидејќи горната табела содржи и ниски и високи нивоа, ние ги конфигурираме овие пинови како пинови за притискање.

PWM

PWM на вториот тајмер е конфигуриран на ист начин како и во претходната приказна, со две разлики:

Прво, треба да го вклучиме прекинот Ажурирајте го настанот (UEV) кој ќе повика функција што ја вклучува активната LED диода. Ова се прави со промена на битот Ажурирање прекини Овозможи во регистар со кажувачко име

Читање на листови со податоци 2: SPI на STM32; PWM, тајмери ​​и прекини на STM8
Регистар за овозможување прекини

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

Втората разлика е поврзана со феноменот на мултиплексирање, како на пр имагинарните облици – паразитски сјај на диоди. Во нашиот случај, може да се појави поради фактот што тајмерот, предизвикувајќи прекин на UEV, продолжува да штиклира, а управувачот со прекини нема време да ја префрли ЛЕР пред тајмерот да почне да пишува нешто на пиновите. За да се борите со ова, ќе треба да ја превртите логиката (0 = максимална осветленост, 255 = ништо не свети) и да избегнете екстремни вредности на работниот циклус. Оние. погрижете се по UEV LED диодите целосно да се изгаснат за еден циклус PWM.

Промена на поларитет:

//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; PWM, тајмери ​​и прекини на 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; PWM, тајмери ​​и прекини на STM8

Ако барем некој ја смета оваа статија корисна, тогаш не ја напишав залудно. Ќе ми биде драго да добивам коментари и забелешки, ќе се обидам да одговорам на се.

Извор: www.habr.com

Додадете коментар