Skip to main content
Chien-Jung Li
Nov. 2013
MCS-51 基礎實習
使用IAR Embedded Workbench (III)
Lab11: Real-Time Clock晶片
2
DS1302 RTC介紹
3
 DS1302是美國DALLAS公司生產的即時時鐘電路(Real-
time clock, RTC),它可對年、月、日、周日、時、分、
秒進行計時並具有閏年補償功能,而且也有24小時制
與12小時制(AM/PM)的選擇。
 工作電壓為 2.0V~5.5V,採用三線通訊介面(3-wire
interface)。內部具有31 Bytes的SRAM。
 DS1302具有主電源/備用電源的雙電源引腳(VCC1為後備
電源,VCC2為主電源,由VCC1 或VCC2 兩者中的較大者
供電),同時提供對備用電源進行涓流充電的能力。
 X1和X2外接32.768 kHz的石英振盪器。RST是重置/晶片
選擇腳,對RST輸入Low重置晶片而將RST設為High可啟
動所有資料傳送。
DS1302 RTC的RST/CE接腳功能
4
 RST/CE輸入有兩種功能:
 RST接至控制邏輯,允許位址/命令序列送入移位暫存器
 RST提供終止資料傳送。當RST為高電壓時,所有的數據傳送被初始化,
允許對DS1302進行操作。如果在傳送過程中RST置為低電壓,則會終止
此次資料傳送,I/O引腳變為高阻態。
 啟動運行時,在VCC>2.0V之前,RST必須保持低電位。只有在SCLK為低
電位時,才能將RST置為高電位。I/O為串列資料輸出端(雙向)。SCLK為
時鐘輸入端。
40 kΩ pull-down
DS1302之指令 – Command Byte
5
 Command Byte用於初始化每次的資料傳輸,bit-0必
定為input staring。
3-Wire通訊介面的讀寫
6
Burst mode: command byte bit1~5 = 11111’b = 31(decimal)
Reads or writes in burst mode start with bit 0 of address 0.
RTC內部暫存器(儲存時間資訊)
7
Trickle Charger (涓流充電)
8
Timing Diagram
9
實習11
10
 目標:製作一個電子時鐘,將日期與時間顯示在LCD上。
這是我們第一次要同時控制兩個較複雜的裝置,它們
的驅動程式如果寫在一個.c檔,程式檔會變很龐大。現
在我們要開始學習如何管理程式碼,這是進入商用產
品開發的第一站。
 DS1302 RTC使用3-wire介面與MCU通訊
 LCD使用LCD1602A的訊號介面與MCU通訊
 練習1: 結構化你的程式碼
 練習2: 加入LCD的驅動程式
 練習3: 練習撰寫API手冊(以LCD驅動程式為例)
 練習4: 加入3-wire通訊介面與DS1302的驅動程式
 練習5: 完成電子時鐘
設計模式與驅動程式
11
 在開始談論如何結構化程式碼之前,我們先來討論
幾種跟硬體相關的嵌入式程式設計模式。
 所講到的觀念,以後會慢慢應用在我們的設計專案
當中(有些觀念其實你已經不知不覺接觸很久了)。
Adapter(或Wrapper)模式
12
 Adapter是十分傳統的設計模式,它將一個物件的介面
轉換成另一個用戶(高階模組)較易使用的介面。
 通常,adapter覆蓋在軟體API之上以蓋掉醜陋的介面。
閃爍LED燈 – 最簡單的寫法
13
#include<ioAT89C52.h>
typedef unsigned char uint8;
void delayms(uint8);
void main(void)
{
P1_bit.P1_0 = 1;
while(1)
{
P1_bit.P1_0 = 0;
delayms(250);
P1_bit.P1_0 = 1;
delayms(250);
}
}
#include<ioAT89C52.h>
#define LED0 P1_bit.P1_0
typedef unsigned char uint8;
void delayms(uint8);
void main(void)
{
LED0 = 1;
while(1)
{
LED0 = 0;
delayms(250);
LED0 = 1;
delayms(250);
}
}
閃爍LED燈 – 使用機板標頭檔
14
// ioMapping.h
#define LED_REGISTER P0
#define LED_BIT (1 << 0)
#include<ioAT89C52.h>
#include "ioMapping.h"
typedef unsigned char uint8;
void delayms(uint8);
void main(void)
{
LED_REGISTER |= LED_BIT;
while(1)
{
LED_REGISTER &= ~LED_BIT;
delayms(250);
LED_REGISTER |= LED_BIT;
delayms(250);
}
}
閃爍LED燈 – 通用機板標頭檔
15
#include<ioAT89C52.h>
#include "ioMapping.h"
typedef unsigned char uint8;
void delayms(uint8);
void main(void)
{
LED_REGISTER |= LED_BIT;
while(1)
{
LED_REGISTER &= ~LED_BIT;
delayms(250);
LED_REGISTER |= LED_BIT;
delayms(250);
}
}
// ioMapping.h
#if COMPILING_BOARD_V1
#include "ioMapping_v1.h"
#elif COMPILING_BOARD_V2
#include "ioMapping_v2.h"
#else
#error "No I/O map selected for the board"
#endif
// ioMapping_v1.h
#define LED_REGISTER P0
#define LED_BIT (1 << 0)
// ioMapping_v2.h
#define LED_REGISTER P3
#define LED_BIT (1 << 4)
I/O處理程式碼
16
閃爍LED燈 – Facade模式
17
 LED的I/O子系統介面與內容,就是LED的驅動程式
 隱藏子系統的細節是良好設計中十分重要的一環,
呼叫端的程式不會依賴子系統的細節
Main
加上按鈕
18
設定中斷
19
 雖然為腳位設定中斷與設定輸入都是屬於初始化,但
這兩件事情通常會將實作分離,只需要在使用中斷的
程式碼中包含設定中斷的複雜度。
 三個中斷處理函式(有時會用巨集來做)
競賽情況(Race Condition)
 在工作間共享記憶體十分危險,必須十分小心。
20
避免競賽情況 – 使用mutex
21
 任何工作間共享的記憶體不論是讀取或寫入,都要在
程式中建立臨界區域(critical section),表示正在存取共
享資源(記憶體或設備),必須保護共享資源,同一時間
只能讓一個工作修改,稱為互斥(mutual exclusion),簡
寫為mutex。
 對於包含OS的系統,當兩個非中斷工作同時執行時,
能夠透過mutex表示兩個工作共享相同的資源,只需要
簡單地透過變數表示資源(或全域變數)可以供其他工作
使用就行了。
 當其中之一是中斷時,資源所有權的改變必須是單元
動作(atomic),單元動作是指無法被系統其他部分中斷
的動作。
結構化你的程式碼: Source Tree
22
 Source Tree:
擺放原始碼檔案的目錄結構,基本原則是一個目錄可以對應到系
統架構中的一個方塊。
 還記得我們在學C語言時,可以藉由撰寫Makefile來幫
助我們同時編譯多個原始碼嗎?
 在開發系統時,你必須告訴寫Makefile的工程師那些要
編譯的檔案放在哪裡,source tree結構規範就是在告訴
工程師這件事。
 寫Makefile這件事情有點麻煩,幸好我們有IDE開發環境
可以使用,我們只要將各種原始碼檔案分門別類放好,
再告訴IDE這些東西的Path在哪裡,剩下的就交給它吧!
常見目錄名稱的意思
23
• /Driver: 驅動程式目錄 (有時也會用/hal)
/Boot-Loader: 開機程式
/Hardware: 各種硬體裝置的驅動程式
/Include: 驅動程式的header files
/API: 將所有驅動程式包裝成API
/Boot: Boot程式
/LCD: LCD程式
/SDRAM: SDRAM驅動程式
• /System: 系統程式
/Include: 系統程式的header files
/API: 將所有系統程式包裝成API
/Common: 系統中的通用功能
/RTOS: 嵌入式OS的目錄
/Include: RTOS的header
/Memory: 記憶體管理
/Sync: Task間同步機制
/IPC: Task間通訊機制
/Glib: 圖形函式庫
/Sub-System:各種子系統
/GUI: 圖形用戶介面
/TCPIP: TCPIP通訊堆疊
/FileSystem: 檔案系統
• /AP: 應用程式 (或/APP)
/Include: 應用程式的header files
/Source: 應用程式的原始碼
/Common: 應用程式的通用功能
/AP1: 應用程式1
/AP2: 應用程式2
/Resource: 應用程式的圖形、字串資源
• /Third_Party_Lib: 第三方函式庫
/HandWriting: 手寫辨識函式庫
/Fonts: 字型
/VoiceComp: 聲音壓縮函式庫
• /Include: header, sys_config.h(系統配置檔)
• /Build: 製作映像檔(執行檔)的東西,
如makefile跟link script
• /Tools: 開發中所需的程式,例如compiler
• /Documents: 開發規範、datasheet、user guide、
Spec, API手冊等
練習1: 結構化你的程式碼
24
 在D槽新建一個目錄結構
 D:MySimpSystem
