1.串口角色:
(1)数据传输通道
(2)控制台
2.通讯参数
(1)波特率:衡量传输速率的快慢,每秒钟传输数据的位数(bit)
(2)数据位:有效数据
(3)起始位:线路空闲的时候是高电平,当检测到低电平认为有数据传输开始,所以是低电平。
(4)奇偶校检位:检测数据传输是否正确
(5)停止位:表明一帧数据传输结束
以上这些参数发送方和接收方要一致
3.硬件引脚:RXD,TXD,地
4.初始化
(1)设置引脚工作模式
(2)设置数据格式(ULCON)
(3)设置工作模式(DMA,轮询,中断),(UCON)
(4)设置波特率(UBRDIV)
5.发送
(1)(UTRSTAT)
(2)(UTXH)
6.接收
(1)(UTRSTAT)
(2)(URXH)
7.关于用到的协助函数
(1) va_list args;
声明一个变参列表
(2)unsigned char str[1024];
声明一个字符数组,用来存放转换以后的字符串
(3) va_start(args,fmt);
开始生成可变参数列表里的元素
(4) vsprintf(str,fmt,args);
将可变参数列表args里的元素按照fmt的格式写入字符串str
(5) va_end();
(5) va_end();
结束转换,清空变参列表,使得args指针无效(因为他是一个列表,所以相当于一个”数组“,故数组名是一个指针)。


(3)修改顶层的Makefile

