问题一:程序直接在iRAM内部可正常执行,而程序搬移(Nand ->SDRAM)之后,就不能正常运行了
#define NAND_SECTOR_SIZE    2048

/* 读函数 */
void nand_read(unsigned char *buf, unsigned long start_addr, int size)
{
int i, j; //if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) if (start_addr & NAND_BLOCK_MASK)
return; /* 地址不对齐则退出 */ /* 选中芯片 */
nand_select_chip(); for(i=start_addr; i < (start_addr + size);)
{
/* 发出READ0命令 */
write_cmd();
/* Write Address */
write_addr(i);
write_cmd(0x30); /* 等待数据就绪 */
wait_idle(); for(j=; j < NAND_SECTOR_SIZE; j++, i++)
{
*buf = read_data();
buf++;
}
} /* 取消片选信号 */
nand_disselect_chip(); return;
}
查看韦东山nand_read代码,发现有这样一条语句:
if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK))
        return;    /* 地址或大小不对齐则退出 */
而在start.S中,size = __bss_start - 0x30000000,这个大小显然不能保证是2048的倍数(事实确也如此,并不是2048的倍数,也就导致了nand_read函数并未正常执行)
地址或大小不对齐则退出,这种方式有问题,大小确实也没必要对齐。若大小刚好超出一个块内存,就将这个块内存完整copy即可。
 
问题二:ARM9裸板开发过程,硬件并不不支持除法运算,所以除法以及取余操作如何实现?
/*
* val : 需要转换的整形值
* bit : 该整形值的位数
* pdst : 转换字符串输出
*/
void int_to_str(int val, unsigned char bit, char *pdst)
{
//根据val位数确定mult. E.G. val-1位 -> mult=1
int mult;
for (mult = ; --bit != ; mult *= ); for ( ; mult != ; mult /= )
{
*pdst++ = (char)(val/mult + '');
val %= mult;
}
}
1、网上搜了一些方法,基本思想是运用移位运算,比较,以及乘法操作等实现。
比如:sum / 6    可转换为    sum * (1 / 6)    (1 / 6) -> (5461 / 32768),而 1/32768 即为(1 >> 15)。这种方法主要是通过将分数转换为被除数是2的次幂的方式实现的,有误差。
2、使用库函数。参考韦东山的方式,在07_IIC工程中添加includelib两个依赖库。同时修改对应地Makefile文件,可以正常编译通过。
#Time: 2018-4-19 11:56:52
#Proj: General Makefile
#Designed by DZH for JZ2440 #Define vars
TARGET_NAME := iic
#final target
TARGET := $(TARGET_NAME).bin
#temp target
BUILD := $(TARGET_NAME).elf
#disa target
DISA := $(TARGET_NAME).dis #default boot from NAND Flash
ENV ?= NAND OBJS += start.o
OBJS += init.o
OBJS += main.o
OBJS += iic.o
OBJS += nand.o
OBJS += serial.o
OBJS += m41t11.o
OBJS += irq_handler.o
OBJS += lib/libc.a

CROSS_COMPILE := arm-linux-
CC := $(CROSS_COMPILE)gcc
LD := $(CROSS_COMPILE)ld
AR := $(CROSS_COMPILE)ar
OBJCOPY := $(CROSS_COMPILE)objcopy
OBJDUMP := $(CROSS_COMPILE)objdump #warning
INCLUDEDIR := $(shell pwd)/include
CCFLAGS += -nostdinc -I$(INCLUDEDIR)

CCFLAGS += -Wall -O2
# 不加-O2优化,链接过程报错:
# lib/libc.a(string.o)(.text+0x38): In function `puts':
# : undefined reference to `putc' #basic address
#ifeq ($(ENV), NAND)
#LDFLAGS += -Ttext=0x0
#else
#LDFLAGS += -Ttext=0xXXX
#endif
LDFLAGS += -Tmap.lds export CC LD OBJCOPY OBJDUMP INCLUDEDIR CCFLAGS AR

#Compile way
all : $(DISA) $(TARGET)
$(DISA) : $(BUILD)
$(OBJDUMP) -D $^ > $@ $(TARGET) : $(BUILD)
$(OBJCOPY) -O binary -S $^ $@ $(BUILD) : $(OBJS)
$(LD) $(LDFLAGS) -o $@ $^ .PHONY : lib/libc.a
lib/libc.a:
cd lib; make; cd ..
%.o : %.S
$(CC) $(CCFLAGS) -c -o $@ $^
%.o : %.c
$(CC) $(CCFLAGS) -c -o $@ $^ clean:
make clean -C lib
rm -f $(TARGET) $(BUILD) $(DISA) *.o
其中加粗部分是为了将除法和取余运算的依赖库包含进来所执行的操作。
 
此外,为了实现时间参数的写入和读出,封装了几个有效的转换函数:
1、string -> int
/*
* pstr : 字符串首地址,空字符结束
* len : 字符串有效字符长度
*/
int str_to_int(const char *pstr, unsigned char len)
{
unsigned int ret = ;
unsigned int mult; //根据pstr长度确定mult. E.G. pstr-1位 -> mult=1
for (mult = ; --len != ; mult *= ); for ( ;*pstr != '\0'; ++pstr)
{
ret += (unsigned int)(*pstr - '') * mult;
mult /= ;
} return (int)ret;
}
 
2、int -> string
/*
* val : 需要转换的整形值
* bit : 该整形值的位数
* pdst : 转换字符串输出
*/
void int_to_str(int val, unsigned char bit, char *pdst)
{
//根据val位数确定mult. E.G. val-1位 -> mult=1
int mult;
for (mult = ; --bit != ; mult *= ); for ( ; mult != ; mult /= )
{
*pdst++ = (char)(val/mult + '');
val %= mult;
}
}
3、参数设置时间参数结构体变量,将字符串类型的时间参数装换为对应的整形值
需要写入的时间参数格式:E.G.(year-mon-day-week-hour-min-sec): 2018 05 15 2 13 29 00,数值间以空格键分开
struct rtc_time {
int tm_sec;
int tm_min;
int tm_hour;
int tm_week;
int tm_mday;
int tm_mon;
int tm_year;
};
void set_rtc_time(char *pstr, struct rtc_time *prtc)
{
char *ptmp = pstr;
char s_time[];
unsigned char i;
unsigned char len[] = {}; //1. 分拆时间字符串
for ( i = ; *ptmp != '\0'; ++ptmp)
{
if (*ptmp != ' ')
len[i]++;
else
{
*ptmp = '\0';
++i;
}
} //2. 设置对应rtc时间参数
send_l(pstr);
prtc->tm_year = str_to_int(pstr, len[]);
pstr += len[] + ;
prtc->tm_mon = str_to_int(pstr, len[]);
pstr += len[] + ;
prtc->tm_mday = str_to_int(pstr, len[]);
pstr += len[] + ;
prtc->tm_week = str_to_int(pstr, len[]);
pstr += len[] + ;
prtc->tm_hour = str_to_int(pstr, len[]);
pstr += len[] + ;
prtc->tm_min = str_to_int(pstr, len[]);
pstr += len[] + ;
prtc->tm_sec = str_to_int(pstr, len[]);
}
void set_time(void)
{
char s_time[] = {};
send_l("E.G.(year-mon-day-week-hour-min-sec): 2018 5 15 2 13 29 0");
recv_l(s_time);
send_s("Input: ");
send_l(s_time);
set_rtc_time(s_time, &g_rtc_time);
m41t11_set_datetime(&g_rtc_time);
}
由于时钟芯片m41t11可能不能正常工作(写入时间无效),故修改set_rtc_time函数作为测试函数如下
void set_rtc_time(char *pstr, struct rtc_time *prtc)
{
char *ptmp = pstr;
char s_time[];
unsigned char i;
unsigned char len[] = {}; //1. 分拆时间字符串
for ( i = ; *ptmp != '\0'; ++ptmp)
{
if (*ptmp != ' ')
len[i]++;
else
{
*ptmp = '\0';
++i;
}
} //2. 设置对应rtc时间参数
send_l(pstr);
prtc->tm_year = str_to_int(pstr, len[]);
pstr += len[] + ;
send_l(pstr);
prtc->tm_mon = str_to_int(pstr, len[]);
pstr += len[] + ;
send_l(pstr);
prtc->tm_mday = str_to_int(pstr, len[]);
pstr += len[] + ;
send_l(pstr);
prtc->tm_week = str_to_int(pstr, len[]);
pstr += len[] + ;
send_l(pstr);
prtc->tm_hour = str_to_int(pstr, len[]);
pstr += len[] + ;
send_l(pstr);
prtc->tm_min = str_to_int(pstr, len[]);
pstr += len[] + ;
send_l(pstr);
prtc->tm_sec = str_to_int(pstr, len[]);
get_rtc_time(&g_rtc_time, s_time);
send_s("g_rtc_time: ");
send_l(s_time);
}
 
