Skip to content

Commit fe67ab5

Browse files
committed
Add demo for OLED displays
1 parent a2e7720 commit fe67ab5

File tree

8 files changed

+686
-9
lines changed

8 files changed

+686
-9
lines changed

fpga/README.md

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
# FPGA
22

3-
TODO:
4-
5-
oled
6-
73
## Setup
84

95
You will need Verilog setup from the [previous step](../verilog/README.md).
@@ -151,15 +147,13 @@ developed by Tim Goddard](https://github.com/cyrozap/osdvu).
151147
I have two OLED screens:
152148

153149
- "Two-color" (actually monochrome) 128x64 screen. The data is laid out in 8
154-
"pages" of 128 bytes each. Each page describes a 128x8 strip, each byte is a
155-
1x8 segment.
150+
rows of 128 bytes each. Each row describes a 128x8 strip, each byte is a 1x8
151+
segment.
156152
- 65536-color 96x64 screen. Each pixel is 16 bits. Note that this is more
157153
memory that Icestick has on board (12 KB; the Icestick's block RAMs hold 8 KB
158154
total).
159155

160-
(TODO get the color display running)
161-
162-
See `oled_mono.v` and `oled_color.v` for details on how to use.
156+
See `oled_pattern.v` and `oled_pattern_color.v` for details on how to use.
163157

164158
You might want to load some initial data into memory. You can use the
165159
[`$readmemh`

fpga/oled.v

Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
`include "ssd1306.v"
2+
`include "ssd1331.v"
3+
4+
module oled(input wire clk,
5+
input wire pin_din,
6+
input wire pin_clk,
7+
input wire pin_cs,
8+
input wire pin_dc,
9+
input wire pin_res,
10+
11+
output wire read,
12+
output wire [5:0] row_idx,
13+
output wire [6:0] column_idx,
14+
// mono: 8-pixel vertical strip
15+
input wire [7:0] data,
16+
// color: 5+6+5 bit RGB value
17+
input wire [15:0] data_rgb,
18+
input wire ack);
19+
20+
parameter color = 0;
21+
22+
wire transmit;
23+
wire is_data;
24+
wire [7:0] tx_byte;
25+
wire ready;
26+
27+
oled_spi spi(.clk(clk),
28+
.transmit(transmit),
29+
.is_data(is_data),
30+
.tx_byte(tx_byte),
31+
.ready(ready),
32+
.pin_din(pin_din),
33+
.pin_clk(pin_clk),
34+
.pin_cs(pin_cs),
35+
.pin_dc(pin_dc));
36+
37+
oled_controller #(.color(color))
38+
controller(.clk(clk),
39+
.pin_res(pin_res),
40+
.spi_transmit(transmit),
41+
.spi_is_data(is_data),
42+
.spi_tx_byte(tx_byte),
43+
.spi_ready(ready),
44+
.read(read),
45+
.row_idx(row_idx),
46+
.column_idx(column_idx),
47+
.data(data),
48+
.data_rgb(data_rgb),
49+
.ack(ack));
50+
51+
endmodule
52+
53+
module oled_spi(input wire clk,
54+
input wire transmit,
55+
input wire is_data,
56+
input wire [7:0] tx_byte,
57+
output wire ready,
58+
59+
output wire pin_din,
60+
output wire pin_clk,
61+
output wire pin_cs,
62+
output reg pin_dc);
63+
assign pin_clk = clk;
64+
65+
reg [7:0] data;
66+
reg [3:0] data_counter = 0;
67+
wire transmitting = data_counter > 0;
68+
assign pin_din = data[7];
69+
assign pin_cs = !transmitting;
70+
71+
assign ready = !transmit && !transmitting;
72+
73+
always @(posedge clk) begin
74+
if (transmit && !transmitting) begin
75+
data <= tx_byte;
76+
pin_dc <= is_data;
77+
data_counter <= 8;
78+
end
79+
80+
if (transmitting) begin
81+
data <= data << 1;
82+
data_counter <= data_counter - 1;
83+
end
84+
end
85+
endmodule
86+
87+
module oled_controller(input wire clk,
88+
output reg pin_res,
89+
90+
output reg spi_transmit,
91+
output reg spi_is_data,
92+
output reg [7:0] spi_tx_byte,
93+
input wire spi_ready,
94+
95+
output reg read,
96+
output reg [5:0] row_idx,
97+
output reg [6:0] column_idx,
98+
input wire [7:0] data,
99+
input wire [15:0] data_rgb,
100+
input wire ack);
101+
102+
parameter color = 0;
103+
104+
localparam N_INIT_COMMANDS = color ? 38 : 25;
105+
localparam N_REFRESH_COMMANDS = 6;
106+
localparam WIDTH = color ? 96 : 128;
107+
localparam HEIGHT = color ? 64 : 8;
108+
109+
localparam
110+
STATE_RESET = 0,
111+
STATE_INIT = 1,
112+
STATE_IDLE = 2,
113+
STATE_REFRESH_BEGIN = 3,
114+
STATE_REFRESH_DATA = 4,
115+
STATE_TRANSMIT_LOWER_BYTE = 5,
116+
STATE_ADVANCE = 6;
117+
118+
reg [7:0] commands[0:N_INIT_COMMANDS+N_REFRESH_COMMANDS-1];
119+
reg [8:0] command_idx = 0;
120+
reg [7:0] command;
121+
reg send_command = 0;
122+
123+
reg [7:0] data_rgb_lower_byte;
124+
125+
reg [3:0] state = STATE_RESET;
126+
127+
integer i;
128+
129+
initial begin
130+
i = -1;
131+
132+
if (color) begin
133+
// Init commands
134+
i++; commands[i] <= `SSD1331_DISPLAY_OFF;
135+
i++; commands[i] <= `SSD1331_SET_CONTRAST_A;
136+
i++; commands[i] <= 'hFF;
137+
i++; commands[i] <= `SSD1331_SET_CONTRAST_B;
138+
i++; commands[i] <= 'hFF;
139+
i++; commands[i] <= `SSD1331_SET_CONTRAST_C;
140+
i++; commands[i] <= 'hFF;
141+
i++; commands[i] <= `SSD1331_MASTER_CURRENT_CONTROL;
142+
i++; commands[i] <= 'h06;
143+
i++; commands[i] <= `SSD1331_SET_PRECHARGE_SPEED_A;
144+
i++; commands[i] <= 'h64;
145+
i++; commands[i] <= `SSD1331_SET_PRECHARGE_SPEED_B;
146+
i++; commands[i] <= 'h78;
147+
i++; commands[i] <= `SSD1331_SET_PRECHARGE_SPEED_C;
148+
i++; commands[i] <= 'h64;
149+
i++; commands[i] <= `SSD1331_SET_REMAP;
150+
i++; commands[i] <= 'h72;
151+
i++; commands[i] <= `SSD1331_SET_DISPLAY_START_LINE;
152+
i++; commands[i] <= 'h00;
153+
i++; commands[i] <= `SSD1331_SET_DISPLAY_OFFSET;
154+
i++; commands[i] <= 'h00;
155+
i++; commands[i] <= `SSD1331_NORMAL_DISPLAY;
156+
i++; commands[i] <= `SSD1331_SET_MULTIPLEX_RATIO;
157+
i++; commands[i] <= 'h3F;
158+
i++; commands[i] <= `SSD1331_SET_MASTER_CONFIGURE;
159+
i++; commands[i] <= 'h8E;
160+
i++; commands[i] <= `SSD1331_POWER_SAVE_MODE;
161+
i++; commands[i] <= 'h00;
162+
i++; commands[i] <= `SSD1331_PHASE_PERIOD_ADJUSTMENT;
163+
i++; commands[i] <= 'h31;
164+
i++; commands[i] <= `SSD1331_DISPLAY_CLOCK_DIV;
165+
i++; commands[i] <= 'hF0;
166+
i++; commands[i] <= `SSD1331_SET_PRECHARGE_VOLTAGE;
167+
i++; commands[i] <= 'h3A;
168+
i++; commands[i] <= `SSD1331_SET_V_VOLTAGE;
169+
i++; commands[i] <= 'h3E;
170+
i++; commands[i] <= `SSD1331_DEACTIVE_SCROLLING;
171+
i++; commands[i] <= `SSD1331_NORMAL_BRIGHTNESS_DISPLAY_ON;
172+
173+
// Refresh commands
174+
i++; commands[i] <= `SSD1331_SET_COLUMN_ADDRESS;
175+
i++; commands[i] <= 0;
176+
i++; commands[i] <= WIDTH - 1;
177+
i++; commands[i] <= `SSD1331_SET_ROW_ADDRESS;
178+
i++; commands[i] <= 0;
179+
i++; commands[i] <= HEIGHT - 1;
180+
end else begin
181+
// Init commands
182+
i++; commands[i] <= `SSD1306_DISPLAYOFF;
183+
i++; commands[i] <= `SSD1306_SETDISPLAYCLOCKDIV;
184+
i++; commands[i] <= 'h80;
185+
i++; commands[i] <= `SSD1306_SETMULTIPLEX;
186+
i++; commands[i] <= 'h3F;
187+
i++; commands[i] <= `SSD1306_SETDISPLAYOFFSET;
188+
i++; commands[i] <= 'h00;
189+
i++; commands[i] <= `SSD1306_SETSTARTLINE | 'h00;
190+
i++; commands[i] <= `SSD1306_CHARGEPUMP;
191+
i++; commands[i] <= 'h14;
192+
i++; commands[i] <= `SSD1306_MEMORYMODE;
193+
i++; commands[i] <= 'h00;
194+
i++; commands[i] <= `SSD1306_SEGREMAP | 'h01;
195+
i++; commands[i] <= `SSD1306_COMSCANDEC;
196+
i++; commands[i] <= `SSD1306_SETCOMPINS;
197+
i++; commands[i] <= 'h12;
198+
i++; commands[i] <= `SSD1306_SETCONTRAST;
199+
i++; commands[i] <= 'h70;
200+
i++; commands[i] <= `SSD1306_SETPRECHARGE;
201+
i++; commands[i] <= 'hF1;
202+
i++; commands[i] <= `SSD1306_SETVCOMDETECT;
203+
i++; commands[i] <= 'h40;
204+
i++; commands[i] <= `SSD1306_DISPLAYALLON_RESUME;
205+
i++; commands[i] <= `SSD1306_NORMALDISPLAY;
206+
i++; commands[i] <= `SSD1306_DISPLAYON;
207+
208+
// Refresh commands
209+
i++; commands[i] <= `SSD1306_COLUMNADDR;
210+
i++; commands[i] <= 0;
211+
i++; commands[i] <= WIDTH - 1;
212+
i++; commands[i] <= `SSD1306_PAGEADDR;
213+
i++; commands[i] <= 0;
214+
i++; commands[i] <= HEIGHT - 1;
215+
end
216+
end
217+
218+
always @(posedge clk) begin
219+
command <= commands[command_idx];
220+
if (send_command) begin
221+
send_command <= 0;
222+
spi_transmit <= 1;
223+
spi_is_data <= 0;
224+
spi_tx_byte <= command;
225+
end else begin
226+
spi_transmit <= 0;
227+
pin_res <= 1;
228+
case (state)
229+
STATE_RESET: begin
230+
if (spi_ready) begin
231+
pin_res <= 0;
232+
state <= STATE_INIT;
233+
command_idx <= 0;
234+
end
235+
end
236+
STATE_INIT: begin
237+
if (spi_ready) begin
238+
send_command <= 1;
239+
240+
command_idx <= command_idx + 1;
241+
if (command_idx+1 == N_INIT_COMMANDS) begin
242+
state <= STATE_REFRESH_BEGIN;
243+
end
244+
end
245+
end
246+
STATE_REFRESH_BEGIN: begin
247+
if (spi_ready) begin
248+
if (command_idx < N_INIT_COMMANDS + N_REFRESH_COMMANDS) begin
249+
send_command <= 1;
250+
command_idx <= command_idx + 1;
251+
end else begin
252+
state <= STATE_REFRESH_DATA;
253+
row_idx <= 0;
254+
column_idx <= 0;
255+
read <= 1;
256+
end
257+
end
258+
end
259+
STATE_REFRESH_DATA: begin
260+
read <= 0;
261+
if (ack) begin
262+
spi_transmit <= 1;
263+
spi_is_data <= 1;
264+
if (color) begin
265+
spi_tx_byte <= data_rgb[15:8];
266+
data_rgb_lower_byte <= data_rgb[7:0];
267+
state <= STATE_TRANSMIT_LOWER_BYTE;
268+
end else begin
269+
spi_tx_byte <= data;
270+
state <= STATE_ADVANCE;
271+
end
272+
end
273+
end
274+
STATE_TRANSMIT_LOWER_BYTE: begin
275+
if (color) begin
276+
if (spi_ready) begin
277+
spi_transmit <= 1;
278+
spi_is_data <= 1;
279+
spi_tx_byte <= data_rgb_lower_byte;
280+
state <= STATE_ADVANCE;
281+
end
282+
end
283+
end
284+
STATE_ADVANCE: begin
285+
if (spi_ready) begin
286+
if (column_idx == WIDTH - 1 && row_idx == HEIGHT - 1) begin
287+
state <= STATE_REFRESH_BEGIN;
288+
command_idx <= N_INIT_COMMANDS;
289+
end else begin
290+
read <= 1;
291+
state <= STATE_REFRESH_DATA;
292+
column_idx <= column_idx + 1;
293+
if (column_idx == WIDTH - 1) begin
294+
column_idx <= 0;
295+
if (color)
296+
row_idx <= row_idx + 1;
297+
else
298+
row_idx <= row_idx + 1;
299+
end
300+
end
301+
end
302+
end
303+
endcase
304+
end
305+
end
306+
endmodule

0 commit comments

Comments
 (0)