龙芯LS1C101单片机实验(1)--UART

龙芯LS1C101单片机实验板在某宝搜索'龙芯LS1C101'可搜到,配套的USB烧录编程器使用ch341a芯片

本文实验UART串口通信,只实现单片机发送

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

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

安装烧录工具flashrom
root@debian:/# apt-get install flashrom

安装串口测试工具cutecom
root@debian:/# apt-get install cutecom

实验板要与PC机通过串口通信,还需准备usb转ttl

二.引脚功能
龙芯LS1C101有3个UART,UART0和UART1使用总线接口时钟,UART2使用32K时钟

引脚复用

 引脚     主功能     描述
-----------------------------------------
GPIO06  UART0_RX   串口0数据输入
GPIO07  UART0_TX   串口0数据输出
GPIO08  UART1_RX   串口1数据输入
GPIO09  UART1_TX   串口1数据输出
GPIO10  UART2_RX   串口2数据输入
GPIO11  UART2_TX   串口2数据输出

实验板GPIO10、GPIO11没引出,但有引出GPIO38、GPIO39(第一复用分别对应UART2_RX、UART2_TX)
也有其它GPIO引脚第一复用对应UART0和UART1,这里就不列了

本实验使用UART1的gpio8、gpio9

三.
1.源代码
代码文件为uart1c101.c,放在/home/linlin/loongson/myls1c/下

#define watchdog 0xbfeb0030 //看门狗

//UART1寄存器地址,其中三对寄存器DAT/DL_L、IER/DL_H、FCR/DL_D每对地址相同
#define UART_SAMPLE_CTRL 0xbfe88004  //bit窗口划分和采样控制寄存器
#define UART_LCR         0xbfe88003  //线路控制寄存器
#define UART_DL_D        0xbfe88002  //分频值小数寄存器
#define UART_DL_H        0xbfe88001  //分频值高字节寄存器
#define UART_DL_L        0xbfe88000  //分频值低字节寄存器
#define UART_FCR         0xbfe88002  //FIFO控制寄存器
#define UART_LSR         0xbfe88005  //线路状态寄存器
#define UART_STATUS      0xbfe88007  //状态寄存器寄存器(手册文档原文如此)
#define UART_IER         0xbfe88001  //中断使能寄存器
#define UART_TxData      0xbfe88000  //数据寄存器(DAT)

//引脚复用,每两位对应一个gpio,即16个gpio一组;gpio8、gpio9落在IOSEL0,位值1为主功能
#define IOSEL0   0xbfeb0010 //UART1 gpio8,gpio9

#define GPIO8    8   // UART1_RX
#define GPIO9    9   // UART1_TX
#define FUNCMAIN 1   // 引脚复用的主功能

void main (void)
{
/*
    设置看门狗复位等待时间长为0x400秒,足方便实验
    1111101111111111  高16位的值FBFF,低16位的反
    0000010000000000  低16位的值0400,其中第15位(同下文第n位以第0位算起)置为0,满足奇校验(与UART无关)
*/
    *(volatile unsigned int*)(watchdog)=0xFBFF0400 ;

    // uart1主功能也必须设复用才能使用(本人刚开始以为'主'功能是默认不用设置,好像默认的是gpio功能)
    *(volatile unsigned int*)(IOSEL0) &= ~(   0x03<<((GPIO8%16)*2));
    *(volatile unsigned int*)(IOSEL0) |= (FUNCMAIN<<((GPIO8%16)*2));
    *(volatile unsigned int*)(IOSEL0) &= ~(   0x03<<((GPIO9%16)*2));
    *(volatile unsigned int*)(IOSEL0) |= (FUNCMAIN<<((GPIO9%16)*2));

    *(volatile unsigned char*)(UART_SAMPLE_CTRL)=0x80 ; // 0x80=10000000,采样窗口长度16份,采样点位置8
    *(volatile unsigned char*)(UART_LCR)=0x80 ;         // 置LCR的第7位dlab(分频模式)为1,访问分频值寄存器(UART_DL_x系列)

    //--v-- 以假定内部8M时钟频率,设置波特率9600,见注1 
    *(volatile unsigned char*)(UART_DL_D)=0x15 ; // 16进制小数部分
    *(volatile unsigned char*)(UART_DL_H)=0x0;   // 高字节
    *(volatile unsigned char*)(UART_DL_L)=52 ;   // 低字节

    //--^--

    *(volatile unsigned char*)(UART_LCR)=0x2F ;  // 0x2F=00101111;奇校验,2停止位,8个数据位;置LCR的dlab位为0,访问正常寄存器(DAT、IER、FCR等)       
    *(volatile unsigned char*)(UART_FCR)=0x86 ;  // 0x86=10000110;第1位复位接收FIFO,第2位复位发送FIFO,位域[7:3]=0x10=10000表示接收trigger值为16字节

    while( ( *(volatile unsigned char*)(UART_STATUS) ) & 0x40); // 查询复位结束,0x40=1000000,第6位
    *(volatile unsigned char*)(UART_IER) = 0;    // 默认关闭所有UART中断

    //以上应是仅串口初始化

    int i;
    while(1)
      for ( i=0;i<256;i++) // 发送是以字节为单位,所以测试一个字节范围内的数据0~255
      {   
        while (!( ( *(volatile unsigned char*)(UART_LSR) ) & 0x20)); // 发送FIFO空,0x20=100000,第5位
        *(volatile unsigned char*)(UART_TxData) = i;                 // 发送数据 
      }   
}

注1:
三对寄存器DAT/DL_L、IER/DL_H、FCR/DL_D,不明白是每对寄存器同一个,还是仅是地址相同、寄存器不同?手册也没详细说明,但用法必须先置LCR寄存器的dlab位
时钟频率8M=8000000,波特率要设为9600
配置分频值参数的计算公式:
分频值=时钟频率/采样窗口长度/波特率=8M/16份/9600波特率=52.0833333333
所以十进制52.0833333333整数部分是52,小数部分0.0833333333,难点是要将十进制小数转换为十六进制小数
转换如下:

0.0833333333*16=1.33333333  取整数部分0x1 ,继续转换小数部分
0.33333333*16  =5.33333333  取整数部分0x5 ,不再转换,足够精度

最终取0x15放置到DL_D寄存器,52放置到DL_L寄存器

外部时钟是固定的8000000=8M;是不是外部时钟需实验板放置晶振才行?
内部时钟需读取0xbf0201b0寄存器真实的值=( *(volatile unsigned int *)(0xbf0201b0) )*1000;手册文档说是片内8M工作时钟,应该接近8M吧
本实验用内部时钟,但为简单,本实验不读取0xbf0201b0寄存器,姑且当内部时钟为8M,PC机端调整波特率就可  

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 uart1c101.c -nostdinc -nostdlib

生成目标文件uart1c101.o

3.链接
指定链接开始地址0xbfc00000
linlin@debian:~/loongson/myls1c$ /home/linlin/loongson/opt/gcc-4.4.7-gnu/bin/mipsel-linux-ld -g -T bin.lds -o uart1c101.elf uart1c101.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

4.转换为二进制格式
linlin@debian:~/loongson/myls1c$ /home/linlin/loongson/opt/gcc-4.4.7-gnu/bin/mipsel-linux-objcopy -O binary uart1c101.elf uart1c101.bin

5.烧写
本实验没用到单片机片上闪存,烧写的是片外板载SPI闪存W25Q64,容量是8MB
先按住实验板复位键不放,然后运行flashrom命令,一直到命令完成才能松开复位键

linlin@debian:~/loongson/myls1c$ su
密码:

root@debian:/home/linlin/loongson/myls1c# /usr/sbin/flashrom -p ch341a_spi -c W25Q64.V -w uart1c101.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#

烧写失败,因为uart1c101.bin大小与闪存不一致
见提示Image size其uart1c101.bin为4112 B,而闪存为8388608 B(8M)

uart1c101.bin需用dd命令填满到8M
计算填充大小
8388608-4112=8384496

root@debian:/home/linlin/loongson/myls1c# dd if=/dev/zero bs=1 count=8384496 >> uart1c101.bin
记录了8384496+0 的读入
记录了8384496+0 的写出
8384496 bytes (8.4 MB, 8.0 MiB) copied, 26.0035 s, 322 kB/s

uart1c101.bin大小已8M
再次烧录已成功
root@debian:/home/linlin/loongson/myls1c# /usr/sbin/flashrom -p ch341a_spi -c W25Q64.V -w uart1c101.bin
...
Reading old flash chip contents... done.
Erasing and writing flash chip... Erase/write done.
Verifying flash... VERIFIED.
root@debian:/home/linlin/loongson/myls1c#

6.测试
PC机在root下运行cutecom,图形界面常用波特率的设置可从下拉框选择,也可直接在波特率文本框直接填写.因为要调试,要设置的波特率不是常用,在cutecom界面就直接填写.
PC机插上usb ttl,连接实验板的GPIO8、GPIO9、地.

本单片机程序是假定内部时钟为8M的值来设置波特率9600,实际的内部时钟频率有所出入,所以PC机端需调整接收的波特率.
PC机端先设波特率9600,接收数据有误码.不断尝试调整PC机端波特率,到了9300时,接收的数据已完全正确.

下表是cutecom接收的正常数据

00000000 00 01 02 03 04 05 06 07   08 09 0a 0b 0c 0d 0e 0f    
00000016 10 11 12 13 14 15 16 17   18 19 1a 1b 1c 1d 1e 1f    
00000032 20 21 22 23 24 25 26 27   28 29 2a 2b 2c 2d 2e 2f      !"#$%&'  ()*+,-./
00000048 30 31 32 33 34 35 36 37   38 39 3a 3b 3c 3d 3e 3f     01234567  89:;<=>?
00000064 40 41 42 43 44 45 46 47   48 49 4a 4b 4c 4d 4e 4f     @ABCDEFG  HIJKLMNO
00000080 50 51 52 53 54 55 56 57   58 59 5a 5b 5c 5d 5e 5f     PQRSTUVW  XYZ[\]^_
00000096 60 61 62 63 64 65 66 67   e8 69 6a 6b 6c 6d 6e 6f     `abcdefg  .ijklmno
00000112 70 71 72 73 74 75 76 77   78 79 7a 7b 7c 7d 7e 7f     pqrstuvw  xyz{|}~.
00000128 80 81 82 83 84 85 86 87   88 89 8a 8b 8c 8d 8e 8f     ........  ........
00000144 90 91 92 93 94 95 96 97   98 99 9a 9b 9c 9d 9e 9f     ........  ........
00000160 a0 a1 a2 a3 a4 a5 a6 a7   a8 a9 aa ab ac ad ae af     ........  ........
00000176 b0 b1 b2 b3 b4 b5 b6 b7   b8 b9 ba bb bc bd be bf     ........  ........
00000192 c0 c1 c2 c3 c4 c5 c6 c7   c8 c9 ca cb cc cd ce cf     ........  ........
00000208 d0 d1 d2 d3 d4 d5 d6 d7   d8 d9 da db dc dd de df     ........  ........
00000224 e0 e1 e2 e3 e4 e5 e6 e7   e8 e9 ea eb ec ed ee ef     ........  ........
00000240 f0 f1 f2 f3 f4 f5 f6 f7   f8 f9 fa fb fc fd fe ff     ........  ........
00000256 00 01 02 03 04 05 06 07   08 09 0a 0b 0c 0d 0e 0f    
00000272 10 11 12 13 14 15 16 17   18 19 1a 1b 1c 1d 1e 1f    
00000288 20 21 22 23 24 25 26 27   28 29 2a 2b 2c 2d 2e 2f      !"#$%&'  ()*+,-./
00000304 30 31 32 33 34 35 36 37   38 39 3a 3b 3c 3d 3e 3f     01234567  89:;<=>?
00000320 40 41 42 43 44 45 46 47   48 49 4a 4b 4c 4d 4e 4f     @ABCDEFG  HIJKLMNO
00000336 50 51 52 53 54 55 56 57   58 59 5a 5b 5c 5d 5e 5f     PQRSTUVW  XYZ[\]^_
00000352 60 61 62 63 64 65 66 67   68 69 6a 6b 6c 6d 6e 6f     `abcdefg  hijklmno
00000368 70 71 72 73 74 75 76 77   78 79 7a 7b 7c 7d 7e 7f     pqrstuvw  xyz{|}~.
00000384 80 81 82 83 84 85 86 87   88 89 8a 8b 8c 8d 8e 8f     ........  ........
00000400 90 91 92 93 94 95 96 97   98 99 9a 9b 9c 9d 9e 9f     ........  ........
00000416 a0 a1 a2 a3 a4 a5 a6 a7   a8 a9 aa ab ac ad ae af     ........  ........
00000432 b0 b1 b2 b3 b4 b5 b6 b7   b8 b9 ba bb bc bd be bf     ........  ........
00000448 c0 c1 c2 c3 c4 c5 c6 c7   c8 c9 ca cb cc cd ce cf     ........  ........
00000464 d0 d1 d2 d3 d4 d5 d6 d7   d8 d9 da db dc dd de df     ........  ........
00000480 e0 e1 e2 e3 e4 e5 e6 e7   e8 e9 ea eb ec ed ee ef     ........  ........
00000496 f0 f1 f2 f3 f4 f5 f6 f7   f8 f9 fa fb fc fd fe ff     ........  ........
00000512 00 01 02 03 04 05 06 07   08 09 0a 0b 0c 0d 0e 0f    

7.反汇编
linlin@debian:~/loongson/myls1c$ /home/linlin/loongson/opt/gcc-4.4.7-gnu/bin/mipsel-linux-objdump -d uart1c101.elf

uart1c101.elf:     file format elf32-tradlittlemips

Disassembly of section .text:

bfc00000 <main>:
bfc00000:    3c02bfeb     lui    v0,0xbfeb
bfc00004:    3c04fbff     lui    a0,0xfbff
bfc00008:    34430030     ori    v1,v0,0x30
bfc0000c:    34840400     ori    a0,a0,0x400
bfc00010:    ac640000     sw    a0,0(v1)
bfc00014:    34420010     ori    v0,v0,0x10
bfc00018:    8c440000     lw    a0,0(v0)
bfc0001c:    3c03fffc     lui    v1,0xfffc
bfc00020:    3463ffff     ori    v1,v1,0xffff
bfc00024:    00831824     and    v1,a0,v1
bfc00028:    ac430000     sw    v1,0(v0)
bfc0002c:    8c440000     lw    a0,0(v0)
bfc00030:    3c030001     lui    v1,0x1
bfc00034:    00831825     or    v1,a0,v1
bfc00038:    ac430000     sw    v1,0(v0)
bfc0003c:    8c440000     lw    a0,0(v0)
bfc00040:    3c03fff3     lui    v1,0xfff3
bfc00044:    3463ffff     ori    v1,v1,0xffff
bfc00048:    00831824     and    v1,a0,v1
bfc0004c:    ac430000     sw    v1,0(v0)
bfc00050:    8c4a0000     lw    t2,0(v0)
bfc00054:    3c040004     lui    a0,0x4
bfc00058:    3c03bfe8     lui    v1,0xbfe8
bfc0005c:    01445025     or    t2,t2,a0
bfc00060:    ac4a0000     sw    t2,0(v0)
bfc00064:    34658003     ori    a1,v1,0x8003
bfc00068:    34648002     ori    a0,v1,0x8002
bfc0006c:    2406ff80     li    a2,-128
bfc00070:    34698004     ori    t1,v1,0x8004
bfc00074:    24020015     li    v0,21
bfc00078:    a1260000     sb    a2,0(t1)
bfc0007c:    34688001     ori    t0,v1,0x8001
bfc00080:    a0a60000     sb    a2,0(a1)
bfc00084:    34678000     ori    a3,v1,0x8000
bfc00088:    a0820000     sb    v0,0(a0)
bfc0008c:    24020034     li    v0,52
bfc00090:    a1000000     sb    zero,0(t0)
bfc00094:    a0e20000     sb    v0,0(a3)
bfc00098:    2402002f     li    v0,47
bfc0009c:    a0a20000     sb    v0,0(a1)
bfc000a0:    2402ff86     li    v0,-122
bfc000a4:    a0820000     sb    v0,0(a0)
bfc000a8:    34638007     ori    v1,v1,0x8007
bfc000ac:    90640000     lbu    a0,0(v1)
bfc000b0:    30840040     andi    a0,a0,0x40
bfc000b4:    1480fffd     bnez    a0,bfc000ac <main+0xac> //b开头的分支指令,b系列指令使用相对地址,指定不同链接地址链接结果指令机器码是相同的
bfc000b8:    3c02bfe8     lui    v0,0xbfe8
bfc000bc:    34438001     ori    v1,v0,0x8001
bfc000c0:    a0600000     sb    zero,0(v1)
bfc000c4:    34468000     ori    a2,v0,0x8000
bfc000c8:    34438005     ori    v1,v0,0x8005
//--v-- 对应C代码while循环语句
bfc000cc:    90620000     lbu    v0,0(v1)
bfc000d0:    30420020     andi    v0,v0,0x20
bfc000d4:    1040fffd     beqz    v0,bfc000cc <main+0xcc>    //反汇编语句(bfc000cc)显示绝对地址,仅方便阅读,指令机器码(1040fffd)不含绝对地址
bfc000d8:    308500ff     andi    a1,a0,0xff
bfc000dc:    24840001     addiu    a0,a0,1
bfc000e0:    28820100     slti    v0,a0,256
bfc000e4:    a0c50000     sb    a1,0(a2)
bfc000e8:    0bf00033     j    bfc000cc <main+0xcc>          //指令机器码(0bf00033),while(1)死循环编译成直接跳转指令,j系列指令使用绝对地址
bfc000ec:    0002200a     movz    a0,zero,v0                 //延迟槽
//--^--
bfc000f0:    000007fc     0x7fc
bfc000f4:    00000000     nop
bfc000f8:    00000431     tgeu    zero,zero,0x10
    ...

