全部博文(320)
分类:
2010-08-07 12:18:49
转自:http://www.cnblogs.com/yuphone/archive/2010/04/22/1717779.html
此处以我所写的MAX7219为范例,从HDL接口描述到C语言软件编程,分析两种表面不一样、但实质是一样的寄存器映射方法,找出其中联系与区别。
01 |
/*-----版权声明-----
|
02 |
*
艾米电子工作室——让开发变得更简单 |
03 |
*
网站:
|
04 |
*
淘宝:
|
05 |
*
QQ(邮箱):amy-studio@qq.com |
06 |
*-----文件信息----- |
07 |
*
文件名称:Amy_S_max7219_avalon_interface.v |
08 |
*
最后修改日期:3.20, 2010 |
09 |
*
描述:Max7219的Avalon接口描述文件 |
10 |
*------------------ |
11 |
*
创建者:张亚峰 |
12 |
*
创建日期:3.20, 2009 |
13 |
*
版本:1.0 |
14 |
*
描述:原始版本 |
15 |
*------------------ |
16 |
*
修改者: |
17 |
*
修改日期: |
18 |
*
版本: |
19 |
*
描述: |
20 |
*-------------------
|
21 |
*/ |
22 |
|
23 |
module Amy_S_max7219_avalon_interface(
|
24 |
//
Clcok Input |
25 |
input csi_clk, |
26 |
input csi_reset_n,
|
27 |
//
Avalon-MM Slave |
28 |
input avs_chipselect,
|
29 |
input
[1:0] avs_address, |
30 |
input avs_write,
|
31 |
input
[31:0] avs_writedata, |
32 |
//
Conduit End |
33 |
output
reg coe_din, |
34 |
output
reg coe_cs, |
35 |
output
reg coe_clk |
36 |
);
|
37 |
|
38 |
// write
|
39 |
always@(posedge csi_clk, negedge
csi_reset_n) |
40 |
begin
|
41 |
if (!csi_reset_n)
|
42 |
begin
|
43 |
coe_din
<= 1'b0; |
44 |
coe_cs
<= 1'b0; |
45 |
coe_clk
<= 1'b0; |
46 |
end
|
47 |
else if
(avs_chipselect & avs_write)
|
48 |
begin
|
49 |
case (avs_address)
|
50 |
0:
coe_din <= avs_writedata[0]; |
51 |
1:
coe_cs <= avs_writedata[0]; |
52 |
2:
coe_clk <= avs_writedata[0]; |
53 |
endcase
|
54 |
end
|
55 |
end
|
56 |
|
57 |
endmodule |
<
;p>在这里,使用了3个寄存器,并通过avs_address来寻址。从50~52行,可以看出,这三个寄存器的偏移地址(Offset)分别是0、1和2。
01 |
//++++++++++++++++++++++++++++++++++++++
|
02 |
// 寄存器映射 开始
|
03 |
// 根据HDL编写
|
04 |
//++++++++++++++++++++++++++++++++++++++
|
05 |
#include
|
06 |
|
07 |
#define IOWR_MAX7219_DIN(base,
data) IOWR(base, 0, data) |
08 |
#define IOWR_MAX7219_CS(base,
data) IOWR(base, 1, data) |
09 |
#define IOWR_MAX7219_CLK(base,
data) IOWR(base, 2, data) |
10 |
//--------------------------------------
|
11 |
// 寄存器映射 结束
|
12 |
//-------------------------------------- |
注意:结尾那个是发博客发出来的,不属于代码。
由于是使用ALtera的API——IOWR(),因此第5行,就得加上#include
代码描述:使用上面的已经映射好的函数
01 |
//++++++++++++++++++++++++++++++++++++++
|
02 |
// 基地址 开始
|
03 |
// 根据SOPC Builder设置编写
|
04 |
//++++++++++++++++++++++++++++++++++++++
|
05 |
#include "system.h"
|
06 |
|
07 |
#define max7219_addr
MAX7219_BASE |
08 |
//--------------------------------------
|
09 |
// 基地址 结束
|
10 |
//--------------------------------------
|
11 |
|
12 |
|
13 |
//++++++++++++++++++++++++++++++++++++++
|
14 |
// 寄存器映射 开始
|
15 |
// 根据HDL编写
|
16 |
//++++++++++++++++++++++++++++++++++++++
|
17 |
#include
|
18 |
|
19 |
#define IOWR_MAX7219_DIN(base,
data) IOWR(base, 0, data) |
20 |
#define IOWR_MAX7219_CS(base,
data) IOWR(base, 1, data) |
21 |
#define IOWR_MAX7219_CLK(base,
data) IOWR(base, 2, data) |
22 |
//--------------------------------------
|
23 |
// 寄存器映射 结束
|
24 |
//--------------------------------------
|
25 |
|
26 |
|
27 |
//++++++++++++++++++++++++++++++++++++++
|
28 |
// 管脚操作 开始
|
29 |
//++++++++++++++++++++++++++++++++++++++
|
30 |
#define SET_DIN
IOWR_MAX7219_DIN(max7219_addr, 1) |
31 |
#define CLR_DIN
IOWR_MAX7219_DIN(max7219_addr, 0) |
32 |
#define SET_CS
IOWR_MAX7219_CS(max7219_addr, 1) |
33 |
#define CLR_CS
IOWR_MAX7219_CS(max7219_addr, 0) |
34 |
#define SET_CLK
IOWR_MAX7219_CLK(max7219_addr, 1) |
35 |
#define CLR_CLK
IOWR_MAX7219_CLK(max7219_addr, 0) |
36 |
//--------------------------------------
|
37 |
// 管脚操作 结束
|
38 |
//-------------------------------------- |
注意:结尾那个是发博客发出来的,不属于代码
代码4 Amy_S_max7219.c代码片段
代码描述:使用Altera API的具体操作
01 |
#include "Amy_S_max7219.h"
|
02 |
|
03 |
/*
|
04 |
*
发送一个字节的子程序: |
05 |
*
上升沿发送数据, |
06 |
* MSB
first |
07 |
*/ |
08 |
void Max7219_WriteByte(alt_u8 byte)
|
09 |
{
|
10 |
alt_u8 i;
|
11 |
for (i=0; i<8; i++)
|
12 |
{
|
13 |
CLR_CLK;
|
14 |
if (byte & 0x80)
|
15 |
SET_DIN; |
16 |
else |
17 |
CLR_DIN; |
18 |
byte
<<= 1; |
19 |
SET_CLK;
|
20 |
}
|
21 |
} |
至此,使用Altera的API来描述寄存器存储映射的方法,告一段落。
其实这种方法,Altera的API的源代码有时也会用到。但是有一个地方需要注意,后面会提到。
如上。
01 |
//++++++++++++++++++++++++++++++++++++++
|
02 |
// 寄存器映射 开始
|
03 |
// 根据HDL编写
|
04 |
//++++++++++++++++++++++++++++++++++++++
|
05 |
#include "system.h"
|
06 |
#include "alt_types.h"
|
07 |
|
08 |
typedef struct |
09 |
{
|
10 |
alt_u32
DIN : 32; |
11 |
alt_u32
CS : 32; |
12 |
alt_u32
CLK : 32; |
13 |
}MAX7219_T;
|
14 |
|
15 |
#define m7219 ((MAX7219_T
*)(MAX7219_BASE)) |
16 |
//--------------------------------------
|
17 |
// 寄存器映射 结束
|
18 |
//-------------------------------------- |
因为Nios II是32位的处理 器,所以之前定义了3个寄存器,都是32位的。此处为了表达这种关系,我们使用了位域。将这个位域(或结构体)重定义为一个类型,然后定义一个该类型的指针变量,起始地址是所需的基地址。这样做,就可以很好地为从基地址开始的连续的3x32位数据寻址(此处为3个寄存器,故数据总长3x32)。
代码描述:使用结构体指针寻址示例
01 |
/*
|
02 |
*
发送一个字节的子程序: |
03 |
*
上升沿发送数据, |
04 |
* MSB
first |
05 |
*/ |
06 |
void Max7219_WriteByte(alt_u8 byte)
|
07 |
{
|
08 |
alt_u8 i;
|
09 |
for (i=0; i<8; i++)
|
10 |
{
|
11 |
m7219->CLK = 0; |
12 |
if (byte & 0x80)
|
13 |
m7219->DIN = 1; |
14 |
else |
15 |
m7219->DIN = 0; |
16 |
byte
<<= 1; |
17 |
m7219->CLK = 1; |
18 |
}
|
19 |
} |
哈哈,是不是可以直接赋值了,更加像单片机了吧。其实Nios II就是单片机,32位的单片机。
做到这里,有些实验者在开发板上演练时,确实成功了;然而有些没有成功?这是为什么呢?我们先看参考资料1。
01 |
IOWR=32DIRECT(GPIO_LED_BASE, 0, 1);
|
02 |
0x04000234 :
movhi r3,2048 |
03 |
0x04000238 :
addi r3,r3,6144 |
04 |
0x0400023c :
movi r2,1 |
05 |
0x04000240 :
stwio r2,0(r3) |
06 |
|
07 |
LED = 1;
|
08 |
0x04000224 :
movhi r3,2048 |
09 |
0x04000228 :
addi r3,r3,6144 |
10 |
0x0400022c :
movi r2,1 |
11 |
0x04000230 :
stw r2,0(r3) |
看到没有,两种寄存器存储映射所对应的汇编不一样。看关键字,一个是stwio,一个是stw。接下来打开手册,Table 3-36。
表1 宽数据传输指令
手册上清楚地写到,I/O外设的数据传输应该使用ldwio和stwio;这两条指令在传输时,是没有cache和buffer的。那怎样让结构体指针的寄存器映射方式也能使用ldwio和stwio呢。接着看手册,在98页,Cache Memory小节,写到 。那还有其他方法来实现cache bypass吗?第38页写到:
图1 Cache Bypass Method
图2 The Bit-31 Cache Bypass Method
好的,看代码。
1 |
#define
ALT_MODULE_CLASS_max7219 Amy_S_max7219 |
2 |
#define MAX7219_BASE 0x1002020
|
3 |
#define MAX7219_IRQ -1
|
4 |
#define
MAX7219_IRQ_INTERRUPT_CONTROLLER_ID -1 |
5 |
#define MAX7219_NAME
"/dev/max7219" |
6 |
#define MAX7219_SPAN 16
|
7 |
#define MAX7219_TYPE
"Amy_S_max7219" |
MAX7219_BASE=0x1002020,第31位是0;因此我们用结构体指针来寄存器存储映射时,传输的数据因数据缓存而出错。那我们就把这个Cache给Bypass(旁路)了。怎么办?把地址总线的第31位置一。
01 |
//++++++++++++++++++++++++++++++++++++++
|
02 |
// 寄存器映射 开始
|
03 |
// 根据HDL编写
|
04 |
//++++++++++++++++++++++++++++++++++++++
|
05 |
#include "system.h"
|
06 |
#include "alt_types.h"
|
07 |
|
08 |
typedef struct |
09 |
{
|
10 |
alt_u32
DIN : 32; |
11 |
alt_u32
CS : 32; |
12 |
alt_u32
CLK : 32; |
13 |
}MAX7219_T;
|
14 |
|
15 |
#define m7219 ((MAX7219_T
*)(MAX7219_BASE | 1<<31)) |
16 |
//--------------------------------------
|
17 |
// 寄存器映射 结束
|
18 |
//-------------------------------------- |
在这里,我们直接通过或(1<<31)的方式,把第31位给置一了;这样从该基地址传输的数据就把数据缓存给旁路了;因为是I/O外设传输嘛。
好了,至此大功告成。我们可以自由切换喜欢的寄存器映射方式。
有时候我们所使用的寄存器并不一定都是32位的,这时要使用嵌套结构体来凑足32位,以防止寄存器寻址错误。
01 |
typedef struct {
|
02 |
struct {
|
03 |
alt_u8
DIN : 8; |
04 |
alt_u32
NC : 24; |
05 |
}offset_0; |
06 |
struct {
|
07 |
alt_u8
CS : 1; |
08 |
alt_u32
NC : 31; |
09 |
}offset_1; |
10 |
struct {
|
11 |
alt_u8
CLK : 1; |
12 |
alt_u32
NC : 31; |
13 |
}offset_2;
|
14 |
}MAX7219_T; |
关于嵌套结构体,此处不解析,请读者自行分析。
两种方法都不错,大家爱用什么就用什么。
参考
1.
2. Altera.Nios II Processor Reference Handbook