在IAR中New一個Worksapce
25
環境設定
26
New一個應用程式File
27
建立Groups
28
完成以下的Groups
29
先把幾個Header File歸入Group
30
D:MySimpSystemhaltargetJC51B
D:MySimpSystemhalinclude
hal_types.h
/*****************************************************
Filename: hal_types.h
Revised: $Date: 2013-10-18 15:20 $
Revision: $Revision: $
Description: Some useful typedef and definitions
******************************************************/
#ifndef _HAL_TYPES_H
#define _HAL_TYPES_H
/* Types */
typedef signed char int8;
typedef unsigned char uint8;
typedef signed short int16;
typedef unsigned short uint16;
typedef signed long int32;
typedef unsigned long uint32;
typedef unsigned char bool;
/* Standard Defines */
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef NULL
#define NULL 0
#endif
#ifndef HIGH
#define HIGH 1
#endif
#ifndef LOW
#define LOW 0
#endif
/* Memory Attributes */
/* ----------- IAR Compiler ----------- */
#ifdef __IAR_SYSTEMS_ICC__
#define CODE __code
#define XDATA __xdata
/* ----------- GNU Compiler ----------- */
#elif defined __KEIL__
#define CODE code
#define XDATA xdata
/* ------- Unrecognized Compiler ------ */
#else
#error "ERROR: Unknown compiler."
#endif
/******************************************
*/
#endif
31
D:MySimpSystemhaltargetJC51B
hal_mcu.h
32
/**************************************************************
Filename: hal_mcu.h
Revised: $Date: 2013-10-18 $
Revision: $Revision: $
Description: Describe the purpose and contents of the file.
****************************************************************/
#ifndef _HAL_MCU_H
#define _HAL_MCU_H
/* Target : AT89C52 (8051 core) */
/* Includes */
#include "hal_defs.h"
#include "hal_types.h"
/* Target Defines */
#define HAL_MCU_AT89C52
/* Compiler Abstraction */
/* ---------------------- IAR Compiler ---------------------- */
#ifdef __IAR_SYSTEMS_ICC__
#include <ioAT89C52.h>
#define HAL_COMPILER_IAR
#define HAL_MCU_LITTLE_ENDIAN() __LITTLE_ENDIAN__
#define _PRAGMA(x) _Pragma(#x)
#define HAL_ISR_FUNC_DECLARATION(f,v) _PRAGMA(vector=v) __near_func __interrupt void f(void)
#define HAL_ISR_FUNC_PROTOTYPE(f,v) _PRAGMA(vector=v) __near_func __interrupt void f(void)
#define HAL_ISR_FUNCTION(f,v) HAL_ISR_FUNC_PROTOTYPE(f,v); HAL_ISR_FUNC_DECLARATION(f,v)
/* ---------------------- Keil Compiler ---------------------- */
#elif defined __KEIL__
#include <reg51.h>
D:MySimpSystemhaltargetJC51B
#define HAL_COMPILER_KEIL
#define HAL_MCU_LITTLE_ENDIAN() 0
#define HAL_ISR_FUNC_DECLARATION(f,v) 
void f(void) interrupt v
#define HAL_ISR_FUNC_PROTOTYPE(f,v) 
void f(void)
#define HAL_ISR_FUNCTION(f,v) 
HAL_ISR_FUNC_PROTOTYPE(f,v); 
HAL_ISR_FUNC_DECLARATION(f,v)
/* ------------ Unrecognized Compiler ---------- */
#else
#error "ERROR: Unknown compiler."
#endif
#pragma vector = extern0
__interrupt void Int_Extern0(void)
33
/* Interrupt Macros */
#define HAL_ENABLE_INTERRUPTS() st( IE_bit.EA = 1; )
#define HAL_DISABLE_INTERRUPTS() st( IE_bit.EA = 0; )
#define HAL_INTERRUPTS_ARE_ENABLED() (IE_bit.EA)
typedef unsigned char halIntState_t;
#define HAL_ENTER_CRITICAL_SECTION(x) st( x = IE_bit.EA; HAL_DISABLE_INTERRUPTS(); )
#define HAL_EXIT_CRITICAL_SECTION(x) st( IE_bit.EA = x; )
#define HAL_CRITICAL_STATEMENT(x) st( halIntState_t _s; HAL_ENTER_CRITICAL_SECTION(_s); 
x; HAL_EXIT_CRITICAL_SECTION(_s); )
#ifdef __IAR_SYSTEMS_ICC__
/* This workaround should only be used with 8051 using IAR compiler. When IAR fixes the problem
* of XCH instruction with EA, compile the following macros to null to disable them.
*/
#define HAL_ENTER_ISR() { halIntState_t _isrIntState = EA; HAL_ENABLE_INTERRUPTS();
#define HAL_EXIT_ISR() IE_bit.EA = _isrIntState; }
#else
#define HAL_ENTER_ISR()
#define HAL_EXIT_ISR()
#endif /* __IAR_SYSTEMS_ICC__ */
/**************************************************************************************************
*/
#endif
hal_board.h & hal_board_cfg.h
34
D:MySimpSystemhalinclude
D:MySimpSystemhaltargetJC51B
#include "hal_board_cfg.h"
/*********************************************
Filename: hal_board_cfg.h
Revised: $Date: 2013-10-18 $
Revision: $Revision: $
Description: Declarations for the
JC51B Development Board.
**********************************************/
#ifndef HAL_BOARD_CFG_H
#define HAL_BOARD_CFG_H
/* ------------------------------------------
* Includes
* ------------------------------------------
*/
#include "hal_mcu.h"
#include "hal_defs.h"
#include "hal_types.h"
#endif
/*********************************************
*/
hal_defs.h
35
/*****************************************************************
Filename: hal_defs.h
Revised: $Date: 2013-10-18 14:48 $
Revision: $Revision: $
Description: This file contains useful macros and data types
******************************************************************/
#ifndef HAL_DEFS_H
#define HAL_DEFS_H
/* Macros */
#ifndef BV
#define BV(n) (1 << (n))
#endif
/* takes a byte out of a uint32 : var - uint32,
ByteNum - byte to take out (0 - 3) */
#define BREAK_UINT32( var, ByteNum ) 
(uint8)((uint32)(((var) >>((ByteNum) * 8)) & 0x00FF))
#define BUILD_UINT32(Byte0, Byte1, Byte2, Byte3)
((uint32)((uint32)((Byte0) & 0x00FF) 
+ ((uint32)((Byte1) & 0x00FF) << 8) 
+ ((uint32)((Byte2) & 0x00FF) << 16) 
+ ((uint32)((Byte3) & 0x00FF) << 24)))
#define BUILD_UINT16(loByte, hiByte) 
((uint16)(((loByte) & 0x00FF) + (((hiByte) & 0x00FF) << 8)))
#define HI_UINT16(a) (((a) >> 8) & 0xFF)
#define LO_UINT16(a) ((a) & 0xFF)
/* This macro is for use by other macros to form a fully
valid C statement. */
#define st(x) do { x } while (__LINE__ == -1)
/**********************************************************
*/
#endif
D:MySimpSystemhalinclude
hal_drivers.h
36
/****************************************************
Filename: hal_drivers.h
Revised: $Date: 2013-10-18 14:52 $
Revision: $Revision: $
Description: This file contains the interface
to the Drivers service.
*****************************************************/
#ifndef HAL_DRIVER_H
#define HAL_DRIVER_H
/* Initialize HW */
extern void HalDriverInit (void);
/****************************************
****************************************/
#endif
D:MySimpSystemhalinclude
練習2: 加入LCD的驅動程式
#include<ioAT89C52.h>
#define BTN1 P3_bit.P3_2
#define BTN2 P3_bit.P3_3
#define LCD_RW P1_bit.P1_1
#define LCD_EN P3_bit.P3_4
#define LCD_RS P3_bit.P3_5
#define LCD_BF P0_bit.P0_7
#define LCD_DATA_PORT P0
#define LCD_SEL_CMD 0
#define LCD_SEL_DATA 1
#define LCD_IO_WRITE 0
#define LCD_IO_READ 1
typedef unsigned char uint8;
D:MySimpSystemhalinclude
/* HD44780 Commands */
#define LCD_CMD_CLS 0x01 // Clear display (also DDRAM)
#define LCD_CMD_FNCT_1 0x30 // 8-bits, 1 line
#define LCD_CMD_FNCT_2 0x38 // 8-bits, 2 line
#define LCD_CMD_FNCT_3 0x20 // 4-bits, 1 line
#define LCD_CMD_FNCT_4 0x28 // 4-bits, 2 line
#define LCD_CMD_ENTRY_MODE 0x06 // Entry mode
#define LCD_CMD_DON_COFF 0x0C // LCD ON, Cursor OFF, Blink OFF
#define LCD_CMD_DON_CON 0x0E // LCD ON, Cursor ON, Blink OFF
#define LCD_CMD_DON_CON_BLN 0x0F // LCD ON, Cursor ON, Blink ON
#define LCD_CMD_SHIFT_LEFT 0x18 // Shift entire display left
#define LCD_CMD_SHIFT_RIGHT 0x1C // Shift entire display right
#define LCD_CMD_CMOVE_LEFT 0x10 // Cursor move left by one char
#define LCD_CMD_CMOVE_RIGHT 0x14 // Cursor move right by one char
/* DDRAM and CGRAM Initial Address */
#define LCD_DDRAM_ADDR0 0x80
#define LCD_DDRAM_ADDR1 0xC0
#define LCD_CGRAM_ADDR0 0x40
37
38
static void LCD_CursorSet(uint8 row, uint8 col);
void LCD_DataWr(uint8 data);
void LCD_CmdWr(uint8 cmd);
extern void LCD_Init(uint8 maxrows, uint8 maxcols);
extern void LCD_DispChar(uint8 row, uint8 col, char c);
extern void LCD_DispStr(uint8 row, uint8 col, char *s);
extern void LCD_ClrLine(uint8 line);
extern void LCD_ClrScr(void);
extern void LCD_DefChar(uint8 id, uint8 *pat);
extern void LCD_DispHorBarInit(void);
extern void LCD_DispHorBar(uint8 row, uint8 col, uint8 val);
void delayms(uint8 time);
39
D:MySimpSystemhaltargetJC51B
/*********************************************
Filename: hal_lcd.c
Revised: $Date: 2013-10-19 16:21 $
Revision: $Revision: $
Description:
*********************************************/
/******* INCLUDES *******/
#include "hal_board_cfg.h"
#include "hal_defs.h"
#include "hal_types.h"
#include "hal_lcd.h"
/******* CONSTANTS ******/
#define LCD_CMD_FNCT LCD_CMD_FNCT_2 // 8-bits, 2 line
#if (HAL_LCD == TRUE)
/******* LOCAL VARIABLES *********/
char StrL1[]="LCD 1602 Test";
char StrL2[]="Start LCD OK!";
static uint8 LCD_MaxCols;
static uint8 LCD_MaxRows;
// Patterns of horizontal bar
static uint8 LCD_DispBar1[] = {0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10};
static uint8 LCD_DispBar2[] = {0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18};
static uint8 LCD_DispBar3[] = {0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C};
static uint8 LCD_DispBar4[] = {0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E};
static uint8 LCD_DispBar5[] = {0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F};
/******* FUNCTIONS – API ********/
/**** LOCAL FUNCTIONS *****/
static void LCD_BusyCheck(void);
static void LCD_DataWr(uint8 data);
static void LCD_CmdWr(uint8 cmd);
static void LCD_CursorSet(uint8 row, uint8 col);
#endif
#if (HAL_LCD == TRUE)
…
#endif
40
/*****************************************************************
* @fn LCD_Init
* @brief LCD initialization
* @param maxrows: max line number, maxcols: max word numbers
* @return None
*****************************************************************/
void LCD_Init(uint8 maxrows, uint8 maxcols)
{
#if (HAL_LCD == TRUE)
LCD_MaxCols = maxcols;
LCD_MaxRows = maxrows;
delayms(30);
LCD_EN = LOW;
LCD_RS = LOW;
LCD_RW = LOW;
LCD_CmdWr(LCD_CMD_FNCT);
delayms(5);
LCD_CmdWr(LCD_CMD_FNCT);
delayms(5);
LCD_CmdWr(LCD_CMD_FNCT);
delayms(5);
LCD_CmdWr(LCD_CMD_FNCT);
LCD_CmdWr(LCD_CMD_DON_COFF);
LCD_CmdWr(LCD_CMD_ENTRY_MODE);
LCD_CmdWr(LCD_CMD_CLS);
delayms(2);
#endif
}
41
void LCD_DispChar(uint8 row, uint8 col, char c)
{
#if (HAL_LCD == TRUE)
略
#endif
}
void LCD_DispStr(uint8 row, uint8 col, char *s)
{
#if (HAL_LCD == TRUE)
略
#endif
}
void LCD_ClrLine(uint8 line)
{
#if (HAL_LCD == TRUE)
略
#endif
}
void LCD_ClrScr(void)
{
#if (HAL_LCD == TRUE)
略
#endif
}
void LCD_DefChar(uint8 id, uint8 *pat)
{
#if (HAL_LCD == TRUE)
略
#endif
}
void LCD_DispHorBarInit(void)
{
#if (HAL_LCD == TRUE)
略
#endif
}
void LCD_DispHorBar(uint8 row, uint8 col, uint8 val)
{
#if (HAL_LCD == TRUE)
略
#endif
}
42
/******** LOCAL FUNCTIONS *************/
#if (HAL_LCD == TRUE)
/***********************************************
* @fn LCD_BusyCheck
* @brief Check if LCD is busy by Busy Flag (BF)
* @param data: the data to be written
* @return None
*************************************************/
void LCD_BusyCheck(void)
{
LCD_DATA_PORT = 0xFF;
LCD_RS = LCD_SEL_CMD;
LCD_RW = LCD_IO_READ;
LCD_EN = HIGH;
while(LCD_BF == HIGH);
LCD_EN = LOW;
}
static void LCD_DataWr(uint8 data)
{
LCD_BusyCheck();
LCD_RS = LCD_SEL_DATA;
LCD_RW = LCD_IO_WRITE;
LCD_DATA_PORT = data;
LCD_EN = HIGH;
asm("nop");
LCD_EN = LOW;
}
static void LCD_CmdWr(uint8 cmd)
{
LCD_BusyCheck();
LCD_RS = LCD_SEL_CMD;
LCD_RW = LCD_IO_WRITE;
LCD_DATA_PORT = cmd;
LCD_EN = HIGH;
asm("nop");
LCD_EN = LOW;
}
static void LCD_CursorSet(uint8 row, uint8 col)
{
略
}
#endif
將delayms()放到common driver
43
/*************************************************************************
Filename: hal_drivers.h
Revised: $Date: 2013-10-18 14:52 $
Revision: $Revision: $
Description: This file contains the interface to the Drivers service.
**************************************************************************/
#ifndef HAL_DRIVER_H
#define HAL_DRIVER_H
/*************************************************************************
* FUNCTIONS - API
*************************************************************************/
extern void delayms(uint8 time);
/*
* Initialize HW
*/
extern void HalDriverInit (void);
/*************************************************************************
*************************************************************************/
#endif
D:MySimpSystemhalinclude
44
D:MySimpSystemhalcommon
/****************************************************************************
Filename: hal_drivers.c
Revised: $Date: 20103-10-19 17:00 $
Revision: $Revision: $
Description: This file contains the common functions used by the driver
*****************************************************************************/
/******** INCLUDES *************/
#include "hal_types.h"
#include "hal_drivers.h"
#include "hal_lcd.h"
/******** FUNCTIONS – API **********/
/***************************************************
* @fn delayms
* @brief delay with ms
* @param time = 0 ~ 255, the maximum delay is 255 ms
* @return None
***************************************************/
void delayms(uint8 time)
{
uint8 n;
while(time>0)
{
n = 162;
while(n>0) n--;
time --;
}
}
/*****************************************************
* @fn HalDriverInit
* @brief Initialize HW
* @param None
* @return None
******************************************************/
void HalDriverInit (void)
{
#if (defined HAL_LCD) && (HAL_LCD == TRUE)
LCD_Init(2, 16);
#endif
}
修改hal_board_cfg.h
45
/***************************************************************
Filename: hal_board_cfg.h
Revised: $Date: 2013-10-19 $
Revision: $Revision: $
Description: Declarations for the JC51B Development Board.
***************************************************************/
#ifndef HAL_BOARD_CFG_H
#define HAL_BOARD_CFG_H
/* ------------- Includes ------------*/
#include "hal_mcu.h"
#include "hal_defs.h"
#include "hal_types.h"
#include "hal_lcd.h"
/* -------- Driver Configuration -------- */
/* Set to TRUE enable LCD usage, FALSE disable it */
#ifndef HAL_LCD
#define HAL_LCD TRUE
#endif
#endif
/**********************************************************************************
*/
準備加入按鍵驅動程式
46
 先修改一下hal_board_cfg.h的內容
/***************************************************************
Filename: hal_board_cfg.h
Revised: $Date: 2013-10-19 $
Revision: $Revision: $
Description: Declarations for the JC51B Development Board.
***************************************************************/
#ifndef HAL_BOARD_CFG_H
#define HAL_BOARD_CFG_H
/* ------------- Includes ------------*/
#include "hal_mcu.h"
#include "hal_defs.h"
#include "hal_types.h"
#include "hal_lcd.h"
#include "hal_key.h"
/* ---- Push Button Configuration ----*/
#define ACTIVE_LOW !
/* double negation forces result to be '1' */
#define ACTIVE_HIGH !!
/* BTN1 */
#define PUSH1_BV BV(2)
#define PUSH1_SBIT P3_bit.P3_2 // INT0
#define PUSH1_POLARITY ACTIVE_LOW
/* BTN2 */
#define PUSH2_BV BV(3)
#define PUSH2_SBIT P3_bit.P3_3 // INT1
#define PUSH2_POLARITY ACTIVE_LOW
/* BTN3 */
#define PUSH3_BV BV(4)
#define PUSH3_SBIT P3_bit.P3_4 // T0
#define PUSH3_POLARITY ACTIVE_LOW
/* BTN4 */
#define PUSH4_BV BV(5)
#define PUSH4_SBIT P3_bit.P3_5 // T1
#define PUSH4_POLARITY ACTIVE_LOW
47
/* ----- Macros ------*/
// ---- Debounce ---- //
#define HAL_DEBOUNCE(expr) { int i; for (i=0; i<500; i++) { if (!(expr)) i = 0; } }
// ---- Push Buttons ---- //
#define HAL_PUSH_BUTTON1() (PUSH1_POLARITY (PUSH1_SBIT))
#define HAL_PUSH_BUTTON2() (PUSH2_POLARITY (PUSH2_SBIT))
#define HAL_PUSH_BUTTON3() (PUSH3_POLARITY (PUSH3_SBIT))
#define HAL_PUSH_BUTTON4() (PUSH4_POLARITY (PUSH4_SBIT))
/* -------- Driver Configuration -------- */
/* Set to TRUE enable LCD usage, FALSE disable it */
#ifndef HAL_LCD
#define HAL_LCD TRUE
#endif
/* Set to TRUE enable KEY usage, FALSE disable it */
#ifndef HAL_KEY
#define HAL_KEY TRUE
#endif
#endif
/**********************************************************************************
*/
加入按鍵的驅動程式
48
/**********************************************************************
Filename: hal_key.h
Revised: $Date: 2013-10-19 17:51 $
Revision: $Revision: $
Description: This file contains the interface to the KEY Service.
***********************************************************************/
#ifndef HAL_KEY_H
#define HAL_KEY_H
/****** INCLUDES ******/
#include "hal_board.h"
/****** CONSTANTS *****/
/* Interrupt option - Enable or disable */
#define HAL_KEY_INTERRUPT_DISABLE 0x00
#define HAL_KEY_INTERRUPT_ENABLE 0x01
/* Switches (keys) */
#define HAL_KEY_SW_1 0x01 // Button 1 (INT0)
#define HAL_KEY_SW_2 0x02 // Button 2 (INT1)
#define HAL_KEY_SW_3 0x04 // Button 3 (T0)
#define HAL_KEY_SW_4 0x08 // Button 4 (T1)
/******* GLOBAL VARIABLES *******/
extern bool Hal_KeyIntEnable;
/******** FUNCTIONS – API *********/
/* Initialize the Key Service */
extern void HalKeyInit(void);
/* Configure the Key Service */
extern void HalKeyConfig(bool interruptEnable);
/* Read the Key status */
extern uint8 HalKeyRead(void);
/**********************************
*************************************/
#endif
D:MySimpSystemhalinclude
49
/*********************************************
Filename: hal_key.c
Revised: $Date: 2013-10-19 22:27 $
Revision: $Revision: $
Description: HAL KEY Service.
**********************************************/
/******* INCLUDES **********/
#include "hal_mcu.h"
#include "hal_defs.h"
#include "hal_types.h"
#include "hal_board.h"
#include "hal_drivers.h"
#include "hal_key.h"
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
/******* CONSTANTS *******/
#define HAL_KEY_NO_EDGE 0
#define HAL_KEY_FALLING_EDGE 1
#define HAL_KEY_DEBOUNCE_VALUE 25
/* CPU port interrupt edge control and flag*/
#define HAL_KEY_CPU_TCON TCON
/* CPU port interrupt enable/disable*/
#define HAL_KEY_CPU_IE IE
D:MySimpSystemhaltargetJC51B
/********* Key 1 ************/
/* SW_1 is at P3.2 */
#define HAL_KEY_SW_1_PORT PUSH1_SBIT
/* edge interrupt */
#define HAL_KEY_SW_1_EDGEBIT BV(0) // TCON => Bit0
#define HAL_KEY_SW_1_EDGE HAL_KEY_FALLING_EDGE
/* SW_1 interrupts, INT0 IE bit and flag */
#define HAL_KEY_SW_1_IEBIT BV(0)
#define HAL_KEY_SW_1_IFG TCON_bit.IE0
/********* Key 2 ************/
/* SW_2 is at P3.3 */
#define HAL_KEY_SW_2_PORT PUSH2_SBIT
/* edge interrupt */
#define HAL_KEY_SW_2_EDGEBIT BV(2) // TCON => Bit2
#define HAL_KEY_SW_2_EDGE HAL_KEY_FALLING_EDGE
/* SW_2 interrupts, INT1 IE bit and flag */
#define HAL_KEY_SW_2_IEBIT BV(2)
#define HAL_KEY_SW_2_IFG TCON_bit.IE1
/********* Key 3 ************/
/* SW_3 is at P3.4, Key3 has no interrupt */
#define HAL_KEY_SW_3_PORT PUSH3_SBIT
/********* Key 4 ************/
/* SW_4 is at P3.5, Key4 has no interrupt */
#define HAL_KEY_SW_4_PORT PUSH4_SBIT
/******** GLOBAL VARIABLES ********/
static uint8 HalKeyConfigured;
/* interrupt enable/disable flag */
bool Hal_KeyIntEnable;
/******* FUNCTIONS – API ************/
/*****************************************
* @fn HalKeyInit
* @brief Initialize Key Service
* @param none
* @return None
*****************************************/
void HalKeyInit( void )
{
HAL_KEY_SW_1_PORT = 1; // make it as an input
HAL_KEY_SW_2_PORT = 1; // make it as an input
HAL_KEY_SW_3_PORT = 1; // make it as an input
HAL_KEY_SW_4_PORT = 1; // make it as an input
/* Start with key is not configured */
HalKeyConfigured = FALSE;
}
void HalKeyConfig (bool interruptEnable)
{
/* Enable/Disable Interrupt */
Hal_KeyIntEnable = interruptEnable; // Hal_KeyIntEnable is a global var.
/* Determine if interrupt is enable or not!
Interrupt is only for Key1 and Key2 */
if (Hal_KeyIntEnable)
{
/********* Key 1 ************/
/* Rising/Falling edge configuration */
HAL_KEY_CPU_TCON &= ~(HAL_KEY_SW_1_EDGEBIT);
/* For falling edge, the bit must be set. */
#if (HAL_KEY_SW_1_EDGE == HAL_KEY_FALLING_EDGE)
HAL_KEY_CPU_TCON |= HAL_KEY_SW_1_EDGEBIT;
#endif
/* Interrupt configuration: */
HAL_KEY_CPU_IE |= HAL_KEY_SW_1_IEBIT;
HAL_KEY_SW_1_IFG = 0;
/********* Key 2 ************/
/* Rising/Falling edge configuration */
HAL_KEY_CPU_TCON &= ~(HAL_KEY_SW_2_EDGEBIT);
/* For falling edge, the bit must be set. */
#if (HAL_KEY_SW_2_EDGE == HAL_KEY_FALLING_EDGE)
HAL_KEY_CPU_TCON |= HAL_KEY_SW_2_EDGEBIT;
#endif
/* Interrupt configuration: */
HAL_KEY_CPU_IE |= HAL_KEY_SW_2_IEBIT;
HAL_KEY_SW_2_IFG = 0;
/********* Key 3 ************/
/* Key3 has no interrupt */
/********* Key 4 ************/
/* Key4 has no interrupt */
50
} else { /* Interrupts NOT enabled */
HAL_KEY_CPU_IE &= ~(HAL_KEY_SW_1_IEBIT);
HAL_KEY_SW_1_IFG = 0;
HAL_KEY_CPU_IE &= ~(HAL_KEY_SW_2_IEBIT);
HAL_KEY_SW_2_IFG = 0;
}
/* Key now is configured */
HalKeyConfigured = TRUE;
}
/*********************************************
* @fn HalKeyRead
* @brief Read the current value of a key
* @param None
* @return keys - current keys status
********************************************/
uint8 HalKeyRead ( void )
{
uint8 keys = 0;
if (HAL_PUSH_BUTTON1())
{
HAL_DEBOUNCE(!HAL_PUSH_BUTTON1());
keys |= HAL_KEY_SW_1;
}
if (HAL_PUSH_BUTTON2())
{
HAL_DEBOUNCE(!HAL_PUSH_BUTTON2());
keys |= HAL_KEY_SW_2;
}
if (HAL_PUSH_BUTTON3())
{
HAL_DEBOUNCE(!HAL_PUSH_BUTTON3());
keys |= HAL_KEY_SW_3;
}
/****** INTERRUPT SERVICE ROUTINE *******/
/****************************************
* @fn halKeySW1Isr
* @brief SW1 ISR
* @param
* @return
***************************************/
HAL_ISR_FUNCTION( halKeySW1Isr, extern0 )
{
HAL_ENTER_ISR();
//Clear the CPU interrupt flag
HAL_KEY_SW_1_IFG = 0;
HAL_EXIT_ISR();
}
/****************************************
* @fn halKeySW2Isr
* @brief SW2 ISR
* @param
* @return
***************************************/
HAL_ISR_FUNCTION( halKeySW2Isr, extern1 )
{
HAL_ENTER_ISR();
//Clear the CPU interrupt flag
HAL_KEY_SW_2_IFG = 0;
HAL_EXIT_ISR();
}
51
52
#else
void HalKeyInit(void)
{
}
void HalKeyConfig(bool interruptEnable)
{
}
uint8 HalKeyRead(void)
{
return 0;
}
#endif /* HAL_KEY */
/******************************************************************
******************************************************************/
加入Library Path
53
 現在你已經準備好幾樣東西,讓我們再複習一下
hal_mcu.h
hal_types.h
hal_board_cfg.h
hal_defs.h
hal_drivers.h
hal_board.h
hal_lcd.h
hal_key.h
hal_drviers.c
hal_lcd.c
hal_key.c
在進行RTC實驗之前
54
 我們現在有按鍵跟LCD驅動程式,何不寫一個應用
程式,完成如exercise4-03.c的進度條顯示程式。
D:MySimpSystemProjectsMyAPP1Source
#include "hal_types.h"
#include "hal_drivers.h"
#include "hal_board_cfg.h"
#include "MyAPP1.h"
void main()
{
uint8 i, percent_bar;
char num[4];
char *str1 = "Dynamic Show";
HalDriverInit();
LCD_DispHorBarInit();
LCD_DispStr(0, 0, str1);
while(1)
{
if (HalKeyRead()==HAL_KEY_SW_1) {
LCD_ClrLine(0);
LCD_ClrLine(1);
for(i=0;i<101;i++)
{
num[0] = (i/100)+0x30;
num[1] = ((i/10)%10)+0x30;
num[2] = (i%10)+0x30;
num[3] = 0x25;
if(num[0]==0x30) {
LCD_DispChar(0, 0, ' ');
if(num[1]==0x30)
LCD_DispChar(0, 1, ' ');
else
LCD_DispChar(0, 1, num[1]);
} else {
LCD_DispChar(0, 0, num[0]);
LCD_DispChar(0, 1, num[1]);
}
LCD_DispChar(0, 2, num[2]);
LCD_DispChar(0, 3, num[3]);
percent_bar = (i*100/125);
LCD_DispHorBar(1, 0, percent_bar);
delayms(200);
}
}
}
}
顯現電路板設定組態檔的威力
 將driver與硬體I/O相關的設定移到hal_borad_cfg.h
#define LCD_RW P1_bit.P1_1
#define LCD_EN P3_bit.P3_4
#define LCD_RS P3_bit.P3_5
#define LCD_BF P0_bit.P0_7
#define LCD_DATA_PORT P0
/****************************************************
Filename: hal_board_cfg.h
*****************************************************/
#ifndef HAL_BOARD_CFG_H
略
/* BTN1 */
#define PUSH1_BV BV(2)
#define PUSH1_SBIT P3_bit.P3_2 // INT0
#define PUSH1_POLARITY ACTIVE_LOW
/* BTN2 */
#define PUSH2_BV BV(3)
#define PUSH2_SBIT P3_bit.P3_3 // INT1
#define PUSH2_POLARITY ACTIVE_LOW
/* BTN3 */
#define PUSH3_BV BV(4)
#define PUSH3_SBIT P3_bit.P3_4 // T0
#define PUSH3_POLARITY ACTIVE_LOW
/* BTN4 */
#define PUSH4_BV BV(5)
#define PUSH4_SBIT P3_bit.P3_5 // T1
#define PUSH4_POLARITY ACTIVE_LOW
/* -- LCD I/O Configuration --*/
#define LCD_RW P1_bit.P1_1
#define LCD_EN P3_bit.P3_4
#define LCD_RS P3_bit.P3_5
#define LCD_BF P0_bit.P0_7
#define LCD_DATA_PORT P0 55
練習3: 撰寫API手冊 (以LCD Driver為例)
56
57
58
59
60
61
為RTC準備什麼
62
 3-wire通訊介面的驅動程式
 DS1302的驅動程式
 寫完後將他們一一加入project的group
 修改hal_board_cfg.h
 寫出RTC的應用程式
練習4: 加入3-wire通訊介面Driver
63
/*************************************************************************
Filename: hal_triwire.h
Revised: $Date: 2013-10-18 $
Revision: $Revision: $
Description: This file contains the interface to the 3-Wire Service.
*************************************************************************/
#ifndef HAL_TRIWIRE_H
#define HAL_TRIWIRE_H
/**** INCLUDES *****/
#include "hal_board.h"
/**** CONSTANTS ****/
/* 3-Wire delay */
#define TriWireDELAY 10
/**** FUNCTIONS – API ****/
/* 3-wire interface: write a byte to the bus */
extern void TriWire_WriteByte(uint8 output_data);
/* 3-wire interface: read a byte from the bus */
extern uint8 TriWire_ReadByte(void);
/* 3-wire interface: write data to the device register */
extern void TriWire_Write(uint8 Reg_Addr, uint8 Reg_Data)
/* 3-wire interface: read data from the device register */
extern uint8 TriWire_Read(uint8 Reg_Addr)
/************************************************************************
************************************************************************/
#endif
加入3-wire的driver: hal_triwire.c
64
/**********************************************************************
Filename: hal_triwire.c
Revised: $Date: 2013-10-18 21:07 $
Revision: $Revision: $
Description: This file contains the interface to the 3-wire driver.
**********************************************************************/
/********* INCLUDES ***********/
#include "hal_board_cfg.h"
#include "hal_defs.h"
#include "hal_types.h“
#include "hal_drivers.h"
#include "hal_triwire.h"
/********* LOCAL FUNCTIONS *******/
/* 3-wire interface: write a byte to the bus */
//void TriWire_WriteByte(uint8 output_data);
/* 3-wire interface: read a byte from the bus */
//uint8 TriWire_ReadByte(void);
/*********************************************************
* @fn TriWire_WriteByte
* @brief Write 1-byte data to the bus
* @param output_data: The data byte to write on the bus
* @return None
*********************************************************/
void TriWire_WriteByte(uint8 output_data)
{
uint8 index;
for(index = 0; index < 8; index++) {
TriWire_SCLK = LOW;
if(output_data & 0x01) // Check LSB bit, LSB goes first
TriWire_IO = HIGH;
else
TriWire_IO = LOW;
delay_time(TriWireDELAY);
TriWire_SCLK = HIGH; // Write at this transition
delay_time(TriWireDELAY);
output_data = output_data >> 1;
}
}
/**************************************************
* @fn TriWire_ReadByte
* @brief Read 1-byte data from the bus
* @param None
* @return The read-in data byte
***************************************************/
uint8 TriWire_ReadByte(void)
{
uint8 index;
uint8 input_data = 0x00;
for(index = 0; index < 8; index++) {
input_data = input_data >> 1;
TriWire_SCLK = LOW; // Read at this transition
delay_time(TriWireDELAY);
if(TriWire_IO) // Check LSB bit, LSB comes first
input_data |= 0x80;
else
input_data |= 0x00;
TriWire_SCLK = HIGH;
delay_time(TriWireDELAY);
}
return(input_data);
}
65
66
/*****************************************************************
* @fn TriWire_Write
* @brief Write data to the register on a 3-wire device
* @param Reg_Addr: Register address, Reg_Data: Data to write-to
* @return None
****************************************************************/
void TriWire_Write(uint8 Reg_Addr, uint8 Reg_Data)
{
TriWire_RST = LOW;
TriWire_SCLK = LOW;
TriWire_RST = HIGH;
delay_time(TriWireDELAY);
TriWire_WriteByte(Reg_Addr);
TriWire_WriteByte(Reg_Data);
TriWire_SCLK = HIGH;
TriWire_RST = LOW;
}
/*****************************************************************
* @fn TriWire_Read
* @brief Read data from the register on a 3-wire device
* @param Reg_Addr: Register address
* @return Data: The read-in data byte@Reg_Addr
****************************************************************/
uint8 TriWire_Read(uint8 Reg_Addr)
{
uint8 Data;
TriWire_RST = LOW;
TriWire_SCLK = LOW;
TriWire_RST = HIGH;
delay_time(TriWireDELAY);
TriWire_WriteByte(Reg_Addr|0x01); // Command - Read
Data = TriWire_ReadByte();
TriWire_SCLK = HIGH;
TriWire_RST = LOW;
return(Data);
}
#if (HAL_TRIWIRE == TRUE)
…
#endif
Add file與修改hal_board_cfg.h
67
/* ----------------------------------------------
* 3-Wire Port Configuration
* ----------------------------------------------
*/
#define TriWire_SCLK P1_bit.P1_4
#define TriWire_IO P1_bit.P1_2
#define TriWire_RST P1_bit.P1_3
測試驅動程式 (I)
68
 寫一個應用程式,測試3-wire的驅動程式
#include "MyAPP1.h"
void main()
{
uint8 read_keys;
HalDriverInit();
while(1)
{
read_keys = 0x00;
read_keys = HalKeyRead();
switch(read_keys)
{
case HAL_KEY_SW_1:
TriWire_WriteByte(0x35);
break;
case HAL_KEY_SW_2:
TriWire_ReadByte();
break;
case HAL_KEY_SW_3:
TriWire_Write(0xA2, 0xB4);
break;
case HAL_KEY_SW_4:
TriWire_Read(0x37);
break;
}
}
}
/* ---------------------------------
* Driver Configuration
* ---------------------------------
*/
/* Set to TRUE enable LCD usage, FALSE disable it */
#ifndef HAL_LCD
#define HAL_LCD FALSE
#endif
/* Set to TRUE enable KEY usage, FALSE disable it */
#ifndef HAL_KEY
#define HAL_KEY TRUE
#endif
/* Set to TRUE enable 3-wire usage, FALSE disable it */
#ifndef HAL_TRIWIRE
#define HAL_TRIWIRE TRUE
#endif
測試驅動程式 (II)
69
case HAL_KEY_SW_1:
TriWire_WriteByte(0x35);
break;
測試驅動程式 (III)
70
case HAL_KEY_SW_2:
TriWire_ReadByte();
break;
測試驅動程式 (IV)
71
case HAL_KEY_SW_3:
TriWire_Write(0xA2, 0xB4);
break;
測試驅動程式 (V)
72
case HAL_KEY_SW_4:
TriWire_Read(0x37);
break;
加入DS1302 RTC驅動程式
/*************************************************************
Filename: hal_ds1302.h
Revised: $Date: 2013-10-18 $
Revision: $Revision: $
Description: This file contains the driver of DS1302 RTC.
*************************************************************/
#ifndef HAL_DS1302_H
#define HAL_DS1302_H
/***** INCLUDES ******/
#include "hal_board.h"
/***** CONSTANTS ******/
// DS1302 Register Address
#define DS1302_SECOND 0x80
#define DS1302_MINUTE 0x82
#define DS1302_HOUR 0x84
#define DS1302_DATE 0x86
#define DS1302_MONTH 0x88
#define DS1302_WEEKDAY 0x8A
#define DS1302_YEAR 0x8C
#define DS1302_WP 0x8E
#define DS1302_TCS 0x90
// DS1302 Register Mask
#define REG_MASK_SECOND 0x70
#define REG_MASK_MINUTE 0x30
#define REG_MASK_HOUR 0x30
#define REG_MASK_DATE 0x30
#define REG_MASK_MONTH 0x10
#define REG_MASK_WEEKDAY 0x00
#define REG_MASK_YEAR 0xF0
#define REG_MASK_HOUR_MODE 0xA0
#define REG_MASK_CH 0x80
// DS1302 Write Protection
#define DS1302_WR_ON 0x00
#define DS1302_WR_OFF 0x80
#define DS1302_START 0x7F
/***** TYPEDEFS ******/
typedef struct __SYSTEMTIME__
{
uint8 Second;
uint8 Minute;
uint8 Hour;
uint8 Hourmode;
uint8 Date;
uint8 Weekday;
uint8 Month;
uint8 Year;
uint8 DateString[11];
uint8 WeekdayString[4];
uint8 TimeString[9];
}SYSTEMTIME;
/***** FUNCTIONS – API ******/
extern void DS1302_Init(void);
extern void DS1302_ReadTime(SYSTEMTIME *Time);
extern void DS1302_WriteTime(SYSTEMTIME *Time);
#endif
73
74
/*********************************************
Filename: hal_ds1302.c
Revised: $Date: 2013-10-18 21:36 $
Revision: $Revision: $
********************************************/
/***** INCLUDES ******/
#include "hal_board_cfg.h"
#include "hal_defs.h"
#include "hal_types.h"
#include "hal_drivers.h"
#include "hal_triwire.h"
#include "hal_ds1302.h"
/***** LOCAL FUNCTIONS ******/
uint8 FormatReg2Dec(uint8 reg_data, uint8 reg_mask);
uint8 FormatDec2Reg(uint8 dec_data);
void DateToStr(SYSTEMTIME *Time);
void TimeToStr(SYSTEMTIME *Time);
/***********************************************
* @fn DS1302_Init
* @brief Initialize DS1302
* @param None
* @return None
***********************************************/
void DS1302_Init(void)
{
delayms(2);
uint8 Clock_halt = TriWire_Read(DS1302_SECOND+1) & REG_MASK_CH;
if(Clock_halt) {
TriWire_Write(DS1302_WP , DS1302_WR_ON); // Allow write
// Write initial date and time: 2013/11/12, TUE, 23:59:55
TriWire_Write(DS1302_YEAR , 0x13);
TriWire_Write(DS1302_MONTH , 0x11); // month: 07
TriWire_Write(DS1302_DATE , 0x12); // date: 25
TriWire_Write(DS1302_WEEKDAY, 0x02); // weekday
TriWire_Write(DS1302_HOUR , 0x23); // 23 (HR)
TriWire_Write(DS1302_MINUTE , 0x59); // 59 (Min)
TriWire_Write(DS1302_SECOND , 0x55); // 55 (Sec)
TriWire_Write(DS1302_WP , DS1302_WR_OFF); // Write prohibited
}
}
/*************************************************************************************************
* @fn DS1302_ReadTime
* @brief Read current time from DS1302, the read values are stored in a SYSTIME struct variable
* @param SYSTEMTIME *Time: Pointer to the SYSTEMTIME struct variable
* @return None
************************************************************************************************/
void DS1302_ReadTime(SYSTEMTIME *Time)
{
uint8 ReadValue;
ReadValue = TriWire_Read(DS1302_SECOND+1); // When read, use (defined address+1)
Time->Second = FormatReg2Dec(ReadValue, REG_MASK_SECOND);
ReadValue = TriWire_Read(DS1302_MINUTE+1);
Time->Minute = FormatReg2Dec(ReadValue, REG_MASK_MINUTE);
ReadValue = TriWire_Read(DS1302_HOUR+1);
Time->Hour = FormatReg2Dec(ReadValue, REG_MASK_HOUR);
Time->Hourmode = ReadValue & REG_MASK_HOUR_MODE;
ReadValue = TriWire_Read(DS1302_DATE+1);
Time->Date = FormatReg2Dec(ReadValue, REG_MASK_DATE);
ReadValue = TriWire_Read(DS1302_MONTH+1);
Time->Month = FormatReg2Dec(ReadValue, REG_MASK_MONTH);
ReadValue = TriWire_Read(DS1302_WEEKDAY+1);
Time->Weekday = FormatReg2Dec(ReadValue, REG_MASK_WEEKDAY);
ReadValue = TriWire_Read(DS1302_YEAR+1);
Time->Year = FormatReg2Dec(ReadValue, REG_MASK_YEAR);
DateToStr(Time);
TimeToStr(Time);
}
75
76
/*******************************************************************************
* @fn DS1302_WriteTime
* @brief Write time to DS1302
* @param SYSTEMTIME *Time: Pointer to the SYSTEMTIME struct variable
* @return None
******************************************************************************/
void DS1302_WriteTime(SYSTEMTIME *Time)
{
TriWire_Write(DS1302_WP , DS1302_WR_ON);
TriWire_Write(DS1302_YEAR , FormatDec2Reg(Time->Year) );
TriWire_Write(DS1302_MONTH , FormatDec2Reg(Time->Month) );
TriWire_Write(DS1302_DATE , FormatDec2Reg(Time->Date) );
TriWire_Write(DS1302_WEEKDAY, FormatDec2Reg(Time->Weekday));
TriWire_Write(DS1302_HOUR , FormatDec2Reg(Time->Hour) );
TriWire_Write(DS1302_MINUTE , FormatDec2Reg(Time->Minute) );
TriWire_Write(DS1302_SECOND , FormatDec2Reg(Time->Second) );
DateToStr(Time);
TimeToStr(Time);
TriWire_Write(DS1302_WP , DS1302_WR_OFF);
}
/****************************************************************************
* LOCAL FUNCTIONS
/****************************************************************************
* @fn FormatReg2Dec
* @brief Convert the register data into decimal
* @param reg_data: the stored data in DS1302, reg_mask: mask of that value
* @return DecValue: The converted decimal value
****************************************************************************/
uint8 FormatReg2Dec(uint8 reg_data, uint8 reg_mask)
{
uint8 DecValue;
DecValue = ((reg_data & reg_mask) >> 4)*10 + (reg_data & 0x0F);
return(DecValue);
}
/********************************************************************
* @fn FormatDec2Reg
* @brief Convert the decimal value in to register data format
* @param dec_data: the decimal value to store
* @return RegValue: the formatted register data
*******************************************************************/
uint8 FormatDec2Reg(uint8 dec_data)
{
uint8 RegValue;
RegValue = (dec_data/10)*16 + dec_data%10;
return(RegValue);
}
/*******************************************************************
* @fn DateToStr
* @brief Convert Date (year, month, date, weekday) into string
* @param *Time: Pointer to the struct of current time
* @return None
******************************************************************/
void DateToStr(SYSTEMTIME *Time)
{
uint8 *WeekdayTable[7] = {"Mon", "Tue", "Wen", "Thu", "Fri", "Sat", "Sun"};
Time->DateString[0] = '2';
Time->DateString[1] = '0';
Time->DateString[2] = Time->Year/10 + 0x30; // '0' : ASCII = 0x30
Time->DateString[3] = Time->Year%10 + 0x30;
Time->DateString[4]='-';
Time->DateString[5] = Time->Month/10 + 0x30;
Time->DateString[6] = Time->Month%10 + 0x30;
Time->DateString[7]='-';
Time->DateString[8] = Time->Date/10 + 0x30;
Time->DateString[9] = Time->Date%10 + 0x30;
Time->DateString[10] = '0';
Time->WeekdayString[0] = *(WeekdayTable[(Time->Weekday%10)-1] + 0);
Time->WeekdayString[1] = *(WeekdayTable[(Time->Weekday%10)-1] + 1);
Time->WeekdayString[2] = *(WeekdayTable[(Time->Weekday%10)-1] + 2);
Time->WeekdayString[3] = '0';
}
77
78
/*****************************************************************************
* @fn TimeToStr
* @brief Convert Time (hour, minute, second) into string
* @param *Time: Pointer to the struct of current time
* @return None
****************************************************************************/
void TimeToStr(SYSTEMTIME *Time)
{
Time->TimeString[0] = Time->Hour/10 + 0x30; // '0' : ASCII = 0x30
Time->TimeString[1] = Time->Hour%10 + 0x30;
Time->TimeString[2] = ':';
Time->TimeString[3] = Time->Minute/10 + 0x30;
Time->TimeString[4] = Time->Minute%10 + 0x30;
Time->TimeString[6] = Time->Second/10 + 0x30;
Time->TimeString[7] = Time->Second%10 + 0x30;
Time->TimeString[8] = '0';
}
#if (HAL_DS1302 == TRUE)
…
#endif
修改hal_board_cfg.h
79
/**************************************************************************************************
Filename: hal_board_cfg.h
Revised: $Date: 2013-11-2 $
Revision: $Revision: $
Description: Declarations for the JC51B Development Board.
**************************************************************************************************/
#ifndef HAL_BOARD_CFG_H
#define HAL_BOARD_CFG_H
/* ------- Includes ---------*/
#include "hal_mcu.h"
#include "hal_defs.h"
#include "hal_types.h"
#include "hal_lcd.h"
#include "hal_key.h"
#include "hal_triwire.h"
#include "hal_ds1302.h"
**** 略 *****
/* ------ 3-Wire Port Configuration ------*/
#define TriWire_SCLK P1_bit.P1_4
#define TriWire_IO P1_bit.P1_2
#define TriWire_RST P1_bit.P1_3
/* --------- Driver Configuration -------------- */
/* Set to TRUE enable LCD usage, FALSE disable it */
#ifndef HAL_LCD
#define HAL_LCD TRUE
#endif
/* Set to TRUE enable KEY usage, FALSE disable it */
#ifndef HAL_KEY
#define HAL_KEY TRUE
#endif
/* Set to TRUE enable 3-wire usage, FALSE disable it */
#ifndef HAL_TRIWIRE
#define HAL_TRIWIRE TRUE
#endif
/* Set to TRUE enable DS1302 RTC usage, FALSE disable it */
#ifndef HAL_DS1302
#define HAL_DS1302 TRUE
#endif
修改hal_drivers.c的HalDriverInit()
80
/**************************************************************************************
* @fn Hal_DriverInit
* @brief Initialize HW - These need to be initialized before anyone.
* @param None
* @return None
************************************************************************************/
void HalDriverInit (void)
{
#if (defined HAL_LCD) && (HAL_LCD == TRUE)
LCD_Init(2, 16);
#endif
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
HalKeyInit();
HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE);
#endif
#if (defined HAL_DS1302) && (HAL_DS1302 == TRUE)
DS1302_Init();
#endif
}
練習5: 寫一支電子時鐘應用程式
81
#include "MyAPP1.h"
SYSTEMTIME currentTime;
/*
SYSTEMTIME currentTime = {
.Second = 30,
.Minute = 50,
.Hour = 22,
.Hourmode = 0,
.Date = 2,
.Weekday = 6,
.Month = 11,
.Year = 13,
};
*/
void main()
{
HalDriverInit();
// DS1302_WriteTime(&currentTime);
while(1)
{
DS1302_ReadTime(&currentTime);
LCD_DispStr(0, 0, currentTime.DateString);
LCD_DispStr(0, 12, currentTime.WeekdayString);
LCD_DispStr(1, 0, currentTime.TimeString);
}
}
Lab12: DS18B20溫度感測器
82
DS18B20溫度感測器
83
 DS18B20是一個數位溫度感測器,它可以提供9~12位元
