三十六.串口控制台

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();
结束转换,清空变参列表,使得args指针无效(因为他是一个列表,所以相当于一个”数组“,故数组名是一个指针)。
     
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)大胆的修改代码进行实验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值