虽然是败笔,但是文中基本上介绍了一个小应用实现及问题分析的全过程,希望能够对你有帮助,大胆贴出!毕竟这篇文章写了好久。。。额,写文章的人伤不起啊伤不起。。。
学生时代总会有这么一句笑话:“XXX!去,给我蹲政教处门口唱国歌!”
一直在思索第一个例子用什么,才能引起学生的兴趣,点亮板子的LED?貌似很无聊啊,你也不能拿着给同学炫耀,毫无吸引力,那么就索性用这个例子吧,顺便也抒发一下我对祖国的热爱。让我们的板子唱起国歌来!
前记:在这篇文章之前,我并没有做过类似的实验,所以我们站在同一个起跑线上,千万不要抱怨别人比你厉害是因为他比你聪明。如果试验成功,板子真的这么唱歌了,像我这么笨的人都做到了,你没有理由做不到,这篇文章让你找回自信。
首先要贴出来一篇参考文章,是基于C51的唱国歌的例子,做人要实在,我不想把它说成是我原创的,因为我也没学过C51,呵呵,但是我会举一反三。因为ARM板裸奔跟写C51的程序差不多,虽然扼杀了它的处理能力(本该是跑系统的),但是要练习对硬件的操控能力和做接口实验,这些是必须的。下边是文章内容:
蜂鸣器使用在很多的场合,他一般用来发出报警或者提示的声音,是一种常用的电子器件,这里我给大家 简单的介绍一下用单片机驱动蜂鸣器的方法, 蜂鸣器有二种 1.本身带有驱动电路, 5v,9v,12v 超电压使用, 分 声音沙哑失真。2.象 call 机,喇叭一样,用软件驱动。频率控制音调,时间控制音量大小,第一种蜂鸣器 一般都有一个固定的频率参数也就是他他发出的声音是不能变化的,就象食堂用的打卡器一样,卡一贴近 就发出都的一声。第 2 种就不同了用单片机驱动第 2 种蜂鸣器后还可以使他演 奏出美妙的音乐,我们只需 要用简单的程序就可以控制单蜂鸣器所奏的频率,也就控制了音调。 c51程序实例: 单片机驱动蜂鸣器演奏中华人民共和国国歌的前 4 节的 c51 程序: #include
完文章,我们来做ARM下的实现:
我想当你看完文章,应该有些思路了吧,不知道我们的思路一样不一样,也不知道这个实验可否成功,让我们一起去探索一下》》》》》》
我的思路是这样的:蜂鸣器的控制应该和LED的控制是一样的,根据电路设计,在对应的控制寄存器中的对应控制位写1(或者0)他就唱,相反,他就不唱。而不同的音调就是通过频率来控制的,而频率就是一个动作周期性变化的次数,上边的程序已经写得非常到位,简洁有力,通过while循环和时延控制频率。理论上我们只用作少量改动就可以使用。而这些改动就要看我们自己的板子的设计了,我的是TQ2440,虽不知你的是不是,但是只要你可以在你的板子上迁移成功,证明你已经提升了,OK,here we go。
先看一下TQ2440 的 buzzer相关的电路(在TQ2440底板原理图里):
我们看到了只有一条接入的控制线 TOUT0 ,让我们到S3C2440的针脚中找找它去》》》》》》(在TQ2440_V2核心板原理图.pdf中,直接ctrl+F搜索一下)
我们看到TOUT0和GPB0是共用一个针脚的,就是可以通过GPIO接口来控制这个针脚输出高低电平,从而控制Buzzer的声响和声调,咱们现在就去S3C2440的datasheet里探寻一下这个针脚的控制信息》》》》》
图中我标出了一些信息,这里有GPBCON,故名思意,CON就是configuration的意思,起到针脚的配置作用,这里还有GPBCON的地址信息。GPBDAT 就是用来控制针脚输出的高低电平的。而在最下边GPB0占用的只是GPBCON的0位和1位[1:0],而当这两位是10时,该阵脚就是TOUT0。Ok,基本上明白了吧,我们以上边的代码为基础,进行简单更改,下边是我改后的代码:
主要逻辑:beep.c
#define GPBCON (*(volatile unsigned long*)0x56000010) #define GPBDAT (*(volatile unsigned long*)0x56000014) /* 标准音调频率 */ unsigned int hzs[]={131,147,165,175,1 96,220,247,262,294,330,349,392,440,494,523,587,659,698,784,880,988,1047,1175,1319,1397,1568,1760,1976}; /*频率控制数组*/ char dots[]={8,28,10,12,12,13,28,28,12,28,10,28,8,12,12,12,10,28,28,8,28,5,5,5,5,5,5,8,'#'}; /*延时*/ void delay(unsigned int u) { while(u--); } void play_hz(unsigned int u) { unsigned int i=u; while(i--){ GPBDAT &= ~(1); GPBDAT &= ~(0); delay(18432/u-24); } } void play_sound(int i) { if(i<28) play_hz(hzs[i-1]); else delay(500); } int main(){ GPBCON = (1<<1); while(1){ int i=0; while(dots[i]!='#') play_sound(dots[i++]); delay(20000); } return 0; }
hard_init.S — 完成硬件初始化,关闭看门狗、初始化堆栈、调用main等的工作:
/*这个文件是写裸机程序都要用到的,所以可以保留,以后的裸机程序都可以专注在C语言部分了,不过我没打算写多少裸机实验,因为我们的目标是Linux*/
@******************************* @ File:hard_init.S @ 功能:通过该程序转入C程序 @ @ author:Jun 2011-7-20 @******************************* .text .global _start _start: ldr r0,=0x53000000 @ WATCHDOG 寄存器地址 mov r1,#0x0 str r1,[r0] ldr sp,=1024*4 @ 设置堆栈,注意不能大于4K,因为现在可用的内存只有4K,是片内的4K SRAM bl main halt_loop: b halt_loop
Makefile /*Makefile是参照的嵌入式Linux应用开发完全手册的编译选项,带有反编译的汇编文件,有助于调试和分析*/
beemp.bin:hard_init.S beemp.c arm-linux-gcc -g -c -o hard_init.o hard_init.S arm-linux-gcc -g -c -o beemp.o beemp.c arm-linux-ld -Ttext 0x0000000 -g hard_init.o beemp.o -o beemp_elf arm-linux-objcopy -O binary -S beemp_elf beemp.bin arm-linux-objdump -D -m arm beemp_elf > beemp.dis clean: rm -f beemp.dis beemp.bin beemp_elf *.o
好的,我们放在/home/jun/arm/ext0/下,然后make。。。。
》》beemp.c:5: error:expected '}' before numeric constant
额,是语法错误,数组中的数误加了一个空格,悲剧,重新编译:
》》/home/jun/arm/ext0/beemp.c:23:undefined reference to `__aeabi_uidiv'
额,经过一番查询,发现arm和 arm-linux –gcc 不支持浮点除法,很纠结啊,要自己实现除法,真蛋疼啊。然后用位移实现除法后代码为:
#define GPBCON (*(volatile unsigned long*)0x56000010) #define GPBDAT (*(volatile unsigned long*)0x56000014) /* 标准音调频率 */ unsigned int hzs[]={131,147,165,175,196,220,247,262,294,330,349,392,440,494,523,587,659,698,784,880,988,1047,1175,1319,1397,1568,1760,1976}; /*频率控制数组*/ char dots[]={8,28,10,12,12,13,28,28,12,28,10,28,8,12,12,12,10,28,28,8,28,5,5,5,5,5,5,8,'#'}; /*延时*/ void delay(unsigned int u) { while(u--); } unsigned int div(unsigned int a,unsigned int b) /*除法的简单唯一实现,不是精确值*/ { b = b >> 1; a = a >> b; return a; } void play_hz(unsigned int u) { unsigned int i=u; while(i--){ GPBDAT &= ~(1); GPBDAT &= ~(0); delay(div(18432,u-24)); } } void play_sound(int i) { if(i<28) play_hz(hzs[i-1]); else delay(500); } int main(){ GPBCON = (1<<1); while(1){ int i=0; while(dots[i]!='#') play_sound(dots[i++]); delay(20000); } return 0; }
好的,排除了这些错误后,再make一下,又杯具了:
报错:
beemp.o:(.ARM.exidx+0x0): undefined reference to`__aeabi_unwind_cpp_pr0'
beemp.o:(.ARM.exidx+0x10): undefined reference to`__aeabi_unwind_cpp_pr1'
make: *** [beemp.bin] Error 1
这个错误是编译器造成的,原来之前配置的EABI-4.3.3 编译内核跟给力(编译busybox也很给力的),编译裸机程序就杯具了,真坑爹。只有换编译器了,换编译器容易,安装好办,不过你要把之前安装的EABI-4.3.3时配置的环境注释掉,换成新的编译器的bin目录,两个共存的话肯定会有问题了,就像老师提问张三回答问题,班里却有两个张三,不知道是哪一个。这里附上3.4.5的编译器(【下载地址】/*115网盘下载,如果链接过期请及时留言提醒,我第一时间更新下载链接*/),直接解压到对应位置,修改环境变了即可,具体方法见前边交叉编译环境配置的文章。
OK,一番周折后,配好新的交叉编译工具,我们重新make一下:
好的,顺利通过,得到了二进制的.bin文件,这个就是我们要的……验证奇迹的时刻到了,我们通过FTP工具把编译好的文件下载到windows下进行烧写;(你可以用sjf2440.exe直接裸板烧写,或者直接用uboot通过DNW下载到nandflash中运行);这里我通过DNW下载,因为之前没有介绍过这个下载过程,这里顺带的介绍一下:
1、开发板除了接电源线以为,还要接串口线和USB(接板子的host端,标准USB接口端连电脑)
2、用USB传输,还要安装相应的USB驱动,这个在开发板的光盘中可以找到,驱动是直接的驱动文件,在电脑的硬件管理里手动安装就好
3、安装好后,并连接好了开发板,打开SecureCRT,文件----》快速链接----》协议里选“Serial”,波特率115200,其他默认就好,然后,上电,在焦点在SecureCRT上的情况下点下空格键(或者其他按键),串口输出会停在uboot的菜单项,然后就可以进行下载操作了。/*不过前提是你的板子上已经烧有uboot,这里用的是TQ2440原厂带的uboot,最好把uboot烧到norflash中,norflash比较稳定,较nandflash位反转情况会稳定些,所以一般把bootloader放在norflash,把系统、测试程序、root文件系统放在nandflash,具体烧写uboot过程,用并口通过jtag接口烧写,也可以用jlink,具体做法在各个板子厂商的板子说明文档里应该都有介绍,并且肯定都比我说的专业、详细。我就不再嗷述了*/
4、 我的串口输出截图:
选择a,Download User Program(eg: uCOS-II or TQ2440_Test)。然后等待传输:
然后打开DNW.exe,按图选择你刚通过FTP考到的beep.bin,进行传输:
选择后提示下载到nand成功。
然后重启开发板,从nandflash启动。。。。。。。。
我的启动,我勒个去……一直“嘀~~~~”不对,应该是这样“嘀——”就不间断,更别提频率和声调了。看来只这样傻帽似的用寄存器控制它是不好办的,这个不懂电路和硬件太可怕了,看来这TOUT0的控制还是得用PWM。
额,我太单纯了,单纯的人总会受伤。此实验以失败告终。。。期待下次尝试,就凭着我们的爱国精神,这国歌是必须要唱的。不过好歹还是有些收获的。希望本文没让你失望了(我还是很自信的,哈哈)。
【本文doc文档及源码下载地址】
。
。