本文主要探讨210定时器相关知识,210定时器主要包含PWN定时器,系统定时器,看门狗,RTC。
PWM定时器
210有5个PWM定时器,timer0、1、2、3通过对应PIO产生PWM波形信号并输出,timer4没有GPIO只产生内部定时器中断
PWM定时器时钟源为PCLK_PSYS,timer0、1共用prescaler0预分频器(8位),timer2、3、4使用prescaler1预分频器(8位),且每个timer有分频器预分频器和分频器构成分频系统,将PCLK_PSYS分频后的时钟给timer作为时钟周期
预分频器分频值范围为1~256,分频器是MUX开关(1/1,1/2,1/4,1/8,1/16)
在PCLK_PSYS(66MHz)下,产生时钟周期范围为0.03us--62.061us,计数器TCNTB值范围为1--2^32,最大时间范围为266548.27s(74h+)
PWN配置寄存器TCFG0、TCFG1、TCON、TCNT、TCNTB、TCNTO、TCMPB
TCFG0用于配置预分频器和死区
TCFG1用于配置分频器
TCON用于配置自动重加载,手动刷入数据,时钟开关
TCNTB是地址寄存器(可写)用于存储计数值便于在自动模式下刷入
TCNT(不可读写)用于周期递减(-1)将计数值写入TCNTB中,启动timer前需要将TCNTB中的值刷到TCNT中(手写寄存器输入一次)
TCNTO(只读)用于读取目前TCNT的值
PWM波形参数:周期T,占空比duty(高电平时间占比),TCMPB决定PWM波形占空比
电平翻转器
电平翻转器是电平取反电路
定义TCNT>TCMPB时为高电平,反之为低电平
当占空比为30%翻转后变为70%(高低电平互换)
死区生成器
PWM应对交流电压进行整流。整流时2路整流分别在正电平和负电平时导通工作,不能同时导通(短路)
实际电路不可能同时上升或下降沿,保留留死区避免短路
死区少容易短路,死区多控制精度低产品性能低
210自带的死区生成器
蜂鸣器
蜂鸣器的2金属片通电吸附撞击产生声音,通过导通频率控制吸附频率控制声音(PWM驱动)
蜂鸣器通过GPD0_2(XpwmTOUT2)引脚连接在SoC
GPD0CON(0xE02000A0),bit8~bit11设置为0b0010(TOUT_2是PWM输出)
看门狗定时器
定时监测cpu防止跑飞,规定周期时间计数内若没有恢复计数,默认系统跑飞则会复位cpu
看门狗配置寄存器WTCON、WTDAT、WTCNT、WTCLRINT
WTCON用于配置时钟源启停,时钟分频,中断启停,复位启停
WTDAT配置第一次使用时间周期(上电到第一次触发的时间)
WTCNT配置时间周期的计数值 WTCLRINT写任意值清除中断
实时时钟RTC
RTC是内部外设,有独立晶振提供RTC时钟源(32.768KHz),内部的寄存器用来记录时间(年月日时分秒星期),系统关机时时间仍在计时(独立电源供电)
闹钟发生器定点时间产生RTC中断
RTC寄存器
INTP 中断挂起寄存器
RTCCON RTC控制寄存器
RTCALM ALMxxx 闹钟功能有关的寄存器
BCDxxx 时间寄存器
BCD码
27<==转换==>0x27
RTC中所有的时间(年月日时分秒星期,闹钟)用BCD码编码
注意:RTC读写是禁止,读写前打开RTC,读写后关闭,读写RTC寄存器时,一定要注意BCD码和十进制之间的转换,BCDYEAR若2023写入(2023-2000)
demo1:
PWN定时器操作蜂鸣器
start.S
#define WTCON 0xE2700000
#define SVC_STACK 0xd0037d80
.global _start
.global IRQ_handler
_start:
//close watchDog
ldr r0,=WTCON
ldr r1,=0x0
str r1,[r0]
//init SVC stack
ldr sp,=SVC_STACK
//init icache
mrc p15,0,r0,c1,c0,0
bic r0,r0,#(1<<12) //close icache
orr r0,r0,#(1<<12) //open icache
mcr p15,0,r0,c1,c0,0
//use func
bl main
main.c
#include "pwn_buzzer.h"
static void dealy_time()
{
volatile unsigned int i = 900000;
while(i--);
}
int main()
{
pwn_buzzer_timer_init();
while(1)
{
dealy_time();
}
return 0;
}
pwn_buzzer.h
void pwn_buzzer_timer_init();
pwn_buzzer.c
#define GPD0CON 0xE02000A0
#define TCFG0 0xE2500000
#define TCFG1 0xE2500004
#define TCON 0xE2500008
#define TCNTB2 0xE2500024
#define TCMPB2 0xE2500028
#define rGPD0CON (*(volatile unsigned int *) GPD0CON)
#define rTCFG0 (*(volatile unsigned int *) TCFG0)
#define rTCFG1 (*(volatile unsigned int *) TCFG1)
#define rTCON (*(volatile unsigned int *) TCON)
#define rTCNTB0 (*(volatile unsigned int *) TCNTB2)
#define rTCMPB2 (*(volatile unsigned int *) TCMPB2)
void pwn_buzzer_timer_init()
{
//set gpio as buzzer
rGPD0CON &= ~(0x0f << 8);
rGPD0CON |= (2 << 8);
//set Prescaler as 65,real Prescaleris 66,Prescaler is 66Mhz /66 = 1Mhz
rTCFG0 &= ~(0xff<<8);
rTCFG0 = (65<<8);
//set div ,set div is 2 that mean 1/2,so 1Mhz /2 = 500000hz = 2us
rTCFG1 &= ~(0x0f<<8);
rTCFG1 = (1<<8);
//set TCON,set Auto Reload open
rTCON = (1<<15);
//set TCNTB0,set the count of cycle
//rTCNTB0 = time you need / set div
//ex: 1ms / 2us = 500
rTCNTB0 = 500;
//set TCMPB2,set the rate of duty,mean the rate of high and low level on all cycle
rTCMPB2 = 250;
//set TCON,when firstly open timer that you Manual Refresh TCNTB0 to TCNT
rTCON |= (1<<13);
//after Manual Refresh,close Manual Refresh,then always Auto Reload
rTCON &= ~(1<<13);
//set TCON,open timer
rTCON |= (1<<12);
}
Makefile
CC = arm-linux-gcc
LD = arm-linux-ld
OBJCOPY = arm-linux-objcopy
OBJDUMP = arm-linux-objdump
#预处理器的flag,flag就是编译器可选的选项
CPPFLAGS := -nostdlib -nostdinc
#C编译器的flag
CFLAGS := -Wall -O2 -fno-builtin
export CC LD OBJCOPY OBJDUMP CPPFLAGS CFLAGS
objs := start.o main.o pwn_buzzer.o
led.bin:$(objs)
$(LD) -Ttext 0x0 -o buzzer.elf $^
$(OBJCOPY) -O binary buzzer.elf buzzer.bin
$(OBJDUMP) -D buzzer.elf > buzzer.dis
gcc mkv210.c -o mkv210
./mkv210 buzzer.bin sd.bin
%.o:%.S
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< -c
%.o:%.c
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< -c
clean:
rm *.o *.elf *.bin *.dis mkv210 -f
demo2:
看门狗中断
start.S
#define WTCON 0xE2700000
#define SVC_STACK 0xd0037d80
#define IRQ_STACK 0xd0037f80
.global _start
.global IRQ_handler
_start:
//close watchDog
ldr r0,=WTCON
ldr r1,=0x0
str r1,[r0]
//init SVC stack
ldr sp,=SVC_STACK
//init icache
mrc p15,0,r0,c1,c0,0
bic r0,r0,#(1<<12) //close icache
orr r0,r0,#(1<<12) //open icache
mcr p15,0,r0,c1,c0,0
//use func
bl main
b .
//all interrupt process:Protect the scene + mian iqr process + return scene
IRQ_handler:
//init IRQ_STACK
ldr sp, =IRQ_STACK
//protect lr
sub lr, lr, #4
//protect r0--r12 that in irq stack
stmfd sp!, {r0-r12, lr}
//mian iqr process
bl irq_handler
//return scene
ldmfd sp!, {r0-r12, pc}^
main.c
#include "uart_printf.h"
#include "wdt_interrupt.h"
#include "stdio.h"
static void dealy_time()
{
volatile unsigned int i = 1000000;
while(i--);
}
int main()
{
//init uart
init_uart();
printf("init uart over\n");
//init outer interupt
printf("init wdt\n");
wdt_interrupt();
//init inner interupt
printf("init inner\n");
init_inner_interrupt();
//dealy time
while(1)
{
printf("..");
dealy_time();
}
return 0;
}
uart_printf.h
void init_uart();
uart_printf.c
#define GPA0CON 0xE0200000
#define ULCON0 0xE2900000
#define UCON0 0xE2900004
#define UFCON0 0xE2900008
#define UMCON0 0xE290000C
#define UTRSTAT0 0xE2900010
#define UTXH0 0xE2900020
#define URXH0 0xE2900024
#define UBRDIV0 0xE2900028
#define UDIVSLOT0 0xE290002C
#define rGPA0CON (*(volatile unsigned int *)GPA0CON)
#define rULCON0 (*(volatile unsigned int *)ULCON0)
#define rUCON0 (*(volatile unsigned int *)UCON0)
#define rUFCON0 (*(volatile unsigned int *)UFCON0)
#define rUMCON0 (*(volatile unsigned int *)UMCON0)
#define rUTRSTAT0 (*(volatile unsigned int *)UTRSTAT0)
#define rUTXH0 (*(volatile unsigned int *)UTXH0)
#define rURXH0 (*(volatile unsigned int *)URXH0)
#define rUBRDIV0 (*(volatile unsigned int *)UBRDIV0)
#define rUDIVSLOT0 (*(volatile unsigned int *)UDIVSLOT0)
//init uart
void init_uart()
{
//set gpio as uart(rx,tx)
rGPA0CON &= ~(0xff);
rGPA0CON |= ((1<<2)|(1<<5));
//set uart base configure(mode)
rULCON0 = 0x3;
rUCON0 = 0x5;
rUMCON0 = 0;
rUFCON0 = 0;
//set uart baud
//DIV_VAL = (PCLK / (bps x 16))-1
//(66000000 /(115200 * 16)) -1 = 34.8
rUBRDIV0 = 34;
//set uart baud calibration
//0.8 * 16 = 13,check 210 table
rUDIVSLOT0 = 0xdfdd;
}
//send data
void putc(char data)
{
while (!(rUTRSTAT0 & (1<<1)));
rUTXH0 = data;
}
//receive data
char getc()
{
while (!(rUTRSTAT0 & (1<<0)));
return (rURXH0 & 0xff);
}
wdt_interrupt.h
void wdt_interrupt();
void init_inner_interrupt()
wdt_interrupt.c
#include "stdio.h"
//watchDog interrupt register
#define WTCON 0xE2700000
#define WTDAT 0xE2700004
#define WTCNT 0xE2700008
#define WTCLRIN 0xE270000C
#define rWTCON *((volatile unsigned int *) WTCON)
#define rWTDAT *((volatile unsigned int *) WTDAT)
#define rWTCNT *((volatile unsigned int *) WTCNT)
#define rWTCLRI *((volatile unsigned int *) WTCLRI)
//inner interrupt register
//VIC base address
#define VIC0_BASE 0xF2000000
#define VIC1_BASE 0xF2100000
#define VIC2_BASE 0xF2200000
#define VIC3_BASE 0xF2300000
//VIC0 register
#define rVIC0IRQSTATUS (*(volatile unsigned int *)(VIC0_BASE + 0x0000))
#define rVIC0FIQSTATUS (*(volatile unsigned int *)(VIC0_BASE + 0x0004))
#define rVIC0INTSELECT (*(volatile unsigned int *)(VIC0_BASE + 0x000C))
#define rVIC0INTENABLE (*(volatile unsigned int *)(VIC0_BASE + 0x0010))
#define rVIC0INTENCLEAR (*(volatile unsigned int *)(VIC0_BASE + 0x0014))
#define rVIC0VECTADDR (VIC0_BASE + 0x100)
#define rVIC0ADDRESS (*(volatile unsigned int *)(VIC0_BASE + 0x0F00))
//VIC1 register
#define rVIC1IRQSTATUS (*(volatile unsigned int *)(VIC1_BASE + 0x0000))
#define rVIC1FIQSTATUS (*(volatile unsigned int *)(VIC1_BASE + 0x0004))
#define rVIC1INTSELECT (*(volatile unsigned int *)(VIC1_BASE + 0x000C))
#define rVIC1INTENABLE (*(volatile unsigned int *)(VIC1_BASE + 0x0010))
#define rVIC1INTENCLEAR (*(volatile unsigned int *)(VIC1_BASE + 0x0014))
#define rVIC1VECTADDR (VIC1_BASE + 0x100)
#define rVIC1ADDRESS (*(volatile unsigned int *)(VIC1_BASE + 0x0F00))
//VIC2 register
#define rVIC2IRQSTATUS (*(volatile unsigned int *)(VIC2_BASE + 0x0000))
#define rVIC2FIQSTATUS (*(volatile unsigned int *)(VIC2_BASE + 0x0004))
#define rVIC2INTSELECT (*(volatile unsigned int *)(VIC2_BASE + 0x000C))
#define rVIC2INTENABLE (*(volatile unsigned int *)(VIC2_BASE + 0x0010))
#define rVIC2INTENCLEAR (*(volatile unsigned int *)(VIC2_BASE + 0x0014))
#define rVIC2VECTADDR (VIC2_BASE + 0x100)
#define rVIC2ADDRESS (*(volatile unsigned int *)(VIC2_BASE + 0x0F00))
//VIC3 register
#define rVIC3IRQSTATUS (*(volatile unsigned int *)(VIC3_BASE + 0x0000))
#define rVIC3FIQSTATUS (*(volatile unsigned int *)(VIC3_BASE + 0x0004))
#define rVIC3INTSELECT (*(volatile unsigned int *)(VIC3_BASE + 0x000C))
#define rVIC3INTENABLE (*(volatile unsigned int *)(VIC3_BASE + 0x0010))
#define rVIC3INTENCLEAR (*(volatile unsigned int *)(VIC3_BASE + 0x0014))
#define rVIC3VECTADDR (VIC3_BASE + 0x100)
#define rVIC3ADDRESS (*(volatile unsigned int *)(VIC3_BASE + 0x0F00))
//interrupt vector table
#define vector_table_base 0xD0037400
#define reset_vector (vector_table_base + 0x00)
#define undef_vector (vector_table_base + 0x04)
#define sotf_interrupt_vector (vector_table_base + 0x08)
#define prefetch_vector (vector_table_base + 0x0C)
#define data_vector (vector_table_base + 0x10)
#define irq_vector (vector_table_base + 0x18)
#define fiq_vector (vector_table_base + 0x1C)
#define r_reset_vector (*(volatile unsigned int *) reset_vector)
#define r_undef_vector (*(volatile unsigned int *) undef_vector)
#define r_sotf_interrupt_vector (*(volatile unsigned int *) sotf_interrupt_vector)
#define r_prefetch_vector (*(volatile unsigned int *) prefetch_vector)
#define r_data_vector (*(volatile unsigned int *) data_vector)
#define r_irq_vector (*(volatile unsigned int *) irq_vector)
#define r_fiq_vector (*(volatile unsigned int *) fiq_vector)
//interrupt number
#define NUM_TIMER0 (21)
#define NUM_TIMER1 (22)
#define NUM_TIMER2 (23)
#define NUM_TIMER3 (24)
#define NUM_TIMER4 (25)
#define NUM_SYSTIMER (26)
#define NUM_WDT (27)
#define NUM_RTC_ALARM (28)
#define NUM_RTC_TICK (29)
//wdt interrupt func
void wdt_interrupt()
{
//set WTCON,set Prescaler,Prescaler is set value + 1
//Prescaler = 65 + 1 = 66,66Mhz / 66 = 1Mhz = 1000000hz
rWTCON &= ~(0xff << 8);
rWTCON |= (65<<8);
//set div is 128,t = 1/(1000000hz /128) = 1.28us
rWTCON &= ~(3 << 3);
rWTCON |= (3<<8);
//set WTCON,open interrupt and close reset
rWTCON |= (1<<2);
rWTCON &= ~(1);
//set WTDAT,set set the time form firstly open timer to count open(WTCNT)
rWTDAT = 1000;
rWTCNT = 1000;
//open wdt
rWTCON |= (1<<5);
}
//dealy_time
static void dealy_time()
{
volatile unsigned int i = 1000000;
while(i--);
}
//outer interrupt key func
static void isr_wdt()
{
static int num = 0;
printf("wdt interrupt,num = %d\n",num++);
dealy_time();
//clear VIC0ADDR,clear using interrupt process
rVIC0ADDRESS = 0;
rVIC1ADDRESS = 0;
rVIC2ADDRESS = 0;
rVIC3ADDRESS = 0;
}
//inner interrupt func
static void reset_func()
{
printf("reset\n");
}
static void undef_func()
{
printf("undef\n");
}
static void sotf_interrupt_func()
{
printf("sotf_intrrupt\n");
}
static void prefetch_func()
{
printf("prefetch\n");
}
static void data_func()
{
printf("data\n");
}
static void fiq_func()
{
printf("irq\n");
}
void IRQ_handler();
static void bind_isr_VICnINTENCLEAR(unsigned long num,void (*handler)())
{
if(num <32)
{
printf("bind ok\n");
*((volatile unsigned int *)(rVIC0VECTADDR + 4 * (num))) = (unsigned)handler;
}
else if(num < 64)
{
*((volatile unsigned int *)(rVIC0VECTADDR + 4 * (num-32))) = (unsigned)handler;
}
else if(num < 96)
{
*((volatile unsigned int *)(rVIC0VECTADDR + 4 * (num-64))) = (unsigned)handler;
}
else
{
*((volatile unsigned int *)(rVIC0VECTADDR + 4 * (num-96))) = (unsigned)handler;
}
}
static void enable_interrupt(unsigned long num)
{
unsigned long tmp;
if(num < 32)
{
tmp = rVIC0INTENABLE;
tmp |= (1<<num);
rVIC0INTENABLE = tmp;
}
else if(num < 64)
{
tmp = rVIC0INTENABLE;
tmp |= (1<<(num-32));
rVIC1INTENABLE = tmp;
}
else if(num < 96)
{
tmp = rVIC0INTENABLE;
tmp |= (1<<(num-64));
rVIC2INTENABLE = tmp;
}
else if(num < 200)
{
tmp = rVIC0INTENABLE;
tmp |= (1<<(num-96));
rVIC3INTENABLE = tmp;
}
else
{
rVIC0INTENABLE = 0xffffffff;
rVIC1INTENABLE = 0xffffffff;
rVIC2INTENABLE = 0xffffffff;
rVIC3INTENABLE = 0xffffffff;
}
}
void init_inner_interrupt()
{
//bind interrupt process on interrupt vector table
r_reset_vector = (unsigned int)reset_func;
r_undef_vector = (unsigned int)undef_func;
r_sotf_interrupt_vector = (unsigned int)sotf_interrupt_func;
r_prefetch_vector = (unsigned int)prefetch_func;
r_data_vector = (unsigned int)data_func;
r_irq_vector = (unsigned int)IRQ_handler;
r_fiq_vector = (unsigned int)fiq_func;
//select interrupt mode(irq)
rVIC0INTSELECT = 0x0;
rVIC1INTSELECT = 0x0;
rVIC2INTSELECT = 0x0;
rVIC3INTSELECT = 0x0;
//diasble interrupt
rVIC0INTENCLEAR = 0xffffffff;
rVIC1INTENCLEAR = 0xffffffff;
rVIC2INTENCLEAR = 0xffffffff;
rVIC3INTENCLEAR = 0xffffffff;
//clear interrupt process address
rVIC0ADDRESS = 0;
rVIC1ADDRESS = 0;
rVIC2ADDRESS = 0;
rVIC3ADDRESS = 0;
//bind isr process on VICnINTENCLEAR
bind_isr_VICnINTENCLEAR(NUM_WDT,isr_wdt);
//enable interrupt
enable_interrupt(NUM_WDT);
}
//judge inner interrupt ,get interrupt occure in which VICnVECTADDR
void irq_handler()
{
volatile unsigned int n = 0;
void (*isr)(void) = NULL;
for(n = 0;n <4;n++)
{
if(n == 0 && rVIC0IRQSTATUS != 0)
{
isr = (void (*)(void))rVIC0ADDRESS;
}
else if(n == 1 && rVIC1IRQSTATUS != 0)
{
isr = (void (*)(void))rVIC1ADDRESS;
}
else if(n == 2 && rVIC2IRQSTATUS != 0)
{
isr = (void (*)(void))rVIC2ADDRESS;
}
else if(n == 3 && rVIC3IRQSTATUS != 0)
{
isr = (void (*)(void))rVIC3ADDRESS;
}
(*isr)();
}
}
link.lds
SECTIONS
{
. = 0xd0020010;
.text :
{
start.o
*(.text)
}
.data :
{
*(.data)
}
.bss :
{
*(.bss)
}
}
Makefile
CC = arm-linux-gcc
LD = arm-linux-ld
OBJCOPY = arm-linux-objcopy
OBJDUMP = arm-linux-objdump
INCDIR := $(shell pwd)
#预处理器的flag,flag就是编译器可选的选项
CPPFLAGS := -nostdlib -nostdinc -I$(INCDIR)/include
#C编译器的flag
CFLAGS := -Wall -O2 -fno-builtin
export CC LD OBJCOPY OBJDUMP CPPFLAGS CFLAGS
objs := start.o uart_printf.o main.o wdt_interrupt.o
objs += lib/libc.a
led.bin:$(objs)
$(LD) -Tlink.lds -o wdt_interrupt.elf $^
$(OBJCOPY) -O binary wdt_interrupt.elf wdt_interrupt.bin
$(OBJDUMP) -D wdt_interrupt.elf > wdt_interrupt.dis
gcc mkv210.c -o mkv210
./mkv210 wdt_interrupt.bin sd.bin
lib/libc.a:
cd lib; make; cd ..
%.o:%.S
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< -c
%.o:%.c
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< -c
clean:
rm *.o *.elf *.bin *.dis mkv210 -f
cd lib; make clean; cd ..
结果示例:
demo3:
看门狗复位
start.S
#define WTCON 0xE2700000
#define SVC_STACK 0xd0037d80
.global _start
.global IRQ_handler
_start:
//close watchDog
ldr r0,=WTCON
ldr r1,=0x0
str r1,[r0]
//init SVC stack
ldr sp,=SVC_STACK
//init icache
mrc p15,0,r0,c1,c0,0
bic r0,r0,#(1<<12) //close icache
orr r0,r0,#(1<<12) //open icache
mcr p15,0,r0,c1,c0,0
//use func
bl main
b .
main.c
#include "uart_printf.h"
#include "wdt_reset.h"
#include "stdio.h"
static void dealy_time()
{
volatile unsigned int i = 1000000;
while(i--);
}
int main()
{
//init uart
init_uart();
printf("init uart over\n");
//init outer interupt
static int num = 1;
printf("init wdt,num = %d\n",num++);
wdt_reset();
//dealy_time
while(1)
{
dealy_time();
}
return 0;
}
uart_printf.h
void init_uart();
uart_printf.c
#define GPA0CON 0xE0200000
#define ULCON0 0xE2900000
#define UCON0 0xE2900004
#define UFCON0 0xE2900008
#define UMCON0 0xE290000C
#define UTRSTAT0 0xE2900010
#define UTXH0 0xE2900020
#define URXH0 0xE2900024
#define UBRDIV0 0xE2900028
#define UDIVSLOT0 0xE290002C
#define rGPA0CON (*(volatile unsigned int *)GPA0CON)
#define rULCON0 (*(volatile unsigned int *)ULCON0)
#define rUCON0 (*(volatile unsigned int *)UCON0)
#define rUFCON0 (*(volatile unsigned int *)UFCON0)
#define rUMCON0 (*(volatile unsigned int *)UMCON0)
#define rUTRSTAT0 (*(volatile unsigned int *)UTRSTAT0)
#define rUTXH0 (*(volatile unsigned int *)UTXH0)
#define rURXH0 (*(volatile unsigned int *)URXH0)
#define rUBRDIV0 (*(volatile unsigned int *)UBRDIV0)
#define rUDIVSLOT0 (*(volatile unsigned int *)UDIVSLOT0)
//init uart
void init_uart()
{
//set gpio as uart(rx,tx)
rGPA0CON &= ~(0xff);
rGPA0CON |= ((1<<2)|(1<<5));
//set uart base configure(mode)
rULCON0 = 0x3;
rUCON0 = 0x5;
rUMCON0 = 0;
rUFCON0 = 0;
//set uart baud
//DIV_VAL = (PCLK / (bps x 16))-1
//(66000000 /(115200 * 16)) -1 = 34.8
rUBRDIV0 = 34;
//set uart baud calibration
//0.8 * 16 = 13,check 210 table
rUDIVSLOT0 = 0xdfdd;
}
//send data
void putc(char data)
{
while (!(rUTRSTAT0 & (1<<1)));
rUTXH0 = data;
}
//receive data
char getc()
{
while (!(rUTRSTAT0 & (1<<0)));
return (rURXH0 & 0xff);
}
wdt_reset.h
void wdt_reset();
wdt_reset.c
#define WTCON 0xE2700000
#define WTDAT 0xE2700004
#define WTCNT 0xE2700008
#define WTCLRIN 0xE270000C
#define rWTCON *((volatile unsigned int *) WTCON)
#define rWTDAT *((volatile unsigned int *) WTDAT)
#define rWTCNT *((volatile unsigned int *) WTCNT)
#define rWTCLRI *((volatile unsigned int *) WTCLRI)
void wdt_reset()
{
//set WTCON,set Prescaler,Prescaler is set value + 1
//Prescaler = 65 + 1 = 66,66Mhz / 66 = 1Mhz = 1000000hz
rWTCON &= ~(0xff << 8);
rWTCON |= (65<<8);
//set div is 128,t = 1/(1000000hz /128) = 1.28us
rWTCON &= ~(3 << 3);
rWTCON |= (3<<8);
//set WTCON,close interrupt and open reset
rWTCON &= ~(1<<2);
rWTCON |= 1;
//set WTDAT,set set the time form firstly open timer to count open(WTCNT)
rWTDAT = 1000;
rWTCNT = 1000;
//open wdt
rWTCON |= (1<<5);
}
link.lds
SECTIONS
{
. = 0xd0020010;
.text :
{
start.o
*(.text)
}
.data :
{
*(.data)
}
.bss :
{
*(.bss)
}
}
Makefile
CC = arm-linux-gcc
LD = arm-linux-ld
OBJCOPY = arm-linux-objcopy
OBJDUMP = arm-linux-objdump
INCDIR := $(shell pwd)
#预处理器的flag,flag就是编译器可选的选项
CPPFLAGS := -nostdlib -nostdinc -I$(INCDIR)/include
#C编译器的flag
CFLAGS := -Wall -O2 -fno-builtin
export CC LD OBJCOPY OBJDUMP CPPFLAGS CFLAGS
objs := start.o uart_printf.o main.o wdt_reset.o
objs += lib/libc.a
led.bin:$(objs)
$(LD) -Tlink.lds -o wdt_reset.elf $^
$(OBJCOPY) -O binary wdt_reset.elf wdt_reset.bin
$(OBJDUMP) -D wdt_reset.elf > wdt_reset.dis
gcc mkv210.c -o mkv210
./mkv210 wdt_reset.bin sd.bin
lib/libc.a:
cd lib; make; cd ..
%.o:%.S
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< -c
%.o:%.c
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< -c
clean:
rm *.o *.elf *.bin *.dis mkv210 -f
cd lib; make clean; cd ..
结果示例:
demo4:
中断(alarm)读写RTC
start.S
#define WTCON 0xE2700000
#define SVC_STACK 0xd0037d80
#define IRQ_STACK 0xd0037f80
.global _start
.global IRQ_handler
_start:
//close watchDog
ldr r0,=WTCON
ldr r1,=0x0
str r1,[r0]
//init SVC stack
ldr sp,=SVC_STACK
//init icache
mrc p15,0,r0,c1,c0,0
bic r0,r0,#(1<<12) //close icache
orr r0,r0,#(1<<12) //open icache
mcr p15,0,r0,c1,c0,0
//use func
bl main
b .
//all interrupt process:Protect the scene + mian iqr process + return scene
IRQ_handler:
//init IRQ_STACK
ldr sp, =IRQ_STACK
//protect lr
sub lr, lr, #4
//protect r0--r12 that in irq stack
stmfd sp!, {r0-r12, lr}
//mian iqr process
bl irq_handler
//return scene
ldmfd sp!, {r0-r12, pc}^
main.c
#include "uart_printf.h"
#include "rtc_alarm.h"
#include "stdio.h"
static void dealy_time()
{
volatile unsigned int i,j;
for (i=0; i<10000; i++)
for (j=0; j<1000; j++);
}
int main()
{
//init uart
init_uart();
printf("init uart over\n");
//init outer interupt
printf("rtc init\n");
rtc_alarm();
//set RTC time
struct rtc_time rtc_write =
{
.year = 2023,
.month = 10,
.date = 22,
.hour = 14,
.minute = 00,
.second = 00,
.day = 0,
};
set_rtc_time(&rtc_write);
//init inner interupt
printf("init inner\n");
init_inner_interrupt();
struct rtc_time rtc_read;
//dealy time
while(1)
{
get_rtc_time(&rtc_read);
printf("rtc time:%d-%d-%d--%d %d:%d:%d\n", rtc_read.year,rtc_read.month,rtc_read.date,rtc_read.day,rtc_read.hour,rtc_read.minute,rtc_read.second);
dealy_time();
}
while(1);
return 0;
}
uart_printf.h
void init_uart();
uart_printf.c
#define GPA0CON 0xE0200000
#define ULCON0 0xE2900000
#define UCON0 0xE2900004
#define UFCON0 0xE2900008
#define UMCON0 0xE290000C
#define UTRSTAT0 0xE2900010
#define UTXH0 0xE2900020
#define URXH0 0xE2900024
#define UBRDIV0 0xE2900028
#define UDIVSLOT0 0xE290002C
#define rGPA0CON (*(volatile unsigned int *)GPA0CON)
#define rULCON0 (*(volatile unsigned int *)ULCON0)
#define rUCON0 (*(volatile unsigned int *)UCON0)
#define rUFCON0 (*(volatile unsigned int *)UFCON0)
#define rUMCON0 (*(volatile unsigned int *)UMCON0)
#define rUTRSTAT0 (*(volatile unsigned int *)UTRSTAT0)
#define rUTXH0 (*(volatile unsigned int *)UTXH0)
#define rURXH0 (*(volatile unsigned int *)URXH0)
#define rUBRDIV0 (*(volatile unsigned int *)UBRDIV0)
#define rUDIVSLOT0 (*(volatile unsigned int *)UDIVSLOT0)
//init uart
void init_uart()
{
//set gpio as uart(rx,tx)
rGPA0CON &= ~(0xff);
rGPA0CON |= ((1<<2)|(1<<5));
//set uart base configure(mode)
rULCON0 = 0x3;
rUCON0 = 0x5;
rUMCON0 = 0;
rUFCON0 = 0;
//set uart baud
//DIV_VAL = (PCLK / (bps x 16))-1
//(66000000 /(115200 * 16)) -1 = 34.8
rUBRDIV0 = 34;
//set uart baud calibration
//0.8 * 16 = 13,check 210 table
rUDIVSLOT0 = 0xdfdd;
}
//send data
void putc(char data)
{
while (!(rUTRSTAT0 & (1<<1)));
rUTXH0 = data;
}
//receive data
char getc()
{
while (!(rUTRSTAT0 & (1<<0)));
return (rURXH0 & 0xff);
}
rtc_struct.h
struct rtc_time
{
unsigned int year;
unsigned int month;
unsigned int date; //几号
unsigned int hour;
unsigned int minute;
unsigned int second;
unsigned int day; //星期
};
rtc_alarm.h
#include "rtc_struct.h"
void rtc_alarm();
void set_rtc_time(struct rtc_time *p);
void get_rtc_time(struct rtc_time *p);
void init_inner_interrupt();
rtc_alarm.c
#include "stdio.h"
#include "rtc_struct.h"
//rtc interrupt register
#define RTC_BASE 0xE2800000
#define rINTP (*((volatile unsigned long *)(RTC_BASE + 0x30)))
#define rRTCCON (*((volatile unsigned long *)(RTC_BASE + 0x40)))
#define rTICCNT (*((volatile unsigned long *)(RTC_BASE + 0x44)))
#define rRTCALM (*((volatile unsigned long *)(RTC_BASE + 0x50)))
#define rALMSEC (*((volatile unsigned long *)(RTC_BASE + 0x54)))
#define rALMMIN (*((volatile unsigned long *)(RTC_BASE + 0x58)))
#define rALMHOUR (*((volatile unsigned long *)(RTC_BASE + 0x5c)))
#define rALMDATE (*((volatile unsigned long *)(RTC_BASE + 0x60)))
#define rALMMON (*((volatile unsigned long *)(RTC_BASE + 0x64)))
#define rALMYEAR (*((volatile unsigned long *)(RTC_BASE + 0x68)))
#define rRTCRST (*((volatile unsigned long *)(RTC_BASE + 0x6c)))
#define rBCDSEC (*((volatile unsigned long *)(RTC_BASE + 0x70)))
#define rBCDMIN (*((volatile unsigned long *)(RTC_BASE + 0x74)))
#define rBCDHOUR (*((volatile unsigned long *)(RTC_BASE + 0x78)))
#define rBCDDATE (*((volatile unsigned long *)(RTC_BASE + 0x7c)))
#define rBCDDAY (*((volatile unsigned long *)(RTC_BASE + 0x80)))
#define rBCDMON (*((volatile unsigned long *)(RTC_BASE + 0x84)))
#define rBCDYEAR (*((volatile unsigned long *)(RTC_BASE + 0x88)))
#define rCURTICCNT (*((volatile unsigned long *)(RTC_BASE + 0x90)))
#define rRTCLVD (*((volatile unsigned long *)(RTC_BASE + 0x94)))
//inner interrupt register
//VIC base address
#define VIC0_BASE 0xF2000000
#define VIC1_BASE 0xF2100000
#define VIC2_BASE 0xF2200000
#define VIC3_BASE 0xF2300000
//VIC0 register
#define rVIC0IRQSTATUS (*(volatile unsigned int *)(VIC0_BASE + 0x0000))
#define rVIC0FIQSTATUS (*(volatile unsigned int *)(VIC0_BASE + 0x0004))
#define rVIC0INTSELECT (*(volatile unsigned int *)(VIC0_BASE + 0x000C))
#define rVIC0INTENABLE (*(volatile unsigned int *)(VIC0_BASE + 0x0010))
#define rVIC0INTENCLEAR (*(volatile unsigned int *)(VIC0_BASE + 0x0014))
#define rVIC0VECTADDR (VIC0_BASE + 0x100)
#define rVIC0ADDRESS (*(volatile unsigned int *)(VIC0_BASE + 0x0F00))
//VIC1 register
#define rVIC1IRQSTATUS (*(volatile unsigned int *)(VIC1_BASE + 0x0000))
#define rVIC1FIQSTATUS (*(volatile unsigned int *)(VIC1_BASE + 0x0004))
#define rVIC1INTSELECT (*(volatile unsigned int *)(VIC1_BASE + 0x000C))
#define rVIC1INTENABLE (*(volatile unsigned int *)(VIC1_BASE + 0x0010))
#define rVIC1INTENCLEAR (*(volatile unsigned int *)(VIC1_BASE + 0x0014))
#define rVIC1VECTADDR (VIC1_BASE + 0x100)
#define rVIC1ADDRESS (*(volatile unsigned int *)(VIC1_BASE + 0x0F00))
//VIC2 register
#define rVIC2IRQSTATUS (*(volatile unsigned int *)(VIC2_BASE + 0x0000))
#define rVIC2FIQSTATUS (*(volatile unsigned int *)(VIC2_BASE + 0x0004))
#define rVIC2INTSELECT (*(volatile unsigned int *)(VIC2_BASE + 0x000C))
#define rVIC2INTENABLE (*(volatile unsigned int *)(VIC2_BASE + 0x0010))
#define rVIC2INTENCLEAR (*(volatile unsigned int *)(VIC2_BASE + 0x0014))
#define rVIC2VECTADDR (VIC2_BASE + 0x100)
#define rVIC2ADDRESS (*(volatile unsigned int *)(VIC2_BASE + 0x0F00))
//VIC3 register
#define rVIC3IRQSTATUS (*(volatile unsigned int *)(VIC3_BASE + 0x0000))
#define rVIC3FIQSTATUS (*(volatile unsigned int *)(VIC3_BASE + 0x0004))
#define rVIC3INTSELECT (*(volatile unsigned int *)(VIC3_BASE + 0x000C))
#define rVIC3INTENABLE (*(volatile unsigned int *)(VIC3_BASE + 0x0010))
#define rVIC3INTENCLEAR (*(volatile unsigned int *)(VIC3_BASE + 0x0014))
#define rVIC3VECTADDR (VIC3_BASE + 0x100)
#define rVIC3ADDRESS (*(volatile unsigned int *)(VIC3_BASE + 0x0F00))
//interrupt vector table
#define vector_table_base 0xD0037400
#define reset_vector (vector_table_base + 0x00)
#define undef_vector (vector_table_base + 0x04)
#define sotf_interrupt_vector (vector_table_base + 0x08)
#define prefetch_vector (vector_table_base + 0x0C)
#define data_vector (vector_table_base + 0x10)
#define irq_vector (vector_table_base + 0x18)
#define fiq_vector (vector_table_base + 0x1C)
#define r_reset_vector (*(volatile unsigned int *) reset_vector)
#define r_undef_vector (*(volatile unsigned int *) undef_vector)
#define r_sotf_interrupt_vector (*(volatile unsigned int *) sotf_interrupt_vector)
#define r_prefetch_vector (*(volatile unsigned int *) prefetch_vector)
#define r_data_vector (*(volatile unsigned int *) data_vector)
#define r_irq_vector (*(volatile unsigned int *) irq_vector)
#define r_fiq_vector (*(volatile unsigned int *) fiq_vector)
//interrupt number
#define NUM_TIMER0 (21)
#define NUM_TIMER1 (22)
#define NUM_TIMER2 (23)
#define NUM_TIMER3 (24)
#define NUM_TIMER4 (25)
#define NUM_SYSTIMER (26)
#define NUM_WDT (27)
#define NUM_RTC_ALARM (28)
#define NUM_RTC_TICK (29)
//rtc interrupt func
//rtc func
static void isr_rtc_alarm()
{
static int num = 0;
printf("rtc interrupt, num = %d...",num++);
//open rtc alarm interrupt
rINTP |= (1<<1);
//clear VIC0ADDR,clear using interrupt process
rVIC0ADDRESS = 0;
rVIC1ADDRESS = 0;
rVIC2ADDRESS = 0;
rVIC3ADDRESS = 0;
}
static unsigned int num_2_bcd(unsigned int num)
{
return (((num / 10)<< 4) | (num % 10));
}
static unsigned int bcd_2_num(unsigned int bcd)
{
return ((((bcd & (0xf0)) >> 4) *10) + (bcd & (0x0f)) );
}
void set_rtc_time(struct rtc_time *p)
{
//set RTCCON,open RTC
rRTCCON |= 1;
//set year month date,hour min,sec,day
rBCDYEAR = num_2_bcd(p->year - 2000);
rBCDMON = num_2_bcd(p->month);
rBCDDATE = num_2_bcd(p->date);
rBCDHOUR = num_2_bcd(p->hour);
rBCDMIN = num_2_bcd(p->minute);
rBCDSEC = num_2_bcd(p->second);
rBCDDAY = num_2_bcd(p->day);
//set RTCCON,close RTC
rRTCCON &= ~(1);
}
void get_rtc_time(struct rtc_time *p)
{
//set RTCCON,open RTC
rRTCCON |= 1;
//get year month date,hour min,sec,day
p->year = bcd_2_num(rBCDYEAR) + 2000;
p->month = bcd_2_num(rBCDMON);
p->date = bcd_2_num(rBCDDATE);
p->hour = bcd_2_num(rBCDHOUR);
p->minute = bcd_2_num(rBCDMIN);
p->second = bcd_2_num(rBCDSEC);
p->day = bcd_2_num(rBCDDAY);
//set RTCCON,close RTC
rRTCCON &= ~(1);
}
void rtc_alarm()
{
//set alarm pre 10 trigger
rALMSEC = num_2_bcd(10);
//set sec used
rRTCALM |= 1<<0;
//set alarm enable
rRTCALM |= 1<<6;
}
//inner interrupt func
static void reset_func()
{
printf("reset\n");
}
static void undef_func()
{
printf("undef\n");
}
static void sotf_interrupt_func()
{
printf("sotf_intrrupt\n");
}
static void prefetch_func()
{
printf("prefetch\n");
}
static void data_func()
{
printf("data\n");
}
static void fiq_func()
{
printf("irq\n");
}
void IRQ_handler();
static void bind_isr_VICnINTENCLEAR(unsigned long num,void (*handler)())
{
if(num <32)
{
printf("bind ok\n");
*((volatile unsigned int *)(rVIC0VECTADDR + 4 * (num))) = (unsigned)handler;
}
else if(num < 64)
{
*((volatile unsigned int *)(rVIC0VECTADDR + 4 * (num-32))) = (unsigned)handler;
}
else if(num < 96)
{
*((volatile unsigned int *)(rVIC0VECTADDR + 4 * (num-64))) = (unsigned)handler;
}
else
{
*((volatile unsigned int *)(rVIC0VECTADDR + 4 * (num-96))) = (unsigned)handler;
}
}
static void enable_interrupt(unsigned long num)
{
unsigned long tmp;
if(num < 32)
{
tmp = rVIC0INTENABLE;
tmp |= (1<<num);
rVIC0INTENABLE = tmp;
}
else if(num < 64)
{
tmp = rVIC0INTENABLE;
tmp |= (1<<(num-32));
rVIC1INTENABLE = tmp;
}
else if(num < 96)
{
tmp = rVIC0INTENABLE;
tmp |= (1<<(num-64));
rVIC2INTENABLE = tmp;
}
else if(num < 200)
{
tmp = rVIC0INTENABLE;
tmp |= (1<<(num-96));
rVIC3INTENABLE = tmp;
}
else
{
rVIC0INTENABLE = 0xffffffff;
rVIC1INTENABLE = 0xffffffff;
rVIC2INTENABLE = 0xffffffff;
rVIC3INTENABLE = 0xffffffff;
}
}
void init_inner_interrupt()
{
//bind interrupt process on interrupt vector table
r_reset_vector = (unsigned int)reset_func;
r_undef_vector = (unsigned int)undef_func;
r_sotf_interrupt_vector = (unsigned int)sotf_interrupt_func;
r_prefetch_vector = (unsigned int)prefetch_func;
r_data_vector = (unsigned int)data_func;
r_irq_vector = (unsigned int)IRQ_handler;
r_fiq_vector = (unsigned int)fiq_func;
//select interrupt mode(irq)
rVIC0INTSELECT = 0x0;
rVIC1INTSELECT = 0x0;
rVIC2INTSELECT = 0x0;
rVIC3INTSELECT = 0x0;
//diasble interrupt
rVIC0INTENCLEAR = 0xffffffff;
rVIC1INTENCLEAR = 0xffffffff;
rVIC2INTENCLEAR = 0xffffffff;
rVIC3INTENCLEAR = 0xffffffff;
//clear interrupt process address
rVIC0ADDRESS = 0;
rVIC1ADDRESS = 0;
rVIC2ADDRESS = 0;
rVIC3ADDRESS = 0;
//bind isr process on VICnINTENCLEAR
bind_isr_VICnINTENCLEAR(NUM_RTC_ALARM,isr_rtc_alarm);
//enable interrupt
enable_interrupt(NUM_RTC_ALARM);
}
//judge inner interrupt ,get interrupt occure in which VICnVECTADDR
void irq_handler()
{
volatile unsigned int n = 0;
void (*isr)(void) = NULL;
for(n = 0;n <4;n++)
{
if(n == 0 && rVIC0IRQSTATUS != 0)
{
isr = (void (*)(void))rVIC0ADDRESS;
}
else if(n == 1 && rVIC1IRQSTATUS != 0)
{
isr = (void (*)(void))rVIC1ADDRESS;
}
else if(n == 2 && rVIC2IRQSTATUS != 0)
{
isr = (void (*)(void))rVIC2ADDRESS;
}
else if(n == 3 && rVIC3IRQSTATUS != 0)
{
isr = (void (*)(void))rVIC3ADDRESS;
}
(*isr)();
}
}
link.lds
SECTIONS
{
. = 0xd0020010;
.text :
{
start.o
*(.text)
}
.data :
{
*(.data)
}
.bss :
{
*(.bss)
}
}
Makefile
CC = arm-linux-gcc
LD = arm-linux-ld
OBJCOPY = arm-linux-objcopy
OBJDUMP = arm-linux-objdump
INCDIR := $(shell pwd)
#预处理器的flag,flag就是编译器可选的选项
CPPFLAGS := -nostdlib -nostdinc -I$(INCDIR)/include
#C编译器的flag
CFLAGS := -Wall -O2 -fno-builtin
export CC LD OBJCOPY OBJDUMP CPPFLAGS CFLAGS
objs := start.o uart_printf.o main.o rtc_alarm.o
objs += lib/libc.a
led.bin:$(objs)
$(LD) -Tlink.lds -o rtc_alarm.elf $^
$(OBJCOPY) -O binary rtc_alarm.elf rtc_alarm.bin
$(OBJDUMP) -D rtc_alarm.elf > rtc_alarm.dis
gcc mkv210.c -o mkv210
./mkv210 rtc_alarm.bin sd.bin
lib/libc.a:
cd lib; make; cd ..
%.o:%.S
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< -c
%.o:%.c
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< -c
clean:
rm *.o *.elf *.bin *.dis mkv210 -f
cd lib; make clean; cd ..
结果示例: