1
GIÁO TRÌNH ARM STM32F103 CƠ BẢN.
I. Giới thiệu:
a. Hình ảnh các dạng đóng gói .
H1: Kiểu đóng gói 64 pin
Biên soan: Cao Bá Vinh – email:
2
H2: kiểu đóng gói 100pin
Biên soan: Cao Bá Vinh – email:
3
H3: kiểu đóng gói 144pin
b. Cấu trúc của chip STM32F103xx:
i. Đây là cấu trúc 32bit .
ii. Được chế tạo bởi cơng ty STMicroelectronics.
iii. Tốc độ max 72Mhz.
iv. Tốc độ tính toán tất cả phép cộng trừ nhân chia số nguyên chỉ có 1 chu kỳ
máy.
v. Ngoại vi của STM32F103XX rất nhiều như:
ADC,DAC,TIMER,PWM,QEI,UART,SPI,I2C,USB,CAN,SDIO…
vi. Nguồn nuôi hoạt động từ 2v-3.6v.
Biên soan: Cao Bá Vinh – email:
4
vii. Bộ nhớ FLASH rất lớn từ 64k-1Mbyte.
viii. Nạp xóa bộ nhớ flash được 10.000 lần.
ix. Được hỗ trợ rất nhiều trình biên dịch như KEILC,IAR,MICROC…
II. Mơi trường làm việc trên trình biên dich KEILC cho arm.
a. Hướng dẫn tạo project trên KEILC.
Giới thiệu cách tạo mới dự án cho vi xử lý ARM Cortex-M3 STM32F103RC bằng Keil ARM.
Cùng với đó là cách tích hợp bộ thư viện chuẩn CMSIS của ST dành cho dòng ARM này.
1. Bộ thư viện CMSIS
ST cung cấp cho người dùng bộ thư viện chuẩn lập trình giao tiếp với thiết bị ngoại vi tương
thích với chuẩn CMSIS. Thông qua bộ thư viện này, lập trình viên dễ dàng giao tiếp với các
thiết bị phần cứng chuẩn của các dòng Cortex-M3 của ST.
Thư viện được chia làm 2 phần:
+ phần hỗ trợ nhân Cortex-M3: bao gồm mã giao tiếp với nhân CPU, và đoạn mã start up
code.
+ phần hỗ trợ các thiết bị ngoại vi: chứa toàn bộ các hàm thư viện điều khiển thiết bị ngoại vi
của ST.
Cấu trúc thư viện CMSIS như sau:
Library
+ CMSIS
+ CM3
+ CoreSupport
//thư mục chứa hàm hỗ trợ nhân Cortex-M3
+ DeviceSupport
+ ST
+ STM32F10X
//System startup code
+ startup
//Start up code
+ Documentation
//tài liệu hỗ trợ
+ STM32F10x_StdPeriph_Driver //thư mục chứa hàm hỗ trợ thiết bị ngoại vi
+ inc
//thư mục chứa header file
+ src
//thư mục chứa mã nguồn
* Lưu ý: Các hàm được viết và đặt tên theo chuẩn CMSIS, lập trình viên cần tuân theo các quy
tắc của CMSIS khi sử dụng hàm, tránh viết lại các hàm truy cập thẳng vào phần cứng khi không
cần thiết.
2. Khởi tạo dự án mới
+ Mở Keil IDE, chọn menu “Project->New uVision Project” để tạo dự án mới. Giả dụ đặt tên dự
Biên soan: Cao Bá Vinh – email:
5
án mới này là led.
* Lưu ý: Thường khi tạo project mới hệ thống file quản lý dự án của Keil hay bố trí ở thư mục
dự án, điều này dễ bị lẫn lộn với các file nguồn, ta nên tạo một thư mục con để quản lý các file
dự án này.
Chọn chip STM32F103RC
Hình 1: Khởi tạo dự án
+ Sau khi dự án mới được tạo, ta nên tổ chức lại hệ thống mã nguồn để dễ dàng theo dõi.
Biên soan: Cao Bá Vinh – email:
6
Hình 2: Tổ chức thư mục mã nguồn
Như hình 2 ở trên ta tạo 4 nhóm file, các nhóm “CMSIS”, “StdPeriph_Driver” và “Start up” sẽ
là các files từ thư viện CMSIS của ST.
* Lưu ý: Khi tạo mới dự án, Keil sẽ hỏi người dùng có sử dụng “start up code” sẵn có khơng.
Chúng ta khơng sử dụng “start up code” này của Keil mà sẽ dùng của ST có trong bộ thư viện
chuẩn.
+ Tích hợp thư viện CMSIS vào chương trình
Biên soan: Cao Bá Vinh – email:
7
Chúng ta sẽ lần lượt tích hợp các thư mục trong thư viện vào dự án như sau:
+ Nhóm “CMSIS”: thêm file core_cm3.c ở thư mục “\Libraries\CMSIS\CM3\CoreSupport”
và system_stm32f10x.c ở thư mục “\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x”
+ Nhóm “StdPeriph_Driver”: thêm các file liên quan đến điều khiển ngoạI vi, ở dự án này
chúng ta cần điều khiển cổng GPIO, UART nên cần thêm các
file:stm32f10x_gpio.c, stm32f10x_usart.c và stm32f10x_rcc.c ở thư mục
“\Libraries\STM32F10x_StdPeriph_Driver\src”.
+ Nhóm “Start up”: thêm file startup_stm32f10x_hd.s ở thư mục
“Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm”.
+ Nhóm “User”: chứa file của người dùng, giả sử thêm file main.c của ta vào đây.
* Lưu ý: Đối với nhóm StdPeriph_Driver, nên căn cứ vào nhu cầu điều khiển ngoại vi để thêm
vào các file tương ứng, tránh thêm các file dư thừa vì làm tăng thời gian biên dịch và tốn tài
nguyên hệ thống.
+ Khai báo thư mục thư viện cho dự án
Sau khi thêm các file cần thiết cho dự án, chúng ta chưa thể biên dịch thành cơng được vì cịn
thiếu đường dẫn tới các file khai báo thư viện CMSIS
Mở khung điều khiển cấu hình dự án
Biên soan: Cao Bá Vinh – email:
8
Chọn tab “C/C++”
Thêm các đường dẫn thư mục sau vào dự án:
+ \Libraries: thư mục chứa Libraries CMSIS
+ \Libraries\CMSIS\CM3\CoreSupport
+ \Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x
+ \Libraries\STM32F10x_StdPeriph_Driver\inc
* Lưu ý : Người dùng có thể thêm vào các đường dẫn thư mục khác của dự án.
3. Cấu hình project
Sau khi đã thêm các file cần thiết cho dự án, chúng ta phải thiết lập các thông số cơ bản để Keil
có thể biên dịch ra file thực thi.
Biên soan: Cao Bá Vinh – email:
9
+ Để nạp chương trình xuống board, chúng ta cần cấu hình Keil biên dịch ra file hex(hoặc bin).
Mở khung cấu hình dự án, chọn tab “Output”, check và ơ “Create HEX File”
+ Để tiện sắp xếp tài nguyên của dự án, ta nên xếp các file tạm được sinh ra bởI Keil vào các thư
mục riêng
Biên soan: Cao Bá Vinh – email:
10
Tương ứng với các file object(tab Output) và linker(tab Listing) ta lưu trong thư mục “Obj” và
“Lst” cho tiện theo dõi sau này.
+ Cần lưu ý là với bộ thư viện CMSIS, chúng ta sử dụng khá nhiều kỹ thuật “macro” trong lập
trình. Có một số “macro” cần khai báo “define” sẵn trong dự án để có thể biên dịch thành công.
* Lưu ý: Nếu sử dụng bộ thư viện chuẩn cho thiết bị ngoại vi, nên khai báo
macro: USE_STDPERIPH_DRIVER.
4. Trình diễn
+ Nếu có sẵn board thí nghiệm, chúng ta có thể nạp trực tiếp file .hex sau khi biên dịch xuống
chíp thơng qua Flash Downloader của ST bằng cổng COM.
+ Nếu khơng có board, chúng ta có thể xem bằng cách dùng Debug Simulator của Keil
Biên soan: Cao Bá Vinh – email:
11
+ Chạy Debug chương trình, mở cửa sổ theo dõi các thiết bị ngoại vi ở menu “Peripherals” chọn
ngoại vi tương ứng, giả sử đó là Port C của GPIO.
Bấm F10(hoặc F11) để chạy debug từng dòng lệnh đồng thời theo dõi giá trị của Port C thay
đổi.
Biên soan: Cao Bá Vinh – email:
12
Hướng dẫn sử dụng Flash Loader Demonstrator
Bài viết này dựa trên kít thí nghiệm, Flash Loader Demonstrator (gọi tắt FLD) là chương trình
hỗ trợ nạp mã thực thi vào chip ARM Cortex-M3 thơng qua UART1. Có thể download FLD
trực tiếp từ trang web hỗ trợ của hãng ST ở đây.
Trước khi nạp chương trình vào chip thơng qua UART1, chúng ta phải thiết lập chip ở chế độ
boot từ System Memory của chip.
Thiết lập chế độ boot từ System Memory của chip.
Để nạp mã thực thi bằng FLD chúng ta cần trải qua 5 bước chính
+ Bước 1: Dùng cable serial nối từ máy tính tới board thơng qua cổng UART1.
Nhấn nút boot xuống(đèn trên kít tắt) sau đó nhấn nút reset.
Biên soan: Cao Bá Vinh – email:
13
Hình 1: Thiết lập kết nối
Bấm “Next” để FLD giao tiếp với chip.
* Lưu ý: Theo kinh nghiệm, ta nên chọn tốc độ UART ở 57600bps, và Parity là Even.
+ Bước 2: Nếu kết nối với chip thành công, FLD đọc cấu hình flash chip
Biên soan: Cao Bá Vinh – email:
14
Hình 2: Thơng tin Flash sau khi kết nối với chip thành công
+ Bước 3: Thông tin thiết bị
Biên soan: Cao Bá Vinh – email:
15
Hình 3: Thơng tin chip
Ở một số phiên bản FLD khác, thơng tin hiển thị có thể hơi khác như sau
Biên soan: Cao Bá Vinh – email:
16
Hình 4: Giao diện thơng tin của flash ở phiên bản Flash Loader Demonstrator v1.2
+ Bước 4: lựa chọn thao tác điều khiển chip
FLD hỗ trợ nhiều thao tác trên chip như
- Xóa dữ liệu từng phần hay hồn tồn trên flash.
- Nạp mã chương trình thực thi xuống flash.
- Lấy mã nguồn từ chip lên.
- Điều khiển các tính năng bảo vệ flash.
- Thay đổi giá trị từng byte trên flash.
Biên soan: Cao Bá Vinh – email:
17
Ở bài này tôi chỉ đề cập đến nạp chương trình thực thi xuống flash, các phần khác các bạn có thể
tham khảo cách dùng FLD tại đây.
Hình 5: Nạp chương trình xuống flash
* Lưu ý: chọn “Global Erase” để chắc rằng dữ liệu cũ sẽ bị xóa, flash sẵn sàng cho dữ liệu mới.
Chọn “Verify after download” để kiểm tra dữ liệu sau khi nạp có đúng hay khơng.
+ Bước 5: Sau khi đã chọn file xong, bấm Next FLD sẽ tự động thực thi nhiệm vụ.
Biên soan: Cao Bá Vinh – email:
18
Hình 6: Hồn tất nạp chương trình
Màn hình báo kết quả nạp đã hồn tất.
Để chạy đoạn chương trình vừa nạp vào flash của chip, chỉ cần nhấn nút boot lại(đèn led sáng
lên) và nhấn nút reset lại 1 lần nữa.
Biên soan: Cao Bá Vinh – email:
19
II:Lý thuyết ngôn ngữ C cho Vi điều Khiển.
1.
Tập lệnh và cấu trúc C:
1.1 KHAI BÁO BIẾN:
Chuẩn C
Chuẩn
Chuẩn stm32 Size
(Bits)
Range
bit
bit
1
0,1
int8_t
char
8
-128 to 127
uint8_t
u8
8
0 to 255
int8_t
s8
8
-128 to 127
int16_t
s16
16
-32768 to 32767
uint16_t
u16
16
0 to 65535
unsigned int
uint32_t
u32
32
0 to 4294967295
signed int
int32_t
s32
32
-2147483648 to 2147483647
uint64_t
u64
64
0 to 264
int64_t
s64
64
-232 to 232
float
float
float
32
±1.175e-38 to ±3.402e38
double
double
double
64
±1.175e-38 to ±3.402e38
keilC
bit
signed
char
unsigned
signed
char
char
signed short
int
unsigned short
unsigned
signed
int
__int64
__int64
Ta có rất nhiều kiểu khai báo biến , tùy ứng dụng mà ta sử dụng cho phù hợp để vừa tiết kiệm bộ
nhớ và tăng tốc độ sử lý.
Từ khóa unsigned là khơng dấu.
Biên soan: Cao Bá Vinh – email:
20
1.2 ĐỊNH NGHĨA :
Định nghĩa có chức năng thay thế cái gì đó để tiện cho ta thay đổi các ứng dụng 1 cách
nhanh chóng.
Từ khóa “#define” dùng để định nghĩa và cuối dịng khơng có dấu “;”.
Vd:
1.3 .
#define
led1_on
GPIOA->ODR|=1<<0
#define
data_led
#define
motor_left
TIM2->CCR1
#define
motor_right
TIM2->CCR2
#define
un
unsigned
#define
high(x)
(((x) & 0xFF00) >> 8)
#define
low(x)
((unsigned char)(x))
GPIOC->ODR
Thay Thế Kiểu :
Để thay thế kiểu ta có thể dung từ khóa “typedef”
Cấu trúc : typedef
kiểu dữ liệu đặt tên;
Vd:
typedef
int
so_nguyen;
typedef
char
byte;
typedef
double
so_thuc;
typedef signed
typedef signed short
char int8_t;
int int16_t;
typedef
signed
int int32_t;
typedef
int32_t
s32;
Biên soan: Cao Bá Vinh – email:
21
typedef
int16_t
s16;
typedef
int8_t
s8;
Sau khi chúng ta đặt tên thì chúng ta có thể sử dụng tên mới này mà đặt tên cho khai báo biến.
Vd:
so_nguyen
I,j,k,l;// lúc này thì các biến I,j,k,l là kiểu số nguyên 16bit.
1.4 .LỆNH IF .. ELSE IF…
Lệnh if … else if là lệnh kiểm tra điều kiện .
if <điều kiện1>
{ Đúng điều kiện 1 thì làm.}
else
{Điều kiện 1 sai thì làm.}
……………………………….
Chú ý nếu thực hiện 1 dịng lệnh thì khơng cần dấu {}.
vd1:
int i=10;
if (i>10) i=i+1;
else i=i-1;
Vd2: int i=5,j;
if (i==7) j=0;
else if(i==10) j=j+2;
else if(i==5) j=i+1;
Biên soan: Cao Bá Vinh – email:
22
else j=j+100;
1.5 .LỆNH WHILE(điều kiện).
Lệnh while là lệnh kiểm tra điều kiện trước rồi mới thực thi khối lệnh .
Nếu điều kiện cịn đúng thì khối lệnh vẫn cịn thực hiện .
Vd: while(1); có nghĩa là đứng yên tại chỗ.
While (1) {led=!led; delay_ms(100);}// chớp tắt led sau mỗi 100ms.
Vd: char i=0;
while(i<250) {i++;delay_ms(100);led=i;}
Cách viết khác.
Vd: char i=0;
while(i++<250) {delay_ms(100);led=i;}// kt đk trước rồi tăng i sau.
Vd: char i=0;
while(++i<250) {delay_ms(100);led=i;}// tăng i sau đó kt đk sau.
1.6 .LỆNH do…while(điều kiện).
Lệnh do…while là lệnh thực hiện khối lệnh trước rồi mới kiểm tra điều kiện sau .
Nếu điều kiện cịn đúng thì khối lệnh vẫn còn thực hiện .
Vd: int char i=0;
Biên soan: Cao Bá Vinh – email:
23
do
{ ++i; delay_ms(100);
}
while(i<200);
Vd: int char i=0;
do
{ delay_ms(100); led=~led;
}
while(++i<200);
1.7. CÊu tróc rẽ nhánh - toán tử switch:
Là cấu trúc tạo nhiều nhánh đặc biệt. Nó căn cứ vào giá trị một biểu thức nguyên
để để chọn một trong nhiều cách nhảy.
Cấu trúc tổng quát của nó là :
switch ( biểu thức nguyªn )
{
case n1
khèi lƯnh 1
case n2
khèi lƯnh 2
.......
Biên soan: Cao Bá Vinh – email:
24
case nk
khối lệnh k
[ default
khối lệnh k+1
]
}
Với ni là các số nguyên, hằng ký tự hoặc biểu thức hằng. Các ni cần có giá trị
khác nhau. Đoạn chơng trình nằm giữa các dấu { } gọi là thân của toán tử switch.
default là một thành phần không bắt buộc phải có trong thân của switch.
Sự hoạt động của toán tử switch phụ thuộc vào giá trị của biểu thức viết trong
dấu ngoặc ( ) nh sau :
Khi giá trị của biểu thức này bằng ni, máy sẽ nhảy tới các câu lệnh có nhÃn là
case ni.
Khi giá trị biểu thức khác tất cả các ni thì cách làm việc của máy lại phụ thuộc
vào sự có mặt hay không của lệnh default nh sau :
Khi có default máy sẽ nhảy tới câu lệnh sau nhÃn default.
Khi không có default máy sẽ nhảy ra khỏi cấu trúc switch.
Chú ý :
Máy sẽ nhảy ra khỏi toán tử switch khi nó gặp câu lệnh break hoặc dấu ngoặc
nhọn đóng cuối cùng của thân switch. Ta cịng cã thĨ dïng c©u lƯnh goto trong thân
của toán tử switch để nhảy tới một câu lệnh bất kỳ bên ngoài switch.
Biờn soan: Cao Bỏ Vinh email:
25
Khi toán tử switch nằm trong thân một hàm nào đó thì ta có thể sử dụng câu lệnh
return trong thân của switch để ra khỏi hàm này ( lệnh return sẽ đề cập sau ).
Khi máy nhảy tới một câu lệnh nào đó thì sự hoạt động tiếp theo của nó sẽ phụ
thuộc vào các câu lệnh đứng sau câu lệnh này. Nh vậy nếu máy nhảy tới câu lƯnh cã
nh·n case ni th× nã cã thĨ thùc hiƯn tất cả các câu lệnh sau đó cho tới khi nào gặp câu
lệnh break, goto hoặc return. Nói cách khác, máy có thể đi từ nhóm lệnh thuộc case ni
sang nhóm lệnh thuộc case thứ ni+1. Nếu mỗi nhóm lệnh đợc kết thúc bằng break thì
toán tử switch sẽ thực hiện chỉ một trong các nhóm lệnh này.
Ví dụ :
Lập chơng trình phân loại học sinh theo điểm sử dụng cÊu tróc switch :
#include “stdio.h”
main()
{
int diem;
tt: printf(“\nVao du lieu :”);
printf(“\n Diem =“);
scanf(“%d”,&diem);
switch (diem)
{
case 0:
case 1:
case 2:
Biên soan: Cao Bá Vinh – email: