第一个跑马灯程序

news/2024/11/24 20:41:28/

    最近准备转行做嵌入式,随弄来一块S3C44B0X的板子,准备好好研究一下。

    板子便宜货,没啥特别完善的资料,都是和网上差不多的ADS环境。

    因为平时还要上班,花了几个晚上的时间,总算是把u-boot和uClinux的编译和下载过程弄熟了。这不,好不容易等来一个周末,决心一定要弄出点名堂来,不能再在门外徘徊了。

    先说说工作环境。
    主机(自己家的,研究Linux内核的那个是公司的><):
        CPU: C4 2.93G
        Memory: 2G
        硬盘: 很大 ~o~
        网络: 电信ADSL 2M
        OS: WinXP SP2
    虚拟机(VirtualBox):
        Memory: 512M
        OS: Ubuntu 7.10 JeOS
        硬盘: 2G + 4G VDI
        网络: 共享主机网络
    开发板:
        MPU: S3C44B0X 66M
        ROM: 8M Flash
        RAM: 2M SDRAM
        IDE: 有
        USB: 1.1
        LCD: 单色128x64
        COM: RS232 UART x 2
        JTAG: 有
        网卡: RTL8019 10M
        其它: 1 x 4键盘,1 x 3 LED,还有其它一些东西是啥我也不懂,再说- -

    因为没多余的网卡,又不想断网,所以先用串口的蜗牛传输撑着吧。其实除了下载uClinux等待时间比较BT以外,对于自己写的程序还好,一般就几k不到,再慢的速度也无所谓倒是。

    其实附送资料上的示例程序里早有了跑马灯,按着文档上的方法跑了一遍,虽然执行正常但是毫无感觉,完全搞不明白是怎么回事,换句话说,毫无成就感。我心想这可不行,这跟没学差不多,等想想办法了。

    仔细想想最莫名其妙的部分就是ADS的一堆设置,以及代码里一些奇怪的外部符号比如|Image$$RO$$Base|之类的,不知道是怎么来的,后来google了老半天才知道是ADS的设置参数。这些东西一来就搞的人头晕眼花的,于是我决定换一个思路,扔掉ADS,手动搞定这一切。

    说起手动,不是一件容易的事情。本来我准备在板子上裸奔的,但发现自己过于菜鸟,一时半会还弄不出一个bootloader来。搞得不好一个周末下来,马没跑成,我的热情都跑光了。

    经过一阵迷茫,我突然想起了u-boot,这个之前我载完uClinux就扔掉的东西。这个开源的bootloader真是好处多多啊,既是bootloader,又能充当BIOS,甚至就像是个没有FS的DOS,可以直接跑裸机程序,但又不需要费劲去弄中断初始化之类的工作。

    于是我的方案就出来了,u-boot引导后,用loadb命令装载我的程序到RAM,然后用go命令跳转到程序入口点执行!

    做人不能光耍嘴皮子,这就立刻动手!

    为了验证我的方案可行,决定先写一个自动重启的程序,思路很简单,直接跳转到00000000h,也就是中断向量表中reset中断的位置执行。代码只有一行:
        mov    pc, #0