linlin@debian:~/loongson/myls1c$

四.调试
1.反汇编uart1c101.o目标文件

linlin@debian:~/loongson/myls1c$ /home/linlin/loongson/opt/gcc-4.4.7-gnu/bin/mipsel-linux-objdump -d uart1c101.o

uart1c101.o:     file format elf32-tradlittlemips

Disassembly of section .text:

00000000 <main>:
   0:    3c02bfeb     lui    v0,0xbfeb
   4:    3c04fbff     lui    a0,0xfbff
...略(同bfc00000 <main>)
  b0:    30840040     andi    a0,a0,0x40
  b4:    1480fffd     bnez    a0,ac <main+0xac>
  b8:    3c02bfe8     lui    v0,0xbfe8
...略(同bfc00000 <main>)
  d0:    30420020     andi    v0,v0,0x20
  d4:    1040fffd     beqz    v0,cc <main+0xcc> //这条指令机器码(1040fffd)与上(bfc00000 <main>)是相同的
  d8:    308500ff     andi    a1,a0,0xff
  dc:    24840001     addiu    a0,a0,1
  e0:    28820100     slti    v0,a0,256
  e4:    a0c50000     sb    a1,0(a2)
  e8:    08000033     j    cc <main+0xcc>       //这条指令机器码(08000033)与上不同,应该是j跳转指令最终的地址要到链接才确定
  ec:    0002200a     movz    a0,zero,v0

linlin@debian:~/loongson/myls1c$

