Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер

В Бірінші бөлім Мен Arduino шалбарынан өскен әуесқой электроника инженерлеріне микроконтроллерлерге арналған деректер парақтарын және басқа құжаттамаларды қалай және неге оқу керектігін айтуға тырыстым. Мәтін үлкен болып шықты, сондықтан мен практикалық мысалдарды жеке мақалада көрсетуге уәде бердім. Ал, ол өзін сүтті саңырауқұлақ деп атады ...

Бүгін мен сізге өте қарапайым, бірақ көптеген жобаларға, STM32 (Blue Pill) және STM8 контроллерлеріндегі тапсырмаларды шешу үшін деректер кестелерін қалай пайдалану керектігін көрсетемін. Барлық демо-жобалар менің сүйікті жарық диодтарыма арналған, біз оларды көп мөлшерде жарықтандырамыз, ол үшін қызықты перифериялық құрылғылардың барлық түрлерін қолдануға тура келеді.

Мәтін қайтадан үлкен болып шықты, сондықтан мен ыңғайлы болу үшін мазмұнды жасаймын:

STM32 Blue Pill: DM16 драйвері бар 634 жарықдиодты шам
STM8: алты PWM түйреуіштерін орнату
STM8: үш істікшедегі 8 RGB жарық диоды, үзілістер

Жауапкершіліктен бас тарту: Мен инженер емеспін, электроникада терең білімім бар деп ойламаймын, мақала мен сияқты әуесқойларға арналған. Негізі мен өзімді екі жыл бұрын мақсатты аудитория деп санадым. Егер сол кезде біреу маған бейтаныс чиптегі деректер парақтарын оқу қорқынышты емес деп айтса, мен Интернетте кодтың кейбір бөліктерін іздеп, қайшы мен жабысқақ таспамен балдақтарды ойлап табуға көп уақыт жұмсамас едім.

Бұл мақаланың басты мақсаты жобаларға емес, деректер парақтарына арналған, сондықтан код өте ұқыпты және жиі тар болмауы мүмкін. Жаңа чиппен алғашқы танысу үшін қолайлы болса да, жобалардың өзі өте қарапайым.

Менің мақалам хоббиге енудің ұқсас кезеңінде біреуге көмектеседі деп үміттенемін.

STM32

DM16 және SPI бар 634 жарық диоды

Blue Pill (STM32F103C8T6) және DM634 LED драйверін пайдаланатын шағын жоба. Деректер парақтарын пайдалана отырып, біз драйверді, STM IO порттарын анықтаймыз және SPI конфигурациялаймыз.

DM634

16 16 биттік PWM шығысы бар Тайвань чипін тізбекте қосуға болады. Төмен деңгейлі 12-биттік модель отандық жобадан белгілі Жеңіл пакет. Бір кездері DM63x және белгілі TLC5940 арасында таңдау жасай отырып, мен бірнеше себептер бойынша DM таңдадым: 1) Aliexpress-тегі TLC сөзсіз жалған, бірақ бұл емес; 2) ДМ-де өзінің жиілік генераторы бар автономды PWM бар; 3) Әлиден сәлемдеме күткенше, оны Мәскеуден арзанға сатып алуға болады. Және, әрине, дайын кітапхананы пайдаланудан гөрі, чипті өзіңіз басқаруды үйрену қызықты болды. Чиптер қазір негізінен SSOP24 пакетінде ұсынылған, оларды адаптерге оңай дәнекерлеуге болады.

Өндіруші Тайвань болғандықтан, datashit чип қытай ағылшын тілінде жазылған, яғни ол қызықты болады. Алдымен біз пинутқа қараймыз (Байланыстыру) қай аяқты немен жалғау керектігін түсіну және түйреуіштердің сипаттамасы (Бекіту сипаттамасы). 16 түйреуіш:

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
Тұрақты ток раковинасының көздері (ашық су төгетін)

Раковина / Ашық су төгетін шығыс – су төгу; келіп түсетін ток көзі; шығыс, белсенді күйде, жерге қосылған - жарық диодтары катодтар арқылы драйверге қосылады. Электрлік тұрғыдан бұл, әрине, «ашық дренаж» емес (ашық дренаж), бірақ деректер парағында ағызу режиміндегі түйреуіштер үшін бұл белгілеу жиі кездеседі.

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
Шығыс ток мәнін орнату үшін REXT және GND арасындағы сыртқы резисторлар

Шығулардың ішкі кедергісін басқаратын REXT істікшесі мен жердің арасында анықтамалық резистор орнатылған, деректер парағының 9-бетіндегі графикті қараңыз. DM634-де бұл қарсылықты жалпы жарықтықты орнатып, бағдарламалық құрал арқылы да басқаруға болады (жаһандық жарықтық); Мен бұл мақалада егжей-тегжейлі айтпаймын, мен мұнда 2.2 - 3 кОм резисторды саламын.

Чипті қалай басқару керектігін түсіну үшін құрылғы интерфейсінің сипаттамасын қарастырайық:

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер

Иә, міне, қытай ағылшыны өзінің барлық даңқымен. Мұны аудару қиын, егер қаласаңыз, оны түсінуге болады, бірақ басқа жолы бар - функционалды ұқсас TLC5940 байланысы деректер парағында қалай сипатталғанын қараңыз:

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
... Құрылғыға деректерді енгізу үшін тек үш түйреуіш қажет. SCLK сигналының көтерілу шеті деректерді SIN істікшесінен ішкі регистрге ауыстырады. Барлық деректер жүктелгеннен кейін қысқа жоғары XLAT сигналы дәйекті түрде тасымалданатын деректерді ішкі регистрлерге бекітеді. Ішкі регистрлер XLAT сигнал деңгейімен іске қосылатын қақпалар болып табылады. Барлық деректер алдымен ең маңызды бит жіберіледі.

Лак – ысырма/ысырма/құлып.
Көтерілген жиек – импульстің алдыңғы шеті
Алдымен MSB – ең маңызды (ең сол жақта) бит алға.
деректерді сағаттау үшін – мәліметтерді дәйекті түрде жіберу (бит бойынша).

Сөз ілгек чиптерге арналған құжаттамада жиі кездеседі және әртүрлі тәсілдермен аударылады, сондықтан түсіну үшін мен өзіме рұқсат етемін

шағын білім беру бағдарламасыЖарық диодты драйвер негізінен ауысым регистрі болып табылады. «Ауыстыру» (Ауысым) атауында - құрылғының ішіндегі деректердің биттік қозғалысы: ішіне итерілген әрбір жаңа бит бүкіл тізбекті оның алдына алға қарай итереді. Ауысым кезінде жарық диодтарының ретсіз жыпылықтауын ешкім байқағысы келмейтіндіктен, процесс жұмыс регистрлерінен демпфермен бөлінген буферлік регистрлерде өтеді (ілгек) - бұл биттер қажетті ретпен орналастырылған күту бөлмесінің бір түрі. Барлығы дайын болғанда, ысырма ашылады және биттер алдыңғы топтаманы ауыстыра отырып, жұмысқа кіріседі. Сөз ілгек микросұлбаларға арналған құжаттамада ол қандай комбинацияларда қолданылса да, әрқашан дерлік мұндай амортизаторды білдіреді.

Сонымен, DM634-ге деректерді беру келесідей жүзеге асырылады: DAI кірісін алыс жарық диодының ең маңызды битінің мәніне орнатыңыз, DCK жоғары және төмен тартыңыз; DAI кірісін келесі биттің мәніне орнатыңыз, DCK тартыңыз; және т.б. барлық биттер жіберілгенше (сағат енгізілді), содан кейін біз LAT тартамыз. Мұны қолмен жасауға болады (бит-банг), бірақ бұл үшін арнайы әзірленген SPI интерфейсін қолданған дұрыс, өйткені ол біздің STM32-де екі данада ұсынылған.

Көк таблетка STM32F103

Кіріспе: STM32 контроллері Atmega328-ге қарағанда қорқынышты болып көрінгеннен әлдеқайда күрделі. Сонымен қатар, энергияны үнемдеу мақсатында барлық дерлік перифериялық құрылғылар іске қосылғанда өшіріледі, ал ішкі көзден тактілік жиілігі 8 МГц құрайды. Бақытымызға орай, STM бағдарламашылары чипті «есептелген» 72 МГц-ке жеткізетін кодты жазды және мен білетін барлық IDE авторлары оны инициализациялау процедурасына енгізді, сондықтан бізге сағатты белгілеудің қажеті жоқ (бірақ шынымен қаласаңыз болады). Бірақ сізге перифериялық құрылғыларды қосу керек болады.

Құжаттама: Blue Pill танымал STM32F103C8T6 чипімен жабдықталған, ол үшін екі пайдалы құжат бар:

Деректер парағында бізді қызықтыруы мүмкін:

  • Pinouts – чип пинуттары – егер біз тақталарды өзіміз жасауды шешсек;
  • Жад картасы – белгілі бір чипке арналған жад картасы. Анықтамалық нұсқаулықта бүкіл жолдың картасы бар және онда бізде жоқ регистрлер туралы айтылады.
  • Pin анықтамалары кестесі – түйреуіштердің негізгі және балама функцияларын тізімдеу; «көк таблетка» үшін сіз Интернетте түйреуіштер мен олардың функцияларының тізімі бар ыңғайлы суреттерді таба аласыз. Сондықтан біз бірден Blue Pill pinout google-ді іздейміз және бұл суретті қолымызда ұстаймыз:

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
Ескертпе: Интернеттен алынған суретте қате болды, ол түсініктемелерде жазылған, сол үшін рахмет. Сурет ауыстырылды, бірақ бұл сабақ - ақпаратты деректер парақтарынан емес, тексерген дұрыс.

Біз деректер парағын алып тастаймыз, анықтамалық нұсқаулықты ашамыз және бұдан былай біз тек оны пайдаланамыз.
Процедура: біз стандартты енгізу/шығыспен айналысамыз, SPI конфигурациялаймыз, қажетті перифериялық құрылғыларды қосамыз.

Кіріс шығыс

Atmega328-де енгізу/шығару өте қарапайым орындалады, сондықтан STM32 опцияларының көптігі шатастыруы мүмкін. Енді бізге қорытындылар ғана керек, бірақ олардың да төрт нұсқасы бар:

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
ашық ағызу, итеру-тарту, балама итеру-тарту, балама ашық ағызу

