Skip to content

Instantly share code, notes, and snippets.

@Jach
Created October 24, 2015 01:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jach/fd934bfb325e1ae2edf8 to your computer and use it in GitHub Desktop.
Save Jach/fd934bfb325e1ae2edf8 to your computer and use it in GitHub Desktop.
cljs code for a seven segment display example from http://www.thejach.com/view/2015/10/re-frame_a_software_fpga
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; calc.cljs
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(ns sevseg.calc)
(defn calc
"Expects a 4-item vector of booleans
representing a big-endian binary number.
Returns a 7-item map with keys prefix + num
corresponding to which segments of the display
should be lit up. e.g. If prefix is :segA,
you will get {:segA0 false, :segA1 true, ...}
Calculated from k-maps long ago, however note that
the initial calculations assume active-low
(i.e. if false, light should be on)
so in the final reduce stage a not is wrapped around each value."
[prefix [a3 a2 a1 a0]]
(let [calcs
[(or
(and (not a3) (not a2) (not a1) a0)
(and (not a3) a2 (not a1) (not a0))
(and a3 a2 (not a1) a0)
(and a3 (not a2) a1 a0))
(or
(and a2 a1 (not a0))
(and a3 a1 a0)
(and a3 a2 (not a0))
(and (not a3) a2 (not a1) a0))
(or
(and a3 a2 (not a0))
(and a3 a2 a1)
(and (not a3) (not a2) a1 (not a0)))
(or
(and a2 a1 a0)
(and (not a3) (not a2) (not a1) a0)
(and (not a3) a2 (not a1) (not a0))
(and a3 (not a2) a1 (not a0)))
(or
(and (not a3) a0)
(and (not a3) a2 (not a1))
(and (not a2) (not a1) a0))
(or
(and (not a3) (not a2) a0)
(and (not a3) (not a2) a1)
(and (not a3) a1 a0)
(and a3 a2 (not a1) a0))
(or
(and (not a3) (not a2) (not a1))
(and (not a3) a2 a1 a0)
(and a3 a2 (not a1) (not a0)))]
out (reduce #(assoc %1
(keyword (str (name prefix) %2)) (not (nth calcs %2)))
{} (range 7))]
out))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; db.cljs
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(ns sevseg.db)
(def default-db
{
; switches
:s0 false
:s1 false
:s2 false
:s3 false
:s4 false
:s5 false
:s6 false
:s7 false
; seven seg display 1
; (initially display 0)
:segA0 true
:segA1 true
:segA2 true
:segA3 true
:segA4 true
:segA5 true
:segA6 false
; seven seg display 2
; (initially display 0)
:segB0 true
:segB1 true
:segB2 true
:segB3 true
:segB4 true
:segB5 true
:segB6 false
})
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; subs.cljs
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(ns sevseg.subs
(:require-macros [reagent.ratom :refer [reaction]])
(:require [re-frame.core :as re-frame]))
; Register :s0-s7 as individual switch reactions
(doseq [switch-num (range 8)]
(let [sid (keyword (str "s" switch-num))]
(re-frame/register-sub
sid
(fn [db]
(reaction (sid @db))))))
; Register segA0-6 and segB0-6 as individual reactions
(doseq [seg-prefix ["segA" "segB"]]
(doseq [seg-num (range 7)]
(let [seg-id (keyword (str seg-prefix seg-num))]
(re-frame/register-sub
seg-id
(fn [db]
(reaction (seg-id @db)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; handlers.cljs
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(ns sevseg.handlers
(:require [re-frame.core :as re-frame]
[sevseg.db :as db]
[sevseg.calc]))
(re-frame/register-handler
:initialize-db
(fn [_ _]
db/default-db))
(def switch-bank-1 (juxt :s0 :s1 :s2 :s3))
(def switch-bank-2 (juxt :s4 :s5 :s6 :s7))
(defn determine-bank [sid]
"Maps s0-s3 to bank 1, s4-s5 to bank 2."
(condp contains? (js/parseInt (last (str sid)))
(set (range 4)) switch-bank-1
(set (range 4 8)) switch-bank-2
(throw (str "Bad sid " sid))))
(defn determine-seg [bank]
"Maps bank 1 to segment A, bank 2 to segment B."
(condp = bank
switch-bank-1 :segA
switch-bank-2 :segB
(throw (str "Bad bank " bank))))
; toggles the switch pin value state on itself.
; also immediately propogate that change to
; everything else that depends on the state,
; namely the segments.
(re-frame/register-handler
:toggle-switch
(fn [db [_ sid]]
(let [db (update-in db [sid] not) ; change switch state itself
bank (determine-bank sid)
seg (determine-seg bank)
switch-bank-vals (bank db)]
(merge db ; update seven seg states
(sevseg.calc/calc seg switch-bank-vals)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; views.cljs
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(ns sevseg.views
(:require [re-frame.core :as re-frame]
[re-com.core :as rc]
[reagent.core :as reagent]
[goog.string :as gstring]))
(defn switch [sid]
"The FPGA 'switch' component is just a checkbox with a db id of sid.
When user clicks the switch, dispatch the toggling."
(let [ticked? (re-frame/subscribe [sid])]
(fn []
[rc/checkbox
:model ticked?
:on-change #(re-frame/dispatch [:toggle-switch sid])])))
(def empty-box ; needed to sepaarate the lines on the sev-seg component.
[rc/box :width "50px" :child (gstring/unescapeEntities " ")])
(def box-style {:border "1px solid lightgray"
:border-radius "4px"
:padding "5px"})
(defn mk-line [visible?]
"Convenience function to create a red line that changes its visible
state depending on visible?"
[rc/line :size "3px" :color "red" :style {:visibility (if visible? "visible" "hidden")}])
(defn sev-seg [seg-prefix]
"Creates a seven segment display component wired to the segments
:[seg-prefix]0 through :[seg-prefix]6.
The correspondence of the pins to the output is as follows:
0
+---------+
+ +
| |
5| |1
| |
+ 6 +
+---------+
+ +
| |
4| |2
| |
+ +
+---------+
3
"
(let [segments (reduce #(conj %1 (re-frame/subscribe
[(keyword (str (name seg-prefix) %2))]))
[] (range 7))
[a0 a1 a2 a3 a4 a5 a6] segments]
(fn []
[rc/v-box
:gap "10px"
:style box-style
:children [[mk-line @a0]
[rc/h-box
:gap "10px"
:children [[mk-line @a5] empty-box [mk-line @a1]]]
(mk-line @a6)
[rc/h-box
:gap "10px"
:children [[mk-line @a4] empty-box [mk-line @a2]]]
[mk-line @a3]]])))
(defn switches [n nums]
"Component of the word 'Switch bank n'
containing nums individual switches."
[rc/v-box
:style box-style
:children [[rc/box :child (str "Switch bank " n)]
[rc/h-box
:gap "10px"
:children (for [n nums] [switch (keyword (str "s" n))])]]])
(defn main-panel []
"Constructs the page. Page consists of
a header, two switch banks, and two seven segment displays."
(fn []
[rc/v-box
:gap "5px"
:children [[rc/box :child [:h1 "Seven Segment Display Control"]] ; header
[rc/h-box ; switches container
:children [[switches 1 (range 4)]
[switches 2 (range 4 8)]]]
[rc/h-box ; sev-segs container
:gap "10px"
:children [[sev-seg :segA]
[sev-seg :segB]]]]
]))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment