-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathall.html
1096 lines (1049 loc) · 240 KB
/
all.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>Курс</title><meta content="yes" name="apple-mobile-web-app-capable"><meta content="black-translucent" name="apple-mobile-web-app-status-bar-style"><meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui" name="viewport"><link href="reveal.js/css/reveal.css" rel="stylesheet"><link rel="stylesheet" href="reveal.js/css/theme/sky.css" id="theme"><!--This CSS is generated by the Asciidoctor-Reveal.js converter to further integrate AsciiDoc's existing semantic with Reveal.js--><style type="text/css">.reveal div.right {
float: right;
}
/* callouts */
.conum[data-value] {display:inline-block;color:#fff!important;background-color:rgba(50,150,50,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
.conum[data-value] *{color:#fff!important}
.conum[data-value]+b{display:none}
.conum[data-value]:after{content:attr(data-value)}
pre .conum[data-value]{position:relative;top:-.125em}
b.conum *{color:inherit!important}
.conum:not([data-value]):empty{display:none}</style><link href="reveal.js/lib/css/zenburn.css" rel="stylesheet"><script>var link = document.createElement( 'link' );
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = window.location.search.match( /print-pdf/gi ) ? "reveal.js/css/print/pdf.css" : "reveal.js/css/print/paper.css";
document.getElementsByTagName( 'head' )[0].appendChild( link );</script><!--[if lt IE 9]><script src="reveal.js/lib/js/html5shiv.js"></script><![endif]--></head><body><div class="reveal"><div class="slides"><section class="title" data-state="title"><h1>Курс</h1><div class="preamble"><hr></div></section>
<section id="_предисловие"><h2>Предисловие</h2><div class="paragraph"><p>Специальность “Информационно-измерительная техника” направлена на создание и применение устройств и
систем, составляющих основу информационных технологий в различных отраслях промышленности.
Особое внимание должно уделяться компьютерной или микропроцессорной техники как со стороны
аппаратного, так и программного обеспечения.</p></div>
<div class="paragraph"><p>В современном мире неотъемлемой частью практически любого измерительного устройства является
микроконтроллер. Важной особенностью применения микроконтроллеров в измерительных устройствах
является тот факт, что для надежной работы такого устройства необходимо не только надежная
аппаратура, но и качественное и надежное программное обеспечение управляющее микроконтроллером.
В настоящее время существует очень много методических пособий и книг по разработке устройств с
использованием микроконтроллеров, однако вопросы разработки программного обеспечения сводятся к
простым примерам на языке ассемблера и Си. Кроме того, существующие пособия значительно отстают от
быстроменяющихся изменений в микропроцессорной технике и тем более языках программирования.
Если еще недавно прорывом в программирование был выход стандарта С11, то уже сегодня существует
стандарт С20 и уже активна работа по стандарту С++23. Следует также заменить, что автором не
найдено ни одной книги или пособия, которые бы затрагивали, например, такие области разработки ПО
для микроконтроллеров, как архитектура программного обеспечения, использования UML и средств
моделирования архитектуры.</p></div>
<div class="paragraph"><p>Предыдущие методические пособия для курса ПОИП, например, <a href="#2">[2]</a> были ориентированы на широкие области
применения информационных технологий, начиная от микроконтроллеров и заканчивая базами данных.
Однако по мнению автора, невозможно хорошо разобраться и усвоить столь большой объем разноплановой
информации. В итоге курс и лабораторные работы дают лишь поверхностное представление о разработке
программного обеспечения, а будущие инженеры не до конца усваивают материал и не могут детально
разобраться в принципах разработки программного обеспечения для измерительных устройств. Основываясь
на данном предубеждение, автором выбран иной путь, а именно более узкоспециализированное и
детальное рассмотрение принципов разработки программного обеспечения измерительных устройств на
базе современных микроконтроллеров.</p></div>
<div class="paragraph"><p>Большую помощь в разработке методического материала оказал обучающийся на кафедре ИнИТ Загоскин Я.
Современные быстроизменяющиеся и эволюционирующие условия диктуют и новый подход к образованию, а
именно все больший упор делается на самообразование, самоусовершенствование и самостоятельный поиск
нужной информации с технической документации, системах поиска, книгах.</p></div>
<div class="paragraph"><p>Поэтому довольно большая часть разделов предлагается студентам для самостоятельного изучения и
выполнения в качестве домашней практической работы.
Большое влияние на составление данного методического пособия оказал труд <a href="#1">[1]</a> Недяка С.П., Шаропина
Ю.Б. откуда были заимствованы некоторые подходы и организационная структура методического пособия.</p></div></section>
<section id="_введение"><h2>Введение</h2><div class="paragraph"><p>Данное методическое пособие предназначено для выполнения лабораторных работ с использованием
отладочных плат XNUCLEO F411RE для измерения физических величин на основе микроконтроллера STM32F411
с архитектурой Cortex-M4, операционной системы реального времени FreeRtos и языка программирования
С++ 14 интегрированной средой разработки IAR Embedded Workbench for ARM ver. 8.20</p></div>
<div class="paragraph"><p>Методическое пособие написано в рамках курса “Программное обеспечение измерительных процессов”, но
оно будет полезным всем желающим освоить принципы разработки измерительных устройств на современных
микроконтроллерах.</p></div>
<div class="paragraph"><p>Предполагается, что для изучения данного курса у студентов есть хорошие знания микропроцессорной
техники и навыки разработки ПО на языке С++ полученные на ранних курсах.</p></div>
<div class="paragraph"><p>Методическое пособие состоит из 3 разделов. Первая часть ориентирована на создание проекта в IAR
Embedded Workbench, работу с периферией микроконтроллера и возможности отладки системы IAR Embedded
Workbench, работе с детальной разработкой простых классов на языке UML в пакете starUML. Вторая
часть посвящена использованию прерываний, работе с операционной системой FreeRtos взаимодействию
между задачами. Заключительная часть ориентирована на принцип разработки архитектуры программного
обеспечения, шаблонам проектирования, разработки детальной архитектуры.</p></div>
<div class="paragraph"><p>Модули связаны с курсовым проектированием в котором буду задействованы все части данного
методического пособия.</p></div>
<div class="paragraph"><p>В 2017 году компания “Метран” безвозмездно предоставила ЮУрГУ на кафедру информационно-измерительная
техника 10 отладочных комплектов на базе микропроцессора Stm32F411RE на ядре Cortex M4 с различными
модулями расширения включающие в себя модули Bluetooth, WiFi, графическим индикатором, различными
сенсорами, включающие в себя датчики Холла, датчики влажности, температуры, звука, освещенности,
дыма, положения и многое другое. По этой причине курс ПОИП и лабораторный практикум выполняется
на отладочных платах XNUCLEO 411RE.</p></div></section>
<section id="_лекция_1"><h2>Лекция 1</h2></section>
<section id="_основные_термины_и_определения"><h2>Основные термины и определения</h2><div class="title">Встраиваемые вычислительные системы (Embedded systems)</div><pre class="highlight listingblock"><code data-noescape class="text language-text">Встраииваемая система (встроенная систеима, англ. embedded system) — специализированная
микропроцессорная система управления, концепция разработки которой заключается в том, что такая
система будет работать, будучи встроенной непосредственно в устройство, которым она управляет.</code></pre>
<div class="title">Микроконтроллер (англ. Micro Controller Unit, MCU)</div><pre class="highlight listingblock"><code data-noescape class="text language-text">Микросхема, предназначенная для управления электронными устройствами. Типичный микроконтроллер
сочетает на одном кристалле функции процессора и периферийных устройств, содержит ОЗУ и (или) ПЗУ.
По сути, это однокристальный компьютер, способный выполнять простые задачи.</code></pre>
<div class="title">Контроллер</div><pre class="highlight listingblock"><code data-noescape class="text language-text">1. Изделие для автоматизации и управления.
2. Микросхема или часть микросхемы реализующая отдельную функцию или задачу управления.</code></pre>
<div class="title">Отладочная, oценочная или демонстрационная плата</div><pre class="highlight listingblock"><code data-noescape class="text language-text">Электронный модуль, как правило, в бескорпусном изготовлении, содержащий минимально необходимый
набор микросхем для разработки ПО для МК.</code></pre>
<div class="title">Интегрированная среда разработки. IDE(англ. IDE, Integrated development environment)</div><pre class="highlight listingblock"><code data-noescape class="text language-text">Cистема программных средств, используемая программистами для разработки программного обеспечения.
Обычно, среда разработки включает в себя:
⦁ текстовый редактор,
⦁ компилятор и/или интерпретатор,
⦁ средства автоматизации сборки,
⦁ отладчик.</code></pre>
<div class="title">SWD - Serial Wire Debug.</div><pre class="highlight listingblock"><code data-noescape class="text language-text">Двухпроводной отладочный порт</code></pre>
<div class="title">Компилятор</div><pre class="highlight listingblock"><code data-noescape class="text language-text">Программа выполняющая трансляцию исходного кода из предметно-ориентированногоязыка на мишинно-
ориентированный язык.</code></pre>
<div class="title">Компоновщик(Линковщик)</div><pre class="highlight listingblock"><code data-noescape class="text language-text">Программа собриющая исходный код на машино-ориентированном языке и производящую сборку в исполняемый
модуль</code></pre>
<div class="title">Стек</div><pre class="highlight listingblock"><code data-noescape class="text language-text">Абстрактный тип данных, представляющий собой список элементов,организованных по принципу LIFO
(англ. last in — first out,«последним пришёл — первым вышел»).
Возможны три операции со стеком: добавление элемента (иначе проталкивание, push), удаление элемента
(pop) и чтение головного элемента (peek).
Мы будем использовать определение Стека, в значении Аппартный стек</code></pre>
<div class="title">Аппаратный стек</div><pre class="highlight listingblock"><code data-noescape class="text language-text">В микроконтроллере стек - это непрерывная область памяти, адресуемая специальными регистрами SP
(указатель стека)</code></pre>
<div class="title">Регистр</div><pre class="highlight listingblock"><code data-noescape class="text language-text">Сверхбыстрая память внутри процессора, предназначенная для хранения адресов и промежуточных
результатов вычислений (регистр общего назначения/регистр данных) или данных, необходимых для
работы самого процессора.</code></pre></section>
<section><section id="_среда_разработки_программ_для_микроконтроллера"><h2>Среда разработки программ для микроконтроллера</h2><div class="paragraph"><p>В учебных целях мы будем использовать интегрированную среду разработки IAR Workbench for ARM.
Компания IAR бесплатно предлагает для ознакомления две версии своего продукта: версию evolution c
полным функционалом и ограничением времени использования 30 дней и версию kickstart (в имени
дистрибутива есть буквы KS) c ограничением на размер генерSируемого исполняемого кода -32 кбайт), но
без ограничения времени использования.</p></div><aside class="notes"><div class="paragraph"><p>Еще каких-то 10-15 лет назад для создания простейших программ, минимально необходимым набором
инструментального ПО являлись: текстовый редактор, транслятор ассемблерного кода и симуляторы для
отладки. С развитием микропроцессоров, с ростом объема оперативной памяти и памяти программ и
широчайшим распространением МК в различных областях техники, а также требований к надежности и
качеству разрабатываемого программного обеспечения минимального набора стало не хватать.</p></div>
<div class="paragraph"><p>Для создания качественных программ и повторного использования уже отлаженного кода, в виде библиотек,
появились редакторы связей (линковщики, компоновщики), появились отладчики, и более совершенные
трансляторы и, наконец, стало возможным и обоснованным применение компиляторов (примерно с середины
90-х прошлого века), появился диалект Embedded C\C++. И все эти средства для удобства использования
стали объединять в один программный продукт - так появились интегрированные среды разработки (IDE) и
целая отрасль разработки ПО. Одними из лидеров в этой области являются фирмы IAR Systems."</p></div></aside><div class="paragraph"><p>Для студенческих нужд размера кода в 32КБ более чем достаточно. В курсе мы будем использовать IAR
Embedded Workbench for ARM ver 8.40. Состав этого инструмента показан на Рисунке <a href="#IAR Workbench">[IAR Workbench]</a>.</p></div></section><section id="_состав_интеграционной_среды_разработки_iar_workbench"><h2>Состав интеграционной среды разработки IAR Workbench</h2><div class="paragraph"><p>Процесс разработки программного обеспечения в общем случае ничем не отличается от процесса
разработки приложения для обычных компьютеров, который включает в себя проектирование (Design),
разработка кода(Develop), отладка(Debug)</p></div>
<div class="imageblock" style=""><img src="Lection1Img/Figure1.png" alt="500" width="500"></div><div class="title">Рисунок 1. Процесс разработки с точки зрения IAR Workbench</div></section><section id="_процесс_создания_исполняемого_образа"><h2>Процесс создания исполняемого образа</h2><div class="paragraph"><p>Процесс преобразования кода на языке программирования высокого уровня С++ в файл, содержащий образ
исполняемой программы, готовый для прошивки в микроконтроллер можно разделить на два этапа:</p></div>
<div class="ulist"><ul><li><p>Трансляция кода в объектный файл</p></li><li><p>Компоновка кода в исполнительный файл</p></li></ul></div></section><section id="_трансляция_кода"><h2>Трансляция кода</h2><div class="paragraph"><p>Трансляцию кода выполняет компилятор. Структурно процесс трансляции с помощью компилятора показан
на рисунке <a href="#Схема Трансляции">[Схема Трансляции]</a>. После трансляции вы можете получить на выходе либо файлы
библиотеки, которые впоследствии можно будет использовать в других проектах, либо объектные файлы.</p></div>
<div class="imageblock" style=""><img src="Lection1Img/Figure2.png" alt="500" width="500"></div><div class="title">Рисунок 2. Схема процесса трансляции</div></section><section id="_компоновка_кода"><h2>Компоновка кода</h2><div class="paragraph"><p>Компоновку кода выполняет линковщик. Структурно процесс компоновки с помощью линковщика показан на
<a href="#Схема компоновки">[Схема компоновки]</a>.</p></div>
<aside class="notes"><div class="paragraph"><p>На входе линковщика могут быть, внешние библиотеки, полученные на этапе трансляции в других проектах
и программах, объектные файлы полученные на предыдущем этапе,стандартные(встроенные) библиотеки С++,
и конфигурационный файл, описывающий настройки по размещению кода и данных в адресном пространстве
микроконтроллера. Компоновщик создает исполняемый файл, который можно запустить на микроконтроллере</p></div></aside>
<div class="imageblock" style=""><img src="Lection1Img/Figure3.png" alt="500" width="500"></div><div class="title">Рисунок 3. Схема процесса компоновки</div></section><section id="_запуск_и_отладка"><h2>Запуск и отладка</h2><div class="paragraph"><p>Последний этап, показаный на рисунке <a href="#IAR Workbench">[IAR Workbench]</a> - отладка. Компоновищик IAR создает файл в
формате ELF, который содержит исполняемый образ программы. Этот файл может быть использован для:</p></div>
<div class="ulist"><ul><li><p>Загрузки в систему отладки IAR-CSPY или в любой другой отладчик, например GDB, способный читать
ELF формат</p></li><li><p>Загрузки образа в ПЗУ микроконтроллера используя программатор.</p></li></ul></div>
<div class="imageblock" style=""><img src="Lection1Img/Figure4.png" alt="350" width="350"></div><div class="title">Рисунок 4. Возможные варианты использования выходного файла</div></section></section>
<section><section id="_запуск_программного_обеспечения"><h2>Запуск программного обеспечения</h2><div class="paragraph"><p>Функция int main() является точкой входа программы, для пользователя программа начинается с вызова
этой функции и выполнения тела это функции. Однако на самом деле, еще до функции main()
микроконтроллер выполняет множество различных действий, например, инициализацию стека, глобальных
переменных, констант.</p></div></section><section id="_инициализация_стека"><h2>Инициализация стека</h2><div class="paragraph"><p>Сразу после подачи питания происходит инициализации указателя стека на конечный адрес стека.</p></div>
<div class="imageblock" style=""><img src="Lection1Img/Figure5.png" alt="500" width="500"></div><div class="title">Рисунок 5. Стадия инициализации стека</div></section><section id="_инициализация_переменных_в_нулевые_значения"><h2>Инициализация переменных в нулевые значения</h2><aside class="notes"><div class="paragraph"><p>После подачи питания на микроконтроллер, регистр адреса команды указывает на 0 адрес,
микроконтроллер начинает работу с адреса 0. По адресу 0, находится таблица векторов перываний, по
начальному вектору находится команда инициализации указателя стека на конечный адрес стека и далее
перехода на функцию инициализации.</p></div></aside>
<div class="paragraph"><p>После подачи питания и инифциализации стека, выполняется функция инициализации памяти нулями (данные
указанные как zero-initialized data, непроинциализированные глобальные переменные, такие как int i;)</p></div>
<div class="imageblock" style=""><img src="Lection1Img/Figure6.png" alt="450" width="450"></div><div class="title">Рисунок 6. Стадия инициализации непроинициализированных переменных</div></section><section id="_инициализация_переменных"><h2>Инициализация переменных</h2><div class="paragraph"><p>Далее должна произойти инициализация данных определенных как initialized data,например int i = 6.
Значения инициализации для каждой переменной будут скопированы из ПЗУ в ОЗУ.</p></div>
<div class="imageblock" style=""><img src="Lection1Img/Figure7.png" alt="400" width="400"></div><div class="title">Рисунок 7. Стадия инициализации проинициализированных переменных</div></section><section id="_запуск_функции_main"><h2>Запуск функции main()</h2><div class="paragraph"><p>Завершающий этап – это вызов функции main().</p></div>
<div class="imageblock" style=""><img src="Lection1Img/Figure8.png" alt="350" width="350"></div><div class="title">Рисунок 8. Стадия запуска функции main()</div>
<aside class="notes"><div class="paragraph"><p>Как видно, перед тем как запуститься функция main необходимо выполнить инициализацию стека и
переменных, кроме того, если в вашем проекте будут использоваться прерывания, то в таблицу векторов
прерываний необходимо добавить переходы на адреса обработчиков ваших прерываний.</p></div></aside></section><section id="_преимущества_iar_embedded_workbench"><h2>Преимущества IAR Embedded Workbench</h2><aside class="notes"><div class="paragraph"><p>За последние время в среде разработки IAR Embedded был сделан огромный скачек с точки зрения
удобства использования, так и с точки зрения поддержки современных стандартов. Так версия 8.X
получила поддержку стандарта С14, а начиная с версии 8.40 и поддержку стандарт С17 и это
является огромным плюсом для разработки надежного, понятного и качественно ПО. Свои мысли по этому
поводу я озвучил в статье <a href="#Можно ли использовать С++ вместо Си для небольших проектов в микроконтроллерах:">[Можно ли использовать С++ вместо Си для небольших проектов в микроконтроллерах:]</a></p></div>
<div class="paragraph"><p>Некоторые характеристики среды вы можете получить из Таблицы -
<a href="#Характеристики IAR Embedded Workbench">[Характеристики IAR Embedded Workbench]</a>, данные взяты из <a href="#IAR C/C++ Development Guide">[IAR C/C++ Development Guide]</a></p></div></aside>
<table class="tableblock frame-all grid-all" style="width:100%"><caption class="title">Таблица 1. Характеристики IAR Embedded Workbench</caption><colgroup><col style="width:50%"><col style="width:50%"></colgroup><thead><tr><th class="tableblock halign-left valign-top">Характеристика</th><th class="tableblock halign-left valign-top">IAR Embedded Workbench</th></tr><tbody><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Языки</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">С/C++</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Стандарты языка</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">С++ 17 начиная с версии 8.40</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Оптимизация кода</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Да, кроме condition_variable, future, mutex, shared_mutex, thread, поддержка
atomic урезана и реализована только для типов для которых есть аппаратная поддержка atomic
специальными командами в микроконтроллерах</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Контроль размера стека</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Да</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Поддержка RTOS</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Да</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Статический анализатор кода с набором правил</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Да - MISRAC++2008, SECURITY,CERT, STDCHECKS</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Динамический анализ кода</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">C-RUN</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Сертификация и проверка соответствию стандартам безопасности</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Сертификация на безопасность по стандартам IEC 61508 и ISO 26262 экспертной организацией TUV SUD – SIL3 сертификат</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Поддержка микроконтроллера STM32F411 RE</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Полная</p></td></tr></table></section><section id="_вопросы_по_разделу"><h2>Вопросы по разделу</h2><div class="qlist qanda"><ol><li><p><em>Дайте определение понятию “Интегрированной среде разработки”</em></p><p>Ответ:</p></li><li><p><em>Что такое компилятор и чем он отличается от транслятора?</em></p><p>Ответ:</p></li><li><p><em>Что такое компоновщик и какие функции он выполняет?</em></p><p>Ответ:</p></li><li><p><em>Почему важен процесс проектирования ПО какие задачи входят в этот процесс?</em></p><p>Ответ:</p></li><li><p><em>Дорисуйте процесс разработки ПО <a href="#IAR Workbench">[IAR Workbench]</a> с учетом итеративности связей в этом процессе</em></p><p>Ответ:</p></li><li><p><em>Зачем нужная отладка и в каких случаях она применяется? Для чего применяются точки остановки?</em></p><p>Ответ:</p></li><li><p><em>Какие еще важные IAR workbench можно добавить в таблицу <a href="#Характеристики IAR">[Характеристики IAR]</a></em></p><p>Ответ:</p></li></ol></div></section><section id="_запуск_программного_обеспечения_2"><h2>Запуск программного обеспечения</h2><h3>Файл cstartup.cpp</h3><div class="paragraph"><p>Действия по инициализации прописываются в файле cstartup. Этот файл может быть написан как на
ассемблере, на Си, так и на С+. Поскольку мы будем использовать С+, то и файл будем использовать
cstartup.cpp, который будет выглядеть примерно так</p></div>
<pre class="highlight listingblock"><code data-noescape class="c language-c">extern "C" void __iar_program_start(void) ;
class InterruptHandler {
public:
static void DummyHandler() { for(;;) {} }
}
};
using tIntFunct = void(*)();
using tIntVectItem = union {tIntFunct __fun; void * __ptr;};
#pragma segment = "CSTACK"
#pragma location = ".intvec"
const tIntVectItem __vector_table[] = {
{ .__ptr = __sfe( "CSTACK" ) }, //инициализация стека
__iar_program_start, //переход на адрес функции __iar_program_start
InterruptHandler::DummyHandler,
...
InterruptHandler::DummyHandler, ////TIM4
};
extern "C" void __cmain(void) ;
extern "C" __weak void __iar_init_core(void) ;
extern "C" __weak void __iar_init_vfp(void) ;
#pragma required = __vector_table
void __iar_program_start(void) {
__iar_init_core() ;
__iar_init_vfp() ;
__cmain() ;
}</code></pre>
<aside class="notes"><div class="paragraph"><p>Немного проясним, что здесь написано, строка:</p></div>
<pre class="highlight listingblock"><code data-noescape class="c language-c">extern "C" void __iar_program_start( void );</code></pre>
<div class="paragraph"><p>Описывает прототип функции __iar_program_start, которая будет отвечать за инициализацию переменных
и запуск функции main(). Реализацию этой функции вы можете увидеть в самом конце файла cstarup.cpp</p></div>
<pre class="highlight listingblock"><code data-noescape class="c language-c">void __iar_program_start( void ) {
__iar_init_core();
__iar_init_vfp();
__cmain();
}</code></pre>
<div class="paragraph"><p>Далее идет определение класса с описание одного единственного метода handler(). Это метод и будет
тем самым обработчиком прерывания который вызовется при срабатывании соответствующего прерывания.
Реализация метода проста – бесконечный цикл, т.е. попав в прерывание программа “навсегда” останется
в нем:</p></div>
<pre class="highlight listingblock"><code data-noescape class="cpp language-cpp">__weak void DummyModule::handler() { for(;;) {} };</code></pre>
<div class="paragraph"><p>Это сделано для того, что пока не планируем использвать никаких прерываний, и если все таки каким то
образом прерывание сработало, значит, что-то было сделано не так. В дальнейшем в разделе
<a href="#Прерывания">[Прерывания]</a> будет показано, как сделать нужный нам обработчик прерывания, но сейчас мы не будем
на этом заострять внимание. Следующий две строки определяют новый тип, который будет использоваться
для задания элементов таблицы векторов прерываний:</p></div>
<pre class="highlight listingblock"><code data-noescape class="cpp language-cpp">typedef void( *intfunc )( void );
typedef union { intfunc __fun; void * __ptr; } intvec_elem;</code></pre>
<div class="paragraph"><p>Как видно этот тип есть объединение двух типов, указателя на функцию типа void и указателя на void.
Это необходимо для того, чтобы правильно интерпретировать элементы таблицы. Ведь начальный вектор
прерывания не содержит никакого обработчика, а просто содержит конечный адрес стека, а последующие
вектора содержат адреса обработчиков, именно поэтому первый элемент таблицы векторов должен иметь
тип указателя на void, а последующие указателей на функцию типа void.
Собственно далее идет и сама таблица лежащая в выделенном для неё сегменте .intvec, который задается в настройках линковщика
#pragma location = ".intvec"
Таблица начинается с адреса стека, который также задается сегментом CSTACK в настройке линковщика, а следующий элемент таблицы есть адрес функции инициализации переменных, а затем адреса обработчиков для конкретных прерываний.</p></div></aside></section></section>
<section><section id="_использование_с"><h2>Использование С++</h2><div class="ulist"><ul><li><p>Так же как когда-то Си пробивал себе дорогу в качестве стандарта для встроенного ПО, так и язык С++
уже вполне может заменить Си в этой области.</p></li></ul></div><aside class="notes"><div class="paragraph"><p>Язык программирования стандарта С+ + и современные компиляторы имеют достаточно средств для того
чтобы создавать компактный код и не уступать по эффективности коду, созданному на Си, а благодаря
нововведениям быть понятнее и надежнее.. Начиная с версии IAR
Workbench 8.40 компилятор поддерживает полезные нововведения стандарта С++17, такие, как например
“структурные привязки”, “инициализация в ветвлениях”, “встроенные переменные”.</p></div></aside><div class="ulist"><ul><li><p>С++ является строго типизированным языком, а значит программы написанные на нем более безопасны, чем
программы написанные на Си и меньше вероятность того, что программист допустит ошибку.</p></li><li><p>С++ является языком программирования полностью поддерживающий парадигму программирования ООП,
которая отлично подходит для разработки программного обеспечения измерительных устройств.</p></li></ul></div><aside class="notes"><div class="paragraph"><p>Ведь нужно понимать, что для измерительного устройства нам нужно описать логику работы, интерфейс
взаимодействия с пользователем, реализовать расчеты, а не помнить, что для того чтобы считать
данные с АЦП, нужно вначале его выбрать с помощью сигнала CS, находящегося на порту GPIOA.3 и
установить его в единицу. Этим должен заниматься разработчик драйверов.</p></div>
<div class="paragraph"><p>Большинство драйверов для работы с аппаратурой уже реализованы производителями микроконтроллеров,
например, в библиотеках CMSIS и CMSIS_HAL, ими можно воспользоваться для обращения к функциям
доступа к аппаратуре, упростить и ускорить разработку.</p></div>
<div class="paragraph"><p>Замечаниями по этому поводу может служить то, что эти библиотеки довольно громоздкие и для
использования в небольших приложениях вряд ли подойдут, кроме того, не всегда они имеют необходимые
сертификаты надежности, а потому при разработке реальных измерительных устройств, применение которых
планируется в местах с повышенной безопасностью промышленных предприятиях, вряд ли стоит
пользоваться этими библиотеками.</p></div></aside><div class="paragraph"><p>Именно поэтому, мы будет использовать С++ от написания драйверов и уровня аппаратуры и до реализации
логики работы с пользователем. Начнем же изучение с создания проекта, системы тактирования и
небольшой программы мигания светодиодом.</p></div></section><section id="_программа_на_с"><h2>Программа на С++</h2><div class="paragraph"><p>Как было сказано в разделе <a href="#_состав_интеграционной_среды_разработки_iar_workbench">[_состав_интеграционной_среды_разработки_iar_workbench]</a>
первоначально мы должны создать исходные файлы на языке программирования С.
В С разделяют два типа файлов:</p></div>
<div class="ulist"><ul><li><p>Исходный файл (файл с раширением *.cpp)</p></li><li><p>Заголовочный файл (файл с расширением *.h, *.hpp)</p></li></ul></div>
<div class="paragraph"><p>Загловочные файлы подключаются с помощью директивы #include и при трансляции
просто вставляются в текст *.cpp файла. Используются они для того, чтобы вынести
общие определения, испольжуемые в нескольких *.cpp файлах в одно место.</p></div>
<div class="title">Вот так может выглядеть ваша программа:</div><pre class="highlight listingblock"><code data-noescape class="cpp language-cpp">#include "gpioaregisters.hpp" //for Gpioa
#include "rccregisters.hpp" //for RCC
int main()
{
RCC::AHB1ENR::GPIOAEN::Enable::Set() ;
GPIOA::MODER::MODER15::Output::Set() ;
GPIOA::ODR::ODR15::Enable::Set() ;
return 0 ;
}</code></pre></section></section>
<section><section id="_создание_проекта_и_работа_в_iar_workbench"><h2>Создание проекта и работа в IAR Workbench</h2><div class="ulist"><ul><li><p>Создать новый проект Project⇒Create New Project.</p></li></ul></div><div class="imageblock" style=""><img src="Lection1Img/Figure9.png" alt="400" width="400"></div><div class="title">Рисунок 9. Создание нового проекта</div></section><section id="_выбор_шаблона_проекта"><h2>Выбор шаблона проекта</h2><div class="ulist"><ul><li><p>Выбирать шаблон проекта( ProjectTemplates): C++ - main</p></li></ul></div>
<div class="imageblock" style=""><img src="Lection1Img/Figure10.png" alt="400" width="400"></div><div class="title">Рисунок 10. Выбор шаблона проекта</div></section><section id="_выбор_микроконтроллера"><h2>Выбор микроконтроллера</h2><div class="ulist"><ul><li><p>Сохранить проект под каким-либо именем</p></li><li><p>В свойствах проекта выбрать модель микроконтроллера ST ⇒ STM32F4⇒ STM32F411⇒ ST STM32F411RE см.
<a href="#_выбор_микроконтроллера">[_выбор_микроконтроллера]</a>. Для этого правой кнопкой мыши щелкнуть по проекту, выбирать Options
и далее в категории General Option выбрать закладку Target.</p></li></ul></div>
<div class="imageblock" style=""><img src="Lection1Img/Figure11.png" alt="500" width="500"></div><div class="title">Рисунок 11. Выбор микроконтроллера</div></section><section id="_запуск_в_режиме_отладки"><h2>Запуск в режиме отладки</h2><div class="paragraph"><p>После создания проекта необходимо сохранить так называемое рабочее пространство или (workspace).</p></div>
<aside class="notes"><div class="paragraph"><p>В рабочее пространство можно загружать несколько проектов (например, проекты всех лабораторных работ)
и переключаться между проектами по мере необходимости.</p></div></aside>
<div class="paragraph"><p>После того, как проект сделан, и имеет вид показанный на <a href="#Вид созданного проекта">[Вид созданного проекта]</a>, можно
попробовать собрать проект, нажав кнопку Ctrl-F7, а затем загрузить полученный бинарный файл в
микропроцессор и запустить на отладку с помощью кнопки Ctrl-D.</p></div>
<aside class="notes"><div class="paragraph"><p>Все тоже самое можно сделать и с помощью кнопок быстрого доступа на панели инструментов, через меню
среды или контекстное меню проекта (загрузить которое можно нажав на правую клавишу мыши на проекте).</p></div></aside>
<div class="imageblock" style=""><img src="Lection1Img/Figure12.png" alt="500" width="500"></div><div class="title">Рисунок 12. Вид созданного проекта</div></section><section id="_запуск_проекта_в_режим_симуляции"><h2>Запуск проекта в режим симуляции</h2><div class="paragraph"><p>По умолчанию загрузка и отладка бинарного файла осуществляется в симулятор выбранного
микроконтроллера. Поэтому, если вы выполнили все верно, то должно получиться нечто похожее,
показанное на <a href="#Проект в режиме отладки">[Проект в режиме отладки]</a>.</p></div>
<div class="imageblock" style=""><img src="Lection1Img/Figure13.png" alt="500" width="500"></div><div class="title">Рисунок 13. Проект в режиме отладки</div>
<aside class="notes"><div class="paragraph"><p>На этом рисунке вы можете видеть, как сам ваш код написанный на С++, так и окно дизассемблера,
показывающее как компилятор преобразовал ваш код в команды ассемблера. Зеленая строчка показывает
текущую исполняющую строчку вашего кода и команду ассемблера.</p></div>
<div class="paragraph"><p>Для того чтобы остановить отладку и выйти в режим разработки необходимо нажать кнопки Ctrl-Shift-D.</p></div></aside></section><section id="_выбор_внутрисхемного_отладчика"><h2>Выбор внутрисхемного отладчика</h2><div class="paragraph"><p>Чтобы загрузить программу в микроконтроллер необходимо вместо симулятора выбрать внутрисхемный
отладчик, которым вы пользуетесь. Это можно сделать, встав на проект и нажать на правую кнопку мыши,
далее выбрать пункт меню Options⇒Debugger⇒Driver и выбрать в нем нужный вам внутрисхемный отладчик,
см <a href="#_выбор_внутрисхемного_отладчика">[_выбор_внутрисхемного_отладчика]</a>. Мы будет использовать отладчик ST-Link.</p></div>
<div class="imageblock" style=""><img src="Lection1Img/Figure14.png" alt="450" width="450"></div><div class="title">Рисунок 14. Выбор внутрисхемного отладчика</div>
<aside class="notes"><div class="paragraph"><p>Теперь, если вы нажмете Ctrl-D, ваша программа загрузиться в микроконтроллер и отладка будет
осуществляться непосредственно на ядре микроконтроллера.
И так вы смогли сделать проект, откомпилировать пустую программу и загрузить её в симулятор и
микроконтроллер, но всех этих действий недостаточно, для того, чтобы начать разрабатывать
программное обеспечение. Рассмотрим, что же еще необходимо сделать для того, чтобы наш проект был
полностью готов.</p></div></aside></section></section>
<section><section id="_структура_проекта"><h2>Структура проекта</h2><div class="paragraph"><p>Для того, чтобы разработка была быстрой и качественной, необходимо структурировать паку проекта.</p></div><div class="ulist"><ul><li><p>Не нужно писать весь код в одном файле. Лучше каждый класс описывать в отдельном файле</p></li><li><p>Файлы с классами, ответственные за один компонент, лучше держать в папках с именем этого компонента</p></li><li><p>Не превращаем проект в мусорку</p></li></ul></div></section><section id="_добавление_файла_cstartup_cpp_в_проект"><h2>Добавление файла (cstartup.cpp) в проект</h2><div class="paragraph"><p>В папку где вы сохранили проекта, необходимо скопировать файл cstartup.cpp. и добавить его к проекту:
Для этого нужно нажать правую кнопку мыши на проекте и выбрав пункт Add⇒Add Files… как показано на
<a href="#Добавление нового файла в проект">[Добавление нового файла в проект]</a>, а затем выбрать файл startup_stm32F411.cpp.</p></div>
<div class="imageblock" style=""><img src="Lection1Img/Figure15.png" alt="450" width="450"></div><div class="title">Рисунок 15. Добавление нового файла в проект</div>
<aside class="notes"><div class="paragraph"><p>Как было сказано выше, в файле cstartup.cpp описывается таблица вектров прерываний и начальная
инициализация. Поэтому первым делоам нужно подкючить файал cstartuo.cpp в проект. Тут следует иметь
ввиду, что таблица векторов прерываний для разных микроконтроллеров разная, и соответственно файлы
cstartup должен быть различных для разных микроконтроллеров. Чтобы не перепутать свой файлы для
разных микроконтроллеров, назовем его startup_stm32F411.cpp и подключим к проекту, нажав правую
кнопку мыши на проекте и выбрав пункт Add⇒Add Files… (см. Рисунок 21 ), а затем выбрав файл
startup_stm32F411.cpp.</p></div></aside></section><section id="_начальная_структура_проекта"><h2>Начальная структура проекта</h2><div class="paragraph"><p>Добавив файл в проект у вас должно получиться следующая структура в среде IAR Workbench:</p></div>
<div class="imageblock" style=""><img src="Lection1Img/Figure16.png" alt="400" width="400"></div><div class="title">Рисунок 16. Начальная структура проекта</div></section><section id="_доступ_к_папке_проекта"><h2>Доступ к папке проекта</h2><div class="paragraph"><p>Теперь нужно разобраться с тем как будет организован наш проект на диске и в системе контроля
версий. Если мы нажмем правой мышкой на проекте и выберем пункт Open Containing Folder см.
<a href="#Открытие папки проекта">[Открытие папки проекта]</a>, то мы попадем в папку нашего проекта.</p></div>
<div class="imageblock" style=""><img src="Lection1Img/Figure17.png" alt="250" width="250"></div><div class="title">Рисунок 17. Открытие папки проекта</div>
<aside class="notes"><div class="paragraph"><p>Мы увидим что у нас есть папки Debug и Settings, а также созданные нами файлы проекта, файлы рабочей
области, main.cpp и startup_stm32F411.cpp. В паке Debug хранятся объектные файлы, двоичные файлы для
прошивки, листинг программы, созданные в режиме Debug (т.е. в режиме, когда в программу добавляется
некая служебная информация и функциональность для того, чтобы можно было поддерживать внутрисхемную
отладку. Существует также режим Release, когда двоичный файл содержит только код программы).</p></div>
<div class="paragraph"><p>В папке Settings хранятся настройки проекта и рабочей области.</p></div></aside></section><section id="_cтруктура_папки_проекта"><h2>Cтруктура папки проекта</h2><div class="paragraph"><p>⦁ Нам нужна будет папка AbstractHardware/Registers. В которой находятся файлы с описанием полей
регистров. Можно скопировать ее путем клонирования папки проекта преподавателя, набрав в командной
сстроке:</p></div>
<pre class="highlight listingblock"><code data-noescape class="text language-text">git clone https://github.com/lamer0k/CortexLib.git</code></pre>
<aside class="notes"><div class="paragraph"><p>Вы можете скопировать папку преподавателя через Git, используя PowerShell. Для этого, нужно нажав на
вашу папку правой кнопкой мыши, удерживая Shift, выбрать меню "Открыть окно PowerShell здесь".</p></div></aside>
<div class="paragraph"><p>⦁ В папке AbsstractHardware будут содержаться файлы для работы с регистрами, аппаратурой и периферией.</p></div>
<aside class="notes"><div class="paragraph"><p>Папка AbstractHardware содержит зависимую от микроконтроллера часть.</p></div></aside>
<div class="paragraph"><p>⦁ Дополнительно создадим еще папку Application, в которой в дальнейшем будут содержаться файлы классов
для работы с логикой программы.</p></div>
<aside class="notes"><div class="paragraph"><p>Папка Application будет содержать полностью независимую часть, которую можно будет перенести на
любую другую платформу и микроконтроллер. А папка AbstractHardware будет содержать модули зависящие
от конкретного микроконтроллера.</p></div></aside>
<div class="paragraph"><p>⦁ В завершение добавим папку FreeRtos – она пригодиться нам при работе с ОСРВ.</p></div>
<div class="imageblock" style=""><img src="Lection1Img/Figure18.png" alt="350" width="350"></div><div class="title">Рисунок 18. Финальное содержимое папки проекта</div></section><section id="_изменение_структуры_проекта"><h2>Изменение структуры проекта</h2><div class="paragraph"><p>Теперь необходимо создать точно такую же структуру в проекте IAR Workbench, как и структура папок.
Для этого необходимо нажать правой мышкой на проект, и выбрать меню Add⇒Ggroup и создать группы
Abstract_Hardware, Application, Common, FreeRtos.</p></div>
<div class="imageblock" style=""><img src="Lection1Img/Figure19.png" alt="400" width="400"></div><div class="title">Рисунок 19. Изменение структуры проекта</div></section><section id="_финальная_структура_проекта"><h2>Финальная структура проекта</h2><div class="paragraph"><p>В конечном итоге у вас должна появиться вот такая структура:</p></div>
<div class="imageblock" style=""><img src="Lection1Img/Figure20.png" alt="400" width="400"></div><div class="title">Рисунок 20. Финальная структура проекта</div></section></section>
<section><section id="_окончательная_настройка_проекта"><h2>Окончательная настройка проекта</h2><div class="paragraph"><p>Для окончательной настройки проекта, нам понадобится настроить компоновщик и установить размер
сегментов памяти, стека и кучи.</p></div><div class="paragraph"><p>Перед тем как производить их настройку разберемся, что такое сегменты памяти, стек и куча и для чего
они нужны.</p></div></section><section id="_организация_памяти"><h2>Организация памяти</h2><aside class="notes"><div class="paragraph"><p>Существует несколько признанных архитектур микропроцессоров
* Архитектура ФонНеймана
* Гарвардская архитектура
В традиционных микропроцессорах используется архитектура Фон Неймана (названную так в честь
американского математика Джона Фон Неймана), см. <a href="#Архитектуры микропроцессоров">[Архитектуры микропроцессоров]</a> A.</p></div>
<div class="paragraph"><p>Эта архитектура состоит из единого блока памяти, в котором хранятся и команды, и данные, и общей
шины для передачи данных и команд в ЦПУ и от него. При такой архитектуре перемножение двухчисел
требует по меньшей мере трех циклов: двух циклов для передачи двух чисел в ЦПУ, и одного – для
передачи команды. Данная архитектура приемлема в том случае, когда все действия могут выполняться
последовательно. По сути говоря, в большинстве компьютеров общего назначения используется сегодня
такая архитектура.</p></div>
<div class="paragraph"><p>Однако для быстрой обработки сигналов больше подходит гарвардская архитектура, см <a href="#Архитектуры микропроцессоров">[Архитектуры микропроцессоров]</a>B.
Данная архитектура получила свое название в связи с работами, проведенными в Гарвардском университете
под руководством Ховарда Айкена. Данные и код программы хранятся в различных блоках памяти и доступ
к ним осуществляется через разные шины, как показано на схеме. Т.к. шины работают независимо, выбор
команд программы и данных может осуществляться одновременно, повышая таким образом скорость по
сравнению со случаем и спользования одной шины вархитектуре Фон Неймана.</p></div>
<div class="paragraph"><p>На <a href="#Архитектуры микропроцессоров">[Архитектуры микропроцессоров]</a>C, представлена модифицированная гарвардская архитектура, где и
команды, и данные могут храниться в памяти программ.</p></div></aside>
<div class="paragraph"><p>ARM является модифицированной гарвардской архитектурой.</p></div>
<div class="imageblock" style=""><img src="Lection1Img/Figure22.png" alt="800" width="800"></div><div class="title">Рисунок 21. Архитектуры микропроцессоров</div>
<div class="paragraph"><p>Доступ к памяти осуществляется по одной шине, а уже устройство управления памятью обеспечивает
разделение шин при помощи управляющих сигналов: чтения, записи или выбора области памяти.</p></div>
<aside class="notes"><div class="paragraph"><p>Данные и код могут находится в одной и той же области памяти. В этом едином адресном пространстве
может находится и ПЗУ и ОЗУ и периферия. А это означает, что собственно и код и данные могут попасть
хоть куда(в ОЗУ или в ПЗУ) и это зависит только от компилятора и линкера.</p></div></aside></section><section id="_настройка_области_памяти_в_комповшике"><h2>Настройка области памяти в комповшике</h2><div class="paragraph"><p>Поэтому чтобы различить области памяти для ПЗУ(ROM) и ОЗУ их обычно указывают в настройках линкера.</p></div>
<div class="title">В настройках линкера IAR 8.40.1 это выглядит вот так:</div><pre class="highlight listingblock"><code data-noescape class="text language-text">define symbol __ICFEDIT_region_ROM_start__ = 0x08000000;
define symbol __ICFEDIT_region_ROM_end__ = 0x0807FFFF;
define symbol __ICFEDIT_region_RAM_start__ = 0x20000000;
define symbol __ICFEDIT_region_RAM_end__ = 0x2001FFFF;
define region ROM_region = mem:[from __ICFEDIT_region_ROM_start__ to __ICFEDIT_region_ROM_end__];
define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__ to __ICFEDIT_region_RAM_end__];</code></pre>
<div class="paragraph"><p>В данном микроконтроллере диапазон адресом для памяти слудющий:</p></div>
<div class="ulist"><ul><li><p>ОЗУ(RAM) 0x20000000…​0х2001FFF,</p></li><li><p>ПЗУ(ROM) с 0x008000000…​0x0807FFFF.</p></li></ul></div>
<aside class="notes"><div class="paragraph"><p>Вы легко можете поменять начальный адрес ROM_start на адрес ОЗУ, скажем RAM_start и конечный адрес
ROM_end<em> на адрес RAM_end</em> и ваша программа будет полностью расположена в ОЗУ.</p></div>
<div class="paragraph"><p>Вы даже можете сделать наоборот и указать ОЗУ в области памяти ROM, и ваша программа успешно
соберется и прошьется, правда работать не будет :)</p></div>
<div class="paragraph"><p>Некоторые микроконтроллеры, такие как, AVR изначально имеют раздельное адресное пространство для
памяти программ, памяти данных и периферии и потому там такие фокусы не пройдут, а программа по
умолчанию записывается в ROM память.</p></div></aside>
<div class="title">Важно</div><pre class="highlight listingblock"><code data-noescape class="text language-text">Все адресное пространство в CortexM единое, и код и данные могут размещаться где угодно. С помощью
настроек линкера можно задать регион для адресов ПЗУ(ROM) и ОЗУ(RAM) памяти. IAR располагает сегмент
кода .text в регионе ROM памяти.</code></pre></section><section id="_объектный_файл_и_сегменты"><h2>Объектный файл и сегменты</h2><aside class="notes"><div class="paragraph"><p>Выше я упомянул про сегмент кода, давайте разберемся, что это такое.</p></div></aside>
<div class="paragraph"><p>На каждый компилируемый модуль создается отдельный объектный файл, который содержит следующую информацию:</p></div>
<div class="ulist"><ul><li><p>Сегменты кода и данных</p></li><li><p>Отладочную информацию в формате DWARF</p></li><li><p>Таблицу символов</p></li></ul></div>
<aside class="notes"><div class="paragraph"><p>Нас интересуют сегменты кода и данных.</p></div></aside>
<div class="paragraph"><p>Сегмент это такой элемент, содержащий часть кода или данных, который должен быть помещен по
физическому адресу в памяти. Сегмент может содержать несколько фрагментов, обычно один фрагмент на
каждую переменную или функцию. Сегмент может быть помещен как в ПЗУ(ROM) так и ОЗУ(RAM).</p></div>
<div class="paragraph"><p>В общем и целом, сегмент это наименьший линкуемый блок.</p></div></section><section id="_атрибуты_сегментов"><h2>Атрибуты сегментов</h2><div class="paragraph"><p>Каждый сегмент имеет имя и атрибут, который определяет его содержимое. Атрибут используется для
определения сегмента в конфигурации для линкера. Например, атрибуты могут быть:
* code — исполняемый код
* readonly — константные переменные
* readwrite — инициализируемые переменные
* zeroinit — инициализируемые нулем переменные</p></div>
<aside class="notes"><div class="paragraph"><p>Конечно есть и другие типы сегментов, например сегменты, содержащие отладочную информацию, но нас
будут интересовать только те, которые содержат код или данные нашего приложения.</p></div>
<div class="paragraph"><p>Повторюсь, сегмент это наименьший линкуемый блок. Однако при необходимости линкеру можно указать и
еще более мелкие блоки(фрагменты). Этот вариант рассматривать не будем, остановимся на сегментах.</p></div></aside></section><section id="_предопределенные_имена_сегментов_в_iar_workbench"><h2>Предопределенные имена сегментов в IAR Workbench</h2><div class="paragraph"><p>Во время компиляции данные и функции размещаются в различные сегменты. А во время линковки, линкер
назначает им реальные физические адреса. В компиляторе IAR есть предопределенные имена сегментов,
некоторые из них приведены ниже:</p></div>
<div class="ulist"><ul><li><p>.bss — Содержит статические и глобальные переменные инициализируемые 0</p></li><li><p>.CSTACK — Содержит стек используемый программой</p></li><li><p>.data — Содержит статические и глобальные инициализируемые переменные</p></li><li><p>.data_init — Содержит начальные значения для данных в .data секции, если используется директива
инициализации для линкера</p></li><li><p>HEAP — Содержит кучу, используемую для размещения динамических данных</p></li><li><p>.intvec — Содержит таблицу векторов прерываний</p></li><li><p>.rodata — Содержит константные данные</p></li><li><p>.text — Содержит код программы</p></li></ul></div>
<aside class="notes"><div class="paragraph"><p>На практике это означает, что если вы определили переменную int val = 3, то сама переменная будет
расположена компилятором в сегмент .data и помечена атрибутом readwrite, а число 3 может быть
помещено либо в сегмент .text, либо в сегмент .rodata или, если применена специальная директива для
линкера в .data_init и также помечается им как readonly.</p></div>
<div class="paragraph"><p>Сегмент .rodata содержит константные данные и включает в себя константные переменные, строки,
агрегатные литералы и так далее. И этот сегмент может быть размещена где угодно в памяти.</p></div></aside></section><section id="_файл_настройки_компоновщика"><h2>Файл настройки компоновщика</h2><div class="paragraph"><p>Файл линкера имеет расширение*.icf. В нащем проекте этот файл называется stm32f411xE.icf. Давайте
теперь поймем, что же прописано в настройках линкера и почему.</p></div>
<pre class="highlight listingblock"><code data-noescape class="text language-text">define symbol __ICFEDIT_region_ROM_start__ = 0x08000000;
define symbol __ICFEDIT_region_ROM_end__ = 0x0807FFFF;
define symbol __ICFEDIT_region_RAM_start__ = 0x20000000;
define symbol __ICFEDIT_region_RAM_end__ = 0x2001FFFF;
define region ROM_region = mem:[from __ICFEDIT_region_ROM_start__ to __ICFEDIT_region_ROM_end__];
define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__ to __ICFEDIT_region_RAM_end__];
// Разместить сегменты .rodata и .data_init (константы и инициализаторы) в (ПЗУ)ROM:
place in ROM_region { readonly };
// Разместить сегменты .data, .bss, .noinit, STACK и HEAP в (ОЗУ)RAM
place in RAM_region { readwrite, block STACK , block HEAP };</code></pre></section></section>
<section><section id="_настройка_стека"><h2>Настройка стека</h2></section><section id="_стек"><h2>Стек</h2><div class="paragraph"><p>Для начала определение из Википедии:</p></div>
<div class="paragraph"><p>Стек (англ. Stack - стопка; читается стэк) - абстрактный тип данных, представляющий собой список
элементов, организованных по принципу LIFO (англ. last in — first out, «последним пришёл — первым
вышел»).</p></div>
<div class="paragraph"><p>В стек можно положить данные, и можно данные забрать, причем те данные которые были положены в стек
последним, забираем из стека первым</p></div>
<div class="paragraph"><p>Стек – это организация памяти, выполненная компоновщиком. На уровне микроконтроллера для работы со
стеком есть специальные ассемблерные команды (например PUSH – положить регистры в стек, и POP –
взять из стека). Так же для сохранения и считывания данных из стека могут использоваться
инструкции STR и LDR</p></div>
<div class="paragraph"><p>Обычно в стеке сохраняются регистры когда вы вызываете подпрограмму, или проваливаетесь в прерывание,
для того, чтобы когда вернуться обратно в вашу программу восстановить весь контекст и все переменные.
Кроме того, если в вашей функции передается слишком много переменных и под все не хватит регистров,
то компилятор расположит их также на стеке. Локальные переменные функции также создаются на стеке.</p></div>
<aside class="notes"><div class="paragraph"><p>В традиционной реализации память для всех локальных переменных функции выделяется сразу, одним
"кадром стека" в начале работы функции. Внутри этого кадра стека компилятор еще на стадии компиляции
разработает некую фиксированную карту расположения локальных переменных. При этом он может (и будет)
располагать локальные переменные в этой карте совершенно произвольным образом, руководствуясь
оптимизационными соображениями выравнивания, экономии памяти и т.д. и т.п.</p></div></aside></section><section id="_правила_задания_размера_стека"><h2>Правила задания размера стека</h2><div class="paragraph"><p>В большинстве "традиционных" платформ стек растет сверху-вниз: от старших адресов к младшим.
Поэтому прежде всего нужно верно указать размер или вершину стека. Для того, чтобы сделать это есть
пара правил:</p></div>
<div class="olist arabic"><ol class="arabic"><li><p>Всегда считаем, что все локальные переменные создаются на стеке (Хотя часть из них могут быть
созданы и на регистрах)</p></li><li><p>У нас 16 регистров + регистры блока с плавающей точкой. Которые должны быть сохранены на стеке</p></li><li><p>Каждая вложенная подпрограмма должна сохранить на стеке все данные из пункта 1 и 2. Т.е. если
вложенность будет 2, то и сохранять придется примерно в два раза больше данных</p></li><li><p>Каждое прерывание должно сохранить данные из пункта 1 и 2.</p></li></ol></div></section><section id="_установка_размера_стека"><h2>Установка размера стека</h2><div class="paragraph"><p>Обычно размер стека вычисляется эмпирически и задается с небольшим запасом.</p></div>
<aside class="notes"><div class="paragraph"><p>Чтобы задать размер стека, нужно нажав на правую кнопку мыши на проекте, выбрать Option⇒Linker и
нажать кнопку Edit, далее выбрать закладку Stack/Heap Size, см. <a href="#Установка размера стека и кучи">[Установка размера стека и кучи]</a></p></div></aside>
<div class="imageblock" style=""><img src="Lection1Img/Figure23.png" alt="500" width="500"></div><div class="title">Рисунок 22. Установка размера стека и кучи</div>
<div class="paragraph"><p>Тоже самое можно сделать руками в файле stm32f411xE.icf, поменяв значение символа <em>__ICFEDIT_size_cstack</em></p></div></section><section id="_контроль_за_размер_стеком"><h2>Контроль за размер стеком</h2><div class="paragraph"><p>IAR Workbench имеет встроенные средства для контроля стека на этапе сборки он может указать
максимально возможный размер стека для вашего приложения для самой глубокой цепочки вызова функций.</p></div>
<aside class="notes"><div class="paragraph"><p>Это значение можно использовать как ориентир при установке максимального значения стека.
Однако следует помнить, что во-первых, в вашей программе возможно никогда не будет самой глубокой
цепочки вложенности, а во вторых не всегда компоновщик сможет определить верно размер, например,
при использовании ОСРВ, указатель стека постоянно изменяется и стек выделяется под каждую задачу
отдельно, в итоге вся программа может работать вообще без единого стека и его размер можно
минимальным. Зато придется указывать размер стека для каждой задачи при её создании. В любом случае,
очень полезно знать об этой особенности и как её задействовать.</p></div></aside>
<div class="paragraph"><p>Для включения достаточно поставить галочку в меню Option⇒Linker⇒Advanced⇒Enable stack usage
analysis см. <a href="#Опция анализа глубины стека">[Опция анализа глубины стека]</a></p></div>
<div class="imageblock" style=""><img src="Lection1Img/Figure24.png" alt="400" width="400"></div><div class="title">Рисунок 23. Опция анализа глубины стека</div></section><section id="_доступ_к_данным_по_анализу_размеру_стека"><h2>Доступ к данным по анализу размеру стека</h2><div class="paragraph"><p>После установки этой опции на выходе компоновщика в файле с раcширением *.map можно будет увидеть
результат анализа, например, такой:</p></div>
<pre class="highlight listingblock"><code data-noescape class="text language-text">Call Graph Root Category Max Use Total Use
------------------------ ------ - -------- -
Program entry 896 896
Uncalled function 0 0
Program entry
"__iar_program_start": 0x08005291
Maximum call chain 896 bytes
"__iar_program_start" 8
"__cmain" 0
"main" 88
"std::ostream::operator <<(float)" 80
"std::numpunct<char>::grouping() const" 8
"std::numpunct<char>::do_grouping() const" 8
"std::string::basic_string(char const *)" 16
"std::string::assign(char const *)" 16
"std::string::assign(char const *, unsigned int)" 16
"std::string::assign(const std::string&, unsigned int, unsigned int)" 32
"std::string::_Grow(unsigned int, bool)" 16
"std::string::_Copy(unsigned int, unsigned int)" 32</code></pre>
<div class="paragraph"><p>В данном случае анализ стека показывает, что размер стека при максимальной цепочке вложенности может
быть 896 байт.</p></div></section><section id="_куча"><h2>Куча</h2><div class="paragraph"><p>Куча (англ. heap) - .структура данных с помощью которой организуется динамически распределение
памяти приложения. Размер кучи — размер памяти, выделенной операционной системой (ОС) для хранения
кучи (под кучу).</p></div>
<div class="paragraph"><p>Компоновщик выделяет раздел памяти под кучу в соответствии с заданным размером кучи, а при запуске
программы происходит инициализация кучи, в ходе которой память, выделенная под кучу, отмечается как
свободная.</p></div>
<div class="paragraph"><p>Куча используется только при динамически выделяемой памяти, для нас это означает, что все объекты
созданные с помощью оператора new будут расположены в куче.</p></div>
<div class="paragraph"><p>Механизм выделения памяти описывать не будем, просто нужно запомнить, что если объект создан с
помощью оператора new, то все его содержимое хранится в куче.</p></div>
<div class="paragraph"><p>Я не советую использовать динамическое создание объектов. Так как динамческое выделение памяти не
рекомендуется для использования в надежном ПО. Лучше делать все объекты статическими.</p></div></section><section id="_определение_размера_кучи"><h2>Определение размера кучи</h2><div class="paragraph"><p>Как определить размер кучи, необходимой под кучу. Можно вооружиться несколькими правилами:</p></div>
<div class="ulist"><ul><li><p>Чтобы узнать размер объекта в куче, можно воспользоваться оператором sizeof, который может вернуть
вам размер в байтах типа объекта (собственно, он будет равен размеру объекта расположенному в куче).
Таким образом узнав размер всех объектов, можно приблизительно вычислить необходимый размер кучи</p></li><li><p>Поскольку на кучи объекты могут как создаваться так и удаляться из неё, то куча может получаться
неаргументированной, т.е. между объектами может быть пустая, незаполненная память. Поэтому если вы
постоянно создаете и удаляете объекты, нужно учитывать этот факт и брать размер кучи с запасом.</p></li><li><p>Размер кучи зависит от алгоритма работы вашей программы, если вы будете создавать и удалять
последовательно объекты 100 раз, то нет никакого резона создавать кучу на 100 объектов, вполне
разумно, создать кучу под 1-2 объекта с запасом на дефрагментацию – скажем 20% и все.</p></li></ul></div>
<aside class="notes"><div class="paragraph"><p>Как вы поняли использование кучу несет ряд трудностей с расчетом её размера, помимо этого
использование кучи может тормозить выполнение программы., см, например, <a href="#Обзор одной российской RTOS">[Обзор одной российской RTOS]</a>.
Поэтому во встроенном ПО использование кучи не приветствуется, по возможности её надо избегать,
однако некоторые архитектурные приемы невозможны без использования динамических объектов (например
для позднего связывания, или факта того, что мы не хотим использовать глобальные объекты), поэтому
использовать в курсовых вы можете, но с одним условием, в нашем программном обеспечении созданные
динамические объекты никогда не должны удаляться. Таким образом мы избежим дефрагментации кучи, а
также слежением за памятью.</p></div></aside>
<div class="paragraph"><p>Для задачи размера кучи, нужно сделать те же действия что для задания размера стека, см.
<a href="#_установка_размера_стека">[_установка_размера_стека]</a></p></div></section></section>
<section><section id="_задания"><h2>Задания</h2><div class="paragraph"><p>3 Задания, кто не успеет в лабораторной, завершить дома.</p></div></section><section id="_задание_1_лекция_1_задание_1"><h2>Задание 1 #Лекция 1 Задание 1</h2><div class="olist arabic"><ol class="arabic"><li><p>Создать проект C++ c main.cpp</p></li><li><p>Подключить к проекту файл cstartup.cpp</p></li><li><p>Создать папки AbstractHardware/Registers/FiledValues, Common, Application, FreeRtos</p></li><li><p>Создать структуру проекта в соотвествии со структурой папок</p></li><li><p>Настроить STACK, HEAP</p></li><li><p>Скопировать содержимое папки Registers и Common с проекта преподавателя в свою папку</p></li><li><p>Написать программу в main.cpp</p></li></ol></div>
<pre class="highlight listingblock"><code data-noescape class="text language-text">#include "gpiocregisters.hpp" //for GPIOC
#include "rccregisters.hpp" //for RCC
int main()
{
RCC::AHB1ENR::GPIOCEN::Enable::Set() ;
GPIOC::MODER::MODER5::Output::Set() ;
GPIOC::ODR::ODR5::Enable::Set() ;
GPIOC::ODR::ODR5::Disable::Set() ;
return 0 ;
}</code></pre>
<div class="olist arabic"><ol class="arabic"><li><p>Посмотреть видео: <a href="https://youtu.be/uC0jJGfDxtM" class="bare">https://youtu.be/uC0jJGfDxtM</a></p></li></ol></div></section><section id="_задание_2"><h2>Задание 2</h2><div class="olist arabic"><ol class="arabic"><li><p>Откомпилировать и отлинковать программу</p></li><li><p>Загрузить программу в симуляторе</p></li><li><p>Сделать пошаговую отладку</p></li><li><p>Настроить Debugger на отладку через StLink</p></li><li><p>Подключить плату к компьютеру</p></li><li><p>Загрузить программу в плату</p></li><li><p>Выполнить пошаговую отладку</p></li><li><p>Описать полученный результат</p></li><li><p>Посмотреть видео: <a href="https://youtu.be/c7CasTJKw7o" class="bare">https://youtu.be/c7CasTJKw7o</a></p></li></ol></div></section><section id="_задание_3"><h2>Задание 3</h2><div class="olist arabic"><ol class="arabic"><li><p>Запустить анализатор стека. Узнать рекомендуемый размер стека.</p></li><li><p>Изменить в проекте размер стека на рекомендуемый</p></li><li><p>Создать map файл</p></li><li><p>Описать что написано в map файле</p></li><li><p>Поставить размер кучи HEAP в 0. Объяснить почему так можно сделать. И почему STACK нельзя</p></li><li><p>Добавить проект в Git и сделать синхронизацию с GitHub</p></li><li><p>Сделать отчет по каждому пункту каждого задания в файле .adoc. Выложить файл в GitHub</p></li><li><p>Прислать ссылку на GitHub преподавателю для проверки</p></li><li><p>Посмотреть видео: <a href="https://youtu.be/TajLTcjBgIg" class="bare">https://youtu.be/TajLTcjBgIg</a></p></li></ol></div></section></section>
<section id="_лекция_2"><h2>Лекция 2</h2></section>
<section><section id="_портируемость_проекта"><h2>Портируемость проекта</h2><div class="paragraph"><p>Для того, чтобы ваш проект мог хорошо портироваться на другие типы микроконтроллеров мы должны
принять некоторые меры.</p></div><div class="ulist"><ul><li><p>Применять одни и те же типы данных, имеющие один и тот же размер</p></li><li><p>Разделять часть, которая отвечает за аппаратуру и аппаратные модули, зависящую от микроконтроллера
и бизнес логику, которая не зависит от аппаратуры</p></li><li><p>Использовать разделение реализации и интерфейсов</p></li></ul></div><div class="paragraph"><p>Сейчас нам важны типы данных.</p></div></section><section id="_типы_данных"><h2>Типы данных</h2><aside class="notes"><div class="paragraph"><p>Одно из главных правил портируемости состоит в том, что для разных ядер микроконтроллеров один и тот
же тип переменной имел одинаковый размер. Для этого давайте разберемся, что такое тип и почему он
может иметь разную длину?
Для нашего микроконтроллера компилятор поддерживает следующие типы, см <a href="#Встроенные типа С++">[Встроенные типа С++]</a>.</p></div></aside>
<div class="imageblock" style=""><img src="Lection2Img/Figure3.png" alt="800" width="1280"></div><div class="title">Рисунок 24. Типы данных в С++</div></section><section id="_встроенные_типы"><h2>Встроенные типы</h2><table class="tableblock frame-all grid-all" style="width:100%"><caption class="title">Таблица 2. Встроенные типы С++</caption><colgroup><col style="width:20%"><col style="width:10%"><col style="width:70%"></colgroup><thead><tr><th class="tableblock halign-left valign-top">Тип</th><th class="tableblock halign-left valign-top">Длина</th><th class="tableblock halign-left valign-top">Комментарий</th></tr><tbody><tr><td class="tableblock halign-left valign-top"><p class="tableblock"><strong>bool</strong></p></td><td class="tableblock halign-left valign-top"><p class="tableblock">1</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Представляет значения, которые могут быть или <strong>true</strong>, или <strong>false</strong>.</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock"><strong>char</strong></p></td><td class="tableblock halign-left valign-top"><p class="tableblock">1</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Используется для символов ASCII в старых строках в стиле C или в объектах std::string,
которые никогда не будут преобразовываться в Юникод.</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock"><strong>unsigned char</strong></p></td><td class="tableblock halign-left valign-top"><p class="tableblock">1</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Аналог байта. В С++17 стандарте появился тип std::byte</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock"><strong>int</strong></p></td><td class="tableblock halign-left valign-top"><p class="tableblock">4</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Целочисленное значение. Выбор по умолчанию для целых чисел</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock"><strong>unsigned int</strong></p></td><td class="tableblock halign-left valign-top"><p class="tableblock">4</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Беззнаковое целое число</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock"><strong>float</strong></p></td><td class="tableblock halign-left valign-top"><p class="tableblock">4</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Число с плавающей точкой, поддерживается аппаратно некоторыми микроконтроллерами</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock"><strong>double</strong></p></td><td class="tableblock halign-left valign-top"><p class="tableblock">8</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Число с плавающей запятой двойной точности. Выбор по умолчанию для значений с плавающей
точкой</p></td></tr></table></section><section id="_модификаторы_типов_данных"><h2>Модификаторы типов данных</h2><table class="tableblock frame-all grid-all" style="width:100%"><caption class="title">Таблица 3. Встроенные типа С++ модификаторы</caption><colgroup><col style="width:20%"><col style="width:10%"><col style="width:70%"></colgroup><thead><tr><th class="tableblock halign-left valign-top">Тип</th><th class="tableblock halign-left valign-top">Длина</th><th class="tableblock halign-left valign-top">Комментарий</th></tr><tbody><tr><td class="tableblock halign-left valign-top"><p class="tableblock"><strong>short int</strong></p></td><td class="tableblock halign-left valign-top"><p class="tableblock">2</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Целочисленное знаковое значение укороченной длины</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock"><strong>unsigned short int</strong></p></td><td class="tableblock halign-left valign-top"><p class="tableblock">2</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Целочисленное беззнаковое значение укороченной длины</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock"><strong>long int</strong></p></td><td class="tableblock halign-left valign-top"><p class="tableblock">8</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Выбор по умолчанию для целочисленных значений. На платформах на которых int равен по
длине unsigned short int может быть длиннее int</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock"><strong>unsigned long int</strong></p></td><td class="tableblock halign-left valign-top"><p class="tableblock">8</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Целое число двойной длины. На платформах на которых int равен по длине unsigned short int может быть
длиннее int</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock"><strong>long double</strong></p></td><td class="tableblock halign-left valign-top"><p class="tableblock">8</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Число с плавающей точкой двойной точности с двойной точностью </p></td></tr></table></section><section id="_размеры_типов_данных"><h2>Размеры типов данных</h2><div class="paragraph"><p>Размеры типов не четко определены и могут отличаться для различных микроконтроллеров. Для размеров
типов существует правило:</p></div>
<pre class="highlight listingblock source"><code data-noescape class="cpp language-cpp">1 <= sizeof(char) <= sizeof() <= sizeof(short) <= sizeof(int) <= sizeof(long)
1 <= sizeof(bool) <= sizeof(long)
sizeof(char) <= sizeof(long)
sizeof(float) <= sizeof(double) <= sizeof(long double)
sizeof(T) == sizeof(signed T) == sizeof(unsigned T)</code></pre>
<div class="paragraph"><p>Поэтому вместо прямых типов типа int, используйте псевдонимы, например:</p></div>
<div class="hdlist"><table><tr><td class="hdlist1">std::uint32_t</td><td class="hdlist2"><p>целое беззнаковое длиной 32 бита</p></td></tr><tr><td class="hdlist1">std::int64_t</td><td class="hdlist2"><p>целое знаковое длинной 64 бита</p></td></tr><tr><td class="hdlist1">std::uint8_t</td><td class="hdlist2"><p>целое знаковое длинной 8 бит</p></td></tr></table></div></section><section id="_пользовательские_типы"><h2>Пользовательские типы</h2><div class="paragraph"><p>Вы можете определить свой тип сами, либо сделать псевдоним типа. Любой класс или структура,
определенная вами, будет являться вашим типом. Например:</p></div>
<pre class="highlight listingblock source"><code data-noescape class="cpp language-cpp">template<typename T>
struct Complex
{
Complex(T r, T im): real{r}, imaginary{im} {} ;
operator T { return sqrt(real*real + imaginary* imaginary) ;}
Complex operator +(Complex value)
{
return Complex(real+ value.real, imaginary + value.imaginary) ;
}
private:
T real; //вещественная часть
T imaginary //мнимая часть
} ;
int main()
{
Complex<float> value1(3.0f, 4.0f) ;
Complex<float> value1(1.0f, 2.0f) ;
value1 += value2 ;
return 0;
}</code></pre></section><section id="_псевдонимы_типов"><h2>Псевдонимы типов</h2><div class="paragraph"><p>Для того, чтобы было было понятнее работать с типом можно вводить их псевдонимы (alias). С помощью
ключевого слова <strong>using</strong> ;</p></div>
<pre class="highlight listingblock"><code data-noescape class="cpp language-cpp">auto t = std::make_tuple(10, "Test", 3.14, 2U); <b>(1)</b>
using tMytype = decltype(t) ; <b>(2)</b>
using tShortType = std::tuple<int, string, double, tU32> ; <b>(3)</b>
void(tMyType & value) { <b>(4)</b>
...
}
int main() {
using tU32 = unsigned int ; <b>(5)</b>
tU32 i = 10U ; <b>(6)</b>
myfunction(t) ; <b>(7)</b>
}</code></pre>
<aside class="notes"><div class="colist arabic"><ol><li><p>Определяем кортеж из 4 элементов разного типа</p></li><li><p>Объявляем псевдоним типа, который имеет кортеж (тип выводится компилятором)</p></li><li><p>Тоже самое что и <2> за исключением того, что указываем тип напрямую</p></li><li><p>Объявляем функцию, принимающую аргумент типа, который имеет кортеж</p></li><li><p>Объявляем псевдоним типа unsigned int</p></li><li><p>Определяем переменную типа unsigned int</p></li></ol></div></aside></section><section id="_неявное_преобразование_типов"><h2>Неявное преобразование типов</h2><div class="paragraph"><p>Базовые/простые типы неявно можно привести друг к другу. Т.е</p></div>
<pre class="highlight listingblock source"><code data-noescape class="cpp language-cpp">int a = 0; <b>(1)</b>
char a = 512; <b>(2)</b>
int a = 3.14; <b>(3)</b>
bool a = -4; <b>(4)</b>
bool a = 0; <b>(5)</b></code></pre>
<div class="colist arabic"><ol><li><p>Присваимаем знаковое целое(int) число переменной целого типа</p></li><li><p>Присваиваем знаковое целое(int) число переменной типа char. Результат в а 0 ;</p></li><li><p>Присваиваем число с плавающей точкой(double) к переменной типа int. Результат в а 3</p></li><li><p>Присваиваем знаковое целое(int) к переменной типа bool. Результат в а true</p></li><li><p>Присваиваем знаковое целое(int) к переменной типа bool. Результат в а false</p></li></ol></div></section><section id="_явное_преобразование_типов"><h2>Явное преобразование типов</h2><div class="paragraph"><p>Так как компилятор может сделать за вас, то, что вы вообще не ожидаете, не нужно использовать неявное
преобразование типа.</p></div>
<div class="paragraph"><p>Вместо этого, лучше указать компилятору явное преобразование из одного типа в другой.
В этом случае, вы говорите компилятору, что я понимаю, что я делаю, это именно так и задумано</p></div>
<div class="paragraph"><p>Для преобразований из одного типа используют 4 вариантов преобразования:</p></div>
<div class="ulist"><ul><li><p>static_cast</p></li><li><p>const_cast</p></li><li><p>reinterpret_cast</p></li><li><p>dynamic_cast</p></li></ul></div></section><section id="_static_cast"><h2>static_cast</h2><div class="paragraph"><p><strong>static_cast</strong> позволяет сделать приведение близких типов (целые, пользовательских типов которые могут
создаваться из типов который приводится, и указатель на void* к указателю на любой тип).</p></div>
<div class="paragraph"><p>Проверка производится на уровне компиляции, так что в случае ошибки сообщение будет получено в момент
сборки приложения или библиотеки.</p></div>
<pre class="highlight listingblock source"><code data-noescape class="cpp language-cpp">int a = static_cast<int>(0); <b>(1)</b>
int a = static_cast<int>(3.14); <b>(2)</b>
bool a = static_cast<bool>(-4); <b>(3)</b>
bool a = static_cast<bool>(0); <b>(4)</b>
float f = 3.14f ; <b>(5)</b>
float f = static_cast<float>(3.14) ; <b>(6)</b>
Complex f = static_cast<3.14> <b>(7)</b></code></pre>
<aside class="notes"><div class="colist arabic"><ol><li><p>Явно говорим, что 0 должен восприниматься как тип (int), хотя он и так является литералом типа int.
Но все ли помнят об этом?</p></li><li><p>Явно говорим, что 3.14 воспринимать как int, т.е взять только целую часть.</p></li><li><p>Явно говорим, -4 нужно воспринять как bool тип, в данном случае true.</p></li><li><p>Явно говорим, 0 нужно воспринять как bool тип, в данном случае false.</p></li><li><p>Явно говорим, что 3.14 это float</p></li><li><p>Явно говорим, что 3.14 это float</p></li><li><p>Комплексное число может создаться из double, поэтому тут будет работать static_cast.</p></li></ol></div></aside></section><section id="_reinterpret_cast"><h2>reinterpret_cast</h2><div class="paragraph"><p><strong>reinterpret_cast</strong> преобразует типы, несовместимыми друг с другом, и используется для:</p></div>
<div class="ulist"><ul><li><p>В свой собственный тип</p></li><li><p>Указателя в интегральный тип</p></li><li><p>Интегрального типа в указатель</p></li><li><p>Указателя одного типа в указатель другого типа</p></li><li><p>Указателя на функцию одного типа в указатель на функцию другого типа</p></li></ul></div>
<pre class="highlight listingblock source"><code data-noescape class="cpp language-cpp">auto ptr = reinterpret_cast<volatile uint32_t *>(0x40010000) ; <b>(1)</b>
auto value = *ptr ; <b>(2)</b></code></pre>
<div class="colist arabic"><ol><li><p>Преобразует адрес 0x40010000 в указатель типа volatile uint32_t</p></li><li><p>Записывает в переменную value (типа) значение лежащее по указателю ptr, указывающего на адрес
0x40010000</p></li></ol></div></section></section>
<section><section id="_память"><h2>Память</h2><div class="paragraph"><p>Как говорилось в первой лекции, ARM имеет общее адресное пространство для данных и команд.</p></div><div class="paragraph"><p>Ядро ARM имеет 4 Гбайт последовательной памяти с адресов 0x00000000 до 0xFFFFFFFF.</p></div><div class="paragraph"><p>Различные типы памяти могут быть расположены по эти адресам. Обычно микроконтроллер имеет постоянную
память, из которой можно только читать (ПЗУ) и оперативную память, из которой можно читать и в
которую можно писать (ОЗУ).</p></div><div class="paragraph"><p>Также часть адресов этой памяти отведены под регистры управления и регистры периферии.</p></div></section><section id="_память_микроконтроллера_cortexm4"><h2>Память микроконтроллера CortexM4</h2><div class="imageblock" style=""><img src="Lection2Img/Figure4.png" alt="300" width="500"></div><div class="title">Рисунок 25. Карта памяти микропроцессора</div>
<aside class="notes"><div class="paragraph"><p>Микроконтроллер на ядре Cortex M4 выполнен по Гарвардской архитектуре, память здесь разделена на
три типа:</p></div>
<div class="ulist"><ul><li><p>ПЗУ (FLASH память в которой храниться программа)</p></li><li><p>ОЗУ память для хранения временных данных (туда же можно по необходимости переместить программу и
выполнить её из ОЗУ), память в которой находятся регистры отвечающие за настройку и работу с
периферией и</p></li><li><p>Память для хранения постоянных данных ЕЕPROM.</p></li></ul></div>
<div class="exampleblock"><div class="content"><div class="paragraph"><p>Адресное пространство памяти программы (ПЗУ) находится по адресам <strong>0x00000000</strong> по <strong>0x1FFFFFFF</strong></p></div>
<div class="paragraph"><p>Адресное пространство ОЗУ находится по адресам <strong>0x20000000</strong> по <strong>0x3FFFFFFF</strong></p></div>
<div class="paragraph"><p>Адресное пространство для регистров периферии находится по адресам с <strong>0x40000000</strong> по <strong>0x5FFFFFFF</strong></p></div>
<div class="paragraph"><p>Памяти EEPROM микропроцессора Stm32F411RE не содержит, см <a href="#Карта памяти микропроцессора">[Карта памяти микропроцессора]</a>.
Более подробно вы можете изучить адресное пространство микропроцессора в спецификации на микропроцессор <a href="#12">[12]</a>.</p></div></div></div></aside></section><section id="_память_для_расположения_данных"><h2>Память для расположения данных</h2><div class="paragraph"><p>Данные в памяти могут быть расположены 3 различными способами:</p></div>
<div class="ulist"><ul><li><p>Авто(локальные) переменные, которые являются локальными в функции располагаются в регистрах или в стеке.</p></li></ul></div>
<aside class="notes"><div class="paragraph"><p>Такие переменные "существуют" только внутри функции, как только функция закончится и вернется к вызывающему
объекту, эти переменные становятся не валидными.</p></div></aside>
<div class="ulist"><ul><li><p>Глобальные переменные или статические переменные. В этом случае они инициализируются единожды.</p></li></ul></div>
<aside class="notes"><div class="paragraph"><p>Static означает, что та память, которая была выделена под эту переменную не будет изменяться и
закрепляется за этой переменной до конца работы приложения.</p></div></aside>
<div class="ulist"><ul><li><p>Динамически размещаемые данные. Данные создаваемые на Куче(Heap)</p></li></ul></div>
<aside class="notes"><div class="paragraph"><p>Если заранее не известно, сколько объектов нужно создать, и сколько памяти они будут отнимать, то придется
создавать их динамически, например с помощью оператора new, в таком случае, объекты будут создаваться в куче.</p></div></aside>
<h3>Память под функции(команды)</h3><div class="paragraph"><p>Для расположения функций используется та же самая память с границами от <strong>0x00000000 - 0xFFFFFFFF</strong>.</p></div>
<div class="paragraph"><p>По умолчанию весь код будет лежать в сегменте .text, который расположен в readonly памяти (обычно в ROM),
но можно разместить функции и в ОЗУ.</p></div></section><section id="_указатели"><h2>Указатели</h2><aside class="notes"><div class="paragraph"><p>Как мы уже поняли, данные могут находится в ОЗУ или ПЗУ. Каждой переменной содержащей данные соответствует
некий адрес памяти. К переменной можно обратиться непосредственно обращаясь к самой переменной, тогда мы
можем напрямую писать или читать значение с адреса переменной, либо можно обратиться косвенно, через указатель
или ссылку.</p></div></aside>
<div class="paragraph"><p>Указатель это переменная, которая хранит адрес какой-то другой переменной:</p></div>
<pre class="highlight listingblock"><code data-noescape class="cpp language-cpp">int main() {
int c = 463 ; <b>(1)</b>
int* ptr = &c ; <b>(2)</b>
return 0;
}</code></pre>
<aside class="notes"><div class="colist arabic"><ol><li><p>Объявляем переменную <strong>c</strong> типа <strong>int</strong></p></li><li><p>объявляем указатель <strong>ptr</strong> на переменную <strong>c</strong> типа <strong>int</strong></p></li></ol></div></aside>
<div class="imageblock" id="Указатель" style=""><img src="Lection2Img/Figure5.png" alt="400" width="400"></div><div class="title">Рисунок 26. Указатель</div>
<div class="paragraph"><p>Размер указателя для нашего микроконтроллера 4 байта (32 бита).</p></div></section><section id="_взятие_адреса_и_разыменование_указателя"><h2>Взятие адреса и разыменование указателя.</h2><pre class="highlight listingblock"><code data-noescape class="cpp language-cpp">int main() {
int c = 463 ; <b>(1)</b>
int* ptr = &c ; <b>(2)</b>
cout << &c ; <b>(3)</b>
cout << c ; <b>(4)</b>
*ptr = 5; <b>(5)</b>
cout << c << ": " << *ptr; <b>(6)</b>
}</code></pre>
<div class="colist arabic"><ol><li><p>Объявление переменной</p></li><li><p>Оператор & - оператор взятия адреса.</p></li><li><p>Выведется адрес переменной <strong>с</strong> (0х100)</p></li><li><p>Выведется значение переменной с (463)</p></li><li><p>Операция разыменование указателя, записываем в переменную по адресу, который лежит в ptr, число 5</p></li><li><p>Вывод значения переменной с и значения лежащего по адресу, на который указывает указатель (5: 5)
По сути с и *ptr это одно и то же.</p></li></ol></div></section><section id="_операции_над_указателями"><h2>Операции над указателями</h2><aside class="notes"><div class="paragraph"><p>Указатели можно складывать, вычитать, сравнивать. Но указатели должны быть одного типа. Т.е. не нужно
например складывать укатель типа <strong>char</strong> * и <strong>int</strong> *</p></div></aside>
<pre class="highlight listingblock"><code data-noescape class="cpp language-cpp">int main() {
int arr[] = {1,2,3,4,5} ; <b>(1)</b>
int* ptr = arr ; <b>(2)</b>
ptr ++ ; <b>(3)</b>
int a = *(ptr + 4) ; <b>(4)</b>
if(ptr != nullptr) <b>(5)</b>
cout << a << ": " << *ptr; <b>(6)</b>
}</code></pre>
<div class="colist arabic"><ol><li><p>Объявление массива <strong>arr</strong> из 5 элементов. В целом можно считать, что массив <strong>arr</strong> это указатель на первый элемент массива.</p></li><li><p>Обявления указателя на массив типа <strong>int</strong> ;</p></li><li><p>Увеличиваем указатель на 1. На самом деле мы смещаемся по адресам на размер равный <strong>size_of(int)</strong>, т.е. на 4 байта. Т.е
в данном случае указатель <strong>ptr</strong> стал указывать на элемент массива <strong>arr[1]</strong>.</p></li><li><p>Объявляем переменную <strong>а</strong> типа <strong>int</strong> и присваиваем ей значение <strong>аrr[4]</strong>.</p></li><li><p>Сравнение указателя с nullptr указателем.</p></li><li><p>Вывод значения <strong>а</strong> и значения по адресу в указателе <strong>ptr</strong>. Вывод (5: 2)</p></li></ol></div></section><section id="_сложение_указателей"><h2>Сложение указателей</h2><pre class="highlight listingblock"><code data-noescape class="cpp language-cpp">int main() {
int arr[] = {1,2,3,4,5} ; <b>(1)</b>
int* ptr = arr ; <b>(2)</b>
ptr ++ ; <b>(3)</b>
int a = *(ptr + 3) ; <b>(4)</b>
}</code></pre>
<div class="imageblock" style=""><img src="Lection2Img/Figure6.png" alt="800" width="800"></div><div class="title">Рисунок 27. Сложение указателей</div>
<aside class="notes"><div class="colist arabic"><ol><li><p>Объявление массива <strong>arr</strong> из 5 элементов. В целом можно считать, что массив <strong>arr</strong> это указатель на первый элемент массива.</p></li><li><p>Обявления указателя на массив типа <strong>int</strong> ;</p></li><li><p>Увеличиваем указатель на 1. На самом деле мы смещаемся по адресам на размер равный <strong>size_of(int)</strong>, т.е. на 4 байта. Т.е
в данном случае указатель <strong>ptr</strong> стал указывать на элемент массива <strong>arr[1]</strong>.</p></li><li><p>Записываем в переменную а типа int данные, находящиеся по адресу, хранящиеся в указателе ptr, смещенном на 3.</p></li></ol></div></aside></section><section id="_константный_указатель_и_указатель_на_константу"><h2>Константный указатель и указатель на константу</h2><pre class="highlight listingblock"><code data-noescape class="cpp language-cpp">int main() {
const auto pi[] = {3.14, 3.14159} ;
const double *ptr = pi ;
*ptr = 3.14159 ; <b>(1)</b>
ptr++ ; <b>(2)</b>
count << *ptr ; <b>(3)</b>
const double * const ptr1 = pi ; <b>(4)</b>
ptr1++ ; <b>(5)</b>
retrun 0 ;
}</code></pre>
<div class="colist arabic"><ol><li><p>Пытаемся поменять значение по указателю <strong>ptr</strong> (pi[0]). Ошибка, указатель на константу, нельзя
поменять значение константы</p></li><li><p>Увеличиваем указатель на 1 (теперь указатель указывает на p[1]).</p></li><li><p>Вывод значения по указателю (3.14159)</p></li><li><p>Объявляем константный указатель на константу</p></li><li><p>Нельзя изменить указатель, он константный</p></li></ol></div></section><section id="_ссылка"><h2>Ссылка</h2><pre class="highlight listingblock"><code data-noescape class="cpp language-cpp">int main(){
int a = 0;
int &ref = a ; <b>(1)</b>
ref = 10; <b>(2)</b>
cout << &ref << ": " << ref ; <b>(3)</b>
return 0 ;
}</code></pre>
<div class="colist arabic"><ol><li><p>Объявляем ссылку на переменную <strong>а</strong></p></li><li><p>Записываем в переменную <strong>а</strong> число 10</p></li><li><p>Выводим адрес перменной <strong>а</strong> и значение переменной <strong>a</strong></p></li></ol></div>
<div class="openblock"><div class="content"><div class="paragraph"><p>Ссылка это псевдоним переменной.</p></div></div></div>
<div class="ulist"><ul><li><p>У ссылки нельзя взять адрес. Если применить оператор взятия адреса к ней, то будет выведен адрес
переменной, на которую она ссылается</p></li><li><p>Ссылка ведет себя почти также как константный указатель. Её нельзя изменять, складывать, вычитать</p></li><li><p>Ссылки нельзя сравнивать</p></li><li><p>Ссылка не может быть не проинициализирована.</p></li></ul></div></section></section>
<section><section id="_регистр"><h2>Регистр</h2><div class="ulist"><ul><li><p>Существуют регистры общего назначения и специальные регистры. Регистры общего назначения расположены
внутри ядра микроконтроллера(сверхбыстрая память).</p></li><li><p>Регистры общего назначения - это сверхбыстрая память внутри процессора, предназначенная для
хранения адресов и промежуточных результатов вычислений (регистр общего назначения/регистр данных)
или данных, необходимых для работы самого процессора.</p></li><li><p>Регистры специального назначения расположены в ОЗУ микроконтроллера и используются для управления
процессором и периферийными устройствами.</p></li><li><p>Каждый регистр в архитектуре ARM представляет собой ресурс памяти и имеет длину в 32 бита, где каждый
бит можно представить в виде выключателя с помощью которого осуществляется управление тем или иным
параметром микроконтроллера <a href="#10">[10]</a>.</p></li></ul></div></section><section id="_регистры_общего_назначения"><h2>Регистры общего назначения</h2><div class="paragraph"><p>С точки зрения прикладного программиста, процессор располагает 16-ю 32-разрядными регистрами общего
назначения (РОН, GPR), из которых три на деле имеют специальные функции:</p></div>
<div class="ulist"><ul><li><p>Оперативные регистры</p></li><li><p>Вспомогательные регистры</p></li><li><p>Специальные регистры</p></li></ul></div></section><section id="_оперативные_регистры"><h2>Оперативные регистры</h2><div class="paragraph"><p>Регистры <strong>R0-R3</strong>, <strong>R12</strong> являются оперативными(sratch) регистрами. Любая функция может использовать эти
регистры по своему усмотрению и уничтожать содержимое этих регистров.</p></div>
<div class="paragraph"><p>Если функции нужны значения этих регистров после вызова другой функции, она должна сохранить их на
стеке, а после вызова восстановить.</p></div></section><section id="_вспомогательные_регистры"><h2>Вспомогательные регистры</h2><div class="paragraph"><p>Регистры от <strong>R4-R11</strong> являются вспомогательными. Любая функция должна сохранить их на входе, а при
выходе восстановить их значение.</p></div></section><section id="_специальные_регистры"><h2>Специальные регистры</h2><div class="ulist"><ul><li><p>Регистр указателя на стек <strong>R13/SP</strong>, должен всегда указывать на последний элемент стека или ниже него.</p></li><li><p>Регистр <strong>R15/PC</strong> есть программный счетчик.</p></li><li><p>Регистр <strong>R14/LR</strong>, содержит адрес возврата функции.</p></li></ul></div></section><section id="_регистр_специального_назачения"><h2>Регистр специального назачения</h2><div class="imageblock" id="Register" style=""><img src="Lection2Img/Figure1.png" alt="800" width="800"></div><div class="title">Рисунок 28. Схематичное изображение регистра</div>
<aside class="notes"><div class="ulist"><ul><li><p>Название регистра</p></li></ul></div></aside>
<div class="ulist"><ul><li><p>Адрес регистра обозначается 32-битным шестнадцатеричным числом.</p></li><li><p>Тип доступа к ячейкам регистра.</p></li><li><p>Длина - количество ячеек в одном регистре. Мы будем работать с 32-битными регистрами.</p></li><li><p>Поле - набор ячеек регистра, отвечающих за работу одной из функции микроконтроллера</p></li><li><p>Значение поля - есть пространство всех возможных величин, которые может принимать поле</p></li></ul></div>
<aside class="notes"><div class="paragraph"><p>Значение поля зависит от длины поля. Т.е. если поле имеет длину 2, то существует 4 возможные
значения поля (0,1,2,3). Так же как у регистра, у полей и значений полей есть режим доступа (чтение,
записать, чтение и запись)</p></div></aside></section><section id="_пример_регистра_специального_назначения"><h2>Пример регистра специального назначения</h2><aside class="notes"><div class="paragraph"><p>Как было сказано выше регистры используются для управления микроконтроллером и его периферией.
Например, чтобы запустить таймер 1 на счет, необходимо в Таймере1, в регистре <strong>CR1(Control Register1)</strong>
в поле <strong>CEN(Counter Enable)</strong> установить значение 1 (Enable).</p></div></aside>
<div class="imageblock" id="RegisterCR1" style=""><img src="Lection2Img/Figure2.png" alt="800" width="800"></div><div class="title">Рисунок 29. Регистр CR1 Таймера 1</div>
<div class="literalblock"><div class="content"><pre>Бит 0 CEN: Включить счетчик
0: Счетчик включен: Disable
1: Счетчик выключен: Enable</pre></div></div>
<div class="paragraph"><p>Здесь, например, CEN — это поле размером 1 бит имеющее смещение 0 относительно начала регистра.
А Enable(1) и Disable(0) это его возможные значения.</p></div></section><section id="_доступ_к_регистру_специального_назначения"><h2>Доступ к регистру специального назначения</h2><div class="paragraph"><p>Так как регистр специального назначения - это просто адресуемая ячейка памяти, то в коде это может
мы можем обратиться к данным по этому адресу, разыменовывая указатель, указывающий на этот адрес:</p></div>
<pre class="highlight listingblock"><code data-noescape class="cpp language-cpp">int main()
{
*reinterpret_cast<uint32_t *>(0x40010000) |= 1 << 0 ; <b>(1)</b>
TIM1::CR1::CEN::Enable::Set() ; <b>(2)</b>
}</code></pre>
<div class="colist arabic"><ol><li><p>Записываем 1 в нулевой бит ячейки памяти (регистра) по адресу 0x40010000</p></li><li><p>Тоже самое, но с использование специального класса на С++</p></li></ol></div></section><section id="_работа_с_регистрами_периферии_через_обертку_на_с"><h2>Работа с регистрами периферии через обертку на С++</h2><aside class="notes"><div class="paragraph"><p>Для того, чтобы настроить определенное периферийное устройство процессора, необходимо изменить
значение поля соответствующем регистре.</p></div>
<div class="paragraph"><p>Для более удобной работы с регистрами можно использовать С++ обертку. Эта обертка позволяет обращаться
к регистрам в форме очень похоже с тем, как эти регистры описаны в документации.</p></div>
<div class="paragraph"><p>Так, например, для запуска внешнего источника частоты, необходимо обратиться к регистру “CR”
периферии “RCC”, полю "HSEON" и установить в нем значение Enable.
Операция обращения к регистру выглядит следующим образом:</p></div></aside>
<pre class="highlight listingblock"><code data-noescape class="cpp language-cpp">---
int main()
{
RCC::CR::HSEON::Enable::Set() ;
}
---</code></pre>
<div class="imageblock" id="RCC::CR" style=""><img src="Lection2Img/Figure9.png" alt="Figure9"></div><div class="title">Рисунок 30. Подсказка для регистра CR модуля периферии RCC</div></section><section id="_некоторые_моменты_при_работе_с_оберткой_с_для_регистров"><h2>Некоторые моменты при работе с оберткой С++ для регистров</h2><aside class="notes"><div class="paragraph"><p>Код для регистров был сгенерирован автоматически, см <a href="#13">[13]</a>. Поэтому по умолчанию все значения полей
называются в формате ValueX, где Х-само значение. Поэтому тот момент когда нужно их использовать,
нужно заглянуть в документацию и поменять слова Value, на что-то более внятное.</p></div>
<div class="paragraph"><p>Для того, чтобы найти место где объявляется значение поля, необходимо правой мышкой нажать на значении
и найти все его объявления.</p></div></aside>
<div class="imageblock" id="RCC::CR" style=""><img src="Lection2Img/Figure10.png" alt="Figure10"></div><div class="title">Рисунок 31. Поиск места объявления значения</div>
<aside class="notes"><div class="paragraph"><p>На самом деле, все значения полей определены в файлах, которые лежат в папке:
AbstractHardware\Registers\STM32F411\FieldValues</p></div>
<div class="paragraph"><p>Можно открыть файл с именем [имя периферии]filedvalues.hpp и найти там структуру названием
ИМЯ ПЕРИФЕРИ_ИМЯ РЕГИСТРА_ИМЯ ПОЛЯ_Values.</p></div>
<div class="paragraph"><p>Например, для значений поля HSEON модуля периферии RCC, регистра CR, необходимо:</p></div></aside>
<div class="olist arabic"><ol class="arabic"><li><p>открыть файл AbstractHardware\Registers\STM32F411\FieldValues\rccfieldvalues.hpp,</p></li><li><p>найти структуру struct RCC_CR_HSEON_Values</p></li><li><p>поменять в этой структуре <strong>Value0</strong> на <strong>Disable</strong>, а <strong>Value1</strong> на <strong>Enable</strong>.</p></li></ol></div></section></section>
<section><section id="_соглашение_об_вызовах"><h2>Соглашение об вызовах</h2><div class="paragraph"><p>Соглашение об вызовах включает в себя:
* Объявление функции
* Компоновка С и С++ кода
* Последовательность использования оперативных регистров и вспомогательные регистров
* Вход в функцию
* Выход из функции
* Обработка адреса возврата</p></div></section><section id="_объявление_функции"><h2>Объявление функции</h2><div class="paragraph"><p>Функция должна быть объявлена в таком порядке, чтобы компилятор мог узнать как её вызвать.
Объявление функции может выглядеть следующим образом:</p></div>
<div class="exampleblock"><div class="content"><div class="paragraph"><p>int MyFunction(int first, char * second);</p></div></div></div>
<div class="paragraph"><p>Все что знает об этой функции компилятор, это то, что она принимает два параметра: целое и указатель
на символ. И функция должна вернуть целое значение. Этого достаточно для компилятора, чтобы понять
как вызвать эту функцию.</p></div></section><section id="_компоновка_с_и_с_кода"><h2>Компоновка С и С++ кода</h2><div class="paragraph"><p>В C+ , функция может компоноваться либо как С +, либо как С функция. Пример объявления функции
с Си компоновкой:</p></div>
<pre class="highlight listingblock"><code data-noescape class="c language-c">extern "C" {
int F(int);
}</code></pre>
<div class="paragraph"><p>Если вы хотите вызвать функции ассемблера из С++, то лучше объявить эту функцию, как имеющую тип
компоновки Си</p></div></section><section id="_вход_в_функцию"><h2>Вход в функцию</h2><div class="paragraph"><p>Параметры передающие в функцию могут использовать два метода:</p></div>
<div class="ulist"><ul><li><p>Через регистры</p></li><li><p>Через стек</p></li></ul></div>
<div class="paragraph"><p>Для большей эффективности параметры передаются через регистры, но их число ограничено, поэтому
если регистров не хватает, то используется стек. Для передачи параметров используются
оперативные регистры <strong>R0:R3</strong></p></div></section><section id="_выход_из_функции"><h2>Выход из функции</h2><div class="paragraph"><p>Функция может вернуть значение. Для возврата значения используются регистры <strong>R0:R1</strong>. Если значение
больше 64 бит, то в регистр R0 записывается адрес где лежат данные.</p></div>
<div class="paragraph"><p>Вызывающая функция обязана очистить стек, после того, как вызываемая функция вернула значение.</p></div></section><section id="_операторы"><h2>Операторы</h2><div class="ulist"><ul><li><p>Арифметические операторы</p></li><li><p>Операторы сравнения</p></li><li><p>Логические операторы</p></li><li><p>Побитовые операторы</p></li><li><p>Составное присваивание</p></li><li><p>Операторы работы с указателями и членами класса</p></li><li><p>Функторы, тернарные операции, sizeof(), запятая, приведение типа, new</p></li></ul></div>
<aside class="notes"><div class="paragraph"><p>Все операторы можно переопределить</p></div></aside></section><section id="_арифметические_операторы"><h2>Арифметические операторы</h2><aside class="notes"><div class="paragraph"><p>Арифметические операторы предоставляют базовые арифметические действия над типами, такие как
сложение, вычитание, деление, умножение, остаток от деления, присваивание. Любой оператор
может быть определен для множества пользовательского типа. Т.е. вы можете создать свой тип и определить
арифметические операторы для вашего типа. Например, можно определить арифметические операторы для
множества комплексных чисел, которые могут быть представлены в виде вашего собственного
пользовательского типа.</p></div></aside>
<table class="tableblock frame-all grid-all" style="width:100%"><caption class="title">Таблица 4. Арифметические операторы</caption><colgroup><col style="width:33.3333%"><col style="width:33.3333%"><col style="width:33.3334%"></colgroup><thead><tr><th class="tableblock halign-left valign-top">Операция</th><th class="tableblock halign-left valign-top">Оператор</th><th class="tableblock halign-left valign-top">Комментарий</th></tr><tbody><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Присваивание</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">=</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">a = b</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Сложение</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">+</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">a + b</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Вычитание</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">-</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">a - b</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Унарный плюс</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">+</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">+a</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Унарный минус</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">-</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">-a</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Умножение</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">*</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">a * b</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Деление</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">/</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">a / b</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Остаток от деления</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">%</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">a % b</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Инкремет (пост и предфиксный)</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">++</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">++a и a++</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Декремент (пост и предфиксный)</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">- -</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">--a и a--</p></td></tr></table></section><section id="_логические_операторы"><h2>Логические операторы</h2><aside class="notes"><div class="paragraph"><p>Логические операторы предоставляют действия над булевым типов. Результат действия этих операторов
может быть только <strong>true</strong> или <strong>false</strong></p></div></aside>
<table class="tableblock frame-all grid-all" style="width:100%"><caption class="title">Таблица 5. Логические операторы</caption><colgroup><col style="width:21.0526%"><col style="width:21.0526%"><col style="width:21.0526%"><col style="width:36.8422%"></colgroup><thead><tr><th class="tableblock halign-left valign-top">Операция</th><th class="tableblock halign-left valign-top">Оператор</th><th class="tableblock halign-left valign-top">Комментарий</th><th class="tableblock halign-left valign-top">Пример</th></tr><tbody><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Логическое отрицание, НЕ</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">!</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">!a</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">!true ⇒ false</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Логическое умножение, И</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">&&</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">a &&</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">true && false ⇒ false</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Логическое сложение, ИЛИ</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">||</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">a | | b</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">true | | false ⇒ true</p></td></tr></table></section><section id="_побитовые_операторы"><h2>Побитовые операторы</h2><aside class="notes"><div class="paragraph"><p>Побитовые операторы предоставляют действия с битами.</p></div></aside>
<table class="tableblock frame-all grid-all" style="width:100%"><caption class="title">Таблица 6. Побитовые операторы</caption><colgroup><col style="width:21.0526%"><col style="width:21.0526%"><col style="width:21.0526%"><col style="width:36.8422%"></colgroup><thead><tr><th class="tableblock halign-left valign-top">Операция</th><th class="tableblock halign-left valign-top">Оператор</th><th class="tableblock halign-left valign-top">Комментарий</th><th class="tableblock halign-left valign-top">Пример</th></tr><tbody><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Побитовая инверсия</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">~</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">~a</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">unsigned char a = 0; ~a ⇒ 0xFF</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Побитовое И</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">&</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">a & b</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">unsigned char a = 1, b = 3; a & b ⇒ 1</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Побитовое ИЛИ</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">|</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">a | b</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">unsigned char a = 1, b = 3; a | b ⇒ 3</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Побитовое исключающее ИЛИ</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">^</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">a ^ b</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">unsigned char a = 1, b = 3; a ^ b ⇒ 2</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Побитовый сдвиг влево</p></td><td class="tableblock halign-left valign-top"><p class="tableblock"><<</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">a << b</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">unsigned char a = 1, b = 3; a << b ⇒ 8</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Побитовый сдвиг вправо</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">>></p></td><td class="tableblock halign-left valign-top"><p class="tableblock">a >> b</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">unsigned char a = 8, b = 3; a >> b ⇒ 1</p></td></tr></table></section></section>
<section id="_отладочная_плата"><h2>Отладочная плата</h2><table class="tableblock frame-all grid-all" style="width:100%"><colgroup><col style="width:50%"><col style="width:50%"></colgroup><tbody><tr><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p><strong>STM32F411RET6 ядро:</strong> ARM® 32-bit Cortex™-M4</p></li></ul></div></div></td><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p><strong>CP2102:</strong> USB - UART преобразователь</p></li></ul></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p><strong>Arduino разъем:</strong> для подключения Arduino шилдов </p></li></ul></div></div></td><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p><strong>ICSP interface:</strong> Arduino ICSP</p></li></ul></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p><strong>USB разъем:</strong> USB коммуникационный интерфейс</p></li></ul></div></div></td><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p><strong>SWD interface:</strong> для программирования и отладки</p></li></ul></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p><strong>ST Morpho разъемы:</strong> для упрощения расширения</p></li></ul></div></div></td><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p><strong>6-12 V DC вход питания</strong></p></li></ul></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p><strong>Пользовательская кнопка</strong></p></li></ul></div></div></td><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p><strong> *Кнопка Сброса</strong></p></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p><strong>Индикатор питания</strong></p></li></ul></div></div></td><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p><strong>Пользовательские светодиоды</strong></p></li></ul></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p><strong>Индикаторы последовательного порта Rx/Tx</strong> </p></li></ul></div></div></td><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p><strong>8 MHz кварцевый резонатор</strong></p></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p><strong>32.768 KHz кварцевый резонатор</strong></p></li></ul></div></div></td><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p><a href="http://www.waveshare.com/xnucleo-F411RE.htm" class="bare">http://www.waveshare.com/xnucleo-F411RE.htm</a></p></div></div></td></tr></table>
<div class="imageblock" style=""><img src="Lection2Img/Figure11.png" alt="300" width="300"></div><div class="title">Рисунок 32. Отладочная плата</div></section>
<section><section id="_микроконтроллер_st32f411re"><h2>Микроконтроллер ST32F411RE</h2><div class="imageblock" id="Микроконтроллер" style=""><img src="Lection2Img/Figure7.png" alt="600" width="600"></div><div class="title">Рисунок 33. Функциональные блоки микроконтроллера STM32F411</div></section><section id="_ядро_cortexm4"><h2>Ядро CortexM4</h2><div class="imageblock" style=""><img src="Lection2Img/Figure12.png" alt="500" width="500"></div><div class="title">Рисунок 34. Ядро CortexM4</div>
<div class="ulist"><ul><li><p>Ядро Cortex построено по гарвардской архитектуре с разделением шины данных и кода. </p></li><li><p>Ядро Cortex-М4 поддерживает 8/16/32-разрядные операции умножения, которые выполняются за 1 цикл (деление со знаком (SDIV) или без (UDIV) занимает от 2 до 12 тактов в зависимости от размера операндов</p></li><li><p>Ядро Cortex-М4 поддерживает 8/16/32-разрядные операции умножения со сложением</p></li></ul></div></section><section id="_характеристики_ядра_cortexm4"><h2>Характеристики ядра CortexM4</h2><table class="tableblock frame-all grid-all" style="width:100%"><colgroup><col style="width:25%"><col style="width:25%"><col style="width:25%"><col style="width:25%"></colgroup><tbody><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Параметр</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">ARM7TDMI</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">ARM Cortex-M3</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">ARM Cortex-M4</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Архитектура</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">ARMv4T (Фон Неймана)</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">ARMv7 (Гарвардская)</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">ARMv7 (Гарвардская)</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Набор инструкций</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Thumb/ARM</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Thumb/Thumb-2</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Thumb/Thumb-2, DSP, SIMD, FP</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Конвейер</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">3 уровня</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">3 уровня + предсказание ветвлений</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">3 уровня + предсказание ветвлений</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Прерывания</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">FIQ/IRQ</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">NMI (немаскируемые) + от 1 до 240 физических источников прерываний</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">NMI (немаскируемые) + от 1 до 240 физических источников прерываний</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Длительность входа в обработчик прерывания</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">24-42 цикла</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">12 циклов</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">12 циклов</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Длительность переключения между обработчиками прерываний</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">24 цикла</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">6 циклов</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">6 циклов</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Режимы пониженного энергопотребления</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Нет</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Встроены</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Встроены</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Защита памяти</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Нет</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Блок защиты памяти с 8 областями</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">Блок защиты памяти с 8 областями</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Производительность по тесту Dhrystone</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">0,95 DMIPS/МГц</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">1,25 DMIPS/МГц</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">1,25 DMIPS/МГц</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Энергопотребление ядра</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">0,28 мВт/МГц</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">0,19 мВт/МГц</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">0,19 мВт/МГц</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">Аппаратный модуль работы с плавающей точкой </p></td><td class="tableblock halign-left valign-top"><p class="tableblock">нет</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">нет</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">есть</p></td></tr></table></section><section id="_характеристики_микроконтроллера"><h2>Характеристики микроконтроллера</h2><aside class="notes"><div class="paragraph"><p>Микроконтроллер имеет следующие характеристики:</p></div></aside>
<table class="tableblock frame-all grid-all" style="width:100%"><colgroup><col style="width:50%"><col style="width:50%"></colgroup><tbody><tr><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p>32 разрядное ядро ARM Cortex-M4</p></li></ul></div></div></td><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p>Блок работы с числами с плавающей точкой FPU</p></li></ul></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p>512 кБайт памяти программ</p></li></ul></div></div></td><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p>128 кБайт ОЗУ</p></li></ul></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p>Встроенный 12 битный 16 канальный АЦП</p></li></ul></div></div></td><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p>DMA контроллер на 16 каналов</p></li></ul></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p>USB 2.0</p></li></ul></div></div></td><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p>3x USART</p></li></ul></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p>5 x SPI/I2S</p></li></ul></div></div></td><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p>3x I2C</p></li></ul></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p>SDIO интерфейс для карт SD/MMC/eMMC</p></li></ul></div></div></td><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p>Аппаратный подсчет контрольной суммы памяти программ CRC</p></li></ul></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p>6 - 16 разрядных и 2 - 32 разрядных Таймера</p></li></ul></div></div></td><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p>1 - 16 битный для управления двигателями</p></li></ul></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p>2 сторожевых таймера</p></li></ul></div></div></td><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p>1 системный таймер</p></li></ul></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p>Работа на частотах до 100Мгц</p></li></ul></div></div></td><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p>81 портов ввода вывода</p></li></ul></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p>Питание от 1.7 до 3.6 Вольт</p></li></ul></div></div></td><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p>Потребление 100 мкА/Мгц</p></li></ul></div></div></td></tr></table></section><section id="_блок_диаграмма_микроконтроллера"><h2>Блок диаграмма микроконтроллера</h2><aside class="notes"><div class="paragraph"><p>Блок схема микроконтроллера схематично изображена на рисунке <a href="#_блок_диаграмма_микроконтроллера">[_блок_диаграмма_микроконтроллера]</a>.</p></div></aside>
<div class="imageblock" style=""><img src="Lection2Img/Figure8.png" alt="500" width="500"></div><div class="title">Рисунок 35. Блок диаграмма микроконтроллера</div></section><section id="_дополнительные_особенности_микроконтроллера"><h2>Дополнительные особенности микроконтроллера</h2><aside class="notes"><div class="paragraph"><p>Из дополнительных особенностей, которые понадобятся для лабораторных работ следует выделить:</p></div></aside>
<div class="ulist"><ul><li><p>Настраиваемые источники тактовой частоты</p></li><li><p>Настраиваемые на различные функции порты</p></li><li><p>Внутренний температурный сенсор</p></li><li><p>Таймеры с настраиваемым модулем ШИМ</p></li><li><p>DMA для работы с модулями (SPI, UART, ADC… )</p></li><li><p>12 разрядный ADC последовательного приближения</p></li><li><p>Часы реального времени</p></li><li><p>Системный таймер и спец. прерывания для облегчения и ускорения работы ОСРВ</p></li></ul></div></section></section>
<section id="_система_тактирования"><h2>Система тактирования</h2></section>
<section><section id="_блок_диаграмма_системы_тактирования"><h2>Блок диаграмма системы тактирования</h2><table class="tableblock frame-all grid-all" style="width:100%"><colgroup><col style="width:50%"><col style="width:50%"></colgroup><tbody><tr><td class="tableblock halign-left valign-top"><div><div class="imageblock" style=""><img src="Lection2Img/Figure13.png" alt="700" width="700"></div><div class="title">Рисунок 36. Система тактирования микроконтроллера STM32F411</div></div></td><td class="tableblock halign-left valign-top"><div><div class="ulist"><ul><li><p>Для формирования системной тактовой частоты SYSCLK могут использоваться 4 основных источника:</p><div class="ulist"><ul><li><p>HSI (high-speed internal) — внутренний высокочастотный RC-генератор.</p></li><li><p>HSE (high-speed external) — внешний высокочастотный генератор.</p></li><li><p>PLL — система ФАПЧ. Точнее сказать, это вовсе и не генератор, а набор из умножителей и делителей,
исходный сигнал он получает от HSI или HSE, а на выходе у него уже другая частота.</p></li></ul></div></li><li><p>Также имеются 2 вторичных источника тактового сигнала:</p><div class="ulist"><ul><li><p>LSI (low-speed internal) — низкочастотный внутренний RC-генератор на 37 кГц</p></li><li><p>LSE (low-speed external) — низкочастотный внешний источник на 32,768 кГц</p></li></ul></div></li></ul></div></div></td></tr></table></section><section id="_модуль_тактирования"><h2>Модуль тактирования.</h2><div class="paragraph"><p>Модуль тактирования (Reset and Clock Control) RCC</p></div>
<div class="ulist"><ul><li><p>Для формирования системной тактовой частоты SYSCLK могут использоваться 4 основных источника:</p><div class="ulist"><ul><li><p>HSI (high-speed internal) — внутренний высокочастотный RC-генератор.</p></li><li><p>HSE (high-speed external) — внешний высокочастотный генератор.</p></li><li><p>PLL — система ФАПЧ. Точнее сказать, это вовсе и не генератор, а набор из умножителей и делителей,
исходный сигнал он получает от HSI или HSE, а на выходе у него уже другая частота.</p></li></ul></div></li><li><p>Также имеются 2 вторичных источника тактового сигнала:</p><div class="ulist"><ul><li><p>LSI (low-speed internal) — низкочастотный внутренний RC-генератор на 37 кГц</p></li><li><p>LSE (low-speed external) — низкочастотный внешний источник на 32,768 кГц</p></li></ul></div></li></ul></div></section><section id="_фазовая_подстройка_частоты_pll"><h2>Фазовая подстройка частоты PLL</h2><div class="ulist"><ul><li><p>PLL Внутренний источник PLL тактируется от внешнего или внутреннего высокочастотных генераторов (HSE либо HSI). </p><div class="ulist"><ul><li><p>С помощью регистров PLLM, PLLN,PLLP можно подобрать любую частоту до 100 Мгц включительно по формуле:</p></li></ul></div></li></ul></div>
<div class="exampleblock"><div class="content"><div class="literalblock"><div class="content"><pre>f = f(PLL clock input) × (PLLN / PLLM) /PLLP</pre></div></div></div></div>
<div class="ulist"><ul><li><p>Кроме системной тактовой частоты SYSCLK, PLL также выдает частоту 48 МГц для интерфейса USB.
При использовании USB входная частота для PLL должна быть в диапазоне от 2 МГц до 24 МГц.</p></li></ul></div>
<div class="exampleblock"><div class="content"><div class="literalblock"><div class="content"><pre>f(USB) = f(PLL clock input) × (PLLN / PLLM) / PLLQ</pre></div></div></div></div></section><section id="_дополнительные_генераторы_тактовой_частоты"><h2>Дополнительные генераторы тактовой частоты</h2><div class="ulist"><ul><li><p>LSE. Низкочастотный внешний генератор частоты.</p><div class="ulist"><ul><li><p>Применение внешнего кварцевого/керамического резонатора на 32,768 кГц на входах OSC32_IN, OSC32_OUT.
Высокостабильный источник, формирует тактовые сигналы для часов реального времени RTC, модуля ЖКИ,
а также для таймеров TIM9/TIM10/TIM11.</p></li><li><p>Использование внешнего источника тактовой частоты (режим LSE bypass). Формируются тактовые
сигналы для часов реального времени и ЖКИ. В этом режиме исходный сигнал поступает с генератора HSE.
Входная частота может быть до 1 МГц, затем сигнал проходит через делитель с коэффициентом деления 2,
4, 8 или 16. Входной сигнал может быть прямоугольной, треугольной формы или синусоидой с 50% скважностью.</p></li></ul></div></li><li><p>LSI. Внутренний RC-генератор частотой около 37 кГц.</p><div class="ulist"><ul><li><p>Как и LSE, позволяет тактировать часы реального времени и модуль ЖКИ. Кроме этого, поддерживает
работоспособность независимого сторожевого таймера IWDG в режимах Stop и Standby.</p></li></ul></div></li></ul></div></section><section id="_регистр_управления_частотой"><h2>Регистр управления частотой.</h2><aside class="notes"><div class="paragraph"><p>Clock Control register (CR)
Как уже упоминалось, системная тактовая частота для серии "STM32F411" может быть до 100 МГц. Для ее
формирования используются 3 основных источника — HSI, HSE, PLL. Включение и выключение основных
генераторов производится через регистр RCC_CR — Clock Control register.</p></div>
<div class="paragraph"><p>Значение по умолчанию: 0x0000 XX81:</p></div></aside>
<div class="imageblock" style=""><img src="Lection2Img/Figure14.png" alt="800" width="700"></div>
<div class="hdlist"><table><tr><td class="hdlist1">Bit 24 PLLON</td><td class="hdlist2"><p>Включить PLL. Этот бит устанавливается и скидывается программно, чтобы включить PLL.
Бит не может быть скинут, если PLL уже используется как системная частота. </p><div class="ulist"><ul><li><p><strong>0</strong>: ОТКЛЮЧИТЬ PLL <strong>1</strong>: ВКЛЮЧИТЬ PLL</p></li></ul></div></td></tr></table></div>
<div class="hdlist"><table><tr><td class="hdlist1">Bit 16: HSEON</td><td class="hdlist2"><p>Включить HSE. Этот бит устанавливается и скидывается программно. Бит не может быть
скинут, если HSE уже используется как системная частота. </p><div class="ulist"><ul><li><p><strong>0</strong>: ОТЛЮЧИТЬ HSE <strong>1</strong>: ВКЛЮЧИТЬ HSE </p></li></ul></div></td></tr></table></div>
<div class="hdlist"><table><tr><td class="hdlist1">Bit 0: HSION</td><td class="hdlist2"><p>Включить HSI. Этот бит устанавливается и скидывается программно. Очищается аппаратно
при входе в режим Stop или Standby. Бит не может быть скинут, если HSI уже используется как системная частота. </p><div class="ulist"><ul><li><p><strong>0</strong>: ВЫКЛЮЧИТЬ HSI <strong>1</strong>: ВКЛЮЧИТЬ HSI </p></li></ul></div></td></tr></table></div></section><section id="_регистр_управления_частотой_контроль"><h2>Регистр управления частотой. Контроль</h2><aside class="notes"><div class="paragraph"><p>Сразу после установки частоты, нужно проверить, что частота с нового источника стабилизировалась. Для
этого используются те же поля того же регистра CR, оканчивающиеся на RDY (Ready)</p></div></aside>
<div class="imageblock" style=""><img src="Lection2Img/Figure14.png" alt="600" width="600"></div>
<div class="hdlist"><table><tr><td class="hdlist1">Bit 25 PLLRDY</td><td class="hdlist2"><p>Флаг готовности частоты PLL. Этот бит устанавливается аппаратно </p><div class="ulist"><ul><li><p><strong>0</strong>: PLL НЕ ЗАПУЩЕН И НЕ ИСПОЛЬЗУЕТСЯ <strong>1</strong>: PLL ИСПОЛЬЗУЕТСЯ</p></li></ul></div></td></tr></table></div>
<div class="hdlist"><table><tr><td class="hdlist1">Bit 17: HSERDY</td><td class="hdlist2"><p>Флаг готовности частоты HSE. Этот бит устанавливается аппаратно. </p><div class="ulist"><ul><li><p><strong>0</strong>: HSE НЕ ГОТОВ <strong>1</strong>: HSE ГОТОВ</p></li></ul></div></td></tr></table></div>
<div class="hdlist"><table><tr><td class="hdlist1">Bit 1: HSIRDY</td><td class="hdlist2"><p>Флаг готовности частоты HSI. Этот бит устанавливается аппаратно</p><div class="ulist"><ul><li><p><strong>0</strong>: HSI НЕ ГОТОВ <strong>1</strong>: HSI ГОТОВ </p></li></ul></div></td></tr></table></div></section><section id="_регистр_конфигурации_частоты_выбор_источника"><h2>Регистр конфигурации частоты. Выбор источника</h2><aside class="notes"><div class="paragraph"><p>После включения генераторов частоты, необходимо выбрать один из них в качестве источника для системной
частоты SYSCLK. Выбор осуществляется через регистр RCC_CFGR — Clock Configuration Register.
Значение по умолчанию: 0x0000 0000</p></div></aside>
<div class="imageblock" style=""><img src="Lection2Img/Figure14.png" alt="600" width="600"></div>
<div class="hdlist"><table><tr><td class="hdlist1">Bits 3:2 SWS[1:0]</td><td class="hdlist2"><p>Статус частоты SYSCLK. </p><div class="ulist"><ul><li><p><strong>00</strong>: ИСТОЧНИК ЧАСТОТЫ HSI <strong>01</strong>: ИСТОЧНИК ЧАСТОТЫ HSE</p></li><li><p><strong>10</strong>: ИСТОЧНИК ЧАСТОТЫ PLL <strong>11</strong>: РЕЗЕРВ</p></li></ul></div></td></tr></table></div>
<div class="hdlist"><table><tr><td class="hdlist1">Bits 1:0 SW[1:0]</td><td class="hdlist2"><p>Выбор источника частоты SYSCLK. </p><div class="ulist"><ul><li><p><strong>00</strong>: HSI <strong>01</strong>: HSE</p></li><li><p><strong>10</strong>: PLL <strong>11</strong>: НЕ ИСПОЛЬЗУЕТСЯ</p></li></ul></div></td></tr></table></div></section><section id="_регистр_конфигурации_частоты_делители"><h2>Регистр конфигурации частоты. Делители</h2><div class="paragraph"><p>Следующие секции регистра HPRE (AHB prescaler), PPRE1 (APB1 prescaler), PPRE2 (APB2 prescaler) —
задают коэффициенты деления системной частоты SYSCLK, которая после предделителей поступает на
матрицы шин. </p></div>
<aside class="notes"><div class="hdlist"><table><tr><td class="hdlist1">AHB (Advanced High Speed Busses)</td><td class="hdlist2"><p>матрица высокоскоростных шин. Она "доставляет" сигналы
тактирования к ядру микроконтроллера, памяти (это как FLASH, так EEPROM и RAM) и модулю DMA
Direct Memory Access — модуль прямого доступа к памяти), системному таймеру. Также, в семействе
STM32F4 на эту шину "посажены" и все порты ввода/вывода GPIO .</p></td></tr></table></div>
<div class="hdlist"><table><tr><td class="hdlist1">APB1, APB2 (Advanced Peripheral Bussess)</td><td class="hdlist2"><p>матрицы шин периферии. Соотвественно, к остальным
периферийным модулям тактовая частота распределяется уже через эти шины. </p></td></tr></table></div></aside>
<div class="imageblock" style=""><img src="Lection2Img/Figure14.png" alt="600" width="600"></div>
<div class="hdlist"><table><tr><td class="hdlist1">Bits 13:11 PPRE2[2:0]</td><td class="hdlist2"><p>Делитель частоты шины APB2. Это устанавливается и очищается программно. </p><div class="ulist"><ul><li><p><strong>0xx</strong>: AHB <strong>100</strong>: AHB/2 <strong>101</strong>: AHB/4 <strong>110</strong>: AHB/8 <strong>111</strong>: AHB/16 </p></li></ul></div></td></tr></table></div>
<div class="hdlist"><table><tr><td class="hdlist1">Bits 10:8 PPRE1[2:0]</td><td class="hdlist2"><p>Делитель частоты шины APB1 Это устанавливается и очищается программно. </p><div class="ulist"><ul><li><p><strong>0xx</strong>: AHB <strong>100</strong>: AHB/2 <strong>101</strong>: AHB/4 <strong>110</strong>: AHB/8 <strong>111</strong>: AHB/16</p></li></ul></div></td></tr></table></div>
<div class="hdlist"><table><tr><td class="hdlist1">Bits 7:4 HPRE[3:0]</td><td class="hdlist2"><p>Делитель частоты шины AHB. </p><div class="ulist"><ul><li><p><strong>0xxx</strong>: SYSCLK <strong>1000</strong>: SYSCLK/2 <strong>1001</strong>: SYSCLK/4 <strong>1010</strong>: SYSCLK/8 <strong>1011</strong>: SYSCLK/16
<strong>1100</strong>: SYSCLK/64 <strong>1101</strong>: SYSCLK/128 <strong>1110</strong>: SYSCLK/256 <strong>1111</strong>: SYSCLK/512</p></li></ul></div></td></tr></table></div></section><section id="_алгоритм_настройки_частоты"><h2>Алгоритм настройки частоты</h2><div class="ulist"><ul><li><p>Определить какие источники частоты нужны</p><div class="ulist"><ul><li><p>Например, PLL нужен для USB</p></li></ul></div></li><li><p>Включить нужный источник</p><div class="ulist"><ul><li><p>Используя Clock Control register (RCC::CR)</p></li></ul></div></li><li><p>Дождаться стабилизации источника </p><div class="ulist"><ul><li><p>Используя соответствующие биты (..RDY) Clock Control register (RCC::CR)</p></li></ul></div></li><li><p>Назначить нужный источник на системную частоту</p><div class="ulist"><ul><li><p>Используя Clock Configuration Register (RCC::CFGR)</p></li></ul></div></li><li><p>Дождаться пока источник не переключиться на системную частоту</p><div class="ulist"><ul><li><p>Используя Clock Configuration Register (RCC::CFGR)</p></li></ul></div></li></ul></div></section></section>
<section id="_контрольные_вопросы"><h2>Контрольные вопросы</h2><div class="olist arabic"><ol class="arabic"><li><p>Что такое POD типы данных?</p></li><li><p>Назовите все виды типов в языке С++</p></li><li><p>Что такое пользовательский тип?</p></li><li><p>Назовите модификаторы типов.</p></li><li><p>Назовите правило установки размеров типов</p></li><li><p>Что делает оператор sizeof()?</p></li><li><p>Что характеризует тип std::size_t</p></li><li><p>Назовите фиксированные типы целых в библиотеке std</p></li><li><p>Что такое псевдоним типа?</p></li><li><p>Что такое явное и неявное преобразование типа?</p></li><li><p>Какие явные преобразования типов вы знаете?</p></li><li><p>Что делает reinterpret_cast?</p></li><li><p>Чем static_cast отличается от reinterpret_cast?</p></li><li><p>Что такое ОЗУ и ПЗУ?</p></li><li><p>Каков размер памяти ARM Cortex микроконтроллеров.</p></li><li><p>По какой архитектуре разработан ARM Cortex микроконтроллер?</p></li><li><p>В чем отличие Гарвардской архитектуры от Архитектура ФонНеймана?</p></li><li><p>Где располагаются локальные переменные?</p></li><li><p>Где располагаются статические переменные?</p></li><li><p>Где располагаются глобальные переменные?</p></li><li><p>Что такое стек?</p></li><li><p>Что такое указатель?</p></li><li><p>Что такое разыменовывание указателя?</p></li><li><p>Что означает взятие адреса?</p></li><li><p>Какие операции можно выполнять над указателями?</p></li><li><p>Что такое константный указатель?</p></li><li><p>Что такое указатель на константу?</p></li><li><p>Что такое ссылка? В чем её отличие от указателя?</p></li><li><p>Что такое регистр?</p></li><li><p>Что такое регистры общего назначения?</p></li><li><p>Что такое регистры специального назначения?</p></li><li><p>Как можно установить бит в регистре специального назначения?</p></li><li><p>Объясните как вызывается функция.</p></li><li><p>Что такое трансляция?</p></li><li><p>Что такое компоновка?</p></li><li><p>Как лучше организовывать структуру проекта и почему?</p></li><li><p>Что такое операторы?</p></li><li><p>Какие арифметические операторы вы знаете?</p></li><li><p>Какие логические операторы вы знаете?</p></li><li><p>Какие побитовые операторы вы знаете?</p></li><li><p>Приведите пример переопределения оператора</p></li><li><p>Какие еще операторы вы знаете?</p></li><li><p>Как сбросить бит с помощью битовых операторов?</p></li><li><p>Как установить бит с помощью битовых операторов?</p></li><li><p>Как поменять значение бита с помощью битовых операторов?</p></li><li><p>Какой микроконтроллер на отладочной плате XNUCLE ST32F411?</p></li><li><p>Какие блоки входят в состав микроконтроллера STM32F411?</p></li><li><p>В чем отличие ядра CortexM4 от CortexM3?</p></li><li><p>Назовите основные характеристики микроконтроллера STM32F411.</p></li><li><p>Назовите дополнительные характеристики микроконтроллера STM32F411.</p></li><li><p>Какие источники тактирования есть у микроконтроллера STM32F411</p></li><li><p>Назовите алгоритм подключения системной частоты к источнику тактирования микроконтроллера STM32F411.</p></li><li><p>Что такое ФАПЧ?</p></li><li><p>Что делает следующий код?</p></li></ol></div>
<pre class="highlight listingblock"><code data-noescape class="cpp language-cpp">int main()
{
int StudentUdacha = 10;
int PrepodUdachca = 0 ;
StudentUdacha = StudentUdacha ^ PrepodUdachca ;
PrepodUdachca = StudentUdacha ^ PrepodUdachca ;
StudentUdacha ^= PrepodUdachca ;
}</code></pre></section>
<section><section id="_порты_общего_назначения"><h2>Порты общего назначения</h2></section><section id="_основные_характеристики"><h2>Основные характеристики</h2><div class="ulist"><ul><li><p>5 портов общего назначения</p></li><li><p>16 линий ввода вывода</p></li><li><p>Режимы входа:</p><div class="ulist"><ul><li><p>цифровой с подтяжкой к 1 и к 0</p></li><li><p>аналоговый</p></li></ul></div></li><li><p>Возможность работы в альтернативном режиме</p></li></ul></div></section><section id="_различные_режимы_работы_портов"><h2>Различные режимы работы портов</h2><div class="ulist"><ul><li><p>Плавющий цифровой вход (Input floating)</p></li><li><p>Цифровой вход с подтяжкой к 1 (Input pull-up)</p></li><li><p>Цифровой вход с подтяжкой к 0 (Input-pull-down)</p></li><li><p>Аналоговый (Analog)</p></li><li><p>Цифровой выход с открытым коллектором с подтяжкой к 1 или к 0 (Output open-drain with pull-up or pull-down capability)</p></li><li><p>Цифровой двухтактный выход с подтяжкой к 1 или к 0 (Output push-pull with pull-up or pull-down capability) </p></li><li><p>Альтернативная функция с открытым коллектором с подтяжкой к 1 или к 0 (Alternate function push-pull with pull-up or pull-down capability)</p></li><li><p>Альтернативная функция двухтактный выход с подтяжкой к 1 или к 0 ()Alternate function open-drain with pull-up or pull-down capability)</p></li></ul></div></section><section id="_цифровой_режим"><h2>Цифровой режим</h2><aside class="notes"><div class="paragraph"><p>Когда мы говорим, что порт работает в цифровом режиме, то обычно подразумеваем, что
порт имеет два состояния 1(<strong>true</strong>) и 0(<strong>false</strong>) или говоря на языке электроники <strong>Hihg</strong> и <strong>Low</strong>.
Эти сигналы соотвествуют уровню питания микроконтроллера, для нашего микроконтроллера
обычно <strong>High</strong> соотвествует 3-3.3В, a <strong>Low</strong> - 0 В.</p></div></aside>
<div class="imageblock" style=""><img src="Lection2Img/Figure16.png" alt="500" width="500"></div><div class="title">Рисунок 37. Цифровой режим</div></section><section id="_работа_в_цифровом_режиме"><h2>Работа в цифровом режиме</h2><div class="paragraph"><p>С помощью портов можно управлять работой других устройств.</p></div>
<aside class="notes"><div class="paragraph"><p>Нарример, можно управлять режимом работы светодида. На рисунке <a href="#Работа в цифровой режиме">[Работа в цифровой режиме]</a>
показан источник питания, резистор, который ограничивает ток и определяет яркость светодиода.
Свтодиод подключен к питанию, а микроконтроллер подключают вторую часть к земеле в итоге ток
течет от + к - и свтодио горит.</p></div></aside>
<div class="imageblock" style=""><img src="Lection2Img/Figure17.png" alt="400" width="400"></div><div class="title">Рисунок 38. Работа в цифровой режиме</div></section><section id="_цифровой_выход"><h2>Цифровой выход</h2><aside class="notes"><div class="paragraph"><p>Когда порт настроен как цифровой выход им можно управлять. Например, если вы задали
состояние порта High, то порт подключается к питнию, в итоге на ножке порта появляется
высокий уровень напряжения. В случае, если вы задали Low, на ножке порта появляется низкий
уровень напряжения или 0.</p></div></aside>
<div class="imageblock" style=""><img src="Lection2Img/Figure18.png" alt="700" width="700"></div><div class="title">Рисунок 39. Цифровой выход</div></section><section id="_цифровой_вход"><h2>Цифровой вход</h2><aside class="notes"><div class="paragraph"><p>Когда порт настроен как цифровой вход его сопротивление бесконечно, контакт никуда
не подключен ни к земле ни к питанию, поэтому ток никуда не течет. Любое напряжение
на такой ножке будет интерпретирована как 1 или 0, в зависимости от уровня напряжения
высокого или низкого. В таком случае это называется "подвешенная" или плавающая ножка и наводка или
шум на этой ножке может быть интерпретирован как 1 или 0 в зависимости от уровня
шума. Таким образом такая плавающая "ножка" не очень хорошо, так как могут генерироваться
ложные переходы</p></div></aside>
<div class="imageblock" style=""><img src="Lection2Img/Figure19.png" alt="400" width="400"></div><div class="title">Рисунок 40. Цифровой вход</div></section><section id="_цифровой_вход_с_подтяжкой"><h2>Цифровой вход с подтяжкой</h2><aside class="notes"><div class="paragraph"><p>Плавающий сигнал на подвешенной ножке может быть причиной следующих проблем</p></div>
<div class="ulist"><ul><li><p>Разное значение при считывании (1 или 0) в разные моменты времени</p></li><li><p>Ложные переходы (если настроено прерывание, то вы псотоянно будет входить в обработчик)</p></li><li><p>Повышенно потребление из-за того, что схема входного буфера для ножки потребляет ток
когда сигнал на ножке не полностью High или Low</p></li></ul></div></aside>
<div class="paragraph"><p>Чтобы избавиться от плавющего сигнала на ножке обычно её подтягивают к 0 или 1.
Обычно эта опция уже есть внутри микроконтроллера и может быть настроена</p></div>
<div class="imageblock" style=""><img src="Lection2Img/Figure20.png" alt="400" width="400"></div><div class="title">Рисунок 41. Цифровой вход с подтяжкой</div></section><section id="_цифровой_вход_с_подтяжкой_к_1"><h2>Цифровой вход с подтяжкой к 1</h2><div class="paragraph"><p>Вход с подтяжкой к 1.</p></div>
<div class="imageblock" style=""><img src="Lection2Img/Figure21.png" alt="500" width="500"></div><div class="title">Рисунок 42. Цифровой вход с подтяжкой к 1</div></section><section id="_регистры_портов_общего_назначения"><h2>Регистры портов общего назначения</h2><div class="ulist"><ul><li><p><strong>GPIOx_MODER (port mode register)</strong>. Задает режимы работы индивидуально каждого из вывода порта. </p><div class="ulist"><ul><li><p>Каждый из выводов GPIO может быть настроен как вход, выход, работать в аналоговом режиме, или подключен к одной из альтернативных функций. </p></li></ul></div></li><li><p><strong>GPIOx_OSPEEDR (port output speed register)</strong>. Задает скорость работы порта: </p><div class="ulist"><ul><li><p>400кГц, 2МГц, 10МГц и 40Мгц.</p></li></ul></div></li><li><p><strong>GPIOx_PUPDR (port pull-up/pull-down register)</strong>. Задает подключение подтягивающих резисторов</p><div class="ulist"><ul><li><p>Без подтягивающего резистора, с подтяжкой к «+» питания, с подтяжкой к «gnd» земле. </p></li></ul></div></li><li><p><strong>GPIOx_IDR (input data register)</strong>. регистр входных данных, из которого считывается состояние входов порта.</p></li><li><p><strong>GPIOx_ODR (output data register)</strong>. регистр выходных данных. Запись числа в младшие 16 бит, приводит к появлению соответствующих уровней на выводах порта.</p></li><li><p><strong>GPIOx_OTYPER (port output type register)</strong>. В режиме выхода или альтернативной функции, соответствующий бит регистра устанавливает тип выхода. </p><div class="ulist"><ul><li><p>Push-Pull (двухтактный) или Open Drain (выход с открытым коллектором).</p></li></ul></div></li></ul></div></section><section id="_регистры_портов_общего_назначения_2"><h2>Регистры портов общего назначения</h2><div class="ulist"><ul><li><p><strong>GPIOx_BSRR (port bit set/reset register)</strong>. Это регистр побитовой установки/сброса
данных на выходных линиях порта.</p></li></ul></div>
<aside class="notes"><div class="paragraph"><p>Этот регистр дает возможность выполнения «атомарных»
операций побитового управления выходными линиями порта. При этом нет риска
возникновения прерывания между операциями чтения и модификации при записи числа
в выходной регистр <strong>GPIOx_ODR</strong>. Атомарные операции с регистром <strong>GPIOx_BSRR</strong>
выполняются за один цикл записи. При этом операции установки/сброса имеют
однократный эффект. Предыдущее состояние модифицируемого бита регистра <strong>GPIOx_BSRR</strong>
совершенно неважно, можно сколько угодно «пихать» туда единицы и каждый раз
регистр <strong>GPIOx_ODR</strong> будет реагировать соответствующим образом.</p></div></aside>
<div class="ulist"><ul><li><p>32 разряда этого регистра позволяют индивидуально установить или сбросить
каждый из 16 младших разрядов регистра <strong>GPIOx_ODR</strong>.</p></li><li><p>Младшие 16 разрядов регистра <strong>GPIOx_BSRR</strong> отвечают за установку соответствующего
бита регистра <strong>GPIOx_ODR</strong> в «1», старшие 16 разрядов сбрасывают этот бит.
Установка/сброс осуществляются записью «1» в соответствующий разряд. Запись «0»
никак не воздействует на состояние соответствующего бита выходного регистра данных.
При одновременной записи двух единиц в биты установки и сброса, приоритет имеет
операция установки бита.</p><div class="ulist"><ul><li><p><strong>GPIOxLCKR (port configuration lock register)</strong>. Позволяет «заморозить», то есть
защитить от изменения текущую настройку конфигурации. Можно запретить
модификацию следующих регистров управления: <strong>GPIOx_MODER</strong>, <strong>GPIOx_OTYPER</strong>,
<strong>GPIOx_OSPEEDR</strong>, <strong>GPIOx_PUPDR</strong>, <strong>GPIOx_AFRL</strong>, <strong>GPIOx_AFRH</strong>.</p></li></ul></div></li></ul></div></section><section id="_работа_с_портами_в_режиме_общего_назначения"><h2>Работа с портами в режиме общего назначения</h2><div class="ulist"><ul><li><p>Определить какой порт нужно использовать</p></li><li><p>Подключить нужный порт к источнику частоты </p><div class="ulist"><ul><li><p>Через регистр <strong>RCC→AHB1ENR</strong></p></li></ul></div></li><li><p>Определить нужна ли какая-то специфическая скорость для конктретного порта и если да, настроить её</p><div class="ulist"><ul><li><p>Через регистр <strong>GPIOx_OSPEEDR</strong></p></li></ul></div></li><li><p>Определить нужна ли подтяжка и какой ти выводов надо использовать</p><div class="ulist"><ul><li><p><strong>GPIOx_PUPDR</strong> и <strong>GPIOx_OTYPER</strong></p></li></ul></div></li><li><p>Определить какие выводы портов нужно использовать как выход, а какие как вход</p></li><li><p>Настроить нужные вывода порта на вход или выход</p><div class="ulist"><ul><li><p>Через регистр <strong>GPIOE→MODER</strong></p></li></ul></div></li></ul></div></section></section>
<section><section id="_задания_2"><h2>Задания</h2><div class="paragraph"><p>3 Задания, кто не успеет в лабораторной, завершить дома.</p></div></section><section id="_содержание_отчета"><h2>Содержание отчета</h2><div class="ulist"><ul><li><p>Описать процесс записи в регистр по его адресу</p></li><li><p>Описать полученный результат записи в регистры MODER и ODR</p></li><li><p>Описать процесс вызова функции в IAR</p></li><li><p>Описать регистры общего назначения для семейства Cortex-m4</p></li><li><p>Описать все виды источников тактирования параметры их настройки</p></li><li><p>Описать процесс получения заданной по варианту частоты тактирования</p></li><li><p>Описать ошибки, сделанные при выполнении работы</p></li><li><p>Ответить на контрольные вопросы</p></li><li><p>Сделать выводы</p></li></ul></div></section><section id="_задание_1"><h2>Задание 1</h2><div class="olist arabic"><ol class="arabic"><li><p>Создать проект в соответствии с Заданием 1 Лекции 1</p></li><li><p>Написать программу в main.cpp</p></li></ol></div>
<pre class="highlight listingblock"><code data-noescape class="cpp language-cpp">#include "rccregisters.hpp" //for RCC
int main() {
RCC::AHB1ENR::GPIOCEN::Enable::Set() ;
for(;;) {
//код лабораторной здесь.
}
return 0 ;
}</code></pre>
<div class="olist arabic"><ol class="arabic"><li><p>Открыть спецификацию на микроконтроллер <a href="https://www.st.com/resource/en/reference_manual/dm00119316.pdf#STM32F411">STM32F411</a> на странице</p><div class="ulist"><ul><li><p>На странице 38 узнать на каком адресе расположен модуль <strong>GPIOC</strong></p></li><li><p>На странице 157, узнать смещение регистра <strong>GPIOС_MODER</strong> относительно адреса <strong>GPIOC</strong> и вычислить адрес регистра <strong>GPIOC_MODER</strong></p></li></ul></div></li><li><p>Записать по адресу регистра <strong>GPIOC_MODER</strong> биты 10,16,18 в 1, а биты 11,17,19 в 0.</p></li><li><p>Открыть спецификацию на микроконтроллер <a href="https://www.st.com/resource/en/reference_manual/dm00119316.pdf#STM32F411">STM32F411</a>.</p><div class="ulist"><ul><li><p>На странице 159, узнать смещение регистра <strong>GPIOС_ODR</strong> относительно адреса <strong>GPIOC</strong> и вычислить адрес регистра <strong>GPIOC_ODR</strong></p></li></ul></div></li><li><p>Записать по адресу регистра <strong>GPIOC_ODR</strong> биты 5,8,9 в 1</p></li><li><p>Написать функцию задержки используя цикл <strong>void Delay()</strong>. И вызвать ей после установки битов</p></li><li><p>После задержки Записать по адресу регистра <strong>GPIOC_ODR</strong> биты 5,8,9 в 0</p></li><li><p>Вызвать функцию сброса битов</p></li><li><p>Запустить программу, в пошаговой отладке в окне Register, посмотреть, что происходит с регистрами
<strong>GPIOС_MODER</strong> и <strong>GPIOС_ODR</strong>.</p></li><li><p>Посмотреть видео <a href="https://www.youtube.com/watch?v=hukr8ZqS5Ys" class="bare">https://www.youtube.com/watch?v=hukr8ZqS5Ys</a></p></li></ol></div></section><section id="_задание_2_2"><h2>Задание 2</h2><div class="olist arabic"><ol class="arabic"><li><p>Создать указатель типа <strong>volatile int</strong>*, которая будет содержать адрес регистра GPIOC_MODER</p></li><li><p>Создать переменную типа <strong>int</strong> и записать туда значение, которое содержится по этому адресу</p></li><li><p>Запустить отладку, запустить окно Memory и проверить, что по этому адресу лежит это значение</p></li><li><p>В отладке открыть окно регистры и проверить, что значение регистра GPIOC_MODER, совпадает со значением
в переменной типа <strong>int</strong></p></li><li><p>Проделать тоже самое с произвольным адресом в ОЗУ.</p></li><li><p>Посмотреть видео <a href="https://www.youtube.com/watch?v=M53lJlcFOZQ" class="bare">https://www.youtube.com/watch?v=M53lJlcFOZQ</a></p></li></ol></div></section><section id="_задание_3_2"><h2>Задание 3</h2><div class="olist arabic"><ol class="arabic"><li><p>Ознакомиться с техническим описанием регистров тактирования микропроцессора</p></li><li><p>Произвести настройку тактирования микропроцессора по варианту см. <a href="#Варианты для системы тактирования">[Варианты для системы тактирования]</a></p></li><li><p>Выполнить пошаговую отладку</p></li></ol></div>
<table class="tableblock frame-all grid-all" style="width:100%"><caption class="title">Таблица 7. Варианты для системы тактирования</caption><colgroup><col style="width:33.3333%"><col style="width:33.3333%"><col style="width:33.3334%"></colgroup><thead><tr><th class="tableblock halign-left valign-top">Номер варианта</th><th class="tableblock halign-left valign-top">Источник тактирования</th><th class="tableblock halign-left valign-top">Частота тактирования</th></tr><tbody><tr><td class="tableblock halign-left valign-top"><p class="tableblock">0</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">HSI</p></td><td class="tableblock halign-center valign-middle" rowspan="3"><p class="tableblock">1 Мгц</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">1</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">HSE</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">2</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">PLL</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">3</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">HSI</p></td><td class="tableblock halign-center valign-middle" rowspan="3"><p class="tableblock">2 Мгц</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">4</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">HSE</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">5</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">PLL</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">6</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">HSI</p></td><td class="tableblock halign-center valign-middle" rowspan="3"><p class="tableblock">4 Мгц</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">7</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">HSE</p></td></tr><tr><td class="tableblock halign-left valign-top"><p class="tableblock">8</p></td><td class="tableblock halign-left valign-top"><p class="tableblock">PLL</p></td></tr></table></section><section id="_задание_4"><h2>Задание 4</h2><div class="olist arabic"><ol class="arabic"><li><p>Сделать программу, которая при нажатии кнопки UserButton на отладочной плате
<a href="http://www.waveshare.com/xnucleo-F411RE.htm" class="bare">http://www.waveshare.com/xnucleo-F411RE.htm</a> меняет состояние всех 4 светодидов</p></li></ol></div></section></section>
<section id="_лекция_3"><h2>Лекция 3</h2></section>
<section><section id="_таймеры"><h2>Таймеры</h2><div class="paragraph"><p>Одна из основных задач таймеров в микроконтроллерах это отсчитывать точные интервалы времени. Но, помимо этого таймеры
могут использоваться для измерения частоты, периодов, генерации ШИМа и переменных сигналов различной формы.
Всего</p></div><div class="hdlist"><table><tr><td class="hdlist1">TIM9-TIM11</td><td class="hdlist2"><p>Самые простые 16 битные таймеры.</p></td></tr><tr><td class="hdlist1">TIM2-TIM5</td><td class="hdlist2"><p>Таймеры общего назначания. (TIM2 и TIM5 32 битные) (TIM3 и TIM4 16 битные)</p></td></tr><tr><td class="hdlist1">TIM1</td><td class="hdlist2"><p>Расширенный 16 битный таймер</p><div class="ulist"><ul><li><p>Также существуют системный таймер <strong>SysTick</strong> таймер и Watchdog таймер.</p></li></ul></div></td></tr></table></div><aside class="notes"><div class="paragraph"><p>В данном курсе мы рассмотрим только 32 битные таймеры общего назначения. Самостоятельно можно будет ознакомиться с TIM1.</p></div></aside></section><section id="_таймеры_tim2_и_tim5_основные_особенности"><h2>Таймеры TIM2 и TIM5, основные особенности</h2><div class="ulist"><ul><li><p>Таймеры 32 битные (то есть могут считать до 2^32), умеют работать:</p><div class="ulist"><ul><li><p>с инкрементальными энкодерами и датчиками Холла,</p></li><li><p>несколько таймеров можно синхронизировать между собой.</p></li></ul></div></li><li><p>Таймеры могут использоваться для:</p><div class="ulist"><ul><li><p>Захвата сигнала (Защелкивать значение, когда на выводе порта например 0 сменился на 1)</p></li><li><p>Сравнения (Считать до значения в регистре сравнения и установить/сбросить/переключить вывод порта)</p></li><li><p>Генерации ШИМ (Генерировать прямоугольный сигнал с различной скважностью на вывод порта)</p></li><li><p>Генерации одиночного импульса</p></li></ul></div></li></ul></div></section><section id="_таймеры_tim2_и_tim5"><h2>Таймеры TIM2 и TIM5</h2><div class="ulist"><ul><li><p>Таймеры могут генеририровать следующие события:</p><div class="ulist"><ul><li><p>Переполнение</p></li><li><p>Захват сигнала</p></li><li><p>Сравнение</p></li><li><p>Событие-триггер</p></li></ul></div></li></ul></div></section><section id="_таймеры_tim2_и_tim5_начальная_запуск"><h2>Таймеры TIM2 и TIM5 начальная запуск</h2><div class="ulist"><ul><li><p>Таймеры тактируются от шины APB1.</p></li></ul></div>
<aside class="notes"><div class="paragraph"><p>Поэтому для каждый отчсет таймера по умолчанию происходит на частоте шины, т.е. если шина <strong>APB1</strong> работает на частоте 1 Мгц,
то один отсчет таймера произойдет через 1 мкс. Таким образом можно организовать измерение времени с разрешением в 1 мкс.
Чтобы таймер заработал, его нужно подключить к системе тактирования, т.е. к шине <strong>APB1</strong>.</p></div></aside>
<div class="ulist"><ul><li><p>Подключение к системе тактирование выполняется через регистр <strong>APB1ENR</strong> модуля <strong>RCC</strong>.</p></li><li><p>Входную частоту таймера можно поделить, записав делитель частоты в решистр <strong>PSC</strong>.</p></li><li><p>Включение таймера производиться с помощью бита <strong>CEN</strong> в регистре <strong>CR1</strong> модуля таймера (TIM2 или TIM5)</p></li></ul></div></section><section id="_таймеры_tim2_и_tim5_переполнение"><h2>Таймеры TIM2 и TIM5 переполнение</h2><aside class="notes"><div class="paragraph"><p>Как только таймер начал считать, его счетчик будет увеличиваться с каждым тактом подающейся на таймер частоты. Т.е. если
входная частота таймера 1 МГц, то через секунду таймер достчитает до 1 000 000.</p></div></aside>
<div class="ulist"><ul><li><p>Значение счетчика таймера можно прочитать из регистра <strong>CNT</strong>.</p><div class="ulist"><ul><li><p>Поскольку таймерs <strong>TIM2</strong> и <strong>TIM5</strong> 32 битных, то переполнение наступит когда в регистре <strong>CNT</strong> будет значение <strong>0xFFFFFFFF</strong>,
нетрудно посчитать, что при частоте работе таймера 1 МГц он переполнится через примерно 71.5 минуты.</p></li><li><p>При переполнении таймера, он сгенерирует событие (запрос на прерывание).</p></li></ul></div></li><li><p>Проверить случилось ли переполнение можно, считав бит <strong>UIF</strong> в регистре <strong>CR</strong>.</p></li></ul></div></section><section id="_таймеры_tim2_и_tim5_режим_счета_до_значения"><h2>Таймеры TIM2 и TIM5 режим счета до значения</h2><aside class="notes"><div class="paragraph"><p>Допустим, нам нужно раз в 71.5 минуты моргнуть светодиодом. Мы можем запустить таймер и и постоянно проверять значение
бита <strong>UIF</strong>, как только оно установится в 1, моргнуть светодиодом.</p></div></aside>
<div class="ulist"><ul><li><p>Используя переполнение невозможно задать таймером произвольный интервал времени.</p></li><li><p>Задать производльный интервал можно, используя регистр автоперезагрузки <strong>ARR</strong>. В этот регистр записывается число, до
которого будет идти счет. При достижении этого значения, содержимое счетчика <strong>CNT</strong> обнуляется и формируются прерывание
или запрос DMA (если они разрешены).</p></li></ul></div>
<div class="paragraph"><p><strong>Например:</strong> мы хотим раз в 1 секунду моргать светодиодом. Частота работы таймера 1 Мгц. Чтобы таймер генерировал запрос на
прерывание каждыые 1 секунду, нужно записать число 1 000 000 в регистр <strong>ARR</strong> и число 0 в регистр <strong>CNT</strong> и после этого
запустить таймер. Как только таймер досчитает до 1 000 000 он выставит флаг <strong>UIF</strong>.</p></div></section><section id="_таймеры_tim2_и_tim5_регистры_для_режима_счета"><h2>Таймеры TIM2 и TIM5 регистры для режима счета</h2><div class="dlist"><dl><dt class="hdlist1">TIMx::CNT</dt><dd><p>Cчетный 16/32 разрядный регистр таймера суммирующий, с приходом каждого тактового импульса инкрементирует свое содержимое.
На вычитание работать не может. </p></dd><dt class="hdlist1">TIMx::PSC</dt><dd><p>16 разрядный регистр - делитель частоты для таймера. Коэффициент деления задается в 16-разрядном регистре, этот
коэффициент можно задать в пределах от 1 до 65536.</p></dd><dt class="hdlist1">TIMx::ARR</dt><dd><p>
16/32 разрядный регистр автоперезагрузки. В этот регистр записывается число, до которого будет идти счет. При достижении
этого значения, содержимое счетчика TIMx_CNT обнуляется и формируются прерывание или запрос DMA (если они разрешены).</p></dd><dt class="hdlist1">TIMx::SR</dt><dd><p>
Регистр статуса. Можно узнать о всех возможных запросах на прерывания от таймера</p></dd></dl></div></section><section id="_таймеры_tim2_и_tim5_управляющий_регистр_cr1"><h2>Таймеры TIM2 и TIM5. Управляющий регистр (CR1)</h2><aside class="notes"><div class="paragraph"><p>Основные настройки таймера производятся через регистр CR1. Нам понадобятся всего несколько бит.</p></div></aside>
<div class="imageblock" style=""><img src="Lection3Img/Pic1.png" alt="400" width="1024"></div><div class="title">Рисунок 43. Регистр CR1</div>
<div class="hdlist"><table><tr><td class="hdlist1">Bit 2: URS</td><td class="hdlist2"><p>Источник генерации прерываний</p><div class="ulist"><ul><li><p><strong>0</strong>: Любые из следующих событий будут генерировать прерывание или запрос DMA, если они включены:</p><div class="ulist"><ul><li><p>Переполнение счетчика или установлен UG бит</p></li></ul></div></li><li><p><strong>1</strong>: Только после переполнения счетчика может сгенерировать прерывание или запрос DMA</p></li></ul></div></td></tr><tr><td class="hdlist1">Bit 1: UDIS</td><td class="hdlist2"><p>Отключить событие по изменению (Update Event)</p><div class="ulist"><ul><li><p><strong>0</strong>: UEV включен. Событие по изменению(UEV) генерируются следующими событиями:</p><div class="ulist"><ul><li><p>Переполнение счетчика или установлен UG бит</p></li></ul></div></li><li><p><strong>1</strong>: UEV отключен. </p></li></ul></div></td></tr><tr><td class="hdlist1">Bit 0 CEN</td><td class="hdlist2"><p>Включить счетчик</p><div class="ulist"><ul><li><p><strong>0</strong>: Counter выключен</p></li><li><p><strong>1</strong>: Counter включен</p></li></ul></div></td></tr></table></div></section><section id="_таймеры_tim2_и_tim5_регистр_статуса_sr"><h2>Таймеры TIM2 и TIM5. Регистр статуса (SR)</h2><aside class="notes"><div class="paragraph"><p>Регистр SR хранит статусы запросов на прерывания</p></div></aside>
<div class="imageblock" style=""><img src="Lection3Img/Pic2.png" alt="500" width="1024"></div><div class="title">Рисунок 44. Регистр SR</div>
<div class="hdlist"><table><tr><td class="hdlist1">Bit0: UIF</td><td class="hdlist2"><p>Флаг прерывания по событию обновления. Бит устанавливается аппаратно, скидываться должен программно</p><div class="ulist"><ul><li><p><strong>0</strong>: Флаг прерывания сбршен</p></li><li><p><strong>1</strong>: Флаг прерывания установлен</p></li></ul></div></td></tr></table></div></section><section id="_работа_с_таймером_в_качестве_счетчика"><h2>Работа с таймером в качестве счетчика</h2><div class="dlist"><dl><dt class="hdlist1">Для организации задержки</dt><dd><div class="ulist"><ul><li><p>Подать тактирование на модуль таймера</p></li><li><p>Установить делитель частоты для таймера в регистре <strong>PSC</strong></p></li><li><p>Установить источник генерации прерываний по событию переполнение с помощью бита <strong>URS</strong> в регистре <strong>CR1</strong></p></li><li><p>Установить значение до которого счетчик будет считать в регистре перезагрузке <strong>ARR</strong></p></li><li><p>Скинуть флаг генерации прерывания <strong>UIF</strong> по событию в регистре <strong>SR</strong></p></li><li><p>Установить начальное значение счетчика в 0 в регистре <strong>CNT</strong></p></li><li><p>Запустить счетчик с помощью бита <strong>EN</strong> в регистре <strong>CR1</strong></p></li><li><p>Проверять пока не будет установлен флаг генерации прерывания по событию <strong>UIF</strong> в регистре <strong>SR</strong></p></li><li><p>Как только флаг установлен остановить счетчик, сбросить бит <strong>EN</strong> в регистре <strong>CR1</strong>, Сбросить флаг генерации прерывания
<strong>UIF</strong> по событию в регистре <strong>SR</strong></p></li></ul></div></dd></dl></div></section><section id="_задание_1_простое"><h2>Задание 1. Простое</h2><div class="ulist"><ul><li><p>Светодиоды должны гореть раз в 500 мс</p></li><li><p>Сделать задержку на 500, 1000, 1500 мс, вместо цикла for(..) c с помощью таймера</p></li></ul></div></section></section>
<section id="_библиография"><h2>Библиография</h2><div class="colist arabic" id="1"><ol><li><p>Недяк С.П., Шаропин Ю.Б. Лабораторный практикум по микроконтроллерам семейства Cortex-M:
Методическое пособие по проведению работ на отладочных платах фирмы "Миландр"- Томск. гос. ун-т
систем упр. и радиоэлектроники, 2017. - 110 с.</p></li><li><p>Волков В.Л. Программное обеспечение измерительных процессов. Учебное пособие для студентов
технических специальностей дневной, заочной, и заочной форм обучения. /АПИ НГТУ. Арзамас,
2008 – 120 с.</p></li><li><p>Руководство по оформлению кода на С++ Стэнфордского университета:
<a href="http://stanford.edu/class/archive/cs/cs106b/cs106b.1158/styleguide.shtml" class="bare">http://stanford.edu/class/archive/cs/cs106b/cs106b.1158/styleguide.shtml</a></p></li><li><p>Объектно-ориентированное программирование:
<a href="https://ru.wikipedia.org/wiki" class="bare">https://ru.wikipedia.org/wiki</a></p></li><li><p>Можно ли использовать С++ вместо Си для небольших проектов в микроконтроллерах:
<a href="https://habr.com/post/347980/" class="bare">https://habr.com/post/347980/</a></p></li><li><p>AsciiDoc шпаргалка: <a href="https://powerman.name/doc/asciidoc" class="bare">https://powerman.name/doc/asciidoc</a></p></li><li><p>Reveal.js: <a href="https://github.com/hakimel/reveal.js#full-setup" class="bare">https://github.com/hakimel/reveal.js#full-setup</a></p></li><li><p>Asciidoctor Reveal.js: <a href="https://asciidoctor.org/docs/asciidoctor-revealjs/#node-javascript-setup" class="bare">https://asciidoctor.org/docs/asciidoctor-revealjs/#node-javascript-setup</a></p></li><li><p>Где хранятся ваши константы на микроконтроллере CortexM: <a href="https://habr.com/ru/post/453262/" class="bare">https://habr.com/ru/post/453262/</a></p></li><li><p>Обзор одной российской RTOS, часть 4. Полезная теория: <a href="https://habr.com/post/337476/" class="bare">https://habr.com/post/337476/</a></p></li><li><p>Начинаем изучать STM32: Что такое регистры? Как с ними работать? <a href="https://habr.com/ru/post/407083/" class="bare">https://habr.com/ru/post/407083/</a></p></li><li><p>Справочное руководство на микроконтроллер STM32F411 <a href="https://www.st.com/resource/en/reference_manual/dm00119316.pdf" class="bare">https://www.st.com/resource/en/reference_manual/dm00119316.pdf</a></p></li><li><p>Безопасный доступ к полям регистров на С++ без ущерба эффективности (на примере CortexM) <a href="https://habr.com/ru/post/459642/" class="bare">https://habr.com/ru/post/459642/</a></p></li></ol></div></section></div></div><script src="reveal.js/lib/js/head.min.js"></script><script src="reveal.js/js/reveal.js"></script><script>Array.prototype.slice.call(document.querySelectorAll('.slides section')).forEach(function(slide) {
if (slide.getAttribute('data-background-color')) return;
// user needs to explicitly say he wants CSS color to override otherwise we might break custom css or theme (#226)
if (!(slide.classList.contains('canvas') || slide.classList.contains('background'))) return;
var bgColor = getComputedStyle(slide).backgroundColor;
if (bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent') {
slide.setAttribute('data-background-color', bgColor);
slide.style.backgroundColor = 'transparent';
}
})
// See https://github.com/hakimel/reveal.js#configuration for a full list of configuration options
Reveal.initialize({
// Display presentation control arrows
controls: true,
// Help the user learn the controls by providing hints, for example by
// bouncing the down arrow when they first encounter a vertical slide
controlsTutorial: true,
// Determines where controls appear, "edges" or "bottom-right"
controlsLayout: 'bottom-right',
// Visibility rule for backwards navigation arrows; "faded", "hidden"
// or "visible"
controlsBackArrows: 'faded',
// Display a presentation progress bar
progress: true,
// Display the page number of the current slide
slideNumber: false,
// Control which views the slide number displays on
showSlideNumber: 'all',
// Push each slide change to the browser history
history: false,
// Enable keyboard shortcuts for navigation
keyboard: true,
// Enable the slide overview mode
overview: true,