«Тарту-итеру» (итеру) Arduino-ның әдеттегі шығысы болып табылады, түйреуіш ЖОҒАРЫ немесе ТӨМЕН мәнін қабылдай алады. Бірақ «ашық канализация» бар қиындықтар, дегенмен мұнда бәрі қарапайым:

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
Шығыс конфигурациясы / порт шығысқа тағайындалғанда: / шығыс буфері қосылған: / – ашық ағызу режимі: шығыс регистріндегі «0» N-MOS қосады, шығыс регистріндегі «1» портты Hi-Z режимінде қалдырады ( P-MOS қосылмаған ) / – push-pull режимі: шығыс регистріндегі «0» N-MOS, шығыс регистріндегі «1» P-MOS белсендіреді.

Ашық дренаж арасындағы барлық айырмашылық (ашық дренаж) «итермелеуден» (итеру) бірінші түйреуіште ЖОҒАРЫ күйді қабылдай алмайды: шығыс регистріне біреуін жазғанда ол жоғары қарсылық режиміне өтеді (жоғары импеданс, Сәлем-Z). Нөлді жазу кезінде түйреуіш екі режимде де логикалық және электрлік тұрғыдан бірдей әрекет етеді.

Қалыпты шығыс режимінде түйреуіш шығыс регистрінің мазмұнын жай ғана таратады. «Баламада» ол тиісті перифериялық құрылғылармен басқарылады (9.1.4-ті қараңыз):

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
Егер порт биті балама функция істікшесі ретінде конфигурацияланса, түйреуіш тізілімі ажыратылады және пин перифериялық істікшеге қосылады.

Әрбір түйреуіштің балама функционалдығы бөлімде сипатталған Істің анықтамалары Деректер парағы жүктеп алынған суретте. Егер түйреуіштің бірнеше баламалы функциялары болса, не істеу керек деген сұраққа жауап деректер парағындағы сілтеме арқылы беріледі:

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
Егер бірнеше перифериялық құрылғылар бір түйреуішті пайдаланса, балама функциялар арасындағы қайшылықты болдырмау үшін бір уақытта тек бір перифериялық құрылғы пайдаланылуы керек, перифериялық сағатты қосу биті арқылы ауыстырылады (тиісті RCC регистрінде).

Соңында, шығыс режиміндегі түйреуіштер де тактілік жылдамдыққа ие. Бұл энергияны үнемдеудің тағы бір мүмкіндігі; біздің жағдайда біз оны максимумға орнатып, оны ұмытып кетеміз.

Сонымен: біз SPI қолданамыз, яғни екі түйреуіш (деректермен және сағат сигналымен) «балама итеру функциясы» болуы керек, ал екіншісі (LAT) «тұрақты итеру» болуы керек. Бірақ оларды тағайындамас бұрын, SPI-мен айналысайық.

SPI

Тағы бір шағын білім беру бағдарламасы

SPI немесе Serial Peripheral Interface (сериялық перифериялық интерфейс) МК-ны басқа МК-мен және жалпы сыртқы әлеммен қосуға арналған қарапайым және өте тиімді интерфейс. Оның жұмыс істеу принципі жоғарыда сипатталған, мұнда қытайлық жарықдиодты драйвер туралы (анықтамалық нұсқаулықта 25-бөлімді қараңыз). SPI негізгі («шебер») және тәуелді («құл») режимінде жұмыс істей алады. SPI төрт негізгі арнадан тұрады, олардың барлығын қолдануға болмайды:

  • MOSI, негізгі шығыс / бағынышты кіріс: бұл түйреуіш негізгі режимде деректерді жібереді және бағынышты режимде деректерді қабылдайды;
  • MISO, Master Input / Slave Output: керісінше, ол шеберде алады, ал бағында жібереді;
  • SCK, Serial Clock: негізгі құрылғыда деректерді беру жиілігін орнатады немесе бағыныштыда тактілік сигналды қабылдайды. Негізінде соққылар;
  • SS, Slave Select: осы арнаның көмегімен құл өзінен бірдеңе сұралғанын біледі. STM32-де ол NSS деп аталады, мұнда N = теріс, яғни. осы арнада жер болса, контроллер құлға айналады. Ол Open Drain Output режимімен жақсы үйлеседі, бірақ бұл басқа әңгіме.

Барлық басқалар сияқты, STM32 жүйесіндегі SPI функционалдылыққа бай, бұл оны түсінуді біршама қиындатады. Мысалы, ол тек SPI-мен ғана емес, сонымен қатар I2S интерфейсімен де жұмыс істей алады, ал құжаттамада олардың сипаттамалары араласады, артықшылығын дер кезінде кесіп тастау қажет. Біздің міндетіміз өте қарапайым: бізге деректерді тек MOSI және SCK арқылы жіберу керек. Біз 25.3.4 бөліміне өтеміз (жартылай дуплексті байланыс, жартылай дуплексті байланыс), онда біз табамыз 1 сағат және 1 бір бағытты деректер сымы (1 тактілік сигнал және 1 бір бағытты деректер ағыны):

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
Бұл режимде қолданба тек жіберу немесе тек қабылдау режимінде SPI пайдаланады. / Тек жіберу режимі дуплексті режимге ұқсас: деректер жіберу істікшесі арқылы беріледі (басты режимдегі MOSI немесе бағынышты режимде MISO), ал қабылдау істікшелі (MISO немесе MOSI сәйкесінше) кәдімгі енгізу/шығару істікшесі ретінде пайдаланылуы мүмкін. . Бұл жағдайда қолданба тек Rx буферін елемеуі керек (егер ол оқылса, онда тасымалданатын деректер болмайды).

Керемет, MISO түйреуіштері бос, оған LAT сигналын қосайық. Slave Select-ті қарастырайық, оны STM32-де бағдарламалық түрде басқаруға болады, бұл өте ыңғайлы. Біз аттас абзацты 25.3.1 SPI бөлімінде оқимыз Жалпы сипаттама:

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
Бағдарламалық құралды басқару NSS (SSM = 1) / Бағынышты таңдау туралы ақпарат SPI_CR1 регистрінің SSI битінде қамтылған. Сыртқы NSS PIN коды басқа қолданба қажеттіліктері үшін бос қалады.

Регистрлерге жазу уақыты келді. Мен SPI2 пайдалануды шештім, оның негізгі мекенжайын деректер кестесінен іздеңіз - 3.3 Жад картасы бөлімінде:

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер

Ал, бастайық:

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

25.3.3 бөлімін «Мастер режимде SPI конфигурациялау» өздігінен түсіндірілетін тақырыппен ашыңыз:

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер

1. SPI_CR2 регистрінде BR[0:1] биттері бар сериялық тактілік жиілікті орнатыңыз.

Регистрлер анықтамалық нұсқаулықтың аттас бөлімінде жинақталған. Мекенжайды ауыстыру (Мекенжайдың ығысуы) CR1 – 0x00 үшін, әдепкі бойынша барлық биттер тазаланады (Мәнді қалпына келтіру 0x0000):

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер

BR биттері контроллердің сағат бөлгішін орнатады, осылайша SPI жұмыс істейтін жиілікті анықтайды. Біздің STM32 жиілігі 72 МГц болады, LED драйвері оның деректер кестесіне сәйкес 25 МГц жиілікте жұмыс істейді, сондықтан төртке бөлу керек (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-беттегі диаграмманы қараңыз)

Біз мұнда деректер кестесін оқып жатқандықтан және схемаларды қарастырмағандықтан, 704-беттегі CPOL және CPHA биттерінің мәтіндік сипаттамасын толығырақ қарастырайық (SPI жалпы сипаттамасы):

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
Сағат фазасы және полярлығы
SPI_CR1 регистрінің CPOL және CPHA биттерін пайдалану арқылы төрт уақыт қатынасын бағдарламалы түрде таңдауға болады. CPOL (сағат полярлығы) биті ешқандай деректер берілмейтін кезде тактілік сигналдың күйін басқарады. Бұл бит негізгі және қосалқы режимдерді басқарады. CPOL қалпына келтірілсе, SCK істікшесі демалыс режимінде төмен болады. CPOL биті орнатылған болса, SCK істікшесі демалыс режимінде жоғары болады.
CPHA (сағаттық фаза) биті орнатылған кезде, жоғары биттік трап стробы SCK сигналының екінші жиегі болып табылады (CPOL анық болса төмендейді, CPOL орнатылған болса жоғарылайды). Мәліметтер тактілік сигналдың екінші өзгерісімен түсіріледі. CPHA биті анық болса, жоғары разрядты ұстау стробы SCK сигналының көтерілу жиегі болып табылады (CPOL орнатылған болса, төмендейтін жиек, CPOL тазартылған болса, көтерілетін жиек). Деректер тактілік сигналдың бірінші өзгерісінде түсіріледі.

Осы білімді сіңіре отырып, біз екі бит нөл болып қалуы керек деген қорытындыға келеміз, өйткені Біз SCK сигналы пайдаланылмаған кезде төмен болғанын және деректер импульстің көтерілу жиегінде берілуін қалаймыз (суретті қараңыз). Rising Edge DM634 деректер парағында).

Айтпақшы, біз алдымен ST деректер парақтарындағы сөздіктің ерекшелігін кездестірдік: оларда «битті нөлге қалпына келтіру» тіркесі жазылған. сәл қалпына келтіру үшін, жоқ аздап тазарту үшін, мысалы, Atmega.

3. Деректер блогының 8-биттік немесе 16-биттік пішім екенін анықтау үшін DFF битін орнатыңыз

Мен DM16 сияқты 634 биттік PWM деректерін жіберуге алаңдамау үшін арнайы 12 биттік DM633 алдым. DFF параметрін біреуіне орнату мағынасы бар:

#define DFF         0x0800

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

4. Блок пішімін анықтау үшін SPI_CR1 регистріндегі LSBFIRST битін теңшеңіз

LSBFIRST, аты айтып тұрғандай, алдымен ең аз маңызды битпен жіберуді конфигурациялайды. Бірақ DM634 ең маңызды биттен бастап деректерді алғысы келеді. Сондықтан біз оны қалпына келтіреміз.

5. Аппараттық режимде, егер NSS істікшесінен енгізу қажет болса, бүкіл байтты тасымалдау тізбегі кезінде NSS істікшесіне жоғары сигнал беріңіз. NSS бағдарламалық құралы режимінде SPI_CR1 регистрінде SSM және SSI биттерін орнатыңыз. Егер NSS істікшесі шығыс ретінде пайдаланылса, тек SSOE битін орнату керек.

NSS аппараттық режимін ұмыту үшін SSM және SSI орнатыңыз:

#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: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
Деректерді тасымалдау тәртібі
Тасымалдау Tx буферіне байт жазылған кезде басталады.
Деректер байты ауысым регистріне жүктеледі параллель режимі (ішкі шинадан) бірінші разрядты беру кезінде, содан кейін ол беріледі ретті MOSI түйреуіш режимі, CPI_CR1 регистріндегі LSBFIRST бит параметріне байланысты бірінші немесе соңғы бит алға. TXE жалауы мәліметтерді жібергеннен кейін орнатылады Tx буферінен ауыстыру регистріне, сондай-ақ CPI_CR1 регистріндегі TXEIE биті орнатылған болса үзуді жасайды.

Мен STM контроллерлерінде SPI енгізудің бір ерекшелігіне назар аудару үшін аудармада бірнеше сөзді атап өттім. Atmega-да TXE жалауы (Tx бос, Tx бос және деректерді қабылдауға дайын) бүкіл байт жіберілгеннен кейін ғана орнатылады шығу. Ал мұнда бұл жалау байт ішкі ауысым регистріне енгізілгеннен кейін орнатылады. Ол барлық биттермен бір уақытта (параллельде) сонда итеріліп, содан кейін деректер дәйекті түрде тасымалданатындықтан, TXE байт толығымен жіберілгенге дейін орнатылады. Бұл маңызды, өйткені біздің LED драйверімізде жібергеннен кейін LAT түйреуіштерін тартуымыз керек всех деректер, яғни. Біз үшін тек TXE жалауы жеткіліксіз болады.

Бұл бізге басқа ту керек дегенді білдіреді. 25.3.7 - «Мәртебелік жалаушаларды» қарастырайық:

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
<...>
Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
BUSY жалаушасы
BSY жалауы аппараттық құрал арқылы орнатылады және тазаланады (оған жазу ешқандай әсер етпейді). BSY жалауы SPI байланыс деңгейінің күйін көрсетеді.
Ол қалпына келтіреді:
тасымалдау аяқталғанда (тасымалдау үздіксіз болса, негізгі режимді қоспағанда)
SPI өшірілгенде
негізгі режим қатесі орын алған кезде (MODF = 1)
Тасымалдау үздіксіз болмаса, әрбір деректерді тасымалдау арасында BSY жалауы жойылады

Жарайды, бұл пайдалы болады. Tx буферінің қай жерде орналасқанын білейік. Ол үшін «SPI деректер тізілімін» оқыңыз:

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
Биттар 15:0 DR[15:0] Деректер тізілімі
Алынған деректер немесе жіберілетін деректер.
Мәліметтер регистрі екі буферге бөлінеді - біреуі жазуға арналған (беру буфері) және екіншісі оқуға арналған (қабылдау буфері). Деректер регистріне жазу Tx буферіне жазылады, ал деректер регистрінен оқу Rx буферіндегі мәнді қайтарады.

Ал, TXE және BSY жалаулары табылған күй регистрі:

Деректер парақтарын оқу 2: STM32 бойынша SPI; 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 есе екі байтты жіберу керек болғандықтан, келесідей нәрсе:

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: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
Жалпы мақсаттағы енгізу/шығару порттарының әрқайсысы (GPIO) екі 32-биттік конфигурация регистрлері (GPIOx_CRL және GPIOx_CRH), екі 32-биттік деректер регистрлері (GPIOx_IDR және GPIOx_ODR), 32-биттік орнату/қалпына келтіру регистрі (GPIOx_BSRR), 16-биттік қалпына келтіру регистрі (GPIOx_BRR) және 32- бит блоктау регистрі (GPIOx_LCKR).

Алғашқы екі регистр әдеттен тыс, сонымен қатар өте ыңғайсыз, өйткені 16 порт түйреуіштері «бір ағаға төрт бит» форматында шашыраңқы. Анау. нөлден жетіге дейінгі түйреуіштер CRL-де, ал қалғандары CRH-де. Сонымен қатар, қалған регистрлер порттың барлық түйреуіштерінің биттерін сәтті қамтиды - көбінесе жартысы «резерленген» қалады.

Қарапайымдылық үшін тізімнің соңынан бастайық.

Бізге блоктау регистрінің қажеті жоқ.

Орнату және қалпына келтіру регистрлері өте күлкілі, өйткені олар бір-бірін жартылай қайталайды: сіз бәрін тек BSRR-де жаза аласыз, мұнда жоғары 16 бит пинді нөлге келтіреді, ал төменгілері 1-ге орнатылады немесе сіз де жасай аласыз. BRR пайдаланыңыз, оның төменгі 16 биттері тек түйреуішті қалпына келтіреді. Маған екінші нұсқа ұнайды. Бұл регистрлер маңызды, өйткені олар түйреуіштерге атомдық қолжетімділікті қамтамасыз етеді:

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
Atomic Set немесе Reset
GPIOx_ODR бит деңгейінде бағдарламалау кезінде үзілістерді өшірудің қажеті жоқ: бір немесе бірнеше биттерді APB2 бір атомдық жазу операциясымен өзгертуге болады. Бұған өзгерту қажет биттің орнату/қалпына келтіру регистріне (GPIOx_BSRR немесе тек қалпына келтіру үшін, GPIOx_BRR) «1» жазу арқылы қол жеткізіледі. Басқа биттер өзгеріссіз қалады.

Деректер регистрлерінің өзіндік түсінікті атаулары бар - IDR = енгізу Бағыт регистрі, енгізу регистрі; ODR = шығыс Бағыт регистрі, шығыс регистрі. Қазіргі жобада олар бізге қажет емес.

