一、概述

前面已经介绍了向RAM中写入静态字模数据来显示静态的字符和汉字。接下来实现动态显示字符在OLED屏的不同位置。

动态显示字符的核心就是从ROM中读取字符的字模,但取出来的字模数据如果直接写进RAM的话,只能实现字符在某一页的显示,而不能实现任意坐标下的显示。所以在写进RAM之前,我们应该对字模数据做一定处理,然后再写进RAM中。接着RAM读取模块(前面已经介绍过了,本次会改变等待的值,提高一下刷新率)会不断的读取字模数据进行显示。

这里可以先参考一下C语言实现的任意坐标下画点。

/*************************************************************************/
/*函数功能: 画点                                                          */
/*入口参数:                                                          */
/*                      x:横坐标       0~127                             */
/*                      y:纵坐标                0~63                     */
/*                      dot:0,清空;1,填充                                                                                */
/*************************************************************************/
void OLED_DrawPoint(u8 x,u8 y,u8 t)
{u8 pos,bx,temp=0;if(x>127||y>63)return;//超出范围了.pos=7-y/8;bx=y%8;temp=1<<(7-bx);if(t)OLED_GRAM[x][pos]|=temp;else OLED_GRAM[x][pos]&=~temp;
}

二、Verilog代码

module oled_show_char(input clk,                 //时钟信号input rst_n,              //按键复位input [7:0] ascll,        //需要显示字符的ascll码input [4:0] font_size,   //显示字符的字体大小 12,16和24input [6:0] x,               //显示的x坐标input [5:0] y,              //显示的y坐标input en_ram_wr,            //模块使能信号input add_dec_x,            //按键改变x坐标input add_dec_y,           //按键改变y坐标output reg wren,           //ram写使能output [9:0]wraddress,  //ram写地址output reg [7:0]data        //写进ram的数据
);//状态说明
//清除RAM中的数据 等待模块使能 读取rom中的数据 保存数据1 保存数据2(rom地址改变地址后要第二个时钟值才会改变)
//根据坐标改变数据 写数据 完成parameter ClearRAM=0,WaitEn=1,ReadData=2,SaveData1=3,SaveData2=4,ChangeData=5,WriteData=6,Done=7;
reg [2:0] state,next_state; //状态存储
reg [7:0] zm;                       //rom中取出的数据
reg [7:0] ram_zm[127:0][7:0];   //写进ram的数据 因为需要根据坐标来变换 所以寄存一下数据 然后一次性写入
reg [3:0] zm_w_cnt;             //字模每一个字节的位计数器
reg [5:0] zm_cnt;                   //字模个数计数器
reg [9:0] rom_address12;        //12号字体的rom地址
reg [10:0] rom_address16;       //16号字体的rom地址
reg [11:0] rom_address24;       //24号字体的rom地址
wire [7:0] zm12_data,zm16_data,zm24_data; //存储12,16和24号字体读出来的数据reg [6:0] ram_zm_cntx;            //读取ram_zm时用到的x计数器
reg [2:0] ram_zm_cnty;          //读取ram_zm时用到的x计数器
reg [10:0] wraddress_cnt;       //ram地址计数器 用11位的有1024reg [4:0]zm_cnty;                  //y坐标计数reg [6:0] xr;//x增加的坐标
reg [5:0] yr;//y增加的坐标//ram写地址赋值
assign wraddress = wraddress_cnt<11'd1024 ? wraddress_cnt : 10'd0;//状态机下一个状态确认
always @(*) beginif(!rst_n)next_state = ClearRAM; //复位进入初始状态else begincase(state)//清除RAMClearRAM: next_state = (wraddress_cnt == 11'd1023) ? WaitEn : ClearRAM;//等待模块使得能WaitEn: next_state = en_ram_wr ? ReadData : WaitEn;//读取rom数据(rden拉高)ReadData: next_state = SaveData1;//rom地址变化后,两个时钟周期才会出值SaveData1: next_state = SaveData2;SaveData2: next_state = ChangeData;//根据坐标变换数据ChangeData:begincase(font_size)//因为进入这个状态后 zm_cnt的值已经加到1了,所以判断的时候要多一个1//判断是否已经读取到最后一个字模数据了且最后一个字模的8位也全部改变完成则进入下一个状态//否则判断是否一个字模数据的8位已经改变完成 完成则读取下一个字模数据5'd12: next_state = (zm_cnt == 6'd13) ? WriteData : (zm_w_cnt == 4'd7) ? ReadData : ChangeData;5'd16: next_state = (zm_cnt == 6'd17) ? WriteData : (zm_w_cnt == 4'd7) ? ReadData : ChangeData;5'd24: next_state = (zm_cnt == 6'd37) ? WriteData : (zm_w_cnt == 4'd7) ? ReadData : ChangeData;endcaseend//向RAM中写数据WriteData: beginnext_state = (wraddress_cnt == 11'd1024) ? Done : WriteData;end//完成//完成一次写操作后开始下一次写操作//可以通过按键改变坐标//拨码开关改变ascll输入//显示不同的字符在不同的坐标Done:next_state = ClearRAM;endcase       end
end//状态逻辑变量赋值
always @(posedge clk,negedge rst_n) beginif(!rst_n) beginrom_address12 <= 12'd0;rom_address16 <= 12'd0;rom_address24 <= 12'd0;zm <= 8'd0;wren <= 1'b1;data <= 8'd0;endelse begincase(state)//RAM清零ClearRAM:beginram_zm[ram_zm_cntx][ram_zm_cnty] = 8'd0;//ram数据寄存器清零wren <= 1'b0;//写使能信号拉高 清除RAM中的数据//持续刷新的话,复位状态就不清除RAM中的数据了,把ram寄存器中数据清除就好end//等待模块使能WaitEn: beginwren <= 1'b0;//写使能信号拉低//到改变数据的时候地址已经加过1了,所以初始地址减1case(font_size)5'd12:rom_address12 <= (ascll-33)*12-1;//12号字体一个字模数据占12字节5'd16:rom_address16 <= (ascll-33)*16-1;//16号字体一个字模数据占16字节5'd24:rom_address24 <= (ascll-33)*36-1;//24号字体一个字模数据占36字节endcaseend//读取ROM中的数据ReadData: begincase(font_size)//地址增加5'd12:rom_address12 <= rom_address12 + 1'b1;5'd16:rom_address16 <= rom_address16 + 1'b1;5'd24:rom_address24 <= rom_address24 + 1'b1;endcaseend//保存读取到的值SaveData2:begincase(font_size)//保存读取到的数据5'd12: zm <= zm12_data;5'd16: zm <= zm16_data;5'd24: zm <= zm24_data;endcaseend//根据坐标给要写进RAM中的数据赋值ChangeData:beginif(zm[7-zm_w_cnt]) begin//zm_cnt多加了一个 所以减1//如果该位为1 就把这个坐标的点置1if(font_size == 5'd24)ram_zm[x+(zm_cnt-1)/3][(y+yr+zm_cnty)/8] <=  ram_zm[x+(zm_cnt-1)/3][(y+yr+zm_cnty)/8] | 1<<(7-(y+yr+zm_w_cnt)%8);//1左移多少位elseram_zm[x+(zm_cnt-1)/2][(y+yr+zm_cnty)/8] <=  ram_zm[x+(zm_cnt-1)/2][(y+yr+zm_cnty)/8] | 1<<(7-(y+yr+zm_w_cnt)%8);//1左移多少位endelse beginif(font_size == 5'd24)ram_zm[x+(zm_cnt-1)/3][(y+yr+zm_cnty+1)/8] <=  ram_zm[x+(zm_cnt-1)/3][(y+yr+zm_cnty+1)/8];elseram_zm[x+(zm_cnt-1)/2][(y+yr+zm_cnty+1)/8] <=  ram_zm[x+(zm_cnt-1)/2][(y+yr+zm_cnty+1)/8];endend//把数据写进RAM中WriteData:beginwren <= 1'b1;//写使能信号拉高data <= ram_zm[ram_zm_cntx][ram_zm_cnty];//数据赋值end//完成Done:beginwren <= 1'b0;//写使能信号拉低data <= 8'd0;endendcaseend
end//当前状态赋值
always @(posedge clk,negedge rst_n) beginif(!rst_n) state <= ClearRAM;elsestate <= next_state;
end//各种计数器控制
always @(posedge clk,negedge rst_n) beginif(!rst_n) beginzm_cnt <= 6'd0;          //字模字节计数器zm_w_cnt <= 4'd0;         //一个字节的位计数器//比如一个16号的字体,按照从左往右的扫描就需要y坐标增加16x坐标才加1zm_cnty <= 5'd0;           //当前坐标开始的y坐标计数器ram_zm_cnty <= 3'd0;        //字模存储数组读取或者清零时用的计数器ram_zm_cntx <= 7'd0;wraddress_cnt <= 11'd0;   //写地址计数器                endelse begincase(state)ClearRAM:begin//写地址增加 清除RAM中的数据wraddress_cnt <= wraddress_cnt + 1'b1;//字模寄存器计数器增加 将寄存器中内容清空ram_zm_cnty <= ram_zm_cnty + 1'b1;if(ram_zm_cnty == 3'd7)ram_zm_cntx <= ram_zm_cntx + 1'b1;elseram_zm_cntx <= ram_zm_cntx;endWaitEn:begin//等待模块使能 计数器的值清零wraddress_cnt <= 11'd0;ram_zm_cnty <= 3'd0;ram_zm_cntx <= 7'd0;zm_cnt <= 6'd0;zm_w_cnt <= 4'd0;zm_cnty <= 5'd0;end//读取rom中的数据    ReadData:beginzm_cnt <= zm_cnt + 1'b1;//每读取字模的一个字节数据 计数器加1zm_w_cnt <= 4'd0;          //位计数器清零//存进ROM的字模数据是从上到下,从左到右扫描的//比如16号字体,一列就有两个字节,所以要读取两个字节后,增加的y坐标才清零//12号字体一列也是两个字节,24号字体一列是3个字节if(font_size==5'd24)      zm_cnty <= (zm_cnt%3==0) ? 5'd0 : zm_cnty;elsezm_cnty <= (zm_cnt%2==0) ? 5'd0 : zm_cnty;endChangeData:beginzm_w_cnt <= zm_w_cnt + 1'b1;//位计数器增加zm_cnty <= zm_cnty + 1'b1;//y坐标增加endWriteData: beginwraddress_cnt <= wraddress_cnt + 1'b1;//写地址计数器增加ram_zm_cntx <= ram_zm_cntx + 1'b1;//字模寄存器 计数器增加,读取寄存器中的内容写进ram中if(ram_zm_cntx == 7'd127)ram_zm_cnty <= ram_zm_cnty + 1'b1;elseram_zm_cnty <= ram_zm_cnty;endDone:beginwraddress_cnt <= 11'd0;//计数器清零ram_zm_cntx <= 7'd0;ram_zm_cnty <= 6'd0;endendcaseend
end//下面这段代码用来按键增加x坐标和y坐标
//y坐标增减可以
//但是xr加进去之后,逻辑组合节点就会增加2.3倍
//暂时不知道什么原因reg dirx,diry;//增加或者减少坐标的方向控制
reg ac_x_r1,ac_x_r2,ac_y_r1,ac_y_r2;//按键边缘检测信号//如果直接使用negedege rst_n异步信号的话,无法消抖
always @(posedge clk) beginif(!rst_n) beginac_x_r1 <= 1'b0;ac_x_r2 <= 1'b0;ac_y_r1 <= 1'b0;ac_y_r2 <= 1'b0;xr <= 7'd0;yr <= 6'd0;dirx <= 1'b0;diry <= 1'b0;endelse beginac_x_r1 <= add_dec_x;ac_x_r2 <= ac_x_r1;ac_y_r1 <= add_dec_y;ac_y_r2 <= ac_y_r1;   dirx = (xr>7'd100) ? 1'b1 : (xr<5'd20) ? 1'b0 : dirx;diry = (yr<6'd40) ? 1'b1 : (yr<3'd5) ? 1'b0 : diry;if(ac_x_r1 & ~ac_x_r2)//上升沿检测if(dirx)xr <= xr+3'd5;elsexr <= xr-3'd5;if(ac_y_r1 & ~ac_y_r2)//上升沿检测yr <= diry ? yr+3'd5 : yr-3'd5;elseyr <= yr;end
end//12号字体rom
zm_12 zm_12_inst(.clock(clk),.address(rom_address12),.q(zm12_data)
);
//16号字体rom
zm_16 zm_16_inst(.clock(clk),.address(rom_address16),.q(zm16_data)
);
//24号字体rom
zm_24 zm_24_inst(.clock(clk),.address(rom_address24),.q(zm24_data)
);endmodule

最后x坐标增加的地方存在问题,在坐标显示上加入改变的x坐标后,会使得组合逻辑节点数量增加2-3倍,但是y坐标加进去不会,暂时也没有找到原因(有看懂的朋友可以回复一下)。


三、关键代码说明

reg [7:0] ram_zm[127:0][7:0];    //写进ram的数据 因为需要根据坐标来变换 所以寄存一下数据 然后一次性写入

将要写进RAM的1024个字节,拆写成x和y坐标的形式,便于观察,后面写进RAM中的时候再转换成连续的1024个字节

//RAM清零ClearRAM:beginram_zm[ram_zm_cntx][ram_zm_cnty] = 8'd0;//ram数据寄存器清零wren <= 1'b0;//读使能信号拉高 清除RAM中的数据//持续刷新的话,复位状态就不清除RAM中的数据了,把ram寄存器中数据清除就好end
            ClearRAM:begin//写地址增加 清除RAM中的数据wraddress_cnt <= wraddress_cnt + 1'b1;//字模寄存器计数器增加 将寄存器中内容清空ram_zm_cnty <= ram_zm_cnty + 1'b1;if(ram_zm_cnty == 3'd7)ram_zm_cntx <= ram_zm_cntx + 1'b1;elseram_zm_cntx <= ram_zm_cntx;en

每次写字模数据前,先把之前寄存的字模数据清除了,RAM的话就不需要清除了,因为是持续写RAM的,下次写进去的时候会覆盖之前的数据。

还有就是感觉使用for循环不是很好,所以选择使用计数器的方式在时序电路里面来实现清零。

            ReadData:beginzm_cnt <= zm_cnt + 1'b1;//每读取字模的一个字节数据 计数器加1zm_w_cnt <= 4'd0;           //位计数器清零//存进ROM的字模数据是从上到下,从左到右扫描的//比如16号字体,一列就有两个字节,所以要读取两个字节后,增加的y坐标才清零//12号字体一列也是两个字节,24号字体一列是3个字节if(font_size==5'd24)      zm_cnty <= (zm_cnt%3==0) ? 5'd0 : zm_cnty;elsezm_cnty <= (zm_cnt%2==0) ? 5'd0 : zm_cnty;end

然后就是写进RAM中的y坐标,这个坐标值不应该是写完一个字节后就回到初始的y值(我一开始就想错了),因为得到的字模数据是从上到下,从左到右扫描出来的,所以要一直增加到一列数据写完(12号和16号字体一列两字节,24号字体一列3字节)之后再回到初始的y值,也就是增值清零。

            //根据坐标给要写进RAM中的数据赋值ChangeData:beginif(zm[7-zm_w_cnt]) begin//zm_cnt多加了一个 所以减1//如果该位为1 就把这个坐标的点置1if(font_size == 5'd24)ram_zm[x+(zm_cnt-1)/3][(y+yr+zm_cnty+1)/8] <=  ram_zm[x+(zm_cnt-1)/3][(y+yr+zm_cnty+1)/8] | 1<<(7-(y+yr+zm_w_cnt+1)%8);//1左移多少位elseram_zm[x+(zm_cnt-1)/2][(y+yr+zm_cnty+1)/8] <=  ram_zm[x+(zm_cnt-1)/2][(y+yr+zm_cnty+1)/8] | 1<<(7-(y+yr+zm_w_cnt+1)%8);//1左移多少位endelse beginif(font_size == 5'd24)ram_zm[x+(zm_cnt-1)/3][(y+yr+zm_cnty+1)/8] <=  ram_zm[x+(zm_cnt-1)/3][(y+yr+zm_cnty+1)/8];elseram_zm[x+(zm_cnt-1)/2][(y+yr+zm_cnty+1)/8] <=  ram_zm[x+(zm_cnt-1)/2][(y+yr+zm_cnty+1)/8];endend

最后就是这个画点的地方,x坐标变化就是给定的x坐标(原始的x坐标)加上字模字节个数的偏移量,12号字体和16号字体,每读两字节,x坐标加1,而24号字体,每读3字节,x坐标加1;y坐标则是给定的y坐标加上当前字节读取的位数,每读一个字节的一位,y坐标加1,12号和16号字体读取两字节后增加的y坐标清零,24号字体则读取3字节后清零。

后面就是对一个字节的位操作,判断输入字节的某一位是否为1,为1则将当前的y坐标对应字节的那一位清零(描述有点不太清除)。就是y坐标是8个字节组成的,所以y坐标不能简单的用一个数来表示,而是某一个字节的某一位来表示y坐标。(用下面的图片简单说明一下)

图中有个错误:(0,3)应该是从第一个字节的第4位开始往低位写


四、字模生成存进ROM

使用PCtoLCD2002+C2MIF生成字模,然后转换为mif文件,最后存进ROM中

软件提取:

链接:https://pan.baidu.com/s/1vHe-5lWX-mxuCFA_y8aZpg
提取码:1poq

参考链接:C2MIF的使用

打开PCtoLCD2002软件,设置选项

然后输入下面的字符并分别生成12号,16号和24号字体大小的字模

!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~

以16号字体为例,生成的字模文本是这样的

我们先将头部的那些说明删掉,然后再删除数据后面的注释

有个使用小技巧,打开Nodepad++,按住Alt键,然后再使用鼠标左键就可以选择多列进行操作(如图,然后点击删除键就可以删除后面的注释)

删除后的样子

接着我们直接CTAL+A赋值所有数据到C2MIF中(C2MIF记得使用管理员身份打开,因为生成的文件是放桌面的,需要管理员权限)

然后点击转换后我们就可以在桌面看到生成的MIF文件了

我也不知道头部内容会不会有影响,但我还是把头部的那些内容删除了

最后的样子

然后我们就可以创建ROM IP核,将mif文件放进去了。

ROM IP核的创建比较简单,就不赘述了,注意ROM的大小的选择。因为不同字号字体需要的ROM深度是不一样的,创建时可以查看一下mif文件中的深度,然后ROM的深度应该大于等于这个值。


五、顶层模块

在顶层模块中增加显示字符的模块并对输出信号做切换来显示静态或者动态的数据。

module oled_drive(input clk,         //时钟信号 50MHzinput rst_n,        //按键复位input ram_rst,        //ram复位 高电平复位input change_show,//用来切换显示静态数据还是动态数据input [2:0] ascll_add,//ascll字符输入 用来动态显示input add_dec_x,   //按键控制显示的x坐标加减input add_dec_y,  //按键控制显示的y坐标加减output oled_rst,  //oled res 复位信号output oled_dc,  //oled dc 0:写命令 1:写数据output oled_sclk,    //oled do 时钟信号output oled_mosi  //oled d1 数据信号
);wire clk_1m;          //分频后的1M时钟
wire ena_write;     //spi写使能信号
wire [7:0] data;        //spi写的数据wire init_done;        //初始化完成信号
wire [7:0] init_data;//初始化输出给spi的数据
wire init_ena_wr;       //初始化的spi写使能信号
wire init_oled_dc;wire [7:0] ram_data;  //读到的ram数据
wire [7:0] show_data;//输出给spi写的数据
wire rden;              //ram的读使能信号
wire [9:0] rdaddress;//ram读地址信号
wire ram_ena_wr;        //ram使能写信号
wire ram_oled_dc;       //ram模块中的oled dc信号wire wren;                //ram写使能信号
wire [9:0] wraddress;//ram写地址
wire [7:0] wrdata;  //写到ram中的数据//下面这样做 主要是把静态显示和动态显示分开,可以通过乒乓开关切换
wire wren_ramw;     //ram写模块输出的写使能信号
wire [9:0] wraddress_ramw;//ram写模块输出的写地址信号
wire [7:0] data_ramw;//ram写模块输出的数据信号wire wren_showchar; //显示字符模块输出的写使能信号
wire [9:0] wraddress_showchar;//显示字符模块输出的写地址信号
wire [7:0] data_showchar;//显示字符模块输出的数据//一个信号只能由一个信号来驱动,所以需要选择一下
//判断是否初始化完成 完成则显示ram中的静态数据
assign data = init_done ? show_data : init_data;
assign ena_write = init_done ? ram_ena_wr : init_ena_wr;
assign oled_dc = init_done ? ram_oled_dc : init_oled_dc;//通过乒乓开关切换静态显示还是动态显示
assign wren = change_show ? wren_showchar : wren_ramw;
assign wraddress = change_show ? wraddress_showchar : wraddress_ramw;
assign wrdata = change_show ? data_showchar : data_ramw;
//改变模块使能信号
wire en_ram_wr = change_show ? 0 : 1;
wire en_showchar = change_show ? 1 : 0;//时钟分频模块 产生1M的时钟
clk_fenpin clk_fenpin_inst(.clk(clk),.rst_n(rst_n),.clk_1m(clk_1m)
);//spi传输模块
spi_writebyte spi_writebyte_inst(.clk(clk_1m),.rst_n(rst_n),.ena_write(ena_write),.data(data),.sclk(oled_sclk),.mosi(oled_mosi),.write_done(write_done)
);//oled初始化模块 产生初始化数据
oled_init oled_init_inst(.clk(clk_1m),.rst_n(rst_n),.write_done(write_done),.oled_rst(oled_rst),.oled_dc(init_oled_dc),.data(init_data),.ena_write(init_ena_wr),.init_done(init_done)
);//ram读模块
ram_read ram_read_inst(.clk(clk_1m),.rst_n(rst_n),.write_done(write_done),.init_done(init_done),.ram_data(ram_data),.rden(rden),.rdaddress(rdaddress),.ena_write(ram_ena_wr),.oled_dc(ram_oled_dc),.data(show_data)
);//ram写模块
ram_write ram_write_inst(.clk(clk_1m),.rst_n(rst_n),.en_ram_wr(en_ram_wr),.wren(wren_ramw),.wraddress(wraddress_ramw),.data(data_ramw)
);//oled显示字符
oled_show_char oled_show_char_inst(.clk(clk_1m),.rst_n(rst_n),.ascll(8'd48+ascll_add),.font_size(5'd24),.x(7'd60),.y(6'd10),.add_dec_x(add_dec_x),.add_dec_y(add_dec_y),.en_ram_wr(en_showchar),.wren(wren_showchar),.wraddress(wraddress_showchar),.data(data_showchar)
);//ram ip核
ram_show ram_show_inst(.clock(clk_1m),.aclr(!ram_rst),.data(wrdata),.rdaddress(rdaddress),.rden(rden),.wraddress(wraddress),.wren(wren),.q(ram_data)
);endmodule

六、testbench代码

当实验现象和自己预期的结果不一样的时候,多使用仿真查看状态变化,计数器值等等可以更快的发现问题并解决问题。

`timescale 1ns/1ns //仿真单位为1ns,精度为1nsmodule oled_show_char_tb();reg clk;
reg rst_n;
reg [7:0]ascll;
reg [4:0]font_size;
reg [6:0]x;
reg [5:0]y;
reg en_ram_wr;wire wren;
wire [9:0] wraddress;
wire [7:0] data;oled_show_char oled_show_char_inst(.clk(clk),.rst_n(rst_n),.ascll(ascll),.font_size(font_size),.x(x),.y(y),.en_ram_wr(en_ram_wr),.wren(wren),.wraddress(wraddress),.data(data)
);initial begin#0   clk = 0;rst_n = 0;ascll = 8'd48;font_size = 5'd16;x = 7'd0;y = 6'd3;en_ram_wr = 1'b1;#20 rst_n = 1;
endalways #5 clk = ~clk;endmodule

FPGA驱动OLED Verilog代码 (五)------ 动态显示字符相关推荐

  1. (79)FPGA面试题-Verilog实现五分频

    1.1 FPGA面试题-Verilog实现五分频 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4)FPGA面试题-Verilog实现五分频: 5)结束语. 1.1.2 本 ...

  2. 基于FPGA的CAN通信,FPGA驱动SJA1000T芯片代码,实现标准帧与扩展帧的通信驱动

    基于FPGA的CAN通信,FPGA驱动SJA1000T芯片代码,实现标准帧与扩展帧的通信驱动,已上板调通 品牌型号 CAN SJA1000T 与世面上的不同,代码不是SJA1000T芯片代码,而是驱动 ...

  3. fpga驱动oled iic显示代码_【接口时序】6、IIC总线的原理与Verilog实现

    欢迎FPGA工程师加入官方微信技术群 点击蓝字关注我们FPGA之家-中国最好最大的FPGA纯工程师社群 一. 软件平台与硬件平台 软件平台: 1.操作系统:Windows-8.1 2.开发套件:ISE ...

  4. 【FPGA】用Verilog代码实现无源蜂鸣器驱动实验

    目录 一.理论部分 1.蜂鸣器概述 2.驱动原理 3.实验目标 二.模块框图 三.波形图 1.时钟与复位信号 2.计数器 (1)0.5s计数器 (2)音调频率计数器 (3)分频计数器 (4)频率变量最 ...

  5. FPGA驱动74HC595实现数码管动态显示

    数码管原理 图a为单个数码管的原理图,实际上和点灯差不多,不过这8个灯按照数字8的形状进行排布. 数码管分共阴极和共阳极两种,图b左边是共阴极数码管,需要给对应的引脚高电平以点亮,右边是共阳极数码管, ...

  6. fpga驱动oled iic显示代码_Arduino提高篇02—OLED屏汉字显示

    上篇中我们已经使用驱动库在OLED屏上显示出英文字符,除去数字.字母.符号等,OLED还能显示汉字.图片及任意图形.本篇介绍取模软件的使用,教大家如何在OLED屏上显示汉字. 1. 字模显示原理及取模 ...

  7. FPGA纯verilog代码读写N25Q128A QSPI Flash 提供工程源码和技术支持

    目录 1.N25Q128A芯片解读 2.N25Q128A读写时序 3.整体设计思路架构 4.verilog读写Flash驱动设计 5.verilog读写Flash控制器设计 6.FIFO缓存设计 7. ...

  8. 基于阿里平头哥的单片机软、硬件i2C驱动oled

    基于RiskV的阿里平头哥MCU开箱文章之软硬件i2C驱动oled 12864 Risk-V简介 国产单片机及开发环境CDK iic简介 iIC代码移植详细介绍 # stm32 iic: # 向国产单 ...

  9. FPGA接口_N25Q128型号的spi flash驱动verilog代码编写

    # N25Q128型号的spi flash驱动verilog代码编写 提示:使用正点原子达芬奇pro做的小例子,由于教程中无flash的读写,因此撰写记录 文章目录 # N25Q128型号的spi f ...

最新文章

  1. strtok()函数详解
  2. 一个不错的安全站点yehg.net
  3. OGNL表达式语言中的#和$的区别
  4. wiki多个文件一起导入_wiki.js 使用 postgres 支持中文全文检索
  5. 基于javaweb的物资配送管理系统_智慧物流之RFID仓库管理系统,为传统的仓库管理带来了希望...
  6. oracle vitu,Supply Chain Management (SCM) a Manufacturing | Oracle Česká Republika
  7. 微商人赚钱的4个关键点
  8. 面对 996,程序员如何利用“碎片时间”涨薪?
  9. IP DHCP SNOOPING工作原理测试
  10. 黑马程序员python15期月薪_2019年传智播客黑马python人工智能15期全套视频教程
  11. vue设计调查问卷:换个思路解决问题
  12. 离线安装selenium
  13. qte5编译dub.json
  14. html页面内检索,如何用 javascript 实现html页面的关键字搜索?
  15. 所以Web 3.0到底是什么?
  16. UE4Lamplight_预结算光照情景
  17. 基于FPGA的频率计
  18. blos设置具体解释
  19. 《强悍》小妹妹买裤子砍价的全过程!
  20. 卡巴斯基 取消 远程控制 限制

热门文章

  1. 苹果三代耳机_苹果三代蓝牙无线耳机
  2. 世界怎样存在---唯物辩证法
  3. 诺基亚手机软件设计大赛
  4. Python实现《合成孔径雷达成像——算法与实现》图4.4
  5. mysql易百_MySQL DATE类型(来自易百)
  6. Error updating database. Cause: java.sql.SQLException: #HY000
  7. 《Cortex-CM3权威指南》——存储器系统
  8. GD32F303RET6 串口空闲中断+DMA数据发送接收+环形缓冲区方式保存数据
  9. 环境变量path的作用、时间序列的学习、标准差与标准误差
  10. T94 EndNote Journal List批量更新方案