2.命令ld不指定链接开始地址
可见是默认链接到80200000,查看链接脚本是预设为80200000
linlin@debian:~/loongson/myls1c$ /home/linlin/loongson/opt/gcc-4.4.7-gnu/bin/mipsel-linux-ld -g -T bin.lds -o uart1c101.elf uart1c101.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$ cat bin.lds

OUTPUT_ARCH(mips)
ENTRY(_start)
SECTIONS
{
  /* Read-only sections, merged into text segment: */
  . = 0x80200000;
  .text      :
  {
...略

linlin@debian:~/loongson/myls1c$

反汇编(按80200000链接地址)
linlin@debian:~/loongson/myls1c$ /home/linlin/loongson/opt/gcc-4.4.7-gnu/bin/mipsel-linux-objdump -d uart1c101.elf

uart1c101.elf:     file format elf32-tradlittlemips

Disassembly of section .text:

80200000 <main>:
80200000:    3c02bfeb     lui    v0,0xbfeb
80200004:    3c04fbff     lui    a0,0xfbff
...略(同bfc00000 <main>)
802000b0:    30840040     andi    a0,a0,0x40
802000b4:    1480fffd     bnez    a0,802000ac <main+0xac>
802000b8:    3c02bfe8     lui    v0,0xbfe8
...略(同bfc00000 <main>)
802000d0:    30420020     andi    v0,v0,0x20
802000d4:    1040fffd     beqz    v0,802000cc <main+0xcc> //这条指令机器码(1040fffd)与上(bfc00000 <main>)是相同的,虽然反汇编语句(802000cc)与上不同,但仅为方便阅读
802000d8:    308500ff     andi    a1,a0,0xff
802000dc:    24840001     addiu    a0,a0,1
802000e0:    28820100     slti    v0,a0,256
802000e4:    a0c50000     sb    a1,0(a2)
802000e8:    08080033     j    802000cc <main+0xcc>       //这条指令机器码(08080033)与上不同
802000ec:    0002200a     movz    a0,zero,v0
802000f0:    000007fc     0x7fc
802000f4:    00000000     nop
802000f8:    00000431     tgeu    zero,zero,0x10
    ...

linlin@debian:~/loongson/myls1c$
因为编译出的汇编含j跳转指令,使用绝对地址,所以当链接到80200000时,烧写到闪存以bfc00000开始处运行,实验观察PC机的usb ttl接收灯只闪亮一次,说明单片机只发了一个数据,就跳到绝对地址802000cc,程序便跑飞了

假如编译出的目标文件不含j跳转指令,无论链接到80200000还是bfc00000,都不含绝对地址,烧写到闪存以bfc00000开始处运行都应正常

说明:
龙芯CPU兼容mips
上述编译、链接后的绝对地址在mips是指程序空间虚地址,程序空间的kseg0和kseg1这两段是映射到相同的物理空间,区别仅是否cache(单片机没cache)
链接地址指定为9FC00000与指定为bfc00000映射到物理空间的地址(都是1fc00000)一致的,烧写到闪存运行应该正常(我没验证),链接后的j指令机器码应该不同(同样没验证)
指定为80200000之所以程序跑飞,那是因为80200000映射到物理地址与bfc00000不一样
有关mips地址空间布局资料详尽,这里不再阐述

五.参考资料
龙芯1C101 用户手册 http://www.loongson.cn/uploadfile/cpu/1C101/Loongson_1C101_user.pdf
龙芯1C101 数据手册 http://www.loongson.cn/uploadfile/cpu/1C101/Loongson_1C101_data.pdf
龙芯1C101 RT-thread 4.0移植 https://gitee.com/zhuangwei123/ls1c101/tree/master/libcpu/mips/ls1c101

( 附:单点登录管理图形界面前端 fgsso-ver0.2.1.zip 源代码 下载地址 http://u.163.com/nnnnnnWYH 提取码: LPzE7fQT )

上一篇:stm32串口通信USART简介


下一篇:基于AndroidThings接入空气质量检测器(温度,湿度,二氧化碳,甲醛,TVOC,PM2.5,PM10)记录