的攝氏溫度輸出,而且可以設定超溫警告閥值。它使
用一條數據傳輸線(1-Wire)介面與MCU進行雙向通訊。
 電壓範圍:3.0~5.5V
 溫度範圍:-55oC~+125oC。-10~+85oC時精度為±0.5oC
 解析度為9~12位元,對應的
可分辨溫度量為 0.5oC、0.25oC、
0.125oC和 0.0625oC。在9位元
解析度時,最多在93.75ms內
能把溫度轉換為數位值;在12
位元解析度時,最多在750ms
內把溫度值轉換為數位值。
DS18B20架構說明
84
DS18B20的記憶體配置
85
與DS18B20進行通訊
86
DS1820B的指令集
87
指令 指令碼 說 明
搜尋ROM 0xF0 確定掛在同一bus上 DS1820 的個數和識別 64 位元ROM地址。為操作各裝置作好準備。
讀ROM 0x33 讀取DS1820 ROM中的64位元ROM位址
匹配ROM 0x55
發出此命令後,接著發出 64 位元ROM位址,使對應的DS1820作出回應,為下一步對
該其讀寫作準備。
跳過ROM 0xCC 忽略 64 位 ROM 地址,直接向 DS1820 下達溫度變換命令。
警告搜索 0xEC 執行後只有溫度超過設定值上限或下限的晶片才做出回應。
指令 指令碼 說 明
溫度轉換 0x44 執行溫度轉換,結果將存入內部9位元組RAM中。
寫入暫存器 0x4E 向RAM的2, 3, 4位元組寫上、下限溫度資料、組態,該命令之後緊接3位元組的資料。
讀取暫存器 0xBE 讀取內部RAM中9位元組的內容。
複製暫存器 0x48 將RAM中第3, 4位元組的內容複製到EEPROM中。
Recall EEPROM 0xB8 將EEPROM中的內容還原到RAM中的第3 , 4位元組。
讀取供電方式 0xB4 寄生供電時DS1820發回 0,外接電源發回 1 。
Read/Write時序圖
88
使用注意事項
89
 DS1820與MCU使用串列通訊,因此對DS1820進行讀寫時必須嚴格
