├── .gitignore ├── Makefile ├── Makefile.common ├── README.md ├── Setup.md ├── am335x ├── app_loader │ ├── include │ │ ├── pruss_intc_mapping.h │ │ └── prussdrv.h │ └── interface │ │ ├── Makefile │ │ ├── __prussdrv.h │ │ └── prussdrv.c └── pasm │ ├── LICENCE.txt │ ├── Makefile │ ├── pasm.c │ ├── pasm.h │ ├── pasmdbg.h │ ├── pasmdot.c │ ├── pasmexp.c │ ├── pasmmacro.c │ ├── pasmop.c │ ├── pasmpp.c │ ├── pasmstruct.c │ └── pru_ins.h ├── bin └── .empty ├── brackets ├── Letters.dxf ├── Write.scad ├── flat-bracket.scad ├── flat-bracket.svg ├── matrix-bracket.scad ├── octoscroller.scad └── spiral.scad ├── default.config ├── dts ├── CAPE-BONE-OCTO-00A0.dtbo ├── LEDscape.dts ├── README.md ├── angstrom.dtb ├── angstrom.dts ├── cape-bone-octo.dts └── ubuntu-3.8.13-bone32.dts ├── fonts ├── json2bmp ├── mta-sign.json ├── nycr.png ├── pf_tempesta_seven.ttf └── spincycle.ttf ├── lib └── .empty ├── obj └── .empty ├── pcb ├── LEDscape pin assignments.csv ├── LEDscape.brd ├── LEDscape.lbr ├── LEDscape.sch ├── TODO ├── datasheets │ ├── 10118074.pdf │ └── MAX3040_.pdf ├── flat-20mm.scad ├── lightbuddy-transmitter.brd ├── lightbuddy-transmitter.sch ├── octoscroller.brd ├── octoscroller.sch ├── receiver-2x.brd ├── receiver-2x.sch ├── receiver-4x.brd ├── receiver-4x.sch ├── receiver.brd └── receiver.sch ├── radials.config ├── src ├── demos │ ├── Makefile │ ├── companion-cube.raw │ ├── cube-image.c │ ├── cube-life.c │ ├── fire.c │ ├── identify.c │ ├── life.c │ ├── lightcycles.c │ └── matrix-test.c ├── ledscape │ ├── Makefile │ ├── bitpattern.h │ ├── config.c │ ├── fixed-font.c │ ├── ledscape.c │ ├── ledscape.h │ ├── matrix.p │ ├── pru.c │ ├── pru.h │ ├── util.c │ ├── util.h │ ├── ws281x.hp │ └── ws281x.p ├── mta │ ├── Makefile │ ├── mta-font.c │ └── mta-sign.c ├── net │ ├── Makefile │ ├── matrix-tcp-rx.c │ ├── matrix-udp-rx.c │ └── opc-rx.c ├── perlin │ ├── Makefile │ ├── gammalut.h │ ├── globals.h │ ├── knobs │ │ └── knobs.ino │ ├── pattern.cpp │ ├── pattern.h │ ├── perlin-ledscape.cpp │ ├── pf2.cpp │ └── pf2.h └── script │ ├── Makefile │ ├── bbb-network-setup │ ├── find-serial │ ├── install │ ├── ledscape.service │ ├── python-test │ ├── run-ledscape │ ├── run-videoplayer │ ├── twitter-scroller │ ├── videoplayer │ └── videoplayer.service ├── strips.config ├── tall-cylinder.config └── ubuntu └── ledscape.conf /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | .*.swo 3 | *.i 4 | *.o 5 | *.a 6 | *.bin 7 | pcb/*.b[^r][^d] 8 | pcb/*.p[^c][^b] 9 | rgb-test 10 | am335x/pasm/pasm 11 | .*.d 12 | *~ 13 | *~ 14 | bin/* 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ######### 2 | # 3 | # Top level Makefile. 4 | # Mostly just recurse into subdirectories. 5 | 6 | SUBDIR-y += src/ledscape 7 | SUBDIR-y += src/demos 8 | SUBDIR-y += src/perlin 9 | SUBDIR-y += src/mta 10 | SUBDIR-y += src/net 11 | SUBDIR-y += src/script 12 | 13 | all: 14 | for dir in $(SUBDIR-y); do \ 15 | $(MAKE) -C $$dir || exit 1; \ 16 | done 17 | 18 | clean: 19 | for dir in $(SUBDIR-y); do \ 20 | $(MAKE) -C $$dir clean; \ 21 | done 22 | 23 | 24 | firmware: 25 | echo CAPE-BONE-OCTO > /sys/devices/bone_capemgr.8/slots 26 | 27 | -------------------------------------------------------------------------------- /Makefile.common: -------------------------------------------------------------------------------- 1 | ######### 2 | # 3 | # Common build targets 4 | # 5 | # 6 | 7 | TOP ?= ../.. 8 | OBJDIR ?= $(TOP)/obj 9 | LIBDIR ?= $(TOP)/lib 10 | BINDIR ?= $(TOP)/bin 11 | 12 | all: \ 13 | $(TARGETS-y) \ 14 | $(foreach O,$(BIN-y),$(BINDIR)/$O) \ 15 | $(foreach O,$(LIB-y),$(LIBDIR)/$O) \ 16 | 17 | ifeq ($(shell uname -m),armv7l) 18 | # We are on the BeagleBone Black itself; 19 | # do not cross compile. 20 | export CROSS_COMPILE:= 21 | 22 | else 23 | # We are not on the BeagleBone and might be cross compiling. 24 | # If the environment does not set CROSS_COMPILE, set our 25 | # own. Install a cross compiler with something like: 26 | # 27 | # sudo apt-get install gcc-arm-linux-gnueabi 28 | # 29 | export CROSS_COMPILE?=arm-linux-gnueabi- 30 | endif 31 | 32 | GENERIC_CFLAGS += \ 33 | -g \ 34 | -W \ 35 | -Wall \ 36 | -D_BSD_SOURCE \ 37 | -Wp,-MMD,$(dir $@).$(notdir $@).d \ 38 | -Wp,-MT,$@ \ 39 | -I. \ 40 | -I$(TOP)/src/ledscape \ 41 | -O2 \ 42 | -mtune=cortex-a8 \ 43 | -march=armv7-a \ 44 | 45 | CFLAGS += \ 46 | -std=c99 \ 47 | $(GENERIC_CFLAGS) \ 48 | 49 | CPPFLAGS += \ 50 | $(GENERIC_CFLAGS) \ 51 | 52 | LDFLAGS += \ 53 | 54 | LDLIBS += \ 55 | -L$(LIBDIR) \ 56 | -lledscape \ 57 | -lpthread \ 58 | -lm \ 59 | 60 | COMPILE.c-o = $(CROSS_COMPILE)gcc $(CFLAGS) -c -o $@ $< 61 | COMPILE.cpp-o = $(CROSS_COMPILE)g++ $(CPPFLAGS) -c -o $@ $< 62 | COMPILE.a = $(CROSS_COMPILE)ar crv $@ $^ 63 | COMPILE.link = $(CROSS_COMPILE)g++ $(LDFLAGS) -o $@ $^ $(LDLIBS) 64 | 65 | $(OBJDIR)/%.o: %.c 66 | $(COMPILE.c-o) 67 | $(OBJDIR)/%.o: %.cpp 68 | $(COMPILE.cpp-o) 69 | 70 | $(LIBDIR)/%.a: 71 | $(RM) $@ 72 | $(COMPILE.a) 73 | 74 | $(BINDIR)/%: 75 | $(COMPILE.link) 76 | 77 | 78 | ##### 79 | # 80 | # The TI "app_loader" is the userspace library for talking to 81 | # the PRU and mapping memory between it and the ARM. 82 | # 83 | APP_LOADER_DIR ?= $(TOP)/am335x/app_loader 84 | APP_LOADER_LIB := $(APP_LOADER_DIR)/lib/libprussdrv.a 85 | CFLAGS += -I$(APP_LOADER_DIR)/include 86 | LDLIBS += $(APP_LOADER_LIB) 87 | 88 | 89 | ##### 90 | # 91 | # The TI PRU assembler looks like it has macros and includes, 92 | # but it really doesn't. So instead we use cpp to pre-process the 93 | # file and then strip out all of the directives that it adds. 94 | # PASM also doesn't handle multiple statements per line, so we 95 | # insert hard newline characters for every ; in the file. 96 | # 97 | PASM_DIR ?= $(TOP)/am335x/pasm 98 | PASM := $(PASM_DIR)/pasm 99 | 100 | $(LIBDIR)/%.bin: %.p $(PASM) 101 | $(CPP) - < $< | perl -p -e 's/^#.*//; s/;/\n/g; s/BYTE\((\d+)\)/t\1/g' > /tmp/$(basename $<).i 102 | $(PASM) -V3 -b /tmp/$(basename $<).i $(basename $@) 103 | $(RM) /tmp/$(basename $<).i 104 | 105 | # 106 | # Generate a target for each of the binaries, with dependencies on 107 | # object files for that source. 108 | # 109 | $(foreach O,$(BIN-y),\ 110 | $(eval $(BINDIR)/$O: $(foreach s,$($O.srcs),$(OBJDIR)/$(basename $s).o) $(LIBDIR)/libledscape.a)) 111 | 112 | $(foreach O,$(LIB-y),\ 113 | $(eval $(LIBDIR)/$O: $(foreach s,$($(basename $O).srcs),$(OBJDIR)/$(basename $s).o) $(APP_LOADER_LIB))) 114 | 115 | #$(TARGETS): 116 | #$(COMPILE.link) 117 | 118 | 119 | .PHONY: clean 120 | 121 | # Include all of the generated dependency files 122 | -include $(OBJDIR)/.*.o.d 123 | 124 | clean: 125 | rm -rf \ 126 | $(OBJDIR)/*.o \ 127 | $(LIBDIR)/*.a \ 128 | $(OBJDIR)/.*.o.d \ 129 | $(LIBDIR)/*.bin \ 130 | 131 | 132 | ########### 133 | # 134 | # The correct way to reserve the GPIO pins on the BBB is with the 135 | # capemgr and a Device Tree file. But it doesn't work. 136 | # 137 | SLOT_FILE=/sys/devices/bone_capemgr.9/slots 138 | DTS=CAPE-BONE-OCTO 139 | DTB=/lib/firmware/$(DTS)-00A0.dtbo 140 | 141 | dts: LEDscape.dts 142 | @SLOT="`grep LEDSCAPE $(SLOT_FILE) | cut -d: -f1`"; \ 143 | if [ ! -z "$$SLOT" ]; then \ 144 | echo "Removing slot $$SLOT"; \ 145 | echo -$$SLOT > $(SLOT_FILE); \ 146 | fi 147 | dtc -O dtb -o /lib/firmware/BB-LEDSCAPE-00A0.dtbo -b 0 -@ LEDscape.dts 148 | echo BB-LEDSCAPE > $(SLOT_FILE) 149 | 150 | 151 | 152 | firmware: $(DTB) 153 | echo $(DTS) > $(SLOT_FILE) 154 | 155 | $(DTB): cape-bone-octo.dts FORCE 156 | dtc -O dtb -o $@ -b 0 -@ $< 157 | FORCE: 158 | 159 | ########### 160 | # 161 | # PRU Libraries and PRU assembler are build from their own trees. 162 | # 163 | $(APP_LOADER_LIB): 164 | $(MAKE) -C $(APP_LOADER_DIR)/interface 165 | 166 | $(PASM): 167 | $(MAKE) -C $(PASM_DIR) 168 | 169 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Octoscroller Interface board](http://farm6.staticflickr.com/5349/10218235983_c55d247088.jpg) 2 | 3 | DANGER! 4 | ======= 5 | 6 | This code works with the PRU units on the Beagle Bone and can easily 7 | cause *hard crashes*. It is still being debugged and developed. 8 | Be careful hot-plugging things into the headers -- it is possible to 9 | damage the pin drivers and cause problems in the ARM, especially if 10 | there are +5V signals involved. 11 | 12 | 13 | Overview 14 | ======== 15 | 16 | The WS281x LED chips are built like shift registers and make for very 17 | easy LED strip construction. The signals are duty-cycle modulated, 18 | with a 0 measuring 250 ns long and a 1 being 600 ns long, and 1250 ns 19 | between bits. Since this doesn't map to normal SPI hardware and requires 20 | an 800 KHz bit clock, it is typically handled with a dedicated microcontroller 21 | or DMA hardware on something like the Teensy 3. 22 | 23 | However, the TI AM335x ARM Cortex-A8 in the BeagleBone Black has two 24 | programmable "microcontrollers" built into the CPU that can handle realtime 25 | tasks and also access the ARM's memory. This allows things that 26 | might have been delegated to external devices to be handled without 27 | any additional hardware, and without the overhead of clocking data out 28 | the USB port. 29 | 30 | The frames are stored in memory as a series of 4-byte pixels in the 31 | order GRBA, packed in strip-major order. This means that it looks 32 | like this in RAM: 33 | 34 | S0P0 S1P0 S2P0 ... S31P0 S0P1 S1P1 ... S31P1 S0P2 S1P2 ... S31P2 35 | 36 | This way length of the strip can be variable, although the memory used 37 | will depend on the length of the longest strip. 4 * 32 * longest strip 38 | bytes are required per frame buffer. The maximum frame rate also depends 39 | on the length of th elongest strip. 40 | 41 | 42 | API 43 | === 44 | 45 | defines the API. The sample rgb-test program pulses 46 | the first three LEDs in red, green and blue. The key components are: 47 | 48 | ledscape_t * ledscape_init(unsigned num_pixels) 49 | ledscape_frame_t * ledscape_frame(ledscape_t*, unsigned frame_num); 50 | ledscape_draw(ledscape_t*, unsigned frame_num); 51 | unsigned ledscape_wait(ledscape_t*) 52 | 53 | You can double buffer like this: 54 | 55 | const int num_pixels = 256; 56 | ledscape_t * const leds = ledscape_init(num_pixels); 57 | 58 | unsigned i = 0; 59 | while (1) 60 | { 61 | // Alternate frame buffers on each draw command 62 | const unsigned frame_num = i++ % 2; 63 | ledscape_frame_t * const frame 64 | = ledscape_frame(leds, frame_num); 65 | 66 | render(frame); 67 | 68 | // wait for the previous frame to finish; 69 | ledscape_wait(leds); 70 | ledscape_draw(leds, frame_num); 71 | } 72 | 73 | ledscape_close(leds); 74 | 75 | The 24-bit RGB data to be displayed is laid out with BRGA format, 76 | since that is how it will be translated during the clock out from the PRU. 77 | The frame buffer is stored as a "strip-major" array of pixels. 78 | 79 | typedef struct { 80 | uint8_t b; 81 | uint8_t r; 82 | uint8_t g; 83 | uint8_t a; 84 | } __attribute__((__packed__)) ledscape_pixel_t; 85 | 86 | typedef struct { 87 | ledscape_pixel_t strip[32]; 88 | } __attribute__((__packed__)) ledscape_frame_t; 89 | 90 | 91 | Low level API 92 | ============= 93 | 94 | If you want to poke at the PRU directly, there is a command structure 95 | shared in PRU DRAM that holds a pointer to the current frame buffer, 96 | the length in pixels, a command byte and a response byte. 97 | Once the PRU has cleared the command byte you are free to re-write the 98 | dma address or number of pixels. 99 | 100 | typedef struct 101 | { 102 | // in the DDR shared with the PRU 103 | const uintptr_t pixels_dma; 104 | 105 | // Length in pixels of the longest LED strip. 106 | unsigned num_pixels; 107 | 108 | // write 1 to start, 0xFF to abort. will be cleared when started 109 | volatile unsigned command; 110 | 111 | // will have a non-zero response written when done 112 | volatile unsigned response; 113 | } __attribute__((__packed__)) ws281x_command_t; 114 | 115 | LED Strips 116 | ========== 117 | 118 | ![Testing LEDscape](http://farm4.staticflickr.com/3834/9378678019_b706c55635_z.jpg) 119 | 120 | * http://www.adafruit.com/products/1138 121 | * http://www.adafruit.com/datasheets/WS2811.pdf 122 | -------------------------------------------------------------------------------- /Setup.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | This is a quick introduction on how to set up LEDscape on a Debian-based image 4 | 5 | # Setting up the BBB environment 6 | 7 | To develop for LEDscape on a Debian environment, Start by copying the latest BBB image to an SD card. These instructions were made using: 8 | 9 | BBB-eMMC-flasher-debian-7.8-console-armhf-2015-03-01-2gb.img.xz 10 | 11 | The latest version can be found at: 12 | 13 | http://elinux.org/Beagleboard:BeagleBoneBlack_Debian#Debian_Releases 14 | 15 | First, we need to expand the image to use the whole SD card. By default, it is only 2GB. 16 | 17 | Note: If using a RevC or lower BBB, the EMMC is only 2GB so this isn't necessicary. 18 | 19 | cd /opt/scripts/tools/ 20 | sudo ./grow_partition.sh 21 | sudo reboot 22 | 23 | Next, update the the Debian environment: 24 | 25 | sudo apt-get update 26 | sudo apt-get install usbmount -y 27 | sudo apt-get install git build-essential python vim -y 28 | 29 | Disable the HDMI output: 30 | 31 | If you are using a Debian image from 2014.8.13 or newer, do this: 32 | 33 | sudo cp /boot/uEnv.txt /boot/uEnv.txt.backup 34 | sudo sed -i 's/#cape_disable=capemgr.disable_partno=BB-BONELT-HDMI,BB-BONELT-HDMIN$/cape_disable=capemgr.disable_partno=BB-BONELT-HDMI,BB-BONELT-HDMIN/g' /boot/uEnv.txt 35 | 36 | 37 | Otherwise, modify the uEnv boot file by hand to disable HDMI and HDMIN overlays. 38 | 39 | Enable a larger shared memory space (8MB) with the PRU: 40 | 41 | echo 'options uio_pruss extram_pool_sz=0x800000' | sudo tee -a /etc/modprobe.d/ledscape.conf 42 | 43 | Finally, reboot to apply these changs: 44 | 45 | sudo reboot 46 | 47 | # Next, set up LEDscape: 48 | 49 | Use git to download the repository: 50 | 51 | cd /opt 52 | sudo git clone http://github.com/osresearch/LEDscape.git 53 | cd LEDscape 54 | sudo make 55 | 56 | Copy the device tree file into place, and add it to the slots: 57 | 58 | sudo cp dts/CAPE-BONE-OCTO-00A0.dtbo /lib/firmware 59 | echo 'CAPE-BONE-OCTO' | sudo tee -a /sys/devices/bone_capemgr.*/slots 60 | 61 | Then run the identification program to test if all is well: 62 | 63 | sudo bin/identify 64 | 65 | # Make a configuration file for your screen 66 | 67 | The configuration file is what tells LEDscape how to draw it's virtual screen onto your matrix tiles or LED strips. There are two basic formats: 68 | 69 | ## Matrix screen 70 | 71 | Let's look at a sample matrix configuration. Here's one for a small display consisting of 4 LED matricies, arranged in a square: 72 | 73 | matrix16 74 | 0,6 N 0,0 75 | 0,7 N 32,0 76 | 1,6 N 0,16 77 | 2,7 N 32,16 78 | 79 | The first line of the configuration file describes the type of matrix. Here are the valid matrix types: 80 | 81 | | Type | Description | 82 | | ------------- |:-------------:| 83 | | matrix16 | 32x16 LED matrix, 1/8th scanning (three address pins) | 84 | 85 | Each following line consists of three sets of information: Controller position, Rotation, and LEDscape virtual screen offset. 86 | 87 | The controller position consists of two values. The first is the output channel. This corresponds to the physical output that the matrix is plugged into on the board. There are 8 outputs available on the OCTOscroller shield. The second is the position in the output chain. This one is a little more tricky, as it is backwards from you might expect. The *first* matrix panel in the chain, which is the one connected to the OCTOscroller shield, is called 7. The second one is called 6, and so on, until the eighth and final one. For example, a single matrix panel connected to output #3 has the following controller position: 88 | 89 | 3,7 90 | 91 | The rotation is any one of the following values: N, U, L, R. 'N' and 'U' are used when the long side of the panel is parallel to the ground, and 'L' and 'R' are used when it is perpendicular. Try one, then the other, to figure out the correct orientation for your panels. 92 | 93 | The Virtual screen offset is the top-left position in the LEDscape virtual screen that will be drawn to this matrix panel. Normally you will want to map sections of the screen into contigouos regions, so the top-left panel in your display should have a virtual screen offset of 0,0, then the panel to the right of that one should be offset by the width of the first panel, either 16,0 or 32,0, and so on. 94 | 95 | 96 | ## WS2812 strips 97 | 98 | Let's look at a sample WS2812 strip configuration. Here's one that can control a single strip output: 99 | 100 | ws2812 101 | 64,48 102 | 103 | The first line of the configuration file describes the type of matrix. Here are the valid matrix types: 104 | 105 | | Type | Description | 106 | | ------------- |:-------------:| 107 | | ws2812 | Strip of WS2812/WS2812B LEDs, aka NeoPixels | 108 | 109 | The following line consists of Width,Height. For LED strip mode, this means: 110 | 111 | W: length of the longest strip 112 | H: number of output strips 113 | 114 | ## Testing your configuration 115 | 116 | For matricies, there is a handy identification program to draw some text that identifies each panel. Run it to test your new configuration: 117 | 118 | sudo bin/identify myconfig.config 119 | 120 | 121 | # Set up the UDP listener to display incoming packets 122 | 123 | To run the matrix listener: 124 | 125 | sudo bin/matrix-udp-rx -W 256 -H 32 -c sign.config & 126 | 127 | Or for WS2812B strips: 128 | 129 | sudo bin/matrix-udp-rx -W 256 -H 32 -c strips.config & 130 | 131 | There are a bunch of command line arguments, and the whole thing seems to be in a bit of flux. Here's what exists now: 132 | 133 | | Argument | Description | Default | 134 | | ------------- |:-------------:| -----:| 135 | | -v | Verbose mode | | 136 | | -n | Skip LEDscape initialization | | 137 | | -c | Configuration file to use. Note: Use full pathname, for example: /home/debian/LEDscape/default.config | | 138 | | -t | Number of seconds the display server will show the previous image before timing out | 60 | 139 | | -W | LEDscape virtual screen width | 256 | 140 | | -H | LEDscape virtual screen height | 128 | 141 | | -m | Message to display at startup | | 142 | 143 | 144 | # Run the UDP listener automatically at system boot 145 | 146 | There's a handy script for starting LEDscape at boot. It should listen on the ethernet interface on port 9999 automatically. 147 | 148 | ## Debian 149 | 150 | Debian uses systemd. Do this: 151 | 152 | sudo cp bin/ledscape.service /etc/systemd/system 153 | sudo systemctl enable ledscape.service 154 | 155 | Extra: for video playback 156 | 157 | sudo cp bin/videoplayer.service /etc/systemd/system 158 | sudo systemctl enable videoplayer.service 159 | 160 | ## Ubuntu 161 | 162 | Note: The Ubuntu startup script is old and probably needs fixing. 163 | 164 | Ubuntu appears to use upstart. Do this: 165 | 166 | sudo cp ubuntu/ledscape.conf /etc/init 167 | sudo start ledscape 168 | 169 | # Extra credit 170 | 171 | ## Make the filesystem read-only 172 | 173 | To help prevent the configuration from breaking, it's helpful to configure the system as read only. The insructions are from: 174 | 175 | http://armsdr.blogspot.com/2014/11/beaglebone-debian-read-only-filesystem.html 176 | 177 | First, back up the original fstab: 178 | 179 | sudo cp /etc/fstab /etc/fstab_rw 180 | 181 | Now, set the root filesystem to be readonly: 182 | 183 | sudo sed -i 's/ext4 noatime/ext4 ro,noatime/g' /etc/fstab 184 | 185 | Finally, add some temporary rw mounts so that the system can continue to function: 186 | 187 | echo "tmpfs /tmp tmpfs nodev,nosuid,size=32M 0 0 188 | tmpfs /srv tmpfs nodev,size=512K 0 0 189 | tmpfs /var/log tmpfs defaults,noatime,size=1M 0 0 190 | tmpfs /var/tmp tmpfs defaults,noatime,size=512K 0 0 191 | tmpfs /var/run tmpfs defaults,noatime,size=512K 0 0" | sudo tee -a /etc/fstab 192 | 193 | 194 | Later, write capability can be restored using the following command: 195 | 196 | sudo mount -o remount,rw / 197 | 198 | And disabled again using: 199 | 200 | sudo mount -o remount,ro / 201 | 202 | ## Speed up boot when Ethernet is not connected 203 | 204 | The ethernet PHY on some BBBs can be flaky and cause a long delay during boot. To work around this, set it to not load at boot: 205 | 206 | sudo sudo sed -i 's/auto eth0/#auto eth0/g' /etc/network/interfaces 207 | 208 | Later it can be enabled by typing: 209 | 210 | sudo ifup eth0 211 | 212 | ## Set the time automatically (when network is available) 213 | 214 | sudo apt-get install ntpdate -y 215 | 216 | 217 | # Video playback 218 | 219 | Playing a video is as simple as running the video player (after running the UDP listener): 220 | 221 | bin/video_player -s 256x32 -l ../Daft\ Punk\ -\ Around\ The\ World.avi 222 | 223 | To use the video player, some additional packages will need to be installed: 224 | 225 | sudo apt-get install libavformat-dev x264 v4l-utils ffmpeg libcv2.3 libcvaux2.3 libhighgui2.3 python-opencv opencv-doc libcv-dev libcvaux-dev libhighgui-dev -y 226 | 227 | 228 | #Helpful things for development 229 | 230 | ## Make VIM prettier 231 | 232 | Enable color highlighting 233 | 234 | echo 'syntax on' >>~/.vimrc 235 | 236 | -------------------------------------------------------------------------------- /am335x/app_loader/include/pruss_intc_mapping.h: -------------------------------------------------------------------------------- 1 | /* 2 | * pruss_intc_mapping.h 3 | * 4 | * Example PRUSS INTC mapping for the application 5 | * 6 | * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ 7 | * 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions 11 | * are met: 12 | * 13 | * Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 16 | * Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the 19 | * distribution. 20 | * 21 | * Neither the name of Texas Instruments Incorporated nor the names of 22 | * its contributors may be used to endorse or promote products derived 23 | * from this software without specific prior written permission. 24 | * 25 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | * 37 | */ 38 | 39 | /* 40 | * ============================================================================ 41 | * Copyright (c) Texas Instruments Inc 2010-11 42 | * 43 | * Use of this software is controlled by the terms and conditions found in the 44 | * license agreement under which this software has been supplied or provided. 45 | * ============================================================================ 46 | */ 47 | 48 | #define AM33XX 49 | #ifdef AM33XX 50 | #define PRU0_PRU1_INTERRUPT 17 51 | #define PRU1_PRU0_INTERRUPT 18 52 | #define PRU0_ARM_INTERRUPT 19 53 | #define PRU1_ARM_INTERRUPT 20 54 | #define ARM_PRU0_INTERRUPT 21 55 | #define ARM_PRU1_INTERRUPT 22 56 | #else 57 | #define PRU0_PRU1_INTERRUPT 32 58 | #define PRU1_PRU0_INTERRUPT 33 59 | #define PRU0_ARM_INTERRUPT 34 60 | #define PRU1_ARM_INTERRUPT 35 61 | #define ARM_PRU0_INTERRUPT 36 62 | #define ARM_PRU1_INTERRUPT 37 63 | #endif 64 | #define CHANNEL0 0 65 | #define CHANNEL1 1 66 | #define CHANNEL2 2 67 | #define CHANNEL3 3 68 | #define CHANNEL4 4 69 | #define CHANNEL5 5 70 | #define CHANNEL6 6 71 | #define CHANNEL7 7 72 | #define CHANNEL8 8 73 | #define CHANNEL9 9 74 | 75 | #define PRU0 0 76 | #define PRU1 1 77 | #define PRU_EVTOUT0 2 78 | #define PRU_EVTOUT1 3 79 | #define PRU_EVTOUT2 4 80 | #define PRU_EVTOUT3 5 81 | #define PRU_EVTOUT4 6 82 | #define PRU_EVTOUT5 7 83 | #define PRU_EVTOUT6 8 84 | #define PRU_EVTOUT7 9 85 | 86 | #define PRU0_HOSTEN_MASK 0x0001 87 | #define PRU1_HOSTEN_MASK 0x0002 88 | #define PRU_EVTOUT0_HOSTEN_MASK 0x0004 89 | #define PRU_EVTOUT1_HOSTEN_MASK 0x0008 90 | #define PRU_EVTOUT2_HOSTEN_MASK 0x0010 91 | #define PRU_EVTOUT3_HOSTEN_MASK 0x0020 92 | #define PRU_EVTOUT4_HOSTEN_MASK 0x0040 93 | #define PRU_EVTOUT5_HOSTEN_MASK 0x0080 94 | #define PRU_EVTOUT6_HOSTEN_MASK 0x0100 95 | #define PRU_EVTOUT7_HOSTEN_MASK 0x0200 96 | 97 | 98 | #define PRUSS_INTC_INITDATA { \ 99 | { PRU0_PRU1_INTERRUPT, PRU1_PRU0_INTERRUPT, PRU0_ARM_INTERRUPT, PRU1_ARM_INTERRUPT, ARM_PRU0_INTERRUPT, ARM_PRU1_INTERRUPT, -1 }, \ 100 | { {PRU0_PRU1_INTERRUPT,CHANNEL1}, {PRU1_PRU0_INTERRUPT, CHANNEL0}, {PRU0_ARM_INTERRUPT,CHANNEL2}, {PRU1_ARM_INTERRUPT, CHANNEL3}, {ARM_PRU0_INTERRUPT, CHANNEL0}, {ARM_PRU1_INTERRUPT, CHANNEL1}, {-1,-1}}, \ 101 | { {CHANNEL0,PRU0}, {CHANNEL1, PRU1}, {CHANNEL2, PRU_EVTOUT0}, {CHANNEL3, PRU_EVTOUT1}, {-1,-1} }, \ 102 | (PRU0_HOSTEN_MASK | PRU1_HOSTEN_MASK | PRU_EVTOUT0_HOSTEN_MASK | PRU_EVTOUT1_HOSTEN_MASK) /*Enable PRU0, PRU1, PRU_EVTOUT0 */ \ 103 | } \ 104 | 105 | -------------------------------------------------------------------------------- /am335x/app_loader/include/prussdrv.h: -------------------------------------------------------------------------------- 1 | /* 2 | * prussdrv.h 3 | * 4 | * Describes PRUSS userspace driver for Industrial Communications 5 | * 6 | * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ 7 | * 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions 11 | * are met: 12 | * 13 | * Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 16 | * Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the 19 | * distribution. 20 | * 21 | * Neither the name of Texas Instruments Incorporated nor the names of 22 | * its contributors may be used to endorse or promote products derived 23 | * from this software without specific prior written permission. 24 | * 25 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | * 37 | */ 38 | 39 | /* 40 | * ============================================================================ 41 | * Copyright (c) Texas Instruments Inc 2010-11 42 | * 43 | * Use of this software is controlled by the terms and conditions found in the 44 | * license agreement under which this software has been supplied or provided. 45 | * ============================================================================ 46 | */ 47 | 48 | #ifndef _PRUSSDRV_H 49 | #define _PRUSSDRV_H 50 | 51 | #include 52 | #include 53 | 54 | #if defined (__cplusplus) 55 | extern "C" { 56 | #endif 57 | 58 | #define NUM_PRU_HOSTIRQS 8 59 | #define NUM_PRU_HOSTS 10 60 | #define NUM_PRU_CHANNELS 10 61 | #define NUM_PRU_SYS_EVTS 64 62 | 63 | #define PRUSS0_PRU0_DATARAM 0 64 | #define PRUSS0_PRU1_DATARAM 1 65 | #define PRUSS0_PRU0_IRAM 2 66 | #define PRUSS0_PRU1_IRAM 3 67 | 68 | //Available in AM33xx series - begin 69 | #define PRUSS0_SHARED_DATARAM 4 70 | #define PRUSS0_CFG 5 71 | #define PRUSS0_UART 6 72 | #define PRUSS0_IEP 7 73 | #define PRUSS0_ECAP 8 74 | #define PRUSS0_MII_RT 9 75 | #define PRUSS0_MDIO 10 76 | //Available in AM33xx series - end 77 | 78 | #define PRU_EVTOUT_0 0 79 | #define PRU_EVTOUT_1 1 80 | #define PRU_EVTOUT_2 2 81 | #define PRU_EVTOUT_3 3 82 | #define PRU_EVTOUT_4 4 83 | #define PRU_EVTOUT_5 5 84 | #define PRU_EVTOUT_6 6 85 | #define PRU_EVTOUT_7 7 86 | 87 | typedef void *(*prussdrv_function_handler) (void *); 88 | typedef struct __sysevt_to_channel_map { 89 | short sysevt; 90 | short channel; 91 | } tsysevt_to_channel_map; 92 | typedef struct __channel_to_host_map { 93 | short channel; 94 | short host; 95 | } tchannel_to_host_map; 96 | typedef struct __pruss_intc_initdata { 97 | //Enabled SYSEVTs - Range:0..63 98 | //{-1} indicates end of list 99 | char sysevts_enabled[NUM_PRU_SYS_EVTS]; 100 | //SysEvt to Channel map. SYSEVTs - Range:0..63 Channels -Range: 0..9 101 | //{-1, -1} indicates end of list 102 | tsysevt_to_channel_map sysevt_to_channel_map[NUM_PRU_SYS_EVTS]; 103 | //Channel to Host map.Channels -Range: 0..9 HOSTs - Range:0..9 104 | //{-1, -1} indicates end of list 105 | tchannel_to_host_map channel_to_host_map[NUM_PRU_CHANNELS]; 106 | //10-bit mask - Enable Host0-Host9 {Host0/1:PRU0/1, Host2..9 : PRUEVT_OUT0..7) 107 | unsigned int host_enable_bitmask; 108 | } tpruss_intc_initdata; 109 | 110 | int prussdrv_init(void); 111 | 112 | int prussdrv_open(unsigned int pru_evtout_num); 113 | 114 | int prussdrv_pru_reset(unsigned int prunum); 115 | 116 | int prussdrv_pru_disable(unsigned int prunum); 117 | 118 | int prussdrv_pru_enable(unsigned int prunum); 119 | 120 | int prussdrv_pru_write_memory(unsigned int pru_ram_id, 121 | unsigned int wordoffset, 122 | unsigned int *memarea, 123 | unsigned int bytelength); 124 | 125 | int prussdrv_pruintc_init(tpruss_intc_initdata * prussintc_init_data); 126 | 127 | int prussdrv_map_l3mem(void **address); 128 | 129 | int prussdrv_map_extmem(void **address); 130 | 131 | int prussdrv_map_prumem(unsigned int pru_ram_id, void **address); 132 | 133 | int prussdrv_map_peripheral_io(unsigned int per_id, void **address); 134 | 135 | unsigned int prussdrv_get_phys_addr(void *address); 136 | 137 | void *prussdrv_get_virt_addr(unsigned int phyaddr); 138 | 139 | int prussdrv_pru_wait_event(unsigned int pru_evtout_num); 140 | 141 | int prussdrv_pru_send_event(unsigned int eventnum); 142 | 143 | int prussdrv_pru_clear_event(unsigned int eventnum); 144 | 145 | int prussdrv_pru_send_wait_clear_event(unsigned int send_eventnum, 146 | unsigned int pru_evtout_num, 147 | unsigned int ack_eventnum); 148 | 149 | int prussdrv_exit(void); 150 | 151 | int prussdrv_exec_program(int prunum, char *filename); 152 | 153 | int prussdrv_start_irqthread(unsigned int pru_evtout_num, int priority, 154 | prussdrv_function_handler irqhandler); 155 | 156 | 157 | #if defined (__cplusplus) 158 | } 159 | #endif 160 | #endif 161 | -------------------------------------------------------------------------------- /am335x/app_loader/interface/Makefile: -------------------------------------------------------------------------------- 1 | ROOTDIR = .. 2 | TARGET = libprussdrv 3 | CROSS_COMPILE?=arm-linux-gnueabi- 4 | 5 | CC = $(CROSS_COMPILE)gcc 6 | AR = $(CROSS_COMPILE)ar 7 | 8 | INCLUDEDIR = ../include 9 | LIBDIR = ../lib 10 | 11 | C_FLAGS += -I. -Wall -I$(INCLUDEDIR) 12 | 13 | COMPILE.c = $(CC) $(C_FLAGS) $(CPP_FLAGS) -c 14 | AR.c = $(AR) rc 15 | 16 | DBGTARGET = $(LIBDIR)/$(TARGET)d.a 17 | RELTARGET = $(LIBDIR)/$(TARGET).a 18 | 19 | DBGCFLAGS = -g -O0 -D__DEBUG 20 | RELCFLAGS = -O3 -mtune=cortex-a8 -march=armv7-a 21 | 22 | SOURCES = $(wildcard *.c) 23 | HEADERS = $(wildcard *.h) 24 | 25 | TARGETHEADERS = $(addprefix $(INCLUDEDIR)/, $(HEADERS)) 26 | 27 | DBGOBJFILES = $(SOURCES:%.c=debug/%.o) 28 | RELOBJFILES = $(SOURCES:%.c=release/%.o) 29 | 30 | .PHONY: clean debug release install 31 | 32 | all: debug release 33 | 34 | install: 35 | 36 | release: $(RELTARGET) 37 | #release: $(RELTARGET) $(TARGETHEADERS) 38 | 39 | debug: $(DBGTARGET) 40 | #debug: $(DBGTARGET) $(TARGETHEADERS) 41 | 42 | 43 | $(RELTARGET): $(RELOBJFILES) 44 | @mkdir -p $(ROOTDIR)/lib 45 | $(AR.c) $@ $(RELOBJFILES) 46 | 47 | $(DBGTARGET): $(DBGOBJFILES) 48 | @mkdir -p $(ROOTDIR)/lib 49 | $(AR.c) $@ $(DBGOBJFILES) 50 | 51 | $(RELOBJFILES): release/%.o: %.c $(HEADERS) 52 | @mkdir -p release 53 | $(COMPILE.c) $(RELCFLAGS) -o $@ $< 54 | 55 | $(DBGOBJFILES): debug/%.o: %.c $(HEADERS) 56 | @mkdir -p debug 57 | $(COMPILE.c) $(DBGCFLAGS) -o $@ $< 58 | 59 | $(TARGETHEADERS): $(HEADERS) 60 | @echo Installing headers... 61 | @install -d $(INCLUDEDIR) 62 | @install -c $< $@ 63 | 64 | clean: 65 | -rm -rf release debug *~ ../lib/* 66 | 67 | -------------------------------------------------------------------------------- /am335x/app_loader/interface/__prussdrv.h: -------------------------------------------------------------------------------- 1 | /* 2 | * __prussdrv.h 3 | * 4 | * 5 | * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ 6 | * 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 12 | * Redistributions of source code must retain the above copyright 13 | * notice, this list of conditions and the following disclaimer. 14 | * 15 | * Redistributions in binary form must reproduce the above copyright 16 | * notice, this list of conditions and the following disclaimer in the 17 | * documentation and/or other materials provided with the 18 | * distribution. 19 | * 20 | * Neither the name of Texas Instruments Incorporated nor the names of 21 | * its contributors may be used to endorse or promote products derived 22 | * from this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 27 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 28 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 29 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 30 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 32 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 34 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | * 36 | */ 37 | 38 | /* 39 | * ============================================================================ 40 | * Copyright (c) Texas Instruments Inc 2010-12 41 | * 42 | * Use of this software is controlled by the terms and conditions found in the 43 | * license agreement under which this software has been supplied or provided. 44 | * ============================================================================ 45 | */ 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | 57 | #include 58 | #include 59 | #include 60 | #include 61 | 62 | #include 63 | 64 | #define DISABLE_L3RAM_SUPPORT 65 | 66 | #define PAGE_SIZE 4096 67 | 68 | #define PRUSS_V1 1 69 | #define PRUSS_V2 2 70 | 71 | #define AM33XX_PRUSS_INTC_REV 0x4E82A900 72 | #define AM18XX_PRUSS_INTC_REV 0x4E825900 73 | 74 | #define PRUSS_MAX_IRAM_SIZE 8192 75 | 76 | #define AM33XX_PRUSS_IRAM_SIZE 8192 77 | #define AM33XX_PRUSS_MMAP_SIZE 0x40000 78 | #define AM33XX_DATARAM0_PHYS_BASE 0x4a300000 79 | #define AM33XX_DATARAM1_PHYS_BASE 0x4a302000 80 | #define AM33XX_INTC_PHYS_BASE 0x4a320000 81 | #define AM33XX_PRU0CONTROL_PHYS_BASE 0x4a322000 82 | #define AM33XX_PRU0DEBUG_PHYS_BASE 0x4a322400 83 | #define AM33XX_PRU1CONTROL_PHYS_BASE 0x4a324000 84 | #define AM33XX_PRU1DEBUG_PHYS_BASE 0x4a324400 85 | #define AM33XX_PRU0IRAM_PHYS_BASE 0x4a334000 86 | #define AM33XX_PRU1IRAM_PHYS_BASE 0x4a338000 87 | #define AM33XX_PRUSS_SHAREDRAM_BASE 0x4a310000 88 | #define AM33XX_PRUSS_CFG_BASE 0x4a326000 89 | #define AM33XX_PRUSS_UART_BASE 0x4a328000 90 | #define AM33XX_PRUSS_IEP_BASE 0x4a32e000 91 | #define AM33XX_PRUSS_ECAP_BASE 0x4a330000 92 | #define AM33XX_PRUSS_MIIRT_BASE 0x4a332000 93 | #define AM33XX_PRUSS_MDIO_BASE 0x4a332400 94 | 95 | #define AM18XX_PRUSS_IRAM_SIZE 4096 96 | #define AM18XX_PRUSS_MMAP_SIZE 0x7C00 97 | #define AM18XX_DATARAM0_PHYS_BASE 0x01C30000 98 | #define AM18XX_DATARAM1_PHYS_BASE 0x01C32000 99 | #define AM18XX_INTC_PHYS_BASE 0x01C34000 100 | #define AM18XX_PRU0CONTROL_PHYS_BASE 0x01C37000 101 | #define AM18XX_PRU0DEBUG_PHYS_BASE 0x01C37400 102 | #define AM18XX_PRU1CONTROL_PHYS_BASE 0x01C37800 103 | #define AM18XX_PRU1DEBUG_PHYS_BASE 0x01C37C00 104 | #define AM18XX_PRU0IRAM_PHYS_BASE 0x01C38000 105 | #define AM18XX_PRU1IRAM_PHYS_BASE 0x01C3C000 106 | 107 | //PRUSS INTC register offsets 108 | #define PRU_INTC_REVID_REG 0x000 109 | #define PRU_INTC_CR_REG 0x004 110 | #define PRU_INTC_HCR_REG 0x00C 111 | #define PRU_INTC_GER_REG 0x010 112 | #define PRU_INTC_GNLR_REG 0x01C 113 | #define PRU_INTC_SISR_REG 0x020 114 | #define PRU_INTC_SICR_REG 0x024 115 | #define PRU_INTC_EISR_REG 0x028 116 | #define PRU_INTC_EICR_REG 0x02C 117 | #define PRU_INTC_HIEISR_REG 0x034 118 | #define PRU_INTC_HIDISR_REG 0x038 119 | #define PRU_INTC_GPIR_REG 0x080 120 | 121 | #define PRU_INTC_SRSR1_REG 0x200 122 | #define PRU_INTC_SRSR2_REG 0x204 123 | 124 | #define PRU_INTC_SECR1_REG 0x280 125 | #define PRU_INTC_SECR2_REG 0x284 126 | 127 | #define PRU_INTC_ESR1_REG 0x300 128 | #define PRU_INTC_ESR2_REG 0x304 129 | 130 | #define PRU_INTC_ECR1_REG 0x380 131 | #define PRU_INTC_ECR2_REG 0x384 132 | 133 | #define PRU_INTC_CMR1_REG 0x400 134 | #define PRU_INTC_CMR2_REG 0x404 135 | #define PRU_INTC_CMR3_REG 0x408 136 | #define PRU_INTC_CMR4_REG 0x40C 137 | #define PRU_INTC_CMR5_REG 0x410 138 | #define PRU_INTC_CMR6_REG 0x414 139 | #define PRU_INTC_CMR7_REG 0x418 140 | #define PRU_INTC_CMR8_REG 0x41C 141 | #define PRU_INTC_CMR9_REG 0x420 142 | #define PRU_INTC_CMR10_REG 0x424 143 | #define PRU_INTC_CMR11_REG 0x428 144 | #define PRU_INTC_CMR12_REG 0x42C 145 | #define PRU_INTC_CMR13_REG 0x430 146 | #define PRU_INTC_CMR14_REG 0x434 147 | #define PRU_INTC_CMR15_REG 0x438 148 | #define PRU_INTC_CMR16_REG 0x43C 149 | 150 | #define PRU_INTC_HMR1_REG 0x800 151 | #define PRU_INTC_HMR2_REG 0x804 152 | #define PRU_INTC_HMR3_REG 0x808 153 | 154 | #define PRU_INTC_SIPR1_REG 0xD00 155 | #define PRU_INTC_SIPR2_REG 0xD04 156 | 157 | #define PRU_INTC_SITR1_REG 0xD80 158 | #define PRU_INTC_SITR2_REG 0xD84 159 | 160 | #define PRU_INTC_HIER_REG 0x1500 161 | 162 | 163 | #define MAX_HOSTS_SUPPORTED 10 164 | 165 | //UIO driver expects user space to map PRUSS_UIO_MAP_OFFSET_XXX to 166 | //access corresponding memory regions - region offset is N*PAGE_SIZE 167 | 168 | #define PRUSS_UIO_MAP_OFFSET_PRUSS 0*PAGE_SIZE 169 | #define PRUSS_UIO_DRV_PRUSS_BASE "/sys/class/uio/uio0/maps/map0/addr" 170 | #define PRUSS_UIO_DRV_PRUSS_SIZE "/sys/class/uio/uio0/maps/map0/size" 171 | 172 | #ifndef DISABLE_L3RAM_SUPPORT 173 | 174 | #define PRUSS_UIO_MAP_OFFSET_L3RAM 1*PAGE_SIZE 175 | #define PRUSS_UIO_DRV_L3RAM_BASE "/sys/class/uio/uio0/maps/map1/addr" 176 | #define PRUSS_UIO_DRV_L3RAM_SIZE "/sys/class/uio/uio0/maps/map1/size" 177 | 178 | #define PRUSS_UIO_MAP_OFFSET_EXTRAM 2*PAGE_SIZE 179 | #define PRUSS_UIO_DRV_EXTRAM_BASE "/sys/class/uio/uio0/maps/map2/addr" 180 | #define PRUSS_UIO_DRV_EXTRAM_SIZE "/sys/class/uio/uio0/maps/map2/size" 181 | 182 | #else 183 | 184 | #define PRUSS_UIO_MAP_OFFSET_EXTRAM 1*PAGE_SIZE 185 | #define PRUSS_UIO_DRV_EXTRAM_BASE "/sys/class/uio/uio0/maps/map1/addr" 186 | #define PRUSS_UIO_DRV_EXTRAM_SIZE "/sys/class/uio/uio0/maps/map1/size" 187 | 188 | 189 | #endif 190 | 191 | 192 | typedef struct __prussdrv { 193 | int version; 194 | int fd[NUM_PRU_HOSTIRQS]; 195 | void *pru0_dataram_base; 196 | void *pru1_dataram_base; 197 | void *intc_base; 198 | void *pru0_control_base; 199 | void *pru0_debug_base; 200 | void *pru1_control_base; 201 | void *pru1_debug_base; 202 | void *pru0_iram_base; 203 | void *pru1_iram_base; 204 | void *l3ram_base; 205 | void *extram_base; 206 | pthread_t irq_thread[NUM_PRU_HOSTIRQS]; 207 | int mmap_fd; 208 | void *pruss_sharedram_base; 209 | void *pruss_cfg_base; 210 | void *pruss_uart_base; 211 | void *pruss_iep_base; 212 | void *pruss_ecap_base; 213 | void *pruss_miirt_base; 214 | void *pruss_mdio_base; 215 | unsigned int pru0_dataram_phy_base; 216 | unsigned int pru1_dataram_phy_base; 217 | unsigned int intc_phy_base; 218 | unsigned int pru0_control_phy_base; 219 | unsigned int pru0_debug_phy_base; 220 | unsigned int pru1_control_phy_base; 221 | unsigned int pru1_debug_phy_base; 222 | unsigned int pru0_iram_phy_base; 223 | unsigned int pru1_iram_phy_base; 224 | unsigned int l3ram_phy_base; 225 | unsigned int extram_phy_base; 226 | unsigned int pruss_sharedram_phy_base; 227 | unsigned int pruss_cfg_phy_base; 228 | unsigned int pruss_uart_phy_base; 229 | unsigned int pruss_iep_phy_base; 230 | unsigned int pruss_ecap_phy_base; 231 | unsigned int pruss_miirt_phy_base; 232 | unsigned int pruss_mdio_phy_base; 233 | unsigned int pruss_phys_base; 234 | unsigned int pruss_map_size; 235 | unsigned int l3ram_phys_base; 236 | unsigned int l3ram_map_size; 237 | unsigned int extram_phys_base; 238 | unsigned int extram_map_size; 239 | } tprussdrv; 240 | 241 | 242 | int __pruss_detect_hw_version(unsigned int *pruss_io) 243 | { 244 | 245 | if (pruss_io[(AM18XX_INTC_PHYS_BASE - AM18XX_DATARAM0_PHYS_BASE) >> 2] 246 | == AM18XX_PRUSS_INTC_REV) 247 | return PRUSS_V1; 248 | else { 249 | if (pruss_io 250 | [(AM33XX_INTC_PHYS_BASE - AM33XX_DATARAM0_PHYS_BASE) >> 2] == 251 | AM33XX_PRUSS_INTC_REV) 252 | return PRUSS_V2; 253 | else 254 | return -1; 255 | } 256 | } 257 | 258 | void __prussintc_set_cmr(unsigned int *pruintc_io, unsigned short sysevt, 259 | unsigned short channel) 260 | { 261 | pruintc_io[(PRU_INTC_CMR1_REG + (sysevt & ~(0x3))) >> 2] |= 262 | ((channel & 0xF) << ((sysevt & 0x3) << 3)); 263 | 264 | } 265 | 266 | 267 | void __prussintc_set_hmr(unsigned int *pruintc_io, unsigned short channel, 268 | unsigned short host) 269 | { 270 | pruintc_io[(PRU_INTC_HMR1_REG + (channel & ~(0x3))) >> 2] = 271 | pruintc_io[(PRU_INTC_HMR1_REG + 272 | (channel & ~(0x3))) >> 2] | (((host) & 0xF) << 273 | (((channel) & 0x3) << 3)); 274 | 275 | } 276 | -------------------------------------------------------------------------------- /am335x/pasm/LICENCE.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * {module name} 3 | * 4 | * {module description} 5 | * 6 | * Copyright (C) {YEAR} Texas Instruments Incorporated - http://www.ti.com/ 7 | * 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions 11 | * are met: 12 | * 13 | * Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 16 | * Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the 19 | * distribution. 20 | * 21 | * Neither the name of Texas Instruments Incorporated nor the names of 22 | * its contributors may be used to endorse or promote products derived 23 | * from this software without specific prior written permission. 24 | * 25 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | * 37 | */ 38 | 39 | 40 | -------------------------------------------------------------------------------- /am335x/pasm/Makefile: -------------------------------------------------------------------------------- 1 | # Builds with whatever the host format is 2 | CC := gcc 3 | 4 | CFLAGS += \ 5 | -O3 \ 6 | -W \ 7 | -Wall \ 8 | -D_UNIX_ \ 9 | 10 | OBJS := \ 11 | pasm.o \ 12 | pasmpp.o \ 13 | pasmexp.o \ 14 | pasmop.o \ 15 | pasmdot.o \ 16 | pasmstruct.o \ 17 | pasmmacro.o \ 18 | 19 | all: pasm 20 | 21 | pasm: $(OBJS) 22 | $(CC) -o $@ $^ 23 | 24 | clean: 25 | $(RM) -f *.o 26 | 27 | 28 | -------------------------------------------------------------------------------- /am335x/pasm/pasmdbg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * pasmdbg.h 3 | * 4 | * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ 5 | * 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * Neither the name of Texas Instruments Incorporated nor the names of 20 | * its contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * 35 | */ 36 | 37 | 38 | /*=========================================================================== 39 | * Copyright (c) Texas Instruments Inc 2010-12 40 | * 41 | * Use of this software is controlled by the terms and conditions found in the 42 | * license agreement under which this software has been supplied or provided. 43 | * ============================================================================ 44 | */ 45 | 46 | 47 | /*=========================================================================== 48 | // PASM - PRU Assembler 49 | //--------------------------------------------------------------------------- 50 | // 51 | // File : pasmdbg.h 52 | // 53 | // Description: 54 | // File format for pView debugger debug file 55 | // 56 | //--------------------------------------------------------------------------- 57 | // Revision: 58 | // 21-Jun-13: 0.84 - Open source version 59 | ============================================================================*/ 60 | 61 | #define DBGFILE_NAMELEN_SHORT 64 62 | 63 | typedef struct _DBGFILE_HEADER { 64 | unsigned int FileID; 65 | #define DBGFILE_FILEID_VER3 (0x10150000 | 0x03) 66 | unsigned int LabelCount; /* Number of label records */ 67 | unsigned int LabelOffset; /* File offset to label records */ 68 | unsigned int FileCount; /* Number of file records */ 69 | unsigned int FileOffset; /* File offset to file records */ 70 | unsigned int CodeCount; /* Number of code records */ 71 | unsigned int CodeOffset; /* File offset to code records */ 72 | unsigned int EntryPoint; /* Program entrypoint */ 73 | unsigned int Flags; /* File format flags */ 74 | #define DBGHDR_FLAGS_BIGENDIAN 0x00000001 75 | } DBGFILE_HEADER; 76 | 77 | typedef struct _DBGFILE_LABEL { 78 | unsigned int AddrOffset; 79 | char Name[DBGFILE_NAMELEN_SHORT]; 80 | } DBGFILE_LABEL; 81 | 82 | typedef struct _DBGFILE_FILE { 83 | char SourceName[DBGFILE_NAMELEN_SHORT]; 84 | } DBGFILE_FILE; 85 | 86 | typedef struct _DBGFILE_CODE { 87 | unsigned char Flags; /* Record flags */ 88 | #define DBGFILE_CODE_FLG_FILEINFO 0x01 89 | #define DBGFILE_CODE_FLG_CANMAP 0x02 90 | unsigned char Resv8; /* Reserved */ 91 | unsigned short FileIndex; /* Source file index */ 92 | unsigned int Line; /* The line number */ 93 | unsigned int AddrOffset; /* Code address offset */ 94 | unsigned int CodeWord; /* Code */ 95 | } DBGFILE_CODE; 96 | 97 | 98 | -------------------------------------------------------------------------------- /am335x/pasm/pru_ins.h: -------------------------------------------------------------------------------- 1 | /* 2 | * pru_ins.h 3 | * 4 | * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ 5 | * 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * Neither the name of Texas Instruments Incorporated nor the names of 20 | * its contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * 35 | */ 36 | 37 | /*=========================================================================== 38 | * Copyright (c) Texas Instruments Inc 2010-12 39 | * 40 | * Use of this software is controlled by the terms and conditions found in the 41 | * license agreement under which this software has been supplied or provided. 42 | * ============================================================================ 43 | */ 44 | 45 | /*=========================================================================== 46 | // PASM - PRU Assembler 47 | //--------------------------------------------------------------------------- 48 | // 49 | // File : pru_ins.h 50 | // 51 | // Description: 52 | // Defines a data structre PRU_INST that can completely describe a 53 | // PRU opcode. 54 | // 55 | //--------------------------------------------------------------------------- 56 | // Revision: 57 | // 21-Jun-13: 0.84 - Open source version 58 | ============================================================================*/ 59 | 60 | typedef struct _PRU_ARG { 61 | uint Type; 62 | uint Flags; /* Flags for RegisterBit type */ 63 | #define PA_FLG_REGPOINTER 0x0001 64 | #define PA_FLG_POSTINC 0x0002 65 | #define PA_FLG_PREDEC 0x0004 66 | uint Value; /* Reg #, Imm Val, Count Val */ 67 | uint Field; /* Field for Registers */ 68 | uint Bit; /* Bit # for RegisterBit type */ 69 | } PRU_ARG; 70 | 71 | #define ARGTYPE_REGISTER 1 /* Standard register and field */ 72 | #define ARGTYPE_IMMEDIATE 2 /* Immediate value */ 73 | #define ARGTYPE_COUNT 3 /* Count for burst */ 74 | #define ARGTYPE_R0BYTE 4 /* Byte from R0 */ 75 | #define ARGTYPE_CONSTANT 5 /* Constant Table Index */ 76 | #define ARGTYPE_OFFSET 6 /* 10 bit offset for jumps */ 77 | #define ARGTYPE_REGISTERBIT 7 /* Register in Rxx.Txx format Field=bitno */ 78 | 79 | #define FIELDTYPE_7_0 0 /* Bits 7:0 */ 80 | #define FIELDTYPE_15_8 1 /* Bits 15:8 */ 81 | #define FIELDTYPE_23_16 2 /* Bits 23:16 */ 82 | #define FIELDTYPE_31_24 3 /* Bits 31:24 */ 83 | #define FIELDTYPE_15_0 4 /* Bits 15:0 */ 84 | #define FIELDTYPE_23_8 5 /* Bits 23:8 */ 85 | #define FIELDTYPE_31_16 6 /* Bits 31:16 */ 86 | #define FIELDTYPE_31_0 7 /* Bits 31:0 */ 87 | 88 | #define FIELDTYPE_OFF_0 0 /* Offset bit 0 */ 89 | #define FIELDTYPE_OFF_8 1 /* Offset bit 8 */ 90 | #define FIELDTYPE_OFF_16 2 /* Offset bit 16 */ 91 | #define FIELDTYPE_OFF_24 3 /* Offset bit 24 */ 92 | 93 | extern char *FieldText[]; 94 | 95 | typedef struct _PRU_INST { 96 | uint Op; /* Operation */ 97 | uint ArgCnt; /* Argument Count */ 98 | PRU_ARG Arg[4]; /* Arguments */ 99 | } PRU_INST; 100 | 101 | #define OP_ADD 1 102 | #define OP_ADC 2 103 | #define OP_SUB 3 104 | #define OP_SUC 4 105 | #define OP_LSL 5 106 | #define OP_LSR 6 107 | #define OP_RSB 7 108 | #define OP_RSC 8 109 | #define OP_AND 9 110 | #define OP_OR 10 111 | #define OP_XOR 11 112 | #define OP_NOT 12 113 | #define OP_MIN 13 114 | #define OP_MAX 14 115 | #define OP_CLR 15 116 | #define OP_SET 16 117 | #define OP_LDI 17 118 | #define OP_LBBO 18 119 | #define OP_LBCO 19 120 | #define OP_SBBO 20 121 | #define OP_SBCO 21 122 | #define OP_LFC 22 123 | #define OP_STC 23 124 | #define OP_JAL 24 125 | #define OP_JMP 25 126 | #define OP_QBGT 26 127 | #define OP_QBLT 27 128 | #define OP_QBEQ 28 129 | #define OP_QBGE 29 130 | #define OP_QBLE 30 131 | #define OP_QBNE 31 132 | #define OP_QBA 32 133 | #define OP_QBBS 33 134 | #define OP_QBBC 34 135 | #define OP_LMBD 35 136 | #define OP_CALL 36 137 | #define OP_WBC 37 138 | #define OP_WBS 38 139 | #define OP_MOV 39 140 | #define OP_MVIB 40 141 | #define OP_MVIW 41 142 | #define OP_MVID 42 143 | #define OP_SCAN 43 144 | #define OP_HALT 44 145 | #define OP_SLP 45 146 | #define OP_RET 46 147 | #define OP_ZERO 47 148 | #define OP_FILL 48 149 | #define OP_XIN 49 150 | #define OP_XOUT 50 151 | #define OP_XCHG 51 152 | #define OP_SXIN 52 153 | #define OP_SXOUT 53 154 | #define OP_SXCHG 54 155 | #define OP_LOOP 55 156 | #define OP_ILOOP 56 157 | #define OP_NOP0 57 158 | #define OP_NOP1 58 159 | #define OP_NOP2 59 160 | #define OP_NOP3 60 161 | #define OP_NOP4 61 162 | #define OP_NOP5 62 163 | #define OP_NOP6 63 164 | #define OP_NOP7 64 165 | #define OP_NOP8 65 166 | #define OP_NOP9 66 167 | #define OP_NOPA 67 168 | #define OP_NOPB 68 169 | #define OP_NOPC 69 170 | #define OP_NOPD 70 171 | #define OP_NOPE 71 172 | #define OP_NOPF 72 173 | #define OP_MAXIDX 72 174 | 175 | extern char *OpText[]; 176 | 177 | -------------------------------------------------------------------------------- /bin/.empty: -------------------------------------------------------------------------------- 1 | git: please make this directory 2 | -------------------------------------------------------------------------------- /brackets/flat-bracket.scad: -------------------------------------------------------------------------------- 1 | /** \file 2 | * mounting bracket for 15mm extrusion. 3 | */ 4 | 5 | 6 | module bracket() 7 | { 8 | translate([0,0,-3]) linear_extrude(height=3) 9 | polygon([[0,0], [8,0], [8,3], [3,15], [0,15]]); 10 | 11 | cube([8,3,8]); 12 | translate([0,0,-10]) cube([3,15,8]); 13 | 14 | } 15 | 16 | /* 17 | translate([15/2,0,3/2]) render() difference() { 18 | cube([15,10,3], center=true); 19 | translate([7.5/2,1,-2]) cylinder(r=4/2, h=5, $fs=1); 20 | translate([-7.5,1,3]) rotate([0,90,0]) cylinder(r=4,h=4); 21 | } 22 | 23 | translate([0,0,10/2]) rotate([0,90,0]) render() difference() { 24 | cube([10,10,3], center=true); 25 | translate([1,1,-2]) cylinder(r=4/2, h=5, $fs=1); 26 | } 27 | */ 28 | 29 | 30 | module right() 31 | { 32 | render() difference() 33 | { 34 | bracket(); 35 | translate([2.5,4,8/2]) rotate([90,0,0]) cylinder(r=3.8/2, h=5, $fs=1); 36 | translate([-1,7.5,-10+4]) rotate([0,90,0]) cylinder(r=4/2, h=5, $fs=1); 37 | } 38 | } 39 | 40 | right(); 41 | 42 | translate([0,0,20]) scale([1,1,-1]) right(); 43 | -------------------------------------------------------------------------------- /brackets/matrix-bracket.scad: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Bracket to hold a 16x2 LED panel in an Octoscroller configuration. 3 | */ 4 | 5 | module bracket_half() 6 | { 7 | render() difference() 8 | { 9 | cube([18,7,10]); 10 | 11 | translate([13,10,5]) 12 | rotate([90,0,0]) 13 | cylinder(r=2, h=20, $fs=1); 14 | } 15 | } 16 | 17 | module bracket() 18 | { 19 | rotate([0,0,+45/2]) bracket_half(); 20 | rotate([0,0,-45/2-180]) translate([0,-7,0]) bracket_half(); 21 | } 22 | 23 | for (i = [0:1]) 24 | { 25 | translate([i*35,0,0]) { 26 | for (j = [0:7]) { 27 | translate([0,10*j,0]) bracket(); 28 | } 29 | } 30 | } 31 | 32 | %translate([0,0,-1]) cube([200,200,2], center=true); 33 | -------------------------------------------------------------------------------- /brackets/octoscroller.scad: -------------------------------------------------------------------------------- 1 | /** \file 2 | * 3D printed brackets for N sided LED matrix displays. 3 | * 4 | * Horizontal or vertical connections are possible. 5 | * The spacing between the holes differs for each. 6 | */ 7 | sides = 32; 8 | 9 | // horizontal has 13 mm from edge to center of hole. 10 | module horizontal_bracket() 11 | { 12 | rotate([0,0,-360/sides/2]) 13 | render() difference() { 14 | translate([-20,0,]) cube([20,6,10]); 15 | translate([-13,15,5]) rotate([90,0,0]) cylinder(r=3,h=20); 16 | } 17 | 18 | rotate([0,0,+360/sides/2]) 19 | render() difference() { 20 | translate([0,0,]) cube([20,6,10]); 21 | translate([13,15,5]) rotate([90,0,0]) cylinder(r=3,h=20); 22 | } 23 | } 24 | 25 | // vertical has only 10mm 26 | module vertical_bracket(depth) 27 | { 28 | rotate([0,0,-360/sides/2]) 29 | render() difference() { 30 | translate([-16,0,]) cube([16,depth,10]); 31 | translate([-10,1+depth,5]) rotate([90,0,0]) cylinder(r=3/2+0.4,h=depth+2, $fs=1); 32 | } 33 | 34 | rotate([0,0,+360/sides/2]) 35 | render() difference() { 36 | translate([0,0,]) cube([16,depth,10]); 37 | translate([10,1+depth,5]) rotate([90,0,0]) cylinder(r=3/2+0.4,h=depth+2, $fs=1); 38 | } 39 | } 40 | 41 | // combo bracket has only 10mm, with 13*2 mm spacing 42 | module vertical_bracket2() 43 | { 44 | rotate([0,0,-360/sides/2]) 45 | render() difference() { 46 | translate([-16,0,]) cube([16,8,26+5+5]); 47 | translate([-10,15,5]) rotate([90,0,0]) cylinder(r=3.5/2,h=20, $fs=1); 48 | translate([-10,15,26+5]) rotate([90,0,0]) cylinder(r=3.5/2,h=20, $fs=1); 49 | } 50 | 51 | rotate([0,0,+360/sides/2]) 52 | render() difference() { 53 | translate([0,0,]) cube([16,8,26+5+5]); 54 | translate([10,15,5]) rotate([90,0,0]) cylinder(r=3.5/2,h=20, $fs=1); 55 | translate([10,15,26+5]) rotate([90,0,0]) cylinder(r=3.5/2,h=20, $fs=1); 56 | } 57 | 58 | translate([-7,3.5,0]) cube([14,5,26+5+5]); 59 | } 60 | 61 | 62 | // bracket 3 has the normal vertical bracket, with an additional 63 | // bit to secure the 15mm extrusion. 64 | module vertical_bracket3() 65 | { 66 | vertical_bracket(12); 67 | 68 | render() difference() { 69 | union() { 70 | rotate([0,0,+360/sides/2]) translate([0,0,9.9]) cube([16,12,13+5+15-10]); 71 | rotate([0,0,-360/sides/2]) translate([-16,0,10]) cube([16,12,13+5+15-10]); 72 | } 73 | 74 | // subtract the extrusion 75 | translate([-15/2,-10,5+13]) cube([15,50,25]); 76 | 77 | // m3 screw holes to secure the extrusion to the bracket 78 | translate([+18,7,13+15/2+5]) rotate([0,90,180]) union() { 79 | cylinder(r=3/2+0.4, h=12, $fs=1); 80 | cylinder(r=6/2+0.4, h=4, $fs=1); 81 | } 82 | translate([-18,7,13+15/2+5]) rotate([0,90,0]) union() { 83 | cylinder(r=3/2+0.4, h=12, $fs=1); 84 | cylinder(r=6/2+0.4, h=4, $fs=1); 85 | } 86 | } 87 | } 88 | 89 | 90 | // the T-bracket is for securing the center struts 91 | module t_bracket() 92 | { 93 | render() difference() 94 | { 95 | translate([0,0,5/2]) 96 | union() { 97 | intersection() { 98 | rotate([0,0,45]) cube([50,50,5], center=true); 99 | cube([50,50,5], center=true); 100 | } 101 | rotate([0,0,0]) translate([0,0,10]) cube([21,50,20], center=true); 102 | rotate([0,0,90]) translate([0,0,10]) cube([21,50,20], center=true); 103 | rotate([0,0,45]) translate([0,0,10]) cube([35,35,20], center=true); 104 | } 105 | 106 | translate([0,0,20/2+5]) rotate([0,0,90]) cube([100,15,20], center=true); 107 | translate([0,0,20/2+5]) rotate([0,0,0]) cube([100,15,20], center=true); 108 | 109 | // and the screw holes 110 | translate([-20,+20,15/2+5]) rotate([0,90,0]) cylinder(r=3/2+0.4, h=40, $fs=1); 111 | translate([-20,-20,15/2+5]) rotate([0,90,0]) cylinder(r=3/2+0.4, h=40, $fs=1); 112 | translate([-20,-20,15/2+5]) rotate([0,90,90]) cylinder(r=3/2+0.4, h=40, $fs=1); 113 | translate([+20,-20,15/2+5]) rotate([0,90,90]) cylinder(r=3/2+0.4, h=40, $fs=1); 114 | } 115 | 116 | } 117 | 118 | 119 | if (0) 120 | { 121 | for (i = [0:3]) 122 | { 123 | for (j = [0:3]) 124 | { 125 | translate([i*15, j*37,0]) 126 | rotate([0,0,90]) 127 | vertical_bracket2(); 128 | } 129 | } 130 | } else { 131 | //vertical_bracket2(); 132 | //vertical_bracket3(); 133 | t_bracket(); 134 | } 135 | -------------------------------------------------------------------------------- /brackets/spiral.scad: -------------------------------------------------------------------------------- 1 | /** \file 2 | * 3D printed brackets for spiral matrix display 3 | * 4 | * Horizontal or vertical connections are possible. 5 | * The spacing between the holes differs for each. 6 | */ 7 | use 8 | 9 | 10 | // vertical has only 10mm 11 | module vertical_bracket(sides, depth) 12 | { 13 | render() difference() 14 | { 15 | 16 | union() { 17 | rotate([0,0,-360/sides/2]) 18 | render() difference() { 19 | translate([-16,0,]) cube([16,depth,10]); 20 | translate([-10,1+depth,5]) rotate([90,0,0]) 21 | { 22 | cylinder(r=3/2+0.4,h=depth+2, $fs=1); 23 | cylinder(r=6/2+0.4,h=3, $fs=1); 24 | } 25 | } 26 | 27 | rotate([0,0,+360/sides/2]) 28 | render() difference() { 29 | translate([0,0,]) cube([16,depth,10]); 30 | translate([10,1+depth,5]) rotate([90,0,0]) 31 | { 32 | cylinder(r=3/2+0.4,h=depth+2, $fs=1); 33 | cylinder(r=6/2+0.4,h=3, $fs=1); 34 | } 35 | } 36 | } 37 | 38 | translate([0,4.5,10]) rotate([0,0,180]) scale(1.5) write(str(sides), center=true); 39 | } 40 | } 41 | 42 | for (i=[0:23]) 43 | { 44 | translate([(i%6)*35, floor(i/6)*30, 0]) 45 | { 46 | translate([0,-6,0]) vertical_bracket(i+5, 8); 47 | translate([0,+6,0]) vertical_bracket(i+5, 8); 48 | } 49 | } 50 | 51 | %translate([0,0,-0.5]) cube([285,153,1]); 52 | -------------------------------------------------------------------------------- /default.config: -------------------------------------------------------------------------------- 1 | matrix16 2 | 0,0 N 0,0 3 | 0,1 N 32,0 4 | 0,2 N 64,0 5 | 0,3 N 96,0 6 | 0,4 N 128,0 7 | 0,5 N 160,0 8 | 0,6 N 192,0 9 | 0,7 N 224,0 10 | 1,0 N 0,16 11 | 1,1 N 32,16 12 | 1,2 N 64,16 13 | 1,3 N 96,16 14 | 1,4 N 128,16 15 | 1,5 N 160,16 16 | 1,6 N 192,16 17 | 1,7 N 224,16 18 | 2,0 N 0,32 19 | 2,1 N 32,32 20 | 2,2 N 64,32 21 | 2,3 N 96,32 22 | 2,4 N 128,32 23 | 2,5 N 160,32 24 | 2,6 N 192,32 25 | 2,7 N 224,32 26 | 3,0 N 0,48 27 | 3,1 N 32,48 28 | 3,2 N 64,48 29 | 3,3 N 96,48 30 | 3,4 N 128,48 31 | 3,5 N 160,48 32 | 3,6 N 192,48 33 | 3,7 N 224,48 34 | 4,0 N 0,64 35 | 4,1 N 32,64 36 | 4,2 N 64,64 37 | 4,3 N 96,64 38 | 4,4 N 128,64 39 | 4,5 N 160,64 40 | 4,6 N 192,64 41 | 4,7 N 224,64 42 | 5,0 N 0,80 43 | 5,1 N 32,80 44 | 5,2 N 64,80 45 | 5,3 N 96,80 46 | 5,4 N 128,80 47 | 5,5 N 160,80 48 | 5,6 N 192,80 49 | 5,7 N 224,80 50 | 6,0 N 0,96 51 | 6,1 N 32,96 52 | 6,2 N 64,96 53 | 6,3 N 96,96 54 | 6,4 N 128,96 55 | 6,5 N 160,96 56 | 6,6 N 192,96 57 | 6,7 N 224,96 58 | 7,0 N 0,112 59 | 7,1 N 32,112 60 | 7,2 N 64,112 61 | 7,3 N 96,112 62 | 7,4 N 128,112 63 | 7,5 N 160,112 64 | 7,6 N 192,112 65 | 7,7 N 224,112 66 | -------------------------------------------------------------------------------- /dts/CAPE-BONE-OCTO-00A0.dtbo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osresearch/LEDscape/f94708c188f593e6313bcddee3cedc47d24989d0/dts/CAPE-BONE-OCTO-00A0.dtbo -------------------------------------------------------------------------------- /dts/LEDscape.dts: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Try to play nice with the Beagle Bone device tree. 3 | * 4 | * But it really sucks. This is a waste of time; we can just use 5 | * a shell script to poke the gpio export and config files since this 6 | * is so borked. 7 | */ 8 | /dts-v1/; 9 | /plugin/; 10 | 11 | / { 12 | compatible = "ti,beaglebone-black"; 13 | 14 | /* identification */ 15 | part-number = "BB-LEDSCAPE"; 16 | version = "00A0"; 17 | dtbo = "cape-ledscape-00A0.dtbo"; 18 | 19 | /* state the resources this cape uses or prepare to get winged! */ 20 | exclusive-use = 21 | /* 32 IO pins for the PRU to use */ 22 | "P8.7", "P8.8", 23 | "P8.9", "P8.10", 24 | "P8.11", "P8.12", 25 | "P8.13", "P8.14", 26 | "P8.15", "P8.16", 27 | "P8.17", "P8.18", 28 | "P8.19", 29 | "P8.26", 30 | 31 | "P9.11", "P9.12", 32 | "P9.13", "P9.14", 33 | "P9.15", "P9.16", 34 | "P9.17", "P9.18", 35 | "P9.19", "P9.20", // overrides the i2c bus 36 | "P9.21", "P9.22", 37 | "P9.23", "P9.24", 38 | "P9.26", 39 | "P9.27", 40 | "P9.41", "P9.42", 41 | 42 | // And we need the PRU 43 | "pruss"; 44 | 45 | fragment@0 { 46 | target = <&am33xx_pinmux>; 47 | __overlay__ { 48 | pruicss_ws281x_pins: pinmux_pruicss_ws281x_pins { 49 | // Mode 7 is GPIO output 50 | // GPIO0: 2 3 4 5 7 12 13 14 15 20 22 23 26 27 30 31 51 | pinctrl-single,pins = < 52 | 0x090 0x7 /* P8.7 gpio2.2 */ 53 | 0x094 0x7 // P8.8 gpio2.3 54 | 0x09c 0x7 // P8.9 gpio2.5 55 | 0x098 0x7 // P8.10 gpio 2.4 56 | 0x034 0x7 // P8.11 gpio1.13 57 | 0x030 0x7 // P8.12 gpio1.12 58 | 0x024 0x7 // P8.13 gpio0.23 59 | 0x028 0x7 // P8.14 gpio0.26 60 | 0x03c 0x7 // P8.15 gpio1.15 61 | 0x038 0x7 // P8.16 gpio1.14 62 | 0x02c 0x7 // P8.17 gpio0.27 63 | 0x08c 0x7 // P8.18 gpio2.1 64 | 0x020 0x7 // P8.19 gpio0.22 65 | 0x07c 0x7 // P8.26 gpio1.29 66 | 67 | 0x070 0x7 // P9.11 gpio0.30 68 | 0x078 0x7 // P9.12 gpio1.28 69 | 0x074 0x7 // P9.13 gpio0.31 70 | 0x048 0x7 // P9.14 gpio1.18 71 | 0x040 0x7 // P9.15 gpio1.16 72 | 0x04c 0x7 // P9.16 gpio1.19 73 | 0x15c 0x7 // P9.17 gpio0.5 74 | 0x158 0x7 // P9.18 gpio0.4 75 | 0x17e 0x7 // P9.19 gpio0.13, overrides i2c 76 | 0x178 0x7 // P9.20 gpio0.12, overrides i2c 77 | 0x154 0x7 // P9.21 gpio0.3 78 | 0x150 0x7 // P9.22 gpio0.2 79 | 0x044 0x7 // P9.23 gpio1.17 80 | 0x184 0x7 // P9.24 gpio0.15 81 | 0x180 0x7 // P9.26 gpio0.14 82 | //0x1a4 0x7 // P9.27 gpio3.19 83 | //0x198 0x7 // P9.30 gpio3.16 84 | 0x1b4 0x7 // P9.41 gpio0.20 85 | 0x164 0x7 // P9.42 gpio0.7 86 | >; 87 | }; 88 | }; 89 | }; 90 | 91 | /* 92 | fragment@1 { 93 | target = <&ocp>; 94 | __overlay__ { 95 | ws281x_pinmux_helper: helper { 96 | compatible = "bone-pinmux-helper"; 97 | pinctrl-names = "default"; 98 | pinctrl-0 = <&pinctrl_ws281x>; 99 | status = "okay"; 100 | }; 101 | }; 102 | }; 103 | */ 104 | 105 | fragment@1{ 106 | target = <&pruss>; 107 | __overlay__{ 108 | status = "okay"; 109 | pinctrl-names = "default"; 110 | pinctrl-0 = <&pruicss_ws281x_pins>; 111 | led_strips { 112 | pin-names = 113 | "s:0", 114 | "s:1", 115 | "s:2", 116 | "s:3", 117 | "s:4", 118 | "s:5", 119 | "s:6", 120 | "s:7", 121 | "s:8", 122 | "s:9", 123 | "s:10", 124 | "s:11", 125 | "s:12", 126 | "s:13", 127 | "s:14", 128 | "s:15", 129 | "s:16", 130 | "s:17", 131 | "s:18", 132 | "s:19", 133 | "s:20", 134 | "s:21", 135 | "s:22", 136 | "s:23", 137 | "s:24", 138 | "s:25", 139 | "s:26", 140 | "s:27", 141 | "s:28", 142 | "s:29", 143 | "s:30", 144 | "s:31"; 145 | 146 | gpios = < 147 | &gpio0 2 0 148 | &gpio0 3 0 149 | &gpio0 4 0 150 | &gpio0 5 0 151 | &gpio0 7 0 152 | &gpio0 12 0 153 | &gpio0 13 0 154 | &gpio0 14 0 155 | &gpio0 15 0 156 | &gpio0 20 0 157 | &gpio0 22 0 158 | &gpio0 23 0 159 | &gpio0 26 0 160 | &gpio0 27 0 161 | &gpio0 30 0 162 | &gpio0 31 0 163 | &gpio1 12 0 164 | &gpio1 13 0 165 | &gpio1 14 0 166 | &gpio1 15 0 167 | &gpio1 16 0 168 | &gpio1 17 0 169 | &gpio1 18 0 170 | &gpio1 19 0 171 | &gpio1 28 0 172 | &gpio1 29 0 173 | &gpio2 1 0 174 | &gpio2 2 0 175 | &gpio2 3 0 176 | &gpio2 4 0 177 | &gpio2 5 0 178 | &gpio2 29 0 179 | //&gpio3 16 0 180 | //&gpio3 19 0 181 | >; 182 | }; 183 | }; 184 | }; 185 | }; 186 | -------------------------------------------------------------------------------- /dts/README.md: -------------------------------------------------------------------------------- 1 | Generate DTS from DTB: 2 | 3 | dtc \ 4 | -I dtb \ 5 | -O dts \ 6 | -o ubuntu-`uname -r`.dts \ 7 | /boot/uboot/dtbs/am335x-boneblack.dtb 8 | 9 | Enable the PRU. Change status from "disabled" to "okay" 10 | 11 | 12 | Generate DTB back from DTS: 13 | 14 | dtc \ 15 | -O dtb \ 16 | -I dts \ 17 | -o /boot/uboot/dtbs/am335x-boneblack.dtb \ 18 | ubuntu-`uname -r`.dts 19 | -------------------------------------------------------------------------------- /dts/angstrom.dtb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osresearch/LEDscape/f94708c188f593e6313bcddee3cedc47d24989d0/dts/angstrom.dtb -------------------------------------------------------------------------------- /dts/cape-bone-octo.dts: -------------------------------------------------------------------------------- 1 | /* 2 | * pru dts file BB-BONE-PRU-00A0.dts 3 | * 4 | * Available outputs on pru 0 and their r30 pins: 5 | * p9.31: 0 6 | * p9.29: 1 7 | * p9.30: 2 8 | * p9.28: 3 9 | * p9.27: 5 10 | * p9.25: 7 11 | * p8.12: 14 12 | * p8.11: 15 13 | 14 | gpio 0: 23, 27, 22, 10, 9, 8, 26, 11, 30, 31, 5, 3, 20, 4, 2, 14, 7 15 | gpio 1: 13, 15, 12, 14, 29, 16, 17, 28, 18, 19, 16 | gpio 2: 2, 5, 22, 23, 14, 12, 10, 8, 6, 3, 4, 1, 24, 25, 17, 16, 15, 13, 11, 9, 7, 17 | gpio 3: 21, 19, 15, 14, 17, 16 18 | 19 | */ 20 | /dts-v1/; 21 | /plugin/; 22 | 23 | / { 24 | compatible = "ti,beaglebone", "ti,beaglebone-black"; 25 | 26 | /* identification */ 27 | part-number = "BB-BONE-OCTO"; 28 | version = "00A0"; 29 | 30 | exclusive-use = 31 | //gpio 0: 23, 27, 22, 10, 9, 8, 26, 11, 30, 31, 5, 3, 20, 4, 2, 14, 7 32 | "P9.11", "P9.12", "P9.13", "P9.14", "P9.15", "P9.16", "P9.17", 33 | "P9.18", 34 | 35 | "P9.21", "P9.22", "P9.23", "P9.24", "P9.25", "P9.26", 36 | "P9.27", "P9.28", "P9.29", "P9.30", 37 | 38 | "P9.41", "P9.42", 39 | 40 | "P8.7", "P8.8", "P8.9", "P8.10", "P8.11", "P8.12", "P8.13", 41 | "P8.14", "P8.15", "P8.16", "P8.17", "P8.18", "P8.19", 42 | 43 | "P8.26", "P8.27", "P8.28", "P8.29", "P8.30", "P8.31", "P8.32", 44 | "P8.33", "P8.34", "P8.35", "P8.36", "P8.37", "P8.38", "P8.39", 45 | "P8.40", "P8.41", "P8.42", "P8.43", "P8.44", "P8.45", "P8.46", 46 | 47 | "pruss", 48 | "ehrpwm1B"; 49 | 50 | fragment@0 { 51 | target = <&am33xx_pinmux>; 52 | __overlay__ { 53 | mygpio: pinmux_mygpio { 54 | pinctrl-single,pins = < 55 | // p9.11-18 56 | 0x070 0x7 57 | 0x078 0x7 58 | 0x074 0x7 59 | 0x048 0x7 60 | 0x040 0x7 61 | 0x04c 0x7 // P9.16 could be PWM for output enable 62 | 0x15c 0x7 63 | 0x158 0x7 64 | 65 | // p9.21-29 66 | 0x154 0x7 67 | 0x150 0x7 68 | 0x044 0x7 69 | 0x184 0x7 70 | //0x1AC 0x5 // PRU0 r30.7 for the clock line 71 | 0x1AC 0x7 // GPIO 72 | 0x180 0x7 73 | 0x1A4 0x7 74 | 0x19C 0x7 75 | 0x194 0x7 76 | 77 | // p9.41-42, but alternate functions to input 78 | 0x1B4 0x7 // gpio0.20 as output 79 | 0x164 0x7 // gpio0.7 as output 80 | 0x1A8 0x27 // gpio3.18 as input 81 | 0x1A0 0x27 // gpio3.18 as input 82 | 83 | // p8.7-19 84 | 0x090 0x7 85 | 0x094 0x7 86 | 0x09c 0x7 87 | 0x098 0x7 88 | 0x034 0x7 89 | 0x030 0x7 90 | 0x024 0x7 91 | 0x028 0x7 92 | 0x03c 0x7 93 | 0x038 0x7 94 | 0x02c 0x7 95 | 0x08c 0x7 96 | 0x020 0x7 97 | 98 | // p8.26-46 99 | 0x07c 0x7 100 | 0x0e0 0x7 101 | 0x0e8 0x7 102 | 0x0e4 0x7 103 | 0x0ec 0x7 104 | 0x0d8 0x7 105 | 0x0dc 0x7 106 | 0x0d4 0x7 107 | 0x0cc 0x7 108 | 0x0d4 0x7 109 | 0x0cc 0x7 110 | 0x0d0 0x7 111 | 0x0c8 0x7 112 | 0x0c0 0x7 113 | 0x0c4 0x7 114 | 0x0b8 0x7 115 | 0x0bc 0x7 116 | 0x0b0 0x7 117 | 0x0b4 0x7 118 | 0x0a8 0x7 119 | 0x0ac 0x7 120 | 0x0a0 0x7 121 | 0x0a4 0x7 122 | >; 123 | }; 124 | }; 125 | }; 126 | 127 | fragment@1 { 128 | target = <&ocp>; 129 | __overlay__ { 130 | test_helper: helper { 131 | compatible = "bone-pinmux-helper"; 132 | pinctrl-names = "default"; 133 | pinctrl-0 = <&mygpio>; 134 | status = "okay"; 135 | }; 136 | }; 137 | }; 138 | 139 | fragment@2{ 140 | target = <&pruss>; 141 | __overlay__ { 142 | status = "okay"; 143 | }; 144 | }; 145 | 146 | /* 147 | // import of am335x_pwm-00A0.dts 148 | // but pru can't write to pwm? 149 | 150 | fragment@3 { 151 | target = <&epwmss0>; 152 | __overlay__ { 153 | status = "okay"; 154 | }; 155 | }; 156 | 157 | fragment@4 { 158 | target = <&ehrpwm0>; 159 | __overlay__ { 160 | status = "okay"; 161 | }; 162 | }; 163 | 164 | fragment@5 { 165 | target = <&ecap0>; 166 | __overlay__ { 167 | status = "okay"; 168 | }; 169 | }; 170 | 171 | fragment@6 { 172 | target = <&epwmss1>; 173 | __overlay__ { 174 | status = "okay"; 175 | }; 176 | }; 177 | 178 | fragment@7 { 179 | target = <&ehrpwm1>; 180 | __overlay__ { 181 | status = "okay"; 182 | }; 183 | }; 184 | 185 | fragment@8 { 186 | target = <&epwmss2>; 187 | __overlay__ { 188 | status = "okay"; 189 | }; 190 | }; 191 | 192 | fragment@9 { 193 | target = <&ehrpwm2>; 194 | __overlay__ { 195 | status = "okay"; 196 | }; 197 | }; 198 | 199 | fragment@10 { 200 | target = <&ecap2>; 201 | __overlay__ { 202 | status = "okay"; 203 | }; 204 | }; 205 | */ 206 | }; 207 | -------------------------------------------------------------------------------- /fonts/json2bmp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | use warnings; 3 | use strict; 4 | 5 | local $/; 6 | local $_ = <>; 7 | 8 | print <<""; 9 | /** \file 10 | * Generated font from json file output by 11 | * http://www.pentacom.jp/pentacom/bitfontmaker2/ 12 | */ 13 | const uint16_t font[][16] = { 14 | 15 | while (m/"(\d+)":\[([\d,]+)\]/msgx) 16 | { 17 | my $ord = $1; 18 | my @bits = split /,/, $2; 19 | 20 | my $c = chr($ord); 21 | $c = '\\\\' if $c eq '\\'; 22 | printf "['%s'] = {\n", $c; 23 | 24 | for (@bits) 25 | { 26 | printf "\t0x%04x,\n", $_; 27 | } 28 | 29 | print "},\n"; 30 | } 31 | 32 | print <<""; 33 | print< 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "ledscape.h" 13 | 14 | 15 | int 16 | main(void) 17 | { 18 | const int width = 128; 19 | const int height = 128; 20 | ledscape_t * const leds = ledscape_init(width, height); 21 | printf("init done\n"); 22 | time_t last_time = time(NULL); 23 | unsigned last_i = 0; 24 | 25 | uint32_t bits[32][32]; 26 | memset(bits, 0, sizeof(bits)); 27 | ssize_t rc = read(0, bits, sizeof(bits)); 28 | if (rc != sizeof(bits)) 29 | { 30 | fprintf(stderr, "only read %zu bytes\n", rc); 31 | return -1; 32 | } 33 | 34 | uint32_t * const p = calloc(4, width*height); 35 | 36 | for (int x = 0 ; x < 32 ; x++) 37 | { 38 | for (int y = 0 ; y < 32 ; y++) 39 | { 40 | uint32_t c = bits[x][y]; 41 | uint32_t r = (c >> 16) & 0xFF; 42 | uint32_t g = (c >> 8) & 0xFF; 43 | uint32_t b = (c >> 0) & 0xFF; 44 | r = r; 45 | g = g; 46 | b = b; 47 | c = (r << 0) | (g << 8) | (b << 16); 48 | printf("%d,%d %08x %d,%d,%d\n", x, y, c, r, g, b); 49 | p[width*((31-y)+0) + x] = c; 50 | p[width*((31-y)+0) + x + 32] = c; 51 | p[width*((31-x)+32) + (31-y)] = c; 52 | p[width*((31-x)+32) + (31-y) + 32] = c; 53 | p[width*((31-x)+64) + (31-y)] = c; 54 | p[width*((31-x)+64) + (31-y) + 32] = c; 55 | } 56 | } 57 | 58 | 59 | while (1) 60 | { 61 | ledscape_draw(leds, p); 62 | usleep(20000); 63 | } 64 | 65 | //ledscape_close(leds); 66 | 67 | return EXIT_SUCCESS; 68 | } 69 | -------------------------------------------------------------------------------- /src/demos/cube-life.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Play the game of life on the matrix cube. 3 | * 4 | * \todo this is broken until the matrix.p can handle different heights 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "ledscape.h" 15 | 16 | 17 | /* LED mappings: 18 | 19 | +--------+ 20 | | | 21 | | Yellow | 22 | |o 3 ^| 23 | +-------|+ 24 | | 2 || 25 | | Green | 26 | |o 0x32 | 27 | +--------+--------+ 28 | |o 0 |o 1 | 29 | | red | purple | 30 | |0x0 --> | 31 | +--------+--------+--------+ 32 | | 5 <-- 4 | 33 | | Teal | Blue | 34 | | o| 0x64 o| 35 | +--------+--------+ 36 | */ 37 | 38 | #define WIDTH 32 39 | typedef struct game game_t; 40 | typedef struct { 41 | game_t * board; 42 | int edge; 43 | } edge_t; 44 | 45 | struct game 46 | { 47 | unsigned px, py; 48 | 49 | edge_t top; 50 | edge_t bottom; 51 | edge_t left; 52 | edge_t right; 53 | 54 | uint8_t board[WIDTH][WIDTH]; 55 | }; 56 | 57 | 58 | static game_t boards[6] = {}; 59 | 60 | static void 61 | randomize( 62 | game_t * const b, 63 | int chance 64 | ) 65 | { 66 | for (int y = 0 ; y < WIDTH ; y++) 67 | { 68 | for (int x = 0 ; x < WIDTH ; x++) 69 | { 70 | #if 1 71 | unsigned live = (rand() % 128 < chance); 72 | b->board[y][x] = live ? 3 : 0; 73 | #else 74 | b->board[y][x] = 0; 75 | #endif 76 | } 77 | } 78 | 79 | } 80 | 81 | 82 | static void 83 | make_glider( 84 | game_t * const b 85 | ) 86 | { 87 | // X 88 | // X 89 | // XXX 90 | int px = (rand() % 8) + 10; 91 | int py = (rand() % 8) + 20; 92 | b->board[py+0][px+0] = 0; 93 | b->board[py+0][px+1] = 3; 94 | b->board[py+0][px+2] = 0; 95 | 96 | b->board[py+1][px+0] = 0; 97 | b->board[py+1][px+1] = 0; 98 | b->board[py+1][px+2] = 3; 99 | 100 | b->board[py+2][px+0] = 3; 101 | b->board[py+2][px+1] = 3; 102 | b->board[py+2][px+2] = 3; 103 | } 104 | 105 | 106 | static uint8_t * 107 | _get_edge( 108 | edge_t * const e, 109 | int pos 110 | ) 111 | { 112 | game_t * const b = e->board; 113 | const int edge = e->edge; 114 | const int neg_pos = WIDTH - pos - 1; 115 | 116 | if (edge == 1) 117 | return &b->board[0][pos]; 118 | if (edge == 2) 119 | return &b->board[pos][WIDTH-1]; 120 | if (edge == 3) 121 | return &b->board[WIDTH-1][pos]; 122 | if (edge == 4) 123 | return &b->board[pos][0]; 124 | 125 | if (edge == -1) 126 | return &b->board[0][neg_pos]; 127 | if (edge == -2) 128 | return &b->board[neg_pos][WIDTH-1]; 129 | if (edge == -3) 130 | return &b->board[WIDTH-1][neg_pos]; 131 | if (edge == -4) 132 | return &b->board[neg_pos][0]; 133 | 134 | printf("bad %d,%d\n", edge, pos); 135 | return NULL; 136 | } 137 | 138 | 139 | static uint8_t 140 | get_edge( 141 | edge_t * const e, 142 | int pos 143 | ) 144 | { 145 | uint8_t * const u = _get_edge(e, pos); 146 | if (u) 147 | return (*u) & 1; 148 | return 0; 149 | } 150 | 151 | 152 | static unsigned 153 | get_space( 154 | game_t * const b, 155 | int x, 156 | int y 157 | ) 158 | { 159 | if (x >= 0 && y >= 0 && x < WIDTH && y < WIDTH) 160 | return b->board[y][x] & 1; 161 | 162 | // don't deal with diagonal connections 163 | if (x < 0 && y < 0) 164 | return 0; 165 | if (x >= WIDTH && y >= WIDTH) 166 | return 0; 167 | 168 | // Check for the four cardinal ones 169 | if (y < 0) 170 | return get_edge(&b->top, x); 171 | if (y >= WIDTH) 172 | return get_edge(&b->bottom, x); 173 | if (x < 0) 174 | return get_edge(&b->left, y); 175 | if (x >= WIDTH) 176 | return get_edge(&b->right, y); 177 | 178 | // huh? 179 | printf("bad %d,%d\n", x, y); 180 | return 9; 181 | } 182 | 183 | 184 | static void 185 | play_game( 186 | game_t * const b 187 | ) 188 | { 189 | for (int y = 0 ; y < WIDTH ; y++) 190 | { 191 | for (int x = 0 ; x < WIDTH ; x++) 192 | { 193 | uint8_t sum = 0; 194 | 195 | sum += get_space(b, x-1, y-1); 196 | sum += get_space(b, x-1, y ); 197 | sum += get_space(b, x-1, y+1); 198 | 199 | sum += get_space(b, x , y-1); 200 | sum += get_space(b, x , y+1); 201 | 202 | sum += get_space(b, x+1, y-1); 203 | sum += get_space(b, x+1, y ); 204 | sum += get_space(b, x+1, y+1); 205 | 206 | 207 | /* 208 | Any live cell with fewer than two live neighbours dies, 209 | as if caused by under-population. 210 | Any live cell with two or three live neighbours lives 211 | on to the next generation. 212 | Any live cell with more than three live neighbours dies, 213 | as if by overcrowding. 214 | Any dead cell with exactly three live neighbours becomes a live cell, 215 | as if by reproduction. 216 | */ 217 | if (b->board[y][x] & 1) 218 | { 219 | // currently live 220 | if (sum == 2 || sum == 3) 221 | b->board[y][x] |= 2; 222 | else 223 | b->board[y][x] &= ~2; 224 | } else { 225 | // currently dead 226 | if (sum == 3) 227 | b->board[y][x] |= 2; 228 | else 229 | b->board[y][x] &= ~2; 230 | } 231 | } 232 | } 233 | } 234 | 235 | 236 | static void 237 | identify( 238 | uint8_t * const out, 239 | int x, 240 | int y 241 | ) 242 | { 243 | uint32_t b = 0; 244 | 245 | if (x < 32) 246 | { 247 | if (y < 32) 248 | b = 0xFF0000; 249 | else 250 | if (y < 64) 251 | b = 0x0000FF; 252 | else 253 | if (y < 96) 254 | b = 0x00FF00; 255 | else 256 | b = 0x411111; 257 | } else 258 | if (x < 64) 259 | { 260 | if (y < 32) 261 | b = 0xFF00FF; 262 | else 263 | if (y < 64) 264 | b = 0x00FFFF; 265 | else 266 | if (y < 96) 267 | b = 0xFFFF00; 268 | else 269 | b = 0x114111; 270 | } else { 271 | b = 0x111141; 272 | } 273 | 274 | out[0] = (b >> 16) & 0xFF; 275 | out[1] = (b >> 8) & 0xFF; 276 | out[2] = (b >> 0) & 0xFF; 277 | } 278 | 279 | static void 280 | copy_to_fb( 281 | uint32_t * const p, 282 | const unsigned width, 283 | const unsigned height, 284 | game_t * const board 285 | ) 286 | { 287 | for (int y = 0 ; y < WIDTH ; y++) 288 | { 289 | for (int x = 0 ; x < WIDTH ; x++) 290 | { 291 | uint32_t * const pix_ptr = &p[width*(y+board->py) + x + board->px]; 292 | uint32_t pix = *pix_ptr; 293 | unsigned r = (pix >> 0) & 0xFF; 294 | unsigned g = (pix >> 8) & 0xFF; 295 | unsigned b = (pix >> 16) & 0xFF; 296 | 297 | // copy the new value to the current value 298 | uint8_t * const sq = &board->board[y][x]; 299 | *sq = (*sq & 2) | (*sq >> 1); 300 | 301 | unsigned live = *sq & 1; 302 | if (live) 303 | { 304 | r += 80; 305 | g += 5; 306 | b += 30; 307 | if (r > 0xFF) 308 | r = 0xFF; 309 | if (g > 0xFF) 310 | g = 0xFF; 311 | if (b > 0xFF) 312 | b = 0xFF; 313 | } else { 314 | #if 1 315 | #define SMOOTH_R 3 316 | #define SMOOTH_G 127 317 | #define SMOOTH_B 63 318 | #else 319 | #define SMOOTH_R 1 320 | #define SMOOTH_G 1 321 | #define SMOOTH_B 1 322 | #endif 323 | r = (r * SMOOTH_R) / (SMOOTH_R+1); 324 | g = (g * SMOOTH_G) / (SMOOTH_G+1); 325 | b = (b * SMOOTH_B) / (SMOOTH_B+1); 326 | } 327 | 328 | if (0 && x == 0 && y == 0) 329 | identify(pix_ptr, board->px, board->py); 330 | else 331 | *pix_ptr = (r << 0) | (g << 8) | (b << 16); 332 | } 333 | } 334 | } 335 | 336 | 337 | static void 338 | check_edge( 339 | game_t * const boards, 340 | int i, 341 | edge_t *edge, 342 | int this_edge 343 | ) 344 | { 345 | game_t * b = &boards[i]; 346 | game_t * n = edge->board; 347 | int e = edge->edge; 348 | 349 | // verify that the back link from the remote board is to this 350 | // edge on this board. 351 | if (e == 1 && n->top.board == b && n->top.edge == this_edge) 352 | return; 353 | if (e == 2 && n->right.board == b && n->right.edge == this_edge) 354 | return; 355 | if (e == 3 && n->bottom.board == b && n->bottom.edge == this_edge) 356 | return; 357 | if (e == 4 && n->left.board == b && n->left.edge == this_edge) 358 | return; 359 | 360 | if (e == -1 && n->top.board == b && n->top.edge == -this_edge) 361 | return; 362 | if (e == -2 && n->right.board == b && n->right.edge == -this_edge) 363 | return; 364 | if (e == -3 && n->bottom.board == b && n->bottom.edge == -this_edge) 365 | return; 366 | if (e == -4 && n->left.board == b && n->left.edge == -this_edge) 367 | return; 368 | 369 | fprintf(stderr, "%d edge %d bad back link?\n", i, this_edge); 370 | exit(-1); 371 | } 372 | 373 | 374 | 375 | int 376 | main(void) 377 | { 378 | const int width = 128; 379 | const int height = 128; 380 | ledscape_config_t * const config = &ledscape_matrix_default; 381 | config->matrix_config.panel_height = 32; 382 | 383 | ledscape_t * const leds = ledscape_init(config, 0); 384 | printf("init done\n"); 385 | time_t last_time = time(NULL); 386 | unsigned last_i = 0; 387 | 388 | unsigned i = 0; 389 | uint32_t * const p = calloc(width*height,4); 390 | 391 | game_t * const red = &boards[0]; 392 | game_t * const purple = &boards[1]; 393 | game_t * const green = &boards[2]; 394 | game_t * const yellow = &boards[3]; 395 | game_t * const blue = &boards[4]; 396 | game_t * const teal = &boards[5]; 397 | 398 | // red 399 | red->px = 0; 400 | red->py = 0; 401 | red->top = (edge_t) { green, 4 }; 402 | red->right = (edge_t) { purple, 4 }; 403 | red->bottom = (edge_t) { blue, -3 }; 404 | red->left = (edge_t) { teal, -3 }; 405 | 406 | // purple 407 | purple->px = WIDTH; 408 | purple->py = 0; 409 | purple->top = (edge_t) { green, 3 }; 410 | purple->right = (edge_t) { yellow, 3 }; 411 | purple->bottom = (edge_t) { blue, -4 }; 412 | purple->left = (edge_t) { red, 2 }; 413 | 414 | // green 415 | green->px = 0; 416 | green->py = 2*WIDTH; 417 | green->top = (edge_t) { teal, -2 }; 418 | green->right = (edge_t) { yellow, 4 }; 419 | green->bottom = (edge_t) { purple, 1 }; 420 | green->left = (edge_t) { red, 1 }; 421 | 422 | // yellow 423 | yellow->px = WIDTH; 424 | yellow->py = 2*WIDTH; 425 | yellow->top = (edge_t) { teal, -1 }; 426 | yellow->right = (edge_t) { blue, -1 }; 427 | yellow->bottom = (edge_t) { purple, 2 }; 428 | yellow->left = (edge_t) { green, 2 }; 429 | 430 | // blue 431 | blue->px = 0; 432 | blue->py = WIDTH; 433 | blue->top = (edge_t) { yellow, -2 }; 434 | blue->right = (edge_t) { teal, 4 }; 435 | blue->bottom = (edge_t) { red, -3 }; 436 | blue->left = (edge_t) { purple, -3 }; 437 | 438 | // teal 439 | teal->px = WIDTH; 440 | teal->py = WIDTH; 441 | teal->top = (edge_t) { yellow, -1 }; 442 | teal->right = (edge_t) { green, -1 }; 443 | teal->bottom = (edge_t) { red, -4 }; 444 | teal->left = (edge_t) { blue, 2 }; 445 | 446 | for (int i = 0 ; i < 6 ; i++) 447 | { 448 | game_t * b = &boards[i]; 449 | check_edge(boards, i, &boards[i].top, 1); 450 | check_edge(boards, i, &boards[i].right, 2); 451 | check_edge(boards, i, &boards[i].bottom, 3); 452 | check_edge(boards, i, &boards[i].left, 4); 453 | } 454 | 455 | srand(getpid()); 456 | 457 | if (1){ 458 | int which = 5; 459 | game_t * const b = &boards[which]; 460 | 461 | b->board[0][4] = 3; 462 | *_get_edge(&b->top, 4) = 3; 463 | 464 | b->board[6][WIDTH-1] = 3; 465 | *_get_edge(&b->right, 6) = 3; 466 | 467 | b->board[WIDTH-1][8] = 3; 468 | *_get_edge(&b->bottom, 8) = 3; 469 | 470 | b->board[8][0] = 3; 471 | *_get_edge(&b->left, 8) = 3; 472 | } 473 | 474 | 475 | while (1) 476 | { 477 | if ((i & 0x3FF) == 0) 478 | { 479 | printf("randomize\n"); 480 | for (int i = 0 ; i < 6 ; i++) 481 | { 482 | randomize(&boards[i], 20); 483 | //make_glider(&boards[i]); 484 | } 485 | //make_glider(&boards[rand() % 6]); 486 | } 487 | 488 | if (i++ % 4 == 0) 489 | { 490 | for (int i = 0 ; i < 6 ; i++) 491 | play_game(&boards[i]); 492 | } 493 | 494 | for (int i = 0 ; i < 6 ; i++) 495 | copy_to_fb(p, width, height, &boards[i]); 496 | 497 | ledscape_draw(leds, p); 498 | usleep(10000); 499 | } 500 | 501 | ledscape_close(leds); 502 | 503 | return EXIT_SUCCESS; 504 | } 505 | -------------------------------------------------------------------------------- /src/demos/fire.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Draw fire patterns, derived from the pyramid Fire code. 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "ledscape.h" 13 | 14 | // sideways pyramid; 256 height, but 128 wide 15 | #define WIDTH 512 16 | #define HEIGHT 64 17 | 18 | 19 | // Borrowed by OctoWS2811 rainbow test 20 | static unsigned int 21 | h2rgb( 22 | unsigned int v1, 23 | unsigned int v2, 24 | unsigned int hue 25 | ) 26 | { 27 | if (hue < 60) 28 | return v1 * 60 + (v2 - v1) * hue; 29 | if (hue < 180) 30 | return v2 * 60; 31 | if (hue < 240) 32 | return v1 * 60 + (v2 - v1) * (240 - hue); 33 | 34 | return v1 * 60; 35 | } 36 | 37 | 38 | // Convert HSL (Hue, Saturation, Lightness) to RGB (Red, Green, Blue) 39 | // 40 | // hue: 0 to 359 - position on the color wheel, 0=red, 60=orange, 41 | // 120=yellow, 180=green, 240=blue, 300=violet 42 | // 43 | // saturation: 0 to 100 - how bright or dull the color, 100=full, 0=gray 44 | // 45 | // lightness: 0 to 100 - how light the color is, 100=white, 50=color, 0=black 46 | // 47 | static uint32_t 48 | hsv2rgb( 49 | unsigned int hue, 50 | unsigned int saturation, 51 | unsigned int lightness 52 | ) 53 | { 54 | unsigned int red, green, blue; 55 | unsigned int var1, var2; 56 | 57 | if (hue > 359) 58 | hue = hue % 360; 59 | if (saturation > 100) 60 | saturation = 100; 61 | if (lightness > 100) 62 | lightness = 100; 63 | 64 | // algorithm from: http://www.easyrgb.com/index.php?X=MATH&H=19#text19 65 | if (saturation == 0) { 66 | red = green = blue = lightness * 255 / 100; 67 | } else { 68 | if (lightness < 50) { 69 | var2 = lightness * (100 + saturation); 70 | } else { 71 | var2 = ((lightness + saturation) * 100) - (saturation * lightness); 72 | } 73 | var1 = lightness * 200 - var2; 74 | red = h2rgb(var1, var2, (hue < 240) ? hue + 120 : hue - 240) * 255 / 600000; 75 | green = h2rgb(var1, var2, hue) * 255 / 600000; 76 | blue = h2rgb(var1, var2, (hue >= 120) ? hue - 120 : hue + 240) * 255 / 600000; 77 | } 78 | return (red << 16) | (green << 8) | (blue << 0); 79 | } 80 | 81 | 82 | // This will contain the pixels used to calculate the fire effect 83 | static uint8_t fire[WIDTH][HEIGHT]; 84 | 85 | // Flame colors 86 | static uint32_t palette[255]; 87 | static float angle; 88 | static uint32_t calc1[WIDTH], calc2[HEIGHT], calc3[WIDTH], calc4[WIDTH], calc5[HEIGHT]; 89 | 90 | static void 91 | fire_draw( 92 | uint32_t * const frame 93 | ) 94 | { 95 | static int rotate_offset = 0; 96 | 97 | memset(frame, 0, WIDTH*HEIGHT*sizeof(*frame)); 98 | 99 | angle = angle + 0.05; 100 | 101 | // Randomize the bottom row of the fire buffer 102 | for (int x = 0; x < WIDTH; x++) 103 | { 104 | fire[x][HEIGHT-1] = random() % 190; 105 | } 106 | 107 | int counter = 0; 108 | // Do the fire calculations for every pixel, from top to bottom 109 | for (int x = 0; x < WIDTH; x++) { // up to 128, leds_height 110 | for (int y = 0; y < HEIGHT; y++) { // up to 256, leds_width 111 | // Add pixel values around current pixel 112 | 113 | fire[x][y] = 114 | ((fire[calc3[x]][calc2[y]] 115 | + fire[calc1[x]][calc2[y]] 116 | + fire[calc4[x]][calc2[y]] 117 | + fire[calc1[x]][calc5[y]]) << 5) / (128+(abs(x-WIDTH/2))/4); // 129; 118 | 119 | // Output everything to screen using our palette colors 120 | const uint32_t c = palette[fire[x][y]]; 121 | //frame[counter] = fire[x][y]; 122 | 123 | // Extract the red value using right shift and bit mask 124 | // equivalent of red(pgTemp.pixels[x+y*WIDTH]) 125 | // Only map 3D cube 'lit' pixels onto fire array needed for next frame 126 | if (((c >> 0) & 0xFF) == 128) 127 | fire[x][y] = 128; 128 | 129 | // skip the bottom few rows 130 | /* 131 | #if 1 132 | if (y > HEIGHT - leds_width) 133 | frame[y - (HEIGHT - leds_width) + x*leds_width] = c; 134 | #else 135 | if (x > HEIGHT - leds_width) 136 | frame[y - (HEIGHT - leds_width) + x*leds_width] = c; 137 | #endif 138 | */ 139 | frame[WIDTH*y + (x + rotate_offset / 16) % WIDTH] = c; 140 | //frame[counter++] = c; 141 | } 142 | } 143 | 144 | rotate_offset++; 145 | } 146 | 147 | 148 | static void 149 | sparkles( 150 | uint32_t * const frame, 151 | int num_sparkles 152 | ) 153 | { 154 | for(int i = 0 ; i < num_sparkles ; i++) 155 | frame[rand() % (WIDTH*HEIGHT)] = 0xFFFFFF; 156 | } 157 | 158 | static int constrain( 159 | int x, 160 | int min, 161 | int max 162 | ) 163 | { 164 | if (x < min) 165 | return min; 166 | if (x > max) 167 | return max; 168 | return x; 169 | } 170 | 171 | static void 172 | init_pallete(void) 173 | { 174 | for (int x = 0; x < 255; x++) { 175 | //Hue goes from 0 to 85: red to yellow 176 | //Saturation is always the maximum: 255 177 | //Lightness is 0..255 for x=0..128, and 255 for x=128..255 178 | palette[x] = hsv2rgb(x/2, 100, constrain(x, 0, 40)); 179 | } 180 | 181 | // Precalculate which pixel values to add during animation loop 182 | // this speeds up the effect by 10fps 183 | for (int x = 0; x < WIDTH; x++) { 184 | calc1[x] = x % WIDTH; 185 | calc3[x] = (x - 1 + WIDTH) % WIDTH; 186 | calc4[x] = (x + 1) % WIDTH; 187 | } 188 | 189 | for (int y = 0; y < HEIGHT; y++) { 190 | calc2[y] = (y + 1) % HEIGHT; 191 | calc5[y] = (y + 2) % HEIGHT; 192 | } 193 | } 194 | 195 | 196 | int 197 | main( 198 | int argc, 199 | const char ** argv 200 | ) 201 | { 202 | ledscape_matrix_config_t * config = &ledscape_matrix_default; 203 | 204 | if (argc > 1) 205 | { 206 | config = ledscape_config(argv[1]); 207 | if (!config) 208 | return EXIT_FAILURE; 209 | } 210 | 211 | config->width = WIDTH; 212 | config->height = HEIGHT; 213 | ledscape_t * const leds = ledscape_init(config, 0); 214 | 215 | printf("init done\n"); 216 | time_t last_time = time(NULL); 217 | unsigned last_i = 0; 218 | 219 | unsigned i = 0; 220 | init_pallete(); 221 | uint32_t * const p = calloc(WIDTH*HEIGHT,4); 222 | 223 | while (1) 224 | { 225 | // Alternate frame buffers on each draw command 226 | 227 | time_t now = time(NULL); 228 | const uint32_t delta = now - last_time; 229 | 230 | fire_draw(p); 231 | sparkles(p, delta); 232 | 233 | ledscape_draw(leds, p); 234 | usleep(50000); 235 | 236 | // wait for the previous frame to finish; 237 | //const uint32_t response = ledscape_wait(leds); 238 | const uint32_t response = 0; 239 | if (delta > 30) 240 | { 241 | printf("%d fps. starting %d previous %"PRIx32"\n", 242 | (i - last_i) / delta, i, response); 243 | last_i = i; 244 | last_time = now; 245 | memset(fire, 0, sizeof(fire)); 246 | } 247 | 248 | } 249 | 250 | ledscape_close(leds); 251 | 252 | return EXIT_SUCCESS; 253 | } 254 | -------------------------------------------------------------------------------- /src/demos/identify.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "ledscape.h" 12 | 13 | 14 | int 15 | main( 16 | int argc, 17 | char ** argv 18 | ) 19 | { 20 | int width = 256; 21 | int height = 32; 22 | 23 | ledscape_config_t * config = &ledscape_matrix_default; 24 | if (argc > 1) 25 | { 26 | config = ledscape_config(argv[1]); 27 | if (!config) 28 | return EXIT_FAILURE; 29 | } 30 | 31 | if (config->type == LEDSCAPE_MATRIX) 32 | { 33 | config->matrix_config.width = width; 34 | config->matrix_config.height = height; 35 | } 36 | 37 | ledscape_t * const leds = ledscape_init(config, 0); 38 | 39 | printf("init done %d,%d\n", width, height); 40 | time_t last_time = time(NULL); 41 | unsigned last_i = 0; 42 | 43 | unsigned i = 0; 44 | uint32_t * const p = calloc(width*height,4); 45 | int scroll_x = 128; 46 | memset(p, 0x10, width*height*4); 47 | 48 | int h = 4; 49 | const uint32_t colors[] = { 50 | 0xFF0000, 51 | 0x00FF00, 52 | 0x0000FF, 53 | 0xFF00FF, 54 | 0x00FFFF, 55 | 0xFFFF00, 56 | }; 57 | 58 | while (1) 59 | { 60 | if (h++ == 2*width) 61 | h = 10; 62 | 63 | for(int y = 0 ; y < height ; y++) 64 | { 65 | uint32_t * const row_ptr = &p[width*y]; 66 | const int scale = 63; 67 | for(int x = 5 ; x < width ; x++) 68 | { 69 | uint32_t color = row_ptr[x]; 70 | int r = (color >> 16) & 0xFF; 71 | int g = (color >> 8) & 0xFF; 72 | int b = (color >> 0) & 0xFF; 73 | r = (r * scale) / (scale+1); 74 | g = (g * scale) / (scale+1); 75 | b = (b * scale) / (scale+1); 76 | if (r < 10) r = 10; 77 | if (g < 10) g = 10; 78 | if (b < 10) b = 10; 79 | row_ptr[x] = r << 16 | g << 8 | b << 0; 80 | } 81 | } 82 | 83 | for(int y = 0 ; y < height ; y++) 84 | { 85 | uint32_t * const row_ptr = &p[width*y]; 86 | uint32_t color = colors[y % 6]; 87 | row_ptr[0] = y & 1 ? 0xFFFFFF : 0x040404; 88 | row_ptr[1] = y & 2 ? 0xFFFFFF : 0x040404; 89 | row_ptr[2] = y & 4 ? 0xFFFFFF : 0x040404; 90 | row_ptr[3] = y & 8 ? 0xFFFFFF : 0x040404; 91 | row_ptr[4] = y & 16 ? 0xFFFFFF : 0x040404; 92 | 93 | row_ptr[5] = color; 94 | row_ptr[h/2] = color; 95 | } 96 | 97 | ledscape_draw(leds, p); 98 | usleep(20000); 99 | 100 | // wait for the previous frame to finish; 101 | //const uint32_t response = ledscape_wait(leds); 102 | const uint32_t response = 0; 103 | time_t now = time(NULL); 104 | if (now != last_time) 105 | { 106 | printf("%d fps. starting %d previous %"PRIx32"\n", 107 | i - last_i, i, response); 108 | last_i = i; 109 | last_time = now; 110 | } 111 | 112 | } 113 | 114 | ledscape_close(leds); 115 | 116 | return EXIT_SUCCESS; 117 | } 118 | -------------------------------------------------------------------------------- /src/demos/life.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Play the game of life on the normal pyramid 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "ledscape.h" 13 | 14 | #define LIFE_R 0xFF 15 | #define LIFE_G 0x35 16 | #define LIFE_B 0x90 17 | #define SMOOTH_R 127 18 | #define SMOOTH_G 20 19 | #define SMOOTH_B 10 20 | #define DELTA_R 10 21 | #define DELTA_G 30 22 | #define DELTA_B 80 23 | 24 | #define WIDTH 512 25 | #define HEIGHT 64 26 | 27 | typedef struct 28 | { 29 | uint8_t board[HEIGHT][WIDTH]; 30 | } game_t; 31 | 32 | static game_t board; 33 | 34 | 35 | static void 36 | randomize( 37 | game_t * const b, 38 | int chance 39 | ) 40 | { 41 | for (int y = 0 ; y < HEIGHT ; y++) 42 | { 43 | for (int x = 0 ; x < WIDTH ; x++) 44 | { 45 | #if 1 46 | unsigned live = (rand() % 128 < chance); 47 | b->board[y][x] = live ? 3 : 0; 48 | #else 49 | b->board[y][x] = 0; 50 | #endif 51 | } 52 | } 53 | 54 | } 55 | 56 | 57 | static void 58 | make_glider( 59 | game_t * const b 60 | ) 61 | { 62 | // X 63 | // X 64 | // XXX 65 | int px = (rand() % 8) + 10; 66 | int py = (rand() % 8) + 20; 67 | b->board[py+0][px+0] = 0; 68 | b->board[py+0][px+1] = 3; 69 | b->board[py+0][px+2] = 0; 70 | 71 | b->board[py+1][px+0] = 0; 72 | b->board[py+1][px+1] = 0; 73 | b->board[py+1][px+2] = 3; 74 | 75 | b->board[py+2][px+0] = 3; 76 | b->board[py+2][px+1] = 3; 77 | b->board[py+2][px+2] = 3; 78 | } 79 | 80 | 81 | 82 | static unsigned 83 | get_space( 84 | game_t * const b, 85 | int x, 86 | int y 87 | ) 88 | { 89 | if (x >= 0 && y >= 0 && x < WIDTH && y < HEIGHT) 90 | return b->board[y][x] & 1; 91 | 92 | if (y < 0 || y >= HEIGHT) 93 | return 0; 94 | 95 | // map the x to a cylinder 96 | if (x < 0) 97 | return b->board[y][WIDTH - x] & 1; 98 | if (x >= WIDTH) 99 | return b->board[y][x - WIDTH] & 1; 100 | 101 | return 0; 102 | } 103 | 104 | 105 | static void 106 | play_game( 107 | game_t * const b 108 | ) 109 | { 110 | for (int y = 0 ; y < HEIGHT ; y++) 111 | { 112 | for (int x = 0 ; x < WIDTH ; x++) 113 | { 114 | uint8_t sum = 0; 115 | 116 | sum += get_space(b, x-1, y-1); 117 | sum += get_space(b, x-1, y ); 118 | sum += get_space(b, x-1, y+1); 119 | 120 | sum += get_space(b, x , y-1); 121 | sum += get_space(b, x , y+1); 122 | 123 | sum += get_space(b, x+1, y-1); 124 | sum += get_space(b, x+1, y ); 125 | sum += get_space(b, x+1, y+1); 126 | 127 | 128 | /* 129 | Any live cell with fewer than two live neighbours dies, 130 | as if caused by under-population. 131 | Any live cell with two or three live neighbours lives 132 | on to the next generation. 133 | Any live cell with more than three live neighbours dies, 134 | as if by overcrowding. 135 | Any dead cell with exactly three live neighbours becomes a live cell, 136 | as if by reproduction. 137 | */ 138 | if (b->board[y][x] & 1) 139 | { 140 | // currently live 141 | if (sum == 2 || sum == 3) 142 | b->board[y][x] |= 2; 143 | else 144 | b->board[y][x] &= ~2; 145 | } else { 146 | // currently dead 147 | if (sum == 3) 148 | b->board[y][x] |= 2; 149 | else 150 | b->board[y][x] &= ~2; 151 | } 152 | } 153 | } 154 | } 155 | 156 | 157 | 158 | static void 159 | copy_to_fb( 160 | uint32_t * const p, 161 | const unsigned width, 162 | const unsigned height, 163 | game_t * const board 164 | ) 165 | { 166 | (void) height; 167 | 168 | for (int y = 0 ; y < HEIGHT ; y++) 169 | { 170 | for (int x = 0 ; x < WIDTH ; x++) 171 | { 172 | uint32_t * const pix_ptr = &p[width*y + x]; 173 | uint32_t pix = *pix_ptr; 174 | unsigned r = (pix >> 0) & 0xFF; 175 | unsigned g = (pix >> 8) & 0xFF; 176 | unsigned b = (pix >> 16) & 0xFF; 177 | 178 | // copy the new value to the current value 179 | uint8_t * const sq = &board->board[y][x]; 180 | *sq = (*sq & 2) | (*sq >> 1); 181 | 182 | unsigned live = *sq & 1; 183 | if (live) 184 | { 185 | r += DELTA_R; 186 | g += DELTA_G; 187 | b += DELTA_B; 188 | if (r > LIFE_R) 189 | r = LIFE_R; 190 | if (g > LIFE_G) 191 | g = LIFE_G; 192 | if (b > LIFE_B) 193 | b = LIFE_B; 194 | } else { 195 | r = (r * SMOOTH_R) / (SMOOTH_R+1); 196 | g = (g * SMOOTH_G) / (SMOOTH_G+1); 197 | b = (b * SMOOTH_B) / (SMOOTH_B+1); 198 | } 199 | 200 | if (r > 255) r = 255; 201 | if (g > 255) g = 255; 202 | if (b > 255) b = 255; 203 | if (r < 0) r = 0; 204 | if (g < 0) g = 0; 205 | if (b < 0) b = 0; 206 | 207 | *pix_ptr = ((r << 16) | (g << 8) | (b << 0)); 208 | } 209 | } 210 | } 211 | 212 | 213 | 214 | int 215 | main( 216 | int argc, 217 | const char ** argv 218 | ) 219 | { 220 | ledscape_config_t * config = &ledscape_matrix_default; 221 | if (argc > 1) 222 | { 223 | config = ledscape_config(argv[1]); 224 | if (!config) 225 | return EXIT_FAILURE; 226 | } 227 | 228 | if (config->type == LEDSCAPE_MATRIX) 229 | { 230 | config->matrix_config.width = WIDTH; 231 | config->matrix_config.height = HEIGHT; 232 | } 233 | 234 | ledscape_t * const leds = ledscape_init(config, 0); 235 | printf("init done\n"); 236 | time_t last_time = time(NULL); 237 | unsigned last_i = 0; 238 | 239 | unsigned i = 0; 240 | uint32_t * const p = calloc(WIDTH*HEIGHT,4); 241 | 242 | srand(getpid()); 243 | 244 | while (1) 245 | { 246 | if ((i & 0x7FF) == 0) 247 | { 248 | printf("randomize\n"); 249 | randomize(&board, 20); 250 | //make_glider(&boards[i]); 251 | } 252 | 253 | if (i++ % 4 == 0) 254 | { 255 | play_game(&board); 256 | } 257 | 258 | copy_to_fb(p, WIDTH, HEIGHT, &board); 259 | 260 | ledscape_draw(leds, p); 261 | usleep(1000); 262 | } 263 | 264 | ledscape_close(leds); 265 | 266 | return EXIT_SUCCESS; 267 | } 268 | -------------------------------------------------------------------------------- /src/demos/lightcycles.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Lightcycles style game on the cylindrical megascroller. 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define WIDTH 512 18 | #define HEIGHT 64 19 | #define MAX_PLAYERS 4 20 | 21 | uint32_t board[HEIGHT][WIDTH]; 22 | 23 | typedef struct { 24 | int x; 25 | int y; 26 | int dir; 27 | uint32_t color; 28 | int alive; 29 | } player_t; 30 | 31 | player_t players[MAX_PLAYERS]; 32 | 33 | 34 | int 35 | player_update( 36 | player_t * const player 37 | ) 38 | { 39 | if (!player->alive) 40 | return 0; 41 | 42 | switch (player->dir) 43 | { 44 | case 0: 45 | player->x = (player->x + 1 + WIDTH) % WIDTH; 46 | break; 47 | case 1: 48 | player->y = (player->y + 1 + HEIGHT) % HEIGHT; 49 | break; 50 | case 2: 51 | player->x = (player->x - 1 + WIDTH) % WIDTH; 52 | break; 53 | case 3: 54 | player->y = (player->y - 1 + HEIGHT) % HEIGHT; 55 | break; 56 | default: 57 | player->dir = 0; 58 | break; 59 | } 60 | 61 | // check for collision 62 | if (board[player->y][player->x] != 0) 63 | { 64 | // dead! 65 | printf("player %08x died at %d,%d\n", 66 | player->color, player->x, player->y); 67 | board[player->y][player->x] = 0xFFFFFF; 68 | player->alive = 0; 69 | } else { 70 | // ok! 71 | board[player->y][player->x] = player->color; 72 | } 73 | 74 | return player->alive; 75 | } 76 | 77 | 78 | static const uint32_t palette[] = 79 | { 80 | 0xFF0000, 81 | 0x00FF00, 82 | 0x0000FF, 83 | 0xFF00FF, 84 | 0xFFFF00, 85 | }; 86 | 87 | 88 | 89 | static void 90 | fill( 91 | uint32_t color 92 | ) 93 | { 94 | for (int y = 0 ; y < HEIGHT ; y++) 95 | for (int x = 0 ; x < WIDTH ; x++) 96 | board[y][x] = color; 97 | } 98 | 99 | 100 | void 101 | new_game( 102 | int num_players 103 | ) 104 | { 105 | fill(0); 106 | 107 | for (int i = 0 ; i < MAX_PLAYERS ; i++) 108 | { 109 | player_t * const player = &players[i]; 110 | player->alive = i < num_players ? 1 : 0; 111 | player->x = 0; // rand() % WIDTH; 112 | player->y = rand() % HEIGHT; 113 | player->dir = 0; //(rand() % 2) ? 0 : 2; 114 | player->color = palette[i]; 115 | } 116 | } 117 | 118 | static int 119 | tcp_socket( 120 | const int port 121 | ) 122 | { 123 | const int sock = socket(AF_INET, SOCK_STREAM, 0); 124 | struct sockaddr_in addr = { 125 | .sin_family = AF_INET, 126 | .sin_port = htons(port), 127 | .sin_addr.s_addr = INADDR_ANY, 128 | }; 129 | 130 | if (sock < 0) 131 | return -1; 132 | if (bind(sock, (const struct sockaddr*) &addr, sizeof(addr)) < 0) 133 | return -1; 134 | 135 | int max_size = 65536; 136 | setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &max_size, sizeof(max_size)); 137 | 138 | 139 | return sock; 140 | } 141 | 142 | 143 | static 144 | void rgb32_to_rgb24_and_decay( 145 | uint8_t * const out, 146 | uint32_t * const in, 147 | const int width, 148 | const int height 149 | ) 150 | { 151 | for (int y = 0 ; y < height ; y++) 152 | { 153 | for (int x = 0 ; x < width ; x++) 154 | { 155 | uint8_t * const pix = out + 3*(x + y*width); 156 | uint32_t * const in_pix = &in[x + y*width]; 157 | const uint32_t c = *in_pix; 158 | 159 | uint8_t r = pix[0] = (c >> 16) & 0xFF; 160 | uint8_t g = pix[1] = (c >> 8) & 0xFF; 161 | uint8_t b = pix[2] = (c >> 0) & 0xFF; 162 | 163 | if (r) r--; 164 | if (g) g--; 165 | if (b) b--; 166 | 167 | *in_pix = (r << 16) | (g << 8) | (b << 0); 168 | } 169 | } 170 | } 171 | 172 | 173 | static void 174 | send_game( 175 | const int sock 176 | ) 177 | { 178 | // copy the game from RGBx to RGB and split into two packets 179 | uint8_t pkt[65536]; 180 | 181 | pkt[0] = 0; 182 | rgb32_to_rgb24_and_decay(&pkt[1], &board[0][0], WIDTH, HEIGHT/2); 183 | if (send(sock, pkt, 1 + (WIDTH*HEIGHT/2) * 3, 0) < 0) 184 | { 185 | perror("send"); 186 | exit(EXIT_FAILURE); 187 | } 188 | 189 | pkt[0] = 1; 190 | rgb32_to_rgb24_and_decay(&pkt[1], &board[HEIGHT/2][0], WIDTH, HEIGHT/2); 191 | send(sock, pkt, 1 + (WIDTH*HEIGHT/2) * 3, 0); 192 | } 193 | 194 | 195 | static struct termios ttystate, ttysave; 196 | 197 | static void 198 | tty_raw(void) 199 | { 200 | //get the terminal state 201 | tcgetattr(STDIN_FILENO, &ttystate); 202 | ttysave = ttystate; 203 | 204 | //turn off canonical mode and echo 205 | ttystate.c_lflag &= ~(ICANON | ECHO); 206 | //minimum of number input read. 207 | ttystate.c_cc[VMIN] = 1; 208 | tcsetattr(STDIN_FILENO, TCSANOW, &ttystate); 209 | } 210 | 211 | static void 212 | tty_reset(void) 213 | { 214 | ttystate.c_lflag |= ICANON | ECHO; 215 | tcsetattr(STDIN_FILENO, TCSANOW, &ttystate); 216 | } 217 | 218 | 219 | // don't allow a turn backwards into yourself 220 | static void 221 | set_dir( 222 | player_t * const player, 223 | int new_dir 224 | ) 225 | { 226 | if (new_dir == 0 && player->dir == 2) 227 | return; 228 | if (new_dir == 1 && player->dir == 3) 229 | return; 230 | if (new_dir == 2 && player->dir == 0) 231 | return; 232 | if (new_dir == 3 && player->dir == 1) 233 | return; 234 | 235 | player->dir = new_dir; 236 | } 237 | 238 | 239 | static void 240 | bomb( 241 | player_t * const player 242 | ) 243 | { 244 | // nothing yet 245 | (void) player; 246 | } 247 | 248 | 249 | 250 | static void * 251 | read_thread( 252 | void * arg 253 | ) 254 | { 255 | (void) arg; 256 | tty_raw(); 257 | atexit(tty_reset); 258 | 259 | while (1) 260 | { 261 | char c; 262 | ssize_t rc = read(STDIN_FILENO, &c, 1); 263 | if (rc < 0) 264 | { 265 | if (rc == EINTR || rc == EAGAIN) 266 | continue; 267 | perror("stdin"); 268 | break; 269 | } 270 | if (rc == 0) 271 | continue; 272 | 273 | switch (c) 274 | { 275 | case 'h': set_dir(&players[0], 2); break; 276 | case 'j': set_dir(&players[0], 1); break; 277 | case 'k': set_dir(&players[0], 3); break; 278 | case 'l': set_dir(&players[0], 0); break; 279 | case ';': bomb(&players[0]); break; 280 | 281 | case 'a': set_dir(&players[1], 2); break; 282 | case 's': set_dir(&players[1], 1); break; 283 | case 'w': set_dir(&players[1], 3); break; 284 | case 'd': set_dir(&players[1], 0); break; 285 | case 'f': bomb(&players[1]); break; 286 | 287 | default: break; 288 | } 289 | } 290 | 291 | return NULL; 292 | } 293 | 294 | 295 | int main(void) 296 | { 297 | int port = 9999; 298 | const char * host = "192.168.1.119"; 299 | 300 | const int sock = tcp_socket(0); 301 | struct sockaddr_in dest = { 302 | .sin_family = AF_INET, 303 | .sin_port = htons(port), 304 | .sin_addr = { inet_addr(host) }, 305 | }; 306 | if (connect(sock, (void*) &dest, sizeof(dest)) < 0) 307 | { 308 | perror(host); 309 | return EXIT_FAILURE; 310 | } 311 | 312 | pthread_t thread_id; 313 | if (pthread_create(&thread_id, NULL, read_thread, NULL) < 0) 314 | { 315 | perror("pthread"); 316 | return EXIT_FAILURE; 317 | } 318 | 319 | 320 | int num_players = 2; 321 | 322 | while (1) 323 | { 324 | printf("new game!\n"); 325 | new_game(num_players); 326 | 327 | while (1) 328 | { 329 | send_game(sock); 330 | 331 | int alive_count = 0; 332 | for (int i = 0 ; i < num_players ; i++) 333 | alive_count += player_update(&players[i]); 334 | 335 | if (alive_count == 1) 336 | break; 337 | usleep(5000); 338 | } 339 | 340 | // one player won! figure out who 341 | player_t * winner = NULL; 342 | for (int i = 0 ; i < num_players ; i++) 343 | { 344 | player_t * const player = &players[i]; 345 | if (!player->alive) 346 | continue; 347 | winner = player; 348 | break; 349 | } 350 | 351 | if (winner == NULL) 352 | { 353 | printf("no one won\n"); 354 | continue; 355 | } 356 | 357 | printf("%08x won\n", winner->color); 358 | 359 | // flash the screen 360 | for (int i = 0 ; i < 3 ; i++) 361 | { 362 | fill(winner->color); 363 | send_game(sock); 364 | usleep(250000); 365 | 366 | fill(0); 367 | send_game(sock); 368 | usleep(250000); 369 | 370 | fill(winner->color); 371 | send_game(sock); 372 | usleep(250000); 373 | 374 | fill(0); 375 | send_game(sock); 376 | usleep(500000); 377 | } 378 | } 379 | } 380 | -------------------------------------------------------------------------------- /src/demos/matrix-test.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Test the matrix LCD PRU firmware with a multi-hue rainbow. 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "ledscape.h" 13 | 14 | 15 | // Borrowed by OctoWS2811 rainbow test 16 | static unsigned int 17 | h2rgb( 18 | unsigned int v1, 19 | unsigned int v2, 20 | unsigned int hue 21 | ) 22 | { 23 | if (hue < 60) 24 | return v1 * 60 + (v2 - v1) * hue; 25 | if (hue < 180) 26 | return v2 * 60; 27 | if (hue < 240) 28 | return v1 * 60 + (v2 - v1) * (240 - hue); 29 | 30 | return v1 * 60; 31 | } 32 | 33 | 34 | // Convert HSL (Hue, Saturation, Lightness) to RGB (Red, Green, Blue) 35 | // 36 | // hue: 0 to 359 - position on the color wheel, 0=red, 60=orange, 37 | // 120=yellow, 180=green, 240=blue, 300=violet 38 | // 39 | // saturation: 0 to 100 - how bright or dull the color, 100=full, 0=gray 40 | // 41 | // lightness: 0 to 100 - how light the color is, 100=white, 50=color, 0=black 42 | // 43 | static uint32_t 44 | makeColor( 45 | unsigned int hue, 46 | unsigned int saturation, 47 | unsigned int lightness 48 | ) 49 | { 50 | unsigned int red, green, blue; 51 | unsigned int var1, var2; 52 | 53 | if (hue > 359) 54 | hue = hue % 360; 55 | if (saturation > 100) 56 | saturation = 100; 57 | if (lightness > 100) 58 | lightness = 100; 59 | 60 | // algorithm from: http://www.easyrgb.com/index.php?X=MATH&H=19#text19 61 | if (saturation == 0) { 62 | red = green = blue = lightness * 255 / 100; 63 | } else { 64 | if (lightness < 50) { 65 | var2 = lightness * (100 + saturation); 66 | } else { 67 | var2 = ((lightness + saturation) * 100) - (saturation * lightness); 68 | } 69 | var1 = lightness * 200 - var2; 70 | red = h2rgb(var1, var2, (hue < 240) ? hue + 120 : hue - 240) * 255 / 600000; 71 | green = h2rgb(var1, var2, hue) * 255 / 600000; 72 | blue = h2rgb(var1, var2, (hue >= 120) ? hue - 120 : hue + 240) * 255 / 600000; 73 | } 74 | return (red << 16) | (green << 8) | blue; 75 | } 76 | 77 | 78 | 79 | static uint32_t rainbowColors[180]; 80 | 81 | 82 | // phaseShift is the shift between each row. phaseShift=0 83 | // causes all rows to show the same colors moving together. 84 | // phaseShift=180 causes each row to be the opposite colors 85 | // as the previous. 86 | // 87 | // cycleTime is the number of milliseconds to shift through 88 | // the entire 360 degrees of the color wheel: 89 | // Red -> Orange -> Yellow -> Green -> Blue -> Violet -> Red 90 | // 91 | static void 92 | rainbow( 93 | uint32_t * const pixels, 94 | unsigned width, 95 | unsigned height, 96 | unsigned phaseShift, 97 | unsigned cycle 98 | ) 99 | { 100 | const unsigned color = cycle % 180; 101 | const unsigned dim = 128; 102 | 103 | static unsigned count = 0; 104 | 105 | count += 1; 106 | 107 | for (unsigned x=0; x < width; x++) { 108 | for (unsigned y=0; y < height; y++) { 109 | const int index = (color + x + y*phaseShift/4) % 180; 110 | const uint32_t in = rainbowColors[index]; 111 | uint8_t * const out = &pixels[x + y*width]; 112 | #if 1 113 | out[0] = ((in >> 0) & 0xFF) * dim / 128; // * y / 16; 114 | out[1] = ((in >> 8) & 0xFF) * dim / 128; // * y / 16; 115 | out[2] = ((in >> 16) & 0xFF) * dim / 128; // * y / 16; 116 | #else 117 | if(y==((count >> 3) & 0x1F) && x<20) { 118 | out[0] = 0xff; 119 | out[1] = 0xff; 120 | out[2] = 0x00; 121 | } 122 | else { 123 | out[0] = 0x00; 124 | out[1] = 0x00; 125 | out[2] = 0xff; 126 | } 127 | #endif 128 | } 129 | } 130 | } 131 | 132 | 133 | static void 134 | gradient( 135 | uint32_t * const pixels, 136 | unsigned width, 137 | unsigned height, 138 | unsigned phaseShift, 139 | unsigned cycle 140 | ) 141 | { 142 | cycle >>= 3; 143 | for (unsigned x=0; x < width; x++) { 144 | for (unsigned y=0; y < height; y++) { 145 | uint8_t * const out = &pixels[x + y*width]; 146 | #if 0 147 | //out[0] = ((x+cycle) % 32) * 8; 148 | //out[1] = ((y+cycle) % 16) * 16; 149 | uint8_t b = 0xFF; 150 | out[1] = b * ((((x + y + cycle) >> 5) ) & 1); 151 | #else 152 | uint32_t b = 0; 153 | 154 | if (x % 32 < (x/32 + 4) && y % 32 < (y/32+1)) 155 | { 156 | b = 0xFFFFFF; 157 | } else 158 | if (x < 32) 159 | { 160 | if (y < 32) 161 | b = 0xFF0000; 162 | else 163 | if (y < 64) 164 | b = 0x0000FF; 165 | else 166 | if (y < 96) 167 | b = 0x00FF00; 168 | else 169 | b = 0x411111; 170 | } else 171 | if (x < 64) 172 | { 173 | if (y < 32) 174 | b = 0xFF00FF; 175 | else 176 | if (y < 64) 177 | b = 0x00FFFF; 178 | else 179 | if (y < 96) 180 | b = 0xFFFF00; 181 | else 182 | b = 0x114111; 183 | } else { 184 | b = 0x111141; 185 | } 186 | 187 | out[0] = (b >> 16) & 0xFF; 188 | out[1] = (b >> 8) & 0xFF; 189 | out[2] = (b >> 0) & 0xFF; 190 | //*out = b; 191 | #endif 192 | } 193 | } 194 | } 195 | 196 | 197 | int 198 | main( 199 | int argc, 200 | const char ** argv 201 | ) 202 | { 203 | // int width = 240; // 256; 204 | // int height = 64; //128; 205 | int width = 135; 206 | int height = 32; 207 | 208 | ledscape_config_t * config = &ledscape_matrix_default; 209 | if (argc > 1) 210 | { 211 | config = ledscape_config(argv[1]); 212 | if (!config) 213 | return EXIT_FAILURE; 214 | } 215 | 216 | if (config->type == LEDSCAPE_MATRIX) 217 | { 218 | config->matrix_config.width = width; 219 | config->matrix_config.height = height; 220 | } 221 | 222 | ledscape_t * const leds = ledscape_init(config, 0); 223 | 224 | 225 | printf("init done\n"); 226 | time_t last_time = time(NULL); 227 | unsigned last_i = 0; 228 | 229 | // pre-compute the 180 rainbow colors 230 | for (int i=0; i<180; i++) 231 | { 232 | int hue = i * 2; 233 | int saturation = 100; 234 | int lightness = 50; 235 | rainbowColors[i] = makeColor(hue, saturation, lightness); 236 | } 237 | 238 | unsigned i = 0; 239 | uint32_t * const p = calloc(width*height,4); 240 | 241 | while (1) 242 | { 243 | if (1) 244 | rainbow(p, width, height, 10, i++); 245 | else 246 | gradient(p, width, height, 10, i++); 247 | 248 | ledscape_draw(leds, p); 249 | 250 | usleep(20000); 251 | 252 | // wait for the previous frame to finish; 253 | //const uint32_t response = ledscape_wait(leds); 254 | const uint32_t response = 0; 255 | time_t now = time(NULL); 256 | if (now != last_time) 257 | { 258 | printf("%d fps. starting %d previous %"PRIx32"\n", 259 | i - last_i, i, response); 260 | last_i = i; 261 | last_time = now; 262 | } 263 | 264 | } 265 | 266 | ledscape_close(leds); 267 | 268 | return EXIT_SUCCESS; 269 | } 270 | -------------------------------------------------------------------------------- /src/ledscape/Makefile: -------------------------------------------------------------------------------- 1 | ######### 2 | # 3 | # Build the PRU and LEDscape libraries as well as the PRU firmware. 4 | # 5 | # 6 | TARGETS-y += $(LIBDIR)/matrix.bin 7 | TARGETS-y += $(LIBDIR)/ws281x.bin 8 | 9 | LIB-y += libledscape.a 10 | 11 | libledscape.srcs += \ 12 | ledscape.c \ 13 | pru.c \ 14 | util.c \ 15 | config.c \ 16 | fixed-font.c \ 17 | 18 | include ../../Makefile.common 19 | 20 | -------------------------------------------------------------------------------- /src/ledscape/bitpattern.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | * ASCII art for bits. 3 | */ 4 | #pragma once 5 | 6 | #define ________ 0x00 // 0 7 | #define _______X 0x01 // 1 8 | #define ______X_ 0x02 // 2 9 | #define ______XX 0x03 // 3 10 | #define _____X__ 0x04 // 4 11 | #define _____X_X 0x05 // 5 12 | #define _____XX_ 0x06 // 6 13 | #define _____XXX 0x07 // 7 14 | #define ____X___ 0x08 // 8 15 | #define ____X__X 0x09 // 9 16 | #define ____X_X_ 0x0a // 10 17 | #define ____X_XX 0x0b // 11 18 | #define ____XX__ 0x0c // 12 19 | #define ____XX_X 0x0d // 13 20 | #define ____XXX_ 0x0e // 14 21 | #define ____XXXX 0x0f // 15 22 | #define ___X____ 0x10 // 16 23 | #define ___X___X 0x11 // 17 24 | #define ___X__X_ 0x12 // 18 25 | #define ___X__XX 0x13 // 19 26 | #define ___X_X__ 0x14 // 20 27 | #define ___X_X_X 0x15 // 21 28 | #define ___X_XX_ 0x16 // 22 29 | #define ___X_XXX 0x17 // 23 30 | #define ___XX___ 0x18 // 24 31 | #define ___XX__X 0x19 // 25 32 | #define ___XX_X_ 0x1a // 26 33 | #define ___XX_XX 0x1b // 27 34 | #define ___XXX__ 0x1c // 28 35 | #define ___XXX_X 0x1d // 29 36 | #define ___XXXX_ 0x1e // 30 37 | #define ___XXXXX 0x1f // 31 38 | #define __X_____ 0x20 // 32 39 | #define __X____X 0x21 // 33 40 | #define __X___X_ 0x22 // 34 41 | #define __X___XX 0x23 // 35 42 | #define __X__X__ 0x24 // 36 43 | #define __X__X_X 0x25 // 37 44 | #define __X__XX_ 0x26 // 38 45 | #define __X__XXX 0x27 // 39 46 | #define __X_X___ 0x28 // 40 47 | #define __X_X__X 0x29 // 41 48 | #define __X_X_X_ 0x2a // 42 49 | #define __X_X_XX 0x2b // 43 50 | #define __X_XX__ 0x2c // 44 51 | #define __X_XX_X 0x2d // 45 52 | #define __X_XXX_ 0x2e // 46 53 | #define __X_XXXX 0x2f // 47 54 | #define __XX____ 0x30 // 48 55 | #define __XX___X 0x31 // 49 56 | #define __XX__X_ 0x32 // 50 57 | #define __XX__XX 0x33 // 51 58 | #define __XX_X__ 0x34 // 52 59 | #define __XX_X_X 0x35 // 53 60 | #define __XX_XX_ 0x36 // 54 61 | #define __XX_XXX 0x37 // 55 62 | #define __XXX___ 0x38 // 56 63 | #define __XXX__X 0x39 // 57 64 | #define __XXX_X_ 0x3a // 58 65 | #define __XXX_XX 0x3b // 59 66 | #define __XXXX__ 0x3c // 60 67 | #define __XXXX_X 0x3d // 61 68 | #define __XXXXX_ 0x3e // 62 69 | #define __XXXXXX 0x3f // 63 70 | #define _X______ 0x40 // 64 71 | #define _X_____X 0x41 // 65 72 | #define _X____X_ 0x42 // 66 73 | #define _X____XX 0x43 // 67 74 | #define _X___X__ 0x44 // 68 75 | #define _X___X_X 0x45 // 69 76 | #define _X___XX_ 0x46 // 70 77 | #define _X___XXX 0x47 // 71 78 | #define _X__X___ 0x48 // 72 79 | #define _X__X__X 0x49 // 73 80 | #define _X__X_X_ 0x4a // 74 81 | #define _X__X_XX 0x4b // 75 82 | #define _X__XX__ 0x4c // 76 83 | #define _X__XX_X 0x4d // 77 84 | #define _X__XXX_ 0x4e // 78 85 | #define _X__XXXX 0x4f // 79 86 | #define _X_X____ 0x50 // 80 87 | #define _X_X___X 0x51 // 81 88 | #define _X_X__X_ 0x52 // 82 89 | #define _X_X__XX 0x53 // 83 90 | #define _X_X_X__ 0x54 // 84 91 | #define _X_X_X_X 0x55 // 85 92 | #define _X_X_XX_ 0x56 // 86 93 | #define _X_X_XXX 0x57 // 87 94 | #define _X_XX___ 0x58 // 88 95 | #define _X_XX__X 0x59 // 89 96 | #define _X_XX_X_ 0x5a // 90 97 | #define _X_XX_XX 0x5b // 91 98 | #define _X_XXX__ 0x5c // 92 99 | #define _X_XXX_X 0x5d // 93 100 | #define _X_XXXX_ 0x5e // 94 101 | #define _X_XXXXX 0x5f // 95 102 | #define _XX_____ 0x60 // 96 103 | #define _XX____X 0x61 // 97 104 | #define _XX___X_ 0x62 // 98 105 | #define _XX___XX 0x63 // 99 106 | #define _XX__X__ 0x64 // 100 107 | #define _XX__X_X 0x65 // 101 108 | #define _XX__XX_ 0x66 // 102 109 | #define _XX__XXX 0x67 // 103 110 | #define _XX_X___ 0x68 // 104 111 | #define _XX_X__X 0x69 // 105 112 | #define _XX_X_X_ 0x6a // 106 113 | #define _XX_X_XX 0x6b // 107 114 | #define _XX_XX__ 0x6c // 108 115 | #define _XX_XX_X 0x6d // 109 116 | #define _XX_XXX_ 0x6e // 110 117 | #define _XX_XXXX 0x6f // 111 118 | #define _XXX____ 0x70 // 112 119 | #define _XXX___X 0x71 // 113 120 | #define _XXX__X_ 0x72 // 114 121 | #define _XXX__XX 0x73 // 115 122 | #define _XXX_X__ 0x74 // 116 123 | #define _XXX_X_X 0x75 // 117 124 | #define _XXX_XX_ 0x76 // 118 125 | #define _XXX_XXX 0x77 // 119 126 | #define _XXXX___ 0x78 // 120 127 | #define _XXXX__X 0x79 // 121 128 | #define _XXXX_X_ 0x7a // 122 129 | #define _XXXX_XX 0x7b // 123 130 | #define _XXXXX__ 0x7c // 124 131 | #define _XXXXX_X 0x7d // 125 132 | #define _XXXXXX_ 0x7e // 126 133 | #define _XXXXXXX 0x7f // 127 134 | -------------------------------------------------------------------------------- /src/ledscape/config.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Parse a matrix config file. 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "ledscape.h" 9 | 10 | 11 | // find the end of the line and strip the whitespace 12 | static ssize_t 13 | readline( 14 | FILE * const file, 15 | char * const buf, 16 | const int max_len 17 | ) 18 | { 19 | if (!fgets(buf, max_len, file)) 20 | return -1; 21 | int len = strlen(buf); 22 | if (len >= max_len) 23 | { 24 | buf[max_len-1] = '\0'; 25 | return max_len; 26 | } 27 | 28 | while (len > 0) 29 | { 30 | const char c = buf[len-1]; 31 | if (!isspace(c)) 32 | break; 33 | // strip the whitespace 34 | buf[--len] = '\0'; 35 | } 36 | 37 | printf("read %d bytes '%s'\n", len, buf); 38 | return len; 39 | } 40 | 41 | 42 | ledscape_config_t * 43 | ledscape_matrix_config( 44 | const char * const filename, 45 | FILE * const file 46 | ) 47 | { 48 | ledscape_config_t * const config_union 49 | = calloc(1, sizeof(*config_union)); 50 | if (!config_union) 51 | return NULL; 52 | ledscape_matrix_config_t * const config = &config_union->matrix_config; 53 | 54 | config->type = LEDSCAPE_MATRIX; 55 | config->panel_width = 32; 56 | config->panel_height = 16; 57 | config->leds_width = 256; 58 | config->leds_height = 128; 59 | 60 | char line[1024]; 61 | int line_num = 1; 62 | 63 | while (1) 64 | { 65 | line_num++; 66 | if (readline(file, line, sizeof(line)) < 0) 67 | break; 68 | 69 | int output, panel, x, y; 70 | char orient; 71 | if (sscanf(line, "%d,%d %c %d,%d", &output, &panel, &orient, &x, &y) != 5) 72 | goto fail; 73 | if (output > LEDSCAPE_MATRIX_OUTPUTS) 74 | goto fail; 75 | if (panel > LEDSCAPE_MATRIX_PANELS) 76 | goto fail; 77 | if (x < 0 || y < 0) 78 | goto fail; 79 | 80 | ledscape_matrix_panel_t * const pconfig 81 | = &config->panels[output][panel]; 82 | 83 | pconfig->x = x; 84 | pconfig->y = y; 85 | 86 | switch (orient) 87 | { 88 | case 'N': pconfig->rot = 0; break; 89 | case 'L': pconfig->rot = 1; break; 90 | case 'R': pconfig->rot = 2; break; 91 | case 'U': pconfig->rot = 3; break; 92 | default: goto fail; 93 | } 94 | } 95 | 96 | fclose(file); 97 | return config_union; 98 | 99 | fail: 100 | fprintf(stderr, "%s: read or parse error on line %d\n", filename, line_num); 101 | fclose(file); 102 | free(config); 103 | return NULL; 104 | } 105 | 106 | 107 | ledscape_config_t * 108 | ledscape_strip_config( 109 | const char * const filename, 110 | FILE * const file 111 | ) 112 | { 113 | ledscape_config_t * const config_union 114 | = calloc(1, sizeof(*config_union)); 115 | if (!config_union) 116 | return NULL; 117 | ledscape_strip_config_t * const config = &config_union->strip_config; 118 | 119 | config->type = LEDSCAPE_STRIP; 120 | 121 | char line[1024]; 122 | int line_num = 2; 123 | 124 | if (readline(file, line, sizeof(line)) < 0) 125 | goto fail; 126 | 127 | // maybe do this better to handle mappings 128 | int width, height; 129 | if (sscanf(line, "%d,%d", &width, &height) != 2) 130 | goto fail; 131 | config->leds_width = width; 132 | config->leds_height = height; 133 | 134 | printf("strips: %d,%d\n", width, height); 135 | return config_union; 136 | 137 | fail: 138 | fprintf(stderr, "%s: line %d: parse error\n", filename, line_num); 139 | free(config_union); 140 | return NULL; 141 | } 142 | 143 | 144 | ledscape_config_t * 145 | ledscape_config( 146 | const char * filename 147 | ) 148 | { 149 | FILE * const file = fopen(filename, "r"); 150 | if (!file) 151 | { 152 | fprintf(stderr, "%s: unable to open\n", filename); 153 | return NULL; 154 | } 155 | 156 | char line[1024]; 157 | 158 | if (readline(file, line, sizeof(line)) < 0) 159 | goto fail; 160 | 161 | if (strcmp(line, "matrix16") == 0) 162 | return ledscape_matrix_config(filename, file); 163 | if (strcmp(line, "ws2812") == 0) 164 | return ledscape_strip_config(filename, file); 165 | 166 | fprintf(stderr, "%s: unknown output type '%s'\n", filename, line); 167 | fclose(file); 168 | return NULL; 169 | 170 | fail: 171 | fclose(file); 172 | return NULL; 173 | } 174 | 175 | -------------------------------------------------------------------------------- /src/ledscape/ledscape.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | * LEDscape for the BeagleBone Black. 3 | * 4 | * Drives up to 32 ws281x LED strips using the PRU to have no CPU overhead. 5 | * Allows easy double buffering of frames. 6 | */ 7 | 8 | #ifndef _ledscape_h_ 9 | #define _ledscape_h_ 10 | 11 | #include 12 | 13 | /** The number of strips supported. 14 | * 15 | * Changing this also requires changes in ws281x.p to stride the 16 | * correct number of bytes per row.. 17 | */ 18 | #define LEDSCAPE_NUM_STRIPS 32 19 | 20 | #define LEDSCAPE_MATRIX 1 21 | #define LEDSCAPE_STRIP 2 22 | 23 | 24 | typedef struct { 25 | int x; 26 | int y; 27 | int rot; // 0 == none, 1 == left, 2 == right, 3 == flip 28 | } ledscape_matrix_panel_t; 29 | 30 | #define LEDSCAPE_MATRIX_OUTPUTS 8 // number of outputs on the cape 31 | #define LEDSCAPE_MATRIX_PANELS 8 // number of panels chained per output 32 | 33 | typedef struct { 34 | int type; 35 | int width; 36 | int height; 37 | int panel_width; 38 | int panel_height; 39 | int leds_width; 40 | int leds_height; 41 | ledscape_matrix_panel_t panels[LEDSCAPE_MATRIX_OUTPUTS][LEDSCAPE_MATRIX_PANELS]; 42 | } ledscape_matrix_config_t; 43 | 44 | 45 | typedef struct { 46 | int type; 47 | int leds_width; // length of the longest strip 48 | int leds_height; // number of output strips 49 | } ledscape_strip_config_t; 50 | 51 | 52 | typedef union { 53 | int type; 54 | ledscape_matrix_config_t matrix_config; 55 | ledscape_strip_config_t strip_config; 56 | } ledscape_config_t; 57 | 58 | 59 | /** LEDscape pixel format is BRGA. 60 | * 61 | * data is laid out with BRGA format, since that is how it will 62 | * be translated during the clock out from the PRU. 63 | */ 64 | typedef struct { 65 | uint8_t b; 66 | uint8_t r; 67 | uint8_t g; 68 | uint8_t a; 69 | } __attribute__((__packed__)) ledscape_pixel_t; 70 | 71 | 72 | /** LEDscape frame buffer is "strip-major". 73 | * 74 | * All 32 strips worth of data for each pixel are stored adjacent. 75 | * This makes it easier to clock out while reading from the DDR 76 | * in a burst mode. 77 | */ 78 | typedef struct { 79 | ledscape_pixel_t strip[LEDSCAPE_NUM_STRIPS]; 80 | } __attribute__((__packed__)) ledscape_frame_t; 81 | 82 | 83 | typedef struct ledscape ledscape_t; 84 | 85 | 86 | #ifdef __cplusplus 87 | extern "C" { 88 | #endif 89 | 90 | extern ledscape_config_t ledscape_matrix_default; 91 | 92 | extern ledscape_t * 93 | ledscape_init( 94 | ledscape_config_t * config, 95 | int no_init_pru 96 | ); 97 | 98 | 99 | extern void 100 | ledscape_draw( 101 | ledscape_t * const leds, 102 | const void * const rgb // 4-byte rgb data 103 | ); 104 | 105 | 106 | extern void 107 | ledscape_set_color( 108 | ledscape_frame_t * const frame, 109 | uint8_t strip, 110 | uint8_t pixel, 111 | uint8_t r, 112 | uint8_t g, 113 | uint8_t b 114 | ); 115 | 116 | 117 | extern uint32_t 118 | ledscape_wait( 119 | ledscape_t * const leds 120 | ); 121 | 122 | 123 | extern void 124 | ledscape_close( 125 | ledscape_t * const leds 126 | ); 127 | 128 | 129 | /** Flip a rectangular frame buffer to map the LED matrices */ 130 | void 131 | ledscape_matrix_remap( 132 | uint32_t * leds_out, 133 | const uint32_t * fb_in, 134 | const ledscape_matrix_config_t * config 135 | ); 136 | 137 | 138 | /** Write with a fixed-width 8px font */ 139 | void 140 | ledscape_printf( 141 | uint32_t * px, 142 | const size_t width, 143 | const uint32_t color, 144 | const char * fmt, 145 | ... 146 | ); 147 | 148 | 149 | /** Parse a config file */ 150 | ledscape_config_t * 151 | ledscape_config( 152 | const char * filename 153 | ); 154 | 155 | #ifdef __cplusplus 156 | }; 157 | #endif 158 | 159 | #endif 160 | -------------------------------------------------------------------------------- /src/ledscape/pru.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Userspace interface to the BeagleBone PRU. 3 | * 4 | * Wraps the prussdrv library in a sane interface. 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "pru.h" 18 | 19 | 20 | static unsigned int 21 | proc_read( 22 | const char * const fname 23 | ) 24 | { 25 | FILE * const f = fopen(fname, "r"); 26 | if (!f) 27 | die("%s: Unable to open: %s", fname, strerror(errno)); 28 | unsigned int x; 29 | fscanf(f, "%x", &x); 30 | fclose(f); 31 | return x; 32 | } 33 | 34 | 35 | pru_t * 36 | pru_init( 37 | const unsigned short pru_num 38 | ) 39 | { 40 | prussdrv_init(); 41 | 42 | int ret = prussdrv_open(PRU_EVTOUT_0); 43 | if (ret) 44 | die("prussdrv_open open failed\n"); 45 | 46 | tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA; 47 | prussdrv_pruintc_init(&pruss_intc_initdata); 48 | 49 | void * pru_data_mem; 50 | prussdrv_map_prumem( 51 | pru_num == 0 ? PRUSS0_PRU0_DATARAM :PRUSS0_PRU1_DATARAM, 52 | &pru_data_mem 53 | ); 54 | 55 | const int mem_fd = open("/dev/mem", O_RDWR); 56 | if (mem_fd < 0) 57 | die("Failed to open /dev/mem: %s\n", strerror(errno)); 58 | 59 | const uintptr_t ddr_addr = proc_read("/sys/class/uio/uio0/maps/map1/addr"); 60 | const uintptr_t ddr_size = proc_read("/sys/class/uio/uio0/maps/map1/size"); 61 | 62 | const uintptr_t ddr_offset = ddr_addr; 63 | const size_t ddr_filelen = ddr_size; 64 | 65 | /* map the memory */ 66 | uint8_t * const ddr_mem = mmap( 67 | 0, 68 | ddr_filelen, 69 | PROT_WRITE | PROT_READ, 70 | MAP_SHARED, 71 | mem_fd, 72 | ddr_offset 73 | ); 74 | if (ddr_mem == MAP_FAILED) 75 | die("Failed to mmap offset %"PRIxPTR" @ %zu bytes: %s\n", 76 | ddr_offset, 77 | ddr_filelen, 78 | strerror(errno) 79 | ); 80 | 81 | close(mem_fd); 82 | 83 | pru_t * const pru = calloc(1, sizeof(*pru)); 84 | if (!pru) 85 | die("calloc failed: %s", strerror(errno)); 86 | 87 | *pru = (pru_t) { 88 | .pru_num = pru_num, 89 | .data_ram = pru_data_mem, 90 | .data_ram_size = 8192, // how to determine? 91 | .ddr_addr = ddr_addr, 92 | .ddr = (void*)(ddr_mem), 93 | .ddr_size = ddr_size, 94 | }; 95 | 96 | fprintf(stderr, 97 | "%s: PRU %d: data %p @ %zu bytes, DMA %p / %"PRIxPTR" @ %zu bytes\n", 98 | __func__, 99 | pru_num, 100 | pru->data_ram, 101 | pru->data_ram_size, 102 | pru->ddr, 103 | pru->ddr_addr, 104 | pru->ddr_size 105 | ); 106 | 107 | return pru; 108 | } 109 | 110 | 111 | void 112 | pru_exec( 113 | pru_t * const pru, 114 | const char * const program 115 | ) 116 | { 117 | char * program_unconst = (char*)(uintptr_t) program; 118 | if (prussdrv_exec_program(pru->pru_num, program_unconst) < 0) 119 | die("%s failed", program); 120 | } 121 | 122 | 123 | void 124 | pru_close( 125 | pru_t * const pru 126 | ) 127 | { 128 | // \todo unmap memory 129 | prussdrv_pru_wait_event(PRU_EVTOUT_0); 130 | prussdrv_pru_clear_event(PRU0_ARM_INTERRUPT); 131 | prussdrv_pru_disable(pru->pru_num); 132 | prussdrv_exit(); 133 | } 134 | 135 | 136 | int 137 | pru_gpio( 138 | const unsigned gpio, 139 | const unsigned pin, 140 | const unsigned direction, 141 | const unsigned initial_value 142 | ) 143 | { 144 | const unsigned pin_num = gpio * 32 + pin; 145 | const char * export_name = "/sys/class/gpio/export"; 146 | FILE * const export = fopen(export_name, "w"); 147 | if (!export) 148 | die("%s: Unable to open? %s\n", 149 | export_name, 150 | strerror(errno) 151 | ); 152 | 153 | fprintf(export, "%d\n", pin_num); 154 | fclose(export); 155 | 156 | char value_name[64]; 157 | snprintf(value_name, sizeof(value_name), 158 | "/sys/class/gpio/gpio%u/value", 159 | pin_num 160 | ); 161 | 162 | FILE * const value = fopen(value_name, "w"); 163 | if (!value) 164 | die("%s: Unable to open? %s\n", 165 | value_name, 166 | strerror(errno) 167 | ); 168 | 169 | fprintf(value, "%d\n", initial_value); 170 | fclose(value); 171 | 172 | char dir_name[64]; 173 | snprintf(dir_name, sizeof(dir_name), 174 | "/sys/class/gpio/gpio%u/direction", 175 | pin_num 176 | ); 177 | 178 | FILE * const dir = fopen(dir_name, "w"); 179 | if (!dir) 180 | die("%s: Unable to open? %s\n", 181 | dir_name, 182 | strerror(errno) 183 | ); 184 | 185 | fprintf(dir, "%s\n", direction ? "out" : "in"); 186 | fclose(dir); 187 | 188 | return 0; 189 | } 190 | -------------------------------------------------------------------------------- /src/ledscape/pru.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Simplified interface to the ARM PRU. 3 | * 4 | */ 5 | #ifndef _pru_h_ 6 | #define _pru_h_ 7 | 8 | #include 9 | #include 10 | #include "util.h" 11 | 12 | 13 | /** Mapping of the PRU memory spaces. 14 | * 15 | * The PRU has a small, fast local data RAM that is mapped into ARM memory, 16 | * as well as slower access to the DDR RAM of the ARM. 17 | */ 18 | typedef struct 19 | { 20 | unsigned pru_num; 21 | 22 | void * data_ram; // PRU data ram in ARM space 23 | size_t data_ram_size; // size in bytes of the PRU's data RAM 24 | 25 | void * ddr; // PRU DMA address (in ARM space) 26 | uintptr_t ddr_addr; // PRU DMA address (in PRU space) 27 | size_t ddr_size; // Size in bytes of the shared space 28 | } pru_t; 29 | 30 | extern pru_t * 31 | pru_init( 32 | const unsigned short pru_num 33 | ); 34 | 35 | 36 | extern void 37 | pru_exec( 38 | pru_t * const pru, 39 | const char * const program 40 | ); 41 | 42 | 43 | extern void 44 | pru_close( 45 | pru_t * const pru 46 | ); 47 | 48 | 49 | 50 | /** Configure a GPIO pin. 51 | * 52 | * Since the device tree won't do it for us, we need to do it via 53 | * /sys/class/gpio to set the direction and initial value for 54 | * all of the pins that we use. 55 | * 56 | * Direction 0 == in, 1 == out. 57 | */ 58 | extern int 59 | pru_gpio( 60 | unsigned gpio, 61 | unsigned pin, 62 | unsigned direction, 63 | const unsigned initial_value 64 | ); 65 | 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /src/ledscape/util.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Various utility functions 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "util.h" 20 | 21 | /** Write all the bytes to a fd, even if there is a brief interruption. 22 | * \return number of bytes written or -1 on any fatal error. 23 | */ 24 | ssize_t 25 | write_all( 26 | const int fd, 27 | const void * const buf_ptr, 28 | const size_t len 29 | ) 30 | { 31 | const uint8_t * const buf = buf_ptr; 32 | size_t offset = 0; 33 | 34 | while (offset < len) 35 | { 36 | const ssize_t rc = write(fd, buf + offset, len - offset); 37 | if (rc < 0) 38 | { 39 | if (errno == EAGAIN) 40 | continue; 41 | return -1; 42 | } 43 | 44 | if (rc == 0) 45 | return -1; 46 | 47 | offset += rc; 48 | } 49 | 50 | return len; 51 | } 52 | 53 | 54 | int 55 | serial_open( 56 | const char * const dev 57 | ) 58 | { 59 | const int fd = open(dev, O_RDWR | O_NONBLOCK | O_NOCTTY, 0666); 60 | if (fd < 0) 61 | return -1; 62 | 63 | // Disable modem control signals 64 | struct termios attr; 65 | tcgetattr(fd, &attr); 66 | attr.c_cflag |= CLOCAL | CREAD; 67 | attr.c_oflag &= ~OPOST; 68 | tcsetattr(fd, TCSANOW, &attr); 69 | 70 | return fd; 71 | } 72 | 73 | 74 | void 75 | hexdump( 76 | FILE * const outfile, 77 | const void * const buf, 78 | const size_t len 79 | ) 80 | { 81 | const uint8_t * const p = buf; 82 | 83 | for(size_t i = 0 ; i < len ; i++) 84 | { 85 | if (i % 8 == 0) 86 | fprintf(outfile, "%s%04zu:", i == 0 ? "": "\n", i); 87 | fprintf(outfile, " %02x", p[i]); 88 | } 89 | 90 | fprintf(outfile, "\n"); 91 | } 92 | 93 | -------------------------------------------------------------------------------- /src/ledscape/util.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Utility functions. 3 | */ 4 | #ifndef _util_h_ 5 | #define _util_h_ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define warn(fmt, ...) \ 15 | do { \ 16 | fprintf(stderr, "%s:%d: " fmt, \ 17 | __func__, __LINE__, ## __VA_ARGS__); \ 18 | } while (0) 19 | 20 | #define warn_once(fmt, ...) \ 21 | do { \ 22 | static unsigned __warned__; \ 23 | if (__warned__) \ 24 | break; \ 25 | __warned__ = 1; \ 26 | warn(fmt, ## __VA_ARGS__ ); \ 27 | } while (0) 28 | 29 | #define die(fmt, ...) \ 30 | do { \ 31 | warn(fmt, ## __VA_ARGS__ ); \ 32 | exit(EXIT_FAILURE); \ 33 | } while(0) 34 | 35 | 36 | extern void 37 | hexdump( 38 | FILE * const outfile, 39 | const void * const buf, 40 | const size_t len 41 | ); 42 | 43 | 44 | extern int 45 | serial_open( 46 | const char * const dev 47 | ); 48 | 49 | 50 | /** Write all the bytes to a fd, even if there is a brief interruption. 51 | * \return number of bytes written or -1 on any fatal error. 52 | */ 53 | extern ssize_t 54 | write_all( 55 | const int fd, 56 | const void * const buf_ptr, 57 | const size_t len 58 | ); 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /src/ledscape/ws281x.hp: -------------------------------------------------------------------------------- 1 | #ifndef _ws281x_HP_ 2 | #define _ws281x_HP_ 3 | 4 | 5 | #define AM33XX 6 | 7 | // *************************************** 8 | // * Global Macro definitions * 9 | // *************************************** 10 | 11 | #ifdef AM33XX 12 | 13 | // Refer to this mapping in the file - \prussdrv\include\pruss_intc_mapping.h 14 | #define PRU0_PRU1_INTERRUPT 17 15 | #define PRU1_PRU0_INTERRUPT 18 16 | #define PRU0_ARM_INTERRUPT 19 17 | #define PRU1_ARM_INTERRUPT 20 18 | #define ARM_PRU0_INTERRUPT 21 19 | #define ARM_PRU1_INTERRUPT 22 20 | 21 | #define CONST_PRUDRAM C24 22 | #define CONST_SHAREDRAM C28 23 | #define CONST_L3RAM C30 24 | #define CONST_DDR C31 25 | 26 | // Address for the Constant table Programmable Pointer Register 0(CTPPR_0) 27 | #define CTBIR_0 0x22020 28 | // Address for the Constant table Programmable Pointer Register 0(CTPPR_0) 29 | #define CTBIR_1 0x22024 30 | 31 | // Address for the Constant table Programmable Pointer Register 0(CTPPR_0) 32 | #define CTPPR_0 0x22028 33 | // Address for the Constant table Programmable Pointer Register 1(CTPPR_1) 34 | #define CTPPR_1 0x2202C 35 | 36 | #else 37 | 38 | // Refer to this mapping in the file - \prussdrv\include\pruss_intc_mapping.h 39 | #define PRU0_PRU1_INTERRUPT 32 40 | #define PRU1_PRU0_INTERRUPT 33 41 | #define PRU0_ARM_INTERRUPT 34 42 | #define PRU1_ARM_INTERRUPT 35 43 | #define ARM_PRU0_INTERRUPT 36 44 | #define ARM_PRU1_INTERRUPT 37 45 | 46 | #define CONST_PRUDRAM C3 47 | #define CONST_HPI C15 48 | #define CONST_DSPL2 C28 49 | #define CONST_L3RAM C30 50 | #define CONST_DDR C31 51 | 52 | // Address for the Constant table Programmable Pointer Register 0(CTPPR_0) 53 | #define CTPPR_0 0x7028 54 | // Address for the Constant table Programmable Pointer Register 1(CTPPR_1) 55 | #define CTPPR_1 0x702C 56 | 57 | #endif 58 | 59 | .macro LD32 60 | .mparam dst,src 61 | LBBO dst,src,#0x00,4 62 | .endm 63 | 64 | .macro LD16 65 | .mparam dst,src 66 | LBBO dst,src,#0x00,2 67 | .endm 68 | 69 | .macro LD8 70 | .mparam dst,src 71 | LBBO dst,src,#0x00,1 72 | .endm 73 | 74 | .macro ST32 75 | .mparam src,dst 76 | SBBO src,dst,#0x00,4 77 | .endm 78 | 79 | .macro ST16 80 | .mparam src,dst 81 | SBBO src,dst,#0x00,2 82 | .endm 83 | 84 | .macro ST8 85 | .mparam src,dst 86 | SBBO src,dst,#0x00,1 87 | .endm 88 | 89 | 90 | #define sp r0 91 | #define lr r23 92 | #define STACK_TOP (0x2000 - 4) 93 | #define STACK_BOTTOM (0x2000 - 0x200) 94 | 95 | .macro stack_init 96 | mov sp, STACK_BOTTOM 97 | .endm 98 | 99 | .macro push 100 | .mparam reg, cnt 101 | sbbo reg, sp, 0, 4*cnt 102 | add sp, sp, 4*cnt 103 | .endm 104 | 105 | .macro pop 106 | .mparam reg, cnt 107 | sub sp, sp, 4*cnt 108 | lbbo reg, sp, 0, 4*cnt 109 | .endm 110 | 111 | // *************************************** 112 | // * Global Structure Definitions * 113 | // *************************************** 114 | 115 | 116 | // *************************************** 117 | // * Global Register Assignments * 118 | // *************************************** 119 | 120 | 121 | #endif //_ws281x_HP_ 122 | -------------------------------------------------------------------------------- /src/mta/Makefile: -------------------------------------------------------------------------------- 1 | ######### 2 | # 3 | # Build the MTA sign demo 4 | # 5 | # 6 | BIN-y += mta-sign 7 | 8 | mta-sign.srcs += mta-sign.c 9 | mta-sign.srcs += mta-font.c 10 | 11 | include ../../Makefile.common 12 | 13 | -------------------------------------------------------------------------------- /src/mta/mta-sign.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Test the matrix LCD PRU firmware with a multi-hue rainbow. 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "ledscape.h" 13 | 14 | const int width = 256; 15 | const int height = 128; 16 | 17 | extern const uint16_t font[][16]; 18 | 19 | static int 20 | font_write( 21 | uint32_t * const buf, 22 | const uint32_t color, 23 | const int x0, 24 | const int y0, 25 | const char * s 26 | ) 27 | { 28 | int x = x0; 29 | int y = y0; 30 | 31 | while (1) 32 | { 33 | char c = *s++; 34 | if (!c) 35 | break; 36 | 37 | if (c == '\n') 38 | { 39 | x = x0; 40 | y += 16 * width; 41 | continue; 42 | } 43 | 44 | const uint16_t * ch = font[(uint8_t) c]; 45 | int max_width = 0; 46 | 47 | if (c == ' ' || c == '.') 48 | max_width = 3; 49 | else 50 | for (int h = 0 ; h < 16 ; h++) 51 | { 52 | int width = 0; 53 | uint16_t row = ch[h] >> 2; 54 | while (row) 55 | { 56 | row >>= 1; 57 | width++; 58 | } 59 | 60 | if (width > max_width) 61 | max_width = width; 62 | } 63 | 64 | // add space after the character 65 | max_width++; 66 | 67 | for (int h = 0 ; h < 16 ; h++) 68 | { 69 | uint16_t row = ch[h] >> 2; 70 | for (int j = 0 ; j < max_width ; j++, row >>= 1) 71 | { 72 | uint32_t pixel_color = (row & 1) ? color : 0; 73 | int ox = x + j; 74 | /* 75 | if (x + j >= width || x + j < 0) 76 | continue; 77 | */ 78 | if (ox >= width) 79 | continue; 80 | 81 | // wrap in x 82 | if (ox < 0) 83 | ox += width; 84 | 85 | if (y + h >= height || y + h < 0) 86 | continue; 87 | 88 | uint8_t * pix = (uint8_t*) &buf[(y+h)*width + ox]; 89 | pix[0] = pixel_color >> 16; 90 | pix[1] = pixel_color >> 8; 91 | pix[2] = pixel_color >> 0; 92 | } 93 | } 94 | 95 | x += max_width; 96 | } 97 | 98 | return x; 99 | } 100 | 101 | 102 | int 103 | main( 104 | int argc, 105 | char ** argv 106 | ) 107 | { 108 | ledscape_config_t * config = &ledscape_matrix_default; 109 | if (argc > 1) 110 | { 111 | config = ledscape_config(argv[1]); 112 | if (!config) 113 | return EXIT_FAILURE; 114 | } 115 | 116 | ledscape_t * const leds = ledscape_init(config, 0); 117 | 118 | printf("init done\n"); 119 | time_t last_time = time(NULL); 120 | unsigned last_i = 0; 121 | 122 | unsigned i = 0; 123 | uint32_t * const p = calloc(width*height, 4); 124 | int scroll_x = width; 125 | 126 | while (1) 127 | { 128 | font_write(p, 0x00FF00, 0, 0, "1.! NYCResistor-Atlantic Pacific"); 129 | font_write(p, 0xFF0000, 11, 0, "!"); 130 | font_write(p, 0x00FF00, 224, 0, "8min"); 131 | 132 | int end_x = font_write(p, 0xFF4000, scroll_x, 16, argc > 2 ? argv[2] : ""); 133 | if (end_x <= 0) 134 | scroll_x = width; 135 | else 136 | scroll_x--; 137 | 138 | ledscape_draw(leds, p); 139 | usleep(20000); 140 | 141 | // wait for the previous frame to finish; 142 | //const uint32_t response = ledscape_wait(leds); 143 | const uint32_t response = 0; 144 | time_t now = time(NULL); 145 | if (now != last_time) 146 | { 147 | printf("%d fps. starting %d previous %"PRIx32"\n", 148 | i - last_i, i, response); 149 | last_i = i; 150 | last_time = now; 151 | } 152 | 153 | } 154 | 155 | ledscape_close(leds); 156 | 157 | return EXIT_SUCCESS; 158 | } 159 | -------------------------------------------------------------------------------- /src/net/Makefile: -------------------------------------------------------------------------------- 1 | ######### 2 | # 3 | # Build the various network receiving tools 4 | # 5 | # 6 | BIN-y += matrix-udp-rx 7 | BIN-y += matrix-tcp-rx 8 | BIN-y += opc-rx 9 | 10 | matrix-udp-rx.srcs += matrix-udp-rx.c 11 | matrix-tcp-rx.srcs += matrix-tcp-rx.c 12 | opc-rx.srcs += opc-rx.c 13 | 14 | include ../../Makefile.common 15 | 16 | -------------------------------------------------------------------------------- /src/net/matrix-tcp-rx.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * TCP image packet receiver. 3 | * 4 | * Based on the HackRockCity LED Display code: 5 | * https://github.com/agwn/pyramidTransmitter/blob/master/LEDDisplay.pde 6 | * 7 | * Designed to render into the LED matrix. 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "util.h" 26 | #include "ledscape.h" 27 | 28 | static int verbose; 29 | 30 | static int 31 | tcp_socket( 32 | const int port 33 | ) 34 | { 35 | const int sock = socket(AF_INET, SOCK_STREAM, 0); 36 | struct sockaddr_in addr = { 37 | .sin_family = AF_INET, 38 | .sin_port = htons(port), 39 | .sin_addr.s_addr = INADDR_ANY, 40 | }; 41 | 42 | if (sock < 0) 43 | return -1; 44 | if (bind(sock, (const struct sockaddr*) &addr, sizeof(addr)) < 0) 45 | return -1; 46 | if (listen(sock, 5) , 0) 47 | return -1; 48 | 49 | int yes = 1; 50 | if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) 51 | return -1; 52 | 53 | return sock; 54 | } 55 | 56 | 57 | static int 58 | wait_socket( 59 | int fd, 60 | int msec_timeout 61 | ) 62 | { 63 | struct timeval tv = { msec_timeout / 1000, msec_timeout % 1000 }; 64 | fd_set fds; 65 | FD_ZERO(&fds); 66 | FD_SET(fd, &fds); 67 | return select(fd+1, &fds, NULL, NULL, &tv); 68 | } 69 | 70 | 71 | static ssize_t 72 | recv_all( 73 | const int sock, 74 | void * const buf_out, 75 | size_t len 76 | ) 77 | { 78 | uint8_t * buf = buf_out; 79 | size_t off = 0; 80 | 81 | while (off < len) 82 | { 83 | const ssize_t rlen = recv(sock, buf+off, len-off, 0); 84 | if (rlen < 0) 85 | return rlen; 86 | if (rlen == 0) 87 | break; 88 | off += rlen; 89 | } 90 | 91 | return off; 92 | } 93 | 94 | 95 | static struct option long_options[] = 96 | { 97 | /* These options set a flag. */ 98 | {"verbose", no_argument, 0, 1}, 99 | {"port", required_argument, 0, 'p'}, 100 | {"width", required_argument, 0, 'W'}, 101 | {"height", required_argument, 0, 'H'}, 102 | {"config", required_argument, 0, 'c'}, 103 | {"timeout", required_argument, 0, 't'}, 104 | {"message", required_argument, 0, 'm'}, 105 | {"noinit", no_argument, 0, 'n'}, 106 | {0, 0, 0, 0} 107 | }; 108 | 109 | 110 | static void usage(void) 111 | { 112 | fprintf(stderr, "usage not yet written\n"); 113 | exit(EXIT_FAILURE); 114 | } 115 | 116 | 117 | const unsigned int packets_per_frame = 2; 118 | 119 | int 120 | main( 121 | int argc, 122 | char ** argv 123 | ) 124 | { 125 | /* getopt_long stores the option index here. */ 126 | int option_index = 0; 127 | int port = 9999; 128 | const char * config_file = NULL; 129 | const char * startup_message = ""; 130 | int timeout = 60; 131 | unsigned width = 512; 132 | unsigned height = 64; 133 | int no_init = 0; 134 | 135 | while (1) 136 | { 137 | const int c = getopt_long( 138 | argc, 139 | argv, 140 | "vp:c:t:W:H:m:n", 141 | long_options, 142 | &option_index 143 | ); 144 | 145 | if (c == -1) 146 | break; 147 | switch (c) 148 | { 149 | case 'v': 150 | verbose++; 151 | break; 152 | case 'n': 153 | no_init++; 154 | break; 155 | case 'c': 156 | config_file = optarg; 157 | break; 158 | case 't': 159 | timeout = atoi(optarg); 160 | break; 161 | case 'W': 162 | width = atoi(optarg); 163 | break; 164 | case 'H': 165 | height = atoi(optarg); 166 | break; 167 | case 'm': 168 | startup_message = optarg; 169 | break; 170 | default: 171 | usage(); 172 | return -1; 173 | } 174 | } 175 | 176 | const int sock = tcp_socket(port); 177 | if (sock < 0) 178 | die("socket port %d failed: %s\n", port, strerror(errno)); 179 | 180 | const size_t image_size = width * height * 3; 181 | const size_t buf_size = (width*height*4)/packets_per_frame + 1; 182 | 183 | // largest possible UDP packet 184 | uint8_t *buf = malloc(buf_size); 185 | #if 0 186 | if (sizeof(buf) < image_size + 1) 187 | die("%u x %u too large for UDP\n", width, height); 188 | #endif 189 | 190 | fprintf(stderr, "%u x %u, TCP port %u\n", width, height, port); 191 | 192 | ledscape_config_t * config = &ledscape_matrix_default; 193 | if (config_file) 194 | { 195 | config = ledscape_config(config_file); 196 | if (!config) 197 | return EXIT_FAILURE; 198 | } 199 | 200 | if (config->type == LEDSCAPE_MATRIX) 201 | { 202 | config->matrix_config.width = width; 203 | config->matrix_config.height = height; 204 | } 205 | 206 | ledscape_t * const leds = ledscape_init(config, no_init); 207 | if (!leds) 208 | return EXIT_FAILURE; 209 | 210 | const unsigned report_interval = 10; 211 | unsigned last_report = 0; 212 | unsigned long delta_sum = 0; 213 | unsigned frames = 0; 214 | 215 | uint32_t * const fb = calloc(width*height,4); 216 | ledscape_printf(fb, width, 0xFF0000, "%s", startup_message); 217 | ledscape_printf(fb+16*width, width, 0x00FF00, "%dx%d UDP port %d", width, height, port); 218 | ledscape_draw(leds, fb); 219 | 220 | while (1) 221 | { 222 | int rc = wait_socket(sock, timeout*1000); 223 | if (rc < 0) 224 | { 225 | // something failed 226 | memset(fb, 0, width*height*4); 227 | ledscape_printf(fb, width, 0xFF0000, "read failed?"); 228 | ledscape_draw(leds, fb); 229 | exit(EXIT_FAILURE); 230 | } 231 | 232 | if (rc == 0) 233 | { 234 | // go into timeout mode 235 | memset(fb, 0, width*height*4); 236 | ledscape_printf(fb, width, 0xFF0000, "timeout"); 237 | ledscape_draw(leds, fb); 238 | continue; 239 | } 240 | 241 | const int new_sock = accept(sock, NULL, NULL); 242 | if (new_sock < 0) 243 | { 244 | perror("accept"); 245 | continue; 246 | } 247 | 248 | while (1) 249 | { 250 | int rc = wait_socket(new_sock, timeout*1000); 251 | if (rc < 0) 252 | break; 253 | if (rc == 0) 254 | continue; 255 | 256 | const size_t packet_size = 1 + 3*512*32; 257 | const ssize_t rlen = recv_all(new_sock, buf, packet_size); 258 | if (rlen < 0) 259 | die("recv failed: %s\n", strerror(errno)); 260 | if ((size_t) rlen != packet_size) 261 | break; 262 | 263 | warn_once("received %zu bytes\n", rlen); 264 | 265 | /* 266 | if (buf[0] == 2) 267 | { 268 | // image type 269 | printf("image type: %.*s\n", 270 | (int) rlen - 1, 271 | &buf[1] 272 | ); 273 | continue; 274 | } 275 | 276 | if (buf[0] != 1) 277 | { 278 | // What is it? 279 | warn_once("Unknown image type '%c' (%02x)\n", 280 | buf[0], 281 | buf[0] 282 | ); 283 | continue; 284 | } 285 | */ 286 | const unsigned frame_part = buf[0]; 287 | if (frame_part != 0 && frame_part != 1) 288 | { 289 | printf("bad type %d\n", frame_part); 290 | continue; 291 | } 292 | 293 | if ((size_t) rlen != image_size + 1) 294 | { 295 | warn_once("WARNING: Received packet %zu bytes, expected %zu\n", 296 | rlen, 297 | image_size + 1 298 | ); 299 | } 300 | 301 | struct timeval start_tv, stop_tv, delta_tv; 302 | gettimeofday(&start_tv, NULL); 303 | 304 | const unsigned frame_num = 0; 305 | 306 | // copy the 3-byte values into the 4-byte framebuffer 307 | // and turn onto the side 308 | for (unsigned x = 0 ; x < width ; x++) // 256 309 | { 310 | for (unsigned y = 0 ; y < 32 ; y++) // 64 311 | { 312 | uint32_t * out = (void*) &fb[(y+32*frame_part)*width + x]; 313 | const uint8_t * const in = &buf[1 + 3*(y*width + x)]; 314 | uint32_t r = in[0]; 315 | uint32_t g = in[1]; 316 | uint32_t b = in[2]; 317 | *out = (r << 16) | (g << 8) | (b << 0); 318 | } 319 | } 320 | 321 | // only draw after the second frame 322 | if (frame_part == 1) 323 | ledscape_draw(leds, fb); 324 | 325 | gettimeofday(&stop_tv, NULL); 326 | timersub(&stop_tv, &start_tv, &delta_tv); 327 | 328 | frames++; 329 | delta_sum += delta_tv.tv_usec; 330 | if (stop_tv.tv_sec - last_report < report_interval) 331 | continue; 332 | last_report = stop_tv.tv_sec; 333 | 334 | const unsigned delta_avg = delta_sum / frames; 335 | printf("%6u usec avg, max %.2f fps, actual %.2f fps (over %u frames)\n", 336 | delta_avg, 337 | report_interval * 1.0e6 / delta_avg, 338 | frames * 1.0 / report_interval, 339 | frames 340 | ); 341 | 342 | frames = delta_sum = 0; 343 | } 344 | 345 | // this socket was closed 346 | fprintf(stderr, "socket closed, waiting for new conncetion\n"); 347 | close(new_sock); 348 | } 349 | 350 | return 0; 351 | } 352 | -------------------------------------------------------------------------------- /src/net/matrix-udp-rx.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * UDP image packet receiver. 3 | * 4 | * Based on the HackRockCity LED Display code: 5 | * https://github.com/agwn/pyramidTransmitter/blob/master/LEDDisplay.pde 6 | * 7 | * Designed to render into the LED matrix. 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "util.h" 26 | #include "ledscape.h" 27 | 28 | static int verbose; 29 | 30 | static int 31 | udp_socket( 32 | const int port 33 | ) 34 | { 35 | const int sock = socket(AF_INET, SOCK_DGRAM, 0); 36 | struct sockaddr_in addr = { 37 | .sin_family = AF_INET, 38 | .sin_port = htons(port), 39 | .sin_addr.s_addr = INADDR_ANY, 40 | }; 41 | 42 | if (sock < 0) 43 | return -1; 44 | if (bind(sock, (const struct sockaddr*) &addr, sizeof(addr)) < 0) 45 | return -1; 46 | 47 | return sock; 48 | } 49 | 50 | 51 | static int 52 | wait_socket( 53 | int fd, 54 | int msec_timeout 55 | ) 56 | { 57 | struct timeval tv = { msec_timeout / 1000, msec_timeout % 1000 }; 58 | fd_set fds; 59 | FD_ZERO(&fds); 60 | FD_SET(fd, &fds); 61 | return select(fd+1, &fds, NULL, NULL, &tv); 62 | } 63 | 64 | 65 | static struct option long_options[] = 66 | { 67 | /* These options set a flag. */ 68 | {"verbose", no_argument, 0, 1}, 69 | {"port", required_argument, 0, 'p'}, 70 | {"width", required_argument, 0, 'W'}, 71 | {"height", required_argument, 0, 'H'}, 72 | {"config", required_argument, 0, 'c'}, 73 | {"timeout", required_argument, 0, 't'}, 74 | {"message", required_argument, 0, 'm'}, 75 | {"noinit", no_argument, 0, 'n'}, 76 | {0, 0, 0, 0} 77 | }; 78 | 79 | 80 | static void usage(void) 81 | { 82 | fprintf(stderr, "usage not yet written\n"); 83 | exit(EXIT_FAILURE); 84 | } 85 | 86 | 87 | int 88 | main( 89 | int argc, 90 | char ** argv 91 | ) 92 | { 93 | /* getopt_long stores the option index here. */ 94 | int option_index = 0; 95 | int port = 9999; 96 | const char * config_file = NULL; 97 | const char * startup_message = ""; 98 | int timeout = 60; 99 | unsigned width = 512; 100 | unsigned height = 64; 101 | int no_init = 0; 102 | 103 | while (1) 104 | { 105 | const int c = getopt_long( 106 | argc, 107 | argv, 108 | "vp:c:t:W:H:m:n", 109 | long_options, 110 | &option_index 111 | ); 112 | 113 | if (c == -1) 114 | break; 115 | switch (c) 116 | { 117 | case 'v': 118 | verbose++; 119 | break; 120 | case 'n': 121 | no_init++; 122 | break; 123 | case 'c': 124 | config_file = optarg; 125 | break; 126 | case 't': 127 | timeout = atoi(optarg); 128 | break; 129 | case 'W': 130 | width = atoi(optarg); 131 | break; 132 | case 'H': 133 | height = atoi(optarg); 134 | if(height%2) { 135 | die("Height must be even!\n"); 136 | } 137 | break; 138 | case 'm': 139 | startup_message = optarg; 140 | break; 141 | default: 142 | usage(); 143 | return -1; 144 | } 145 | } 146 | 147 | const int sock = udp_socket(port); 148 | if (sock < 0) 149 | die("socket port %d failed: %s\n", port, strerror(errno)); 150 | 151 | const size_t image_size = width * height * 3; 152 | const size_t frame_size = 1 + image_size/2; 153 | 154 | // largest possible UDP packet 155 | // 1 header byte + width*height/2 for a half frame 156 | uint8_t *buf = calloc(frame_size,1); 157 | 158 | fprintf(stderr, "%u x %u, UDP port %u\n", width, height, port); 159 | 160 | ledscape_config_t * config = &ledscape_matrix_default; 161 | 162 | if (config_file) 163 | { 164 | config = ledscape_config(config_file); 165 | if (!config) 166 | return EXIT_FAILURE; 167 | } 168 | 169 | if (config->type == LEDSCAPE_MATRIX) 170 | { 171 | config->matrix_config.width = width; 172 | config->matrix_config.height = height; 173 | } 174 | 175 | ledscape_t * const leds = ledscape_init(config, no_init); 176 | if (!leds) 177 | return EXIT_FAILURE; 178 | 179 | const unsigned report_interval = 10; 180 | unsigned last_report = 0; 181 | unsigned long delta_sum = 0; 182 | unsigned frames = 0; 183 | 184 | uint32_t * const fb = calloc(width*height,4); 185 | 186 | ledscape_printf(fb, width, 0xFF0000, "%s", startup_message); 187 | ledscape_printf(fb+16*width, width, 0x00FF00, "%dx%d UDP port %d", width, height, port); 188 | ledscape_draw(leds, fb); 189 | ledscape_draw(leds, fb); // TODO: Why do we have to do this twice? 190 | 191 | while (1) 192 | { 193 | int rc = wait_socket(sock, timeout*1000); 194 | if (rc < 0) 195 | { 196 | // something failed 197 | memset(fb, 0, width*height*4); 198 | ledscape_printf(fb, width, 0xFF0000, "read failed?"); 199 | ledscape_draw(leds, fb); 200 | exit(EXIT_FAILURE); 201 | } 202 | 203 | if (rc == 0) 204 | { 205 | // go into timeout mode 206 | memset(fb, 0, width*height*4); 207 | ledscape_printf(fb, width, 0xFF0000, "timeout"); 208 | ledscape_draw(leds, fb); 209 | continue; 210 | } 211 | 212 | const ssize_t rlen = recv(sock, buf, frame_size, 0); 213 | if (rlen < 0) 214 | die("recv failed: %s\n", strerror(errno)); 215 | warn_once("received %zu bytes\n", rlen); 216 | 217 | const unsigned frame_part = buf[0]; 218 | if (frame_part != 0 && frame_part != 1) 219 | { 220 | printf("bad type %d\n", frame_part); 221 | continue; 222 | } 223 | 224 | if ((size_t) rlen != frame_size) 225 | { 226 | printf("WARNING: Received packet %zu bytes, expected %zu\n", 227 | rlen, 228 | frame_size 229 | ); 230 | } 231 | 232 | struct timeval start_tv, stop_tv, delta_tv; 233 | gettimeofday(&start_tv, 0); 234 | 235 | // copy the 3-byte values into the 4-byte framebuffer 236 | for (unsigned x = 0 ; x < width ; x++) 237 | { 238 | for (unsigned y = 0 ; y < height ; y++) 239 | { 240 | uint32_t * out = &fb[(height*frame_part/2 + y)*width + x]; 241 | const uint8_t * in = &buf[1 + 3*(y*width + x)]; 242 | 243 | uint8_t r = in[0]; 244 | uint8_t g = in[1]; 245 | uint8_t b = in[2]; 246 | 247 | *out = (r << 16) | (g << 8) | (b << 0); 248 | } 249 | } 250 | 251 | // only draw after the second frame 252 | if (frame_part == 1) 253 | ledscape_draw(leds, fb); 254 | 255 | gettimeofday(&stop_tv, 0); 256 | timersub(&stop_tv, &start_tv, &delta_tv); 257 | 258 | frames++; 259 | delta_sum += delta_tv.tv_usec; 260 | if (stop_tv.tv_sec - last_report < report_interval) 261 | continue; 262 | last_report = stop_tv.tv_sec; 263 | 264 | const unsigned delta_avg = delta_sum / frames; 265 | printf("%6u usec avg, max %.2f fps, actual %.2f fps (over %u frames)\n", 266 | delta_avg, 267 | report_interval * 1.0e6 / delta_avg, 268 | frames * 1.0 / report_interval, 269 | frames 270 | ); 271 | 272 | frames = delta_sum = 0; 273 | } 274 | 275 | return 0; 276 | } 277 | -------------------------------------------------------------------------------- /src/net/opc-rx.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * OPC image packet receiver. 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "util.h" 20 | #include "ledscape.h" 21 | 22 | typedef struct 23 | { 24 | uint8_t channel; 25 | uint8_t command; 26 | uint8_t len_hi; 27 | uint8_t len_lo; 28 | } opc_cmd_t; 29 | 30 | static int 31 | tcp_socket( 32 | const int port 33 | ) 34 | { 35 | const int sock = socket(AF_INET, SOCK_STREAM, 0); 36 | struct sockaddr_in addr = { 37 | .sin_family = AF_INET, 38 | .sin_port = htons(port), 39 | .sin_addr.s_addr = INADDR_ANY, 40 | }; 41 | 42 | if (sock < 0) 43 | return -1; 44 | if (bind(sock, (const struct sockaddr*) &addr, sizeof(addr)) < 0) 45 | return -1; 46 | if (listen(sock, 5) == -1) 47 | return -1; 48 | 49 | return sock; 50 | } 51 | 52 | 53 | int 54 | main( 55 | int argc, 56 | char ** argv 57 | ) 58 | { 59 | const int port = 7890; 60 | const int sock = tcp_socket(port); 61 | if (sock < 0) 62 | die("socket port %d failed: %s\n", port, strerror(errno)); 63 | 64 | const unsigned width = 32; 65 | const unsigned height = 16; 66 | const size_t image_size = width * height * 3; 67 | 68 | // largest possible UDP packet 69 | uint8_t buf[65536]; 70 | if (sizeof(buf) < image_size + 1) 71 | die("%u x %u too large for UDP\n", width, height); 72 | 73 | fprintf(stderr, "%u x %u, TCP port %u\n", width, height, port); 74 | 75 | ledscape_config_t * config = &ledscape_matrix_default; 76 | ledscape_t * const leds = ledscape_init(config, 0); 77 | 78 | const unsigned report_interval = 10; 79 | unsigned last_report = 0; 80 | unsigned long delta_sum = 0; 81 | unsigned frames = 0; 82 | 83 | uint32_t * const fb = calloc(width*height,4); 84 | 85 | int fd; 86 | while ((fd = accept(sock, NULL, NULL)) >= 0) 87 | { 88 | while(1) 89 | { 90 | opc_cmd_t cmd; 91 | ssize_t rlen = read(fd, &cmd, sizeof(cmd)); 92 | if (rlen < 0) 93 | die("recv failed: %s\n", strerror(errno)); 94 | if (rlen == 0) 95 | { 96 | close(fd); 97 | break; 98 | } 99 | 100 | const size_t cmd_len = cmd.len_hi << 8 | cmd.len_lo; 101 | warn("received %zu bytes: %d %zu\n", rlen, cmd.command, cmd_len); 102 | 103 | size_t offset = 0; 104 | while (offset < cmd_len) 105 | { 106 | rlen = read(fd, buf + offset, cmd_len - offset); 107 | if (rlen < 0) 108 | die("recv failed: %s\n", strerror(errno)); 109 | if (rlen == 0) 110 | break; 111 | offset += rlen; 112 | } 113 | 114 | if (cmd.command != 0) 115 | continue; 116 | 117 | struct timeval start_tv, stop_tv, delta_tv; 118 | gettimeofday(&start_tv, NULL); 119 | 120 | const unsigned frame_num = 0; 121 | 122 | for (unsigned y = 0 ; y < height ; y++) 123 | { 124 | for (unsigned x = 0 ; x < width ; x++) 125 | { 126 | uint8_t * const out = (void*) &fb[x + y*width]; 127 | const uint8_t * const in = &buf[3*(x + y*width)]; 128 | out[0] = in[0]; 129 | out[1] = in[1]; 130 | out[2] = in[2]; 131 | } 132 | } 133 | 134 | ledscape_draw(leds, fb); 135 | 136 | gettimeofday(&stop_tv, NULL); 137 | timersub(&stop_tv, &start_tv, &delta_tv); 138 | 139 | frames++; 140 | delta_sum += delta_tv.tv_usec; 141 | if (stop_tv.tv_sec - last_report < report_interval) 142 | continue; 143 | last_report = stop_tv.tv_sec; 144 | 145 | const unsigned delta_avg = delta_sum / frames; 146 | printf("%6u usec avg, max %.2f fps, actual %.2f fps (over %u frames)\n", 147 | delta_avg, 148 | report_interval * 1.0e6 / delta_avg, 149 | frames * 1.0 / report_interval, 150 | frames 151 | ); 152 | 153 | frames = delta_sum = 0; 154 | } 155 | } 156 | 157 | return 0; 158 | } 159 | -------------------------------------------------------------------------------- /src/perlin/Makefile: -------------------------------------------------------------------------------- 1 | #============================================================================================== 2 | # LED Matrix Animated Pattern Generator 3 | # Copyright 2014 by Glen Akins. 4 | # All rights reserved. 5 | # 6 | # Set editor width to 96 and tab stop to 4. 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | #============================================================================================== 21 | 22 | BIN-y += perlin 23 | perlin.srcs += \ 24 | perlin-ledscape.cpp \ 25 | pf2.cpp \ 26 | pattern.cpp \ 27 | 28 | include ../../Makefile.common 29 | -------------------------------------------------------------------------------- /src/perlin/gammalut.h: -------------------------------------------------------------------------------- 1 | //============================================================================================= 2 | // LED Matrix Animated Pattern Generator 3 | // Adafruit LED Panel Gamma Lookup Table 4 | // Modified by Glen Akins, Feb 2014 5 | // Table renamed to prevent conflicts with names of functions in math.h. 6 | //============================================================================================= 7 | 8 | #ifndef __gammalut_h_ 9 | #define __gammalut_h_ 10 | 11 | static const uint8_t gammaLut[] = { 12 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 13 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 14 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 15 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 16 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 17 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 18 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 19 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 20 | 0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01, 21 | 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 22 | 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 23 | 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 24 | 0x01,0x01,0x01,0x01,0x01,0x01,0x02,0x02, 25 | 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 26 | 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 27 | 0x02,0x02,0x02,0x02,0x02,0x03,0x03,0x03, 28 | 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 29 | 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x04, 30 | 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 31 | 0x04,0x04,0x04,0x04,0x04,0x04,0x05,0x05, 32 | 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 33 | 0x05,0x05,0x05,0x06,0x06,0x06,0x06,0x06, 34 | 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x07, 35 | 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 36 | 0x07,0x07,0x08,0x08,0x08,0x08,0x08,0x08, 37 | 0x08,0x08,0x08,0x08,0x09,0x09,0x09,0x09, 38 | 0x09,0x09,0x09,0x09,0x09,0x0a,0x0a,0x0a, 39 | 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0b,0x0b, 40 | 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0c,0x0c, 41 | 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0d,0x0d, 42 | 0x0d,0x0d,0x0d,0x0d,0x0d,0x0e,0x0e,0x0e, 43 | 0x0e,0x0e,0x0e,0x0e,0x0f,0x0f,0x0f,0x0f 44 | }; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /src/perlin/globals.h: -------------------------------------------------------------------------------- 1 | //============================================================================================= 2 | // LED Matrix Animated Pattern Generator 3 | // Copyright 2014 by Glen Akins. 4 | // All rights reserved. 5 | // 6 | // Set editor width to 96 and tab stop to 4. 7 | // 8 | // This program is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // This program is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with this program. If not, see . 20 | //============================================================================================= 21 | 22 | #ifndef __globals_h_ 23 | #define __globals_h_ 24 | 25 | #define DISPLAY_WIDTH 512 26 | #define DISPLAY_HEIGHT 64 27 | 28 | extern uint32_t gLevels[DISPLAY_HEIGHT][DISPLAY_WIDTH]; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/perlin/knobs/knobs.ino: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Teensy 2.0 firmware to read four analog knobs and print 3 | * their values periodically. 4 | */ 5 | 6 | #define LED_PIN 11 7 | 8 | void setup(void) 9 | { 10 | Serial.begin(9600); 11 | pinMode(LED_PIN, OUTPUT); 12 | } 13 | 14 | void loop(void) 15 | { 16 | digitalWrite(LED_PIN, HIGH); 17 | uint16_t p1 = analogRead(A3); 18 | uint16_t p2 = analogRead(A2); 19 | uint16_t p3 = analogRead(A5); 20 | uint16_t p4 = analogRead(A4); 21 | digitalWrite(LED_PIN, LOW); 22 | 23 | Serial.print(p1); 24 | Serial.print(' '); 25 | Serial.print(p2); 26 | Serial.print(' '); 27 | Serial.print(p3); 28 | Serial.print(' '); 29 | Serial.println(p4); 30 | 31 | delay(100); 32 | } 33 | -------------------------------------------------------------------------------- /src/perlin/pattern.cpp: -------------------------------------------------------------------------------- 1 | //============================================================================================= 2 | // LED Matrix Animated Pattern Generator 3 | // Copyright 2014 by Glen Akins. 4 | // All rights reserved. 5 | // 6 | // Set editor width to 96 and tab stop to 4. 7 | // 8 | // This program is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // This program is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with this program. If not, see . 20 | //============================================================================================= 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "globals.h" 27 | #include "gammalut.h" 28 | #include "pattern.h" 29 | 30 | #define MAKE_COLOR(r,g,b) \ 31 | (((r) << 16) | ((g) << 8) | ((b) << 0)) 32 | 33 | //--------------------------------------------------------------------------------------------- 34 | // convert a hue from 0 to 95 to its 12-bit RGB color 35 | // 36 | // hue: 0 = red, 32 = blue, 64 = green 37 | // 38 | 39 | uint32_t Pattern::translateHue (int32_t hue) 40 | { 41 | uint8_t hi, lo; 42 | uint32_t r, g, b; 43 | 44 | hi = hue >> 4; 45 | lo = ((hue & 0xf) << 4) | (hue & 0xf); 46 | 47 | #if 1 48 | switch (hi) { 49 | case 0: r = 0xff; g = 0; b = lo; break; 50 | case 1: r = 0xff-lo, g = 0, b = 0xff; break; 51 | case 2: r = 0, g = lo, b = 0xff; break; 52 | case 3: r = 0, g = 0xff, b = 0xff-lo; break; 53 | case 4: r = lo, g = 0xff, b = 0; break; 54 | case 5: r = 0xff, g = 0xff-lo, b = 0; break; 55 | } 56 | //printf("hue=%d=%d,%d: %d,%d,%d\n", hue, hi, lo, r, g, b); 57 | //r = gammaLut[r] << 3; 58 | //g = gammaLut[g] << 3; 59 | //b = gammaLut[b] << 3; 60 | r /= 2; 61 | b /= 2; 62 | g /= 2; 63 | #else 64 | b = hue; 65 | r = 0; 66 | g = 0; 67 | #endif 68 | 69 | 70 | uint32_t color = MAKE_COLOR (r,g,b); 71 | //printf("color=%08x\n", color); 72 | return color; 73 | } 74 | 75 | 76 | //--------------------------------------------------------------------------------------------- 77 | // convert a hue from 0 to 95 and a brightnewss from 0 to 1.0 to its 12-bit RGB color 78 | // 79 | // hue: 0 = red, 32 = blue, 64 = green 80 | // value: 0 = off, 1.0 = 100% 81 | // 82 | 83 | uint32_t Pattern::translateHueValue (int32_t hue, float value) 84 | { 85 | uint8_t hi, lo; 86 | uint8_t r, g, b; 87 | 88 | hi = hue >> 4; 89 | lo = ((hue & 0xf) << 4) | (hue & 0xf); 90 | 91 | switch (hi) { 92 | case 0: r = 0xff; g = 0; b = lo; break; 93 | case 1: r = 0xff-lo, g = 0, b = 0xff; break; 94 | case 2: r = 0, g = lo, b = 0xff; break; 95 | case 3: r = 0, g = 0xff, b = 0xff-lo; break; 96 | case 4: r = lo, g = 0xff, b = 0; break; 97 | case 5: r = 0xff, g = 0xff-lo, b = 0; break; 98 | } 99 | 100 | r = ((float)r + 0.5) * value; 101 | g = ((float)g + 0.5) * value; 102 | b = ((float)b + 0.5) * value; 103 | 104 | //r = gammaLut[r] << 4; 105 | //g = gammaLut[g] << 4; 106 | //b = gammaLut[b] << 4; 107 | 108 | return MAKE_COLOR (r,g,b); 109 | } 110 | -------------------------------------------------------------------------------- /src/perlin/pattern.h: -------------------------------------------------------------------------------- 1 | //============================================================================================= 2 | // LED Matrix Animated Pattern Generator 3 | // Copyright 2014 by Glen Akins. 4 | // All rights reserved. 5 | // 6 | // Set editor width to 96 and tab stop to 4. 7 | // 8 | // This program is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // This program is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with this program. If not, see . 20 | //============================================================================================= 21 | 22 | #ifndef __pattern_h_ 23 | #define __pattern_h_ 24 | 25 | extern const uint8_t gammaLut[]; 26 | 27 | class Pattern 28 | { 29 | public: 30 | 31 | // constructor 32 | Pattern (const int32_t width, const int32_t height) : 33 | m_width(width), m_height(height) { } 34 | 35 | // destructor 36 | ~Pattern (void) { } 37 | 38 | // reset to first frame in animation 39 | virtual void init (void) = 0; 40 | 41 | // calculate next frame in the animation 42 | virtual bool next (void) = 0; 43 | 44 | // get width and height 45 | void getDimensions (int32_t &width, int32_t &height) { 46 | width = m_width; height = m_height; 47 | } 48 | 49 | uint32_t translateHue (int32_t hue); 50 | uint32_t translateHueValue (int32_t hue, float value); 51 | 52 | protected: 53 | const int32_t m_width; 54 | const int32_t m_height; 55 | 56 | private: 57 | }; 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /src/perlin/perlin-ledscape.cpp: -------------------------------------------------------------------------------- 1 | //============================================================================================= 2 | // LED Matrix Animated Pattern Generator 3 | // Copyright 2014 by Glen Akins. 4 | // Updated to work with LEDscape by Trammell Hudson 5 | // All rights reserved. 6 | // 7 | // Set editor width to 96 and tab stop to 4. 8 | // 9 | // This program is free software: you can redistribute it and/or modify 10 | // it under the terms of the GNU General Public License as published by 11 | // the Free Software Foundation, either version 3 of the License, or 12 | // (at your option) any later version. 13 | // 14 | // This program is distributed in the hope that it will be useful, 15 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | // GNU General Public License for more details. 18 | // 19 | // You should have received a copy of the GNU General Public License 20 | // along with this program. If not, see . 21 | //============================================================================================= 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | using namespace std; 41 | 42 | #include "globals.h" 43 | #include "pattern.h" 44 | #include "pf2.h" 45 | #include "ledscape.h" 46 | 47 | static ledscape_t * leds; 48 | 49 | // global levels to write to FPGA 50 | uint32_t gLevels[DISPLAY_HEIGHT][DISPLAY_WIDTH]; 51 | 52 | // global object to create animated pattern 53 | Perlin *gPattern = NULL; 54 | 55 | // prototypes 56 | void Quit (int sig); 57 | void BlankDisplay (void); 58 | void WriteLevels (void); 59 | void timer_handler (int signum); 60 | 61 | 62 | static void * 63 | read_thread( 64 | void * arg 65 | ) 66 | { 67 | char line[128]; 68 | float new_size; 69 | printf("read thread waiting\n"); 70 | int old_p0 = -1; 71 | int old_p1 = -1; 72 | int old_p2 = -1; 73 | int old_p3 = -1; 74 | 75 | while (1) 76 | { 77 | if (fgets(line, sizeof(line), stdin) == NULL) 78 | break; 79 | //printf("read '%s'\n", line); 80 | int p0, p1, p2, p3; 81 | int rc = sscanf(line, "%d %d %d %d", &p0, p1, p2, p3); 82 | if (rc != 4) 83 | { 84 | printf("scanf failed: %d: '%s'\n", rc, line); 85 | continue; 86 | } 87 | 88 | if (p0 != old_p0) 89 | gPattern->setScale(p0 / 1024.0); 90 | old_p0 = p0; 91 | } 92 | 93 | fprintf(stderr, "read thread exiting\n"); 94 | return NULL; 95 | } 96 | 97 | int main (int argc, char *argv[]) 98 | { 99 | struct sigaction sa; 100 | struct itimerval timer; 101 | 102 | // trap ctrl-c to call quit function 103 | signal (SIGINT, Quit); 104 | 105 | ledscape_config_t * config = &ledscape_matrix_default; 106 | if (argc > 1) 107 | { 108 | config = ledscape_config(argv[1]); 109 | if (!config) 110 | return EXIT_FAILURE; 111 | } 112 | 113 | float hue_options = 0.005; 114 | if (argc > 2) 115 | hue_options = atof(argv[2]); 116 | 117 | config->matrix_config.width = DISPLAY_WIDTH; 118 | config->matrix_config.height = DISPLAY_HEIGHT; 119 | leds = ledscape_init(config, 0); 120 | 121 | ledscape_printf((uint32_t*)(uintptr_t)gLevels, DISPLAY_WIDTH, 0xFF0000, "Perlin noise by Glen Akins"); 122 | ledscape_draw(leds, gLevels); 123 | 124 | // initialize levels to all off 125 | BlankDisplay (); 126 | 127 | // create a new pattern object -- perlin noise, mode 2 long repeating 128 | gPattern = new Perlin( 129 | DISPLAY_WIDTH, 130 | DISPLAY_HEIGHT, 131 | 2, // mode 132 | 1.0/64.0, // size of blobs: smaller value == larger blob 133 | 1/64.0, // speed 134 | 256.0, 135 | hue_options 136 | ); 137 | 138 | // spin off a thread to read size, speed and other settings 139 | pthread_t read_id; 140 | if (pthread_create(&read_id, NULL, read_thread, NULL) < 0) 141 | return EXIT_FAILURE; 142 | 143 | // create a new pattern object -- perlin noise, mode 1 short repeat 144 | // gPattern = new Perlin (DISPLAY_WIDTH, DISPLAY_HEIGHT, 1, 8.0/64.0, 0.0125, 1.0, 0.2); 145 | 146 | // reset to first frame 147 | gPattern->init (); 148 | 149 | while (1) { 150 | WriteLevels (); 151 | 152 | // calculate next frame in animation 153 | gPattern->next (); 154 | usleep(1000); 155 | } 156 | 157 | // delete pattern object 158 | delete gPattern; 159 | 160 | return 0; 161 | } 162 | 163 | 164 | void Quit (int sig) 165 | { 166 | exit (-1); 167 | } 168 | 169 | 170 | void BlankDisplay (void) 171 | { 172 | // initialize levels to all off 173 | for (int32_t row = 0; row < DISPLAY_HEIGHT; row++) { 174 | for (int32_t col = 0; col < DISPLAY_WIDTH; col++) { 175 | gLevels[row][col] = 0x0000; 176 | } 177 | } 178 | 179 | // send levels to board 180 | WriteLevels (); 181 | } 182 | 183 | 184 | void WriteLevels (void) 185 | { 186 | ledscape_draw(leds, gLevels); 187 | } 188 | 189 | -------------------------------------------------------------------------------- /src/perlin/pf2.h: -------------------------------------------------------------------------------- 1 | //============================================================================================= 2 | // LED Matrix Animated Pattern Generator 3 | // Copyright 2014 by Glen Akins. 4 | // All rights reserved. 5 | // 6 | // Set editor width to 96 and tab stop to 4. 7 | // 8 | // This program is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // This program is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with this program. If not, see . 20 | // 21 | // Major inspiration for the use of Perlin noise to generate pseudorandom RGB patterns comes 22 | // from the TI RGB LED coffee table project and the following resources: 23 | // 24 | // TI RGB LED Coffee Table: 25 | // 26 | // http://e2e.ti.com/group/microcontrollerprojects/m/msp430microcontrollerprojects/447779.aspx 27 | // https://github.com/bear24rw/rgb_table/tree/master/code/table_drivers/pytable 28 | // 29 | // Casey Duncan's Python C Noise Library: 30 | // 31 | // https://github.com/caseman/noise 32 | // 33 | // Ken Perlin's Original Source Code: 34 | // 35 | // http://www.mrl.nyu.edu/~perlin/doc/oscar.html 36 | // 37 | // Excellent explanation of Perlin noise and seamless looping and tiling here: 38 | // 39 | // http://webstaff.itn.liu.se/~stegu/TNM022-2005/perlinnoiselinks/perlin-noise-math-faq.html 40 | // 41 | //============================================================================================= 42 | 43 | #ifndef __pf2_h_ 44 | #define __pf2_h_ 45 | 46 | class Perlin : public Pattern 47 | { 48 | public: 49 | 50 | // constructor 51 | Perlin (const int32_t width, const int32_t height, int32_t mode); 52 | 53 | // constructor 54 | // m_hue_option is hue offset from 0.0 to 1.0 for mode 1, hue step for modes 2 and 3 55 | Perlin (const int32_t width, const int32_t height, 56 | const int32_t mode, const float xy_scale, 57 | const float z_step, const float z_depth, const float hue_options); 58 | 59 | // destructor 60 | ~Perlin (void); 61 | 62 | // reset to first frame in animation 63 | void init (void); 64 | 65 | // calculate next frame in the animation 66 | bool next (void); 67 | 68 | // get / set scale 69 | float getScale (void) { 70 | return m_xy_scale / 256.0; 71 | } 72 | void setScale (float xy_scale) { 73 | m_xy_scale = xy_scale * 256.0; 74 | } 75 | 76 | // get / set z step 77 | float getZStep (void) { 78 | return m_z_step; 79 | } 80 | void setZStep (float z_step) { 81 | m_z_step = z_step; 82 | } 83 | 84 | // get / set z depth 85 | float getZDepth (void) { 86 | return m_z_depth; 87 | } 88 | void setZDepth (float z_depth) { 89 | m_z_depth = z_depth; 90 | m_z_state = 0; 91 | } 92 | 93 | // get / set hue options 94 | float getHueOptions (void) { 95 | return m_hue_options; 96 | } 97 | void setHueOptions (float hue_options) { 98 | m_hue_options = hue_options; 99 | } 100 | 101 | private: 102 | 103 | // 3d perlin noise function 104 | int32_t noise (uint16_t x, uint16_t y, uint16_t z); 105 | 106 | // mode: 107 | // 1 = fixed background hue 108 | // 2 = hue rotates and varies with noise 109 | // 3 = hue rotates, noise varies brightness 110 | const int32_t m_mode; 111 | 112 | // x and y scale of noise 113 | int32_t m_xy_scale; 114 | 115 | // step in the z direction between displayed x-y planes 116 | float m_z_step; 117 | 118 | // depth in the z direction before the pattern repeats 119 | float m_z_depth; 120 | 121 | // background hue for mode 1, from 0.0 to 1.0 122 | // hue step size for modes 2 and 3 123 | float m_hue_options; 124 | 125 | // current z coordinate, mod z depth 126 | float m_z_state; 127 | 128 | // current hue, mod 1.0 129 | float m_hue_state; 130 | 131 | // current minimum and maximum noise values for normalization 132 | float m_min, m_max; 133 | }; 134 | 135 | #endif 136 | -------------------------------------------------------------------------------- /src/script/Makefile: -------------------------------------------------------------------------------- 1 | SCRIPTDIR = ../src/script/ 2 | BINDIR = ../../bin/ 3 | 4 | scripts = bbb-network-setup \ 5 | find-serial \ 6 | install \ 7 | ledscape.service \ 8 | python-test \ 9 | run-ledscape \ 10 | run-videoplayer \ 11 | twitter-scroller \ 12 | videoplayer \ 13 | videoplayer.service 14 | 15 | all: $(scripts) 16 | 17 | .PHONY: $(scripts) 18 | 19 | $(scripts): 20 | if test ! -e $(BINDIR)$@; \ 21 | then ln -s $(SCRIPTDIR)$@ $(BINDIR); \ 22 | fi 23 | -------------------------------------------------------------------------------- /src/script/bbb-network-setup: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | DIR="`dirname $0`" 3 | cd "$DIR" 4 | 5 | route add default gw 192.168.7.1 6 | echo 'nameserver 8.8.8.8' > /etc/resolv.conf 7 | echo >&2 "setting date" 8 | ntpdate -s pool.ntp.org 9 | date 10 | 11 | echo >&2 "probing serial ports" 12 | ./find-serial 13 | -------------------------------------------------------------------------------- /src/script/find-serial: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Locate the serial ports and their serial numbers. 3 | # This can then be used to map devices to positions in the matrix. 4 | 5 | lsusb -v > /dev/null 6 | 7 | for dev in `find /sys/devices/ -name '*ACM*' `;do 8 | DIR=`dirname $dev` 9 | TTY=`basename $dev` 10 | SERIAL=`cat $DIR/../../serial` 11 | echo "/dev/$TTY: $SERIAL" 12 | done 13 | -------------------------------------------------------------------------------- /src/script/install: -------------------------------------------------------------------------------- 1 | #!/bin/sh -x 2 | # 3 | # Install the device tree fragment, the angstrom service file, 4 | # and make it runable. 5 | # 6 | 7 | DIRNAME="`dirname $0`" 8 | cd "$DIRNAME/.." 9 | 10 | die() { 11 | echo >&2 "$*" 12 | exit 1 13 | } 14 | 15 | cp dts/CAPE-BONE-OCTO-00A0.dtbo /lib/firmware 16 | 17 | if [ ! -d /etc/systemd ]; then 18 | die "/etc/systemd does not exist? Is this not angstrom?" 19 | fi 20 | 21 | BOOT=/media/BEAGLEBONE 22 | UENV="$BOOT/uEnv.txt" 23 | 24 | if ! mount | grep -q "$BOOT"; then 25 | mkdir "$BOOT" 26 | mount -t vfat /dev/mmcblk0p1 "$BOOT" || die "Failed to mount $BOOT" 27 | fi 28 | 29 | cp "$UENV" "$UENV.backup" 30 | 31 | cat < "$UENV" 32 | optargs=quiet capemgr.disable_partno=BB-BONELT-HDMI,BB-BONELT-HDMIN 33 | END 34 | 35 | sync 36 | umount "$BOOT" 37 | 38 | cp bin/ledscape.service /etc/systemd/system/ 39 | systemctl enable ledscape.service 40 | systemctl status ledscape.service 41 | 42 | -------------------------------------------------------------------------------- /src/script/ledscape.service: -------------------------------------------------------------------------------- 1 | # 2 | # Copy this file to /etc/systemd/system/ and run: 3 | # systemctl enable ledscape.service 4 | # to enable LEDscape to start on each boot. 5 | # 6 | 7 | [Unit] 8 | Description=BeagleBone LED matrix driver 9 | 10 | [Service] 11 | WorkingDirectory=/opt/LEDscape/ 12 | ExecStart=/opt/LEDscape/bin/run-ledscape 13 | KillMode=process 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /src/script/python-test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Draw images with PIL and send them to the display. 3 | # Dual scrolling example with fixed time on each side and 4 | # the date scrolling around. 5 | # 6 | import Image, ImageFont, ImageDraw 7 | import socket 8 | import time, datetime 9 | from colorsys import hsv_to_rgb 10 | 11 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 12 | dest = ("localhost", 9999) 13 | 14 | #print im.format, im.size, im.mode 15 | # use a truetype font 16 | font = ImageFont.truetype("fonts/spincycle.ttf", 32) 17 | font_sm = ImageFont.truetype("fonts/spincycle.ttf", 24) 18 | #font_sm = ImageFont.truetype("fonts/pf_tempesta_seven.ttf", 8) 19 | logo = Image.open("fonts/nycr.png") 20 | #logo.resize((64,64)) 21 | 22 | i = 0 23 | width = 512 24 | height = 64 25 | disp = Image.new("RGB", (width,height), "black") 26 | im = Image.new("RGB", (width,height), "black") 27 | im_draw = ImageDraw.Draw(im) 28 | disp_draw = ImageDraw.Draw(disp) 29 | 30 | def rainbow(i): 31 | rgb = [int(x*256) for x in hsv_to_rgb(i/256.0,0.8,0.8)] 32 | return (rgb[0],rgb[1],rgb[2]) 33 | 34 | def internet_time(): 35 | "Swatch Internet Time. Biel meridian." 36 | "More granular Swatch time. Courtesy https://github.com/gcohen55/pebble-beapoch" 37 | return (((time.time() + 3600) % 86400) * 1000) / 86400 38 | #h, m, s = time.gmtime()[3:6] 39 | #h += 1 # Biel time zone: UTC+1 40 | #seconds = s + (60.0*m) + (60.0*60.0*h) 41 | #beats = seconds * 1000.0 / (60.0*60.0*24.0) 42 | #beats = beats % 1000.0 43 | #return beats 44 | 45 | 46 | 47 | while True: 48 | im.paste("black", (0,0,width,height)) 49 | now = datetime.datetime.now() 50 | d = now.strftime("%a %d %b") 51 | t1 = now.strftime("%H:%M:%S") 52 | t2 = "%07.3f" % (internet_time()) 53 | 54 | # Draw the date 55 | im_draw.text((60, 4), d, font=font, fill=rainbow(i)) 56 | im_draw.text((90, 32), t1, font=font_sm, fill=rainbow(i)) 57 | im_draw.text((256+60, 4), d, font=font, fill=rainbow(i)) 58 | im_draw.text((256+80, 32), t2, font=font_sm, fill=rainbow(i)) 59 | im_draw.text((256+180, 32), ".beats", font=font_sm, fill=rainbow(i)) 60 | 61 | im.paste(logo, (0,8)) 62 | im.paste(logo, (256,8)) 63 | 64 | # Make it scroll 65 | disp.paste(im.crop((0,0,i,height)), (width-i,0)) 66 | disp.paste(im.crop((i+1,0,width-1,height)), (0,0)) 67 | 68 | # draw the time on each face 69 | #for x in range(0,7): 70 | #disp_draw.text((4+x*32, 8-3), t, font=font_sm) 71 | 72 | # Split it into two pieces and send it to the drawing server 73 | s = disp.tostring() 74 | s1 = s[:(width*height*3/2)] 75 | s2 = s[(width*height*3/2):] 76 | sock.sendto(chr(0) + s1, dest) 77 | sock.sendto(chr(1) + s2, dest) 78 | i = (i+1) % width 79 | time.sleep(0.025) 80 | -------------------------------------------------------------------------------- /src/script/run-ledscape: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DIRNAME="`dirname "$0"`" 3 | cd "$DIRNAME/.." 4 | 5 | # Enable the octoscroller overlay 6 | # Note that we can't do this at boot because it won't load then, for some reason 7 | echo CAPE-BONE-OCTO > /sys/devices/bone_capemgr*/slots 8 | 9 | # Wait for unknown and unimaginable things to happen 10 | sleep 10 11 | 12 | CONFIGFILE="$DIRNAME/strips.config" 13 | USBMOUNT="/media/usb0" 14 | 15 | # If a USB stick is mounted, try to load the config file from it 16 | if [ -e "$USBMOUNT/ledscape.config" ] 17 | then 18 | CONFIGFILE="$USBMOUNT/ledscape.config" 19 | fi 20 | 21 | #exec ./bin/matrix-test "$CONFIGFILE" >/dev/null 22 | #exec ./bin/matrix-udp-rx -m "Starting LEDscape" -c "$CONFIGFILE" >/dev/null 23 | exec ./bin/matrix-udp-rx -W 120 -H 32 -c "$CONFIGFILE" >/dev/null 24 | -------------------------------------------------------------------------------- /src/script/run-videoplayer: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DIRNAME="`dirname "$0"`" 3 | cd "$DIRNAME" 4 | 5 | USBMOUNT="/media/usb0" 6 | 7 | # Wait for a USB drive to be inserted 8 | while true 9 | do 10 | grep -qs $USBMOUNT /proc/mounts 11 | if [ $? -eq 0 ]; then 12 | echo "Mount success!" 13 | break 14 | fi 15 | 16 | sleep 1 17 | done 18 | 19 | 20 | # Turn on extended filename globbing 21 | shopt -s extglob 22 | 23 | # Play the first video we find, on a loop. 24 | for video in "$USBMOUNT/"*+(.mp4|.MP4|.avi|.AVI) 25 | do 26 | exec ./videoplayer --screenGeometry 120x32 -w 120 -l "$video" 27 | break 28 | done 29 | -------------------------------------------------------------------------------- /src/script/twitter-scroller: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import Image, ImageFont, ImageDraw 3 | import socket 4 | import time, datetime 5 | import pickle 6 | import tweepy 7 | import time 8 | import thread 9 | from botomatic import TBot 10 | from colorsys import hsv_to_rgb 11 | 12 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 13 | dest = ("localhost", 9999) 14 | 15 | #print im.format, im.size, im.mode 16 | # use a truetype font 17 | font = ImageFont.truetype("fonts/spincycle.ttf", 32) 18 | font_sm = ImageFont.truetype("fonts/spincycle.ttf", 24) 19 | 20 | i = 0 21 | width = 512 22 | height = 64 23 | disp = Image.new("RGB", (width,height), "black") 24 | im = Image.new("RGB", (width,height), "black") 25 | im_draw = ImageDraw.Draw(im) 26 | disp_draw = ImageDraw.Draw(disp) 27 | 28 | tweets = [] 29 | seen = {} 30 | 31 | 32 | class Searcher(TBot): 33 | debug_mode = True 34 | 35 | def __init__(self): 36 | handle = "searcher" 37 | super(Searcher, self).__init__(handle) 38 | 39 | def run(self): 40 | while True: 41 | results = self.search('"I"') 42 | for result in results: 43 | if result.id in seen: 44 | continue 45 | tweets.append(str(result.text)) 46 | seen[result.id] = 1 47 | 48 | #print "-----\n" + str(result.text) + "\n" 49 | #try: 50 | #result.retweet() 51 | #except tweepy.error.TweepError: # private status update? 52 | #continue 53 | time.sleep(10) 54 | 55 | 56 | self.wrap_up() 57 | 58 | def start_bot(): 59 | p = Searcher() 60 | 61 | thread.start_new_thread(start_bot, ()) 62 | 63 | 64 | while True: 65 | if len(tweets) == 0: 66 | time.sleep(1) 67 | continue 68 | tweet = tweets.pop() 69 | print "---\n" + tweet + "\n" 70 | continue 71 | 72 | im.paste("black", (0,0,width,height)) 73 | now = datetime.datetime.now() 74 | d = now.strftime("%a %d %b") 75 | t = now.strftime("%H:%M:%S") 76 | 77 | # Draw the date 78 | im_draw.text((60, 4), d, font=font, fill=rainbow(i)) 79 | im_draw.text((90, 32), t, font=font_sm, fill=rainbow(i)) 80 | im_draw.text((256+60, 4), d, font=font, fill=rainbow(i)) 81 | im_draw.text((256+90, 32), t, font=font_sm, fill=rainbow(i)) 82 | 83 | im.paste(logo, (0,8)) 84 | im.paste(logo, (256,8)) 85 | 86 | # Make it scroll 87 | disp.paste(im.crop((0,0,i,height)), (width-i,0)) 88 | disp.paste(im.crop((i+1,0,width-1,height)), (0,0)) 89 | 90 | # draw the time on each face 91 | #for x in range(0,7): 92 | #disp_draw.text((4+x*32, 8-3), t, font=font_sm) 93 | 94 | # Split it into two pieces and send it to the drawing server 95 | s = disp.tostring() 96 | s1 = s[:(width*height*3/2)] 97 | s2 = s[(width*height*3/2):] 98 | sock.sendto(chr(0) + s1, dest) 99 | sock.sendto(chr(1) + s2, dest) 100 | i = (i+1) % width 101 | time.sleep(0.025) 102 | -------------------------------------------------------------------------------- /src/script/videoplayer: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Play a video onto the screen 3 | 4 | import cv2, cv 5 | import socket 6 | import numpy 7 | import datetime 8 | import time 9 | import argparse 10 | 11 | parser = argparse.ArgumentParser( 12 | description="Display a video on the LEDscape screen" 13 | ) 14 | parser.add_argument( 15 | "-s", "--screenGeometry", 16 | dest="screenGeometry", 17 | help="LEDscape screen size (ex: 256x128)", 18 | default="256x128", 19 | ) 20 | parser.add_argument( 21 | "-w", "--scaleWidth", 22 | dest="scaleWidth", 23 | help="Width to scale video to during playback (ex: 256)", 24 | default="256", 25 | type=int, 26 | ) 27 | parser.add_argument( 28 | "-a", "--address", 29 | dest="address", 30 | help="TCP address to connect to (ex: localhost)", 31 | default="localhost", 32 | ) 33 | parser.add_argument( 34 | "-p", "--port", 35 | dest="port", 36 | help="TCP port to send data to (ex: 9999)", 37 | default=9999, 38 | type=int, 39 | ) 40 | parser.add_argument( 41 | "-l", "--loop", 42 | action="store_true", 43 | dest="loop", 44 | help="Loop the video continuously", 45 | default=False, 46 | ) 47 | parser.add_argument( 48 | dest="filename", 49 | help="Filename to load", 50 | ) 51 | 52 | config = parser.parse_args() 53 | 54 | # LEDscape screen geometry 55 | screenWidth = int(config.screenGeometry.split('x')[0]) 56 | screenHeight = int(config.screenGeometry.split('x')[1]) 57 | 58 | # If the screen height is not a divisor of 2, then the subframe scheme won't work. 59 | if screenHeight % 2: 60 | print "Error, screen height must be a multiple of 2!" 61 | exit(1) 62 | 63 | # LEDscape packet geometry 64 | # Note: SubframeCount *must* be an integer divider of screenHeight!! 65 | # Note: This is designed for large screen support, a la the megasidescroller 66 | # Small screens only need one frame to transmit, so set subFrameCount to 1. 67 | subFrameCount = 2 68 | subFrameHeight = screenHeight / subFrameCount 69 | subFrameSize = 1 + subFrameHeight*screenWidth*3 70 | 71 | # LEDscape message setup 72 | message = numpy.zeros(subFrameSize*subFrameCount, numpy.uint8); 73 | for subFrame in range(0, subFrameCount): 74 | message[subFrame*subFrameSize] = subFrame 75 | 76 | # Socket to send to 77 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 78 | sock.setsockopt(socket.SOL_SOCKET,socket.SO_SNDBUF,int(subFrameSize)) 79 | 80 | # Test if the frame size is acceptable 81 | # TODO: Fix this 82 | if sock.getsockopt(socket.SOL_SOCKET,socket.SO_SNDBUF) < subFrameSize: 83 | print "Error configuring TCP socket: buffer too big (reduce LEDscape image size?)" 84 | exit(1) 85 | 86 | # Open the video for playback 87 | cap = cv2.VideoCapture(config.filename) 88 | 89 | 90 | # Test that the video was loaded 91 | if not cap.isOpened(): 92 | print "Error opening video: Check that the file exists and the format is correct" 93 | exit(1) 94 | 95 | fps = cap.get(cv.CV_CAP_PROP_FPS) 96 | frameDelay = 1.0/fps 97 | 98 | nextTime = time.time() + frameDelay 99 | 100 | while cap.isOpened(): 101 | # Get the video frame 102 | ret, frame = cap.read() 103 | 104 | # If we've reached the end, reset the position to the beginning 105 | if not ret: 106 | if config.loop: 107 | cap.set(cv.CV_CAP_PROP_POS_MSEC, 0) 108 | ret, frame = cap.read() 109 | else: 110 | exit(0) 111 | 112 | # Resize the video to be the width that we actually want 113 | originalHeight = frame.shape[0] 114 | originalWidth = frame.shape[1] 115 | originalAspect = float(originalWidth)/originalHeight 116 | 117 | scaleWidth = config.scaleWidth 118 | scaleHeight = int(scaleWidth/originalAspect) 119 | 120 | smaller = cv2.resize(frame,(scaleWidth, scaleHeight)) 121 | frame = smaller 122 | 123 | # Copy the image data into the LEDscape format 124 | frameHeight = frame.shape[0] 125 | frameWidth = frame.shape[1] 126 | 127 | flattenedFrame = frame.reshape(frameHeight, frameWidth*3) 128 | 129 | copyWidth = min(screenWidth, frameWidth) 130 | copyHeight = min(screenHeight, frameHeight) 131 | 132 | copyLength = copyWidth*3 133 | 134 | for row in range(0, copyHeight): 135 | offset = 1 + (row / subFrameHeight) 136 | messageOffset = (row*screenWidth)*3 + offset 137 | 138 | message[messageOffset:messageOffset+copyLength] = flattenedFrame[row, 0:copyLength] 139 | 140 | # Send the data to the LEDscape host 141 | for subFrame in range(0, subFrameCount): 142 | sock.sendto(message[subFrame*subFrameSize:(subFrame+1)*subFrameSize], (config.address, config.port)) 143 | 144 | # Delay until it's time to show the next frame. 145 | while time.time() < nextTime: 146 | pass 147 | 148 | nextTime += frameDelay 149 | -------------------------------------------------------------------------------- /src/script/videoplayer.service: -------------------------------------------------------------------------------- 1 | # 2 | # Copy this file to /etc/systemd/system/ and run: 3 | # systemctl enable videoplayer.service 4 | # to enable videoplayer to start on each boot. 5 | # 6 | 7 | [Unit] 8 | Description=LEDscape video player 9 | Requires=ledscape 10 | After=ledscape 11 | 12 | [Service] 13 | WorkingDirectory=/opt/LEDscape/bin 14 | ExecStart=/opt/LEDscape/bin/run-videoplayer 15 | KillMode=process 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /strips.config: -------------------------------------------------------------------------------- 1 | ws2812 2 | 240,32 3 | -------------------------------------------------------------------------------- /tall-cylinder.config: -------------------------------------------------------------------------------- 1 | matrix16 2 | 0,0 L 0,0 3 | 0,1 R 16,0 4 | 0,2 L 32,0 5 | 0,3 R 48,0 6 | 0,4 L 64,0 7 | 0,5 R 80,0 8 | 0,6 L 96,0 9 | 0,7 R 112,0 10 | 3,0 L 128,0 11 | 3,1 R 144,0 12 | 3,2 L 160,0 13 | 3,3 R 176,0 14 | 3,4 L 192,0 15 | 3,5 R 208,0 16 | 3,6 L 224,0 17 | 3,7 R 240,0 18 | 2,7 L 256,0 19 | 2,6 R 272,0 20 | 2,5 L 288,0 21 | 2,4 R 304,0 22 | 2,3 L 320,0 23 | 2,2 R 336,0 24 | 2,1 L 352,0 25 | 2,0 R 368,0 26 | 1,7 L 384,0 27 | 1,6 R 400,0 28 | 1,5 L 416,0 29 | 1,4 R 432,0 30 | 1,3 L 448,0 31 | 1,2 R 464,0 32 | 1,1 L 480,0 33 | 1,0 R 496,0 34 | 4,0 L 0,32 35 | 4,1 R 16,32 36 | 4,2 L 32,32 37 | 4,3 R 48,32 38 | 4,4 L 64,32 39 | 4,5 R 80,32 40 | 4,6 L 96,32 41 | 4,7 R 112,32 42 | 7,0 L 128,32 43 | 7,1 R 144,32 44 | 7,2 L 160,32 45 | 7,3 R 176,32 46 | 7,4 L 192,32 47 | 7,5 R 208,32 48 | 7,6 L 224,32 49 | 7,7 R 240,32 50 | 6,7 L 256,32 51 | 6,6 R 272,32 52 | 6,5 L 288,32 53 | 6,4 R 304,32 54 | 6,3 L 320,32 55 | 6,2 R 336,32 56 | 6,1 L 352,32 57 | 6,0 R 368,32 58 | 5,7 L 384,32 59 | 5,6 R 400,32 60 | 5,5 L 416,32 61 | 5,4 R 432,32 62 | 5,3 L 448,32 63 | 5,2 R 464,32 64 | 5,1 L 480,32 65 | 5,0 R 496,32 66 | -------------------------------------------------------------------------------- /ubuntu/ledscape.conf: -------------------------------------------------------------------------------- 1 | # 2 | # This task runs at boot to start the LEDscape service 3 | 4 | description "start LEDscape udp-rx receiver" 5 | 6 | start on startup 7 | 8 | task 9 | exec /opt/LEDscape/bin/run-ledscape 10 | --------------------------------------------------------------------------------