龙芯俱乐部开源技术社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
热搜: 活动 交友 discuz
查看: 11603|回复: 10

龙芯LS1C101单片机实验板

[复制链接]

8

主题

28

帖子

1116

积分

金牌会员

Rank: 6Rank: 6

积分
1116
发表于 2020-8-6 08:34:15 | 显示全部楼层 |阅读模式
本人从淘宝购买了龙芯单片机实验板,非白菜板,真正的单片机.借此白菜板宝地.购买链接就不给出了,有意者请到淘宝搜索龙芯LS1C101

LS1C101单片机实验板,店面的介绍:
  1颗龙芯1C101,引脚是QFP64
  1颗板载SPI闪存W25Q64,容量是8MB
  8个红色LED灯,用于显示引脚电平,进行GPIO相实验测试

为烧录,还从店主购入配套的USB烧录编程器,店面介绍是使用ch341a芯片

本人第一次接触单片机,下文有错误的地方,请指正.

实验板正面,板上数字是GPIO编号

左侧排针                右侧排针
--------------------------------------------
GND  3V                 CA   R?(连上14)
B0   12                 R?   R?
B1   13                 3V   RA
09   08                 GND  RB
07   06                 14   15
A0   05      REST       16   3V

。。。
49   N49
50   N50
51   N51


              32 (8个LED灯,代表gpio32~gpio39)
              33
              ...
              39

        -------------------
        | 一组SPI引脚排针 |
        -------------------

8

主题

28

帖子

1116

积分

金牌会员

Rank: 6Rank: 6

积分
1116
 楼主| 发表于 2020-8-6 08:49:34 | 显示全部楼层
到手的实验板:
  49-N49用跳帽短接,50-N50用跳帽短接,14和CA同一行的'R?'用杜邦线连上.我也不懂有什么作用.
  3V-RA跳帽短接,GND-RB跳帽短接.
  已烧录了程序,应是烧录在外部板载闪存,而不是在LS1C101 CPU片上闪存.
  SPI排针已插上烧录编程器

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

8

主题

28

帖子

1116

积分

金牌会员

Rank: 6Rank: 6

积分
1116
 楼主| 发表于 2020-8-6 09:08:03 | 显示全部楼层

初步使用:
  将烧录器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           8192  SPI      
Winbond       W25Q64.W               PREW           8192  SPI      
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 -EL  ls1c_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输出电平寄存器地址


0

主题

31

帖子

7089

积分

论坛元老

Rank: 8Rank: 8

积分
7089
发表于 2020-8-6 11:33:11 | 显示全部楼层
本帖最后由 VG2018 于 2020-8-6 11:38 编辑

谢谢分享。

8

主题

28

帖子

1116

积分

金牌会员

Rank: 6Rank: 6

积分
1116
 楼主| 发表于 2020-8-6 18:57:36 | 显示全部楼层
淘宝链接我给出
https://item.taobao.com/item.htm ... amp;id=610067293029
店主铺面
https://csee.taobao.com

广告嫌疑,呵呵

46

主题

115

帖子

4146

积分

论坛元老

Rank: 8Rank: 8

积分
4146
发表于 2020-8-6 19:46:37 | 显示全部楼层
那個1c300b的有沒有人試過

84

主题

465

帖子

44万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
440441
发表于 2020-8-10 17:43:30 | 显示全部楼层
不错 你用的是什么ide 容易学习吗?

8

主题

28

帖子

1116

积分

金牌会员

Rank: 6Rank: 6

积分
1116
 楼主| 发表于 2020-8-24 11:18:10 | 显示全部楼层
没用到IDE,店主有提供自己开发的IDE,但可能不适合程序员,应该是适合不需编程的电气工程师吧.
下面继续我的文章:裸机编程

8

主题

28

帖子

1116

积分

金牌会员

Rank: 6Rank: 6

积分
1116
 楼主| 发表于 2020-8-24 11:34:25 | 显示全部楼层
本帖最后由 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.o  led1c101.S

链接
linlin@debian:~/loongson/myls1c$ /home/linlin/loongson/opt/gcc-4.4.7-gnu/bin/mipsel-linux-ld -g -T  bin.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.bin  led1c101.elf  led1c101.o  led1c101.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复位值是灭的,所以过程应如下:

-->启动-->灯灭-->延时-->点亮-->程序结束-->保持灯亮-->看门狗复位---|
|                                                                 |
|-----------------------------------------------------------------|  



8

主题

28

帖子

1116

积分

金牌会员

Rank: 6Rank: 6

积分
1116
 楼主| 发表于 2020-11-10 13:32:49 | 显示全部楼层
本帖最后由 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[153600]={   // 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_RST  0xbfeb00A3   //gpio35
#define    SPI_SDA  0xbfeb00A4   //gpio36
#define    SPI_SCL  0xbfeb00A5   //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[5:0]
  s_com(0xC1);  //Power control
  s_data(0x11); //SAP[2:0];BT[3:0]
  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 -T  bin.lds  -o tft1c101.elf  tft_start.o  tft1c101.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进入子函数保存和恢复.如果手工写汇编不按规范,可能导致不可预料的后果
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|龙芯俱乐部开源技术社区

GMT+8, 2024-3-28 19:40 , Processed in 0.107781 second(s), 22 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表