ARM从零开始学--点灯^_^
首先,我也是个初学者,刚学会点灯^_^,就先把这灯点起来,和同样的或者还在迷茫着点灯的初学者一起学习^_^点击此处下载 ourdev_390873.rar(文件大小:42K) (原文件名:test2124IO.rar)
介绍下我用的环境:ads1.2 proteus7.1 PLC2124
哈哈 虚拟着玩 好更容易的参与
PLC2124片内有256KB的FLASH和16K的RAM,直接先当单片机使。
随便建立个后缀.S的文档,把下面的代码copy过去。
AREA SAMPLE,CODE,READONLY
ENTRY
START
LDR R0,=0X00000000 ;将P0口全部配置为GPIO口
LDR R1,=0XE002C000 ;PINSEL0地址
STR R0,
LDR R0,=0xffffffff ;IO口全部设置为输出
LDR R1,=0xe0028008 ;IO0DIR地址
STR R0, ;
LOOP
LDR R0,=0x55555555 ;间隔高电平输出
LDR R1,=0xe0028004 ;IO0SET地址
STR R0,
LDR R0,=0XAAAAAAAA
LDR R1,=0XE002800C ;IO0CLR地址
STR R0,
BL DELAY
LDR R0,=0X55555555
LDR R1,=0XE002800C ;IO0CLR地址
STR R0,
LDR R0,=0xAAAAAAAA ;间隔高电平输出
LDR R1,=0xe0028004 ;IO0SET地址
STR R0,
BL DELAY
B LOOP ;跳回去START,一个死循环:)
DELAY
MOV R0,#100
0
MOV R1,#1000
1
SUBS R1,R1,#1
BNE %B1
SUBS R0,R0,#1
BNE %B0
MOV PC,LR
END
设置编译选项并编译:
1.在ADS1.2环境里新建一个可执行镜像项目文件,将上面的.s文件添加到项目。
2.在Edit->DebugRel Settings..->Target Settings->Post-linker里选择 ARM fromELF
->ARM Linker->Output->RO Base里填0x00
->Options->Image entry point里填0x00
->ARM fromELF->Output format里选择Inetl 32 bit Hex
->Output file name里填led.hex,这个反正后缀是hex就行,生成proteus能仿真的hex文件
3.点击OK,编译。
在proteus里建立硬件电路,也就拉几个灯出来就可以了,把执行文件选到led.hex,运行,灯就该点着了。
http://cache.amobbs.com/bbs_upload782111/files_10/ourdev_390855.jpg
(原文件名:未命名.jpg)
一般上电复位后,PC都是指到0x0000这个地址的,单片机也是这样,如果不考虑中断什么的,要执行的代码从0地址一直往下放就是,这个点灯的程序也是这样的,后面的编译选项就是把代码和入口地址都设置到0地址,也就是系统一复位,就跟着程序执行了,点灯的程序也简单,先IO口设置为通用IO口,交错高低电平,延时,反过来,再延时,死循环,灯就闪了。 酷
直接用C就好了 LPC2124 顶 继续点灯
呵呵^_^,没有直接用C,因为在ARM里运行C,先要有个汇编的引导代码,我刚接触,就先从汇编开始了,从最简单的开始咯
不好意思 ARM芯片型号写错了 应该是LPC2124 汗。。。。
汇编点亮了灯,就想着要用C来点了,因为ARM不是51系列的,直接在KEIL下弄个C,编译就可以运行的,ARM下还需要先用汇编初始化一下C程序的运行环境才能运行C程序。
点击此处下载 ourdev_391493.rar(文件大小:56K) (原文件名:test2124C.rar)
先来弄个最简单的初始化C运行环境的汇编文件。
新建一个Statrup.s的汇编文件,把下面的代码COPY过去,保存。
; 启动文件,初始化C程序的运行环境,然后进入C程序代码。
IMPORT |Image$$RO$$Limit|
IMPORT |Image$$RW$$Base|
IMPORT |Image$$ZI$$Base|
IMPORT |Image$$ZI$$Limit|
IMPORT main ; 声明C程序中的Main()函数
CODE32 ; 声明32位ARM指令
AREA init,CODE,READONLY ; 声明代码段Start
ENTRY ; 标识程序入口
Reset
LDR SP,=0x40003F00 ; 设置堆栈指针
; 初始化C程序的运行环境
LDR R0,=|Image$$RO$$Limit|
LDR R1,=|Image$$RW$$Base|
LDR R3,=|Image$$ZI$$Base|
CMP R0,R1
BEQ LOOP1
LOOP0
CMP R1,R3;如果RW区不为空,将加载域的RW数据COPY到运行域
LDRCC R2,,#4
STRCC R2,,#4
BCC LOOP0
LOOP1
LDR R1,=|Image$$ZI$$Limit|
MOV R2,#0
LOOP2
CMP R3,R1;如果ZI区不为空,将ZI区域初始化为0
STRCC R2,,#4
BCC LOOP2
B main ;跳转到c语言入口 Jump to the entry point of C program
END
说明一下,
|Image$$RO$$Limit| 就是Edit->DebugRel Settings..->ARM Linker->Output->RO Base里设置的地址地址和代码的长度,其值为|Image$$RO$$Base|+Code sizes+RO Data sizes
|Image$$RW$$Base| 就是Edit->DebugRel Settings..->ARM Linker->Output->RW Base 里面设置的地址
因为LPC2124的64K片内RAM被映射到0x40000000-0x4000ffff地址空间,这里把堆栈指针设置到0x40003f00
在初始化完C运行环境后,跳到C程序的入口就可以运行C程序了。
/*******************************************************************************
*File: Main.c
*******************************************************************************/
#include"LPC2124.h"
#define uint32 unsigned int
/*******************************************************************************
*名称: DelayNS()
*功能: 长软件延时
*******************************************************************************/
void DelayNS(uint32 dly)
{uint32i;
for (;dly>0;dly--)
for(i=0;i<50000;i++);
}
/*******************************************************************************
*名称: main()
*******************************************************************************/
int main(void)
{
PINSEL0=0x00000000; //设置所有引脚连接GPIO
IO0DIR=0xffffffff;
while(1)
{
IO0SET=0x55555555;
IO0CLR=0xaaaaaaaa;
DelayNS(1);
IO0SET=0xaaaaaaaa;
IO0CLR=0x55555555;
DelayNS(1);
}
}
包含的LPC2124.h的文件是对LPC2124的一些寄存器的定义
新建C文件,将上面的C代码COPY过去,保存。
设置编译选项并编译:
1.在ADS1.2环境里新建一个可执行镜像项目文件,将上面的c文件和s添加到项目。
2.在Edit->DebugRel Settings..->Target Settings->Post-linker里选择 ARM fromELF
->ARM Linker->Output->RO Base里填0x00 RW Base里填 0x40003000
->Options->Image entry point里填0x00
->Layout->Object/Symbol 里填Startup.oSection里面填 init
这里的Startup就是上面的汇编文件的文件名,init就是文件里入口的标号
->ARM fromELF->Output format里选择Inetl 32 bit Hex
->Output file name里填led_c.hex,生成proteus能仿真的hex文件
3.点击OK,编译。
在proteus里搭建硬件电路,导入hex文件,运行,OK。 ku 想学。。。。 点个灯都酷了?不过楼主很不错啊!我当初是直接用MDK里的自带的启动代码+C语言来学的!呵呵…… 灯点亮了,C代码可以运行了,再来点个屏,呵呵:-),都是没什么技术含量的东西,初学罢了
点击此处下载 ourdev_392613.rar(文件大小:63K) (原文件名:lcd1602.rar)
http://cache.amobbs.com/bbs_upload782111/files_10/ourdev_392614.jpg
(原文件名:hello,world.jpg)
启动代码就可以用点灯的了,反正也没用到复杂的东东,编译的设置也可以不用变,直接改下主程序就可以咯^_^
/****************************************************************************
* File: main.c
* 功能:向LCD输出字符
****************************************************************************/
#include"config.h"
#define rs (1<<8)
#define rw (1<<9)
#define en (1<<10)
#define busy (1<<7)
#define MAXX 16
unsigned char str0[]={"Hello,World!"};
unsigned char str1[]={"Hello,ARM!"};
/****************************************************************************
* 名称:ChkBusy()
* 功能:检查总线是否忙
****************************************************************************/
void ChkBusy()
{
IO0DIR=0x700;
while(1)
{
IO0CLR=rs;
IO0SET=rw;
IO0SET=en;
if(!(IO0PIN & busy))break;
IO0CLR=en;
}
IO0DIR=0x7ff;
}
/****************************************************************************
* 名称:WC_Lcd()
* 功能:写函数
****************************************************************************/
void WC_Lcd(unsigned char dat)
{
ChkBusy();
IO0CLR=rs; //全部清零
IO0CLR=rw;
IO0CLR=0xff; //先清零
IO0SET=dat; //再送数
IO0SET=en;
IO0CLR=en;
}
/****************************************************************************
* 名称:WD_Lcd()
* 功能:写数据函数
****************************************************************************/
void WD_Lcd(unsigned char dat)
{
ChkBusy();
IO0SET=rs;
IO0CLR=rw;
IO0CLR=0xff; //先清零
IO0SET=dat; //再送数
IO0SET=en;
IO0CLR=en;
}
/****************************************************************************
* 名称:lcd_init()
* 功能:lcd初始化函数
****************************************************************************/
void lcd_init(void)
{
WC_Lcd(0x01); //显示模式设置,开始要求每次检测忙信号
WC_Lcd(0x38); //关闭显示
WC_Lcd(0x0f); //显示清屏
WC_Lcd(0x06); // 显示光标移动设置
WC_Lcd(0x0C); // 显示开及光标设置
}
/********************************************************************************
* 名称:DispChar()
* 功能:在指定位置显示一个字符
*********************************************************************************/
void DispChar(unsigned char X, unsigned char Y, unsigned char DData)
{
Y &= 0x1;
X &= 0xF; //限制X不能大于15,Y不能大于1
if (Y) X |= 0x40; //当要显示第二行时地址码+0x40;
X |= 0x80; //算出指令码
WC_Lcd(X); //这里不检测忙信号,发送地址码
WD_Lcd(DData);
}
/********************************************************************************
* 名称:DispString()
* 功能:在指定位置显示一串字符
*********************************************************************************/
void DispString(unsigned char X, unsigned char Y, unsigned char *DData)
{
unsigned char StrLen;
StrLen = 0;
Y &= 0x1;
X &= 0xF; //限制X不能大于15,Y不能大于1
while(DData!='\0') //若到达字串尾则退出
{
if (X <= 0xF) //X坐标应小于0xF
{
DispChar(X, Y, DData); //显示单个字符
StrLen++;
X++;
}
}
}
/****************************************************************************
* 名称:strlen()
* 功能:计算字符串长度
****************************************************************************/
int strlen(unsigned char s[])
{
int i=0;
while(s!='\0')
i++;
return i;
}
/****************************************************************************
* 名称:main()
* 功能:显示文本
****************************************************************************/
intmain(void)
{
lcd_init();
IO0DIR=0x7ff; //设置为输出
IO0CLR=0x7ff;
DispString((MAXX-strlen(str0))/2, 0, str0);//居中显示字符串
DispString((MAXX-strlen(str1))/2, 1, str1);//居中显示字符串
while(1);
} 楼主无敌啊,交个朋友啊 我的QQ是95818270 楼主的多少加小弟一个奥 不错,浅显易懂,很适合我们这些初学者,支持楼主! 请问LZ,我现在通过软件仿真上面的程序都正常,但是通过LPC2000 flash utility 写入到芯片FLASH里面,效验也正确,但是无论怎么复位重启芯片,程序就是不运行,我已经确定DEBUG管脚接地,P0.14已经接正电,确定退出ISP和调试模式。
现在不明白ARM是不是启动的时候是不是先执行芯片的BOOTLOADER,而我的程序写入的地址也是从0地址开始的,是不是他们的地址重叠而无法运行呢! 呵呵 如果你的上面编译后的bin都是从0地址开始写进去的话,哪么你的bootloader就应该被干掉了,上电后,程序指针一般都是从0开始的,刚开始弄的时候,你完全可以把它当个普通的单片机来弄,不要想什么bootloader之类的,反正程序一上电pc就是0,然后一条一条指令重地址0开始执行,你0地址开始的是什么就执行什么,你可以像烧写bootloader一样,把最开始那个汇编的点灯弄进芯片,应该可以执行的。 不错! 不错,入门就把ARM当单片机使再说 我遇到一个问题,我把工程移动之后就不能正常编译了
http://cache.amobbs.com/bbs_upload782111/files_10/ourdev_398154.jpg
(原文件名:Y(W0QQT0@GT22O2A(QI)K
目标HEX我也改过 我用KEIL for ARM 就可以,启动文件自己加入。。 ads不能有中文路径 你看看是不是这个问题 楼主,为什么我在ADS1.2中按照你说的方法,在编译的时候出错呢,大家试验都通过了吗?
我的提示这些错误
Error:A1163E:Unknow opcode
project:1.mpc,Target:DebugRel,Source File:1.s
1.s line 1
1 00000000AREA SAMPLE,CODE,READONLY
Error:A1105E:Area directive missing
project:1.mpc,Target:DebugRel,Source File:1.s
1.s line 3
3 00000000 ENTRY
Warning:A1088W:Faking declaration of area AREA|$$$$$$$|
project:1.mpc,Target:DebugRel,Source File:1.s
1.s line 3
3 00000000 ENTRY
2 Errors,1Warning
有错误啊,在KEIL里面也试验了提示:
Build target 'Target 1'
assembling asmtest.s...
linking...
asmtest.sct(7): error: L6236E: No section matches selector - no section to be FIRST/LAST.
"asmtest.axf" - 1 Error(s), 0 Warning(s).
请问这是什么原因啊,初学了,请楼主指点下!多谢 记号 mark to【18楼】 95818270
Error:A1163E:Unknow opcode
这个估计是文本格式有问题 你注意下空格 我传上来的都有工程文件 你可以直接编译的 注意不要放中文路径下 还要请教楼主啊,这次编译通过了,软件仿真也可以,但是通过ISP方式写到芯片里面退出ISP状态复位后芯片没反应,哪个端口都没有电平的变化,但是我的硬件换个别人给的用KEILC编译好的HEX,写到里面却能正常运行。
现在就不明白这点,如果是用ADS1.2汇编编译的程序PROTUS仿真正常,但是写到芯片却不能运行呢? 不知道你是把程序写在哪里的 是flash里面吗 如果是flash里面 要从0地址开始 我没有LPC的硬件 我用的S3C44B0的板子 用FlashProgrammer下载程序的 是可以实现这些功能的啊 如果你烧写了这个程序到硬件 你的bootloader还在 那就没把flash地址弄对 你买开发板 应该有说怎么烧写那个bootloader吧 你就把汇编点灯的那个程序当bootloader写进去啊 关键就是程序要在上电后0地址的地方 浅显易懂 记号 哦 谢谢楼主,继续顶顶顶顶顶顶顶顶顶顶!!!
顺便还得请教个问题,我现在是自己做的LPC2103的开发板,没有什么说明书,只能从网上找些资料,楼主帮帮忙,我现在能确定我的硬件没问题,然后用的下载软件是LPC2000 Flash Utility ,在Flash buffer 里面的Address Range 里面的Start项已经填上了&H00000000,Code Execution 的Run from Address 里面也填入了&H00000000-ARM
像上面这样的配置如果写入 用KEIL C编译的程序运行一切正常,但是换上这个ADS1.2汇编编译的程序,复位后芯片没有任何反应,真的是不理解为什么,而且用protus仿真无论是这个KEIL编译的还是ADS编译的,都是正常运行,没什么区别。
我看了很多资料都没有理解这个地方,现在我也有些怀疑楼主说的是芯片运行了芯片内部的BOOT,但是LPC2000 Flash Utility 里面确实没有关闭或擦除BOOT的选项,或者跳过BOOT从0地址运行的选项?
还是从物理硬件上面,有管脚可以屏蔽掉BOOT而直接从0地址运行呢 ? 如果同样的程序 keil可以 ads不行 有可能是生成的HEX格式不一样 你看下keil是什么标准的hex 在ads里面换一下 现在问题是KEIL和ADS的编译完,用PROTUS仿真都可以,基本可以排除HEX格式问题;
但是只要现在把程序写到芯片里面,说什么都不运行,但是别人拿来的程序写上马上就运行了,就是不理解这个,人家别人的程序用protus仿真也是正常。
都是同样的编译软件,都是同样的硬件,同样的设置,同样的仿真好使,同样的LPC2000 Flash Utility ,同样的方法写到芯片里;
为什么汇编的这个无论怎样ARM的硬件就是不运行呢? 你是用的汇编的还是C的呢 如果是汇编的 是不是同样的程序 用KEIL编译的下载到电路板上就可以工作 而ADS的就不可以呢 还是两者编译生成的HEX都不能工作 要别人拿来的就可以? 如果是C的 我看了下LPC2103的片内只有8K的SRAM 从0x40000000--0x40001FFF 如果你堆栈还是像我上面例子设置的话 就有问题了 堆栈和RW_base的设置都不能超过这个范围 比如你的RW_base可以设置为0x40000000堆栈可以设置为0x40001F00 记号 好文章,有空的时候再学习 这个帖子置COOL,应该没有人有意见吧 :) 从0开始 楼主还有吗?玩了几个回味无穷啊!呵呵。 哈哈 居然有裤子穿了 谢谢阿莫老大了 这个冬天不怕冷 了 ^_^
这两天在弄TFT屏 虚的也还有 弄了慢慢传上来 和与我一样的初学者共同学习./emotion/em035.gif 嗯,多谢了。
标记,以后学习ARM时翻翻。 熟悉芯片资源 呵呵 来个ADC转换的^_^
点击此处下载 ourdev_411128.rar(文件大小:78K) (原文件名:adc.rar)
http://cache.amobbs.com/bbs_upload782111/files_10/ourdev_411148.jpg
(原文件名:adc.jpg)
工程的建立和环境的设置就和上面的一样啦,这个代码好像是周XX的,小小的改了下输出显示的:-)
/****************************************************************************
* 名 称:main()
* 功 能:进行通道0、1电压ADC转换,并把结果转换成电压值,然后发送到串口。
* 说 明:在CONFIG.H文件中包含stdio.h。
****************************************************************************/
intmain(void)
{
uint32ADC_Data;
char str;
lcd_init();
IO0DIR=0x7ff; //设置为输出
IO0CLR=0x7ff;
PINSEL1 = 0x01400000; // 设置P0.27、P0.28连接到AIN0、AIN1
/* 进行ADC模块设置,其中x<<n表示第n位设置为x(若x超过一位,则向高位顺延) */
ADCR = (1 << 0) | // SEL = 1 ,选择通道0
((Fpclk / 1000000 - 1) << 8) | // CLKDIV = Fpclk / 1000000 - 1 ,即转换时钟为1MHz
(0 << 16) | // BURST = 0 ,软件控制转换操作
(0 << 17) | // CLKS = 0 ,使用11clock转换
(1 << 21) | // PDN = 1 , 正常工作模式(非掉电转换模式)
(0 << 22) | // TEST1:0 = 00 ,正常工作模式(非测试模式)
(1 << 24) | // START = 1 ,直接启动ADC转换
(0 << 27); // EDGE = 0 (CAP/MAT引脚下降沿触发ADC转换)
DelayNS(10);
ADC_Data = ADDR; // 读取ADC结果,并清除DONE标志位
while(1)
{
ADCR = (ADCR&0x00FFFF00)|0x01|(1 << 24); // 设置通道1,并进行第一次转换
while( (ADDR&0x80000000)==0 ); // 等待转换结束
ADCR = ADCR | (1 << 24); // 再次启运转换
while( (ADDR&0x80000000)==0 ); // 等待转换结束
ADC_Data = ADDR; // 读取ADC结果
ADC_Data = (ADC_Data>>6) & 0x3FF; // 提取AD转换值
ADC_Data = ADC_Data * 3300/1024; // 数值转换
sprintf(str, "VIN1=%4dmV", ADC_Data);
DispString(0,0,str);
DelayNS(1);
ADCR = (ADCR&0x00FFFF00)|0x02|(1 << 24); // 设置通道2,并进行第一次转换
while( (ADDR&0x80000000)==0 ); // 等待转换结束
ADCR = ADCR | (1 << 24); // 再次启运转换
while( (ADDR&0x80000000)==0 ); // 等待转换结束
ADC_Data = ADDR; // 读取ADC结果
ADC_Data = (ADC_Data>>6) & 0x3FF; // 提取AD转换值
ADC_Data = ADC_Data * 3300/1024; // 数值转换
sprintf(str, "VIN2=%4dmV", ADC_Data);
DispString(0,1,str);
DelayNS(1);
}
} 继续关注,支持楼主! 不错,又多了一个,继续关注,强烈支持! 咱来移植UCOS吧。。。。偶不想把ARM当单片机使用了。。:)不过还是帮顶 楼主真是很强。
我记得我当时看书的时候,感觉是一头雾水,过了好长的时间才算是比较清晰了。但是看了楼主的学习过程,真的楼主学是很轻松,思路我感觉也是很正确,呵呵。 绝对要记号的 不错,赞一个! 楼主你知道为了给你顶一下 我才注册的.....谢谢你...希望再接再厉啊 留爪 赞一个!
教别人学习,好精神 楼主不继续更新了么?
俺看了此贴,迅速的装好了protues,等楼主更新哦! 大家帮我看一下行吗 我这个按键的程序那里有问题 为什么就是没反应呢 #include "config.h"
//#define LEDON 0x00000080 //定义P0.7引脚控制B1,低电平灯亮
#define PIN_P014 0x00004000//定义P0.14屏蔽字
#define uint32 unsigned int
#define key0 (1<<16)
#define key1 (1<<17)
#define key2 (1<<18)
#define key3 (1<<19)
#define Key0 (key0&IO0PIN)
#define Key1 (key1&IO0PIN)
#define Key2 (key2&IO0PIN)
#define Key3 (key3&IO0PIN)
#define Key_Down 0x11
#define Key_Up 0x12
#define Key_Left 0x13
#define Key_Right 0x14
void DelayMS(uint32 time)//1毫秒延时
{
uint32 i;
for(;time>0;time--)
for(i=8929;i>0;i--);
}
uint8 KeyScan(void)
{
// int Key0,Key1,Key2,Key3;
// Key0 = (key0&IO0PIN);
// Key1 = (key1&IO0PIN);
// Key2 = (key2&IO0PIN);
// Key3 = (key3&IO0PIN);
if((Key0==0)||(Key1==0)||(Key2==0)||(Key3==0)){
DelayMS(10);
if((Key0==0)||(Key1==0)||(Key2==0)||(Key3==0)){
if((Key0==0)&&(Key1==1)&&(Key2==1)&&(Key3==1)){ //Key0
return Key_Up;
}else if((Key0==1)&&(Key1==0)&&(Key2==1)&&(Key3==1)){//Key1
return Key_Right;
}else if((Key0==1)&&(Key1==1)&&(Key2==0)&&(Key3==1)){//Key2
return Key_Left;
}else if((Key0==1)&&(Key1==1)&&(Key2==1)&&(Key3==0)){//Key3
return Key_Down;
}else{
return FALSE;
}
}else {
return FALSE;
} //no key press
}else{
return FALSE;
}
}
uint8 WaitKey(void)
{
while(1){
switch(KeyScan()){
case Key_Up:{
return Key_Up;
break;
}
case Key_Down:{
return Key_Down;
break;
}
case Key_Left:{
return Key_Left;
break;
}
case Key_Right:{
return Key_Right;
break;
}
default:
break;
}
}
}
int main(void)
{
PINSEL0=0x00000000; //设置引脚链接GPIO
PINSEL2=0x00000000;
IO0DIR=PIN_P014;
IO1DIR=0x00000000;
IO0SET=PIN_P014;
DelayMS(100);
IO0CLR=PIN_P014;
// while(1);
while(1)
{ uint8 i;
i= WaitKey();
if(i==Key_Up)IO0SET=PIN_P014;
DelayMS(1000);
}
//while(1)
//{
//if((IO0PIN&PIN_P014)!=0)
// IO0SET=PIN_P014;
// else IO0CLR=LEDON;
// for(i=0;i<1000;i++);d
// }
return(0);
} 很支持楼主的点灯的这种先驱精神!
我也很希望能像楼主一样,带领我们协会一起前进,可有时却感到力不从心,毕竟大家都是自学遇到困难也很多。。
希望能一直看到你点的灯,也希望有更多人加入你的行列!
arm其实不难,贵在坚持!
我在学习uc/os-2,希望以后多交流!
我的qq:824340139 很好,从头开始 占个位置学习。 to 【89楼】 xiangyuan_122
我是想问这句define的语法,为什么不是 #define EXTINT 0xE01FC140 这种形式的
0xE01FC140 应该是寄存器 EXTINT 的 地址吧。
请赐教。 记号 不知道你问的为什么是指的哪个 如果是后面的 建议你看看芯片的数据手册 #define EXTINT (*((volatile unsigned char *) 0xE01FC140))
请问楼主 EXTINT 为什么这样定义? 沙发! 顶哦,谢谢楼主,跟着学~~~ mark proteus是个很好的工具
另外楼主的代码写的很规范嘛 不错,原来ARM这么难啊。 顶!@ ok ~~~ 学习 谢楼主,先收下 猜想问题主要是I2C_end在中断里没被值1 所以会在while(0==I2C_end); 这里死循环 后面的代码就执行不下去了 但不能单步看代码 具体问题出在哪里就不知道了 郁闷的冬天http://www.ourdev.cn/bbs/emotion/em040.gif 让人极度郁闷的I2C总线仿真
一直想把I2C总线仿真出来的 却怎么也没成功 可惜没弄成proteus和ads的联调 不知道问题出在了哪里 我现在能仿真的 就是能把数据写进2402 然后程序就不往下执行了 或者能读出数据 然后程序也不往下执行了
http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_504016.JPG
(原文件名:i2c.JPG)
点击此处下载 ourdev_504017.rar(文件大小:75K) (原文件名:forIIClcd1602.rar)
贴上主程序 代码都是周XX的好像 仿真时要注意 接I2C器件上拉电阻的电源不要标具体数值 上拉电阻选择数字模式http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_504034.jpg
(原文件名:res.jpg)
运行下面的程序 可以通过I2C debugger查看写入2402的数据 是ASCII码的数据 不过也在液晶屏上显示出来 但是程序好像就这里停住了 数据写完就一直等着了 停止仿真 不要关掉proteus 注释掉程序写数据的部分 重新编译 再运行仿真 可以读出刚才写入的数据 但读出来后又卡壳了 程序还是不往下执行 本来是让读出的数据再显示到液晶屏的 就这里出问题了哦 如果关掉proteus 在运行读2402的程序 会发现读出的数据全部是FF,说明刚才写数据是写进去了 也读出来了 但程序陷入了某个死循环 只要执行读写i2c 就出不来了 所以不能往下执行 后面的往屏幕写读出的数据也执行不了的
不知道谁能解决下这个问题吗
/*******************************************************************************
*File: Main.C
*功能: 使用硬件I2C对EEPROM进行操作,利用中断方式操作
*******************************************************************************/
#include "config.h"
#include"lcd.c"
#define CAT24WC02 0xA0 /*定义器件地址*/
/*以下为I2C操作时所需要的变量,在调用I2C子程序前要设置好这些变量*/
volatile uint8 I2C_sla; //从机地址
volatile uint8 I2C_suba; //子地址
volatile uint8 *I2C_buf; //数据缓冲区指针(读操作时会被更改)
volatile uint8 I2C_num; //操作数据个数
volatile uint8 I2C_end; //操作结束标志,为1时表示操作结束,0xFF表示操作失败
volatile uint8 I2C_suba_en; //子地址使能控制,读操作设置为1,写操作设置为2
/****************************************************************************
* 名称:IRQ_I2C()
* 功能:I2C中断,通过判断I2C状态字进行相应的操作。
* 入口参数:无
* 出口参数:无
****************************************************************************/
void __irq IRQ_I2C(void)
{ uint8 sta;
sta = I2STAT; // 读出I2C状态字
switch(sta)
{ case 0x08: // 己发送起始条件
if(1==I2C_suba_en) I2DAT = I2C_sla&0xFE; // 指定子地址读时,先写入地址
else I2DAT = I2C_sla; // 否则直接发送从机地址
I2CONCLR = 0x28; // SI=0
break;
case 0x10:
I2DAT = I2C_sla; // 重启动总线后,发送从地址
I2CONCLR = 0x28; // SI=0
break;
case 0x18: // 已发送SLA+W,并已接收应答
if(0==I2C_suba_en) // 无子地址,则直接发送数据
{ if(I2C_num>0)
{ I2DAT = *I2C_buf++;
I2CONCLR = 0x28;
I2C_num--;
}
else
{ I2CONSET = 0x10; // 无数据发送,结束总线
I2CONCLR = 0x28;
I2C_end = 1; // 设置总线操作结束标志
}
break;
}
if(1==I2C_suba_en) // 发送子地址
{ I2DAT = I2C_suba;
I2CONCLR = 0x28;
}
if(2==I2C_suba_en)
{ I2DAT = I2C_suba;
I2CONCLR = 0x28;
I2C_suba_en = 0; // 子地址己处理
}
break;
case 0x28: // 已发送I2C数据,并接收到应答
if(0==I2C_suba_en) // 无子地址,则直接发送数据
{ if(I2C_num>0)
{ I2DAT = *I2C_buf++;
I2CONCLR = 0x28;
I2C_num--;
}
else
{ I2CONSET = 0x10; // 无数据发送,结束总线
I2CONCLR = 0x28;
I2C_end = 1;
}
break;
}
if(1==I2C_suba_en) // 若是指定地址读,则重新启动总线
{ I2CONSET = 0x20;
I2CONCLR = 0x08;
I2C_suba_en = 0; // 子地址己处理
}
break;
case 0x20:
case 0x30:
case 0x38:
I2CONCLR = 0x28; // 总线进入不可寻址从模式
I2C_end = 0xFF; // 总线出错,设置标志
break;
case 0x40: // 己发送SLA+R,并已接收到应答
if(1==I2C_num) // 最后一字节,接收数据后发送非应答信号
{ I2CONCLR = 0x2C; // AA=0,接收到数据后产生非应答
}
else // 接收数据并发送应答信号
{ I2CONSET = 0x04; // AA=1,接收到数据后产生应答
I2CONCLR = 0x28;
}
break;
case 0x50:
*I2C_buf++ = I2DAT; // 读取数据
I2C_num--;
if(1==I2C_num)
{ I2CONCLR = 0x2C; // AA=0,接收到数据后产生非应答
}
else
{ I2CONSET = 0x04; // AA=1,接收到数据后产生应答
I2CONCLR = 0x28;
}
break;
case 0x58:
*I2C_buf++ = I2DAT; // 读取最后一字节数据
I2CONSET = 0x10; // 结束总线
I2CONCLR = 0x28;
I2C_end = 1;
break;
case 0x48:
I2CONCLR = 0x28; // 总线进入不可寻址从模式
I2C_end = 0xFF;
break;
default:
break;
}
VICVectAddr = 0x00; // 中断处理结束
}
/****************************************************************************
* 名称:ISendByte()
* 功能:向无子地址器件发送一字节数据。
* 入口参数:sla 器件地址
* dat 要发送的数据
* 出口参数:返回值为0时表示出错,为1时表示操作正确。
* 说明:使用前要初始化好I2C引脚功能和I2C中断,并已使能I2C主模式
****************************************************************************/
uint8 ISendByte(uint8 sla, uint8 dat)
{ /* 参数设置 */
I2C_sla = sla; // 写操作的器件地址
I2C_buf = &dat; // 待发送的数据
I2C_num = 1; // 发送1字节数据
I2C_suba_en = 0; // 无子地址
I2C_end = 0;
I2CONCLR = 0x2C;
I2CONSET = 0x60; // 设置为主机,并启动总线
while(0==I2C_end);
if(1==I2C_end) return(1);
else return(0);
}
/****************************************************************************
* 名称:ISendStr()
* 功能:向有子地址器件发送多字节数据。
* 入口参数:sla 器件从机地址
* suba 器件子地址
* s 数据发送缓冲区指针
* no 发送数据个数
* 出口参数:返回值为0时表示出错,为1时表示操作正确。
* 说明:使用前要初始化好I2C引脚功能和I2C中断,并已使能I2C主模式
****************************************************************************/
uint8 ISendStr(uint8 sla, uint8 suba, uint8 *s, uint8 no)
{ /* 参数设置 */
I2C_sla = sla; // 写操作的器件地址
I2C_suba = suba; // 子地址
I2C_buf = s;
I2C_num = no;
I2C_suba_en = 2; // 有子地址写
I2C_end = 0;
I2CONCLR = 0x2C;
I2CONSET = 0x60; // 设置为主机,并启动总线
while(0==I2C_end);
if(1==I2C_end) return(1);
else return(0);
}
/****************************************************************************
* 名称:IRcvByte()
* 功能:向无子地址器件读取一字节数据。
* 入口参数:sla 器件地址
* dat 接收数据的变量指针
* 出口参数:返回值为0时表示操作出错,为1时表示操作正确。
* 说明:使用前要初始化好I2C引脚功能和I2C中断,并已使能I2C主模式
****************************************************************************/
uint8 IRcvByte(uint8 sla, uint8 *dat)
{ /* 参数设置 */
I2C_sla = sla+1; // 读操作的器件地址
I2C_buf = dat;
I2C_num = 1;
I2C_suba_en = 0; // 无子地址
I2C_end = 0;
I2CONCLR = 0x2C;
I2CONSET = 0x60; // 设置为主机,并启动总线
while(0==I2C_end);
if(1==I2C_end) return(1);
else return(0);
}
/****************************************************************************
* 名称:IRcvStr()
* 功能:向有子地址器件读取多字节数据。
* 入口参数:sla 器件地址
* suba 器件子地址
* s 数据接收缓冲区指针
* no 读取数据个数
* 出口参数:返回值为0时表示操作出错,为1时表示操作正确。
* 说明:使用前要初始化好I2C引脚功能和I2C中断,并已使能I2C主模式
****************************************************************************/
uint8 IRcvStr(uint8 sla, uint8 suba, uint8 *s, uint8 no)
{ if(0==no) return(0);
/* 参数设置 */
I2C_sla = sla+1; // 读操作的器件地址
I2C_suba = suba;
I2C_buf = s;
I2C_num = no;
I2C_suba_en = 1; // 有子地址读
I2C_end = 0;
I2CONCLR = 0x2C;
I2CONSET = 0x60; // 设置为主机,并启动总线
while(0==I2C_end);
if(1==I2C_end) return(1);
else return(0);
}
/*******************************************************************************
*名称:I2C_Init()
*功能:I2C初始化,包括初始化其中断为向量IRQ中断
*******************************************************************************/
void I2C_Init(uint32 fi2c)
{
if(fi2c>400000) fi2c = 400000; //I2C总线的数据传送速率不能超过400K
PINSEL0 = 0x00000050; // 设置管脚50,58为I2C功能
I2SCLH = (Fpclk/fi2c + 1) / 2; // 设置I2C高电平保持的时钟周期数
I2SCLL = (Fpclk/fi2c) / 2; // 设置I2C低电平保持的时钟周期数
/*设置I2C中断允许*/
VICIntSelect=0x00000000; //设置所有通道为IRQ中断
VICVectCntl0=0x29; //I2C通道分配到IRQ Slot0,即优先级最高
VICVectAddr0=(int)IRQ_I2C; //设置I2C中断向量地址
VICIntEnable=0x0200; //使能I2C中断
}
/*******************************************************************************
*名称: DelayNS()
*功能: 长软件延时
*******************************************************************************/
void DelayNS(uint32 dly)
{ uint32 i;
for(;dly>0;dly--)
for(i=0;i<50000;i++);
}
/*******************************************************************************
*名称: main()
*功能: 向E2PROM写入10字节数据,然后读出判断是否正确写入
*******************************************************************************/
int main(void)
{ uint8 i;
uint8 data_in;
uint8 data_out;
PINSEL0=0x00000050; //设置I/O口工作模式,使用I2C
PINSEL1=0x00000000;
IO0DIR=0x00000000; //设置I/O为输入
IO1DIR=0x7ffffff;
IO1CLR=0x7ffffff;
lcd_init();
I2C_Init(100000);/*设置I2C时钟为100kHz*/
for (i=0;i<10;i++)
{
data_in=i+'0';//初始化写入EEPROM的数据
data_out=i+'a';
}
for(i=0;i<10;i++)
{
DispChar(i,0,data_in);//显示写入的数据
}
ISendStr(CAT24WC02, 0x00, data_in, 10); // 在0x00地址处写入10字节数据
DelayNS(1);
IRcvStr(CAT24WC02, 0x00, data_out, 10); // 在0x00地址处读出10字节数据
for(i=0;i<10;i++)
{
DispChar(i,1,data_out);//显示读出的数据以检验
}
while(1);
} 谢谢楼主,太谢谢你了,问题已解决,是1.8V那我接3.3V了。你的这个帖子挺适合入门的,望继续搞些例子让像我这样的入门者好尽快入门。 不知道你是直接用的上面的工程还是怎么的 如果是直接用的 估计就是版本有问题 我用的是proteus7.1的 问题好像是1.8V电压那里 你看是不是接好了的 或者你自己再按照图自己搭建个原理图看看 咋没人回答我啊?我一仿真就出现这个错误,几个例子都是这个问题,难道是仿真软件版本的问题?我用的是PRO6.9版的 为什么我实验楼主的第一个点灯例子会出现以下错误呢?
http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_494912.JPG
(原文件名:未命名.JPG) 楼主伟大!后续还有吗?期待中,,, 感觉非常cool 留个标号 楼主的头像好面熟 lz也是ARM初学者吗?给你介绍本书跟开发板要不?
本贴被 Joyce 编辑过,最后修改时间:2008-11-10,14:10:40. 不愧 酷 字!~ xue xi zhong 好帖,我顶 现在正需要这些基础的例程 楼主的持之以恒很值得敬佩 谢谢 好人啊,谢谢啊!! 不好意思 确实不是液晶显示连线的问题 是因为下面两个转换口而影响的显示 为什么会有影响我也没弄清楚http://www.ourdev.cn/bbs/emotion/em152.gif
不过仿真总是仿真 如果有实物 最好在实物上做一下 看是否同样存在问题 估计是ad转换那边的问题 我现在先把p0.27 和 p0.28断开 能得到 hello world
http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_427283.gif
(原文件名:1.gif)
连接就出现 hello goblo
http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_427445.gif
(原文件名:2.gif)
请问怎么回事啊??? 点击此处下载 ourdev_426884.rar(文件大小:166K) (原文件名:test2.rar)
这个是你的工程 我加了个proteus仿真文件 用的一样的ARM芯片 你选一样的程序仿真 看看就知道了 我查了一遍 和你的接线是一样的啊
7 8 9 10 11 12 13 14 4 5 6 lcd
19 21 22 26 27 29 30 31 33 34 35 lpc2131
请赐教啊!!! 问题是你液晶显示部分的图接线有问题 http://www.ourdev.cn/bbs/emotion/em040.gif 好不容易才发现 程序都是没问题的 图片上传太大。。重新上传。
http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_423703.gif
(原文件名:1.gif) ADC转换
我把源文件放上来 请帮忙看一下!
源文件含proteusourdev_423663.rar(文件大小:116K) (原文件名:test2.rar)
点击此处打开 ourdev_423664.jpg(文件大小:1.07M,只有400K以内的图片才能直接显示) (原文件名:1.jpg) 哦,这个我明白~ 这个头文件应该是直接拷贝的别人的吧~~~(不会是自己写的哦)O(∩_∩)O
谢谢楼主了! 楼主:
你好
在 你的《ADC转换》中那个例程中 我现在用lpc2131 调试 结果 在lm016l中老是显示FIN MF 之类的字样。
用的是周立功的模板。 这个头文件是LPC2124芯片的一些寄存器的定义 把寄存器地址对应到符号名称 lz 你好~ 我刚刚才做了你的那个点灯的试验。有一个疑问!你的LPC2124.h这个头文件是哪来的啊 ?
我是不折不扣的新手! 望楼主解答! 标记一下 ~~等楼主大大继续上货 恩恩,继续,不错不错。挺好,终于不裸奔了。。哈哈 谁有arm芯片的proteus元件仿真库啊 我也是初学者,继续关注,希望楼主有精彩的奉献 移植UCOS 修改的别人的 把某些部分简化了下 先上工程文件 细节在慢慢讨论
点击此处下载 ourdev_421563.rar(文件大小:279K) (原文件名:UCOS.rar)
前面的启动文件Startup.s只是进行了简单的C运行环境的建立,对中断向量,各种模式下的堆栈都没有进行设置,为了应用中断等,需要对启动文件进行改造^_^,从0x0000 0000开始是ARM的中断向量入口地址,这个和51系列的有点类似,通过装载或是跳转到真正的中断执行程序。
;下面是对arm处理器模式寄存器对应值的常数定义,arm处理器中有一个CPSR程序状态寄存器,它的后五位决定目前的处理器模式
;Pre-defined constants
USERMODE EQU 0x10 ;0b10000 用户模式
FIQMODE EQU 0x11 ;0b10001 FIQ模式
IRQMODE EQU 0x12 ;0b10010 IRQ模式
SVCMODE EQU 0x13 ;0b10011 管理模式
ABORTMODE EQU 0x17 ;0b10111 中止模式
UNDEFMODE EQU 0x1b ;0b11011 未定义
MODEMASK EQU 0x1f ;0b11111 系统模式
NOINT EQU 0xc0
;定义堆栈的大小,根据需要改变
FIQ_STACK_LEGTH EQU 0
IRQ_STACK_LEGTH EQU 64
ABT_STACK_LEGTH EQU 0
UND_STACK_LEGTH EQU 0
MACRO
$HandlerLabel HANDLER $HandleLabel
EXPORT $HandlerLabel ; 输出的标号
IMPORT $HandleLabel ; 引用的外部标号
$HandlerLabel
SUB LR, LR, #4 ; 计算返回地址
STMFD SP!, {R0-R3, R12, LR} ; 保存任务环境
MRS R3, SPSR ; 保存状态
STMFD SP!, {R3}
LDR R2, =OSIntNesting ; OSIntNesting++
LDRB R1,
ADD R1, R1, #1
STRB R1,
BL $HandleLabel ; 调用c语言的中断处理程序
MSR CPSR_c, #0x92
BL OSIntExit
LDR R0, =OSTCBHighRdy
LDR R0,
LDR R1, =OSTCBCur
LDR R1,
CMP R0, R1
LDMFD SP!, {R3}
MSR SPSR_cxsf, R3
LDMEQFD SP!, {R0-R3, R12, PC}^ ; 不进行任务切换
LDR PC, =OSIntCtxSw ; 进行任务切换
MEND
; 启动文件,初始化C程序的运行环境,然后进入C程序代码。
IMPORT |Image$$RO$$Limit|
IMPORT |Image$$RW$$Base|
IMPORT |Image$$ZI$$Base|
IMPORT |Image$$ZI$$Limit|
IMPORT main ; 声明C程序中的Main()函数
IMPORT SoftwareInterrupt ;软中断入口
IMPORT FIQ_Exception ;快速中断异常处理程序
IMPORT OSIntCtxSw ;中断中任务切换函数
IMPORT OSIntExit ;中断退出函数
IMPORT OSTCBCur ;指向当前任务TCB的指针
IMPORT OSTCBHighRdy ;指向将要运行的任务TCB的指针
IMPORT OSIntNesting ;中断嵌套计数器
CODE32 ; 声明32位ARM指令
AREA init,CODE,READONLY ; 声明代码段Start
ENTRY ; 标识程序入口
start
LDR PC, ResetAddr
LDR PC, UndefinedAddr
LDR PC, SWI_Addr
LDR PC, PrefetchAddr
LDR PC, DataAbortAddr
DCD 0xb9205f80
LDR PC,
LDR PC, FIQ_Addr
ResetAddr DCD Reset
UndefinedAddr DCD Undefined
SWI_Addr DCD SoftwareInterrupt
PrefetchAddr DCD PrefetchAbort
DataAbortAddr DCD DataAbort
nouse DCD 0
IRQ_Addr DCD IRQ_Handler
FIQ_Addr DCD FIQ_Handler
;未定义指令
Undefined
b Undefined
;取指令中止
PrefetchAbort
b PrefetchAbort
;取数据中止
DataAbort
b DataAbort
;中断
IRQ_Handler HANDLER IRQ_Exception
;快速中断
FIQ_Handler
STMFD SP!, {R0-R3, LR}
BL FIQ_Exception
LDMFD SP!, {R0-R3, LR}
SUBS PC, LR, #4
;定时器0中断
Timer0_Handler HANDLER Timer0
InitStack
MOV R0, LR
;设置中断模式堆栈
MSR CPSR_c, #IRQMODE|NOINT;#0xd2
LDR SP, StackIrq
;设置快速中断模式堆栈
MSR CPSR_c, #FIQMODE|NOINT;#0xd1
LDR SP, StackFiq
;设置中止模式堆栈
MSR CPSR_c, #ABORTMODE|NOINT;#0xd7
LDR SP, StackAbt
;设置未定义模式堆栈
MSR CPSR_c, #UNDEFMODE|NOINT;#0xdb
LDR SP, StackUnd
;设置系统模式堆栈
MSR CPSR_c, #MODEMASK|NOINT;#0xdf
LDR SP, StackIrq
MOV PC, R0
StackIrq DCD (IrqStackSpace + IRQ_STACK_LEGTH * 4 - 4)
StackFiq DCD (FiqStackSpace + FIQ_STACK_LEGTH * 4 - 4)
StackAbt DCD (AbtStackSpace + ABT_STACK_LEGTH * 4 - 4)
StackUnd DCD (UndtStackSpace + UND_STACK_LEGTH * 4 - 4)
Reset
BL InitStack
; 初始化C程序的运行环境
LDR R0,=|Image$$RO$$Limit|
LDR R1,=|Image$$RW$$Base|
LDR R3,=|Image$$ZI$$Base|
CMP R0,R1
BEQ LOOP1
LOOP0
CMP R1,R3;如果RW区不为空,将加载域的RW数据COPY到运行域
LDRCC R2,,#4
STRCC R2,,#4
BCC LOOP0
LOOP1
LDR R1,=|Image$$ZI$$Limit|
MOV R2,#0
LOOP2
CMP R3,R1;如果ZI区不为空,将ZI区域初始化为0
STRCC R2,,#4
BCC LOOP2
B main ;跳转到c语言入口 Jump to the entry point of C program
;/* 分配堆栈空间 */
AREA MyStacks, DATA, NOINIT
IrqStackSpace SPACE IRQ_STACK_LEGTH * 4 ;中断模式堆栈空间
FiqStackSpace SPACE FIQ_STACK_LEGTH * 4 ;快速中断模式堆栈空间
AbtStackSpace SPACE ABT_STACK_LEGTH * 4 ;中止义模式堆栈空间
UndtStackSpace SPACE UND_STACK_LEGTH * 4 ;未定义模式堆栈
END
本贴被 xiangyuan_122 编辑过,最后修改时间:2008-09-18,10:30:06. cool cool cool