保證讀寫時序,否則將無法讀取測溫結果。
 實際應用中1-Wire bus上所掛DS1820數量並非任意多個,當數量超
過8個時就需要解決MCU的匯流排驅動問題。
 連接DS1820的匯流排電纜有長度限制,經驗中使用一般電線長度
超過50m時,讀取的測溫資料將發生錯誤。使用雙絞線帶遮罩電
纜時,正常通訊距離可達150m。
 DS1820測溫程式中,向DS1820發出溫度轉換命令後,程式總要等
待DS1820的返回訊號,一旦某個DS1820接觸不好或斷線,當程式
讀該DS1820時,將沒有返回訊號,程式進入閉迴路。這一點在進
行DS1820硬體連接和軟體設計時也要給予一定的重視。 測溫電纜
線建議採用遮罩4芯雙絞線,其中一對線接地線與訊號線,另一組
接VCC和地線,遮罩層在源端單點接地。
/*************************************************
Filename: hal_onewire.c
Revised: $Date: 2013-11-5 21:07 $
**************************************************/
/****** INCLUDES ******/
#include "hal_board_cfg.h"
#include "hal_defs.h"
#include "hal_types.h"
#include "hal_drivers.h"
#include "hal_onewire.h"
/****** CONSTANTS ******/
/* 1-Wire delay*/
#define OneWireDELAY_Write 6
#define OneWireDELAY_Read 4
/************************************************
* @fn OneWire_WriteByte
* @brief Write 1-byte data to the bus
***********************************************/
void OneWire_WriteByte(uint8 output_data)
{
uint8 index;
for(index = 0; index < 8; index++) {
OneWire_DQ = LOW;
OneWire_DQ = output_data & 0x01; // 5us
delay_time(OneWireDELAY_Write); // 76us
OneWire_DQ = HIGH;
output_data = output_data >> 1;
}
}
1-Wire通訊介面驅動程式
90
/***********************************************
Filename: hal_onewire.h
Revised: $Date: 2013-11-6 $
Revision: $Revision: $
Description: This file contains the
interface to the 1-Wire service.
***********************************************/
#ifndef HAL_ONEWIRE_H
#define HAL_ONEWIRE_H
/****** INCLUDES ******/
#include "hal_board.h“
/****** FUNCTIONS - API ******/
/* 1-wire interface: write a byte to the bus
*/
extern void OneWire_WriteByte(uint8 output_data);
/* 1-wire interface: read a byte from the bus
*/
extern uint8 OneWire_ReadByte(void);
/* 1-wire interface: Reset devices on the bus
*/
extern void OneWire_BusReset(void);
#endif
91
/********************************************
* @fn OneWire_ReadByte
* @brief Read 1-byte data from the bus
* @param None
* @return The read-in data byte
*******************************************/
uint8 OneWire_ReadByte(void)
{
uint8 index;
uint8 input_data = 0x00;
for(index = 0; index < 8; index++) {
OneWire_DQ = LOW;
input_data = input_data >> 1; // takes 5us
OneWire_DQ = HIGH;
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
if(OneWire_DQ) input_data |= 0x80;
delay_time(OneWireDELAY_Read);
}
return(input_data);
}
/*********************************************
* @fn OneWire_BusReset
* @brief Reset all devices on the bus
* @param None
* @return None
**********************************************/
void OneWire_BusReset(void)
{
OneWire_DQ = LOW;
delay_time(100);
OneWire_DQ = HIGH;
}
#if (HAL_ONEWIRE == TRUE)
…
#endif
DS18B20溫度感測器驅動程式
/***************************************************
Filename: hal_ds18b20.h
Revised: $Date: 2013-11-05 $
Revision: $Revision: $
Description: This file contains the interface
to the ds18b20 temperature sensor.
***************************************************/
#ifndef HAL_DS18B20_H
#define HAL_DS18B20_H
/******* INCLUDES *******/
#include "hal_board.h“
/******* CONSTANTS *******/
// DS18B20 ROM CMDs
#define DS18B20_CMD_SEARCH_ROM 0xF0
#define DS18B20_CMD_READ_ROM 0x33
#define DS18B20_CMD_MATCH_ROM 0x55
#define DS18B20_CMD_SKIP_ROM 0xCC
#define DS18B20_CMD_ALARM_SEARCH 0xEC
// DS18B20 RAM CMDs
#define DS18B20_CMD_CONVERT_TEMP 0x44
#define DS18B20_CMD_READ_SCRATCH 0xBE
#define DS18B20_CMD_WRITE_SCRATCH 0x4E
#define DS18B20_CMD_COPY_SCRATCH 0x48
#define DS18B20_CMD_RECALL_EPROM 0xB8
#define DS18B20_CMD_READ_PSUPPLY 0xB4
// DS18B20 Resolution
#define DS18B20_RES_9BITS 0x1F
#define DS18B20_RES_10BITS 0x3F
#define DS18B20_RES_11BITS 0x5F
#define DS18B20_RES_12BITS 0x7F
#define DS18B20_CONV_TIME 752
/******* TYPEDEFS *******/
typedef struct __TEMPCONFIG__
{
// 2's complement, +85C = 0101, 0101 = 0x55
uint8 Alarm_TH;
// 2's complement, -10C = 1111, 0110 = 0xF6
uint8 Alarm_TL;
uint8 Resolution;
}TEMPCONFIG;
typedef struct __SENSEDTEMP__
{
bool Temp_Sign;
uint8 Temp_Integer;
uint8 Temp_Decimal;
uint8 Resolution;
char TempString[8];
}SENSEDTEMP;
/******* FUNCTIONS - API *******/
extern void DS18B20_Init(void);
extern void DS18B20_ReadTemp(SENSEDTEMP *ttemp);
extern void DS18B20_Config(TEMPCONFIG *tconfig);
#endif
92
93
/***********************************************************************************
Filename: hal_ds18b20.c
Revised: $Date: 2013-11-05 21:36 $
Revision: $Revision: $
Description: This file contains the interface to the ds18b20 temperature sensor.
***********************************************************************************/
/******* INCLUDES *******/
#include "hal_board_cfg.h"
#include "hal_defs.h"
#include "hal_types.h"
#include "hal_drivers.h"
#include "hal_ds18b20.h"
#include "hal_onewire.h"
/******* LOCAL FUNCTIONS *******/
void DS18B20_Wait_Conv(uint8 Resolution);
/****************************************************
* @fn DS18B20_Init
* @brief Initialize the DS18B20 on the bus
* @param None
* @return None
****************************************************/
void DS18B20_Init(void)
{
OneWire_DQ = HIGH;
delay_time(1);
OneWire_BusReset(); // Master Send Reset
delay_time(50);
}
/*********************************************************
* @fn DS18B20_Config
* @brief DS18B20 configuration with temperature alarm
* and resolution settings
* @param *tconfig: pointer to the TEMPCONFIG struct
* @return None
********************************************************/
void DS18B20_Config(TEMPCONFIG *tconfig)
{
OneWire_WriteByte(DS18B20_CMD_SKIP_ROM);
OneWire_WriteByte(tconfig->Alarm_TH);
OneWire_WriteByte(tconfig->Alarm_TL);
OneWire_WriteByte(tconfig->Resolution);
OneWire_ReadByte();
OneWire_ReadByte();
}
/*****************************************************************************************
* @fn DS18B20_ReadTemp
* @brief Read temperature from DS18B20
* @param *ttemp: pointer to SENSEDTEMP struct
* @return None
****************************************************************************************/
void DS18B20_ReadTemp(SENSEDTEMP *ttemp)
{
uint8 temp_low_byte = 0;
uint8 temp_high_byte = 0;
uint8 temp_sign = 0;
// send command: 1.Initialize, 2.ROM CMD, 3.RAM CMD
DS18B20_Init();
OneWire_WriteByte(DS18B20_CMD_SKIP_ROM);
OneWire_WriteByte(DS18B20_CMD_CONVERT_TEMP);
DS18B20_Wait_Conv(ttemp->Resolution);
DS18B20_Init();
OneWire_WriteByte(DS18B20_CMD_SKIP_ROM);
OneWire_WriteByte(DS18B20_CMD_READ_SCRATCH);
temp_low_byte = OneWire_ReadByte(); // temp low byte
temp_high_byte = OneWire_ReadByte(); // temp high byte
temp_sign = temp_high_byte & 0xF8;
if(temp_sign) {
ttemp->Temp_Sign = 1; // negative
ttemp->Temp_Integer = ((LO_UINT8(temp_high_byte) << 4) | HI_UINT8(temp_low_byte));
ttemp->Temp_Integer = ~(ttemp->Temp_Integer) + 1;
ttemp->Temp_Decimal = LO_UINT8(~(temp_low_byte & 0x0F));
}else{
ttemp->Temp_Sign = 0; // positive
ttemp->Temp_Integer = ((LO_UINT8(temp_high_byte) << 4) | HI_UINT8(temp_low_byte));
ttemp->Temp_Decimal = LO_UINT8(temp_low_byte & 0x0F);
}
94
95
// String construction
if(temp_sign) ttemp->TempString[0] = '-';
else ttemp->TempString[0] = ' ';
if((ttemp->Temp_Integer)/100) { // hundred's digit
ttemp->TempString[1] = ((ttemp->Temp_Integer)/100) + '0';
}else{
if(temp_sign) {
ttemp->TempString[0] = ' ';
ttemp->TempString[1] = '-';
}else{
ttemp->TempString[1] = ' ';
}
}
if(((ttemp->Temp_Integer)%100)/10) { // ten's digit
ttemp->TempString[2] = (((ttemp->Temp_Integer)%100)/10) + '0';
}else{
if(temp_sign) {
ttemp->TempString[1] = ' ';
ttemp->TempString[2] = '-';
}else{
ttemp->TempString[2] = ' ';
}
}
ttemp->TempString[3] = ((ttemp->Temp_Integer)%10) + '0';
ttemp->TempString[4] = '.';
ttemp->TempString[5] = (ttemp->Temp_Decimal*625/1000) + '0';
ttemp->TempString[6] = 'C';
ttemp->TempString[7] = '0';
}
96
/************************************************
* @fn DS18B20_Wait_Conv
* @brief Waiting for temperature conversion
* @param Resolution: The DS18B20 resolution
* @return None
************************************************/
void DS18B20_Wait_Conv(uint8 Resolution)
{
switch(Resolution) {
case DS18B20_RES_9BITS:
delayms(DS18B20_CONV_TIME/8);
break;
case DS18B20_RES_10BITS:
delayms(DS18B20_CONV_TIME/4);
break;
case DS18B20_RES_11BITS:
delayms(DS18B20_CONV_TIME/4);
delayms(DS18B20_CONV_TIME/4);
break;
case DS18B20_RES_12BITS:
delayms(DS18B20_CONV_TIME/4);
delayms(DS18B20_CONV_TIME/4);
delayms(DS18B20_CONV_TIME/4);
delayms(DS18B20_CONV_TIME/4);
break;
default:
break;
}
}
#if (HAL_DS18B20 == TRUE)
…
#endif
1-Wire & DS18B20 驅動程式測試
97
實習12
98
 現在,請綜合驅動程式,寫一個應用程式,讓LCD不只
