龙芯LS1C101单片机实验板
本人从淘宝购买了龙芯单片机实验板,非白菜板,真正的单片机.借此白菜板宝地.购买链接就不给出了,有意者请到淘宝搜索龙芯LS1C101LS1C101单片机实验板,店面的介绍:
1颗龙芯1C101,引脚是QFP64
1颗板载SPI闪存W25Q64,容量是8MB
8个红色LED灯,用于显示引脚电平,进行GPIO相实验测试
为烧录,还从店主购入配套的USB烧录编程器,店面介绍是使用ch341a芯片
本人第一次接触单片机,下文有错误的地方,请指正.
实验板正面,板上数字是GPIO编号
左侧排针 右侧排针
--------------------------------------------
GND3V CA R?(连上14)
B0 12 R? R?
B1 13 3V RA
09 08 GNDRB
07 06 14 15
A0 05 REST 16 3V
。。。
49 N49
50 N50
51 N51
32 (8个LED灯,代表gpio32~gpio39)
33
...
39
-------------------
| 一组SPI引脚排针 |
-------------------
到手的实验板:
49-N49用跳帽短接,50-N50用跳帽短接,14和CA同一行的'R?'用杜邦线连上.我也不懂有什么作用.
3V-RA跳帽短接,GND-RB跳帽短接.
已烧录了程序,应是烧录在外部板载闪存,而不是在LS1C101 CPU片上闪存.
SPI排针已插上烧录编程器
初步使用:
将烧录器USB端插到电脑,通上电可见运行效果,其功能8个LED灯依次点亮,不断循环.
拨掉3V-RA,GND-RB这两个的跳帽,实验板仍会运行,不知这两个是作什么的.
实验板的CPU供电应该也是通过SPI排针上的烧录器.
关于烧录,该实验板没有白菜板介绍那样的拨码开关可拨到烧录模式.我也没烧录的经验,死马当活马医,发现只要复位键一直按住不放,就可烧录成功.
本人对电路一窍不通,上电复位复位键按下不松开应该不能理解为断开给CPU的供电的吧.哪位解析一下复位键按住到松开的过程
下面是实验的过程,并按店主出厂的跳线、跳帽原貌
一.运行环境:x86,debian
店主提供了win下的烧录程序,本着Linux的精神,找到Linux下强大的刷闪存的命令行工具flashrom
安装flashrom
root@debian:/# apt-get install flashrom
linlin@debian:~$ /usr/sbin/flashrom -p ch341a_spi --help
flashrom v1.2 on Linux 5.5.0-1-amd64 (x86_64)
flashrom is free software, get the source code at https://flashrom.org
...
-r | --read <file> read flash and save to <file>
-w | --write <file> write <file> to flash
-v | --verify <file> verify flash against <file>
-E | --erase erase flash memory
-V | --verbose more verbose output
-c | --chip <chipname> probe only for specified flash chip
...
-L | --list-supported print supported devices
-p | --programmer <name>[:<param>] specify the programmer device. One of
internal, dummy, nic3com, nicrealtek, gfxnvidia, drkaiser, satasii, atavia,
it8212, ft2232_spi, serprog, buspirate_spi, dediprog, developerbox,
rayer_spi, pony_spi, nicintel, nicintel_spi, nicintel_eeprom, ogp_spi,
satamv, linux_mtd, linux_spi, usbblaster_spi, pickit2_spi, ch341a_spi,
digilent_spi, stlinkv3_spi.
You can specify one of -h, -R, -L, -E, -r, -w, -v or no operation.
If no operation is specified, flashrom will only probe for flash chips.
linlin@debian:~$
可见支持编程器ch341a芯片
linlin@debian:~$ /usr/sbin/flashrom -L |grep W25Q64
Winbond W25Q64.V PREW 8192SPI
Winbond W25Q64.W PREW 8192SPI
linlin@debian:~$
可见支持闪存W25Q64芯片
以下需在root根用户下运行flashrom命令
插上USB烧录器,8个LED灯依次点亮,不断循环
root@debian:/home/linlin# /usr/sbin/flashrom -p ch341a_spi
flashrom v1.2 on Linux 5.5.0-1-amd64 (x86_64)
flashrom is free software, get the source code at https://flashrom.org
Using clock_gettime for delay loops (clk_id: 1, resolution: 1ns).
No EEPROM/flash device found.
Note: flashrom can never write if the flash chip isn't found automatically.
root@debian:/home/linlin#
命令提示没找到闪存设备
应该是一通电,单片机cpu在运行,外部程序无法读取板载的flash,不知这样理解对不对。
二.读、擦除、写闪存过程
步骤1~4都要先按住实验板复位键不放,然后运行flashrom命令,一直到命令完成才能松开复位键
一直按住复位键不放,一排LED灯全程是灭的,应该店主已烧录好的程序没在运行,即表示CPU不运行吧。
1.读闪存芯片型号
root@debian:/home/linlin# /usr/sbin/flashrom -p ch341a_spi
flashrom v1.2 on Linux 5.5.0-1-amd64 (x86_64)
flashrom is free software, get the source code at https://flashrom.org
Using clock_gettime for delay loops (clk_id: 1, resolution: 1ns).
Found Winbond flash chip "W25Q64.V" (8192 kB, SPI) on ch341a_spi.
No operations were specified.
root@debian:/home/linlin#
命令很快就完成
可见型号是W25Q64.V
2.读ROM
备份ROM原来的程序到ls1c_old.bin
root@debian:/home/linlin# /usr/sbin/flashrom -p ch341a_spi -c W25Q64.V -r ls1c_old.bin
flashrom v1.2 on Linux 5.5.0-1-amd64 (x86_64)
flashrom is free software, get the source code at https://flashrom.org
Using clock_gettime for delay loops (clk_id: 1, resolution: 1ns).
Found Winbond flash chip "W25Q64.V" (8192 kB, SPI) on ch341a_spi.
Reading flash... done.
root@debian:/home/linlin# ls
ls1c_old.bin
root@debian:/home/linlin#
命令花费1分钟左右读完
3.擦除flash
root@debian:/home/linlin# /usr/sbin/flashrom -p ch341a_spi -c W25Q64.V -E
flashrom v1.2 on Linux 5.5.0-1-amd64 (x86_64)
flashrom is free software, get the source code at https://flashrom.org
Using clock_gettime for delay loops (clk_id: 1, resolution: 1ns).
Found Winbond flash chip "W25Q64.V" (8192 kB, SPI) on ch341a_spi.
Erasing and writing flash chip... Erase/write done.
root@debian:/home/linlin#
命令花费1分钟左右擦除完毕
root@debian:/home/linlin# /usr/sbin/flashrom -p ch341a_spi -c W25Q64.V -r ls1c_null.bin
导出已擦除的ROM到ls1c_null.bin,用二进制查看工具查看ls1c_null.bin已全为FF,说明闪存已全部擦除
插上USB烧录器通电,8个LED灯不亮,应该说明CPU不在运行,应该说明闪存已没可运行的程序
4.烧写
我没编写程序,就用备份的ls1c_old.bin来测试烧录效果
root@debian:/home/linlin# /usr/sbin/flashrom -p ch341a_spi -c W25Q64.V -w ls1c_old.bin
flashrom v1.2 on Linux 5.5.0-1-amd64 (x86_64)
flashrom is free software, get the source code at https://flashrom.org
Using clock_gettime for delay loops (clk_id: 1, resolution: 1ns).
Found Winbond flash chip "W25Q64.V" (8192 kB, SPI) on ch341a_spi.
Reading old flash chip contents... done. 读了一分钟左右提示Erasing and writing
Erasing and writing flash chip... Erase/write done. 很快就提示Verifying.这里不是很理解,难道写入很快吗?不懂flashrom的机制
Verifying flash... VERIFIED. 校验又花了一分钟左右
root@debian:/home/linlin#
命令总共花费2分钟左右烧录完成
实验板通上电,8个LED灯依次点亮,不断循环.说明烧录成功
5.反汇编
1)
下载龙芯交叉编译器http://ftp.loongnix.org/embedd/ls1c/gcc-4.4.7-gnu.tar.gz
并解压到/home/linlin/loongson/
2)
linlin@debian:~$ /home/linlin/loongson/opt/gcc-4.4.7-gnu/bin/mipsel-linux-objdump -D -b binary -m mips -ELls1c_old.bin > ls1c_old.txt
linlin@debian:~$
objdump 导出的ls1c_old.txt一下子有67M
查看ls1c_old.txt结果,代码很长很长,我也不清楚原来的程序在哪结束,那些是烧录的垃圾数据.我截断前一小段,大概可看出功能.
Disassembly of section .data:
00000000 <.data>:
0: 00000000 nop
4: 40806000 mtc0 zero,$12
8: 40806800 mtc0 zero,$13
c: 20040007 addi a0,zero,7
10: 3c06bfe7 lui a2,0xbfe7
14: 34c60004 ori a2,a2,0x4
18: a0c40000 sb a0,0(a2)
1c: 20070005 addi a3,zero,5
20: 3c09bfe7 lui t1,0xbfe7
24: 35290006 ori t1,t1,0x6
28: a1270000 sb a3,0(t1)
2c: 200a00ff addi t2,zero,255 注1
30: 3c0cbfeb lui t4,0xbfeb 注2
34: 358c0050 ori t4,t4,0x50 注3
38: ad8a0000 sw t2,0(t4)
3c: 200d005c addi t5,zero,92 注4
40: 3c04bfeb lui a0,0xbfeb 注5
44: 34840054 ori a0,a0,0x54 注6
48: ac8d0000 sw t5,0(a0)
4c: 3c16be00 lui s6,0xbe00 以下的一大段代码就不懂了,省略
50: 36d60000 ori s6,s6,0x0
54: 00000000 nop
58: 341e033c li s8,0x33c
5c: 03d6f020 add s8,s8,s6
60: 03c0f809 jalr s8
64: 00000000 nop
68: 3c06a000 lui a2,0xa000
6c: 34c61ffc ori a2,a2,0x1ffc
70: acc20000 sw v0,0(a2)
74: 00000000 nop
...
参考龙芯LS1C101手册
PMU电源管理模块,其基址:0xbfeb0000
GPIO功能在该模块中,就是基址上的偏移
名称 偏移 描述
----------------------------------
GPIOB_OE 0x50 GPIOB输出使能
GPIOB_O 0x54 GPIOB输出电平
说明:电源管理模块的基址低16位刚好全为0,所以汇编写起了就简单了
80脚封装有GPIO00~GPIO63应构成两组GPIO,即GPIO00~GPIO31和GPIO32~GPIO63两组,但手册没说明对应那个组,只能猜测GPIO32~GPIO63对应GPIOB组(即第二组gpio ?).
但查看手册64脚封装图好像没GPIO00和GPIO63,本实验板的CPU是64脚封装
手册没描述寄存器位域和GPIO编号对应关系,猜测GPIOB组的GPIO32对应位域第0位,GPIO33对应第1位,依次类推.以下是猜测的对应关系
寄存器位域 31 30 29 ... 3 2 1 0
/ \ / \
GPIO62 GPIO61 GPIO33 GPIO32
注1 这个255=0xFF=011111111 ,应该置GPIO32~GPIO39输出使能
注2、注3 这个的高16位0xbfeb加上低16位0x50应就是GPIOB输出使能寄存器地址,0xbfeb即PMU基址高16位
注4 这个 92=0x5C=001011100 ,第2位(从右往左第0位开始)为1应该代表GPIO34输出电平1,这个应是同时点亮GPIO34、35、36、38吧,其它灭
注5、注6 这个的高16位0xbfeb加上低16位0x54应就是GPIOB输出电平寄存器地址
本帖最后由 VG2018 于 2020-8-6 11:38 编辑
谢谢分享。 淘宝链接我给出
https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4004-18993838242.6.60c51821rLEUUD&id=610067293029
店主铺面
https://csee.taobao.com
广告嫌疑,呵呵 那個1c300b的有沒有人試過 不错 你用的是什么ide 容易学习吗? 没用到IDE,店主有提供自己开发的IDE,但可能不适合程序员,应该是适合不需编程的电气工程师吧.
下面继续我的文章:裸机编程 本帖最后由 szhm 于 2020-11-10 13:21 编辑
6.裸机编程
极其简单的程序,仅仅点亮LED
我的第一次编写汇编,呵呵
1)功能
仅点亮一排LED灯中的一个GPIO35灯
2)下载龙芯公司的裸机演示例子
下载http://ftp.loongnix.org/embedd/ls1b/func/function-ls1c.tar.gz
解压到/home/linlin/loongson/myls1c
注意:该tar包的演示例子是LS1C,不是LS1C101,有关龙芯单片机(LS1D、LS1C101)的资料极其稀缺
我的裸机程序要用到tar包的asm.h和bin.lds
bin.lds是裸机的链接脚本
asm.h我不是很懂,不知是不是因程序里用到li宏指令
gcc编译命令行参考了tar包make脚本
3)读写GPIO
我没用章节5.通常的GPIO组寄存器位域(与GPIO编号对应)读写方法,而是使用LS1C101手册中GPIO位访问端口方式(不知道龙芯系列CPU中是不是LS1C101所特有)
GPIO位访问端口也在PMU电源管理模块,其相对PMU基址的偏移0x80~0xBF,共64个字节空间,每个字节对应一个GPIO
位域 描述
-----------------
7:2 手册没描述
1 GPIO方向,1为输出
0 应该是该位域值1为高电平吧,手册描述不清楚
4)源代码,文件名led1c101.S
#include <asm.h>
/* Delay macro */
#define DELAY(count) \
li t2, count; \
99: \
nop; \
subu t2, 0x1; \
bnez t2, 99b; \
nop
.set noreorder
nop
//reset:
mtc0 zero, c0_status //$12清零cp0 status寄存器
mtc0 zero, c0_cause //$13清零cp0 cause寄存器
//--v--
li t1, 0xbfeb0080//0xbfeb0000+0x80 对应gpio00
DELAY(20000) //延时
li t2,0x3 // 0x3 即11为灯亮
sb t2, 35(t1)// sb 为字节写,35(t1)应是相对t1偏移35个字节,即代表gpio35
//--^--
nop
5)编译、链接
led1c101.S放在function-ls1c.tar.gz解压后所在目录/home/linlin/loongson/myls1c
linlin@debian:~$ cd /home/linlin/loongson/myls1c
编译
linlin@debian:~/loongson/myls1c$ /home/linlin/loongson/opt/gcc-4.4.7-gnu/bin/mipsel-linux-gcc -O2 -fno-builtin -mips32 -I /home/linlin/loongson/myls1c/include -include common.h -fno-pic -mno-abicalls -g -I include -I .-c led1c101.S -nostdinc -nostdlib
linlin@debian:~/loongson/myls1c$ ls led*
led1c101.oled1c101.S
链接
linlin@debian:~/loongson/myls1c$ /home/linlin/loongson/opt/gcc-4.4.7-gnu/bin/mipsel-linux-ld -g -Tbin.lds-o led1c101.elf led1c101.o
/home/linlin/loongson/opt/gcc-4.4.7-gnu/bin/mipsel-linux-ld: warning: cannot find entry symbol _start; defaulting to 0000000080200000
linlin@debian:~/loongson/myls1c$ ls led*.elf
led1c101.elf
转换为二进制格式
linlin@debian:~/loongson/myls1c$ /home/linlin/loongson/opt/gcc-4.4.7-gnu/bin/mipsel-linux-objcopy -O binary led1c101.elf led1c101.bin
linlin@debian:~/loongson/myls1c$ ls led*.bin
led1c101.bin
led1c101.bin就是要烧写的文件
linlin@debian:~/loongson/myls1c$ ls led*
led1c101.binled1c101.elfled1c101.oled1c101.S
linlin@debian:~/loongson/myls1c$ /home/linlin/loongson/opt/gcc-4.4.7-gnu/bin/mipsel-linux-objdump -d led1c101.elf
led1c101.elf: file format elf32-tradlittlemips
Disassembly of section .text:
80200000 <_ftext>:
80200000: 00000000 nop
80200004: 40806000 mtc0 zero,c0_status
80200008: 40806800 mtc0 zero,c0_cause
8020000c: 3c09bfeb lui t1,0xbfeb
80200010: 35290080 ori t1,t1,0x80
80200014: 240a4e20 li t2,20000
80200018: 00000000 nop
8020001c: 254affff addiu t2,t2,-1
80200020: 1540fffd bnez t2,80200018 <_ftext+0x18>
80200024: 00000000 nop
80200028: 240a0003 li t2,3
8020002c: a12a0023 sb t2,35(t1)
...
80200040: 00000600 sll zero,zero,0x18
80200044: 00000000 nop
80200048: 00000009 jalr zero,zero
8020004c: 00000000 nop
...
6)烧写
root@debian:/home/linlin/loongson/myls1c# /usr/sbin/flashrom -p ch341a_spi -c W25Q64.V -w led1c101.bin
flashrom v1.2 on Linux 5.5.0-1-amd64 (x86_64)
flashrom is free software, get the source code at https://flashrom.org
Using clock_gettime for delay loops (clk_id: 1, resolution: 1ns).
Found Winbond flash chip "W25Q64.V" (8192 kB, SPI) on ch341a_spi.
Error: Image size (4112 B) doesn't match the flash chip's size (8388608 B)!
root@debian:/home/linlin/loongson/myls1c#
烧写失败,因为led1c101.bin大小与闪存不一致
见提示Image size其led1c101.bin为4112 B,而闪存为8388608 B(8M)
led1c101.bin需用dd命令填满到8M(参考白菜板教程)
计算填充大小
8388608-4112=8384496
root@debian:/home/linlin/loongson/myls1c# dd if=/dev/zero bs=1 count=8384496 >> led1c101.bin
记录了8384496+0 的读入
记录了8384496+0 的写出
8384496 bytes (8.4 MB, 8.0 MiB) copied, 26.0035 s, 322 kB/s
led1c101.bin大小已8M
再次烧录已成功
root@debian:/home/linlin/loongson/myls1c# /usr/sbin/flashrom -p ch341a_spi -c W25Q64.V -w led1c101.bin
flashrom v1.2 on Linux 5.5.0-1-amd64 (x86_64)
flashrom is free software, get the source code at https://flashrom.org
Using clock_gettime for delay loops (clk_id: 1, resolution: 1ns).
Found Winbond flash chip "W25Q64.V" (8192 kB, SPI) on ch341a_spi.
Reading old flash chip contents... done.
Erasing and writing flash chip... Erase/write done.
Verifying flash... VERIFIED.
root@debian:/home/linlin/loongson/myls1c#
7)运行
上电,已见到gpio35已亮,亮3秒,灭1秒,不断反复
手册中看门狗默认每4秒复位,因为程序里没喂狗,应该就是每4秒CPU复位一次,应该GPIO复位值是灭的,所以过程应如下:
-->启动-->灯灭-->延时-->点亮-->程序结束-->保持灯亮-->看门狗复位---|
| |
|-----------------------------------------------------------------|
本帖最后由 szhm 于 2020-11-12 09:02 编辑
7.点亮TFT
参考本站链接http://www.openloongson.org/foru ... =116&extra=page%3D1
本人早期的拙作<龙芯1C智龙主板如何驱动LCD>,本章节移植该文
仍是ILI9341芯片的TFT液晶屏,与LS1C101实验板接线可参考上文
1)源码
// tft_start.S
.set noreorder
nop
/* 0xa000_1000+1024=0xA0001400 作为栈空间开始地址,使用了片上内存(0xa000_1000~0xa000_1fff 数据RAM,不可取指) */
la $sp, 0xA0001400
/* 跳转到C程序main函数 */
jal main
nop
// tft1c101.h
// size:240*320,格式:RGB565(16位,2字节),转换图片工具见https://sourceforge.net/projects ... es/version%200.6.2/
static const unsigned char imgBuffer={ // 240*320*2=153600=150K
24,195,24,196,24,196,24,195,24,195,
24,195,24,163,16,163,16,163,24,162,
33,3,41,68,57,165,74,6,98,168,
...略
222,219,222,219,222,219,222,219,214,187,
214,187,214,154,214,187,214,187,206,154,
206,154,214,154,206,154,214,154,214,187
};
// tft1c101.c
// GPIO模拟SPI(3线)
#include "tft1c101.h"
#define watchdog 0xbfeb0030 //看门狗
//引脚GPIO位访问端口地址
#define SPI_RST0xbfeb00A3 //gpio35
#define SPI_SDA0xbfeb00A4 //gpio36
#define SPI_SCL0xbfeb00A5 //gpio37
#define SPI_CS 0xbfeb00A6 //gpio38
void s_send(unsigned char X)
{
int j=8;
do
{
if(X&0x80)// 0x80= 10000000
*(volatile unsigned char*)(SPI_SDA)=3 ;// 输出 1
else
*(volatile unsigned char*)(SPI_SDA)=2 ;// 输出 0
*(volatile unsigned char*)(SPI_SCL)=2 ; // 0
*(volatile unsigned char*)(SPI_SCL)=3 ; // 1
--j;
X<<=1;
}while(j);
}
void s_com(unsigned char X)//写命令
{
*(volatile unsigned char*)(SPI_CS)=2 ; //0
*(volatile unsigned char*)(SPI_SDA)=2 ;//0 :3线,先发一位: 命令 -- 0
*(volatile unsigned char*)(SPI_SCL)=2 ;//0
*(volatile unsigned char*)(SPI_SCL)=3 ;//1
s_send(X);
*(volatile unsigned char*)(SPI_CS)=3 ; //1
}
void s_data(unsigned char X) //写数据
{
*(volatile unsigned char*)(SPI_CS)=2 ; //0
*(volatile unsigned char*)(SPI_SDA)=3 ;//1 :3线,先发一位: 数据 -- 1
*(volatile unsigned char*)(SPI_SCL)=2 ;//0
*(volatile unsigned char*)(SPI_SCL)=3 ;//1
s_send(X);
*(volatile unsigned char*)(SPI_CS)=3 ; //1
}
void coordinate(unsigned int col,unsigned int page)
{
s_com(0x2a); //Column Address Set
s_data(0x00);
s_data(col);
s_data(0x00);
s_data(0xef);
s_com(0x2b); //Row Address Set
s_data((page&0xff00)>>8);
s_data(page&0x00ff);
s_data(0x01);
s_data(0x3f);
s_com(0x2c); //Memory Write
}
void main (void)
{
*(volatile unsigned int*)(watchdog)=0xFBFF0400 ; //设置看门狗复位等待时间长为0x400秒
// LCD Reset
*(volatile unsigned char*)(SPI_RST)=2 ;//0
*(volatile unsigned char*)(SPI_RST)=2 ;//0
int i;
// LS1C101主频只8MHz,以下的延时功能可能不需要
for (i=9;i>0;i--);//延时
*(volatile unsigned char*)(SPI_RST)=3 ;//1
*(volatile unsigned char*)(SPI_RST)=3 ;//1
for (i=9;i>0;i--);//延时
//--v-- ILI9341初始化
s_com(0xCF);
s_data(0x00);
s_data(0x81);
s_data(0x30);
s_com(0xED);
s_data(0x64);
s_data(0x03);
s_data(0x12);
s_data(0x81);
s_com(0xE8);
s_data(0x85);
s_data(0x10);
s_data(0x78);
s_com(0xCB);
s_data(0x39);
s_data(0x2C);
s_data(0x00);
s_data(0x34);
s_data(0x02);
s_com(0xF7);
s_data(0x20);
s_com(0xEA);
s_data(0x00);
s_data(0x00);
s_com(0xB1);
s_data(0x00);
s_data(0x13);
s_com(0xB6);// Display Function Control
s_data(0x0A);
s_data(0xA2);
s_com(0xC0);//Power control
s_data(0x21); //VRH
s_com(0xC1);//Power control
s_data(0x11); //SAP;BT
s_com(0xC5);//VCM control
s_data(0x3F);
s_data(0x3C);
s_com(0xC7);//VCM control2
s_data(0xa4);
s_com(0x36);// Memory Access Control
s_data(0x08);
s_com(0x3A);// Pixel Format Set
s_data(0x55);
s_com(0xF2);// 3Gamma Function Disable
s_data(0x00);
s_com(0x26);//Gamma curve selected
s_data(0x01);
s_com(0xE0);//Set Gamma
s_data(0x0F);
s_data(0x26);
s_data(0x24);
s_data(0x0B);
s_data(0x0E);
s_data(0x09);
s_data(0x54);
s_data(0xA8);
s_data(0x46);
s_data(0x0C);
s_data(0x17);
s_data(0x09);
s_data(0x0F);
s_data(0x07);
s_data(0x00);
s_com(0xE1);//Set Gamma
s_data(0x00);
s_data(0x19);
s_data(0x1B);
s_data(0x04);
s_data(0x10);
s_data(0x07);
s_data(0x2A);
s_data(0x47);
s_data(0x39);
s_data(0x03);
s_data(0x06);
s_data(0x06);
s_data(0x30);
s_data(0x38);
s_data(0x0F);
s_com(0x11); //Exit Sleep
for (i=9;i>0;i--);//延时
s_com(0x29); //Display on
s_com(0x22);
s_data(0x00);
for (i=9;i>0;i--);//延时
//--^-- ILI9341
coordinate(0,0);//窗口坐标
// 显示完图像约 4分钟
for (i=0;i<240*320*2-1;i++)
s_data(imgBuffer);
}
以上源文件放在/home/linlin/loongson/myls1c/下
2)编译
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 tft1c101.c -nostdinc -nostdlib
linlin@debian:~/loongson/myls1c$ /home/linlin/loongson/opt/gcc-4.4.7-gnu/bin/mipsel-linux-as -o tft_start.o tft_start.S
linlin@debian:~/loongson/myls1c$
3)链接
指定链接开始地址0xbfc00000
linlin@debian:~/loongson/myls1c$ /home/linlin/loongson/opt/gcc-4.4.7-gnu/bin/mipsel-linux-ld -g -Tbin.lds-o tft1c101.elftft_start.otft1c101.o -Ttext 0xbfc00000
/home/linlin/loongson/opt/gcc-4.4.7-gnu/bin/mipsel-linux-ld: warning: cannot find entry symbol _start; defaulting to 00000000bfc00000
linlin@debian:~/loongson/myls1c$
注意tft_start.o在tft1c101.o之前
4)转换为二进制格式
linlin@debian:~/loongson/myls1c$ /home/linlin/loongson/opt/gcc-4.4.7-gnu/bin/mipsel-linux-objcopy -O binary tft1c101.elf tft1c101.bin
5)烧写
root@debian:/home/linlin/loongson/myls1c# /usr/sbin/flashrom -p ch341a_spi -c W25Q64.V -w tft1c101.bin
...
Error: Image size (155664 B) doesn't match the flash chip's size (8388608 B)!
root@debian:/home/linlin/loongson/myls1c#
按烧写失败提示计算填充大小
8388608-155664=8232944
用dd命令填满到8M
root@debian:/home/linlin/loongson/myls1c# dd if=/dev/zero bs=1 count=8232944 >> tft1c101.bin
记录了8232944+0 的读入
记录了8232944+0 的写出
8232944 bytes (8.2 MB, 7.9 MiB) copied, 25.2647 s, 326 kB/s
root@debian:/home/linlin/loongson/myls1c#
重新烧录已成功
root@debian:/home/linlin/loongson/myls1c# /usr/sbin/flashrom -p ch341a_spi -c W25Q64.V -w tft1c101.bin
...
Erasing and writing flash chip... Erase/write done.
Verifying flash... VERIFIED.
root@debian:/home/linlin/loongson/myls1c#
成功输出图像
6)反汇编
见反汇编结果调用函数是j开头的跳转指令,j系列指令使用绝对地址,所以链接时必须指定地址为0xbfc00000(闪存开始运行处)
linlin@debian:~/loongson/myls1c$ /home/linlin/loongson/opt/gcc-4.4.7-gnu/bin/mipsel-linux-objdump -d tft1c101.elf |more
tft1c101.elf: file format elf32-tradlittlemips
Disassembly of section .text:
bfc00000 <_ftext>:
bfc00000: 00000000 nop
bfc00004: 3c1da000 lui sp,0xa000
bfc00008: 37bd1400 ori sp,sp,0x1400
bfc0000c: 0ff0009a jal bfc00268 <main> //跳转指令jal会自动保存返回地址到ra,这里ra值应为bfc0000c+8=bfc00014
//查找有关文档有说是该条跳转指令的地址+8,但文档常说的是PC+4,具体怎区分我不太明白?
bfc00010: 00000000 nop //延迟槽,紧跟分支跳转指令后面.跳转指令未完成,本条指令就执行了
...反汇编没见到bfc00014、bfc00018、bfc0001c,不知main返回到bfc00014会出现什么状况?
bfc00020 <s_send>: //本函数是叶子函数(没有调用其它函数),无需保存ra到栈中
bfc00020: 3c02bfeb lui v0,0xbfeb
bfc00024: 344500a5 ori a1,v0,0xa5
bfc00028: 308400ff andi a0,a0,0xff
bfc0002c: 24030008 li v1,8
bfc00030: 344200a4 ori v0,v0,0xa4
bfc00034: 24090002 li t1,2
bfc00038: 24070003 li a3,3
bfc0003c: 0bf00018 j bfc00060 <s_send+0x40> //反汇编显示绝对地址,j系列指令指定不同链接地址链接结果指令机器码是不相同的
bfc00040: 24080002 li t0,2
bfc00044: 2463ffff addiu v1,v1,-1
bfc00048: a0490000 sb t1,0(v0)
bfc0004c: 00042040 sll a0,a0,0x1
bfc00050: a0a80000 sb t0,0(a1)
bfc00054: a0a70000 sb a3,0(a1)
bfc00058: 1060000c beqz v1,bfc0008c <s_send+0x6c> //b开头的分支指令,b系列指令使用相对地址,指定不同链接地址链接结果指令机器码是相同的
bfc0005c: 308400ff andi a0,a0,0xff
bfc00060: 00043600 sll a2,a0,0x18
bfc00064: 00063603 sra a2,a2,0x18
bfc00068: 04c1fff6 bgez a2,bfc00044 <s_send+0x24> //反汇编显示绝对地址,仅方便阅读,指令机器码不含绝对地址
bfc0006c: 00000000 nop
bfc00070: 2463ffff addiu v1,v1,-1
bfc00074: a0470000 sb a3,0(v0)
bfc00078: 00042040 sll a0,a0,0x1
bfc0007c: a0a80000 sb t0,0(a1)
bfc00080: a0a70000 sb a3,0(a1)
bfc00084: 1460fff6 bnez v1,bfc00060 <s_send+0x40>
bfc00088: 308400ff andi a0,a0,0xff
bfc0008c: 03e00008 jr ra //ra为函数返回的地址
bfc00090: 00000000 nop //延迟槽,填充空操作nop指令
bfc00094 <s_com>: // C程序调用了s_send函数,汇编代码没见到跳转到<s_send>,可能编译时指定了-O2给优化了(如内联函数,好像只有-O3级别才优化内联?)
bfc00094: 3c05bfeb lui a1,0xbfeb
bfc00098: 24030002 li v1,2
bfc0009c: 34a200a5 ori v0,a1,0xa5
bfc000a0: 34a600a4 ori a2,a1,0xa4
bfc000a4: 34a500a6 ori a1,a1,0xa6
bfc000a8: a0a30000 sb v1,0(a1)
bfc000ac: a0c30000 sb v1,0(a2)
bfc000b0: a0430000 sb v1,0(v0)
bfc000b4: 24030003 li v1,3
bfc000b8: a0430000 sb v1,0(v0)
bfc000bc: 308400ff andi a0,a0,0xff
bfc000c0: 24030008 li v1,8
bfc000c4: 24090002 li t1,2
bfc000c8: 24070003 li a3,3
bfc000cc: 0bf0003d j bfc000f4 <s_com+0x60>
bfc000d0: 24080002 li t0,2
bfc000d4: 00042040 sll a0,a0,0x1
bfc000d8: 2463ffff addiu v1,v1,-1
bfc000dc: a0c90000 sb t1,0(a2)
bfc000e0: 308400ff andi a0,a0,0xff
bfc000e4: a0480000 sb t0,0(v0)
bfc000e8: a0470000 sb a3,0(v0)
bfc000ec: 1060000d beqz v1,bfc00124 <s_com+0x90>
bfc000f0: 00000000 nop
bfc000f4: 00042e00 sll a1,a0,0x18
bfc000f8: 00052e03 sra a1,a1,0x18
bfc000fc: 04a1fff5 bgez a1,bfc000d4 <s_com+0x40>
bfc00100: 00000000 nop
bfc00104: 00042040 sll a0,a0,0x1
bfc00108: 2463ffff addiu v1,v1,-1
bfc0010c: a0c70000 sb a3,0(a2)
bfc00110: 308400ff andi a0,a0,0xff
bfc00114: a0480000 sb t0,0(v0)
bfc00118: a0470000 sb a3,0(v0)
bfc0011c: 1460fff6 bnez v1,bfc000f8 <s_com+0x64>
bfc00120: 00042e00 sll a1,a0,0x18
bfc00124: 3c02bfeb lui v0,0xbfeb
bfc00128: 344200a6 ori v0,v0,0xa6
bfc0012c: 24030003 li v1,3
bfc00130: a0430000 sb v1,0(v0)
bfc00134: 03e00008 jr ra //跳转到返回地址
bfc00138: 00000000 nop
bfc0013c <s_data>:
bfc0013c: 3c03bfeb lui v1,0xbfeb
bfc00140: 346200a5 ori v0,v1,0xa5
bfc00144: 24070002 li a3,2
bfc00148: 346500a4 ori a1,v1,0xa4
bfc0014c: 24060003 li a2,3
bfc00150: 346300a6 ori v1,v1,0xa6
bfc00154: a0670000 sb a3,0(v1)
bfc00158: 308400ff andi a0,a0,0xff
bfc0015c: a0a60000 sb a2,0(a1)
bfc00160: 24030008 li v1,8
bfc00164: a0470000 sb a3,0(v0)
bfc00168: 24090002 li t1,2
bfc0016c: a0460000 sb a2,0(v0)
bfc00170: 24070003 li a3,3
bfc00174: 0bf00067 j bfc0019c <s_data+0x60>
bfc00178: 24080002 li t0,2
bfc0017c: 00042040 sll a0,a0,0x1
bfc00180: 2463ffff addiu v1,v1,-1
bfc00184: a0a90000 sb t1,0(a1)
bfc00188: 308400ff andi a0,a0,0xff
bfc0018c: a0480000 sb t0,0(v0)
bfc00190: a0470000 sb a3,0(v0)
bfc00194: 1060000d beqz v1,bfc001cc <s_data+0x90>
bfc00198: 00000000 nop
bfc0019c: 00043600 sll a2,a0,0x18
bfc001a0: 00063603 sra a2,a2,0x18
bfc001a4: 04c1fff5 bgez a2,bfc0017c <s_data+0x40>
bfc001a8: 00000000 nop
bfc001ac: 00042040 sll a0,a0,0x1
bfc001b0: 2463ffff addiu v1,v1,-1
bfc001b4: a0a70000 sb a3,0(a1)
bfc001b8: 308400ff andi a0,a0,0xff
bfc001bc: a0480000 sb t0,0(v0)
bfc001c0: a0470000 sb a3,0(v0)
bfc001c4: 1460fff6 bnez v1,bfc001a0 <s_data+0x64>
bfc001c8: 00043600 sll a2,a0,0x18
bfc001cc: 3c02bfeb lui v0,0xbfeb
bfc001d0: 344200a6 ori v0,v0,0xa6
bfc001d4: 24030003 li v1,3
bfc001d8: a0430000 sb v1,0(v0)
bfc001dc: 03e00008 jr ra
bfc001e0: 00000000 nop
//MIPS约定:a0、a1等为入参,s0、s1等为变量(函数必需先保存和返回之前恢复它们),t0、t1等为临时变量(函数无需保存和恢复)
bfc001e4 <coordinate>://本函数是非叶子函数(调用了其它函数),需保存ra到栈中
bfc001e4: 27bdffe0 addiu sp,sp,-32//入栈,栈是从高地址往低地址增长了32(MIPS C语言规范),加操作指令,负数就是减小地址
bfc001e8: afb00014 sw s0,20(sp) //保存寄存器的值到栈,<coordinate>里共只保存3个寄存器,3*4=12个字节
bfc001ec: 00808021 move s0,a0 //传入到coordinate(col,page)的入参a0先存放到变量s0(因为见注7会先用到a0),s0见注8会用到
bfc001f0: 2404002a li a0,42 //42=0x2a => a0,见注7会用到
bfc001f4: afbf001c sw ra,28(sp) //保存ra(返回地址)到栈中,相对sp偏移28(MIPS C语言规范),-32+28=-4
bfc001f8: afb10018 sw s1,24(sp)
bfc001fc: 0ff00025 jal bfc00094 <s_com>//注7:对应C代码s_com(0x2a)
bfc00200: 00a08821 move s1,a1 //入参a1 => 变量s1 ,为何不用t1 ?因为t1可能会被下面子函数用到而改变,而按规范s1会被子函数返回之前恢复
bfc00204: 0ff0004f jal bfc0013c <s_data>
bfc00208: 00002021 move a0,zero
//--v--
bfc0020c: 0ff0004f jal bfc0013c <s_data> //----->对应C代码s_data(col)
bfc00210: 320400ff andi a0,s0,0xff //---/注8:即s0 => a0 => col (unsigned char) ,a0传参给<s_data>
//--^-- 因延迟槽,不方便顺序阅读理解,延迟槽改为放置nop空操作调整次序就好理解多了 [ andi a0,s0,0xff ] --> [ jal bfc0013c <s_data> ] --> [ nop ]
bfc00214: 0ff0004f jal bfc0013c <s_data>
bfc00218: 00002021 move a0,zero
bfc0021c: 0ff0004f jal bfc0013c <s_data>
bfc00220: 240400ef li a0,239
bfc00224: 0ff00025 jal bfc00094 <s_com>
bfc00228: 2404002b li a0,43
bfc0022c: 3224ff00 andi a0,s1,0xff00
bfc00230: 0ff0004f jal bfc0013c <s_data>
bfc00234: 00042202 srl a0,a0,0x8
bfc00238: 0ff0004f jal bfc0013c <s_data>
bfc0023c: 322400ff andi a0,s1,0xff
bfc00240: 0ff0004f jal bfc0013c <s_data>
bfc00244: 24040001 li a0,1
bfc00248: 0ff0004f jal bfc0013c <s_data> //好像只有jal、jalr才会同时自动保存返回地址到ra
bfc0024c: 2404003f li a0,63
//--v--
bfc00250: 8fbf001c lw ra,28(sp) //因上面调用了<s_data>等,ra值被改变,所以需从栈中恢复返回地址到ra
bfc00254: 8fb10018 lw s1,24(sp) //恢复
bfc00258: 8fb00014 lw s0,20(sp)
bfc0025c: 2404002c li a0,44 //44=0x2c,<s_com>入参
bfc00260: 0bf00025 j bfc00094 <s_com> //指令j、jr应不会自动保存返回地址到ra,不会改变ra值
//--^--
//C代码<coordinate>函数里最后一行是s_com(0x2c),汇编代码<coordinate>里最后不见用jr ra 跳转到返回地址,应该是优化了
//如果不优化的话,应该是 jal <s_com> --> lw ra,28(sp) --> jr ra
//而优化结果是 lw ra,28(sp) --> j <s_com> ,由<s_com>里最后返回到ra
bfc00264: 27bd0020 addiu sp,sp,32 //出栈
bfc00268 <main>:
bfc00268: 3c06bfeb lui a2,0xbfeb
bfc0026c: 3c07fbff lui a3,0xfbff
bfc00270: 27bdffe0 addiu sp,sp,-32
bfc00274: 34c200a3 ori v0,a2,0xa3
bfc00278: 24050002 li a1,2
bfc0027c: 24030003 li v1,3
bfc00280: 34c60030 ori a2,a2,0x30
bfc00284: 34e70400 ori a3,a3,0x400
bfc00288: afbf001c sw ra,28(sp)
bfc0028c: acc70000 sw a3,0(a2)
bfc00290: afb10018 sw s1,24(sp)
bfc00294: afb00014 sw s0,20(sp)
bfc00298: 240400cf li a0,207
bfc0029c: a0450000 sb a1,0(v0)
bfc002a0: a0450000 sb a1,0(v0)
bfc002a4: a0430000 sb v1,0(v0)
bfc002a8: a0430000 sb v1,0(v0)
bfc002ac: 0ff00025 jal bfc00094 <s_com>
bfc002b0: 3c10bfc0 lui s0,0xbfc0 //立即数加载到高位,存放在闪存的图片开始位置(bfc0XXXX)
bfc002b4: 0ff0004f jal bfc0013c <s_data>
bfc002b8: 00002021 move a0,zero
bfc002bc: 0ff0004f jal bfc0013c <s_data>
bfc002c0: 24040081 li a0,129
bfc002c4: 0ff0004f jal bfc0013c <s_data>
bfc002c8: 24040030 li a0,48
bfc002cc: 0ff00025 jal bfc00094 <s_com>
bfc002d0: 240400ed li a0,237 //237=0xED,C代码的s_com(0xED)语句,函数调用的参数
...略
bfc0037c: 0ff0004f jal bfc0013c <s_data>
bfc00380: 24040013 li a0,19 //这里的0xbfc00380本应是中断入口,本程序没用中断,没问题
bfc00384: 0ff00025 jal bfc00094 <s_com>
bfc00388: 240400b6 li a0,182
...
bfc0051c: 0ff0004f jal bfc0013c <s_data>
bfc00520: 2404000f li a0,15
bfc00524: 0ff00025 jal bfc00094 <s_com> // C代码for (i=9;i>0;i--)语句给优化没了
bfc00528: 24040011 li a0,17
bfc0052c: 0ff00025 jal bfc00094 <s_com>
bfc00530: 24040029 li a0,41
bfc00534: 0ff00025 jal bfc00094 <s_com>
bfc00538: 24040022 li a0,34
bfc0053c: 0ff0004f jal bfc0013c <s_data>
bfc00540: 00002021 move a0,zero //延迟槽,应该对应C代码s_data(0x00)
bfc00544: 00002021 move a0,zero //两个入参a0、a1,对应C代码coordinate(0,0)语句
bfc00548: 00002821 move a1,zero
bfc0054c: 0ff00079 jal bfc001e4 <coordinate> //自动填入返回地址到ra=bfc0054c+8=bfc00554
bfc00550: 3c11bfc2 lui s1,0xbfc2 //延迟槽,立即数加载到高位,图片存放在闪存的位置(bfc0XXXX~bfc2XXXX),C代码的i变量给优化为图片地址
bfc00554: 26100580 addiu s0,s0,1408 // 1408=0x0580,s0=0xbfc00580
bfc00558: 26315d7f addiu s1,s1,23935 //23935=0x5D7F,s1=0xBFC25D7F=0xBFC25D80-1,图片的最后地址
//--v--对应C代码for (i=0;i<240*320*2-1;i++)
bfc0055c: 92040000 lbu a0,0(s0)
bfc00560: 0ff0004f jal bfc0013c <s_data>
bfc00564: 26100001 addiu s0,s0,1 //增加1
bfc00568: 1611fffc bne s0,s1,bfc0055c <main+0x2f4>//比较0xBFC25D80-1的值,变量i给优化
//--^--
bfc0056c: 8fbf001c lw ra,28(sp) //从栈中恢复返回地址到ra
bfc00570: 8fb10018 lw s1,24(sp)
bfc00574: 8fb00014 lw s0,20(sp)
bfc00578: 03e00008 jr ra //跳转到返回地址
bfc0057c: 27bd0020 addiu sp,sp,32
bfc00580 <imgBuffer>: //图片数据 0xbfc00580+153600=0xBFC25D80
bfc00580: c418c318 c318c418 c318c318 a310a318 ................
bfc00590: a218a310 44290321 064aa539 e86aa862 ....!.)D9.J.b.j.
bfc005a0: 0873c86a 09730973 49732973 6a736973 j.s.s.s.s)sIsisj
...略
bfc25d40: 38c659ce 79ce38c6 bad6bad6 fbdedad6 .Y.8.8.y........
bfc25d50: fbdebad6 9ad6bad6 fcdedbd6 bbd6dbd6 ................
bfc25d60: dbdebbd6 dbdedbde bbd6dbde 9ad6bbd6 ................
bfc25d70: bbd6bbd6 9ace9ace 9ace9ad6 bbd69ad6 ................
bfc25d80: a0000000 00000000 00000000 00000000 ................
...
linlin@debian:~/loongson/myls1c$
7)小结
通过反汇编,可见C编译器通过栈保证了MIPS规范要求的ra、s0~s7进入子函数保存和恢复.如果手工写汇编不按规范,可能导致不可预料的后果
页:
[1]
2