乍看 initial_module.v 既包含了 initial_control_module.v 和 spi_write_module.v。spi_write_module.v 前面已经说过了, 至于 initial_control_module.v 吗 ~ 我们知道我们需要一个控制模块来执行,初始化的步骤,而该模块就是这个初衷。
第11~17行定义了输出和输入口相关的信息,具体和图形一样。在22行定义了 rData寄存器,它是用来驱动 SPI_Data (94行)。第23行定义了 isSPI_Start 标志寄存器,如命名般一样,是用来驱动 SPI_Start_Sig, 换句话就是 SPI发送模块的是能信号。
第26~88是该模块的核心部分。当上一层将 Start_Sig 拉高的时候(注意:initial_control_module.v 的 Start_Sig外部连线是Initial_Start_Sig),该模块就开始工作(35行)。全核心部分都是使用“仿顺序操作”的写法。
前三个命令是液晶的“显示配置命令”(38~48行),然而我们知道要对液晶写数据的时候,CS和A0都必须拉低,由于 SPI_Data 位分配的关系。rData寄存器第9 .. 8 位都是赋予 2'b00。
假设 i 等于 0。那么机会发送第一个命令,亦即 0xaf,
(39行)一开始由于条件if没有达到,(40行)rData会被赋予 2'b00 , 8'haf, 并且 isSPI_Start 会设置位逻辑1,这时候 SPI发送模块就会开始工作。直到SPI发送模块发送一字节数据,并且反馈一个完成信号的高脉冲(SPI_Done_Sig),if条件就会成立(39行),然后 isSPI_Start就会被设置为逻辑0,然后i递增以示下一步步骤。
类似上面的操作会一直重复,直到完成发送 3个“显示配置命令”,2个“扫描次序配置命令”,和6个“内部电源配置命令”(38~80行)。直到最后该模块会反馈一个完成信号给上一层模块(82~86行),并且(83行)复位 rData寄存器(前两位必须设置为逻辑1,而后八位可以是任意值)。
initial_module.v 是 initial_control_module.v 和 spi_write_module 的组合模块。连线关系基本上和“图形一样”。
有一点可能会使读者们困惑。因为“低级建模”的全部功能不可能在一个模块中完成,多多少少,读者们会对模块与模块之间的关系会有“不解”的情况。笔者在这里要求读者们要保持平常心去理解,因为Verilog HDL语言的建模本来就需要很强的逻辑性。目前面对的难题就当做是为日后的修行吧。
draw_module.v 是一个组合模块,同样 draw_module.v 有包含 spi_write_module.v 。
此外draw_module.v也含有draw_control_module.v和pika_rom_module.v,pika_rom_module.v 是一个 8 bits x 1024 words 的rom。
draw_control_module.v 控制模块主要是控制绘图的所有操作步骤,然而 pika_rom_module.v 包含了所需要的图片资料。该控制模块对 spi_write_module.v 的链接也和 initial_control_module.v 一样。
第13~20行的定义基本上都和“图形”一样,除了Start_Sig 和 Done_Sig 比较特别,它们在外部的连线时 Draw_Start_Sig 和 Draw_Done_Sig。
第31~67行是该模块的核心部分,但是别被它吓到了,它不过是充气胖子。在这里我们简单复习一下在“顺序操作”中的 Draw_Function() 的操作。
01 | Draw_Fucntion() |
02 | { |
03 | for ( int page = 0; page < 8; page++) |
04 | { |
05 | Send_Command( 0xb0|page); //页地址配置 |
06 | Send_Command( 0x10 ); //列地址高四位配置 |
07 | Send_Command( 0x00 ); //列地址第四位配置 |
08 |
09 | for ( int x = 0; x < 128; x++ ) Send_Data( *p++ ); //发送128次列填充 |
10 | } |
11 | } |
上述的一段函数代码中,一个Draw_Function()函数的功能表达的一了百了,而且该函数中最大作用就是for循环,很可惜 Verilog HDL语言是不推荐使用 for循环。(不要问我为什么,很多的参考书上都是这样写的,如果以我的角度说,我表示for循环不适合Verilog HDL语言的风格)。在Draw_Function() 函数之中,第一个for循环控制 page,亦即页。并且在每一个页的开始都重新配置列地址。至于第二个for循环是用于控制 128次的列填充。
那么 Verilog HDL语言该如何呢?
在34~39行中,i控制执行步骤,x控制列扫描地址(列填充次数),y控制页扫描次序,rData是用来驱动 SPI_Out (73行),而 isSPI_Start是用来驱动 SPI_Start_Sig。当 Start_Sig被拉高的时候(41行),该控制模块就开始工作。
我们先假设一个情况:
当i等于0的时候,由于if条件不成立(45行)。由于“顺序操作”关系,必须先设置页地址,rData 被赋予 2'b00 ( CS=0, A0 = 0, 亦即发送命令 ) 和 y寄存器的值,Y寄存器复位值是 8'd0。然后isSPI_Start寄存器被设置为逻辑1(46行)。此时SPI发送模块开始工作。
当SPI发送完一字节的数据,就会反馈一个高脉冲至完成信号SPI_Done_Sig。此时if条件就会成立(45行),isSPI_Start 寄存器被设置为0,然后i递增以示下一步步骤。
当页地址设置完毕后,接下来的操作就要设置列地址。48~50行是设置列地址的高四位,52~54行是设置列地址的第四位。具体操作和设置也地址一样。不一样的是,每一次设置“新一页”,列地址都必须复原为0。
当页地址和列地址设置okay后,接下来就是128次的列填充操作了。x寄存是用来控制“列填充次数”,同期间也充当“列扫描地址”。一开始的时候,由于if条件和else if条件同样无法达成(57~58行),rData会被赋予 2'b01(CS = 0, A0 = 1, 亦即数据)和八位数据,然而八位数据 Draw_Data 的取值是来至 pika_rom_module.v的地址0的值。同期间 isSPI_Start 被设置为逻辑1,亦即 SPI发送模块被时能。
在这里我们先暂停一下!
为了迎合CGRAM 的分配,pika_rom_module.v 同样也是采用一样的分配方式。pika_rom_module.v 是 8 bits x 1024 words 的分配,如果迎合 CGRAM 的分配方式 pika_rom_module.v 可以这样定义 8 pages x 8 bits x 128 words,这也就说每一页之间的页偏移量是 128个words。在72行是 Rom_Addr 的输出,至于为什么驱动的表达式是 x + ( y << 7 )呢?“ y << 7 ”等价于“y * 128”,我们知道,y寄存器代表了液晶设置的当前页, x寄存器代表了液晶当前的列填充次数。
为了从 pika_rom_module.v 中读取到正确的 Draw_Data 值,“x + ( y << 7 )”表达式也成为了 Rom 地址的转换表达式。
我假设一个情况:
当 y寄存器等于0值时,亦即液晶正在就绪列填充第0页。而第0页的列填充值是在 pika_rom_module.v 的 0~127中。假设模块开始填充第0列,经过表达式转换后 :
( 0 + ( 0 << 7 ) )= 0; 也就是说第0页第0列的填充值在pika_rom_module.v的地址0。
再假设一个情况:
当 y寄存器的值等于3,亦即液晶正在填充第3页,然而第3页的列填充值是在 pika_rom_module.v 的 384~ 491中。假设列填充在63开始,经过表达式转换后 :
( 63 + ( 3 << 7 ) )= 447; 也就是说第3页第63列的填充值在pika_rom_module.v的地址447。
好了继续上面的话题。
直到SPI发送模块完成一字节数据的发送后,就会反馈一个高脉冲的完成信号。此时(58行)else if 条件成立,isSPI_Start 被设置为逻辑0,x寄存器递增,以示下一个列填充。这个时候,会再一次进入 else(59行),isSPI_Start被设置为逻辑1,rData被赋值为 2'b01 和 Draw_Data, 由于72行表达式的关系,这时候列填充的值是来至 pika_rom_module.v 地址1的值。
上述的内容会一直重复到 x 递增至 128 次,直到 if条件成立(57行)y寄存器递增,以示下一个页地址,然后x寄存器被赋值为0,最后 i递增以示下一个步骤。
“设置页地址,设置初始列地址,128次的列填充”这样的过程会一直重复,直到完成液晶全部8个页的所有列填充,draw_control_module.v 就会产生一个完成信号(61~65行),同期间也会复位 rData 和 y 寄存器的值(62行)。
就这样一副 12864 液晶扫描完毕。
Draw_module.v 是一个组合模块,具体上和“图形”一样。
上图中的 lcd_module.v 是已经完成的组合模块。Lcd_control_module.v 顾名思义就是控制液晶操作的控制模块。我们来看一下该模块与“顺序操作”的关系。
main()
{
Initial_Function();
Draw_Function();
while(1);
}
在主函数中,先调用 Initial_Function(), 然后再调用 Draw_Function()。然而液晶控制模块的操作却是如此,先使能initial_module.v 然后再使能 draw_module.v ,最后保持沉默。
至于选择器与4-1章的一样。无论是 initial_module.v 或者 draw_module.v 它们各自都拥有 spi_write_module.v 。为了协调它们共享输出资源,选择器是必须的。
整个组合模块比较简单,只要照“顺序操作”的思路,就能理解它们。
在16~17行定义了使能初始化模块和绘图模块的标志寄存器 isInit 和 isDraw。在27~40行是该控制模块的核心部分。一开始先使能初始化模块(29~31行),当初始化完成后(30行),便使能绘图模块(33~35行)。绘图完成后(34行),便进入停止(37~38行)。
lcd_module.v 组合模块基本上和“图形”一样,自己看着办吧。
这个实验是模仿“顺序操作”的“低级建模”。如 Initial_module.v 组合模块,包含了 initial_control_module.v 和 spi_write_module.v ,initial_control_module.v 控制初始化的次序,spi_write_module 却将数据已SPI模式输出。
draw_module.v 组合模块包含了draw_control_module.v , pika_rom_module.v 和 spi_write_module.v。Rom模块储存了 64 x 128 的图片信息,控制模块控制了显示图片的步骤,最后SPI发送模块将数据已SPI模式输出。
lcd_module.v 同时拥有 initial_module.v 和 draw_module.v 这两个组合模块,然后利用自身的 lcd_control_module.v 去控制“先使能 iniital_module.v 还是 draw_module.v?”。
宏观上,lcd_module.v都是由一层一层不同功能的组合模块组合而成,实际上它却是遵守“顺序操作”的执行步骤建模而成。
“仿顺序操作”始终有一个避免不了的问题,那就是“两仪性或者多义性”,initial_module.v 或者 draw_module.v 它们自身都拥有 spi_write_module.v ,两个模块不可能同时拥有一样的输出资源,这时候就需要选择器的帮助。
完成后的扩展图:
initial_module.v
draw_module.v
lcd_module.v
在宏观上“仿顺序操作”是利用建模模仿“顺序操作”。在微观上“仿顺序操作”是利用Verilog HDL语言本身的特性去模仿“顺序操作”。如 intial_module.v 和 draw_module.v 的 spi_write_module.v。在上述的内容中,“顺序操作”语言都无法顾及 spi传输的小细节,但是 Verilog HDL语言却顾及到这些小细节。还有一点,虽然Verilog HDL在设计上却不如“顺序语言”般的方便,所以在建模的时候应该“多善用位操作”来填补写问题。
最后我们来讨论一个问题:
Verilog HDl代码的设计是不是应该尽量简化!?虽然在网上,众多的人们的答案是“不是”。如果你的代码没有固定的结构(代码风格的问题),自然而然模块也不会执行也太稳定。相反的,如果你拥很强的经验能力,当然你可以忽略这样的问题,但是另一个问题是,你的代码只有你看得懂,别人却看不懂!(这种感受,估计很多新手都尝受过 ... )
如果给笔者选择,笔者会选择中规中矩的做法。笔者要求“代码风格”理应受到维护,但是代码量是在精简和臃肿之间,代码的设计是倾向“理解”的方向。
“低级建模”与“仿顺序操作”之间的优点,笔者就不多说了,读者可以从试验中直接的了解到。但是有一点笔者必须强调“仿顺序操作”不仅只是利用模块来模仿“顺序操作” 而已,设计者应该多利用Verilog HDL语言本身的特性去完成建模设计。
所以说,“仿顺序操作”许多时候也不是完全的模仿“顺序操作”。在顺序操作的语言之中,常常也使用多函数方式,去完成编程。毕竟,Verilog HDL语言与顺序操作语言是两个世界的东西,Verilog HDL更能顾及底层的小细节。
但是也就是这个原因给 Verilog HDL 语言带来许多麻烦。如同笔者在第一章所说,Verilog HDL语言如同乐高积木那样,它“太细”了,如果没有“手段”和“技巧”,是组合不了好的积木模型。换句话说,Verilog HDL语言的建模,如果不存在着结构性,是建立不了好的模块。然而这个“手段”或者“技巧”就是“低级建模”的存在。
用户377235 2012-9-6 14:59
请问pika_rom_module 模块的代码呢 去哪里找得到?
用户1528479 2010-10-8 11:57
用户1010551 2010-5-14 09:18