forked from filipamator/vu_meter
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtft_ili9341.sv
More file actions
162 lines (137 loc) · 4.37 KB
/
tft_ili9341.sv
File metadata and controls
162 lines (137 loc) · 4.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/** Simple frame-buffer based driver for the ILI9341 TFT module */
module tft_ili9341(
input clk,
input tft_sdo,
output wire tft_sck,
output wire tft_sdi,
output wire tft_dc,
output reg tft_reset,
output wire tft_cs,
input[15:0] framebufferData,
output wire framebufferClk,
output px_count,
output wire fb_start
);
parameter INPUT_CLK_MHZ = 120; /* recommended */
// Initial assignments
initial tft_reset = 1'b1;
// Assign pins and modules
reg[8:0] spiData;
reg spiDataSet = 1'b0;
wire spiIdle;
reg frameBufferLowNibble = 1'b1;
assign framebufferClk = !frameBufferLowNibble;
assign px_count = PixelCounter;
tft_ili9341_spi spi(
.spiClk(clk),
.data(spiData),
.dataAvailable(spiDataSet),
.tft_sck(tft_sck),
.tft_sdi(tft_sdi),
.tft_dc(tft_dc),
.tft_cs(tft_cs),
.idle(spiIdle)
);
// Init Sequence Data (based upon https://github.com/notro/fbtft/blob/master/fb_ili9341.c)
localparam INIT_SEQ_LEN = 52;
reg[5:0] initSeqCounter = 6'b0;
reg[8:0] INIT_SEQ [0:INIT_SEQ_LEN-1] = '{
// Turn off Display
{1'b0, 8'h28},
// Init (??)
{1'b0, 8'hCF}, {1'b1, 8'h00}, {1'b1, 8'h83}, {1'b1, 8'h30},
{1'b0, 8'hED}, {1'b1, 8'h64}, {1'b1, 8'h03}, {1'b1, 8'h12}, {1'b1, 8'h81},
{1'b0, 8'hE8}, {1'b1, 8'h85}, {1'b1, 8'h01}, {1'b1, 8'h79},
{1'b0, 8'hCB}, {1'b1, 8'h39}, {1'b1, 8'h2C}, {1'b1, 8'h00}, {1'b1, 8'h34}, {1'b1, 8'h02},
{1'b0, 8'hF7}, {1'b1, 8'h20},
{1'b0, 8'hEA}, {1'b1, 8'h00}, {1'b1, 8'h00},
// Power Control
{1'b0, 8'hC0}, {1'b1, 8'h26},
{1'b0, 8'hC1}, {1'b1, 8'h11},
// VCOM
{1'b0, 8'hC5}, {1'b1, 8'h35}, {1'b1, 8'h3E},
{1'b0, 8'hC7}, {1'b1, 8'hBE},
// Memory Access Control
{1'b0, 8'h3A}, {1'b1, 8'h55},
// Frame Rate
{1'b0, 8'hB1}, {1'b1, 8'h00}, {1'b1, 8'h1B},
// Gamma
{1'b0, 8'h26}, {1'b1, 8'h01},
// Brightness
{1'b0, 8'h51}, {1'b1, 8'hFF},
// Display
{1'b0, 8'hB7}, {1'b1, 8'h07},
{1'b0, 8'hB6}, {1'b1, 8'h0A}, {1'b1, 8'h82}, {1'b1, 8'h27}, {1'b1, 8'h00},
{1'b0, 8'h29}, // Enable Display
{1'b0, 8'h2C} // Start Memory-Write
};
// state machine with delay + idle support (used for initialization)
reg[23:0] remainingDelayTicks = 24'b0;
reg[16:0] PixelCounter = 17'b0;
enum logic[2:0] { START, HOLD_RESET, WAIT_FOR_POWERUP, SEND_INIT_SEQ, LOOP} state = START;
always @ (posedge clk) begin
// clear data flag first
spiDataSet <= 1'b0;
// always decrement delay ticks
if (remainingDelayTicks > 0) begin
remainingDelayTicks <= remainingDelayTicks - 1'b1;
end
else if (spiIdle && !spiDataSet) begin
// advance state machine to next state, but only do this if we
// didn't just clock in the last byte (since idle is not yet updated)
case (state)
// initialize all pins in START mode; reset the LCD
START: begin
tft_reset <= 1'b0;
remainingDelayTicks <= 24'(INPUT_CLK_MHZ * 10); // min: 10us
state <= HOLD_RESET;
end
// wait for RESET to kick in; then release pin & wait for power up
HOLD_RESET: begin
tft_reset <= 1'b1; // release pin
remainingDelayTicks <= 24'(INPUT_CLK_MHZ * 120000); // min: 120ms
state <= WAIT_FOR_POWERUP;
frameBufferLowNibble <= 1'b0; // request first pixel
end
// if power up is completed -> sw reset
WAIT_FOR_POWERUP: begin
spiData <= {1'b0, 8'h11}; // take out of sleep mode
spiDataSet <= 1'b1;
remainingDelayTicks <= 24'(INPUT_CLK_MHZ * 5000); // min: 5ms
state <= SEND_INIT_SEQ;
frameBufferLowNibble <= 1'b1;
PixelCounter <= PixelCounter + 1'b1;
end
// setup the LCD by sending the init sequence
SEND_INIT_SEQ: begin
if (initSeqCounter < INIT_SEQ_LEN) begin
spiData <= INIT_SEQ[initSeqCounter];
spiDataSet <= 1'b1;
initSeqCounter <= initSeqCounter + 1'b1;
end else begin
state <= LOOP;
remainingDelayTicks <= 24'(INPUT_CLK_MHZ * 10000); // min: 10ms
end
end
// frame buffer loop
default: begin
spiData <= !frameBufferLowNibble ? {1'b1, framebufferData[15:8]} :{1'b1, framebufferData[7:0]};
spiDataSet <= 1'b1;
frameBufferLowNibble <= !frameBufferLowNibble;
if (frameBufferLowNibble)
begin
PixelCounter <= (PixelCounter + 1'b1) % 17'(320*240);
end
if (PixelCounter == 76799)
begin
fb_start <= 1'b1;
end
else
begin
fb_start <= 1'b0;
end
end
endcase
end
end
endmodule