NỘI DUNG
1. Giới thiệu
Hướng dẫn cách sử dụng công cụ phần mềm Keil MDK-ARM để thiết lập một project, phát triển mã nguồn chương trình ứng dụng, biên dịch, nạp chương trình và chạy thử nghiệm trên bo mạch phát triển FRDM-KL46Z.
Link video hướng dẫn sử dụng phần mềm Keil MDK-ARM để tạo project, biên dịch, và nạp chương trình ứng dụng vào bo mạch FRDM-KL46Z ở đây.
Link download các tệp mã nguồn ở đây.
Bạn có thể mua bo mạch FRDM-KL46Z trên trang thương mại điện tử Lazada ở đây.
2. Tạo một project mới
Mở phần mềm KEIL µVision5 nếu phần mềm chưa được mở. Để tạo ra một Project mới, từ thanh menu chọn Project>New µVision Project… sau đó xác định thư mục nơi sẽ lưu project và đặt tên cho project. Chẳng hạn, trong ví dụ minh họa này tôi đặt tên Project như sau: C:\FRDM_KL46Z\Lab1\Lab1.uvproj. Nói chung nên đặt cho mỗi project một tên riêng gần với chức năng của project đó để dễ quản lý. Bấm Save để chuyển sang bước tiếp theo.
Hình B – 1. Tạo ra một project mới.
Tiếp đến chúng ta cần xác định tên bộ vi điều khiển sẽ được dùng để thực thi chương trình trong project. Trong trường hợp này chúng ta chúng ta sẽ chọn tên vi điều khiển là MKL46Z256VLL4. Vi điều khiển này có thể được tìm thấy trong đường dẫn NXP → MKL46Z4 → MKL46Z256xxx4 → MKL46Z256VLL4 như chỉ ra trong Hình B – 2. Bấm OK để tiếp tục.
Hình B – 2. Xác định bộ vi điều khiển được sử dụng trong project.
Trong hộp thoại Manage Run-Time Enviroment thêm vào project các thành phần phần mềm ::CMSIS:CORE và ::DEVICE:Startup bằng cách đánh dấu vào các checkbox như chỉ ra trong Hình B – 3. Các thành phần này cung cấp các tệp trung tâm sau:
- Tệp startup_<device>.s khai báo trình điều khiển ngắt reset handler và các vector ngoại lệ
- Tệp cấu hình system_<device>.c thiết lập chế độ làm việc các thiết bị cơ bản (chẳng hạn như nguồn phát xung nhịp, bộ nhớ, BUS, ..).
- Tệp <device>.h bao gồm các khai báo thanh ghi, hằng số, … cho phép mã nguồn của người truy xuất tới cấu trúc phần cứng của vi điều khiển.
Tệp tiêu đề <device> .h cần phải được bao gồm (include) trong các tệp mã nguồn C của người sử dụng để định nghĩa:
- Truy cập ngoại vi với thanh ghi chuẩn hóa.
- Truy cập vào các ngắt và ngoại lệ và bộ điều khiển véc tơ lồng nhau (NVIC).
- Chức năng nội tại để tạo các lệnh đặc biệt, chẳng hạn để kích hoạt chế độ ngủ.
- Chức năng Systick Timer (SYSTICK) để định cấu hình và bắt đầu ngắt timer định kỳ.
Hình B – 3. Thêm các thành phần phần mềm vào project.
Nhấp chuột vào nút “OK” để kết thúc quá trình tạo project. Nếu các bước khởi tạo ở trên được thực hiện đúng thì cửa sổ project sẽ xuất hiện giống như trong Hình B – 4 với các tệp tin của hai thành phần mềm CMSIS:CORE và DEVICE:Startup đã được thêm vào project.
Hình B – 4. Cửa sổ project.
Tiếp theo chúng ta sẽ cần thêm một tập tin c vào project. Để thực hiện việc này, nhấp chuột phải vào thư mục Source Group 1 và chọn Add New Item to Group ‘Source Group 1’…. Điều này sẽ cho phép chúng ta tạo một tệp mới để đưa vào project. Vì tệp này được dự định chứa mã nguồn chương trình, nó cần phải là tệp C. Chọn C file (.c), sau đó đặt tên cho nó, chẳng hạn tôi sẽ đặt tên tệp là main.c như thể hiện trong Hình B – 6.
Hình B – 5. Thêm một hạng mục mới tới project.
Hình B – 7
Để kích hoạt các thành phần của MCU, chúng ta sẽ thao tác trên các thanh ghi cơ sở của nó. Để giúp thực hiện điều này, nhấp chuột phải vào cửa sổ mã và chọn Insert ‘#include <MKL46Z4.H>. Tệp tiêu đề này chứa tên các thanh ghi và “mặt nạ” cho phép việc các thao tác trên các thanh ghi đó đỡ rối hơn. Nếu bạn muốn xem toàn bộ tệp, nhấp chuột phải vào <MKL46Z4.H> và chọn Open Document <MKL46Z4.H>. Đối với hầu hết các phần, các mặt nạ được đặt tên dựa trên tài liệu tham khảo hướng dẫn sử dụng chip KL46 (tài liệu [14]) theo định dạng ‘CHAPTER NAME’_’REGISTER NAME’_’BIT NAME’_MASK, tất cả đều ở dạng viết hoa. Một ví dụ về việc sử dụng mặt nạ để sửa đổi một thanh ghi được chỉ ra trong Ví dụ B – 1 dưới đây. Có nhiều cách để làm điều này cả có và không có mặt nạ, tôi sẽ chỉ ra một vài cách và giải thích tại sao một số có thể tốt hơn những cách khác.
Ví dụ B – 1: Thao tác với các bit của thanh ghi |
/*In order to allow PortA to be used we must first enable the clock to it. The PortA clock enable is bit 9 of SIM_SCGC5. All of the following lines of code do this, but it is much easier to see what is happening with some of them.*/ SIM->SCGC5 |= SIM_SCGC5_PORTA_MASK; /*MKL46Z4.h defines SIM_SCGC5_PORTA_MASK as 0x200u, or 1 left shifted by 9. Using this line makes it clear that we are enabling PORTA as we are setting the PORTA mask.*/ SIM->SCGC5 |= (1u << SIM_SCGC5_PORTA_SHIFT); /*This is very similar to the previous line, but it defines SIM_SCGC_PORTA_SHIFT as 9, or the number of the bit in the SIM_SCGC5 register.*/ SIM->SCGC5 |= (1u << 9); /*This one is harder to follow. All we know is that we are setting the 9th bit of SIM_SCGC5, and an outside reader (or possibly you when you proofread your code) will have a hard time discerning what this line means.*/ SIM->SCGC5 |= (0x200u); /*This works, but it doesn't even tell what bit you're setting immediately. It's hex, so it's in groups of 4 bits, which is its one saving grace.*/ SIM->SCGC5 |= (512u); /*This also works, but makes no sense. Using a decimal number to describe which bit to set is unintuitive and almost impossible to read unless you're the author or the code is very well commented (at which point it's easier to just use the mask)*/
Lưu ý việc sử dụng lệnh gán “| =” trong ví dụ trên. Lệnh này thực hiện lấy đối số đầu tiên và tiến hành phép tính logic OR với đối số thứ hai sau đó trả kết quả về đối số thứ nhất. Đây là một lệnh hữu ích được sử dụng để thiết lập một số bit trong một thanh ghi mà không thay đổi các bit khác.
Mặt nạ được sử dụng trong các lệnh ở dòng 4 và 8 có thể hữu ích vì nhiều lý do. Đầu tiên (như trong ví dụ) là khả năng đọc. Nó cho phép người đọc nhanh chóng hiểu những gì đang xảy ra. Bằng cách sử dụng sơ đồ đặt tên theo quy chuẩn, người đọc có thể thấy thanh ghi nào và bit nào trong thanh ghi đó đang được thiết lập. Một lý do thứ hai là tính di động. Giả sử mã C được viết hoàn hảo và người lập trình muốn sử dụng lại nó khi chuyển sang thiết bị khác, điều này có thể được thực hiện đơn giản bằng cách thay đổi tệp tiêu đề (header) được bao gồm (include) trong tệp mã ngưồn.
3. Viết chương trình ứng dụng
Bây giờ chúng ta có thể viết các chương trình để thực hiện điều khiển nhấp nháy LED xanh trên Kit FRDM-KL46Z. Chúng ta bắt đầu bằng viết một hàm khởi tạo các chân cổng PTD5 đường dùng để điều khiển LED xanh như trong Ví dụ B – 2.
Ví dụ B – 2: Cấu hình cho các chân điều khiển LED
void InitLED(void){ /*This function enables the red LED on the FRDM-KL46Z development board*/ /*Note, each chapter from the KL46 Reference Manual is a structure, while each register is a member of that structure, so we address them by Chapter -> Register*/ SIM->SCGC5 |= SIM_SCGC5_PORTD_MASK; //This enables the clock to PORTD PORTD->PCR[5] = PORT_PCR_MUX(1u); //This sets the Mux control of PTD5 to 001, or GPIO PTD->PDDR |= (1u<<5); /*This sets PTD5 as an output. There are no masks defined for each pin so we have to do it by hand and just comment well.*/ }
Trong mã trên SIM (System Integration Module) là mô-đun tích hợp hệ thống cho phép điều khiển cấp xung nhịp tới một thiết bị ngoại nào đó trên MCU. Bằng cách ngắt cấp xung nhịp tới một ngoại vi sẽ giúp MCU tiết giảm năng lượng tiêu thụ nhưng lại khiến ngoại vi không thể hoạt động. Trong mô-đun SIM, thanh ghi điều khiển xung nhịp hệ thống số (System Clock Gating Control Register) 5 ký hiệu là SIM_SCGC5 và được truy xuất tới trong Keil dưới định dạng SIM->SCGC5. Thanh ghi này giữ chức năng kích hoạt tín hiệu xung nhịp cho các cổng khác nhau trên MCU. ĐỂ SỬ DỤNG BẤT KỲ CHÂN NÀO TRÊN MỘT CẢNG THÌ CẢNG ĐÓ PHẢI ĐƯỢC KÍCH HOẠT TÍN HIỆU XUNG NHỊP. Ví dụ, để sử dụng đèn LED màu xanh trên chân PTD5, trước tiên chúng ta phải bật tín hiệu xung nhịp cho Cổng D trong thanh ghi SCGC5.
Thanh ghi điều khiển cổng PCR (Pin Control Register) được sử dụng để xác định một chân cổng được sử dụng cho thiết bị ngoại vi nào trên MCU. Thanh ghi này được truy xuất bằng cấu trúc PORTx->PCR[n] trong phần mềm Keil. Ví dụ, chúng ta cần điều khiển đèn LED màu xanh trên bo mạch FRDM-KL46, vì vậy chúng ta phải truy xuất thanh ghi PCR số 5 trong mảng thanh ghi PCR của cổng PORTD bằng cấu trúc như được chỉ ra trong lệnh trong dòng 7 của Ví dụ B – 2.
Ngay sau khi một chân cổng được thiết lập kết nối với một khối GPIO thì thanh ghi hướng dữ liệu cổng GPIO_PDDR (Port Data Direction Register) (được truy xuất tới trong Keil bằng cấu trúc PTx->PDDR) được dùng để cấu hình khối GPIO hoạt động ở chế độ đầu vào hay đầu ra. Số thứ tự của chân tương ứng với số thứ tự bit trong thanh ghi. Giá trị 0 trong một bit của PDDR sẽ cấu hình chân cổng tương ứng là đầu vào và giá trị 1 cấu hình chân là đầu ra. Vì vậy để cấu hình chân PTD5 làm đầu ra, chúng ta phải đặt bit số 5 của PTD->PDDR thành 1.
Bây giờ chúng ta viết các hàm điều khiển nháy LED. Điều này có thể được thực hiện theo một trong ba cách như sẽ được trình bày trong Ví dụ B – 3, Ví dụ B – 4 và Ví dụ B – 5 dưới đây.
Ví dụ B – 3: Điều khiển nháy LED thông qua cặp thanh ghi PSOR và PCOR
void BlinkLED1(void){ //This method turns the LED off, then back on using two separate commands. uint32_t i = 0; //Create a loop variable PTD->PSOR = (1u<<5); //Set PTD5 = 1, turns LED OFF (Cathode connected to PTD5) for(i=0; i < 3000000; i++){}; //Burn some time PTD->PCOR = (1u<<5); //Clear PTD5 = 0, turns LED ON for(i=0; i < 3000000; i++){}; //Burn some time }
Thanh ghi đặt dữ liệu cổng GPIO_PSOR (Port Data Set Register) – Thanh ghi này sẽ đặt đầu ra của một chân lên 1. Ghi giá trị 0 vào thanh ghi PSOR sẽ không thay đổi giá trị hiện tại của chân, trong khi giá trị 1 trong một bit của thanh ghi PSOR sẽ đặt chân tương ứng thành 1.
Thanh ghi xóa dữ liệu cổng GPIO_PCOR (Port Data Clear Register) – Thanh ghi này sẽ xóa đầu ra của một chân về 0. Ghi giá trị 0 vào thanh ghi PCOR sẽ không thay đổi giá trị hiện tại của chân, trong khi giá trị 1 trong một bit của thanh ghi PSOR sẽ đặt chân tương ứng thành 0.
Ví dụ B – 4: Điều khiển nháy LED thông qua thanh ghi PDOR
void BlinkLED2(void){ //This method turns the LED off, then back on using PDOR /*Note the use of |= and &=. This allows you to change the 5th bit of PDOR without disturbing the other bits. The other methods do not require this as setting PSOR/PCOR/PTOR = 0 does not change the output.*/ uint32_t i = 0; //Create a loop variable PTD->PDOR |= (1u<<5); //Change PTD5 to 1, turns LED OFF (Cathode connected to PTD5) for(i=0; i < 3000000; i++){}; //Burn some time PTD->PDOR &= ~((uint32_t)(1u<<5)); /*Change PTD5 to 0 to turns LED ON. This line is a bit weird. First it takes 1u<<5 and typecasts it to a 32 bit value. It then takes the inverse of it, so all bits are 1 except bit 5. It then logical ands it with the current PDOR, which will retain all values except bit 5 which will be changed to 0.*/ for(i=0; i < 3000000; i++){}; //Burn some time }
Thanh ghi đầu ra dữ liệu cổng GPIO_PDOR (Port Data Output Register) được dùng để sẽ thay đổi đầu ra của một chân tới một giá trị đã cho. Vì vậy, ghi giá trị 0 tới thanh ghi này sẽ thay đổi đầu ra trên chân cổng thành 0, trong khi ghi giá trị 1 tới thanh ghi này sẽ thay đổi đầu ra trên chân cổng thành 1.
Ví dụ B – 5: Điều khiển nháy LED thông qua thanh ghi PTOR
void BlinkLED3(void){ /*This method turns the LED off, then back on using a single command. It must be run twice in order to blink the LED.*/ uint32_t i = 0; //Create a loop variable PTD->PTOR = (1u<<5); //Toggles PTD5 and the LED for(i=0; i < 3000000; i++){}; //Burn some time }
Thanh ghi đảo dữ liệu cổng GPIO_PTOR (Port Data Toggle Register) – Thanh ghi này sẽ gây ra giá trị trên đầu ra của một chân đảo giá trị so với giá trị hiện tại. Ghi giá trị 0 tới thanh ghi này sẽ không làm thay đổi giá trị hiện tại của chân, trong khi một bit có giá trị 1 sẽ thay đổi trạng thái của chân tương ứng thành nghịch đảo của trạng thái hiện tại.
Cuối cùng chúng ta viết cho mã cho hàm main() như chỉ ra trong Ví dụ B – 6. Chúng ta tạo ra giữ mọi thứ trong một vòng lặp để đảm bảo rằng chúng tôi không bao giờ rời khỏi chính vì điều đó có thể gây ra hành vi không xác định.
Ví dụ B – 6: Hàm Main() |
int main(void){ InitLED(); //Initialize LED for use while(1){ //Main loop BlinkLED3(); } //End main loop } //End main
4. Biên dịch và nạp chương trình vào kit
Hình B – 8. Project sau khi thêm vào các tệp mã nguồn.
Để lập trình MCU trên bo mạch FRDM-KL46Z chúng ta cần thiết lập một số tùy chọn. Để thực hiện điều này nhấp chuột phải vào thư mục “Target” trong cửa sổ Project và chọn “Options for Target ‘Target 1’…”. Trong hộp thoại “Options for Target ‘Target 1’…” chuyển đến tab Debug và chọn “PEMicro Debuger” như thể hiện trong Hình B – 9 và bấm nút “Setting” bên cạnh để cấu hình cho Debuger.
Hình B – 9. Hộp thoại chọn trình debugger.
Trong hộp thoại “P&E Connection Manager” cấu hình cho trình Debugger như thể hiện trong Hình B – 10.
Hình B – 10. Hộp thoại thiết lập cấu hình cho trình Debugger.
Để biên dịch chương trình nhấp chuột vào nút “Rebuild” trên thanh toolbar. Đợi cho quá trình biên dịch kết thúc thì bấm nút “Download” trên thanh toolbar để nạp chương trình vào MCU trên bo mạch. Sau khi quá trình nạp kết thúc, bấm nut Reset trên bo mạch, nếu mọi bước ở trên thực hiện đúng sẽ thấy đèn LED màu xanh trên bo mạch FRMD-KL46Z nhấp nháy.
Hình B – 11. Biên dịch và nạp chương trình.
Link video hướng dẫn sử dụng phần mềm Keil MDK-ARM để tạo project, biên dịch, và nạp chương trình ứng dụng vào bo mạch FRDM-KL46Z ở đây.
Link download các tệp mã nguồn ở đây.
Bạn có thể mua bo mạch FRDM-KL46Z trên trang thương mại điện tử Lazada ở đây.
Video hướng dẫn sử dụng phần mềm Keil MDK-ARM:
Thưa thầy của em không có NXP mà chỉ có của ARM thôi ạ
À do em chưa cài các gói của nó ạ. Giờ em làm được rồi
Thầy có thể hướng dẫn thêm phần tạo project trong linux, biên dịch mã nguồn bằng GNU make được không ạ!
Em tự tìm cách cài đặt phần mềm Keil MDK-ARM trong Linux, sau đó cách tạo project giống như trong Windows