Соңында, бақылау регистрлері. Бізді екінші SPI түйреуіштері, атап айтқанда PB13, PB14 және PB15 қызықтыратындықтан, біз бірден CRH-ге қараймыз:

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер

Біз 20-дан 31-ге дейін биттермен бірдеңені жазуымыз керек екенін көреміз.

Біз жоғарыда түйреуіштерден не қалайтынымызды анықтадық, сондықтан мен скриншотсыз жасаймын, тек MODE бағытты (егер екі бит 0-ге орнатылған болса, енгізу) және түйреуіш жылдамдығын (бізге 50 МГц қажет, яғни. екі түйреуіш те «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;

Тиісінше, сіз BRR және BSRR регистрлері арқылы бұралатын LAT анықтамаларын жаза аласыз:

/*** 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 аббревиатурасын байқадық. Біз оны құжаттамадан іздейміз: бұл Қалпына келтіру және сағатты басқару.

Жоғарыда айтылғандай, бақытымызға орай, сағаттық тақырыптың ең қиын бөлігін STM қызметкерлері жасады, бұл үшін біз оларға үлкен алғыс айтамыз (тағы да сілтеме беремін. Ди Халттың сайты, оның қаншалықты түсініксіз екенін түсіну үшін). Бізге тек перифериялық сағатты қосуға жауапты регистрлер қажет (Перифериялық сағатты қосу регистрлері). Алдымен, RCC негізгі мекенжайын табайық, ол «Жад картасының» ең басында:

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер

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

Содан кейін тақтадан бірдеңе табуға тырысатын сілтемені басыңыз немесе одан да жақсысы туралы бөлімдердегі қосу регистрлерінің сипаттамаларын қараңыз. регистрлерді қосу. RCC_APB1ENR және RCC_APB2ENR қайдан табамыз:

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және 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: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер

STM8 PWM

STM8 жүйесіндегі PWM

Мен осы мақаланы енді ғана жоспарлаған кезде, мен етіксіз етікші болмас үшін, мысал ретінде тек деректер кестесін пайдаланып, бейтаныс чиптің кейбір функционалдығын меңгеруге тырысуды шештім. STM8 бұл рөл үшін өте қолайлы болды: біріншіден, менде STM8S103 бар бірнеше қытай тақталары болды, екіншіден, ол өте танымал емес, сондықтан Интернетте оқуға және шешім табуға азғырылу осы шешімдердің жоқтығына байланысты.

Чипте де бар datashit и анықтамалық нұсқаулық RM0016, біріншісінде pinout және тіркелу мекенжайлары бар, екіншісінде - қалғанының бәрі. STM8 C тілінде қорқынышты IDE-де бағдарламаланған ST Visual Develop.

Сағат және енгізу/шығару

Әдепкі бойынша, STM8 2 МГц жиілікте жұмыс істейді, бұл дереу түзетілуі керек.

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
HSI (жоғары жылдамдықты ішкі) сағаты
HSI тактісінің сигналы бағдарламаланатын бөлгіші (16-ден 1-ге дейін) бар ішкі 8 МГц RC осцилляторынан алынған. Ол сағатты бөлу регистрінде (CLK_CKDIVR) орнатылады.
Ескерту: басында 8 бөлгіші бар HSI RC осцилляторы тактілік сигналдың жетекші көзі ретінде таңдалады.

Біз деректер парағында тізілім мекенжайын, refman ішіндегі сипаттаманы табамыз және регистрді тазалау қажет екенін көреміз:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Біз PWM-ді іске қосып, жарықдиодты шамдарды қосатындықтан, пинутқа қарайық:

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер

Чип кішкентай, көптеген функциялар бір түйреуіштерде тоқтатылады. Шаршы жақшадағы «балама функционалдылық» болып табылады, ол «опция байттары» арқылы ауыстырылады (опция байттары) – Atmega сақтандырғыштары сияқты нәрсе. Сіз олардың мәндерін бағдарламалық түрде өзгерте аласыз, бірақ бұл қажет емес, өйткені Жаңа функция қайта жүктегеннен кейін ғана іске қосылады. Бұл байттарды өзгерте алатын ST Visual Programmer (Visual Develop арқылы жүктелген) пайдалану оңайырақ. Пинус бірінші таймердің CH1 және CH2 түйреуіштері төртбұрышты жақшаларда жасырылғанын көрсетеді; STVP-де AFR1 және AFR0 биттерін орнату қажет, ал екіншісі екінші таймердің CH1 шығысын PD4-тен PC5-ке ауыстырады.

Осылайша, 6 түйреуіш жарық диодтарын басқарады: бірінші таймер үшін PC6, PC7 және PC3, екіншісі үшін PC5, PD3 және PA3.

STM8 жүйесінде енгізу/шығару түйреуіштерін орнату STM32-ге қарағанда қарапайым және қисындырақ:

  • Atmega DDR деректер бағыты регистрінен таныс (Деректер бағыты тіркелімі): 1 = шығыс;
  • бірінші басқару регистрі CR1, шығарылған кезде итеру режимін (1) немесе ашық ағызуды (0) орнатады; Мен светодиодтарды чипке катодтармен қосатындықтан, мен мұнда нөлдерді қалдырамын;
  • екінші басқару регистрі CR2, шығыс кезінде, тактілік жиілікті орнатады: 1 = 10 МГц

#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 жұмыс циклі – PWM жұмыс циклі, көбінесе «жұмыс факторы» деп аталады;
  • Мәнді түсіру/салыстыру – таймер есептеген түсіру/салыстыру мәні бірдеңе жасайды (PWM жағдайында ол шығыс сигналын инверсиялайды);
  • Алдын ала жүктеу мәні – алдын ала жүктелген мән. Мәнді салыстыру таймер өтіп жатқанда өзгерте алмайды, әйтпесе PWM циклі үзіледі. Сондықтан, жаңа жіберілген мәндер буферге орналастырылады және таймер кері санақтың соңына жеткенде және қалпына келтірілгенде шығарылады;
  • Жиегі бойынша тураланған и Ортаға тураланған режимдер – шекара бойымен және ортада туралау, Атмелдікімен бірдей Жылдам PWM и Фазалық дұрыс PWM.
  • OCiREF, шығысты салыстыру анықтамалық сигналы – анықтамалық шығыс сигналы, шын мәнінде, PWM режимінде сәйкес істікшеде пайда болатын нәрсе.

Түйіннен көрініп тұрғандай, екі таймердің PWM мүмкіндіктері бар - бірінші және екінші. Екеуі де 16 биттік, біріншісінде көптеген қосымша мүмкіндіктер бар (атап айтқанда, ол жоғары да, төмен де санай алады). Екеуміз де бірдей жұмыс істеуіміз керек, сондықтан жоқ нәрсені кездейсоқ пайдаланбау үшін мен анық кедей екіншісінен бастауды шештім. Кейбір мәселе, анықтамалық нұсқаулықтағы барлық таймерлердің PWM функциясының сипаттамасы бірінші таймер (17.5.7 PWM режимі) туралы тарауда берілген, сондықтан құжат бойына үнемі алға және артқа секіру керек.

STM8-дегі PWM Atmega-дағы PWM-ден маңызды артықшылығы бар:

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
Шектелген PWM
Тіркелгі конфигурациясы төменнен жоғарыға
TIM_CR1 регистріндегі DIR биті тазартылса, төменнен жоғары санау белсенді болады
Мысал:
Мысал бірінші 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 (жарық диоды қосулы) болғанша шығуы керек. мәнін салыстыру және 1 кейін.

Кейбіреулері туралы біз бұрыннан оқыдық PWM режимі, сондықтан біз осы фразаны анықтамалық нұсқаулықта іздеу арқылы екінші таймердің қажетті регистрін табамыз (18.6.8 - TIMx_CCMR1):

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
110: Бірінші PWM режимі – төменнен жоғарыға қарай санағанда, TIMx_CNT < TIMx_CCR1 кезінде бірінші арна белсенді болады. Әйтпесе, бірінші арна белсенді емес. [бұдан әрі құжатта 1-таймерден қате көшіру-қою бар] 111: Екінші PWM режимі – төменнен жоғарыға қарай санау кезінде TIMx_CNT < TIMx_CCR1 кезінде бірінші арна белсенді емес. Әйтпесе, бірінші арна белсенді болады.

Жарықдиодтар катодтар арқылы МК-ға қосылғандықтан, екінші режим бізге сәйкес келеді (біріншісі де, бірақ біз оны әлі білмейміз).

Деректер парақтарын оқу 2: STM32 бойынша SPI; 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;

Қорытындыларды және екінші таймерді қосу ғана қалады. Бірінші мәселе регистрлер арқылы шешіледі Түсіру/Салыстыру қосу: олардың бойымен асимметриялы түрде шашыраңқы екі, үш арна бар. Мұнда біз сигналдың полярлығын өзгертуге болатынын да біле аламыз, яғни. Негізінде 1 PWM режимін пайдалану мүмкін болды. Біз жазамыз:

#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: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Салыстыру үшін нақты мәндерді таймерге жіберетін AnalogWrite() қарапайым аналогын жазайық. Регистрлер болжамды түрде аталды Регистрлерді түсіру/салыстыру, әрбір арна үшін олардың екеуі бар: TIM8_CCRxL ішіндегі төменгі ретті 2 бит және 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 мәнде сигнал бір таймер циклі үшін төңкеріледі). Жарықдиодты шамдар үшін бұл маңызды емес және мұқият оқырман оны қалай түзетуге болатынын болжауы мүмкін.

Екінші таймердегі PWM жұмыс істейді, біріншіге көшейік.

Бірінші таймердің бірдей регистрлерде бірдей биттері бар (тек екінші таймерде «сақталған» қалған биттер біріншісінде барлық жетілдірілген нәрселер үшін белсенді қолданылады). Сондықтан деректер парағында бірдей регистрлердің мекенжайларын тауып, кодты көшіру жеткілікті. Жиілік бөлгіштің мәнін өзгертіңіз, себебі... бірінші таймер екінің дәрежесін емес, екі регистрдегі дәл 16-биттік мәнді алғысы келеді Prescaler High и төмен. Біз бәрін жасаймыз және... бірінші таймер жұмыс істемейді. Не болды?

Мәселені тек 1-таймердің басқару регистрлері туралы бүкіл бөлімді қарау арқылы шешуге болады, онда біз екінші таймерде жоқ біреуін іздейміз. Мында болады 17.7.30 Үзіліс регистрі (TIM1_BKR), бұл бит қай жерде:

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
Негізгі шығысты қосыңыз

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Мұның бәрі қазір анық, код сол жерде.

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер

STM8 мультиплексі

STM8-де мультиплекстеу

Үшінші шағын жоба - сегіз RGB жарық диодты PWM режиміндегі екінші таймерге қосу және оларды әртүрлі түстермен көрсету. Ол жарықдиодты мультиплекстеу тұжырымдамасына негізделген, яғни егер сіз жарықдиодты шамдарды өте және өте жылдам қоссаңыз және өшірсеңіз, олар үнемі қосулы болып көрінеді (көру қабілетінің тұрақтылығы, көрнекі қабылдау инерциясы). Мен бір рет жасадым Arduino-да осындай нәрсе.

Жұмыс алгоритмі келесідей көрінеді:

  • бірінші RGB светодиодының анодын қосты;
  • катодтарға қажетті сигналдарды жібере отырып, оны жағу;
  • PWM циклінің соңына дейін күтті;
  • екінші RGB светодиодының анодын қосты;
  • жанды...

Ал, т.б. Әрине, әдемі жұмыс істеу үшін анодты қосу және жарық диодты бір уақытта «жану» қажет. Жақсы немесе дерлік. Қалай болғанда да, екінші таймердің үш арнасында мәндерді шығаратын кодты жазу керек, UEV жеткенде оларды өзгерту және сонымен бірге қазіргі уақытта белсенді RGB жарық диодты өзгерту керек.

Жарықдиодты ауыстыру автоматты болғандықтан, үзіліс өңдеушісі деректерді қабылдайтын «бейне жадын» жасау керек. Бұл қарапайым массив:

uint8_t colors[8][3];

Белгілі бір жарықдиодтың түсін өзгерту үшін осы массивке қажетті мәндерді жазу жеткілікті болады. Ал айнымалы шама белсенді жарық диоды санына жауапты болады

uint8_t cnt;

Демукс

Дұрыс мультиплекстеу үшін бізге CD74HC238 демультиплексоры керек. Демультиплексор – аппараттық құралда операторды жүзеге асыратын чип <<. Үш кіріс түйреуіштері (0, 1 және 2 биттері) арқылы біз оған үш разрядты X санын береміз және жауап ретінде ол шығыс нөмірін белсендіреді (1<<X). Чиптің қалған кірістері бүкіл дизайнды масштабтау үшін пайдаланылады. Бұл чип бізге микроконтроллердің бос тұрған түйреуіштерінің санын азайту үшін ғана емес, сонымен қатар қауіпсіздік үшін қажет - кездейсоқ жарық диодтарын мүмкіндігінше көп қоспау және МК-ны күйдірмеу үшін. Чиптің құны бір тиын және әрқашан үйдегі дәрі-дәрмек шкафында сақталуы керек.

Біздің CD74HC238 қажетті жарықдиодтың анодына кернеу беру үшін жауапты болады. Толық мультиплексте ол P-MOSFET арқылы бағанға кернеу береді, бірақ бұл демонстрацияда бұл тікелей мүмкін, өйткені сәйкес 20 мА тартады абсолютті максималды рейтингтер деректер парағында. бастап CD74HC238 деректер парағы Бізге түйреуіштер мен мына парақ қажет:

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
H = жоғары кернеу деңгейі, L = төмен кернеу деңгейі, X – бәрібір

Біз E2 және E1-ді жерге, E3, A0, A1 және A3-ті STM5 PD3, PC4, PC5 және PC8 түйреуіштеріне қосамыз. Жоғарыдағы кестеде төменгі және жоғары деңгейлер бар болғандықтан, біз бұл түйреуіштерді итергіш түйреуіштер ретінде конфигурациялаймыз.

PWM

Екінші таймердегі PWM екі айырмашылығы бар алдыңғы әңгімедегідей конфигурацияланған:

Біріншіден, біз үзуді қосуымыз керек Жаңарту оқиғасы (UEV) ол белсенді ЖШД ауыстыратын функцияны шақырады. Бұл битті өзгерту арқылы жасалады Жаңарту үзілісін қосу аты бар тізілімде

Деректер парақтарын оқу 2: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер
Үзіліс қосу тіркелімі

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

Екінші айырмашылық мультиплексирлеу құбылысымен байланысты, мысалы құпияшылдықтың – диодтардың паразиттік жарқырауы. Біздің жағдайда, бұл UEV-де үзіліс тудырған таймер таңбалауды жалғастыруымен және таймер түйреуіштерге бірдеңе жаза бастағанға дейін үзіліс өңдеушісінің жарық диодты ауыстырып үлгермеуіне байланысты пайда болуы мүмкін. Мұнымен күресу үшін логиканы төңкеру керек (0 = максималды жарықтық, 255 = ештеңе жанбайды) және экстремалды жұмыс циклінің мәндерін болдырмау керек. Анау. UEV кейін жарық диодтары бір 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: STM32 бойынша SPI; 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: STM32 бойынша SPI; PWM, таймерлер және STM8 үзілістер

Егер кем дегенде біреу бұл мақаланы пайдалы деп тапса, мен оны бекер жазған жоқпын. Пікірлер мен ескертулерді алуға қуаныштымын, мен бәріне жауап беруге тырысамын.

Ақпарат көзі: www.habr.com

пікір қалдыру