reset.s
.text     mov    pc# 0

    注意,因为使用GNU工具链有着各种各样的好处,并且对我来说也是轻车熟路,因此这里的汇编语法是GNU AS的语法,不熟悉的人自己google去吧,其实和ADS的ARMASM没什么本质的不同。

    因为这程序太简单了,连Makefile也没必要写,在Linux下执行:
        arm-elf-as -o reset.o reset.s
        arm-elf-objcopy -O binary -R .comment -R .note -S reset.o reset.bin

    于是乎就得到了裸机程序reset.bin,大小只有4字节,汗,因为就只有一个指令。

    OK,给板子加电,打开超级终端,进入u-boot,输入loadb 0xC0000000后把reset.bin传过去,瞬间就传完了(因为只有4字节。。汗),然后go 0xC0000000。

    成功!板子立刻重启了。这证明了我的方案是可行的,剩下的活就是怎么点灯了。

    因为我是极度菜鸟,完全不知道怎么下手,看示例代码又完全不知所云。无奈使出google大法,找了一大堆资料。最后我明白了问题的关键就在于I/O(ARM的可编程I/O,PGIO)上,但是由于该芯片的I/O端口是复用的,所以需要编程操作I/O控制寄存器。

    顺便提一个概念。与x86机器有很大的不同,ARM是统一编址的(这个概念我以前也只有在课本上见过XD),而x86是独立编址,也就是说存储器和设备寄存器的编址是分开的,因此我们通常需要in out指令来操作IBM PC硬件寄存器,而所谓的硬件端口其实就是不同于存储器的另一套编址方式。对ARM来说,设备寄存器和存储器的编址是统一的,也就是说,直接通过mov等传送指令就可以访问硬件寄存器。独立编址和统一编址各有利弊,这里不做讨论,有兴趣的人请回去补习计算机体系结构。

    S3C44B0X这款芯片的硬件寄存器被映射到了物理地址01c00000h~01ffffffh之间4M的地址空间里,对它们访问则可以控制硬件。

    我看到网上某篇文章讲到,LED0和LED1通过23(nGCS4)和24(nGCS5)号引脚连接到S3C44B0X芯片,而根据S3C44B0X芯片手册,nGCS4和nGCS5引脚受PCONB控制寄存器控制,当该寄存器的第9位PB9或第10位PB10为1时,通过访问PDATB的第9位或第10 位则可以控制LED0和LED1,低电平LED亮,高电平LED灭。

    看起来并不难,动手写了个C文件按照该文所说实现了一个程序led.c,并且在跑马灯结束后自动reset,Linux下:
       arm-elf-gcc -nostdinc -c -o led.o led.c
       arm-elf-objcopy -O binary -R .comment -R .note -S led.o led.bin

    用loadb(不写参数默认载到0x0c008000)载到板上跑了下一看,无效。。。。而且死机(没有reset)。

    我郁闷,经过无数的检查之后恍然大悟,直接objcopy led.o,由于led.c里并没有函数入口点,默认入口点会被当作是00000000h,而我的程序是跑在0c008000h处。这么一来,长跳转和函数调用的目标地址都会比正确值小0x0c008000,那当然会有问题了!于是需要这么干:
       arm-elf-gcc -c -o led.o led.c
       arm-elf-ld -nostdinc -Ttext 0x0c008000 -o led.elf led.o
       arm-elf-objcopy -O binary -R .comment -R .note -S led.elf led.bin

    载到板上,还是无效!不过不会死机了。

    继续郁闷,突然想起来早上泡的衣服还没晾,急急忙忙去晾衣服。在晾衣服的过程中,我突然想到了是不是由于那文章所用的板和我的不一样,因此发光二极管接的位置也不同?于是洗完衣服后我急忙翻出PCB原理图看了起来。幸好大学时候没白费光阴,多少还是能看懂一些的。根据原理图,我发现我的板子LED0~2分别接在117(GPC1)、116(GPC2)、115(GPC3)三个引脚上,并且三个LED都多接了一个反相器(74HC04),如下图。看来果然问题就出在这里。



我重新翻出I/O控制寄存器表,发现这三个引脚是由PCONC控制的,哈,这下好办了。根据资料,我把先前的程序又改了一下,编译通过,载到板子,成功! LED灯终于亮了!不过亮灭是反的。。。怎么回事呢?问题就出在那三个反向器,我把它们给忘了,先前参考的文章里是没有它们的。于是把高低电平反过来,编译,下载,运行,成功!这次LED终于如预期般的亮灭了。

    喜悦之余也感叹前面的路途遥远,点个灯都这么难,自己还是太菜了。

    最后奉上源代码和Makefile:

led.c
#define  PCONC   ((volatile int *)0x1d20010)
#define  PDATC   ((volatile int *)0x1d20014)
#define  PUPC    ((volatile int *)0x1d20018)

void  init( void );
void  led( int int );
void  delay( int );

