ARM7裸机开发学习日志01

来源:互联网 发布:拳击书籍知乎 编辑:程序博客网 时间:2024/04/30 07:29


总目标:

从启动代码开始写出一套代码让芯片能够跑起来,并且能够实现简单的功能部件编程。



学习目标:

1.复习ARM基础

2.重新对功能部件编程

3.开始着手启动代码


1.嵌入式系统的分类

Embedded Microprocessor Unit——EMPU——单板机——嵌入式微处理器(通用)

Microcontroller Unit——MCU——单片机——微控制器(通用)

Digital Signal Processor ——DSP——DSP处理器(专用)

System On Chip——SoC——片上系统


2.ARM7体系结构

ARM处理器直接支持1字节,2字节,4字节的数据类型。

1字=4字节

分为字方式存储和半字方式存储

1字节=8个二进制位   1Byte=8bit


采用3级流水线,即PC总指向当前指令后面第二条(执行-译码-取值),ARM状态下:PC值=当前程序执行位置+8字节。Thumb状态下:PC值=当前程序执行位置+4字节


ARM处理模式用户模式1种,特权模式6种.

usr用户,sys系统

特权模式下的异常模式:svc管理,abt中止,und未定义,irq中断,fiq快速中断

只有在特权模式下才允许对当前程序状态寄存器CPSR的所有控制位直接进行读写访问,而在非特权模式下只允许对CPSR的控制位进行间接访问。


CPSR和SPSR相互之间的关系:

当一个特定的异常中断发生时,将CPSR的当前值保存到相应的异常模式下的SPSR,然后设置CPSR为相应的异常模式.

从异常中断程序退出返回时,可通过保存在SPSR中的值来恢复CPSR.(利用这一点来切换模式)



3.功能部件编程

流程:对各个引脚以及功能部件进行初始化——>编写中断服务程序——>设置中断向量控制器(中断控制的中心)——>CPU响应中断


引脚连接模块PINSELx(引脚配置功能都需要用到这个寄存器)

32位宽度每2位代表一个引脚,因此每个引脚有4种不同的功能选择


通用输入输出GPIO

包含4个寄存器可以配置,均为32位宽度,每一位代表一个引脚

IOxPIN:读取引脚当前电平状态寄存器

IOxSET:GPIO引脚高电平输出寄存器

IOxDIR:GPIO引脚输入/输出方向控制寄存器

IOxCLR:GPIO引脚低电平输出寄存器

实验实例:

#include "config.h"#define LED 0X04 //P0.2引脚控制LED,高电平LED亮#define PIN_P005 0X00000020 //定义P0.05屏蔽字int main (void){PINSEL0=PINSEL0&0XFFFFF3CF;//设置引脚P0.2和P0.5连接GPIOIO0DIR|=LED;//设置LED控制口为输出,其他I/O为输入while(1){if((IO0PIN&PIN_P005)!=0) IO0SET=LED;//如果按下按钮则LED控制口输出高电平else IO0CLR=LED;//否则LED控制口输出低电平}    return 0;}

用ADS1.2开发环境编译和PROTETUES仿真软件仿真通过



中断向量控制器(ARM称作VIC)Vector Interrupt Controller

用于管理所有的中断信号再通过该控制器传入开中断的CPU中(I或F位置0开中断,可通过模式切换指令来实现,具体利用:SPSR->CPSR)

中断向量控制器的所有寄存器均为32位宽度,每一位代表一个中断源通道,中断源的通道通过查表得知.

中断选择寄存器(VICIntSelect,0xFFFF F00C):置1选择为FIQ中断,置0选择为IRQ中断,默认为IRQ中断

中断使能寄存器(VICIntEnable,0xFFFF F010):置1中断使能,置0无效.

当中断执行后切入异常向量表中对应的中断代码,FIQ直接切到一个中断服务程序(ISR)中,IRQ因为有优先级的区别,所以一般做法是切到向量地址寄存器(VICVectAddr,0xFFFF F030)中.中断服务程序不能直接将地址写入该寄存器因为当该寄存器内的值不为0则判断中断服务程序未执行完毕,该寄存器的值从VICDefVectAddr或者VICVectAddr0~15中复制而得到的

IRQ中断分为:非向量IRQ中断和向量IRQ中断.

*设置非向量IRQ中断服务程序(ISR)只需要将ISR的地址赋值到VICDefVectAddr这个寄存器中

设置向量IRQ中断服务程序(ISR)需要2个步骤:1.分配优先级 2.设置向量地址寄存器

向量控制寄存器0~15(VICVectCntl0~15,0xFFFF F200~23C):用于分配优先级,必须和向量地址寄存器0~15配对使用,优先级高低按向量控制寄存器0~15递减.低5位用于分配通道号,第6位必须置1才能使优先级分配有效,其余位保留

向量地址寄存器0~15(VICVectCntl0~15,0xFFFF F100~13C):用于保存向量IRQ通道的中断服务程序地址

*IRQ状态寄存器(VICIRQStatus,0xFFFF F000)

*FIQ状态寄存器(VICFIQStatus,0xFFFF F004)

*所有中断的状态寄存器(VICRawIntr,0xFFFF F0008)

*保护使能寄存器(VICProtection,0xFFFF F020)


软件中断服务代码

NoInt       EQU 0x80;//noint这个名字相当于0x80NoFIQEQU0x40;//nofiq这个名字相当于0x40

;软中断SoftwareInterruptCMPR0,#4LDRLOPC,[PC,R0,LSL#2]MOVSPC,LRSwiFunctionDCDIRQDisable;0DCDIRQEnable;1DCDFIQDisable;2DCDFIQEnable;3IRQDisable;禁止IRQ中断MRSR0,SPSRORRR0,R0,#NoIntMSRSPSR_c,R0MOVSPC,LRIRQEnable;使能IRQ中断MRSR0,SPSRBICR0,R0,#NoIntMSRSPSR_c,R0MOVSPC,LRFIQDisable;禁止FIQ中断MRSR0,SPSRORRR0,R0,#NoFIQMSRSPSR_c,R0MOVSPC,LRFIQEnable;使能FIQ中断MRSR0,SPSRBICR0,R0,#NoFIQMSRSPSR_c,R0MOVSPC,LR

在启动代码中添加这段代码来实现调用软中断__swi(0x00)  void SwiHandle1(int handler)开关F或I位

__swi(功能号)  返回值 名称(参数列表)

功能号:即软中断指令中的24位立即数,软中断号

名称:即调用软中断时用于描述软中断的函数名称

参数:软中断函数的参数,根据ATPCS规则,如果软中断函数有不超过4个参数时,通过R0~R3传递,超过4个参数时用堆栈来传递

利用这个特性就可以使用上面软中断的代码



外部中断输入

外部中断的寄存器均为8位位宽,低4位分别对应外部中断0~3,高4位保留

外部中断模式寄存器(EXTMODE,0xE01F C148):用于选择每个EINT脚的触发方式(电平触发和边沿触发)置1为边沿触发,置0为电平触发

外部中断极性寄存器(EXTPOLAR,0xE01F C14C):用于选择触发有效的电平,置0为低电平或者下降沿触发,置1为高电平或者上升沿触发

外部中断标志寄存器(EXTINT,0xE01F C140):当触发外部中断的时候EXTTNT为1,自动向VIC发出一个中断申请,当置1后EXTINT为0停止发送中断申请.用这个寄存器来控制外部中断是否能够再次响应

*外部中断唤醒寄存器(EXTWAKE,0xE01F C144)

实验实例:

#include "config.h"__swi(0x00)voidSwiHandle1(int Handle);//返回R0=Handle#defineIRQDisable()SwiHandle1(0)//禁止IRQ中断#defineIRQEnable()SwiHandle1(1)//使能IRQ中断#define LED0X04//P0.2引脚控制LED,高电平亮#define PIN_P0050X00000020//定义P0.05屏蔽字void P20_Eint3_Init(void){PINSEL1|=0x00000300;//P0.20为eint3功能EXTMODE=(1<<3);EXTPOLAR=(0<<3);//下降边沿触发}uint8 IRQ_Init(uint8 no,uint32 slot,uint32 addr){if(no>31) return(0);if(slot>15) return(0);IRQEnable();VICIntSelect&=~(1<<no);//将no通道设置为IRQ*(volatile uint32 *)((&VICVectCntl0)+slot)=0x20|no;*(volatile uint32 *)((&VICVectAddr0)+slot)=addr;VICIntEnable=(1<<no);return(1);}void  __irq Eint3_ISR(void){uint32  i;i=IO0SET;                        //读出当前LED控制值if((i&LED)==0) IO0SET=LED;       //控制LED控制值else IO0CLR=LED;while((EXTINT&0x08)!=0)          //清除中断标志{EXTINT=0x08;}VICVectAddr=0;//通知VIC中断处理结束}int main (void){PINSEL0&=0XFFFF2CF;//设置P0.2和P0.5为GPIO功能IO0DIR|=LED;//设置LED控制口为输出,其他不不变(输入)IO0SET=LED;IO0CLR=LED;P20_Eint3_Init();IRQ_Init(17,0,(uint32)Eint3_ISR);while(1);    return 0;}


定时器

定时器主要分为3个功能:1.计时功能 2.匹配功能(MAT) 3.捕获功能(CAP)

计时功能:定时器内部的预分频器对定时器时钟源进行分频,输入计数器,对其进行计数

匹配功能:匹配时动作有3种,(1)匹配时定时器继续工作可选择产生中断,(2)匹配时停止定时器可选择产生中断,(3)匹配时复位定时器可选择产生中断.

                    匹配时的外部输出有4种,(1)匹配时设置为低电平,(2)匹配时设置为高电平,(3)匹配时电平翻转,(4)匹配时无动作

                    可任意搭配1种或者2种使用

捕获功能:捕获引脚信号选择是否产生中断.

定时器计数器TC(定时器0,T0TC:0XE000 4008;定时器1,T1TC:0XE000 8008):当预分频计数器达到计数的上限时,32位定时器计数器TC+1

预分频寄存器PR(T0PR:0xE000 400C;T1PR:0xE000 800C):32位预分频寄存器指定预分频计数器的最大值

预分频计数器PC(T0PC:0xE000 4010; T1PC:0xE000 8010):每个PCLK周期加1

外设时钟PCLK(输入时间)——>预分频计数器PC——>预分频寄存器PR(控制倍率关系)——>定时器计数器TC

定时器周期=(PR+1)/Fpclk

匹配寄存器MR(T0MR0~T0MR3):存入一个值和定时器计数器TC进行比较,当匹配完成自动触发匹配时的动作

匹配控制寄存器MCR(T0MCR:0xE000 4014;T1MCR:0xE000 8014):用于控制匹配发生时的动作

外部匹配寄存器EMR(T0EMR:0xE000 403C;T1EMR:0xE000 803C):用于控制外部输出动作

捕获寄存器CR(T0CR0~T0CR3):捕获信号

捕获控制寄存器CCR(T0CCR:0xE000 4028;T1CCR:0xE000 8028):控制捕获事件

中断标志寄存器IR(T0IR:0xE000 4000;T1IR:0xE000 8000):清除中断标志,置1有效

定时器控制寄存器TCR(T0TCR:0xE000 4004;T1TCR:0xE000 8004):控制定时器使能和复位

实验实例:

#include "config.h"__swi(0x00)voidSwiHandle1(int Handle);//其中0x00为软中断功能号(软中断号);软中断函数名称为SwiHandle1;只有一个参数,则使用R0来传递;函数没有返回值。#defineIRQDisable()SwiHandle1(0)//禁止IRQ中断#defineIRQEnable()SwiHandle1(1)//使能IRQ中断#define LED0X01#define LED10X20uint8 IRQ_Init(uint8 no,uint32 slot,uint32 addr){if(no>31) return(0);if(slot>15) return(0);IRQEnable();VICIntSelect&=~(1<<no);//将no通道设置为IRQ*(volatile uint32 *)((&VICVectCntl0)+slot)=0x20|no;*(volatile uint32 *)((&VICVectAddr0)+slot)=addr;VICIntEnable=(1<<no);return(1);}void Timer(void){T0TC=0;//定时器设置为0T0PR=0;//时钟不分频T0MCR=0X03;//设置T0MR0匹配后复位T0TC并产生中断T0EMR=0x30;//匹配引脚电位翻转T0MR0=Fpclk/100;//设置匹配值计算公式:定时时间=MR*(PR+1)/FpclkT0TCR=0x01;//启动定时器0}void __irq timer(void){IO0SET=LED;T0IR=0xff;VICVectAddr=0;//通知VIC中断处理结束}int main (void){PINSEL0&=0xFFFFFFFE;//设置引脚0.0为GPIOIO0DIR=LED;PINSEL0&=0xFFFFFFCF;//设置引脚0.3为定时器匹配0PINSEL0|=0x00000080;Timer();IRQ_Init(4,1,(uint32)timer);while(1);    return 0;}




看门狗WATCH DOG

错误/不完整的喂狗时序会导致周期性复位/中断(看门狗已经使能)

由软件使能但只能由硬件复位或看门狗复位来禁止

看门狗模式寄存器(WDMOD):用于设置基本模式和状态

                                                          低四位分别对应看门狗中断使能WDEN,看门狗复位使能WDRESET,看门狗超时标志WDTOF,看门狗中断标志WDINT,置1有效

看门狗定时器计数器(WDTC):决定超时时间的值(范围:0xFF~0xFFFF FFFF)

看门狗喂狗寄存器(WDFEED):先写入0xAA再写入0x55,完成一次喂狗动作

看门狗定时器值寄存器(WDTV):用于读取看门狗定时器的当前值(0xFF复位)

实验实例:

#include "config.h"#define KEY (1<<20)#define BEEP (1<<7)__swi(0x00)voidSwiHandle1(int Handle);#defineIRQDisable()SwiHandle1(0)//禁止IRQ中断#defineIRQEnable()SwiHandle1(1)//使能IRQ中断void WDT_Init(uint32 time){WDTC=time;//设置看门狗的溢出时间WDMOD=0x03;//看门狗定时器溢出后,中断且复位WDFEED=0xAA;//执行喂狗WDFEED=0x55;}void FeedDog(void){IRQDisable();//禁止中断WDFEED=0xAA;//执行喂狗WDFEED=0x55;IRQEnable();}int main (void){uint32 dly;PINSEL0=0x00;PINSEL1=0x00;IO0DIR=BEEP;IO0CLR=BEEP;//复位后蜂鸣器叫一声for(dly=0;dly<5000;dly++);IO0SET=BEEP;WDT_Init(0x10000000);//初始化看门狗定时器,溢出产生复位while(1){while((IO0PIN&KEY)==0);for(dly=0;dly<100;dly++);FeedDog();}return 0;}




通用非同步收发传输器UART

16字节的FIFO(移位寄存器)

UART0只提供TXD和RXD信号引脚,UART1还提供一个调试解调器接口

UART接收器缓存寄存器UnRBR(1字节):作为接收缓存保存最先接收到的1字节数数据(若无1字节则填充0),是接收移位寄存器UnRSR的接口

UART发送保持寄存器UnTHR(1字节):是发送移位寄存器UnTSR的入口

UART除数锁存寄存器Divisor Latch Register:包含2个寄存器(UnDLL和UnDLM)用于设置波特率,访问时除数锁存访问位(DLAB)为1

波特率=Fpclk/(16*(UnDLM:UnDLL))通过取整运算/和取余运算%来设置DLL和DLM

UART中断使能寄存器UnIER:用于使能4个UARTn中断源(0~3依次为RBR中断,THRE中断,Rx状态中断,Modem状态中断)

UART中断标识寄存器UnIIR:用于UART中断状态和FIFO使能

UART FIFO控制寄存器UnFCR:用于FIFO使能和设置FIFO的触发节点

UART控制寄存器UnLCR:设置发送和接收数据字符的格式 

*UART1 Modem寄存器U1MCR

UART状态寄存器UnLSR:查询UART寄存器状态

实验实例:

#include "config.h"__swi(0x00)voidSwiHandle1(int Handle);//返回R0=Handle#defineIRQDisable()SwiHandle1(0)//禁止IRQ中断#defineIRQEnable()SwiHandle1(1)//使能IRQ中断#define UART_BPS  9600     //定义通信波特率uint8 RcvBuf[32];//定义个全局变量用于存储接收到数据int RcvBufLength = 0;uint8 SendBuf[32] = { "******Game Over!!!!!!*****\r" };int SendBufLength = 0;//===============中断向量控制器初始化函数===============uint8 IRQ_Init(uint8 no, uint32 slot, uint32 addr){if (no>31) return(0);if (slot>15) return(0);IRQEnable();VICIntSelect &= ~(1 << no);//将no通道设置为IRQ*(volatile uint32 *)((&VICVectCntl0) + slot) = 0x20 | no;*(volatile uint32 *)((&VICVectAddr0) + slot) = addr;VICIntEnable = (1 << no);return(1);}//==============UART0初始化函数=========================void UART0_Init(void){uint16 Fdiv;U0LCR = 0x83;//1000 0011设置DLAB开启除数锁存器设置Fdiv = (Fpclk / 16) / UART_BPS;U0DLM = Fdiv / 256;U0DLL = Fdiv % 256;U0LCR = 0x03;//设置LCR关闭除数锁存器设置}//==============UART0中断服务程序=======================void __irqUART0_ISR(void){int i;if (RcvBufLength == 31)RcvBufLength = 0;//接收满回写switch (U0IIR & 0x0f){case 0x04://发生RDA中断for (i = 0; i<8; i++)//连续读取U0RBR寄存器8次{RcvBuf[RcvBufLength++] = U0RBR;//将收到的数据保存到接收缓冲区RcvBuf中,并自动清除中断标志}break;case 0x0c:    //发生超时中断--CTIwhile ((U0LSR & 0x01) == 1){//如果接收FIFO中含有数据,就读取UnRBR寄存器将数据放到原有数据后面RcvBuf[RcvBufLength++] = U0RBR;}break;case 0x02:     //发生THRE中断for (; SendBuf[SendBufLength] != '\0';){U0THR = SendBuf[SendBufLength++];for (i = 0; i<4500; i++);//cpu过快需要延迟不然复写fifo会出错}break;default:break;}VICVectAddr = 0;}//==============CTI无法触发利用定时器模拟==============void __irq Timer0_ISR(void){while ((U0LSR & 0x01) == 1){//如果接收FIFO中含有数据,就读取UnRBR寄存器将数据放到原有数据后面RcvBuf[RcvBufLength++] = U0RBR;}T0IR = 0x01;//清除匹配通道0的中断标志VICVectAddr = 0;}void Time0Init(void){T0TC = 0;//计时器初始化0T0PR = 0;//时钟不分频T0MCR = 0x03;//匹配计时器后MR0复位并中断T0MR0 = Fpclk * 3;//匹配值为3秒T0TCR = 0x01;//启动定时器T0IR = 0x01;//清除中断标志IRQ_Init(4, 2, (uint32)Timer0_ISR);//定时器匹配中断}//==============UART0中断初始化函数=====================void UART0_INTInit(void){U0IER = (111 << 0);//RBR中断使能,THRE中断使能U0FCR = 0x81;//UART0接收换从区触发点为8字节IRQ_Init(6, 1, (uint32)UART0_ISR);//vic设置}//=============UART0发送单字节数据函数==================void UART0_SendByte(uint8 data)//查询方式下{U0THR = data;//发送数据while ((U0LSR & 0x40) == 0);//等待发送完毕}//=============UART0发送字符串数据函数==================void UART0_SendStr(uint8 const *str)//查询方式下{while (1){if (*str == '\0') break;UART0_SendByte(*str++);}}//=============UART0接收单字节数据函数==================uint8 UART0_RcvByte(void)//查询方式下{uint8 rcv_data;while ((U0LSR & 0x01) == 0);rcv_data = U0RBR;return(rcv_data);}//==========UART0接收最大长度为n个字节字符串数据函数====void UART0_RcvStr(uint8 *str, int n)//查询方式下{for (; n>0; n--){*str = UART0_RcvByte();if (*str == '\x08')str -= 2;//退格键if (*str == '\r')//回车输入结束{*str = '\0';break;}str++;}}
基本操作方法:

1.设置I/O连接到UART

2.设置串口波特率(UnDLM和UnDLL)

3.设置串口工作模式(UnLCR和UnFCR)

4.发送或接受数据(UnTHR和UnRBR)

5.检查串口状态字或等待串口中断(UnLSR)



0 0
原创粉丝点击