3、读取时间参数结构体变量,并将其装换为字符串
void get_rtc_time(const struct rtc_time *prtc, char *pdst)
{
//格式: 2018-05-15 2 19:30:00
int_to_str(prtc->tm_year, , pdst);
*(pdst+) = '-';
int_to_str(prtc->tm_mon, , pdst+);
*(pdst+) = '-';
int_to_str(prtc->tm_mday, , pdst+);
*(pdst+) = ' ';
int_to_str(prtc->tm_week, , pdst+);
*(pdst+) = ' ';
int_to_str(prtc->tm_hour, , pdst+);
*(pdst+) = ':';
int_to_str(prtc->tm_min, , pdst+);
*(pdst+) = ':';
int_to_str(prtc->tm_sec, , pdst+);
*(pdst+) = '\0';
} void show_time(void)
{
//格式: 2018-05-15 2 13:29:00
char s_time[] = {};
send_s("Now is: ");
m41t11_get_datetime(&g_rtc_time);
get_rtc_time(&g_rtc_time, s_time);
send_l(s_time);
}
 
最终实现的效果如下:
1、菜单界面
          
2、[R]ead  data&time.
            
3、[W]rite data&time.
      
从以上结果可以看出,int 与 string 之间的转换函数均可正常工作。
其中最后一行的 g_rtc_time 参数,是逆向转换后的输出结果,能够说明写入m41t11时钟芯片的参数没有问题
m41t11_set_datetime(&g_rtc_time);
不过之后读出的结果并没有发生改变:
      
故猜测可能是由于m41t11时钟芯片原因所致(不过纽扣电池电量不足应该可以排除,否则应该不能从中读出数据吧?这个并没有兴趣折腾了)

