2.11.1. Bản đồ bộ nhớ
Trang 27
2.11.2. Lập trình thanh ghi
- Một thanh ghi chức năng đƣợc điều khiển bởi 3 thanh ghi ngƣời dùng, một thanh ghi để đọc trạng thái, một thanh ghi để xóa và một thanh ghi để ghi dữ liệu lên.
Hình 2.25.Cấu trúc tổng quát khi truy cập thanh ghi chức năng trong ARM7
2.11.3. Memory Acelerator Module (MAM)
- Là bộ nhớ nằm giữa bộ nhớ Flash và CPU ARM, có tốc độ thực thi cao.
Hình 2.26: Mô hình bộ nhớ MAM
- CPU ARM có thể chạy ở tốc độ 80MHz, mỗi lần chíp Flash truy cập hết 50ns. - Flash chạy ở tốc độ 20MHz
Trang 28
- MAM đƣợc tạo ra nhƣ là một cache đầy đủ cho phép CPU dễ dàng truy cập trực tiếp vào FLASH.
Hình 2.27. Truy cập bộ nhớ FLASH qua MAM - Khi đọc các lệnh từ bank thứ nhất thì bank thứ hai đƣợc chốt.
- MAM là trong suốt với ngƣời dùng và đƣợc cấu hình bởi 2 thanh ghi: điều khiển (MAMCR) và định thời (MAMTIM). Thanh ghi định thời đƣợc sử dụng để điều khiển mối quan hệ giữa CPU và FLASH bằng cách thiết lập 3 bít đầu tiên của nó để chỉ định chu kỳ xung nhịp của CPU đƣợc yêu cầu bởi MAM để truy cập vào FLASH.
- Khi FLASH có tốc độ 20MHz và CPU có có thể có tốc độ cực đại là 60MHz, số chu kỳ yêu cầu truy cập FLASH là 3.
Thí dụ: Cấu hình MAM #include "LPC21xx.h"
void ChangeGPIOPinState(unsigned int state); int main(void){
unsigned int delay,val;
unsigned int FLASHer = 0x00010000; // Khai báo cục bộ IODIR0 = 0x00FF0000; // Thiết lập các chân ra
VPBDIV = 0x02;
ADCR = 0x00270601; // Thiết lập A/D: 10-bit AIN0 @ 3MHz ADCR |= 0x01000000; // Khởi tạo bộ chuyển đổi A/D
while(1) { do {
Trang 29 }while ((val & 0x80000000) == 0); val = ((val >> 6) & 0x03FF);
if (val <0x80) { MAMCR = 0; MAMTIM = 0x03; MAMCR = 0x02; }else { MAMCR = 0x0; }
for(delay = 0;delay<0x100000;delay++) //tạo vòng lặp {;}
ChangeGPIOPinState(FLASHer); //Đổi trạng thái các chân ra ở cổng FLASHer = FLASHer <<1; //Dịch đến đèn led tiếp
if(FLASHer&0x01000000) {
FLASHer = 0x00010000; //Lặp lại đèn đầu tiên // overflow
}}}
void ChangeGPIOPinState(unsigned int state) {
IOSET0 = state; //set output pins IOCLR0 = ~state; //clear output pins }
2.11.4. PLL- Phase Locked Loop
- Tạo ra một tần số dao động ngoài từ 10-25MHz từ mạch dao động cơ bản và có thể tăng lên 60 MHz để cung cấp cho CPU ARM và thiết bị ngoại vi.
- Tần số đầu ra của PLL có thể thay đổi tự động, cho phép thiết bị điều chỉnh theo tốc độ thực thi để duy trì nguồn năng lƣợng khi ở trạng thái rảnh rỗi.
Trang 30 Hình 2.28
- Hai hằng M và P phải đƣợc lập trình để quyết định xung clock (Cclk) cho CPU và AHB.
- Hăng thứ nhất đƣợc nhân một cách tuyến tính với tần số dao động bên ngoài đƣa vào. Tần số ra của PLL là: Cclk=M x Osc
- Ngƣợc lại PLL lại đƣợc điều khiển bởi một dao động hoạt động hiện hành (CCO) ở dải tần 156MHz-320MHz. Hằng số thứ 2 phải đƣợc lập trình để đảm bảo sao cho CCO đƣợc giữ một giá trị cụ thể: Fcco = Cclk x 2 x P
- Trên board phát triển thì dao động thạch anh là 12MHz, bởi vậy để CPU đạt đƣợc tốc độ tối đa 60MHz thì: M = Cclk/Osc = 60/12 =5
và 156< Fcco <320 = 60 x 2 x P Thực nghiệm thì P=2.
- Giao diện lập trình PLL:
Hình 2.29.
- Giá trị trong các thanh ghi PLLCON, PLLCFG và PLLSTAT sẽ đƣợc ghi sau khi giá trị trong PLL FEED đƣợc ghi.
Trang 31
- Khi cập nhật giá trị thanh ghi PLLCON và PLLCFG thì phải ghi liên tiếp hai giá trị 0x000000AA và 0x00000055 cho thanh ghi PLLFEED, các giá trị này phải ghi trong các chu kỳ liên tiếp.
- Nếu lập trình cho phép các ngắt thì ngắt sẽ sinh ran gay sau khi từ đầu tiên đƣợc ghi và các thiết lập mới cho PLL sẽ không có ảnh hƣờng.
- Để cài đặt PLL phải ghi các giá trị cho P và M tới thanh ghi PLLCFG, sau đó set D0 của thanh ghi PLLCON để cho phép PLL khởi động.
- Giá trị của M chiếm 5 bít thấp (D4-D0), P chiếm 2 bít D6D5:
Hình 2.30. Giá trị của P và M trong thanh ghi PLLCFG
- PLL mất một khoảng thời gian xác định đủ để sử dụng nguồn xung clock. Sự khởi động PLL có thể đƣợc kiểm tra bằng cách đọc bít LOCK (D10) trong thanh ghi trạng thái PLLSTAT.
- Khi LOCK bít =1, PLL có thể đƣợc sử dụng nhƣ nguồn xung clock chính. Một ngắt có thể đƣợc sinh ra khi PLL khóa, bởi vậy ngƣời lập trình có thể thực hiện các nhiệm vụ khác khi PLL khởi động. Khi PLL bị khóa thì có thể thay thế nguồn xung cho Cclk bằng cách điều khiển bít PLLC trong thanh ghi PLLCON.
Trang 32
Hình 2.31. Trình tự khởi động PLL
2.11.5. Bộ chia bus (VLSI Peripheral Bus Divider)
Mạch dao động ngoài hoặc đầu ra của PLL đƣợc sử dụng để tạo ra nguồn xung Cclk cho CPU ARM hoặc hệ thống bus có tần số cao. Các thiết bị ngoại vi có thể sử dụng bus VPB riêng biệt.
Hình 2.32. Tạo xung Pclk từ Cclk
- Bộ chia có thể chia tốc độ Cclk xuống 2 đến 24 lần. Thanh ghi trong bộ chia
VPBDIV có thể lập trình được và chứa số lần giảm tốc độ. Tại thời điểm khởi động,
giá trị cực đại đƣợc nạp và bằng ¼ giá trị Cclk lúc khởi động.
- Hiện nay tất cả các thiết bị ngoại vi của họ vi điều khiển LPC có thể chạy ở tần số 60MHz. Vì vậy, bộ chia tần VPB thƣờng đƣợc sử dụng để tiết kiệm nguồn bằng cách tạo xung clock chấp nhận đƣợc cho các ứng dụng.
Trang 33
Cấu hình PLL để tạo ra tần số Cclk là 60MHz và Pclk là 30MHz với xung đầu vào là 12MHz:
void init_PLL(void) {
PLLCFG = 0x00000024; // Thiết lập hệ số nhân và bộ chia cho PLL // give 60.00 MHz
PLLCON = 0x00000001; // Kích hoạt PLL
PLLFEED = 0x000000AA; // Cập nhật thanh ghi PLLFEED PLLFEED = 0x00000055;
while (!(PLLSTAT & 0x00000400)); // kiểm tra bít Lock PLLCON = 0x00000003; // Kết nối tới PLL
PLLFEED = 0x000000AA; //Cập nhật các thanh ghi PLL PLLFEED = 0x00000055;
VPBDIV = 0x00000002; //Thiết lập bus VLSI với tần số 30.000MHz }
2.12. Các cổng vào ra
- Các cổng vào ra của ARM là 32 bít, vi điều khiển có thể chỉ có 1 cổng hoặc có nhiều hơn, nhƣng các chân dùng cho cổng vào/ra cũng là các chân dùng chung cho các mục đích khác nhƣ biến đổi AD, ngắt, giao diện SPI, I2C,…
- Các chân đƣợc sử dụng vào mục đích nào tùy vào việc cấu hình chúng cho mục đích đó. Trong chƣơng 3 sẽ trình bày cụ thể về cách lập trình cho các chân cổng vào ra.
Trang 34
CHƢƠNG 3: LẬP TRÌNH VI ĐIỀU KHIỂN ARM 3.1. Hƣớng dẫn sử dụng phần mềm Keil C
B1: Tạo một project mới:
B2: Tạo thƣ mục mới để chứa project mới tạo:
B3: Đặt tên cho thƣ mục là TD1 và click đúp lên thƣ mục này để mở ra và đặt tên cho project là TD1:
Trang 35
B4: Chọn vi điều khiển với core ARM7, thí dụ LPC2101:
Trang 36
B6: Chọn New để thêm file mới để soạn thảo mã nguồn của vi điều khiển:
Trang 37 B8: Ghi lại với tên MAM.C
Trang 38 B10: Chọn file MAM.c:
Trang 39 B12: Trong tab Target chọn tần số là 12:
Trang 40 B14: Biên dịch project:
Trang 41 B16: Nạp file .hex cho vi điều khiển:
Trang 42
3.2. Truy nhập các chân vào ra chung
Các chân vào (General purpose I/O input pins) ra đƣợc điều khiển bởi 4 thanh ghi:
Hình 3.1. Các chân vào ra đƣợc dùng cho mục đích chung
- Mỗi chân GPIO đƣợc điều khiển bởi 4 bít để điều khiển hƣớng dữ liệu, set, xóa và trạng thái của chân.
- Mỗi bít trong thanh ghi IODIR: 0 cho phép cấu hình các chân là đầu vào 1 cho phép cấu hình các chân là đầu ra
Trang 43
- Khi các chân là đầu ra thì các bít tƣơng ứng trên các thanh ghi IOSET và IOCLR cho phép ngƣời lập trình điều khiển trạng thái của các bít.
- Để xóa chân nào thì đƣa set bít đó =1 tƣơng ứng trong thanh ghi IOCLR - Để đọc nội dung của chân sử dụng thanh ghi IOPIN
Thí dụ 1: Viết chƣơng trình điều khiển led đơn sáng nhấp nháy
Ghép nối một led đơn với chân P0.0 của LPC2101, Sơ đồ nối chân Proteus:
Chƣơng trình điều khiển:
//Thi du chuong trinh dieu khien led nhap nhay tai chan p0.1 //LPC 2101
#include"LPC21xx.h" void delay(unsigned int a); int main(void){ IODIR0=0x00000001 ; while(1){ IOSET0=0x00000001 ; delay(0x100000);
Trang 44 IOCLR0=0x00000001 ;
delay(0x100000); } }
void delay(unsigned int a){ unsigned int i;
for (i=0;i<a;i++){;} }
Thí dụ 2: Chƣơng trình hiển thị chữ trên LCD
Sơ đồ trên proteus:
Chƣơng trình điều khiển:
Trang 45 Tệp lcd.h định nghĩa địa chỉ dành cho lcd:
/***************************************/ /* LCD routines for OLIMEX LPC-MT-2106 */ /* 16x2 Character LCD 1602K */
/* 4-bit mode operation */
/***************************************/ /* DEFINE IOMASK */
#define LCD_D4 0x10 //P0.04 #define LCD_D5 0x20 //P0.05
Trang 46 #define LCD_D6 0x40 //P0.06 #define LCD_D7 0x80 //P0.07 #define LCD_EN 0x400000 //P0.22 #define LCD_RS 0x800000 //P0.23 #define LCD_RW 0x1000000 //P0.24 #define LCD_DATA (LCD_D4|LCD_D5|LCD_D6|LCD_D7) #define LCD_IOALL (LCD_D4|LCD_D5|LCD_D6|LCD_D7|LCD_EN|LCD_RS|LCD_RW) #define LCD_GPIO_SEL0 0x0000FF00 //MASK for P0.04-P0.07 #define LCD_GPIO_SEL1 0x0003F000 //MASK for P0.22-P0.24
/* Functions Header */ /* internal I/O functions */
#define lcd_rs_set() IOSET |= LCD_RS #define lcd_rs_clr() IOCLR |= LCD_RS #define lcd_en_set() IOSET |= LCD_EN #define lcd_en_clr() IOCLR |= LCD_EN #define lcd_rw_set() IOSET |= LCD_RW #define lcd_rw_clr() IOCLR |= LCD_RW
/* wait until lcd controller is free */
void lcd_wait();
void lcd_out_data4(unsigned char); void lcd_write_nibbles(unsigned char); void lcd_write_control(unsigned char);
/* initialize both the GPIO of lpc and LCD */
void lcd_init();
#define lcd_clear() lcd_write_control(0x01) #define lcd_cursor_home() lcd_write_control(0x02) #define lcd_display_on() lcd_write_control(0x0E) #define lcd_display_off() lcd_write_control(0x08) #define lcd_cursor_blink() lcd_write_control(0x0F) #define lcd_cursor_on() lcd_write_control(0x0E)
Trang 47
#define lcd_cursor_off() lcd_write_control(0x0C) #define lcd_cursor_left() lcd_write_control(0x10) #define lcd_cursor_right() lcd_write_control(0x14) #define lcd_display_sleft() lcd_write_control(0x18) #define lcd_display_sright() lcd_write_control(0x1C) /* put a character out to lcd */
void lcd_putchar(unsigned char); /* print a string */
void lcd_print(unsigned char*);
Tệp lcd.c định nghĩa các phƣơng thức liên quan đến các thao tác trên LCD: #include "lcd.h"
#include "lpc210x.h" void lcd_wait(){
int loop=2800; //more than enough //busy loop
while(loop--); }
void lcd_out_data4(unsigned char val){ IOCLR |= (LCD_DATA);
IOSET |= (val<<4); }
void lcd_write_nibbles(unsigned char val){ //higher-order byte lcd_en_set(); lcd_out_data4((val>>4)&0x0F); lcd_en_clr(); lcd_wait(); //lower-order byte lcd_en_set(); lcd_out_data4((val)&0x0F); lcd_en_clr();
Trang 48 lcd_wait();
}
void lcd_write_control(unsigned char val){ lcd_rs_clr();
lcd_write_nibbles(val); }
void lcd_init(){
PINSEL0 &= (~LCD_GPIO_SEL0); PINSEL1 &= (~LCD_GPIO_SEL1); /* we only work on OUTPUT so far */ IODIR |= LCD_IOALL;
/* IO init complete, init LCD */ /* init 4-bit ops*/
lcd_rs_clr(); lcd_rw_clr(); lcd_en_clr(); //wait VDD raise > 4.5V lcd_wait(); //dummy inst lcd_write_nibbles(0x30); lcd_write_nibbles(0x30); lcd_write_nibbles(0x30); //FUNCTION SET //001DL N F XX //DL=1: 8bit //DL=0: 4bit //N=0: 1 line display //N=1: 2 line display //F=0: 5x7 dots //F=1: 5x10 dots //our case:
Trang 49 //0010 1000 lcd_en_set(); lcd_out_data4(0x2); lcd_en_clr(); lcd_wait(); lcd_write_nibbles(0x28); //LCD ON lcd_write_nibbles(0x0E); //Clear Display lcd_write_nibbles(0x01); //Entry mode lcd_write_nibbles(0x06); }
void lcd_putchar(unsigned char c){ lcd_rs_set();
lcd_write_nibbles(c); }
void lcd_print(unsigned char* str){ int i;
//limit 1 line display for prints for (i=0;i<16 && str[i]!=0;i++){ lcd_putchar(str[i]);
}}
Tệp test.c chứa chƣơng trình chính: #include "lpc210x.h"
#include "lcd.h" int main(void) {
lcd_init();
lcd_print("Trieu Dai Gia"); return 1;
Trang 50 }
3.3. Lập trình ngắt
Trong phần này sẽ trình bày 2 loại ngắt ngoài: ngắt sinh ra qua các chân vào ra (IRQ) và ngắt nhanh (FIQ).
- Cấu trúc tổng quát của một chƣơng trình con phục vụ ngắt:
void tên_ctcpvn (void) __Kiểu_chương_trình_phục_vụ_ngắt {
// các lệnh thân chương trình con phục vụ ngắt }
- Có 3 kiểu chƣơng trình con phục vụ ngắt: _IRQ, _SWI, _ABORT,
- Chân EINT1 đƣợc kết nối tới một công tác trên board cho phép bầy lỗi nhanh một ngắt và quan sát trên trình debug.
3.3.1. Chân Connect Block
- Tất cả các chân trên LPC2000 đƣợc kết nối tới một số chức năng ở bên trong qua một bộ đa khối đa thành phần đƣợc gọi là chân select block. Chân select block cho phép một user cấu hình một chân qua GPIO hoặc chọn 3 chân khác.
Hình 3.2. Chân I/O qua khối đa thành phần
- Khi khởi động thì tất cả các chân qua khối đa thành phần là chân GPIO, chức năng thứ 2 sẽ đƣợc chọn nhờ thanh ghi PINSEL. Chân ngắt EINT1 vừa là chân I/O, vừa là chân GPIO 0.14 và là đƣờng điều khiển UART1. Do vậy, để sử dụng EINT1 phải cấu hình chân chọn thanh ghi chuyển từ GPIO sang EINT1.
Trang 51
Các thanh ghi PINSEL khi đƣợc khởi động lên có giá trị mặc định là 0 và cấu hình các chân dành cho mục đích và ra.
Các chân của thanh ghi PINSEL cấu hình cho mục đích ngắt:
PINSEL0: (Thí dụ cho LPC213x)
STT Chân Kí hiệu Giá trị Loại ngắt
1 3:2 P0.1 11 EINT0 2 7:6 P0.3 11 EINT1 3 15:14 P0.7 11 EINT2 4 19:18 P0.9 11 EINT3 5 29:28 P0.14 10 EINT1 6 31:30 P0.15 10 EINT2 PINSEL1: (Thí dụ cho LPC213x)
STT Chân Kí hiệu Giá trị Loại ngắt
1 1:0 P0.16 11 EINT0
2 9:8 P0.20 11 EINT3
3 29:28 P0.30 11 EINT3
3.3.2. Các chân ngắt ngoài
Các ngắt ngoài đƣợc điều khiển bởi 4 thanh ghi nhƣ hình dƣới. Thanh ghi EXMODE chọn ngắt kích theo mức hoặc theo sƣờn. Nếu một ngắt ngoài đƣợc cấu hình là kích theo sƣờn thì thanh ghi EXPOL đƣợc sử dụng để theo dõi ngắt hoạt động tăng lên hay giảm xuống. Trong trƣờng hợp kích theo mức, ngắt ngoài có thể chỉ ở mức logic 0. Nếu ở chế độ tiết kiệm nguồn thì thanh ghi EXWAKE có thể cho phép một ngắt đánh thức CPU. Để cài đặt một nguồn ngắt đơn giản chƣơng trình cần cấu hình chân EINT1 kích theo mức và kết nối tới CPU qua thanh ghi PINSEL0.
Trang 52
Hình 3.3. Chân ngắt ngoài dễ dàng đƣợc cấu hình để tạo nguồn ngắt.
3.3.3. Cấu trúc ngắt
- ARM7 có 2 chân ngắt ngoài cho yêu cầu ngắt nhanh (FIQ) và yêu cầu chế độ ngắt đa mục đích. Tất cả các ngắt đều phải kết nối tới ngắt IRQ. Trong một hệ thống đơn giản có thể kết nối qua cổng OR.
- VIC cho phép điều khiển ngắt một cách hiệu quả:
Hình 3.4 Bộ điều khiển ngắt VIC cho phép điều khiển ngắt hiệu quả
- Các ngắt phát sinh trong ứng dụng đƣợc đƣa đên VIC và đƣợc điều khiển nhƣ ngắt FIQ hoặc một vecto ngắt hoặc không có vecto ngắt.
Trang 53 Hình 3.5.
Các thanh ghi điều khiển ngắt:
VICIRQStatus: Trạng thái các ngắt IRQ VICFIQStatus: Trạng thái các ngắt FIQ
VICIntSelect: Thanh ghi chọn ngắt FIQ hay IRQ với các chân từ 14 đến 17:
Nếu các chân có giá trị 0 tƣơng ứng với ngắt IRQ đƣợc chọn, bằng 1 tƣơng ứng với ngát FIQ đƣợc chọn: STT Bít Loại ngắt Giá trị 1 14 EINT0 =0 loại ngắt IRQ =1 loại ngắt FIQ 2 5 EINT1 3 16 EINT2 4 17 EINT3
VICIntEnable: Thanh ghi cho phép ngắt.
Sử dụng các bít từ bít 14 đến bít 17 để cho phép các ngắt từ EINT0 đến EINT3:
STT Bít Loại ngắt Giá trị 1 14 EINT0 =0 không cho phép ngắt =1 cho phép ngắt 2 5 EINT1 3 16 EINT2 4 17 EINT3
VICIntEnClr: Xóa các ngắt sử dụng các bít từ 14 đến 17 tƣơng ứng với các ngắt từ