6. ARM接口编程


SOC最终需要与外设进行通信,SOC外面有很多接口,通过接口对外设进行读写。
接口编程分三类:

  • 一:是单个管脚的,自己模拟时序的,称为GPIO编程。
  • 二:是串行协议编程,如串口编程。
  • 三:是带协议的并行接口,如nandflash编程

1. GPIO编程

GPIO就是芯片上的那些干啥都行的引脚,这时就需要一个控制器来管理这些引脚,通过它来配置各个引脚的功能,即GPIO控制器。下面以操作蜂鸣器为例

1.1 学会看原理图和手册

首先,查看蜂鸣器电路原理图
图
由图,只需在XpwmTOUCH1输出高电平,则三极管处导通,有源蜂鸣器就能发声。

其次,查看核心板原理图
tu
得到XpwmTOUCH1接到GPD0_1

最后,查看数据手册
在这里插入图片描述
得到控制GPD0_1的寄存器地址后,就可以进行编程了

1.2 蜂鸣器驱动汇编版本

start.s:

.gloabal _start
_start:
		bl buzzer_init
for:
		bl buzzer_on
		@printf("buzzer on\n")
		ldr r0, =str_on
		mov lr, pc
		ldr pc, =0x3ff13e54
		bl delay
		
		bl buzzer_off
		@printf("buzzer off\n")
		ldr r0, =str_off
		mov lr, pc
		ldr pc, =0x3ff13e54
		bl delay
		
		b for

buzzer_init:
		@GPD0CON
		ldr r0, =0xE02000A0
		ldr r1, [r0]
		bic r1, r1, #(0xf<<4) @4~7=0000
		orr r1, r1, #(0x1<<4) @4~7=0001
		str r1, [r0]
		
		mov pc, lr
		
buzzer_on:
		@GPD0CON
		ldr r0, =0xE02000A4
		ldr r1, [r0]
		orr r1, r1, #(0x1<<1) @1=1
		str r1, [r0]
		
		mov pc, lr
		
buzzer_off:
		@GPD0CON
		ldr r0, =0xE02000A4
		ldr r1, [r0]
		bic r1, r1, #(0x1<<1) @1=0
		str r1, [r0]
		
		mov pc, lr

delay:
		mov r0, #0x100000
d:
		subs r0, r0, #1
		bne d
		mov pc, lr
		
str_on:
		.asciz "buzzer on\n"
		
str_off:
		.asciz "buzzer off\n"
	

1.3 编译和运行

编译
结果

1.4 蜂鸣器驱动C语言版本

start.s:

.global _start
_start:
		bl main

buzzer.c:

#define GPD0CON (*(volatile unsigned int *)0xE02000A0)
#define GPD0DAT (*(volatile unsigned int *)0xE02000A4)
void buzzer_init()
{
	GPD0CON=GPD0CON&~(0xf<<4);
	GPD0CON=GPD0CON|(0x1<<4);
	
}
void buzzer_on()
{
	GPD0DAT|=0x1<<1;
}
void buzzer_off()
{
	GPD0DAT&=~(0x1<<1);
}
void delay()
{
	int i=1000000;
	while(i--);

}
int main()
{
	int (*printf)(char *format,...)=(void*)0x3ff13e54;
	buzzer_init();
	while(1)
	{
		buzzer_on();
		printf("buzzer on\n");
		delay();
	
		buzzer_off();
		printf("buzzer off\n");
		delay();
	}
	return 0;
}

1.5 编译和运行

编译
结果

2. 串口编程

2.1 并行和串行接口介绍

图
内存与DMC(内存控制器)之间是通过地址总线和数据总线连接,都是并行接口。

有些外设如传感器则不必要用总线(并行接口)来传输,用串行接口即可满足需求,串行接口有以下几种:

  • 串口:异步全双工,适用于双方都有独立的时钟,不需要时钟线的场景。缺点是一对一,且速度慢
  • IIC:同步半双工,适用于只需要一方提供时钟信号进行同步的,IIC是总线性质的,可以挂载多个设备通过软件寻址找到设备。缺点是半双工。
  • SPI:同步全双工,适用于只需要一方提供时钟信号可以挂载多个设备通过硬件寻址(CS片选线)找到设备。
  • USB:协议固定,高速。

2.1 串口电路原理图和手册

电路原理图
图SP3232EEN:电平转换芯片,为了避免串口长距离传输电阻增高导致电压不足。将3.3V的电平转为RS232(±15V)电平输出

核心板原理图
图
数据手册
GPIO
图
UART
图

2.2 串口编程

uart.c

#define GPA0CON  (*(volatile unsigned int *)0xE0200000)