顯示出時間,也可顯示目前溫度。
 步驟1: 加入1-wrie驅動程式
1. 加入hal_onewire.h / hal_onewire.c
2. 修改hal_board_cfg.h
3. 修改hal_drivers.c
 步驟2: 加入DS18B20溫度感測器驅動程式
1. 加入hal_ds18b20.h / hal_ds18b20.c
2. 修改hal_board_cfg.h
3. 修改hal_drivers.c
 步驟3: 撰寫應用程式
含溫度顯示的電子時鐘應用程式
99
#include "MyAPP1.h"
SYSTEMTIME currentTime;
SENSEDTEMP currentTemp;
TEMPCONFIG configTemp = {0x55, 0xF6, DS18B20_RES_11BITS};
void main()
{
HalDriverInit();
DS18B20_Config(&configTemp);
OneWire_BusReset();
currentTemp.Resolution = configTemp.Resolution;
while(1)
{
DS1302_ReadTime(&currentTime);
LCD_DispStr(0, 0, currentTime.DateString);
LCD_DispStr(0, 12, currentTime.WeekdayString);
LCD_DispStr(1, 0, currentTime.TimeString);
DS18B20_ReadTemp(&currentTemp);
LCD_DispStr(1, 9, currentTemp.TempString);
}
}
Lab13 加入LED與Timer驅動程式
100