目錄
一、前言
本文是在上一篇文章《【JZ2440筆記】串口通信》的基礎上寫的,上一篇使用的是查詢的方式,這一篇使用中斷的方式,具體的串口介紹和配置就不寫了,都在上一篇了,關於中斷方面的設置在《【JZ2440筆記】裸機實驗使用中斷》中有講到,這裏直接貼出本文代碼。
二、程序編寫
程序分爲以下幾個文件:
head.S;啓動文件。
init.c:關閉看門狗,初始化時鐘的函數。
uart.c:串口相關配置。
uart.h:串口頭文件。
main.c:主函數。
Makefile:編譯程序。
每個文件具體內容如下:
head.S
@*************************************************************************
@ File:head.S
@ 功能:設置FCLK到400MHz,然後初始化串口
@*************************************************************************
.text
.global _start
_start:
@******************************************************************************
@ 中斷向量,本程序中,除Reset和HandleIRQ外,其它異常都沒有使用
@******************************************************************************
b Reset
@ 0x04: 未定義指令中止模式的向量地址
HandleUndef:
b HandleUndef
@ 0x08: 管理模式的向量地址,通過SWI指令進入此模式
HandleSWI:
b HandleSWI
@ 0x0c: 指令預取終止導致的異常的向量地址
HandlePrefetchAbort:
b HandlePrefetchAbort
@ 0x10: 數據訪問終止導致的異常的向量地址
HandleDataAbort:
b HandleDataAbort
@ 0x14: 保留
HandleNotUsed:
b HandleNotUsed
@ 0x18: 中斷模式的向量地址
b HandleIRQ
@ 0x1c: 快中斷模式的向量地址
HandleFIQ:
b HandleFIQ
Reset:
ldr sp, =4096 @設置堆棧,因爲要調用C語言函數
bl disable_watch_dog @關WATCH DOG
bl init_system_clk @初始化系統時鐘,FCLK=400MHz,HCLK=100MHz,PCLK=50MHz
msr cpsr_c, #0xd2 @ 進入中斷模式
ldr sp, =3072 @ 設置中斷模式棧指針
msr cpsr_c, #0xd3 @ 進入管理模式
ldr sp, =4096 @ 設置管理模式棧指針,
@ 其實復位之後,CPU就處於管理模式,
@ 前面的“ldr sp, =4096”完成同樣的功能,此句可省略
msr cpsr_c, #0x53 @ 設置I-bit=0,開IRQ中斷
bl main @跳轉執行main函數
halt_loop:
b halt_loop
HandleIRQ:
sub lr, lr, #4 @ 計算返回地址
stmdb sp!, { r0-r12,lr } @ 保存使用到的寄存器
@ 注意,此時的sp是中斷模式的sp
@ 初始值是上面設置的3072
ldr lr, =int_return @ 設置調用ISR即EINT_Handle函數後的返回地址
ldr pc, =UART0_Handle @ 調用中斷服務函數
int_return:
ldmia sp!, { r0-r12,pc }^ @ 中斷返回, ^表示將spsr的值複製到cpsr
init.c
/* WOTCH DOG register */
#define REG_WTCON (*(volatile unsigned long *)0x53000000)
/* Sys Clk Config */
#define REG_CLKDIVN (*(volatile unsigned long *)0x4C000014)
#define REG_CAMDIVN (*(volatile unsigned long *)0x4C000018)
#define REG_MPLLCON (*(volatile unsigned long *)0x4C000004)
void disable_watch_dog();
void init_system_clk();
/*上電後,WATCH DOG默認是開着的,要把它關掉 */
void disable_watch_dog()
{
REG_WTCON = 0;
}
void init_system_clk()
{
//HCLK = FCLK/4, 當 CAMDIVN[9] = 0 時
//PCLK 設置爲 HCLK/2
//完成配置FCLK : HCLK : PCLK = 1 : 1/4 : 1/8,DIVN_UPLL是USB的時鐘不用管
REG_CLKDIVN = (2 << 1) | (1 << 0);
/* 如果HDIVN非0,CPU的總線模式應該從“fast bus mode”變爲“asynchronous bus mode” */
__asm__(
"mrc p15, 0, r1, c1, c0, 0\n" /* 讀出控制寄存器 */
"orr r1, r1, #0xc0000000\n" /* 設置爲“asynchronous bus mode” */
"mcr p15, 0, r1, c1, c0, 0\n" /* 寫入控制寄存器 */
);
//m=MDIV+8, p=PDIV+2, s=SDIV, Mpll = ( 2 × m × Fin ) / ( p × 2^s )
//FCLK = (2 * (92 + 8) * 12000000) / ((1 + 2) * 2) = 400000000 = 400MHz
//配置完MPLL後時鐘停振,CPU停止運行等待時鐘輸出穩定,之後FCLK=400MHz,HCLK=100MHz,PCLK=50MHz */
REG_MPLLCON = (92<<12)|(1<<4)|(1<<0);
}
uart.c
#include "uart.h"
void init_uart(DWORD buadrate)
{
//Step1,配置GPIO,GPH3(RXD0),GPH2(TXD0)
//清除相關配置位
REG_GPHCON &= ~((DWORD)(3 << (2*3)) | (3 << (2*2)));
REG_GPHDAT &= ~((DWORD)(1 << 3) | (1 << 2));
REG_GPHUP &= ~((DWORD)(1 << 3) | (1 << 2));
//設置相關配置位
REG_GPHCON |= ((DWORD)2 << (2*3)) | (2 << (2*2));
REG_GPHDAT |= ((DWORD)1 << 3) | (1 << 2);
REG_GPHUP |= ((DWORD)1 << 3) | (1 << 2);
//無校驗,1個停止位,8個數據位
REG_ULCON0 = 0x03;
//發送和接受設置爲查詢/中斷模式
REG_UCON0 = 0x05;
// REG_UCON0 |= 1 << 5; //環回模式
//不使用FIFO
REG_UFCON0 = 0;
//不使用流控
REG_UMCON0 = 0;
//UBRDIVn = (int)( UART clock / ( buad rate x 16) ) –1
REG_UBRDIV0 = (PCLK_SPEED / (115200 * 16)) - 1;
//中斷配置
REG_INTMSK &= ~((DWORD)1 << 28); //開UART0中斷
REG_INTSUBMSK &= ~((DWORD)1 << 0); //開RXT0中斷
}
void uart_send(BYTE ch)
{
while(!(REG_UTRSTAT0 & (1 << 2)))
{
;
}
REG_UTXH0 = ch;
}
BYTE uart_receive()
{
while(!(REG_UTRSTAT0 & (1 << 0)))
{
;
}
return REG_URXH0;
}
void uart_sendString(BYTE *str)
{
while(*str != '\0')
{
uart_send(*str);
str++;
}
}
uart.h
#ifndef _UART_H_
#define _UART_H_
#define BYTE unsigned char
#define WORD unsigned short
#define DWORD unsigned int
/* Uart Config */
#define PCLK_SPEED 50000000
#define REG_GPHCON (*(volatile unsigned long *)0x56000070)
#define REG_GPHDAT (*(volatile unsigned long *)0x56000074)
#define REG_GPHUP (*(volatile unsigned long *)0x56000078)
#define REG_ULCON0 (*(volatile unsigned long *)0x50000000)
#define REG_UCON0 (*(volatile unsigned long *)0x50000004)
#define REG_UFCON0 (*(volatile unsigned long *)0x50000008)
#define REG_UMCON0 (*(volatile unsigned long *)0x5000000C)
#define REG_UTXH0 (*(volatile unsigned long *)0x50000020)
#define REG_URXH0 (*(volatile unsigned long *)0x50000024)
#define REG_UBRDIV0 (*(volatile unsigned long *)0x50000028)
#define REG_UTRSTAT0 (*(volatile unsigned long *)0x50000010)
#define REG_INTMSK (*(volatile unsigned long *)0X4A000008)
#define REG_INTOFFSET (*(volatile unsigned long *)0x4A000014)
#define REG_INTSUBMSK (*(volatile unsigned long *)0X4A00001C)
#define REG_SUBSRCPND (*(volatile unsigned long *)0X4A000018)
#define REG_SRCPND (*(volatile unsigned long *)0X4A000000)
#define REG_INTPND (*(volatile unsigned long *)0X4A000010)
void init_uart(DWORD buadrate);
void uart_send(BYTE ch);
BYTE uart_receive();
void uart_sendString(BYTE *str);
#endif
main.c
#include "uart.h"
int main()
{
init_uart(115200);
while(1)
{
;
}
return 0;
}
void UART0_Handle()
{
uart_send(REG_URXH0);
//清中斷
REG_SUBSRCPND |= (1 << 0);
REG_SRCPND |= (DWORD)1 << REG_INTOFFSET;
REG_INTPND |= (DWORD)1 << REG_INTOFFSET;
}
Makefile
objs := head.o init.o uart.o main.o
uart.bin: $(objs)
arm-linux-ld -Ttext 0x0000000 -g -o uart_elf $^
arm-linux-objcopy -O binary -S uart_elf $@
arm-linux-objdump -D -m arm uart_elf > uart.dis
%.o:%.c
arm-linux-gcc -Wall -O2 -c -o $@ $<
%.o:%.S
arm-linux-gcc -Wall -O2 -c -o $@ $<
clean:
rm -f uart.bin uart_elf uart.dis *.o