void entry( void )   /* 对于裸机C程序,入口函数必须放在文件的最前面 */
{
    init();
    while (1{
        led(
1,
1);      /* 点亮LED1 */
        delay(
1000);    /* 延时 */
        led(
2,
1);
      /* 点亮LED2 */
        delay(1000);
       
led(3, 1);
      /* 点亮LED3 */
        delay(1000);
       
led(10);
      /* 熄灭LED1 */
        delay(1000);
       
led(2, 0);
      /* 熄灭LED2 */
        delay(1000);
       
led(3, 0);
      /* 熄灭LED3 */
        delay(1000);
    }
}


void init(
void)
{
   /* 下面两句将PCONC寄存器的PC1、PC2、PC3都置为01,意为将其作为LED输出 */
    *PCONC &= ~0xa8;
    *
PCONC |= 0x54;
}


void led(int num, int light)
{
    
if (light)
        
*PDATC |= 1 << num;      /* 点亮第num盏灯 */
    
else
        
*
PDATC &= ~(1 << num);   /* 熄灭第num盏灯 */
}

void delay(int times)
{
    
volatile int i;   /* volatile声明防止下面的循环被编译器和谐 */
    
for (i = 0; i < (times << 10); ++i);   /* 非精确延时,临时方法 */
}

Makefile
CC  =  arm -elf- gcc
AS arm-elf-as
LD 
=  arm -elf- ld
OBJCOPY 
=  arm -elf- objcopy
ADDRESS 
=   0x0c008000

.PHONY : all clean

all : led.bin

clean :
    rm 
- f led.bin
    rm 
- led.o
    rm 
- f led.elf
    rm 
- led.S

led.bin : led.elf
    $(OBJCOPY
- O binary  - R .comment  - R .note  - S $ <  $@
    chmod a
- x $@

led.elf : led.o
    $(LD
- Ttext $(ADDRESS -nostdinc - o $@ $ <
    chmod a - x $@

led.o : led.S
    $(AS
- o $@ $ <

led.S : led.c
    $(CC
-S - o $@ $ <

    在u-boot下使用loadb命令,通过超级终端把led.bin下载到开发板的RAM里,然后通过go 0x0c008000命令跳转到程序入口点即可。板子和我的不同的可以根据资料自行修改。


http://www.ppmy.cn/news/847990.html

相关文章

单片机跑马灯源代码+仿真

源代码&#xff1a; #include<reg52.h> //库文件 #define uchar unsigned char//宏定义无符号字符型 #define uint unsigned int //宏定义无符号整型 /********************************************************************初始定义 **********************…

跑马灯效果的代码

http://www.htmleaf.com/jQuery/Text-Link-Effects/201505221881.html

学无止境·MySQL⑦(索引和视图)

索引和视图练习 索引练习1、建立一个utf8编码的数据库test12、建立商品表goods和栏目表category3、删除 goods 表中的 goods_desc 字段及货号字段,并增加 click_count 字段4、在 goods_name 列上加唯一性索引&#xff08;用alter table方式&#xff09;5、在 shop_price 列上加…

键盘按键代码

常数名称 十六进制值 十进制值 对应按键 VK_LBUTTON011鼠标的左键VK_RBUTTON022鼠标的右键VK-CANCEL 033Ctrl Break(通常不需要处理)VK_MBUTTON044鼠标的中键&#xff08;三按键鼠标)VK_BACK088 Backspace键VK_TAB099 Tab键VK_CLEAR0C12Clear键&#xff08;Num Lock关闭时的数字…

html跑马灯代码大全(图片文字移动代码)

<marquee>...</marquee>普通卷动 <marqueebehaviorslide>...</marquee>滑动 <marqueebehaviorscroll>...</marquee>预设卷动 <marqueebehavioralternate>...</marquee>来回卷动 <marqueedirectiondown>...</marq…

用代码简单的实现跑马灯效果

在res资源文件夹下的layout中定义一个TextView控件代码如下&#xff1a; <TextViewandroid:text"string/hehe"android:layout_width"wrap_content"android:layout_height"wrap_content"android:singleLine"true"android:ellipsize&…

跑马灯 代码

布局 <com.xxxx.aaaa.cccc.view.MarqueeTextViewandroid:id"id/invest_detail_more_marquee"android:layout_width"match_parent"android:layout_height"wrap_content"android:ellipsize"marquee"android:textSize"dimen/TF2…

html跑马灯/走马灯效果

http://blog.sina.com.cn/s/blog_a7b8ab2801019gog.html 实现跑马灯的方法很多&#xff0c;其中最简单的是采用一句Html代码来实现&#xff0c;我们在需要出现跑马灯效果的地方插入“<marquee>滚动的文字</marquee>”语句&#xff0c;它的效果如下所示&#xff1a…