1857|2

172

帖子

3

TA的资源

一粒金砂(高级)

楼主
 

【国产FPGA评测】安路(型号SF1S60CG121I) 05OLED 驱动模块设计、DDS显示以及问题 [复制链接]

  本帖最后由 EPTmachine 于 2023-4-2 23:34 编辑

本文通过设计驱动OLED的FPGA模块,实现在OLED屏幕上显示字符的功能,并于之前的DDS模块相结合,最终实现输出波形的参数显示,但是在调试过程中出现了问题,这里对驱动的过程进行介绍。

1.1 硬件连接

扩展板上的OLED屏幕与SF1 Demo板的连接如下表所示

SF1S60CG1121I引脚

OLED引脚

IO_R10P_0,GCLK13,D0(A10)

DC

IO_R11P_0(B9)

RST

IO_R11N_0(B8)

SPI_MOSI

IO_R12P_0,GCLK14,USRCLK(A9)

SPI_SCK

IO_R12N_0,GCLK15,SCLK(A8)

板上按键K2(6号接口)

连接的实物图如图所示。

1.2 OLED驱动模块设计

驱动OLED屏幕的模块主要包括字库和运行状态机两部分组成。字库模块在复位时进行初始化。具体的代码如下:

	//5*8点阵字库数据
	always@(posedge rst_n)
		begin
			mem[  0] = {8'h3E, 8'h51, 8'h49, 8'h45, 8'h3E};   // 48  0
			mem[  1] = {8'h00, 8'h42, 8'h7F, 8'h40, 8'h00};   // 49  1
			mem[  2] = {8'h42, 8'h61, 8'h51, 8'h49, 8'h46};   // 50  2
			mem[  3] = {8'h21, 8'h41, 8'h45, 8'h4B, 8'h31};   // 51  3
			mem[  4] = {8'h18, 8'h14, 8'h12, 8'h7F, 8'h10};   // 52  4
			mem[  5] = {8'h27, 8'h45, 8'h45, 8'h45, 8'h39};   // 53  5
			mem[  6] = {8'h3C, 8'h4A, 8'h49, 8'h49, 8'h30};   // 54  6
			mem[  7] = {8'h01, 8'h71, 8'h09, 8'h05, 8'h03};   // 55  7
			mem[  8] = {8'h36, 8'h49, 8'h49, 8'h49, 8'h36};   // 56  8
			mem[  9] = {8'h06, 8'h49, 8'h49, 8'h29, 8'h1E};   // 57  9
			mem[ 10] = {8'h7C, 8'h12, 8'h11, 8'h12, 8'h7C};   // 65  A
			mem[ 11] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h36};   // 66  B
			mem[ 12] = {8'h3E, 8'h41, 8'h41, 8'h41, 8'h22};   // 67  C
			mem[ 13] = {8'h7F, 8'h41, 8'h41, 8'h22, 8'h1C};   // 68  D
			mem[ 14] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h41};   // 69  E
			mem[ 15] = {8'h7F, 8'h09, 8'h09, 8'h09, 8'h01};   // 70  F

			mem[ 32] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00};   // 32  sp 
			mem[ 33] = {8'h00, 8'h00, 8'h2f, 8'h00, 8'h00};   // 33  !  
			mem[ 34] = {8'h00, 8'h07, 8'h00, 8'h07, 8'h00};   // 34  
			mem[ 35] = {8'h14, 8'h7f, 8'h14, 8'h7f, 8'h14};   // 35  #
			mem[ 36] = {8'h24, 8'h2a, 8'h7f, 8'h2a, 8'h12};   // 36  $
			mem[ 37] = {8'h62, 8'h64, 8'h08, 8'h13, 8'h23};   // 37  %
			mem[ 38] = {8'h36, 8'h49, 8'h55, 8'h22, 8'h50};   // 38  &
			mem[ 39] = {8'h00, 8'h05, 8'h03, 8'h00, 8'h00};   // 39  '
			mem[ 40] = {8'h00, 8'h1c, 8'h22, 8'h41, 8'h00};   // 40  (
			mem[ 41] = {8'h00, 8'h41, 8'h22, 8'h1c, 8'h00};   // 41  )
			mem[ 42] = {8'h14, 8'h08, 8'h3E, 8'h08, 8'h14};   // 42  *
			mem[ 43] = {8'h08, 8'h08, 8'h3E, 8'h08, 8'h08};   // 43  +
			mem[ 44] = {8'h00, 8'h00, 8'hA0, 8'h60, 8'h00};   // 44  ,
			mem[ 45] = {8'h08, 8'h08, 8'h08, 8'h08, 8'h08};   // 45  -
			mem[ 46] = {8'h00, 8'h60, 8'h60, 8'h00, 8'h00};   // 46  .
			mem[ 47] = {8'h20, 8'h10, 8'h08, 8'h04, 8'h02};   // 47  /
			mem[ 48] = {8'h3E, 8'h51, 8'h49, 8'h45, 8'h3E};   // 48  0
			mem[ 49] = {8'h00, 8'h42, 8'h7F, 8'h40, 8'h00};   // 49  1
			mem[ 50] = {8'h42, 8'h61, 8'h51, 8'h49, 8'h46};   // 50  2
			mem[ 51] = {8'h21, 8'h41, 8'h45, 8'h4B, 8'h31};   // 51  3
			mem[ 52] = {8'h18, 8'h14, 8'h12, 8'h7F, 8'h10};   // 52  4
			mem[ 53] = {8'h27, 8'h45, 8'h45, 8'h45, 8'h39};   // 53  5
			mem[ 54] = {8'h3C, 8'h4A, 8'h49, 8'h49, 8'h30};   // 54  6
			mem[ 55] = {8'h01, 8'h71, 8'h09, 8'h05, 8'h03};   // 55  7
			mem[ 56] = {8'h36, 8'h49, 8'h49, 8'h49, 8'h36};   // 56  8
			mem[ 57] = {8'h06, 8'h49, 8'h49, 8'h29, 8'h1E};   // 57  9
			mem[ 58] = {8'h00, 8'h36, 8'h36, 8'h00, 8'h00};   // 58  :
			mem[ 59] = {8'h00, 8'h56, 8'h36, 8'h00, 8'h00};   // 59  ;
			mem[ 60] = {8'h08, 8'h14, 8'h22, 8'h41, 8'h00};   // 60  <
			mem[ 61] = {8'h14, 8'h14, 8'h14, 8'h14, 8'h14};   // 61  =
			mem[ 62] = {8'h00, 8'h41, 8'h22, 8'h14, 8'h08};   // 62  >
			mem[ 63] = {8'h02, 8'h01, 8'h51, 8'h09, 8'h06};   // 63  ?
			mem[ 64] = {8'h32, 8'h49, 8'h59, 8'h51, 8'h3E};   // 64  @
			mem[ 65] = {8'h7C, 8'h12, 8'h11, 8'h12, 8'h7C};   // 65  A
			mem[ 66] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h36};   // 66  B
			mem[ 67] = {8'h3E, 8'h41, 8'h41, 8'h41, 8'h22};   // 67  C
			mem[ 68] = {8'h7F, 8'h41, 8'h41, 8'h22, 8'h1C};   // 68  D
			mem[ 69] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h41};   // 69  E
			mem[ 70] = {8'h7F, 8'h09, 8'h09, 8'h09, 8'h01};   // 70  F
			mem[ 71] = {8'h3E, 8'h41, 8'h49, 8'h49, 8'h7A};   // 71  G
			mem[ 72] = {8'h7F, 8'h08, 8'h08, 8'h08, 8'h7F};   // 72  H
			mem[ 73] = {8'h00, 8'h41, 8'h7F, 8'h41, 8'h00};   // 73  I
			mem[ 74] = {8'h20, 8'h40, 8'h41, 8'h3F, 8'h01};   // 74  J
			mem[ 75] = {8'h7F, 8'h08, 8'h14, 8'h22, 8'h41};   // 75  K
			mem[ 76] = {8'h7F, 8'h40, 8'h40, 8'h40, 8'h40};   // 76  L
			mem[ 77] = {8'h7F, 8'h02, 8'h0C, 8'h02, 8'h7F};   // 77  M
			mem[ 78] = {8'h7F, 8'h04, 8'h08, 8'h10, 8'h7F};   // 78  N
			mem[ 79] = {8'h3E, 8'h41, 8'h41, 8'h41, 8'h3E};   // 79  O
			mem[ 80] = {8'h7F, 8'h09, 8'h09, 8'h09, 8'h06};   // 80  P
			mem[ 81] = {8'h3E, 8'h41, 8'h51, 8'h21, 8'h5E};   // 81  Q
			mem[ 82] = {8'h7F, 8'h09, 8'h19, 8'h29, 8'h46};   // 82  R
			mem[ 83] = {8'h46, 8'h49, 8'h49, 8'h49, 8'h31};   // 83  S
			mem[ 84] = {8'h01, 8'h01, 8'h7F, 8'h01, 8'h01};   // 84  T
			mem[ 85] = {8'h3F, 8'h40, 8'h40, 8'h40, 8'h3F};   // 85  U
			mem[ 86] = {8'h1F, 8'h20, 8'h40, 8'h20, 8'h1F};   // 86  V
			mem[ 87] = {8'h3F, 8'h40, 8'h38, 8'h40, 8'h3F};   // 87  W
			mem[ 88] = {8'h63, 8'h14, 8'h08, 8'h14, 8'h63};   // 88  X
			mem[ 89] = {8'h07, 8'h08, 8'h70, 8'h08, 8'h07};   // 89  Y
			mem[ 90] = {8'h61, 8'h51, 8'h49, 8'h45, 8'h43};   // 90  Z
			mem[ 91] = {8'h00, 8'h7F, 8'h41, 8'h41, 8'h00};   // 91  [
			mem[ 92] = {8'h55, 8'h2A, 8'h55, 8'h2A, 8'h55};   // 92  .
			mem[ 93] = {8'h00, 8'h41, 8'h41, 8'h7F, 8'h00};   // 93  ]
			mem[ 94] = {8'h04, 8'h02, 8'h01, 8'h02, 8'h04};   // 94  ^
			mem[ 95] = {8'h40, 8'h40, 8'h40, 8'h40, 8'h40};   // 95  _
			mem[ 96] = {8'h00, 8'h01, 8'h02, 8'h04, 8'h00};   // 96  '
			mem[ 97] = {8'h20, 8'h54, 8'h54, 8'h54, 8'h78};   // 97  a
			mem[ 98] = {8'h7F, 8'h48, 8'h44, 8'h44, 8'h38};   // 98  b
			mem[ 99] = {8'h38, 8'h44, 8'h44, 8'h44, 8'h20};   // 99  c
			mem[100] = {8'h38, 8'h44, 8'h44, 8'h48, 8'h7F};   // 100 d
			mem[101] = {8'h38, 8'h54, 8'h54, 8'h54, 8'h18};   // 101 e
			mem[102] = {8'h08, 8'h7E, 8'h09, 8'h01, 8'h02};   // 102 f
			mem[103] = {8'h18, 8'hA4, 8'hA4, 8'hA4, 8'h7C};   // 103 g
			mem[104] = {8'h7F, 8'h08, 8'h04, 8'h04, 8'h78};   // 104 h
			mem[105] = {8'h00, 8'h44, 8'h7D, 8'h40, 8'h00};   // 105 i
			mem[106] = {8'h40, 8'h80, 8'h84, 8'h7D, 8'h00};   // 106 j
			mem[107] = {8'h7F, 8'h10, 8'h28, 8'h44, 8'h00};   // 107 k
			mem[108] = {8'h00, 8'h41, 8'h7F, 8'h40, 8'h00};   // 108 l
			mem[109] = {8'h7C, 8'h04, 8'h18, 8'h04, 8'h78};   // 109 m
			mem[110] = {8'h7C, 8'h08, 8'h04, 8'h04, 8'h78};   // 110 n
			mem[111] = {8'h38, 8'h44, 8'h44, 8'h44, 8'h38};   // 111 o
			mem[112] = {8'hFC, 8'h24, 8'h24, 8'h24, 8'h18};   // 112 p
			mem[113] = {8'h18, 8'h24, 8'h24, 8'h18, 8'hFC};   // 113 q
			mem[114] = {8'h7C, 8'h08, 8'h04, 8'h04, 8'h08};   // 114 r
			mem[115] = {8'h48, 8'h54, 8'h54, 8'h54, 8'h20};   // 115 s
			mem[116] = {8'h04, 8'h3F, 8'h44, 8'h40, 8'h20};   // 116 t
			mem[117] = {8'h3C, 8'h40, 8'h40, 8'h20, 8'h7C};   // 117 u
			mem[118] = {8'h1C, 8'h20, 8'h40, 8'h20, 8'h1C};   // 118 v
			mem[119] = {8'h3C, 8'h40, 8'h30, 8'h40, 8'h3C};   // 119 w
			mem[120] = {8'h44, 8'h28, 8'h10, 8'h28, 8'h44};   // 120 x
			mem[121] = {8'h1C, 8'hA0, 8'hA0, 8'hA0, 8'h7C};   // 121 y
			mem[122] = {8'h44, 8'h64, 8'h54, 8'h4C, 8'h44};   // 122 z
		end

OLED屏幕驱动包括初始化状态机和运行状态机,主要可以分为Idle,Init,Main,Scan ,Write ,Delay。相互之间的切换关系如图所示。

 

状态机的代码如下:

always @ (posedge clk_oled or negedge rst_n)begin
	if(!rst_n)begin
    	cnt_main <= 1'b0; cnt_init <= 1'b0; cnt_scan <= 1'b0; cnt_write <= 1'b0;
		y_p <= 1'b0; x_ph <= 1'b0; x_pl <= 1'b0;
		num <= 1'b0; char <= 1'b0; char_reg <= 1'b0;
		num_delay <= 16'd5; cnt_delay <= 1'b0; cnt <= 1'b0;
		oled_rst <= HIGH; oled_dcn <= CMD; oled_clk <= HIGH; oled_dat <= LOW;
    	state <= IDLE; state_back <= IDLE;
    end else begin
		case(state)
        	IDLE:begin
				cnt_main <= 1'b0; cnt_init <= 1'b0; cnt_scan <= 1'b0; cnt_write <= 1'b0;
				y_p <= 1'b0; x_ph <= 1'b0; x_pl <= 1'b0;
                num_delay <= 16'd5; cnt_delay <= 1'b0; cnt <= 1'b0;
                oled_rst <= HIGH; oled_dcn <= CMD; oled_clk <= HIGH; oled_dat <= LOW;
                state <= MAIN; state_back <= MAIN;
            end
            
            MAIN:begin
            	if(cnt_main >= 5'd12) cnt_main <= 5'd9;//接下来执行空操作,实现数据只刷新一次//1值为最后一个case的值 2值为动态数据起始值
				else cnt_main <= cnt_main + 1'b1;
                case(cnt_main)	//MAIN状态	
                	5'd0:begin state <= INIT; end
                    //y_p   是页寻址模式下的    设置页起始地址 从b0到b7 1页是8行
                    //x_ph  是页寻址模式下的    设置起始列地址高位  从10到17    8大列
                    //x_pl  是页寻址模式下的    设置起始列地址低位  从00到0f    每一大列里面有16小列
                    //num   表示写入的字符串的位数 会不断-1直到0     
                    5'd1:begin y_p <= 8'hb0; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "                ";state <= SCAN; end
					5'd2:begin y_p <= 8'hb1; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "                ";state <= SCAN; end    
					5'd3:begin y_p <= 8'hb2; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "                ";state <= SCAN; end
					5'd4:begin y_p <= 8'hb3; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "                ";state <= SCAN; end
					5'd5:begin y_p <= 8'hb4; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "                ";state <= SCAN; end
					5'd6:begin y_p <= 8'hb5; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "                ";state <= SCAN; end
					5'd7:begin y_p <= 8'hb6; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "                ";state <= SCAN; end
					5'd8:begin y_p <= 8'hb7; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "                ";state <= SCAN; end
					
					5'd9 :	begin y_p <= 8'hb2; x_ph <= 8'h00; x_pl <= 8'h08; num <= 5'd16; char <= "WAVE:           ";state <= SCAN; end 
					5'd10:	begin y_p <= 8'hb4; x_ph <= 8'h00; x_pl <= 8'h08; num <= 5'd16; char <= "FRE:            ";state <= SCAN; end
					5'd11:	begin y_p <= 8'hb6; x_ph <= 8'h00; x_pl <= 8'h08; num <= 5'd16; char <= "AMP:            ";state <= SCAN; end
                    5'd12:	begin y_p <= 8'hb0; x_ph <= 8'h00; x_pl <= 8'h08; num <= 5'd16; char <= "   EEWORLD-DDS  ";state <= SCAN; end 
					
//					5'd13:	begin y_p <= 8'hb2; x_ph <= 8'h12; x_pl <= 8'h08; num <= 5'd3;  char <= dis_type_buff;state <= SCAN; end
//					5'd14:	begin y_p <= 8'hb4; x_ph <= 8'h12; x_pl <= 8'h08; num <= 5'd10;  char <= dis_freq_buff;state <= SCAN; end
//					5'd15:	begin y_p <= 8'hb6; x_ph <= 8'h12; x_pl <= 8'h08; num <= 5'd7;  char <= dis_amp_buff;state <= SCAN; end			
					
					default: state <= IDLE;   //如果你需要动态刷新一些信息,此行应该取消注释								
                endcase
            end

			INIT:begin
            	case(cnt_init)
                	5'd0:	begin oled_rst <= LOW; cnt_init <= cnt_init + 1'b1; end	//复位有效
					5'd1:	begin num_delay <= 16'd25000; state <= DELAY; state_back <= INIT; cnt_init <= cnt_init + 1'b1; end	//延时大于3us
					5'd2:	begin oled_rst <= HIGH; cnt_init <= cnt_init + 1'b1; end	//复位恢复
					5'd3:	begin num_delay <= 16'd25000; state <= DELAY; state_back <= INIT; cnt_init <= cnt_init + 1'b1; end	//延时大于220us
					5'd4:	begin 
								if(cnt>=INIT_DEPTH) begin	//当23条初始化指令及数据发出后,配置完成
										cnt <= 1'b0;
									cnt_init <= cnt_init + 1'b1;
								end else begin	
									cnt <= cnt + 1'b1; num_delay <= 16'd5;
									oled_dcn <= CMD; char_reg <= cmd[cnt]; state <= WRITE; state_back <= INIT;
									end
								end
					5'd5:	begin cnt_init <= 1'b0; state <= MAIN; end	//初始化完成,返回MAIN状态					
                	default:state<=IDLE;
                endcase
            end

			SCAN:begin
            	if(cnt_scan == 5'd11) begin
                	if(num) cnt_scan <= 5'd3;
					else cnt_scan <= cnt_scan + 1'b1;
                end
				else if(cnt_scan == 5'd12) cnt_scan <= 1'b0;
				else cnt_scan <= cnt_scan + 1'b1;
				case(cnt_scan)
					5'd00:	begin oled_dcn <= CMD; char_reg <= y_p; state <= WRITE; state_back <= SCAN; end		//定位列页地址
					5'd01:	begin oled_dcn <= CMD; char_reg <= x_pl; state <= WRITE; state_back <= SCAN; end	//定位行地址低位
					5'd02:	begin oled_dcn <= CMD; char_reg <= x_ph; state <= WRITE; state_back <= SCAN; end	//定位行地址高位

					5'd03:	begin num <= num - 1'b1;end
					5'd04:	begin oled_dcn <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end	//将5*8点阵转换为8*8显示
					5'd05:	begin oled_dcn <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end	//将5*8点阵转换为8*8显示
					5'd06:	begin oled_dcn <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end	//将5*8点阵转换为8*8显示
					5'd07:	begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][39:32]; state <= WRITE; state_back <= SCAN; end	
					5'd08:	begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][31:24]; state <= WRITE; state_back <= SCAN; end	
					5'd09:	begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][23:16]; state <= WRITE; state_back <= SCAN; end	
					5'd10:	begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][15: 8]; state <= WRITE; state_back <= SCAN; end
					5'd11:	begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][ 7: 0]; state <= WRITE; state_back <= SCAN; end
					5'd12:	begin state <= MAIN; end		
                	default: state <= IDLE;
                endcase			
            end

			WRITE:begin
            	if(cnt_write >= 5'd17) cnt_write <= 1'b0;
				else cnt_write <= cnt_write + 1'b1;
				
				case(cnt_write)
                	5'd00:	begin oled_csn <= LOW; end	//9位数据最高位为命令数据控制位
					5'd01:	begin oled_clk <= LOW; oled_dat <= char_reg[7]; end	//先发高位数据
					5'd02:	begin oled_clk <= HIGH; end
					5'd03:	begin oled_clk <= LOW; oled_dat <= char_reg[6]; end
					5'd04:	begin oled_clk <= HIGH; end
					5'd05:	begin oled_clk <= LOW; oled_dat <= char_reg[5]; end
					5'd06:	begin oled_clk <= HIGH; end
					5'd07:	begin oled_clk <= LOW; oled_dat <= char_reg[4]; end
					5'd08:	begin oled_clk <= HIGH; end
					5'd09:	begin oled_clk <= LOW; oled_dat <= char_reg[3]; end
					5'd10:	begin oled_clk <= HIGH; end
					5'd11:	begin oled_clk <= LOW; oled_dat <= char_reg[2]; end
					5'd12:	begin oled_clk <= HIGH; end
					5'd13:	begin oled_clk <= LOW; oled_dat <= char_reg[1]; end
					5'd14:	begin oled_clk <= HIGH; end
					5'd15:	begin oled_clk <= LOW; oled_dat <= char_reg[0]; end	//后发低位数据
					5'd16:	begin oled_clk <= HIGH; end
					5'd17:	begin oled_csn <= HIGH; state <= DELAY; end	//
					default: state <= IDLE;
                endcase
            end

			DELAY:begin
            	if(cnt_delay >= num_delay) begin
					cnt_delay <= 16'd0; state <= state_back; 
				end else cnt_delay <= cnt_delay + 1'b1;				
            end

			default:state <= IDLE;
       
        endcase
    end
end

代码中的state_back指定了当前状态机在运行结束后返回到哪个状态,便于管理状态机的切换。

程序系统上电后进入Idle状态,随后进入Main状态进行OLED初始化和显示逻辑管理。

Init状态实现OLED屏幕的初始化,扩展板上的OLED屏幕的初始化指令序列如下:

	//OLED初始化配置指令数据
	always@(posedge rst_n)
		begin
			cmd[0 ] = {8'hae}; //--turn off oled panel
			cmd[1 ] = {8'hd5}; //set display clock divide ratio/oscillator frequency
			cmd[2 ] = {8'h80}; //--set divide ratio, Set Clock as 100 Frames/Sec
			cmd[3 ] = {8'ha8}; //--set multiplex ratio(1 to 64)
			cmd[4 ] = {8'h3f}; //--1/64 duty
			cmd[5 ] = {8'hd3}; //-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
			cmd[6 ] = {8'h00}; //-not offset
			cmd[7 ] = {8'h40}; //--set start line address  Set Mapping RAM Display Start Line 
			cmd[8 ] = {8'h8d}; //--set Charge Pump enable/disable
			cmd[9 ] = {8'h14}; //--set(0x10) disable
			cmd[10] = {8'h20}; //-Set Page Addressing Mode (0x00/0x01/0x02)
			cmd[11] = {8'h02}; //
			cmd[12] = {8'hc8}; //Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
			cmd[13] = {8'ha1}; //--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
			cmd[14] = {8'hda}; //--set com pins hardware configuration
			cmd[15] = {8'h12}; //
			cmd[16] = {8'h81}; //--set contrast control register
			cmd[17] = {8'hcf}; // Set SEG Output Current Brightness
			cmd[18] = {8'hd9}; //--set pre-charge period
			cmd[19] = {8'hf1}; //Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
			cmd[20] = {8'hdb}; //--set vcomh
			cmd[21] = {8'h40}; //Set VCOM Deselect Level
			cmd[22] = {8'haf}; //--turn on oled panel
			
		end 

在Init状态中,通过控制OLED的RST引脚电平和Delay状态机实现对OLED的复位,随后通过设定state_back=Init,跳转到Write状态和Dealy状态(实现SPI通信时序),向OLED发送配置数据。在配置完成后,跳转回Main状态。

Main状态主要负责管理刷新到OLED屏幕的字符内容和显示的位置,确定要刷新的字符后,通过state_back=Main,跳转到Scan状态将字符数据发送到OLED屏幕。

Scan状态通过查找字库中字符对应的点阵信息,确定发送的数据,在通过Write和Delay状态将数据发送出去。

OLED屏幕的驱动过程大致如上。

1.3 OLED模块与DDS模块相结合

之前实现的DDS模块的控制了波形的类型、频率以及幅度三个参数,通过将这三个控制信号发送到OLED模块中,并通过对信号的判断,改变显示在OLED屏幕上的信息。OLED模块的定义如下:

module oeld_show( 
	clk_oled,
    rst_n,
    
    wave_type,
    wave_freqm,
	wave_amp,
    
    oled_csn,
    oled_rst,
    oled_dcn,
    oled_clk,
    oled_dat);

input clk_oled;
input rst_n;

input [2:0]  wave_type;
input [27:0] wave_freqm;
input [7:0]  wave_amp;

output reg oled_csn;
output reg oled_rst;
output reg oled_dcn;
output reg oled_clk;
output reg oled_dat;

对信号类型的判断,以及字符串更新程序如下:

reg [(3*8-1):0] 		dis_type_buff; //显示波形类型

always@(posedge clk_oled or negedge rst_n)begin
	if(!rst_n)begin
		dis_type_buff <= "Sin";
	end else begin
		case(wave_type)
			3'd0: dis_type_buff <= "Sin";
			3'd1: dis_type_buff <= "Squ";
			3'd2: dis_type_buff <= "Trg";
			3'd3: dis_type_buff <= "Saw";
		endcase
	end
end

虽然从逻辑上来说按照上述流程是可以实现信息的更新的,但是在调试的时候出现了一些意外,程序运行效果如视频所示:

动态刷新失败

屏幕信息显示错误的同时,DDS模块的运行也出现了问题,具体原因没有确定,问题应该是模块设计问题,我在其他的FPGA芯片上也尝试了,出现了相同的问题,就算降低OLED的驱动频率也不行。

1.4 总结

OLED的驱动模块虽然可以显示静态的文字,但是在动态更新时出现了问题,这一问题有待解决。

工程如下: DDS_Pareller_DAC_OLED.7z (2.43 MB, 下载次数: 14)
 

 

 

 

最新回复

DDS模块的运行也出现了问题,检测一下硬件连接   详情 回复 发表于 2023-4-3 19:24
点赞 关注
 
 

回复
举报

6807

帖子

0

TA的资源

五彩晶圆(高级)

沙发
 

DDS模块的运行也出现了问题,检测一下硬件连接

点评

我在另一块FPGA板子上运行了一样的程序,也是一样的效果,接线问题应该不是,后续要再调试了。  详情 回复 发表于 2023-4-3 20:59
 
 
 

回复

172

帖子

3

TA的资源

一粒金砂(高级)

板凳
 
Jacktang 发表于 2023-4-3 19:24 DDS模块的运行也出现了问题,检测一下硬件连接

我在另一块FPGA板子上运行了一样的程序,也是一样的效果,接线问题应该不是,后续要再调试了。

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表