在实验室研究了一段时间的裸板程序,放假回家了本来还想继续玩下去的,结果USB转串口线出问题了,不能在我的笔记本上玩裸板,只好先看看U-boot移植。
我的虚拟机环境是RedHat 5,昨天晚上尝试解压U-boot源码包的时候报如下错误:
tar: uboot1.1.6/include/asm-arm/arch: Cannot create symlink to `arch-s3c64xx': Operation not supported
tar: uboot1.1.6/include/asm-arm/proc: Cannot create symlink to `proc-armv': Operation not supported
tar: uboot1.1.6/include/regs.h: Cannot create symlink to `s3c6410.h': Operation not supported
tar: uboot1.1.6/include/asm: Cannot create symlink to `asm-arm': Operation not supported
tar: uboot1.1.6/tools/crc32.c: Cannot create symlink to `../lib_generic/crc32.c': Operation not supported
tar: uboot1.1.6/tools/environment.c: Cannot create symlink to `../common/environment.c': Operation not supported
tar: Exiting with failure status due to previous errors
我刚开始以为是权限问题,也不对啊,我用的是root。后来上网查过资料,原来解压U-boot源码包不能在共享文件夹下进行。因为宿主机的文件系统和虚拟机上的Linux文件系统不同,是不能创建上述的一些链接文件。
把源码包拷贝到Linux文件系统上一个地方,解压就解决问题了。
//总得来说三个主要步骤
//(1)映射虚拟内存,注册中断等
//(2)填充结构体struct imx_i2c_struct
//(3)调用i2c_register_adapter注册I2C设备
static int __init i2c_imx_probe(struct platform_device *pdev)
{
//分析了几个驱动发现,平台驱动有很多相似的地方,比如说在prode里一般都会定义
//一个platform_data结构体,一个对应设备的结构体比如struct imx_i2c_struct
struct imx_i2c_struct *i2c_imx;
struct resource *res;
struct imxi2c_platform_data *pdata;
//__iomem表示指针是指向一个I/O的内存空间
void __iomem *base;
//resource_size_t = u32
resource_size_t res_size;
int irq;
int ret;
dev_dbg(&pdev->dev, "<%s>\n", __func__);
//获取IO资源和中断资源
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "can't get device resources\n");
return -ENOENT;
}
//获取中断资源
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "can't get irq number\n");
return -ENOENT;
}
pdata = pdev->dev.platform_data;
if (pdata && pdata->init) {
ret = pdata->init(&pdev->dev);
if (ret)
return ret;
}
res_size = resource_size(res);
//检测该IO资源是否可用,若可用,并标志为已用
if (!request_mem_region(res->start, res_size, DRIVER_NAME)) {
ret = -EBUSY;
goto fail0;
}
//映射物理地址到虚拟内存
base = ioremap(res->start, res_size);
if (!base) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -EIO;
goto fail1;
}
//给i2c_imx分配内存,并初始化为0
i2c_imx = kzalloc(sizeof(struct imx_i2c_struct), GFP_KERNEL);
if (!i2c_imx) {
dev_err(&pdev->dev, "can't allocate interface\n");
ret = -ENOMEM;
goto fail2;
}
//填充结构体i2c_imx
strcpy(i2c_imx->adapter.name, pdev->name);
//i2c_adapter,Linux的I2C驱动框架中的主要数据结构(i2c_driver、i2c_client、i2c_adapter和i2c_algorithm)之一出现了
//对应于物理上的一个适配器
i2c_imx->adapter.owner = THIS_MODULE;
//i2c_algorithm也出现了
//i2c_algorithm主要提供通信的函数
i2c_imx->adapter.algo = &i2c_imx_algo;
i2c_imx->adapter.dev.parent = &pdev->dev;
i2c_imx->adapter.nr = pdev->id;
i2c_imx->irq = irq;
i2c_imx->base = base;
i2c_imx->res = res;
/* Get I2C clock */
//开启I2C时钟源
i2c_imx->clk = clk_get(&pdev->dev, "i2c_clk");
if (IS_ERR(i2c_imx->clk)) {
ret = PTR_ERR(i2c_imx->clk);
dev_err(&pdev->dev, "can't get I2C clock\n");
goto fail3;
}
/* Request IRQ */
//注册中断函数i2c_imx_isr
ret = request_irq(i2c_imx->irq, i2c_imx_isr, 0, pdev->name, i2c_imx);
if (ret) {
dev_err(&pdev->dev, "can't claim irq %d\n", i2c_imx->irq);
goto fail4;
}
/* Init queue */
//初始化queue
init_waitqueue_head(&i2c_imx->queue);
/* Set up adapter data */
//把I2C_imx保存到adapter中,最终可以调用i2c_get_adapdata获取
i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);
/* Set up clock divider */
//这个pdata->bitrate在板级配置文件中设置
if (pdata && pdata->bitrate)
i2c_imx_set_clk(i2c_imx, pdata->bitrate);
else
i2c_imx_set_clk(i2c_imx, IMX_I2C_BIT_RATE);
/* Set up chip registers to defaults */
writeb(0, i2c_imx->base + IMX_I2C_I2CR);
writeb(0, i2c_imx->base + IMX_I2C_I2SR);
/* Add I2C adapter */
//重要的函数来了,
//i2c_add_numbered_adapter最终调用i2c_register_adapter添加I2C控制器
ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
if (ret < 0) {
dev_err(&pdev->dev, "registration failed\n");
goto fail5;
}
/* Set up platform driver data */
platform_set_drvdata(pdev, i2c_imx);
//打印调试信息
dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", i2c_imx->irq);
dev_dbg(&i2c_imx->adapter.dev, "device resources from 0x%x to 0x%x\n",
i2c_imx->res->start, i2c_imx->res->end);
dev_dbg(&i2c_imx->adapter.dev, "allocated %d bytes at 0x%x \n",
res_size, i2c_imx->res->start);
dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n",
i2c_imx->adapter.name);
dev_dbg(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");
return 0; /* Return OK */
fail5:
free_irq(i2c_imx->irq, i2c_imx);
fail4:
clk_put(i2c_imx->clk);
fail3:
kfree(i2c_imx);
fail2:
iounmap(base);
fail1:
release_mem_region(res->start, resource_size(res));
fail0:
if (pdata && pdata->exit)
pdata->exit(&pdev->dev);
return ret; /* Return error number */
}从爸爸公司拿了一条好的USB转串口线回来,我的OK6410终于可以和我的笔记本连接上了,secureCRT显示和接收按键都没有问题,感谢爸爸!
如果secureCRT能显示U-boot启动信息,但是就是不能接收按键,那么可以先看看有没有设置流控制,如果设置了,那么都流控制设置成无,也就是什么都不选。如果还是不行...那么
1.换个终端试试,例如DNW、minicom、超级终端...
2.换台地电脑试试,网上有人说重装系统,真的是大动干戈...
如果以上都试过了,还是不行,就是你的USB转串口坏掉了,赶紧换一条吧。
USB转串口好了,接下来又可以玩裸板了,按照韦东山老师的教学视频,知道怎么玩系统时钟了,分享一下。
我们的OK6410开发板上的晶振频率是12Mhz
外部晶振提供的12Mhz给S3C6410中的APLL,设置一定的参数可提高系统时钟,可以让CPU跑得更快
在S3C6410的芯片手册上,系统控制-》时钟架构
MUX多路选择器 0:可以原原本本输入12MHz,1:是经过APLL提升的时钟频率
上图的ARMCLK就是CPU时钟
设置APLL
如果我们要设置CPU时钟频率为532MHz,我们可以使用芯片手册上提供给我们的FOUT公司进行进行计算
FOUT = MDIV * FIN / (PDIV * 2^SDIV) = 532MHz
FIN就是晶振频率12Mhz,其余的参数要自己设定
DIV分频器
ARMCLK = DOUTAPLL / (ARM_RADIO + 1)
设置LOCK_TIME
设置APLL_LOCK_TIME
ARM时钟频率提高需要一定时间,所要要设置APPL_LOCK_TIME,当中CPU不工作
APLL给ARM CPU使用
MPLL给主设备、HCLK(内存,DDR),PCLK(外设片上模块)
EPLL给其它模块
OTHERS寄存器设置为异步模式
读入OTHERS内存的值,并bic清0xc0
当各种时钟使用不同的频率,所以要设置成异步
循环判断,等待设置完成
其实也不用清零的,因为从芯片手册删可以看出,复位的时候就是零
然后等待异步模式设置完成,那就要等待bit8~bit11为0即可,至于为什么,S3C6410的手册介绍的还不是很清楚...
DOUT为分频
设置CLK_DVIO
设置ARM_RATIO 0 主核分频为1分频
设置HCLCKX2_RATIO 1 将输入的时钟进行2分频
设置HCLK_RATIO 1 再将输入的时钟2分频,能供内存使用
设置PCLK_RATIO 3 其他RATIO的设置
设置好了,才启动ARLL,然后再设置MUX切换时钟
设置APLL,先配置APLL_CON寄存器,公式为MDIV * Fin / (PDIV * 2^SDIV) = 266 * 12 / (3 * 2^1) = 532
最高位为使能端(1 << 31)
MDIV (266 << 16)
PDIV (3 << 8)
SDIV 1
设置MPLL,同APLL设置
设置MUX切换时钟,设置CLOCK_SRC
MPLL_SET[1] 0:FINMAPLL,1:FOUTMPLL
APLL_SET[0] 0:FINMAPLL,1:FOUTMPLL
先设置参数,再使能,再切换
以下是芯片手册的寄存器地址和相关值的位
下面贴出代码clock.c的代码:
在汇编启动代码中,在跳转到main函数之前,加上一下一句,即可设置时钟
bl clock_init
main函数,我就按照韦东山老师的视频教学上说的,写流水灯代码,可以看到如果延时不够,则流水灯跑得非常快。
在编写这个程序的Makefile就OK了,啊...写得有点累了