#define ULCON0   (*(volatile unsigned int *)0xE2900000)
#define ULON0    (*(volatile unsigned int *)0xE2900004)
#define UTRSTAT0 (*(volatile unsigned int *)0xE2900010)
#define UTXH0    (*(volatile unsigned int *)0xE2900020)
#define URXH0    (*(volatile unsigned int *)0xE2900024)
#define UBRDIV0  (*(volatile unsigned int *)0xE2900028)
#define UDIVSLOT0 (*(volatile unsigned int *)0xE290002C)
void uart_init()
{
	//GPIO配置
	GPA0CON &= ~0xff;
	GPA0CON |= 0x22;
	//UART配置传输格式
	//8位长,一个停止位,没有校验位
	ULCON0 = 0x3;
	//读写模式: 轮询模式
	UCON0 = 0x5;
	//波特率
	//DIV_VAL=(PCLK/(bps x 16))-1
	//34.8= 66*1000*1000/(115200*16)-1
	UBRDIV0 = 34;
	UDIVSLOT0=0xDFDD;
}
char _getc()
{
	char ch;
	//查询是否有有效数据
	while((UTRSTAT0 & 0x1)==0)
		;
	ch = URXH0;
	return ch;
}
void _putc(char ch)
{
	//查询是否可以发送数据 UTRSTAT0的第二位
	while((UTRSTAT0 & (0x1<<2)) == 0)
		;
	UTXH0 = ch;
	
}

main.c

int main()
{
	uart_init();
	_putc('h');
	_putc('e');
	_putc('l');
	_putc('l');
	_putc('o');
	_putc('\n');
	_putc('\r');
	_putc('h');
	_putc('e');
	_putc('l');
	_putc('l');
	_putc('o');
	_putc('\n');
	_putc('\r');
	char ch;
	while(1)
	{
		ch = _getc();
		_putc('=');
		_put(ch);
	}
	return 0;
]

start.s:

.global _start
_start:
		bl main

2.3 编译和测试

编译
结果

2.4 从0实现printf函数

修改uart.c

#define GPA0CON  (*(volatile unsigned int *)0xE0200000)

#define ULCON0   (*(volatile unsigned int *)0xE2900000)
#define ULON0    (*(volatile unsigned int *)0xE2900004)
#define UTRSTAT0 (*(volatile unsigned int *)0xE2900010)
#define UTXH0    (*(volatile unsigned int *)0xE2900020)
#define URXH0    (*(volatile unsigned int *)0xE2900024)
#define UBRDIV0  (*(volatile unsigned int *)0xE2900028)
#define UDIVSLOT0 (*(volatile unsigned int *)0xE290002C)
void uart_init()
{
	//GPIO配置
	GPA0CON &= ~0xff;
	GPA0CON |= 0x22;
	//UART配置传输格式
	//8位长,一个停止位,没有校验位
	ULCON0 = 0x3;
	//读写模式: 轮询模式
	UCON0 = 0x5;
	//波特率
	//DIV_VAL=(PCLK/(bps x 16))-1
	//34.8= 66*1000*1000/(115200*16)-1
	UBRDIV0 = 34;
	UDIVSLOT0=0xDFDD;
}
char _getc()
{
	char ch;
	//查询是否有有效数据
	while((UTRSTAT0 & 0x1)==0)
		;
	ch = URXH0;
	return ch;
}
void _putc(char ch)
{
	//查询是否可以发送数据 UTRSTAT0的第二位
	while((UTRSTAT0 & (0x1<<2)) == 0)
		;
	UTXH0 = ch;
	
}
void _puts(char *str)
{
	while(*str != '\0')
	{
		_putc(*str);
		if(*str == '\n')
			_putc('\r');
		str++;
	}
}
#include <stdarg.h>
void itoa(int num,char *buf)
{
	int i;
	if(num < 10)
	{
		buf[0] = num+'0';
		buf[1] = '\0';
		
		return ;
	}
	itoa(num / 10,buf);
	for(i = 0; buf[i] != '\0';i++)
		;
	buf[i] = num % 10 + '0';
	buf[i+1] = '\0';
	return ;
}
void itox(int num,char *buf)
{
	int i;
	if(num < 16)
	{
		if(num < 10)
		{
			buf[0] = num + '0';
			buf[1] = '\0';
		}
		else
		{
			buf[0] = num - 10 + 'a';
			buf[1] = '\0';
		}
		return ;
	}
	itox(num / 16,buf);
	for(i = 0;buf[i] != '\0';i++)
		;
	if(num % 16 < 10)
	{
		buf[i] = num % 16 + '0';
		buf[i+1] = '\0';
	}
	else
	{
		buf[i] = num %16 - 10 +‘a';
		buf[i+1] = '\0';	
	}
	return ;
}
int u_printf(const char *format,...)
{
	va_list list;//系统提供的宏
	
	int n;
	char buf[128];
	char *s;
	
	va_start(list,format);//初始化宏
	
	while(*format != '\0')
	{
		if(*format == '%')
		{
			format++;
			switch(*format)
			{
				case 'd':
						n = va_arg(list,int)//取可变参数列表中的值
						itoa(n,buf);
						_puts(buf);
						break;
				case 'x':
						n = va_arg(list,int)
						itox(n,buf);
						_puts(buf);
						break;
				case 'c':
						n = va_arg(list,int)
						_putc(n);
						break;
				case 's':
						s = va_arg(list,char *);
						_puts(s);
						break;
				
			}
			format++;//跳过逗号
		}
		else
		{
			_putc(*format);
			if(*format == '\n')
				_putc('\r');
			format++;
			
		}
	}
	va_end(list);
	return 0;
}

