OpenCores
URL https://opencores.org/ocsvn/lpd8806/lpd8806/trunk

Subversion Repositories lpd8806

[/] [lpd8806/] [trunk/] [lpd8806_pack.vhd] - Blame information for rev 3

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 3 jclaytons
--------------------------------------------------------------------------
2
-- Package of dds components
3
--
4
--
5
 
6
library ieee;
7
use ieee.std_logic_1164.all;
8
use ieee.numeric_std.all;
9
 
10
package lpd8806_pack is
11
 
12
  component lpd8806_LED_chain_driver
13
    generic (
14
      SCLK_FREQ    : real;    -- Desired lpd8806 serial clock frequency
15
      UPDATE_FREQ  : real;    -- Desired LED chain update frequency
16
      SYS_CLK_RATE : real;    -- underlying clock rate
17
      N_LEDS       : integer  -- Number of LEDs in chain
18
    );
19
    port (
20
 
21
      -- System Clock, Reset and Clock Enable
22
      sys_rst_n  : in  std_logic;
23
      sys_clk    : in  std_logic;
24
      sys_clk_en : in  std_logic;
25
 
26
      -- Selection of color information
27
      c_adr_o    : out unsigned(7 downto 0);
28
      c_dat_i    : in  unsigned(6 downto 0);
29
 
30
      -- Output
31
      sclk_o     : out std_logic;
32
      sdat_o     : out std_logic
33
    );
34
  end component;
35
 
36
  component spi_byte_writer
37
    generic (
38
      SCLK_FREQ    : real;    -- Desired lpd8806 serial clock frequency
39
      SYS_CLK_RATE : real     -- underlying clock rate
40
    );
41
    port (
42
 
43
      -- System Clock, Reset and Clock Enable
44
      sys_rst_n  : in  std_logic;
45
      sys_clk    : in  std_logic;
46
      sys_clk_en : in  std_logic;
47
 
48
      -- Data to send.
49
      sel_i      : in  std_logic;
50
      we_i       : in  std_logic;
51
      dat_i      : in  unsigned(7 downto 0);
52
 
53
      -- Output
54
      ssel_o     : out std_logic;
55
      sclk_o     : out std_logic;
56
      sdat_o     : out std_logic
57
    );
58
  end component;
59
 
60
end lpd8806_pack;
61
 
62
package body lpd8806_pack is
63
end lpd8806_pack;
64
 
65
-------------------------------------------------------------------------------
66
-- LPD8806 "GRB" LED chain driver module
67
-------------------------------------------------------------------------------
68
--
69
-- Author: John Clayton
70
-- Update: Apr.  8, 2013 Started Coding, wrote description.
71
--         Apr. 10, 2013 Simulated and tested in hardware, with
72
--         semi-satisfactory results.  All is not yet well.
73
--
74
-- Description
75
-------------------------------------------------------------------------------
76
-- This module outputs a serial stream of clock and data pulses which
77
-- are intended for driving a chain of LPD8806 LED PWM driver ICs.
78
--
79
-- This type of LED driver is commonly sold pre-built into strips
80
-- of LEDs, with each LPD8806 circuit driving two multi-color LEDs.
81
--
82
-- The LEDs are driven with 21-bit color, that is 7 bits for red
83
-- intensity, 7 bits for green intensity and 7 bits for blue intensity.
84
--
85
-- I stopped short of calling it an "RGB LED driver" since lpd8806
86
-- receives the data in the order "GRB."  The ordering of the color
87
-- bits should not really matter anyway, so just be happy, OK?
88
--
89
-- The first bit, the MSB, is set for updating the LED colors, and cleared
90
-- for resetting the drivers.  Therefore, after each sequence of color
91
-- information bits, about 24 bits of zero are sent out to reset
92
-- everything in order to be ready for the next update.
93
--
94
-- This module generates its own serial bit clock by using a DDS based
95
-- on the underlying system clock rate.  It has been suggested that
96
-- 2 MHz is a good number, although I'll bet many other speeds will
97
-- also work, both higher and lower.
98
--
99
-- Another DDS generates the update rate pulse, which kicks off
100
-- the updating process.  Beware, if one sets the update rate too
101
-- high, it may become impossible to transmit all the required color
102
-- bits, and so the LEDs at the end of the chain may get left out
103
-- of the process!
104
--
105
-- Yes, that is correct, the LEDs closest to the source of SCLK
106
-- and SDAT get lit first, then they become "passthrough" so that
107
-- the next one gets set, and so forth.
108
--
109
-- This module latches color information from an external source.
110
-- It provides the c_adr_o signal to specify which information
111
-- should be selected.  In this way, the module can be used with
112
-- different numbers of drivers and LED pairs in the strip.
113
--
114
-- Just to keep things on an even keel, the c_adr_o address
115
-- advances according to the following pattern:
116
--
117
-- 0,1,2,4,5,6,8,9,A,C,D,E...
118
--
119
-- What is happening is that one address per LED is getting skipped
120
-- or "wasted" in order to start with each LEDs green value on an
121
-- even multiple N*4, where N is the LED number, beginning with zero
122
-- for the "zeroth" LED.  Then the red values are at N*4+1, while
123
-- the blue values are at N*4+2.  The N*4+3 values are simply skipped.
124
-- Isn't that super organized?
125
--
126
--
127
 
