Created
October 24, 2015 01:29
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; 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