ARM裸板开发:07_IIC 通过IIC总线接口读写时钟芯片时间参数实现的总结的更多相关文章

  1. ARM裸板开发:04_MMU 链接地址与运行地址不一致时,(SDRAM)初始化程序地址无关码问题的分析

    ARM裸板开发过程,程序的链接地址设置为为0x30000000,而前期的启动代码以及相关硬件的初始化代码需要在内部iRAM(steppingstone,起始地址0x0)的4K中运行.链接地址与运行地址 ...

  2. 关于在arm裸板编程时使用printf问题的解决方法

    在ARM裸板驱动编程中,是不允许程序直接调用C库程序的.为什么呢?因为此时kernel还没有被加载,所以在封装在kernel层的C库的API是用不了的,那怎么办? 在开发过程中,printf的功能我不 ...

  3. STC8H开发(十三): I2C驱动DS3231高精度实时时钟芯片

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  4. arm裸板驱动总结(makefile+lds链接脚本+裸板调试)

    在裸板2440中,当我们使用nand启动时,2440会自动将前4k字节复制到内部sram中,如下图所示: 然而此时的SDRAM.nandflash的控制时序等都还没初始化,所以我们就只能使用前0~40 ...

  5. 对arm裸板调试的理解

    由于arm芯片一般都包含的由jtag调试这项功能,cpu向外部发出信号时,一般都要同jtag发送出去,它就像一个路口的交警一样,能够控制车辆的运行,当然在arm中指的是cpu发出的数据和地址,我们在调 ...

  6. 第8课.第一个ARM裸板程序(点亮led)及申引

    1.原理图 2.芯片手册 3.几条汇编代码 1.ldr:读内存 ldr R0, [R1] 假设R1的值是x,读取地址x上的数据(4字节),保存到R0中 ldr R0, =0x12345678 (4字节 ...

  7. ARM裸板调试思路总结、笔记

    1. 点灯 2. 串口打印 3. JTAG调试器3.1 命令行调试 3.2 源码级别的调试前提a. 程序必须已经重定位好,位于它的链接地址a.1 如果程序的链接地址是SDRAM, 使用openocd初 ...

  8. 【接口时序】6、IIC总线的原理与Verilog实现

    一. 软件平台与硬件平台 软件平台: 1.操作系统:Windows-8.1 2.开发套件:ISE14.7 3.仿真工具:ModelSim-10.4-SE .ChipScope 硬件平台: 1. FPG ...

  9. C51—模拟IIC总线实现EEPROM存取数据

    a - 什么是IIC总线 -什么是EEPROM -IIC总线的通信格式 模块化设计注解 整体代码 - 什么是IIC总线 IIC总线是同步通信的一种特殊形式,具有接线口少.控制简单.器件封装形式小.通信 ...

随机推荐

  1. Java中的正则表达式Pattern与Matcher

    一般来说比起功能有限的String类,我们更愿意构造功能强大的正则表达式.我们可以通过Pattern 与 Matcher 来构建功能强大的正则表达式 import java.io.File; impo ...

  2. 用java代码将数组元素顺序颠倒

    package test; public class Recover { public int[] reverse(int[] a) { int[] b = new int[a.length]; in ...

  3. 编写 R Markdown 文档

    数据分析师的工作不仅是将数据放入模型并得出一些结论.通常需要完成从数据收集.数据清理.可视化.建模再到最后编写报告或制作演示文稿的完整工作流程.在前面几章中,我们从不同方面深入学习 R 编程语言,从各 ...

  4. Docker 容器十诫

    [编者按]本文作者为 Rafael Benevides,主要介绍使用 Docker 容器时应该注意的十个陷阱. Docker 容器十诫 当你刚开始使用容器时,会发现容器能解决许多问题,而且好处很多: ...

  5. mysql 超大数据/表管理技巧

    如果你对长篇大论没有兴趣,也可以直接看看结果,或许你对结果感兴趣.在实际应用中经过存储.优化可以做到在超过9千万数据中的查询响应速度控制在1到20毫秒.看上去是个不错的成绩,不过优化这条路没有终点,当 ...

  6. Codeforces 534B - Covered Path

    534B - Covered Path 思路:贪心,每一秒取尽可能大并且可以达到的速度. 画张图吧,不解释了: 代码: #include<bits/stdc++.h> using name ...

  7. angular5 组件之间监听传值变化

    http://www.cnblogs.com/SLchuck/p/5904000.html https://i.cnblogs.com/EditPosts.aspx?postid=7995179&am ...

  8. 雷林鹏分享:Ruby 块

    Ruby 块 您已经知道 Ruby 如何定义方法以及您如何调用方法.类似地,Ruby 有一个块的概念. 块由大量的代码组成. 您需要给块取个名称. 块中的代码总是包含在大括号 {} 内. 块总是从与其 ...

  9. [.NET开发] C#编程调用Cards.dll实现图形化发牌功能示例

    本文实例讲述了C#编程调用Cards.dll实现图形化发牌功能.分享给大家供大家参考,具体如下: using System; using System.Collections.Generic; usi ...

  10. JS-Object(2) 原型对象 ,prototype属性。

    基础✅ prototype(✅) JS中的继承 使用JSON数据 构建对象实战 基础 关键字"this"指向了当前代码运行时的对象( 原文:the current object t ...