LS1C101单片机实验板,8个LED灯,代表gpio32~gpio39

一.准备工作 下载龙芯交叉编译器http://ftp.loongnix.cn/embedd/ls1c/gcc-4.4.7-gnu.tar.gz 解压到/home/linlin/loongson/

下载裸机演示源码http://ftp.loongnix.cn/embedd/ls1b/func/function-ls1c.tar.gz 解压到/home/linlin/loongson/myls1c/ 要用到tar包的asm.h和bin.lds

二.实验目的

用定时器产生中断,了解中断处理过程

三.实验过程 操作GPIO(即LED灯)和定时器 1.GPIO 1.1) LS1C101共64个GPIO,操作GPIO,通常的做法是分成两组,每组共32位域,输入输出分不同的地址硬件寄存器,寄存器32位对应32个GPIO,即每一位对应一个GPIO。 对输入地址写32位整型值便是同时对32个GPIO输入电平。 对输出地址写32位整型值便是同时使32个GPIO输出电平。

1.2) 本实验不要求同时点亮/熄灭多个LED,所以用另一种方式,即采用LS1C101提供的GPIO位访问端口方式,很为方便。 LS1C101提供64个字节的空间,对应64个GPIO,需按字节访问,即每一个字节对应一个GPIO。 第0位应该是输入/输出高低电平,对应本实验值1为亮,值0为灭。 第1位应该是方向,对应本实验值1为输出电平。 第2~7位LS1C101手册没说明。

第一个GPIO是gpio00,其GPIO位访问端口地址是0xbfeb0080

2.定时器 2.1) LS1C101集成的硬件控制UART、SPI、I2C等都有硬件寄存器,定时器也不例外。LS1C101提供了硬件定时器,包括计数寄存器、比较寄存器、步进寄存器。 硬件寄存器和内存是统一编址,所以读写硬件寄存器内容通过lw、lb、sw、sb指令访问地址。

2.2) LS1C101是MIPS兼容,MIPS提供了内核定时器(同样包括count计数寄存器、compare比较寄存器),其读写是通过mtc0指令,不同于硬件定时器

2.3) 本实验是内核定时器中断,没用硬件定时器,所以下文的定时器是指内核定时器

3.验证 灯gpio35,用于观察已进入中断 灯gpio33,用于观察已返回断点

gpio35灯亮1秒左右便熄灭,代表发生中断 gpio33灯灭1秒左右又点亮,代表返回断点

四. 1.源代码 代码文件为irqtime_start.S,放在/home/linlin/loongson/myls1c/下


#include <asm.h>
#define DELAY(count)    \
    li      t3, count;  \
88:                     \
    nop;                \
    subu    t3, 0x1;    \
    bnez    t3, 88b;    \
    nop

    .set noreorder

    nop

li t1,0xbfeb0030     // watchdog地址
li t2,0xFBFF0400     // 看门狗复位等待时间长为0x400秒
sw t2,0(t1) 

li t1,0xbfeb0080     // 0xbfeb0000+0x80 对应gpio00地址
li t2,0x3            // 0x3 即11为灯亮
sb t2,39(t1)         // sb  为字节写,39(t1)应是相对t1偏移39个字节,即代表gpio39

mtc0  zero,c0_count  // 定时器计数清0
li    t2,999000      // 定时器比较数值,约几秒钟;如果设为很小如值3,会因太快了,在执行清c0_cause开始计数,很快触发中断,来不及进入while(1),就无法通过灯亮/灭验证中断实验
mtc0  t2,c0_compare  // 通用寄存器t2的内容赋给协处理器0的c0_compare寄存器

mtc0  zero,c0_cause  // 必须清c0_cause,计数器才计数;cp0_cause.dc复位为1(表示禁止计数),所以需清零cause.dc位

li    a0,0x00400201  // 定时器允许中断
mtc0  a0,c0_status

li    t1,0xbfeb0080

//--v-- while(1)    // 死循环,中断断点将发生在此里边,便于实验验证
    li v0, 100
99:        

li t4,0x3           // 中断里使用t2值点、灭灯,为排除实验干扰,所以这里不能用t2
sb t4,33(t1)        // 点亮gpio33
    
bnez  v0,99b
nop
//--^--

sb t4,38(t1)        // gpio38不亮,说明while(1)正确,说明此句不执行到,同上不能用t2

//--^ 上面的代码要确保编译后地址不能超过中断入口地址0x380  

.org 0x380          // 中断入口地址,链接的时候应该会安排下面语句到此地址,最终0xbfc00000(链接开始地址)+0x380=0xbfc00380

is_int:

li t1,0xbfeb0080
li t2,0x3
sb t2,35(t1)       // 点亮gpio35,便于观察已进入中断


