Close

Another blinky: dipsy as SPI slave with PWM output

A project log for DIPSY

DIY System on Chip based on FPGA, priced below 5 USD

christophChristoph 09/07/2015 at 14:471 Comment

Let's start with the result:

You can see a Teensy 3.1 and a dipsy connected to it. The Teensy configures the Dipsy and then sends single bytes over the SPI to set LED brightness. Here's the teensy code:

#include <SPI.h>
#include <dipsydownloader.h>

extern const uint8_t dipsy_bitmap[];
extern const uint16_t dipsy_bitmap_size;

static const uint8_t DIPSY_CRESETB = 0;
static const uint8_t DIPSY_CDONE = 1;
static const uint8_t DIPSY_SS = 2;

void setup() {
  while(!Serial.available());
  while(Serial.available()) Serial.read();

  SPI.begin();
  dipsy::ArrayConfig config(dipsy_bitmap_size, dipsy_bitmap);
  if (dipsy::configure(DIPSY_CRESETB, DIPSY_CDONE, DIPSY_SS, SPI, config))
  {
    Serial.print("config done");
  }
  else
  {
    Serial.print("config error");
    while(1);
  }

  SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE3));
  pinMode(DIPSY_SS, OUTPUT);
  digitalWriteFast(DIPSY_SS, 1);
  delay(10);
}

void loop() {
  uint8_t val = 0;
  digitalWriteFast(DIPSY_SS, 0);
  SPI.transfer(val);
  digitalWriteFast(DIPSY_SS, 1);
  Serial.println(val);
  delay(1000);

  val = 127;
  digitalWriteFast(DIPSY_SS, 0);
  SPI.transfer(val);
  digitalWriteFast(DIPSY_SS, 1);
  Serial.println(val);
  delay(1000);

  val = 255;
  digitalWriteFast(DIPSY_SS, 0);
  SPI.transfer(val);
  digitalWriteFast(DIPSY_SS, 1);
  Serial.println(val);
  delay(1000);
}
After configuring the dipsy, the Teensy enters a single loop where it selects the slave and sends a single byte before the slave is deselected again. The dipsy code is probably more interesting.

The top level (top.v):

module top(
  input SCK,
  input MOSI,
  input CS,
  output LED
);

wire SYSCLK;
// hard IP, 84 MHz oscillator divided down to 6 MHz
SB_HFOSC #(.CLKHF_DIV("0b11")) osc(
  .CLKHFEN(1'b1),
  .CLKHFPU(1'b1),
  .CLKHF(SYSCLK)
) /* synthesis ROUTE_THROUGH_FABRIC= 0 */;


wire[7:0] DUTY_CYCLE;
simple_spi_8bit_mode3 spi(
  .SCK(SCK),
  .MOSI(MOSI),
  .CS(CS),
  .DOUT(DUTY_CYCLE)
);

// connects PWM generator to LED driver
wire PWM;
simple_pwm pwm(
  .CLK(SYSCLK),
  .DUTY_CYCLE(DUTY_CYCLE),
  .PWM(PWM)
);

// again hard IP, constant current driver with pwm input
SB_RGBA_DRV RGBA_DRIVER (
.CURREN(1'b1),
.RGBLEDEN(1'b1),
.RGB0PWM(PWM),
.RGB1PWM(1'b0),
.RGB2PWM(1'b0),
.RGB0(LED)
);
defparam RGBA_DRIVER.CURRENT_MODE = "0b0";
defparam RGBA_DRIVER.RGB0_CURRENT = "0b1";
defparam RGBA_DRIVER.RGB1_CURRENT = "0b1";
defparam RGBA_DRIVER.RGB2_CURRENT = "0b1";

endmodule
Here we have the modules I/O (SPI SCK, MOSI, CS and PWM out) and some submodules:

The SPI is a shift register with a latch (simple_spi_8bit_mode3.v):

module simple_spi_8bit_mode3(
  input SCK,
  input MOSI,
  output MISO,
  input CS,
  output reg[7:0] DOUT
);

reg[7:0] shiftreg = 0;

assign MISO = CS ? 1'bz : shiftreg[7];

always @(posedge CS)
begin
  DOUT = shiftreg;
end

always @(posedge SCK)
if(!CS)
begin
  shiftreg = {shiftreg[6:0],MOSI};
end
endmodule
And the PWM is a counter and a comparator. It latches when it rolls over to zero to avoid glitches (simple_pwm.v):
module simple_pwm
(
  CLK,
  DUTY_CYCLE,
  PWM
);

parameter BITS = 8;
parameter TOP = 2**BITS-1;

input CLK;
input[BITS-1:0] DUTY_CYCLE;
output PWM;

reg[BITS-1:0] count = TOP;
reg[BITS-1:0] duty_cycle_i = 0;
assign PWM = (count < duty_cycle_i) ? 1'b1:1'b0;

always @(posedge CLK)
begin
  count = count + "1";
  if(count == (TOP))
  begin
    count = 0;
    duty_cycle_i = DUTY_CYCLE;
  end
end
endmodule
Pin assignments:
###IOSet List 3
set_io CS D1
set_io MOSI D2
set_io SCK C1 -io_std SB_LVCMOS -pullup no
I'm not really sure how these *should* look, but these settings worked for me.

Now this might not be overly impressive, but it shows that dipsy can be used to create custom peripherals. This example used 43 of 1248 logic cells.

Discussions

Andrei Errapart wrote 09/08/2015 at 07:15 point

Cool :)

  Are you sure? yes | no