注意:本节串口的printf等函数的实现方法可以参考OK6410的裸机“11th_uart_stdio”实验源码,该方法更实用。
start.S源码:
.global _start
_start:
ldr sp, =0xD0030000 @初始化堆栈
b main
===================================================================
main.c
#include "clock.h"
#include "led.h"
#include "uart.h"
#include "lib.h"
int main(void)
{
led_init(); // 设置对应管脚为输出
clock_init(); // 初始化时钟
uart_init(); // 初始化UART0
wy_printf("\n********************************\n");
wy_printf(" wy_bootloader\n");
wy_printf(" vars: %d \n",2012);
wy_printf("********************************\n");
while (1)
{
char c = 'x';
putchar('\n');
c = uart_getchar();
putchar(c);
for (c = 'a'; c <= 'z'; c++)
putchar(c);
break;
}
while(1)
{
led_water();
}
return 0;
}
===================================================================
leds.c源码:
#include "lib.h"
#define GPJ2CON (*(volatile unsigned int *)0xe0200280)
#define GPJ2DAT (*(volatile unsigned int *)0xe0200284)
#define GPH2CON (*(volatile unsigned int *)0xE0200C40)
#define GPH2DAT (*(volatile unsigned int *)0xE0200C44)
void led_init(void)
{
// 配置GPJ2_0为输出引脚
GPJ2CON = 0x1111;
}
void led_water(void)
{
int i = 0;
while (1)
{
GPJ2DAT = i;
i++;
if (i == 16)
i = 0;
delay();
}
}
===================================================================
clock.c源码:
#define APLL_CON (*(volatile unsigned int *)0xe0100100)
#define CLK_SRC0 (*(volatile unsigned int *)0xe0100200)
#define CLK_DIV0 (*(volatile unsigned int *)0xe0100300)
#define MPLL_CON (*(volatile unsigned int *)0xe0100108)
void clock_init(void)
{
// 设置时钟为:
// ARMCLK=1000MHz, HCLKM=200MHz, HCLKD=166.75MHz
// HCLKP =133.44MHz, PCLKM=100MHz, PCLKD=83.375MHz,
// PCLKP =66.7MHz
// SDIV[2:0] : S = 1
// PDIV[13:8] : P = 0x3
// MDIV[25:16]: M = 0x7d
// LOCKED [29]: 1 = 使能锁
// ENABLE [31]: 1 = 使能APLL控制器
// 得出FoutAPLL = 500MHz
APLL_CON = (1<<31)|(1<<29)|(0x7d<<16)|(0x3<<8)|(1<<0);
// 时钟源的设置
// APLL_SEL[0] :1 = FOUTAPLL
// MPLL_SEL[4] :1 = FOUTMPLL
// EPLL_SEL[8] :1 = FOUTEPLL
// VPLL_SEL[12]:1 = FOUTVPLL
// MUX_MSYS_SEL[16]:0 = SCLKAPLL
// MUX_DSYS_SEL[20]:0 = SCLKMPLL
// MUX_PSYS_SEL[24]:0 = SCLKMPLL
// ONENAND_SEL [28]:1 = HCLK_DSYS
CLK_SRC0 = (1<<28)|(1<<12)|(1<<8)|(1<<4)|(1<<0);
// 设置分频系数
// APLL_RATIO[2:0]: APLL_RATIO = 0x0
// A2M_RATIO [6:4]: A2M_RATIO = 0x4
// HCLK_MSYS_RATIO[10:8]: HCLK_MSYS_RATIO = 0x4
// PCLK_MSYS_RATIO[14:12]:PCLK_MSYS_RATIO = 0x1
// HCLK_DSYS_RATIO[19:16]:HCLK_DSYS_RATIO = 0x3
// PCLK_DSYS_RATIO[22:20]:PCLK_DSYS_RATIO = 0x1
// HCLK_PSYS_RATIO[27:24]:HCLK_PSYS_RATIO = 0x4
// PCLK_PSYS_RATIO[30:28]:PCLK_PSYS_RATIO = 0x1
CLK_DIV0 = (0x1<<28)|(0x4<<24)|(0x1<<20)|(0x3<<16)|(0x1<<12)|(0x4<<8)|(0x4<<4);
// SDIV[2:0] : S = 1
// PDIV[13:8] : P = 0xc
// MDIV[25:16]: M = 0x29b
// VSEL [27]: 0
// LOCKED [29]: 1 = 使能锁
// ENABLE [31]: 1 = 使能MPLL控制器
// 得出FoutAPLL = 667MHz
APLL_CON = (1<<31)|(1<<29)|(0x29d<<16)|(0xc<<8)|(1<<0);
}
===================================================================
uart.c源码:
#define GPA0CON (*(volatile unsigned int *)0xE0200000)
#define ULCON0 (*(volatile unsigned int *)0xE2900000)
#define UCON0 (*(volatile unsigned int *)0xE2900004)
#define UTRSTAT0 (*(volatile unsigned int *)0xE2900010)
#define UTXH0 (*(volatile unsigned char *)0xE2900020)
#define URXH0 (*(volatile unsigned char *)0xE2900024)
#define UBRDIV0 (*(volatile unsigned int *)0xE2900028)
#define UDIVSLOT0 (*(volatile unsigned int *)0xE290002C)
void uart_init(void)
{
// 设置对应GPIO用于UART0
GPA0CON |= 0x22;
// 设置UART0寄存器
// bit[1:0]:0x3 = 8位数据位
// 其他位默认,即1位停止位,无校验,正常模式
ULCON0 |= (0x3<<0);
// Receive Mode [1:0]:1 = 接收采用查询或者中断模式
// Transmit Mode[3:2]:1 = 发送采用查询或者中断模式
// bit[6]:1 = 产生错误中断
// bit[10]:0 = 时钟源为PCLK
UCON0 = (1<<6)|(1<<2)|(1<<0);
// 设置波特率(详细信息请参考手册或者学习日记)
// DIV_VAL = UBRDIVn + (num of 1's in UDIVSLOTn)/16
// DIV_VAL = (PCLK / (bps x 16)) - 1
UBRDIV0 = 0x23;
UDIVSLOT0 = 0x808;
return;
}
char uart_getchar(void)
{
char c;
// 查询状态寄存器,直到有有效数据
while (!(UTRSTAT0 & (1<<0)));
c = URXH0; // 读取接收寄存器的值
return c;
}
void uart_putchar(char c)
{
// 查询状态寄存器,直到发送缓存为空
while (!(UTRSTAT0 & (1<<2)));
UTXH0 = c; // 写入发送寄存器
return;
}
===================================================================
lib.c源码:
#include "uart.h"
#define UTRSTAT0 (*(volatile unsigned int *)0xE2900010)
#define UTXH0 (*(volatile unsigned char *)0xE2900020)
#define URXH0 (*(volatile unsigned char *)0xE2900024)
void delay(void)
{
volatile int i = 0x100000;
while (i--);
}
void putchar_hex(char c)
{
char * hex = "0123456789ABCDEF";
uart_putchar(hex[(c>>4) & 0x0F]);
uart_putchar(hex[(c>>0) & 0x0F]);
return;
}
int putchar(int c)
{
if(c == '\r')
{
while(!(UTRSTAT0&(1<<1)));
UTXH0 = '\n';
}
if(c == '\n')
{
while(!(UTRSTAT0&(1<<1)));
UTXH0 = '\r';
}
while(!(UTRSTAT0&(1<<1)));
UTXH0 = c;
}
int puts(const char * s)
{
while (*s)
putchar(*s++);
return 0;
}
void putint_hex(int a)
{
putchar_hex( (a>>24) & 0xFF );
putchar_hex( (a>>16) & 0xFF );
putchar_hex( (a>>8) & 0xFF );
putchar_hex( (a>>0) & 0xFF );
}
char * itoa(int a, char * buf)
{
int num = a;
int i = 0;
int len = 0;
do
{
buf[i++] = num % 10 + '0';
num /= 10;
} while (num);
buf[i] = '\0';
len = i;
for (i = 0; i < len/2; i++)
{
char tmp;
tmp = buf[i];
buf[i] = buf[len-i-1];
buf[len-i-1] = tmp;
}
return buf;
}
typedef int * va_list;
#define va_start(ap, A) (ap = (int *)&(A) + 1)
#define va_arg(ap, T) (*(T *)ap++)
#define va_end(ap) ((void)0)
int wy_printf(const char * format, ...)
{
char c;
va_list ap;
va_start(ap, format);
while ((c = *format++) != '\0')
{
switch (c)
{
case '%':
c = *format++;
switch (c)
{
char ch;
char * p;
int a;
char buf[100];
case 'c':
ch = va_arg(ap, int);
putchar(ch);
break;
case 's':
p = va_arg(ap, char *);
puts(p);
break;
case 'x':
a = va_arg(ap, int);
putint_hex(a);
break;
case 'd':
a = va_arg(ap, int);
itoa(a, buf);
puts(buf);
break;
default:
break;
}
break;
default:
putchar(c);
break;
}
}
return 0;
}
===================================================================
Makefile文件:
uart.bin:start.s main.c uart.c clock.c led.c lib.c
arm-linux-gcc -nostdlib -c start.s -o start.o
arm-linux-gcc -nostdlib -c main.c -o main.o
arm-linux-gcc -nostdlib -c uart.c -o uart.o
arm-linux-gcc -nostdlib -c lib.c -o lib.o
arm-linux-gcc -nostdlib -c clock.c -o clock.o
arm-linux-gcc -nostdlib -c led.c -o led.o
arm-linux-ld -Ttext 0xD0020010 start.o main.o uart.o lib.o clock.o led.o -o uart_elf
arm-linux-objcopy -O binary -S uart_elf uart.bin
clean:
rm -rf *.o *.bin uart_elf *.dis
===================================================================
--printf的实现
问:什么是可选参数?
答:比如函数int printf(const char * format, ...),那么参数format后面的都是可选参数(注:不包含format),即可以传入可以不传入的参数。
问:C函数是怎么被组织进C程序的?
答:C语言的函数是从下(低地址)向上(高地址)压入堆栈的,如下图所示:
栈底 高地址
| .......
| 函数返回地址
| .......
| 函数最后一个参数
| ......
| 函数第一个可变参数 <--va_start后ap指向
| 函数最后一个固定参数
| ......
| 函数第一个固定参数
栈顶 低地址
问:实现可选参数,需要做些什么呢?
答:解析如下:
1.关键的宏:
typedef int * va_list; //va_list等价于int *即整型指针,该变量类型应该根据具体的架构(ARM,X86)确定
#define va_start(ap, A) (ap = (int *)&(A) + 1) //(int *)&得到A所在的地址,并强制类型转换为int *,然后这 //个地址加上A的大小,则使ap指向第一个可变参数!!
#define va_arg(ap, T) (*(T *)ap++) //先对指针ap(即地址)进行强制类型转换,转换为该变量实际的类型,
//然后ap(即当前地址)自加1(即加一个类型的大小,指向下一个可变参
//数的地址),这类应该注意的是,先使用后自加,然后取出该地址的值!!
#define va_end(ap) ((void)0)
辅助理解(结合C函数是怎么被组织进C程序的):
1).va_start(arg_ptr, argN):使参数列表指针arg_ptr指向函数参数列表中的第一个可选参数,说明:argN是位于第一个可选参数之前的固定参数,(或者说,最后一个固定参数,或者说,…之前的一个参数),函数参数列表中参数在内存中的存放顺序与函数声明时的顺序是一致的。如果有一va_test()函数的声明是void va_test(char a, char b, char c, …),则它的固定参数(和在内存中存放的顺序)依次是a,b,c,最后一个固定参数argN为c,因此就是va_start(arg_ptr, c);
2).va_arg(arg_ptr, type):返回参数列表中指针arg_ptr所指的参数,返回类型为type,并使指针arg_ptr指向参数列表中的下一个可选参数;
va_end(arg_ptr):清空参数列表,并置参数指针arg_ptr无效。说明:指针arg_ptr被置无效后,可以通过调用va_start()、va_copy()恢复arg_ptr。每次调用va_start()或va_copy()后,必须得有相应的va_end()与之匹配。参数指针可以在参数列表中随意地来回移动,但必须在va_start() … va_end()之间。
精简版prinf的例子:
typedef int * va_list;
#define va_start(ap, A) (ap = (int *)&(A) + 1)
#define va_arg(ap, T) (*(T *)ap++)
#define va_end(ap) ((void)0)
void putchar_hex(char c) //用于显示一字节的十六进制数,也供函数putint_hex()调用
{
char * hex = "0123456789ABCDEF"; //正确的定义和初始化字符串
//char hex[] = "0123456789ABCDEF"; //错误的定义和初始化字符串
putchar(hex[(c & 0xf0)>>4]); //显示高一位
putchar(hex[(c & 0x0f)>>0]); //显示低一位
}
void putint_hex(int a) //用于显示四字节的十六进制数
{
putchar_hex( (a>>24) & 0xFF ); //显示最高处的一字节的十六进制数
putchar_hex( (a>>16) & 0xFF );
putchar_hex( (a>>8) & 0xFF );
putchar_hex( (a>>0) & 0xFF );
}
char * itoa(int a, char * buf) //显示十进制数
{
int num = a;
int i = 0;
int len = 0;
do
{
buf[i++] = num % 10 + '0'; //将数字转换成它的ASCII码,存放在字符串数组中
num /= 10;
} while (num); //直到十进制数变为0
buf[i] = '\0'; //给字符串加一个结束标志位
len = i;
for (i = 0; i < len/2; i++) //该循环用于对数组buf中的元素进行倒序排序
{
char tmp;
tmp = buf[i];
buf[i] = buf[len-i-1];
buf[len-i-1] = tmp;
}
return buf; //返回数组的首地址
}
int printf(const char * format, ...)
{
char c;
va_list ap; //定义一个int *型的指针ap
va_start(ap, format); //初始化ap,让它指向第一个可变参数的地址
while ((c = *format++) != '\0') //开始解析printf函数中的字符串(即第一个固定参数)
{
switch (c) //在while中,依次读出字符串中的字符,在这里依依地进行判断和解析
{
case '%': //如果字符是%,即格式申明符号,则解析该格式
c = *format++; //让c等于%后的第一个字母
switch (c) //对上述字母进行解析
{
char ch;
char * p;
int a;
char buf[100];
case 'c': //如果是%c,即输出字母
ch = va_arg(ap, int); //获取当前可变参数的值,并指向下一个可变参数
putchar(ch); //调用底层(实际操作硬件的那层)来完成输出结果
break;
case 's': //如果是%s,即输出字符串
p = va_arg(ap, char *);
puts(p);
break;
case 'x': //如果是%x,即以十六进制输出
a = va_arg(ap, int);
putint_hex(a);
break;
case 'd': //如果是%d,即以十进制输出
a = va_arg(ap, int);
itoa(a, buf);
puts(buf);
break;
default:
break;
}
break;
default:
putchar(c); //输出字符串中为普通字符的字符
break;
}
}
return 0;
}
注意:
在实际给出的代码里面是用wy_printf来代替printf的,目的只是为了让编译通过,当然还有其他的解决办法,详细讲解,请看韦东山的自己写bootloader的视频。
上一篇:Tiny210裸机简单命令的实现
下一篇:Tiny210裸机之UART串口操作
推荐阅读
史海拾趣
2023年10月,华为坤灵(HUAWEI eKit)在香港成功举办了分销新品发布会。针对香港市场的特点,HUAWEI eKit展示了面向SOHO办公、酒店餐饮、商业地产、零售商超等场景的新品,并通过整合政策、产品、解决方案、服务和数字工具平台等措施,助力香港区域分销伙伴不断开拓中小企业市场。通过与联强国际(香港)有限公司(SYNNEX)的紧密合作,HUAWEI eKit成功吸引了超过100名香港分销商参与此次发布会,进一步巩固了其在香港市场的地位。
AURORA公司成立于2016年,初出茅庐便面临着巨大的挑战。然而,公司的三位联合创始人Chris Urmson、J. Andrew Bagnell和Sterling Anderson均来自自动驾驶汽车领域的顶尖团队,拥有丰富的经验和深厚的技术背景。他们凭借对自动驾驶技术的深刻理解和前瞻性眼光,为AURORA的发展奠定了坚实的基础。在初创时期,AURORA通过与多家汽车制造商合作,共同设计和开发传感器、软件和数据服务,逐渐在自动驾驶领域崭露头角。
EOI深知客户的重要性,因此公司一直致力于提供优质的服务。无论是售前咨询还是售后服务,EOI都认真对待每一个客户的需求和问题。公司建立了完善的客户服务体系,为客户提供及时、专业的支持。同时,EOI还定期举办客户培训和技术交流活动,提高客户的技术水平和使用效果。这些努力使得EOI赢得了客户的信任和忠诚度。
随着技术的不断成熟,Daco开始积极寻求市场拓展。公司首先与几家知名的电子设备制造商建立了合作关系,为其提供定制的半导体解决方案。同时,Daco也积极参与国际半导体展览和交流活动,与全球的行业同仁建立了广泛的联系。通过不断的市场拓展和战略合作,Daco的产品逐渐进入了全球市场,公司的知名度和影响力也得到了显著提升。
Daco Semiconductor在成立之初就专注于半导体技术的研发与创新。公司由一群对半导体行业充满热情的工程师创立,他们致力于开发更高效、更稳定的半导体产品。一次偶然的实验中,Daco的研发团队发现了一种新型的材料组合,这种材料组合能够显著提高半导体的性能和稳定性。这一技术突破成为了Daco的核心竞争力,也为其在竞争激烈的半导体市场中打开了新的局面。
在机器人和开源硬件领域取得一定成绩后,DFRobot开始关注教育市场。他们发现,越来越多的学校和教育机构开始引入机器人和编程课程,以培养学生的创新能力和实践能力。于是,DFRobot推出了一系列针对青少年和创客爱好者的教育产品,如机器人套件、3D打印机等,并通过举办各类竞赛和培训活动,吸引更多的学生和教师参与。这一举措不仅拓展了公司的业务领域,也为公司的长期发展奠定了人才基础。
中国半导体行业协会将申请成为世界半导体理事会成员 美国半导体行业协会表示中国半导体行业协会的加入将增强这一世界性组织的影响力 2006-06-15 应世界半导体理事会(WSC)的邀请,中国半导体行业协会(CSIA)开始WSC的成员资格的申请工作 ...… 查看全部问答∨ |
BTHelper可以帮助您优化BT客户端软件,提高下载速度。通过BTHelper优化的计算机将显著提高下载时的连接速度,加速查找更多的种子并立即连接到它们。BTHelper还可以为BitTorrent下载提供磁盘保护功能。 … 查看全部问答∨ |
笔记本计算机进入Vista过渡期,促2007年DRAM放量增长 根据DRAMeXchange的预估,笔记本计算机出货量在2007年应维持稳定增长,除取代台式电脑现象持续之外,微软(Microsoft)Vista操作系统的推出,以及笔记本计算机本身规格的提升,都将继续推动笔记本计算机的增长。DRAMeXchange预期,2007年全球笔记本计 ...… 查看全部问答∨ |
分析了大功率白色发光二极管的发光强度 即光强 、光通量和色坐标与测量位置的关系, 提出了解决的方法。同时, 对大功率白色发光二极管法向光强、光通量和峰值长随电流和时间的变化情况做了分析, 说明PN 结温度对于大功率发光二极管的发光具有较大的 ...… 查看全部问答∨ |
各位高手,我把我的应用程序编译进内核后,启动vxworks后,经常会打印出一些杂乱的信息,甚至丢失了命令提示符,比如下面这个(我在RAM里建立了文件系统,并建立临时文件,最后销毁): ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] ]]]]]]]]]]]] ...… 查看全部问答∨ |
|
各位大虾; 有个问题请教大家, 我在ARM系统的开发板上要访问网络,网卡芯片是CS8900, 驱动也都有了, 在WINCE下我需要加哪些组件呢? 另外,如何配置呢/ 请知道的大虾指点一下, 不胜感激.… 查看全部问答∨ |
|
IAR STM8S报价:2.3万人民币 COSMIC STM8S报价:2.3万人民币 Raisonance STM8S报价:990欧元(算成人民币大约:8600元不到) 三个用起来感觉Raisonance用起来好用些,价格也算是最便宜的!不过没有石皮 角刀牛。 比起AVR的CodeVisionAVR ...… 查看全部问答∨ |
C#上位机学习资料 https://bbs.eeworld.com.cn/viewthread.php?tid=308129&page=1#pid1198878上周逛论坛看到上面的C#串口教程,觉得挺有趣的,跟着学了一下,结合LaunchPad写了一个简单的ADC10+串口上位机。第一次用C#,线程、运行机制呀什么的全 ...… 查看全部问答∨ |