nop
DELAY(0x5fff)      // 1秒左右
nop
li t2,0x2          // 0x2为灯灭
sb t2,35(t1)       // 熄灭gpio35
sb t2,33(t1)       // 熄灭gpio33

DELAY(0x5fff)      // 让gpio33熄灭1秒左右;如果1秒后gpio33重新点亮,说明正常返回while(1)断点
nop
li t1,0xbfeb0080
li t2,0x3          // #1
sb t2,32(t1)
nop
nop

//在中断处理函数里必须清中断标志,最后eret;不清中断标志的话,eret返回又立即进入中断
//内核定时器清中断是通过写CP0_COMPARE实现(并不是设置状态寄存器禁止中断)
//li  t2,999100     // #2
mtc0  t2,c0_compare // #3

eret                // 这里gpio33灯灭后,如果没有#3处清除中断标志位,返回又会立即中断,并没返回断点(可观察gpio33一直熄灭);发生的断点在while(1)里重新点亮gpio33
nop

2.解析 2.1)#1处 对t2赋值0x3

2.2)#2处 程序开始比较数值设定值是999000,如果要继续响应定时中断,通常做法是在此设置比较数值大于999000的值(因为累计计数值已大于999000),手册不建议清零计数寄存器。 如果要定时999000的时间间隔,应设置999000+999000的值;应该可读取到当前计数的值(读取c0_count),再加999000赋给c0_compare比较寄存器(本文没实验定时间隔)

MIPS指令长度固定32位 #2处999100是立即数,li指令中的立即数的值可以是32位长,这就占满指令长度了。 原来li指令不是真正的机器指令,而是为方便编写汇编程序的宏指令,如果li的立即数大于16位的话,会编译为两条机器指令,加载低16位和高16位,具体什么机器指令不表了。

2.3)#3处 #3处是清中断标志 #2处注释掉赋值t2,所以#3处t2的值是#1处值0x3,但此时的计数值大于999000,即大于比较数值3,因此不会再触发定时器中断

2.4)cause 原因寄存器 第27位是DC位,值1停止count寄存器计数

2.5)status 状态寄存器 第0位是IE位,值1允许中断,值0禁止中断

第15至8位是中断屏蔽IM7至IM0,内核定时器对应IM1,即对应第9位

第22位是bev位,值1为ROM中断入口(通常是0xbfc00380),值0为RAM中断入口(通常是0x80000180)

本实验是在ROM运行,所以bev位需设为1,因此要使内核定时器中断必须设值0x00400201=10000000000001000000001

2.6) sw、sb指令就是访存,C语言可以编译出这类指令,如下一行C语言代码

*(volatile unsigned char*)(0xbfeb0080+39)=0x3 ;// 点亮gpio39 

很简单,就是对地址(0xbfeb0080+39)写内容,即C语言对地址内容进行赋值。 MIPS是硬件和内存统一编址,但C语言是无法区分那是硬件,那是内存;并且C编译器往往尽量优化,对变量能只用寄存器就用寄存器,不会再sw、sb回内存;因此C语言对硬件操作必须用关键字volatile防止编译器的优化,这样才一定会生成sw、sb指令写到硬件 这便是C语言是低级的高级语言,直接操作硬件,单片机使用C语言的缘由

协处理器0(CP0)是MIPS的系统控制协处理器,mtc0指令和CP0相关。个人瞎猜高级语言应该没有语法支持编译出mtc0指令,只能在高级语言程序里嵌入汇编代码

3.编译

linlin@debian:~/loongson/myls1c$ /home/linlin/loongson/opt/gcc-4.4.7-gnu/bin/mipsel-linux-gcc -O2 -fno-builtin -mips32  -fno-pic -mno-abicalls -g -I include -I .  -c irqtime_start.S -nostdinc -nostdlib

生成目标文件irqtime_start.o

4.链接 指定链接开始地址0xbfc00000

linlin@debian:~/loongson/myls1c$ /home/linlin/loongson/opt/gcc-4.4.7-gnu/bin/mipsel-linux-ld -g -T  bin.lds  -o irqtime1c101.elf  irqtime_start.o   -Ttext 0xbfc00000

5.转换为二进制格式

linlin@debian:~/loongson/myls1c$ /home/linlin/loongson/opt/gcc-4.4.7-gnu/bin/mipsel-linux-objcopy -O binary irqtime1c101.elf irqtime1c101.bin

6.烧写 使用flashrom工具,参考<龙芯LS1C101单片机实验(1)--UART> (https://blog.51cto.com/u_13752418/2579076)

( 附:简易分布式容器平台 cocont-ver0.0.1.zip 源代码 下载地址 http://u.163.com/WZ2tpkZHb 提取码: 0ENnMnJp )

( 附:单点登录 Kerberos+LDAP 一键安装脚本 onekeysso-ver0.2.1.zip 源代码 下载地址 http://u.163.com/2ZhkXgw67 提取码: GcAcO2sQ )