128
 
129
library IEEE;
130
use IEEE.STD_LOGIC_1164.ALL;
131
use IEEE.NUMERIC_STD.ALL;
132
use IEEE.MATH_REAL.ALL;
133
 
134
library work;
135
use work.dds_pack.all;
136
 
137
  entity lpd8806_LED_chain_driver is
138
    generic (
139
      SCLK_FREQ    : real;    -- Desired lpd8806 serial clock frequency
140
      UPDATE_FREQ  : real;    -- Desired LED chain update frequency
141
      SYS_CLK_RATE : real;    -- underlying clock rate
142
      N_LEDS       : integer  -- Number of LEDs in chain
143
    );
144
    port (
145
 
146
      -- System Clock, Reset and Clock Enable
147
      sys_rst_n  : in  std_logic;
148
      sys_clk    : in  std_logic;
149
      sys_clk_en : in  std_logic;
150
 
151
      -- Selection of color information
152
      c_adr_o    : out unsigned(7 downto 0);
153
      c_dat_i    : in  unsigned(6 downto 0);
154
 
155
      -- Output
156
      sclk_o     : out std_logic;
157
      sdat_o     : out std_logic
158
    );
159
    end lpd8806_LED_chain_driver;
160
 
161
architecture beh of lpd8806_LED_chain_driver is
162
 
163
-- Constants
164
constant N_ZERO_BYTES : natural := 3;
165
 
166
-- Signals
167
signal sclk            : std_logic;
168
signal sclk_pulse      : std_logic;
169
signal update_pulse    : std_logic;
170
signal bit_count       : unsigned(3 downto 0);
171
signal byte_count      : unsigned(7 downto 0); -- Widen for chains of 64+ LEDs.
172
signal c_adr           : unsigned(7 downto 0); -- Widen for chains of 64+ LEDs.
173
signal c_dat           : unsigned(7 downto 0);
174
 
175
-----------------------------------------------------------------------------
176
begin
177
 
178
  c_adr_proc: Process(sys_rst_n,sys_clk)
179
  begin
180
    if (sys_rst_n = '0') then
181
      byte_count <= (others=>'0');
182
      c_adr      <= (others=>'0');
183
      c_dat      <= (others=>'0');
184
      bit_count  <= (others=>'0');