8.关于移植函数库
(1)将提供的lib和include文件夹复制到junboot目录下,主要是编译lib目录
(2)由下面内容可以看出lib目录下的文件最终编译的结果是得到lib.o,所以我们要将lib.o整合到前面产生的jun-boot中
修改顶层Makefile,使得将当前路径下lib目录的编译结果加入到原有的jun-boot一起进行新的编译。
(3)最终目标jun-boot.bin,
(4)jun-boot.elf是由.o文件产生的,将这些.o文件赋值给一个变量,当然这个截图里少了uart.o文件。
则jun-boot.elf的产生:
(5)将print.c剪切到lib下
(6)将OBJS添加上lib/lib.o
(7)添加编译子目录下的lib.o的规则,以及clean伪目标规则。其中-C指明后面的目录lib,all是由于在lib子目录的Makefile中最终目标就是all。
(8)此时编译会出现以下错误
但是查看ctype.c里,发现引用的头文件里已经定义了这些符号,原因在于交叉编译器寻找头文件的路径没有包含这个路径。所以我们要利用-I选项添加头文件路径。而这个选项则应该加在lib子目录下的Makefile文件中,打开该文件发现
已经有了一个CFLAGS变量,该变量用来保存一些编译选项,我们只要在改变量里面添加-I选项就可以。但是这个变量一般应该出现在顶层目录的Makefile中,打开顶层 Makefile发现并未定义,所以在顶层Makefile中定义:
最后还要输出CFLAGS以供子目录使用
(9)此时编译还有以下错误
原因是我们没有添加必要的头文件vsprintf.h,于是在print.c里面添加头文件。
(10)再次编译发现还是有错误
原因是内联函数造成的,所以编译选项还要再改进。即CFLAGS
同时现在的CFLAGS主要使用到main.c所以还要修改main.c的编译选项,即加进去CFLAGS编译选项
(11)printf以及scanf的实现(print.c)
9.代码结构优化
(1)将一般外设.c文件剪切到新建的dev子目录
(2)编写dev子目录的Makefile
(3)修改顶层的Makefile
(3)修改顶层的Makefile
10.210查错
加头程序有bug,限制了bin的大小,检查又只能检查前面16*1024-16*8的部分。
(1)多用排除法去调试代码
(2)大胆的修改代码进行实验。
11.6410代码
/****************************
@File:uart.c
@
@Tiny6410裸机下学期代码
@串口控制台(UART0)
@Author:小君君
@****************************/
#include "common.h"
void uart_init()
{
/*(1)设置引脚工作模式*/
(vi UARTCON) &= ~0xff;
(vi UARTCON) |= 0x22;
/*(2)设置数据格式(ULCON)*/
(vi ULCON0) = 0x3; // 数据位:8, 无校验, 停止位: 1, 8n1
/*(3)设置工作模式(DMA,轮询,中断),(UCON)*/
(vi UCON0) = 0x5; // 时钟:PCLK,禁止中断,普通传输,
//不使用回环方式
//使能UART发送、接收(轮询模式,
//这里在使用DMA的时候要修改)
#ifdef UART_FIFO_ENABLE
(vi UFCON0) = 0x01; // 使能FIFO
#else
(vi UFCON0) = 0x0; //不使能FIFO
#endif
(vi UMCON0) = 0; // 无流控
/*(4)设置波特率(UBRDIV)*/
//PCLK = HCLKX2 / (PCLK_RATIO + 1) = 266/(3+1) = 66.5 MHz(start.S文件可以查看)
// DIV_VAL = (PCLK / (bps x 16 ) ) - 1 = (66500000/(115200x16))-1 = 35.08,以后修改波特率只需计算将115200替换以后的值即可
// DIV_VAL = 35.08 = UBRDIVn + (num of 1’s in UDIVSLOTn)/16
//16*0.08 = 1.28,整数部分是1,所以UDIVSLOTn只有1个1,故UDIVSLOTn他的值只要只有1个位是1即可,取值有16种
UBRDIV0 = 35;
UDIVSLOT0 = 0x1;
}
#ifdef UART_FIFO_ENABLE
/* 接收一个字符 */
char getchar(void)
{
while (((vi UFSTAT0) & 0x7f) == 0); // 如果RX FIFO空,等待
return URXH0; // 取数据
}
/* 发送一个字符 */
void putchar(char c)
{
while ((vi UFSTAT0) & (1<<14)); // 如果TX FIFO满,等待
UTXH0 = c; // 写数据
}
void send_string(char* str)
{
while(*str){
putchar(*str++);
}
}
#else
/* 接收一个字符 */
unsigned char getchar(void)
{
unsigned char ret = 0;
while((UTRSTAT0 & (1 << 0)) == 0); // 如果Receive buffer data ready 为0,表示没有数据可读,等待
ret = URXH0;
return ret;
}
/* 发送一个字符 */
void putchar(unsigned char c)
{
while (!(vi UTRSTAT0) & (1<<2)); // 如果Transmitter empty为0,表示不为空,等待发送完成(等待变为1)
UTXH0 = c; // 写数据
}
void send_string(unsigned char* str)
{
while(*str){
putchar(*str++);
}
}
#endif
/****************************
@File:common.h
@
@Tiny6410裸机下学期代码
@常用头文件定义
@Author:小君君
@****************************/
#ifndef __COMMON_H__
#define __COMMON_H__
#define vi *( volatile unsigned long * )
#define ulong unsigned long
/*取消使用mmu*/
//#define MMU_ENABLE 1
/*LED初始化*/
#ifndef MMU_ENABLE
#define LED_CON 0x7F008800
#define LED_DAT 0x7F008808
#else
#define LED_CON 0xA0008800
#define LED_DAT 0xA0008808
#endif
void mmu_init();
void led_init();
void led_on();
void led_off();
void led1_on();
void led2_on();
void led3_on();
void led4_on();
void led5_on();
void led6_on();
void led7_on();
void led8_on();
/*按键相关初始化*/
#define KEYCON 0x7f008830
#define KEYCON1 0x7f008814
#define K1_MSK (3 << 0)
#define K2_MSK (3 << 2)
#define K3_MSK (3 << 4)
#define K4_MSK (3 << 6)
#define K5_MSK (3 << 8)
#define K6_MSK (3 << 10)
#define K7_MSK (0xF << 12)
#define K8_MSK (0xF << 16)
#define K1_OK (2 << 0)
#define K2_OK (2 << 2)
#define K3_OK (2 << 4)
#define K4_OK (2 << 6)
#define K5_OK (2 << 8)
#define K6_OK (2 << 10)
#define K7_OK (0x3 << 12)
#define K8_OK (0x3 << 16)
void button_init();
/*中断控制器相关的寄存器地址*/
#define EXT_INT_0_CON 0x7f008900
#define EXT_INT_1_CON 0x7f008904
#define EXT_INT_0_MASK 0x7f008920
#define EXT_INT_0_PEND 0x7f008924
#define VIC0INTENABLE 0x71200010
#define VIC1INTENABLE 0x71300010
#define EINT0_VECTADDR 0x71200100 /*每个中断源有一个寄存器存放相应的中断处理函数的地址,共32+32 = 64个*/
#define EINT1_VECTADDR 0x71200104
#define EINT2_VECTADDR 0x71200108
#define EINT3_VECTADDR 0x7120010C
#define EINT4_VECTADDR 0x71200110
#define EINT5_VECTADDR 0x71200114/*以上是VIC0,见6410datasheet的P414*/
#define EINT19_VECTADDR 0x71300100
#define EINT20_VECTADDR 0x71300104 /*以上是VIC1*/
#define VIC0ADDRESS 0x71200f00
#define VIC1ADDRESS 0x71300f00
void irq_init();
/*nandflash相关寄存器定义*/
#define NFCONF 0x70200000
#define NFCONT 0x70200004
#define NFCMMD 0x70200008
#define NFADDR 0x7020000C
#define NFDATA 0x70200010
#define NFDATA8 (*(volatile unsigned char *)0x70200010)
#define NFSTAT 0x70200028
int nand_erase(unsigned int block_addr);
int Nand_PageWrite(unsigned long start_addr,char *buf);
/*UART相关寄存器定义*/
#define UARTCON 0x7F008000
#define ULCON0 0x7F005000
#define UCON0 0x7F005004
#define UFCON0 0x7F005008
#define UMCON0 0x7F00500C
#define UTRSTAT0 0x7F005010
#define UFSTAT0 0x7F005018
#define UTXH0 (*((volatile unsigned char *)0x7F005020))//注意是char类型的
#define URXH0 (*((volatile unsigned char *)0x7F005024))
#define UBRDIV0 (*((volatile unsigned short *)0x7F005028))//注意是short类型的
#define UDIVSLOT0 (*((volatile unsigned short *)0x7F00502C))
#define UART_FIFO_ENABLE 1 //最好使用FIFO模式,就是必须有这个宏定义
void uart_init();
#ifdef UART_FIFO_ENABLE
char getchar(void);
void putchar(char c);
void send_string(char* str);
#else
unsigned char getchar(void);
void putchar(unsigned char c);
void send_string(unsigned char* str);
#endif
#endif
/****************************
@File:main.c
@
@Tiny6410裸机下学期代码
@串口控制台测试文件
@Author:小君君
@****************************/
#include "common.h"
int main(void)
{
int num = 1000;
//mmu_init();//MMU初始化,这里不使用MMU
led_init();//LED的GPIO初始化
button_init();//按键初始化
irq_init();//中断初始化
led_on();//点亮4颗LED
uart_init();//串口初始化
putchar('a');
putchar('\r');
putchar('\n');
putchar('b');
putchar('\r');
putchar('\n');
while(1){
printf("=================================================\n\r");
printf("===================JUN-BOOT======================\n\r");
printf("0.Send the ARP to get yhe host's MAC address\n\r");
printf("1.Download the linux kernel from tftp\n\r");
printf("2.Boot linux OS from SDRAM\n\r");
printf("3.Junjun is a houmorous\n\r");
printf("请输入0-3任意一个数字:\n\r");
scanf("%d",&num);
switch(num){
case 0:
printf("请支持成都国嵌\n\r");
break;
case 1:
printf("国嵌学院=====打造你的嵌入式人生\n\r");
break;
case 2:
printf("学ARM,学Linux,学C++,学安卓,学嵌入式,就到成都国嵌学院\n\r");
break;
default:
printf("只要你肯努力,你的明天就会等你!!\n\r");
break;
}
}
return 0;
}
@****************************
@File:start.S
@
@Tiny6410裸机下学期代码
@
@Author:小君君
@****************************
.text
.global _start
@异常向量表
_start:
b reset
ldr pc, _Undefined_instruction
ldr pc, _Software_interrupt
ldr pc, _Command_abort
ldr pc, _Data_abort
ldr pc, _Nothing_used
ldr pc, _IRQ
ldr pc, _FIQ
@存放实际异常入口地址开辟的存储单元
_Undefined_instruction:
.word Undefined_instruction
_Software_interrupt:
.word Software_interrupt
_Command_abort:
.word Command_abort
_Data_abort:
.word Data_abort
_Nothing_used:
.word Nothing_used
_IRQ:
.word IRQ
_FIQ:
.word FIQ
@各种实际的异常处理函数
Undefined_instruction:
nop
Software_interrupt:
nop
Command_abort:
nop
Data_abort:
nop
Nothing_used:
nop
IRQ:
nop
FIQ:
nop
@上电复位以后就执行这里
reset:
bl set_svc
bl set_peri_port
bl disable_watchdog
bl disable_interrupt
bl disable_mmu
bl clock_init
bl mem_init
bl init_stack
bl clean_bss
bl nand_init
bl copy_to_ddr
@bl light_led
ldr pc, =main
halt:
b halt
@通过读-修改-写的方式控制CPSR从而改变工作模式的svc模式
set_svc:
mrs r0, cpsr
bic r0,r0,#0x1F
orr r0,r0,#0xD3
msr cpsr, r0
mov pc, lr
@关闭看门狗
#define pWTCON 0x7E004000
disable_watchdog:
ldr r0, =pWTCON
mov r1, #0x00
str r1, [r0]
mov pc, lr
@屏蔽中断,要操作两个寄存器,目的是将使能中断寄存器的相关位清除
#define VIC0INTENCLEAR 0x71200014
#define VIC1INTENCLEAR 0x71300014
disable_interrupt:
ldr r0, =VIC0INTENCLEAR
mvn r1, #0x0
str r1, [r0]
ldr r0, =VIC1INTENCLEAR
str r1, [r0]
mov pc, lr
@关闭MMU和Cache
disable_mmu:
mcr p15,0,r0,c7,c7,0
mrc p15,0,r0,c1,c0,0
bic r0,r0,#0x07
mcr p15,0,r0,c1,c0,0
mov pc, lr
@外设地址初始化,要放在进入svc模式以后的第一步操作
set_peri_port:
ldr r0, =0x70000000 @对于6410来说,内存(0x00000000~0x60000000),外设(0x70000000-0x7fffffff)
orr r0, r0, #0x13 @外设大小:256M
mcr p15,0,r0,c15,c2,4 @把r0的值(包括了外设基地址+外设大小)告诉cpu
mov pc, lr
@时钟初始化
#define APLL_LOCK 0x7E00F000
#define CLK_DIV0 0x7E00F020
#define OTHERS 0x7E00F900
#define MPLL_CON 0x7E00F010
#define APLL_CON 0x7E00F00C
#define CLK_SRC 0x7E00F01C
@#define ARM_RATIO 0 @ARMCLK = DOUTAPLL / (ARM_RATIO + 1) = 532/(0+1) = 532 MHz
@#define MPLL_RATIO 0 @ DOUTMPLL = MOUTMPLL / (MPLL_RATIO + 1) = 532/(0+1) = 532 MHz
@#define HCLKX2_RATIO 1 @ HCLKX2 = HCLKX2IN / (HCLKX2_RATIO + 1) = 532/(1+1) = 266 MHz
@#define HCLK_RATIO 1 @ HCLK = HCLKX2 / (HCLK_RATIO + 1) = 266/(1+1) = 133 MHz
@#define PCLK_RATIO 3 @ PCLK = HCLKX2 / (PCLK_RATIO + 1) = 266/(3+1) = 66.5 MHz
#define DIV_VAL ((0 << 0)|(0 << 4)|(1 << 8)|(1 << 9)|(3 << 12)) @注意这里不能用#define DIV_VAL (ARM_RATIO) | (MPLL_RATIO << 4) | (HCLK_RATIO << 8) | (HCLKX2_RATIO << 9) | (PCLK_RATIO << 12)
@原因暂时未查明,不知道是不是我电脑最近鼠标抽风,把字符编码格式改变了导致的结果
clock_init:
@ 1. 设置各PLL的LOCK_TIME,使用默认值
ldr r0, =APLL_LOCK @ APLL_LOCK,供cpu使用
ldr r1, =0x0000FFFF
str r1, [r0]
str r1, [r0, #4] @ MPLL_LOCK,供AHB(存储/中断/lcd等控制器)/APB(看门狗,定时器,SD等)总线上的设备使用
str r1, [r0, #8] @ EPLL_LOCK,供UART,IIS,IIC使用
@ 2. 设置为异步模式
ldr r0, =OTHERS @ OTHERS
@ 《linux installation for u-boot》3.7中:用MPLL作为HCLK和PCLK的Source是异步(ASYNC)模式
@ 用APLL是同步(SYNC)模式
ldr r1, [r0]
bic r1, r1, #0xc0 @ bit[6:7]清0,即SYNCMODE=0/SYNCMUXSEL=0
str r1, [r0]
loop_clock:
ldr r0, =OTHERS
ldr r1, [r0]
and r1, r1, #0xf00
cmp r1, #0
bne loop_clock
@ 3. 设置分频系数
ldr r0, =CLK_DIV0 @CLK_DIV0
ldr r1, =DIV_VAL
str r1, [r0]
@ 4. 设置PLL,放大时钟
@ 4.1 配置APLL
#define APLL_CON_VAL ((1<<31) | (266 << 16) | (3 << 8) | (1))
ldr r0, =APLL_CON @ APLL_CON
ldr r1, =APLL_CON_VAL @ FOUT = MDIV X FIN / (PDIV X 2SDIV) = 266*12/(3*2^1) = 532MHz
str r1, [r0]
@ 4.2 配置MPLL
#define MPLL_CON_VAL ((1<<31) | (266 << 16) | (3 << 8) | (1))
ldr r0, =MPLL_CON @ MPLL_CON
ldr r1, =MPLL_CON_VAL @ FOUT = MDIV X FIN / (PDIV X 2SDIV) = 266*12/(3*2^1) = 532MHz
str r1, [r0]
#define MPLL_SEL 1
#define APLL_SEL 1
@ 5.选择PLL的输出作为时钟源
ldr r0, =CLK_SRC @ CLK_SRC
ldr r1, =(MPLL_SEL<<1) | (APLL_SEL<<0)
str r1, [r0]
mov pc, lr
@将bin文件从_start开始到bss_start结束的数据搬移到_start指定的链接地址(0x50008000)
copy_to_ddr:
mov r0, #0x00
ldr r1, =_start
ldr r2, =bss_start
sub r2,r2,r1
mov ip,lr
bl copy2ddr
mov lr,ip
mov pc,lr
@copy_loop:
@ ldr r3,[r0],#4
@ str r3,[r1],#4
@ cmp r1,r2
@ bne copy_loop
@ mov pc, lr
@初始化栈
init_stack:
mrs r0, cpsr@用读-修改-写 的方式设置CPU的工作模式(CPSR寄存器)
bic r0,r0,#0x1F
orr r0,r0,#0xD2
msr cpsr, r0
ldr sp, =0x5F700000 @设置IRQ模式的堆栈
mrs r0, cpsr
bic r0,r0,#0x1F
orr r0,r0,#0xD3
msr cpsr, r0
ldr sp, =0x5F000000@设置SVC模式的堆栈
mov pc, lr
@清除BSS段
clean_bss:
ldr r0, =bss_start
ldr r1, =bss_end
cmp r0, r1
moveq pc, lr
clean_loop:
mov r2, #0x0
str r2,[r0],#4
cmp r0, r1
bne clean_loop
mov pc, lr
@点亮LED
#define GPKCON0 0x7F008800
#define GPKDAT 0x7F008808
light_led:
@设置GPKCON0
ldr r1, =GPKCON0
ldr r0, =0x11110000
str r0, [r1]
@设置GPKDAT点亮4颗LED
ldr r0, =GPKDAT
ldr r1, =0x0
str r1, [r0]
@延时函数,将会使得开机的时候4颗led先亮一段时间,大约4秒钟左右,之后只亮两颗LED表明进入C语言的main函数
ldr r0, =0xFFFFFF
loop_led:
sub r0,r0,#1
cmp r0, #1
bne loop_led
mov pc, lr
(3)修改顶层的Makefile
10.210查错
加头程序有bug,限制了bin的大小,检查又只能检查前面16*1024-16*8的部分。
(1)多用排除法去调试代码
(2)大胆的修改代码进行实验。