ProHoster > Блог > интернет новини > Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
Прочетете таблици с данни 2: SPI на STM32; ШИМ, таймери и прекъсвания на STM8
В първата част Опитах се да кажа на хоби инженерите по електроника, които са израснали от панталоните на Arduino, как и защо трябва да четат таблици с данни и друга документация за микроконтролери. Текстът се оказа голям, затова обещах да покажа практически примери в отделна статия. Е, той се нарече товарач ...
Днес ще ви покажа как да използвате таблици с данни за решаване на доста прости, но необходими задачи за много проекти на STM32 (Blue Pill) и STM8 контролери. Всички демо проекти са посветени на любимите ми светодиоди, ще ги запалим в големи количества, за които ще трябва да използваме всякакви интересни периферни устройства.
Текстът отново се оказа огромен, така че за удобство правя съдържанието:
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 пина:
Мивка DC източници (отворен дренаж)
мивка / Изход с отворен дренаж - наличност; източник на входящ ток; изход, свързан към маса в активно състояние - светодиодите са свързани към драйвера чрез катоди. Електрически това, разбира се, не е "отворен дренаж" (отворен дренаж), но в таблиците с данни често се среща такова обозначение за изходи в режим на източване.
Външни резистори между REXT и GND за задаване на стойността на изходния ток
Между щифта REXT и земята е инсталиран референтен резистор, който контролира вътрешното съпротивление на изходите, вижте графиката на страница 9 от листа с данни. В DM634 това съпротивление може също да се контролира от софтуер чрез настройка на общата яркост (глобална яркост); Няма да навлизам в подробности в тази статия, просто ще сложа тук резистор от 2.2 - 3 kOhm.
За да разберете как да управлявате чипа, нека да разгледаме описанието на интерфейса на устройството:
Да, ето го, китайски английски в целия му блясък. Проблемно е да се преведе това, можете да го разберете, ако желаете, но има друг начин - да погледнете как е описана връзката в листа с данни към функционално близкия TLC5940:
… За въвеждане на данни в устройството са необходими само три пина. Нарастващият ръб на сигнала 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, има два полезни документа за него:
Спецификации за микроконтролери STM32F103x8 и STM32F103xB;
Pinouts - чипове пинаути - в случай, че решим сами да си правим платки;
Карта на паметта - карта на паметта за определен чип. Справочникът има карта за цялата линия, споменава регистри, които не са на нашия.
Таблица с дефиниции на щифтове - списък на основните и алтернативни функции на щифта; за „синьото хапче“ в интернет можете да намерите по-удобни снимки със списък на щифтовете и техните функции. Затова незабавно търсим в Google Pinout на Blue Pill и държим тази снимка под ръка:
NB: имаше грешка в снимката от интернет, забелязана в коментарите, за което благодаря. Картината е сменена, но това е урок - по-добре е да проверявате информацията, а не от таблиците с данни.
Премахваме листа с данни, отваряме Справочното ръководство, оттук нататък използваме само него.
Процедура: работа със стандартен вход / изход, конфигуриране на SPI, включване на необходимите периферни устройства.
Вход изход
При Atmega328 I/O е изключително прост, поради което изобилието от STM32 опции може да бъде объркващо. Сега имаме нужда само от изводи, но дори има четири варианта:
"Дърпам бутам" (Натисни Дръпни) - обичайният изход от Arduino, щифтът може да бъде HIGH или LOW. Но с "отворен дренаж" възникват трудности, въпреки че всъщност всичко е просто тук:
Конфигурация на изхода / когато портът е присвоен на изхода: / изходният буфер е активиран: / – отворен режим на източване: "0" в изходния регистър разрешава N-MOS, "1" в изходния регистър оставя порта в режим Hi-Z (P -MOS не е активиран ) / - режим на натискане и изтегляне: "0" в изходния регистър активира N-MOS, "1" в изходния регистър активира P-MOS.
Цялата разлика в отворения дренаж (отворен дренаж) от "бутане-дърпане" (Натисни Дръпни) е, че в първия щифт не може да приеме състояние HIGH: когато единица бъде записана в изходния регистър, тя преминава в режим на високо съпротивление (висок импеданс, Здравей-Z). При писане на нула щифтът и в двата режима се държи еднакво, както логически, така и електрически.
В нормален изходен режим щифтът просто превежда съдържанието на изходния регистър. В "алтернатива" се управлява от съответната периферия (вижте 9.1.4):
Ако битът на порта е конфигуриран като изход за алтернативна функция, изходният регистър е деактивиран и щифтът е свързан към изходния сигнал на периферното устройство.
Алтернативната функционалност на всеки щифт е описана в Определения на ПИН Листът с данни е на изтеглената снимка. На въпроса какво да направите, ако щифтът има няколко алтернативни функции, отговорът е даден от бележка под линия в листа с данни:
Ако множество периферни устройства използват един и същ щифт, за да се избегне конфликт между алтернативни функции, трябва да се използва само едно периферно устройство в даден момент, като се превключва с помощта на бита за активиране на часовника на периферията (в съответния 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 еднопосочен поток от данни):
В този режим приложението използва SPI в режим само за предаване или само за получаване. / Режимът само за предаване е подобен на дуплексния режим: данните се предават на щифта за предаване (MOSI в главен режим или MISO в подчинен режим), докато щифтът за получаване (съответно MISO или MOSI) може да се използва като нормален I/O карфица. В този случай е достатъчно приложението да игнорира Rx буфера (ако е прочетен, няма да има предавани данни).
Страхотно, щифтът MISO е свободен, нека свържем LAT сигнала към него. Нека се заемем със Slave Select, който може да се управлява програмно на STM32, което е изключително удобно. Четем параграфа със същото име в раздел 25.3.1 Общо описание на SPI:
Софтуерно управление на NSS (SSM = 1) / информация за избор на подчинен се съдържа в бита SSI на регистъра SPI_CR1. Външният NSS щифт е оставен свободен за други нужди на приложението.
Време е да пишете в регистрите. Реших да използвам SPI2, търсим основния му адрес в листа с данни - в раздел 3.3 Карта на паметта (Карта на паметта):
Отваряме раздел 25.3.3 с показателното заглавие „Конфигуриране на SPI в главен режим“:
1. Задайте часовника на серийния интерфейс с битовете BR[2:0] в регистъра SPI_CR1.
Регистрите са събрани в едноименния раздел на справочното ръководство. Промяна на адреса (отместване на адреса) CR1 има 0x00, по подразбиране всички битове са изчистени (Нулиране на стойността 0x0000):
BR битовете задават делителя на часовника на контролера, като по този начин определят честотата, на която ще работи SPI. Честотата на STM32 ще бъде 72 MHz, светодиодният драйвер, според неговия лист с данни, работи на честота до 25 MHz, така че трябва да разделим на четири (BR[2:0] = 001).
2. Задайте битовете CPOL и CPHA, за да определите връзката между трансфера на данни и часовника на серийния интерфейс (вижте диаграмата на страница 240)
Тъй като тук четем лист с данни и не разглеждаме схеми, нека разгледаме по-отблизо текстовото описание на битовете CPOL и CPHA на страница 704 (Общо описание на SPI):
Фаза и полярност на часовника
Използвайки битовете 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 има смисъл да се постави в единство:
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 като главен и го включваме:
SPI е конфигуриран, нека веднага напишем функции, които изпращат байтове към драйвера. Продължете да четете 25.3.3 „Конфигуриране на SPI в главен режим“:
Процедура за прехвърляне на данни
Трансферът започва, когато байт бъде записан в Tx буфера.
Байтът с данни се зарежда в регистъра за смяна на паралелен режим (от вътрешната шина) по време на предаването на първия бит, след което се предава в последователен MOSI pin режим, първи или последен бит напред в зависимост от настройката на бита LSBFIRST в регистъра CPI_CR1. Флагът TXE се задава след предаване на данни от Tx буфер към преместващ регистъри се генерира прекъсване, ако битът TXEIE в регистъра CPI_CR1 е зададен.
Подчертах няколко думи в превода, за да привлека вниманието към една особеност на внедряването на SPI в STM контролери. На Atmega флагът TXE (Tx празен, Tx е празен и готов за получаване на данни) се задава само след като целият байт е изпратен навън. И тук този флаг е зададен, след като байтът е бил пъхнат във вътрешния регистър за преместване. Тъй като се пъхва там с всички битове едновременно (паралелно) и след това данните се предават последователно, TXE се задава преди байтът да бъде напълно изпратен. Това е важно, защото в случай на нашия LED драйвер, трябва да издърпаме LAT щифта след изпращане всички данни, т.е. само флагът TXE няма да ни е достатъчен.
Което означава, че имаме нужда от друго знаме. Нека да разгледаме 25.3.7 - "Флагове за състояние":
<…>
Флаг ЗАЕТ
Флагът BSY се задава и изчиства от хардуера (записването в него няма ефект). Флагът BSY показва състоянието на SPI комуникационния слой.
Нулира се:
когато прехвърлянето приключи (освен в главен режим, ако прехвърлянето е непрекъснато)
когато SPI е деактивиран
когато възникне грешка в главния режим (MODF=1)
Ако предаването не е непрекъснато, флагът BSY се изчиства между всяко предаване на данни.
Добре, ще ми е от полза. Разберете къде се намира Tx буферът. За да направите това, прочетете "SPI Data Register":
Битове 15:0 DR[15:0] регистър на данните
Получени данни или данни за предаване.
Регистърът на данните е разделен на два буфера, един за запис (буфер за предаване) и един за четене (буфер за получаване). Записът в регистъра на данните записва в Tx буфера, а четенето от регистъра на данните ще върне стойността, съдържаща се в Rx буфера.
Е, регистърът на състоянието, където има флагове TXE и BSY:
Е, тъй като трябва да прехвърлим 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:
Всеки от 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 бита от които само нулират щифта. Харесвам втория вариант. Тези регистри са важни, защото осигуряват атомен достъп до щифтовете:
Atomic инсталиране или нулиране
Не е необходимо да забранявате прекъсванията, когато програмирате GPIOx_ODR на битово ниво: можете да промените един или повече бита с една операция за атомно записване на APB2. Това се постига чрез записване на "1" в регистъра за настройка/нулиране (GPIOx_BSRR или, само за нулиране, GPIOx_BRR) на бита, който трябва да се промени. Останалите битове ще останат непроменени.
Регистрите на данни имат доста красноречиви имена - IDR = Вход Регистър на посоката, входен регистър; ODR= Продукция Регистър на посоката, изходен регистър. В настоящия проект нямаме нужда от тях.
И накрая контролните регистри. Тъй като се интересуваме от щифтовете на втория SPI, а именно PB13, PB14 и PB15, веднага разглеждаме CRH:
И виждаме, че ще е необходимо да напишем нещо в битове от 20-ти до 31-ви.
Вече разбрахме какво искаме от щифтовете по-горе, така че тук ще направя без екранна снимка, просто кажете, че MODE задава посоката (въвежда се, ако и двата бита са зададени на 0) и скоростта на щифта (имаме нужда от 50MHz, т.е. и двата щифта на "1"), а CNF задава режима: нормален "натискане-натискане" - 00, "алтернатива" - 10. По подразбиране, както виждаме по-горе, всички щифтове имат третия бит отдолу ( CNF0), той ги настройва в режим плаващ вход.
Тъй като планирам да направя нещо друго с този чип, за простота, като цяло дефинирах всички възможни стойности на MODE и CNF както за долния, така и за горния контролен регистър.
(LAT_low просто по инерция, някак винаги е било така, нека си остане за вас)
Сега всичко е супер, просто не става. Тъй като това е STM32, тук пестят електричество, което означава, че трябва да включите клокването на необходимата периферия.
Включете часовника
Часовникът е отговорен за часовника, те също са часовник. И вече можехме да забележим съкращението RCC. Търсим го в документацията: това е Reset and Clock Control (Управление на нулиране и часовник).
Както споменахме по-горе, за щастие хората от STM ни направиха най-трудната част от темата за клокването, за което много им благодаря (пак ще дам линк към Уебсайтът на Di Haltза да стане ясно колко е объркан). Нуждаем се само от регистри, отговорни за активирането на периферно тактоване (Peripheral Clock Enable Registers). Първо, нека намерим основния адрес на RCC, той е в самото начало на "Карта с памет":
И след това или кликнете върху връзката, където да опитате да намерите нещо в таблицата, или, много по-добре, прегледайте описанията на включващите регистри от разделите за активирайте регистрите. Къде намираме RCC_APB1ENR и RCC_APB2ENR:
И в тях, съответно, битове, които включват клокването на SPI2, IOPB (I / O порт B) и алтернативни функции (AFIO).
Ако има възможност и желание за тестване, тогава свързваме DM634 така: DAI към PB15, DCK към PB13, LAT към PB14. Захранваме водача от 5 волта, не забравяйте да комбинирате основанията.
STM8 PWM
ШИМ на STM8
Когато току-що планирах тази статия, реших например да се опитам да овладея някои функции на непознат чип само с помощта на лист с данни, така че обущар да не излезе без ботуши. STM8 беше идеален за тази роля: първо, имах няколко китайски дъски с STM8S103, и второ, не е много популярен и следователно изкушението да четеш и да намериш решение в Интернет почива на липсата на същите тези решения.
По подразбиране STM8 работи на честота от 2 MHz, това трябва да се коригира незабавно.
HSI часовник (висок вътрешен)
Часовникът HSI се получава от вътрешен 16 MHz RC осцилатор с програмируем делител (1 до 8). Задава се в регистъра на делителя на часовника (CLK_CKDIVR).
Забележка: HSI RC осцилатор с делител 8 е избран като източник на главен часовник при стартиране.
Намираме адреса на регистъра в листа с данни, описанието в refman и виждаме, че регистърът трябва да бъде изчистен:
Тъй като ще стартираме ШИМ и ще свържем светодиоди, нека да разгледаме разводката:
Чипът е малък, много функции са окачени на едни и същи щифтове. Това, което е в квадратни скоби, е „алтернативна функционалност“, превключва се от „опционални байтове“ (опция байтове) - нещо като предпазители 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
Автоматично презареждане, AR – автоматично заредена стойност, до която ще брои таймерът (период на импулса);
Събитие за актуализиране, UEV – събитие, което се случва, когато таймерът е отброил до AR;
Работен цикъл на ШИМ - PWM работен цикъл, често наричан "работен цикъл";
Улавяне/Сравняване на стойност – стойност за заснемане/сравняване, броене до която таймерът ще направи нещо (в случай на ШИМ инвертира изходния сигнал);
стойност на предварително натоварване – предварително заредена стойност. сравни стойност не може да се промени, докато таймерът тиктака, в противен случай ШИМ цикълът ще се прекъсне. Следователно новите предадени стойности се поставят в буфера и се изтеглят, когато таймерът достигне края на обратното броене и се нулира;
Подравнени по ръбовете и Централно подравнени режими – подравняване на границата и в центъра, същото като atmelovskie Бърз ШИМ и Фазово правилен ШИМ.
OCiREF, референтен сигнал за сравнение на изхода - референтният изходен сигнал, всъщност това, което се появява на съответния щифт в режим PWM.
Както вече става ясно от pinout, два таймера имат PWM възможности - първият и вторият. И двата са 16-битови, като първият има много допълнителни функции (по-специално може да брои нагоре и надолу). Трябва и двата да работят еднакво, затова реших да започна с очевидно по-бедния втори, за да не използвам случайно нещо, което го няма в него. Известен проблем е, че описанието на функционалността на PWM на всички таймери в справочното ръководство е в главата за първия таймер (17.5.7 Режим PWM), така че трябва да прескачате напред-назад през документа през цялото време.
PWM на STM8 има важно предимство пред Atmega PWM:
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):
110: Първи ШИМ режим - при броене отдолу нагоре, първият канал е активен, докато TIMx_CNT < TIMx_CCR1. В противен случай първият канал е неактивен. [по-нататък в документа, грешно копиране-поставяне от таймер 1] 111: Втори режим на ШИМ - при броене отдолу нагоре, първият канал е неактивен, докато TIMx_CNT < TIMx_CCR1. В противен случай първият канал е активен.
Тъй като светодиодите са свързани към MK с катоди, вторият режим ни подхожда (първият също, но все още не знаем това).
Бит 3 OC1PE: Разрешаване на предварително зареждане на изход 1
0: Регистърът за предварително зареждане на TIMx_CCR1 е деактивиран. Можете да пишете на TIMx_CCR1 по всяко време. Новата стойност работи веднага.
1: Регистърът за предварително зареждане на TIMx_CCR1 е активиран. Операциите за четене/запис имат достъп до регистъра за предварително зареждане. Предварително заредената стойност на TIMx_CCR1 се зарежда в скрития регистър по време на всяко събитие за актуализиране.
*Забележка: Регистрите за предварително зареждане трябва да са активирани, за да може режимът PWM да работи правилно. Това не е задължително в режим на единичен сигнал (битът OPM е зададен в регистъра TIMx_CR1).
Добре, включете всичко необходимо за трите канала на втория таймер:
Вторият таймер може да брои само отдолу нагоре, подравняване на границата, нищо не трябва да се променя. Задайте честотния делител например на 256. За втория таймер делителят е зададен в регистъра TIM2_PSCR и е степен на две:
Остава да включите заключенията и самия втори таймер. Първата задача се решава с регистри Заснемане/Сравнение Разреши: има два от тях, три канала са разпръснати асиметрично върху тях. Тук също можем да научим, че е възможно да променим полярността на сигнала, т.е. по принцип може да се използва и PWM Mode 1. Пишем:
Нека напишем прост аналог на AnalogWrite (), който ще предаде действителните стойности на таймера за сравнение. Регистрите са предвидимо наименувани Заснемане/Сравняване на регистри, има два от тях за всеки канал: ниските 8 бита в TIM2_CCRxL и високите бита в TIM2_CCRxH. Тъй като започнахме 8-битова ШИМ, достатъчно е да напишем само ниските битове:
Внимателният читател ще забележи, че имаме леко дефектна ШИМ, която не може да даде 100% запълване (при максимална стойност 255, сигналът се обръща с един цикъл на таймера). За светодиодите това не играе роля и внимателният читател вече се досеща как да го поправи.
PWM на втория таймер работи, отидете на първия.
Първият таймер има абсолютно същите битове в същите регистри (просто онези битове, които са останали "запазени" във втория таймер, се използват активно за всякакви разширени неща в първия). Следователно е достатъчно да намерите адресите на същите регистри в листа с данни и да копирате кода. Е, променете стойността на делителя на честотата, защото. първият таймер иска да получи не степен на две, а точна 16-битова стойност в два регистъра Prescaler High и ниско. Правим всичко и ... първият таймер не работи. Какъв е проблема?
Единственият начин да решим проблема е като разгледаме целия раздел за контролните регистри на таймер 1, където търсим такъв, който вторият таймер няма. ще има 17.7.30 Регистър за прекъсване (TIM1_BKR), където има малко като това:
Третият мини-проект е да свържете осем 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 имаме нужда от щифтове и този измамен лист:
H = ниво на високо напрежение, L = ниво на ниско напрежение, X - не ме интересува
Свързваме E2 и E1 към маса, E3, A0, A1 и A3 към щифтове PD5, PC3, PC4 и PC5 на STM8. Тъй като таблицата по-горе съдържа както ниски, така и високи нива, ние настройваме тези щифтове като щифтове за натискане и издърпване.
ШИМ
ШИМ на втория таймер е конфигуриран по същия начин, както в предишната история, с две разлики:
Първо, трябва да активираме прекъсването Актуализиране на събитието (UEV), който ще извика функция за превключване на активния светодиод. Това става чрез смяна на бита Разрешаване на прекъсване на актуализиране в регистър с говорещо име
Втората разлика е свързана с такова явление на мултиплексиране като отблясъците - паразитно светене на диоди. В нашия случай може да се появи поради факта, че таймерът, след като е причинил прекъсване на UEV, продължава да тиктака и манипулаторът на прекъсванията няма време да превключи светодиода, преди таймерът да започне да записва нещо на изходите. За да се борите с това, ще трябва да обърнете логиката (0 = максимална яркост, 255 = нищо не е включено) и да не позволявате екстремни стойности на работния цикъл. Тези. уверете се, че след UEV светодиодите са напълно изгасени за един цикъл на ШИМ.
Избягвайте да задавате r, g и b на 255 и не забравяйте да ги обърнете, когато използвате.
Прекъсва
Същността на прекъсването е, че при определени обстоятелства чипът спира да изпълнява основната програма и извиква някаква външна функция. Прекъсванията възникват поради външни или вътрешни влияния, включително от таймера.
Когато за първи път създадохме проект в ST Visual Develop, освен main.c получихме прозорец с мистериозен файл stm8_interrupt_vector.cавтоматично се включва в проекта. В този файл към всяко прекъсване е прикачена функция NonHandledInterrupt. Трябва да обвържем нашата функция с желаното прекъсване.
Листът с данни има таблица с прекъсващи вектори, където намираме тези, от които се нуждаем:
13 Актуализация/препълване на TIM2
14 TIM2 Улавяне/Сравняване
Трябва да сменим светодиода на UEV, така че е необходимо прекъсване #13.
Съответно, първо, във файла stm8_interrupt_vector.c променете името на функцията, отговорна за прекъсване номер 13 (IRQ13) по подразбиране на нашето собствено:
{0x82, TIM2_Overflow}, /* irq13 */
Второ, ще трябва да създадем файл main.h такова съдържание:
@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 - Изключва прекъсванията. Те трябва да бъдат деактивирани, докато новите стойности се записват във "видео паметта", така че прекъсването, причинено в злополучен момент, да не развали масива.