main.c

int main()
{
	uart_init();
	_puts("hello world\n");
	_puts("hello world\n");
	_puts("hello world\n");
	_puts("hello world\n");
	u_printf("hello a = %d, c = %c, x = %x, s = %s\n",99,'c',0x11,"world");
	char ch;
	while(1)
	{
		ch = _getc();
		_putc('=');
		_put(ch);
	}
	return 0;
]

start.s

.global _start
_start:
		bl main

2.5 编译和运行

编译
结果

3. Nand Flash编程

Nand Flash在嵌入式系统中的地位与PC上的硬盘类似。用于保存数据。与内存掉电后数据丢失不同,NandFlash中的数据在掉电后仍可永久保存。

不能随机写入,必须先擦除再写入的存储器一般称为ROM
ROM有两种:
1.读写以字节为单位,容量小,称为EEPROM
2.读写以页为单位,擦除以块为单位,被称为Flash
Flash也分两类:
- Nor Flash 接口像内存一样,有地址总线和数据总线CPU可以直接访问,和CPU总线共同编址容量要考虑CPU的地址总线
- Nand Flash 没有地址总线和数据总线,通过通用IO管脚访问,CPU不能直接访问,和CPU总线独立编址容量可以非常大

3.1 Nand Flash管脚定义

查看Nand Flash数据手册
管脚图
管脚定义
N.C:不用连接,起固定作用和考虑到兼容性
I/O:数据、地址、指令都是通过IO管脚发送
ALE 0 CLE 1:拉高表示传输的是命令
ALE 1 CLE 0: 拉高表示传输的是地址
ALE0 CLE 0:表示传输的是数据
CE:片选信号线 低电平有效
RE:读使能
WE:写使能
WP:写保护使能
RB:状态管脚,判断是否准备好数据,低电平表示忙

3.2 Nand Flash地址结构图

图
device就相当于书架,block相当于书,page相当于页
1 device = 8192 block
1 block = 64 page
1 page = 2K + 64
1 device = 1GB = 2的30次方

因此一个地址是30位的,而IO一次只能传8位,所以分为5个周期传一条完整的地址,如下图所示:
图
页内地址 = addr % 2048
页地址 = addr / 2048

3.3 Nand Flash电路原理图和手册

电路原理图和SOC连接图
电路图
Flash控制寄存器数据手册
手册

由NandFlash手册里需要的时序分析得到Flash控制寄存器里应该要配置的值
结果
计算具体值
计算

3.4 Nand Flash编程实现初始化

main.c

#define NFCONF (*(volatile unsigned int *)0xB0E00000)
#define NFCONT (*(volatile unsigned int *)0xB0E00004)
#define NFCMMD (*(volatile unsigned int *)0xB0E00008)
#define NFADDR (*(volatile unsigned int *)0xB0E0000C)
#define NFDATA (*(volatile unsigned int *)0xB0E00010)
#define NFSTAT (*(volatile unsigned int *)0xB0E00028)

void nand_init()
{
	//配置寄存器:时序
#define TACLS 0
#define TWRPH0 2
#define TWRPH1 0
	NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)|(1<<1);
	//开启flash控制器
	NFCONT |= 0x1;
	//选住NandFlash
	NFCONT &= ~(0x1<<1)
	
}
void nand_read(unsigned int ddr,unsigned int nand,unsigned int len)
{
	int i = 0;
	unsigned int page_addr = nand / 2048;
	NFCMMD = 0x00;
	
	NFADDR = 0;
	NFADDR = 0;
	NFADDR = page_addr & 0xff;
	NFADDR = (page_addr>>8)&0xff;
	NFADDR = (page_addr>>16)&0xff;
	
	NFCMMD = 0x30;
	while((NFSTAT & 0x1) == 0)
		;
	//读2k数据
	for(i = 0;i < 512; i++,ddr +=4{
		*(unsigned int *)ddr = NFDATA;//每次取四个字节
	}
	
}
int main()
{
	char buf[128];
	
	uart_init();
	
	nand_init();
	//buf内存地址,0x600000是nandflash里的地址 2048是长度
	nand_read((unsigned int)buf,0x600000,2048);
	
	buf[30] = '\0';
	
	uprintf(%s\n",buf);
	
	return 0;
]

3.5 编译和测试

编译
内存中已有数据:
结果
从内存写到Flash:
结果

测试程序从Flash中读出来的结果:
结果

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值