185
    elsif (sys_clk'event and sys_clk='1') then
186
      if (sys_clk_en='1') then
187
        if (byte_count=0) then
188
          c_adr <= (others=>'0');
189
        end if;
190
        if (byte_count>0 and sclk_pulse='1') then
191
          bit_count <= bit_count-1;
192
          if (bit_count<8) then
193
            c_dat <= c_dat(6 downto 0) & '0'; -- Default is to shift color data
194
            if (bit_count=0) then
195
              byte_count <= byte_count-1;
196
              bit_count  <= to_unsigned(7,bit_count'length);
197
              -- Set color data MSB appropriately
198
              if (byte_count<=4) then
199
                c_dat <= (others=>'0');
200
              else
201
                c_dat <= '1' & c_dat_i;
202
              end if;
203
              -- Cause c_adr to skip every address.ending in "11"
204
              if (c_adr(1 downto 0)="10") then
205
                c_adr <= c_adr+2;
206
              else
207
                c_adr <= c_adr+1;
208
              end if;
209
            end if;
210
          end if;
211
        end if;
212
        -- Update pulse gets highest priority
213
        if (update_pulse='1') then
214
          byte_count <= to_unsigned(N_LEDS*3+1+N_ZERO_BYTES,byte_count'length);
215
          if (sclk_pulse='1') then
216
            bit_count  <= to_unsigned(7,bit_count'length);
217
          else
218
            bit_count  <= to_unsigned(8,bit_count'length); -- means "pending"
219
          end if;
220
          c_dat      <= '1' & c_dat_i;
221
          c_adr      <= c_adr+1;
222
        end if;
223
      end if;
224
    end if; -- sys_clk
225
  end process;
226
  sdat_o <= c_dat(7) when (byte_count>0 and bit_count<8) else '0';
227
  c_adr_o <= c_adr;
228
 
229
  -------------------------
230
  -- Update pulse generation
231
  update_gen: dds_constant_squarewave
232
    generic map(
233
      OUTPUT_FREQ  => UPDATE_FREQ,  -- Desired output frequency
234
      SYS_CLK_RATE => SYS_CLK_RATE, -- underlying clock rate
235
      ACC_BITS     => 24 -- Bit width of DDS phase accumulator
236
    )
237
    port map(
238
 
239
      sys_rst_n    => sys_rst_n,
240
      sys_clk      => sys_clk,
241
      sys_clk_en   => sys_clk_en,
242
 
243
      -- Output
244
      pulse_o      => update_pulse,
245
      squarewave_o => open
246
    );
247
 
248
  -------------------------
249
  -- Serial clock generation
250
  sclk_gen: dds_constant_squarewave
251
    generic map(
252
      OUTPUT_FREQ  => SCLK_FREQ,   -- Desired output frequency
253
      SYS_CLK_RATE => SYS_CLK_RATE, -- underlying clock rate
254
      ACC_BITS     => 16 -- Bit width of DDS phase accumulator
255
    )
256
    port map(
257
 
258
      sys_rst_n    => sys_rst_n,
259
      sys_clk      => sys_clk,
260
      sys_clk_en   => sys_clk_en,
261
 
262
      -- Output
263
      pulse_o      => sclk_pulse,
264
      squarewave_o => sclk
265
    );
266
  sclk_o <= not sclk when (byte_count>0 and bit_count<8) else '0';
267
 
268
end beh;
269
 
270
 
271
-------------------------------------------------------------------------------
272
-- SPI byte writer module
273
-------------------------------------------------------------------------------
274
--
275
-- Author: John Clayton
276
-- Update: Apr.  8, 2013 Started Coding, wrote description.
277
--
278
-- Description
279
-------------------------------------------------------------------------------
280
-- I conceived of this module while debugging a serial driver for a
281
-- string of LPD8806 LED drivers.  It essentially latches the input
282
-- data, and sends it out serially at the desired rate.  When the byte
283
-- is fully transmitted, it stops and waits for the next byte.
284
--
285
-- The output clock is gated by ssel_o.  If you need a continuous
286
-- clock, please ungate it yourself.
287
--
288
-- Unlike with asynchronous data which shift out the lsb first, this
289
-- unit shifts out the msb first.
290
--
291
 
292
 
293
library IEEE;
294
use IEEE.STD_LOGIC_1164.ALL;
295
use IEEE.NUMERIC_STD.ALL;
296
use IEEE.MATH_REAL.ALL;
297
 
298
library work;
299
use work.dds_pack.all;
300
 
301
  entity spi_byte_writer is
302
    generic (
303
      SCLK_FREQ    : real;    -- Desired lpd8806 serial clock frequency
304
      SYS_CLK_RATE : real     -- underlying clock rate
305
    );
306
    port (
307
 
308
      -- System Clock, Reset and Clock Enable
309
      sys_rst_n  : in  std_logic;
310
      sys_clk    : in  std_logic;
311
      sys_clk_en : in  std_logic;
312
 
313
      -- Data to send.
314
      sel_i      : in  std_logic;
315
      we_i       : in  std_logic;
316
      dat_i      : in  unsigned(7 downto 0);
317
 
318
      -- Output
319
      ssel_o     : out std_logic;
320
      sclk_o     : out std_logic;
321
      sdat_o     : out std_logic
322
    );
323
    end spi_byte_writer;
324
 
325
architecture beh of spi_byte_writer is
326
 
327
-- Signals
328
signal sclk            : std_logic;
329
signal sclk_pulse      : std_logic;
330
signal ssel            : std_logic;
331
signal sdat            : unsigned(7 downto 0);
332
signal bit_count       : unsigned(3 downto 0);
333
 
334
-----------------------------------------------------------------------------
335
begin
336
 
337
  spi_write_proc: Process(sys_rst_n,sys_clk)
338
  begin
339
    if (sys_rst_n = '0') then
340
      sdat      <= (others=>'0');
341
      bit_count <= (others=>'0');
342
    elsif (sys_clk'event and sys_clk='1') then
343
      if (sys_clk_en='1') then
344
        if (sclk_pulse='1') then
345
          if (bit_count>0) then
346
            bit_count <= bit_count-1;
347
          end if;
348
          if ssel='1' then
349
            sdat <= sdat(6 downto 0) & '0';
350
          end if;
351
        elsif (sel_i='1' and we_i='1') then
352
          if (sclk_pulse='1') then
353
            bit_count  <= to_unsigned(8,bit_count'length);
354
          else
355
            bit_count  <= to_unsigned(9,bit_count'length); -- means "pending"
356
          end if;
357
          sdat <= dat_i;
358
        end if;
359
      end if;
360
    end if; -- sys_clk
361
  end process;
362
  sdat_o <= sdat(7) when ssel='1' else '0';
363
 
364
  -------------------------
365
  -- Serial clock generation
366
  sclk_gen: dds_constant_squarewave
367
    generic map(
368
      OUTPUT_FREQ  => SCLK_FREQ,   -- Desired output frequency
369
      SYS_CLK_RATE => SYS_CLK_RATE, -- underlying clock rate
370
      ACC_BITS     => 16 -- Bit width of DDS phase accumulator
371
    )
372
    port map(
373
 
374
      sys_rst_n    => sys_rst_n,
375
      sys_clk      => sys_clk,
376
      sys_clk_en   => sys_clk_en,
377
 
378
      -- Output
379
      pulse_o      => sclk_pulse,
380
      squarewave_o => sclk
381
    );
382
  sclk_o <= not sclk when ssel='1' else '0';
383
  ssel   <= '1'  when (bit_count<9 and bit_count>0) else '0';
384
  ssel_o <= ssel;
385
 
386
end beh;
387
 
388
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.