├── .gitignore ├── dtbos ├── Makefile └── bone_pru0_out-00A0.dts ├── Makefile ├── enable_pru0_outputs.sh ├── patches └── Remove-needless-printfs.patch ├── install_pruss.sh ├── README.md ├── main.cpp ├── include ├── pruPWM.h ├── pru.h └── pru.hp └── src ├── pruPWM.cpp ├── pwm.p └── pru.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | prupwm 2 | pwm.bin 3 | am335x_pru_package 4 | dtbos/*.dtbo 5 | src/*.o 6 | -------------------------------------------------------------------------------- /dtbos/Makefile: -------------------------------------------------------------------------------- 1 | all: bone_pru0_out-00A0.dtbo 2 | 3 | install: 4 | cp -v *.dtbo /lib/firmware 5 | 6 | clean: 7 | rm *.dtbo 8 | 9 | %.dtbo : %.dts 10 | dtc -O dtb -o $@ -b 0 -@ $< -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS=-Wall -I./include 2 | 3 | all: prupwm 4 | 5 | prupwm: main.cpp src/pruPWM.o src/pru.o include/pruPWM.h include/pru.h pwm.bin 6 | g++ $(CXXFLAGS) main.cpp src/pruPWM.o src/pru.o -lprussdrv -lpthread -o prupwm 7 | 8 | pwm.bin: src/pwm.p include/pru.hp 9 | cd src; pasm -b pwm.p; mv pwm.bin ../; cd .. 10 | 11 | clean: 12 | rm pwm.bin prupwm src/*.o -------------------------------------------------------------------------------- /enable_pru0_outputs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ $EUID -ne 0 ]]; then 4 | echo "This script must be run with sudo" 1>&2 5 | exit 1 6 | fi 7 | 8 | set -e 9 | 10 | # Build and install PRU DTBO 11 | echo "Installing PRU Device Tree Overlay" 12 | cd dtbos/ 13 | make 14 | make install 15 | 16 | # Enable PRU 17 | echo "Enabling PRU" 18 | echo bone_pru0_out > /sys/devices/bone_capemgr.*/slots 19 | 20 | exit 0 -------------------------------------------------------------------------------- /patches/Remove-needless-printfs.patch: -------------------------------------------------------------------------------- 1 | --- a/pru_sw/app_loader/interface/prussdrv.c 2013-08-20 10:00:44.812458775 +0000 2 | +++ b/pru_sw/app_loader/interface/prussdrv.c 2013-08-20 10:02:04.573912410 +0000 3 | @@ -98,7 +98,7 @@ 4 | switch (prussdrv.version) { 5 | case PRUSS_V1: 6 | { 7 | - printf("AM18XX\n"); 8 | + // printf("AM18XX\n"); 9 | prussdrv.pru0_dataram_phy_base = AM18XX_DATARAM0_PHYS_BASE; 10 | prussdrv.pru1_dataram_phy_base = AM18XX_DATARAM1_PHYS_BASE; 11 | prussdrv.intc_phy_base = AM18XX_INTC_PHYS_BASE; 12 | @@ -112,7 +112,7 @@ 13 | break; 14 | case PRUSS_V2: 15 | { 16 | - printf("AM33XX\n"); 17 | + // printf("AM33XX\n"); 18 | prussdrv.pru0_dataram_phy_base = AM33XX_DATARAM0_PHYS_BASE; 19 | prussdrv.pru1_dataram_phy_base = AM33XX_DATARAM1_PHYS_BASE; 20 | prussdrv.intc_phy_base = AM33XX_INTC_PHYS_BASE; 21 | @@ -652,7 +652,7 @@ 22 | if (fPtr == NULL) { 23 | printf("File %s open failed\n", filename); 24 | } else { 25 | - printf("File %s open passed\n", filename); 26 | + // printf("File %s open passed\n", filename); 27 | } 28 | // Read file size 29 | fseek(fPtr, 0, SEEK_END); 30 | -------------------------------------------------------------------------------- /dtbos/bone_pru0_out-00A0.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | /plugin/; 3 | 4 | / { 5 | compatible = "ti,beaglebone", "ti,beaglebone-black"; 6 | 7 | /* identification */ 8 | part-number = "pru0_outputs"; 9 | 10 | fragment@0 { 11 | target = <&am33xx_pinmux>; 12 | __overlay__ { 13 | pru0_output_pins: pru0_output_pins { 14 | pinctrl-single,pins = < 15 | 0x030 0x06 /* P8_12 to PRU output */ 16 | 0x034 0x06 /* P8_11 to PRU output */ 17 | 0x190 0x05 /* P9_31 to PRU output */ 18 | 0x194 0x05 /* P9_29 to PRU output */ 19 | 0x198 0x05 /* P9_30 to PRU output */ 20 | 0x19C 0x05 /* P9_28 to PRU output */ 21 | 0x1A0 0x05 /* P9_42 to PRU output */ 22 | 0x1A4 0x05 /* P9_27 to PRU output */ 23 | 0x1A8 0x05 /* P9_41 to PRU output */ 24 | 0x1AC 0x05 /* P9_25 to PRU output */ 25 | 0x1B4 0x20 /* CLKOUT2 to input as per datasheet (to enable P9_41) */ 26 | 0x164 0x20 /* GPIO0_7 to input as per datasheet (to enable P9_42) */ 27 | >; 28 | }; 29 | }; 30 | }; 31 | 32 | fragment@1 { 33 | target = <&pruss>; 34 | __overlay__ { 35 | status = "okay"; 36 | pinctrl-names = "default"; 37 | pinctrl-0 = <&pru0_output_pins>; 38 | }; 39 | }; 40 | }; 41 | 42 | -------------------------------------------------------------------------------- /install_pruss.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ $EUID -ne 0 ]]; then 4 | echo "This script must be run with sudo" 1>&2 5 | exit 1 6 | fi 7 | 8 | set -e 9 | 10 | # Clear anything that may already exist 11 | rm -rf am335x_pru_package 12 | 13 | # Load the PRU kernel driver 14 | echo "Loading PRU kernel driver (uio_pruss)" 15 | modprobe uio_pruss 16 | 17 | # Get the userspace driver 18 | echo "Grabbing userspace driver" 19 | git clone https://github.com/beagleboard/am335x_pru_package.git 20 | cd am335x_pru_package 21 | 22 | # Apply the patch to prevent interrupts being fired twice 23 | echo "Patching driver to prevent double interrupts" 24 | wget http://e2e.ti.com/cfs-file.ashx/__key/telligent-evolution-components-attachments/00-791-00-00-00-23-97-35/attachments.tar.gz 25 | tar -xzf attachments.tar.gz 26 | patch -p1 < 0001-Fix-for-duplicated-interrupts-when-interrupts-are-se.patch 27 | 28 | # Apply patch to remove needless printf statements 29 | patch -p1 < ../patches/Remove-needless-printfs.patch 30 | 31 | # Build driver as shared library 32 | echo "Building driver" 33 | cd pru_sw/app_loader/interface/ 34 | gcc -I. -Wall -I../include -c -fPIC -O3 -mtune=cortex-a8 -march=armv7-a -shared -o prussdrv.o prussdrv.c 35 | gcc -shared -o libprussdrv.so prussdrv.o 36 | 37 | # Install driver and headers 38 | echo "Installing driver" 39 | cp libprussdrv.so /usr/lib/ 40 | cp ../include/*.h /usr/include/ 41 | 42 | # Build the assembler 43 | echo "Building assembler" 44 | cd ../../utils/pasm_source 45 | ./linuxbuild 46 | 47 | # Install the assembler 48 | echo "Installing assembler" 49 | cp ../pasm /usr/bin/ 50 | 51 | echo "PRUSS successfully installed" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PWM servo control with the BeableBone Black PRUs 2 | ================================================ 3 | 4 | This project contains everything you need to get a simple C++ application controlling servos via with BBB PRUs. The reason for using the PRUs over the onboard PWMs is the acheivable resolution (~100ns with the PRU) and number of channels (24 across both PRUs, only 8 used by this project at present). 5 | 6 | This work is based on my [node.js module](https://github.com/omcaree/node-pru), but designed to work from C++ instead. 7 | 8 | Update 28th August 2013: 9 | 10 | * Modified PRU code and C++ class to implement a simple failsafe timeout. If host code hangs or crashes the PRU will revert the channels to predefined PWM values after a timeout has elapsed 11 | 12 | Prerequisites 13 | ------------- 14 | Make sure you have build-essential installed so that you can compile things 15 | 16 | sudo apt-get install build-essential 17 | 18 | PRU set up 19 | ----------- 20 | I've previous written up the PRU set up proceedure in my [node.js module](https://github.com/omcaree/node-pru), however I have now automated this process in an bash script. Firstly, get the source 21 | 22 | git clone https://github.com/omcaree/bbb-prupwm 23 | cd bbb-prupwm 24 | 25 | Now install the PRU userspace driver 26 | 27 | sudo ./install_pruss.sh 28 | 29 | In the dtbos/ directory I have created a device tree overlay which enables the PRU and muxes all the available output pins for PRU0 (P8_11, P8_12, P9_25, P9_27, P9_28, P9_29, P9_30, P9_31, P9_41, P9_42). To install and enable this overlay, run 30 | 31 | sudo ./enable_pru0_outputs.sh 32 | 33 | Run example 34 | ----------- 35 | Once the PRU is set up you can build and run the example code with 36 | 37 | make 38 | sudo ./prupwm 39 | 40 | This code defaults all outputs to 1500us at 50Hz, then cycles channel 0 (P9_31) and channel 7 (P9_25) indefinately. Pressintg Ctrl+C will return all channels to 1500us before exiting. -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2013 Owen McAree 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #include "pruPWM.h" 22 | 23 | // Entry point 24 | int main() { 25 | // Initialise PRU PWM 26 | PRUPWM *myPWM = new PRUPWM(); 27 | 28 | // Set Channel 0 failsafe value 29 | myPWM->setFailsafeValue(0, 1000000); 30 | 31 | // Set a 2s failsafe timeout 32 | myPWM->setFailsafeTimeout(2000); 33 | 34 | // Start the PRU 35 | myPWM->start(); 36 | 37 | // An example loop 38 | int pwm0 = 1000000, pwm7 = 2000000; 39 | int step = 100; 40 | while (true) { 41 | myPWM->setChannelValue(0,pwm0); 42 | myPWM->setChannelValue(7,pwm7); 43 | pwm0 += step; 44 | pwm7 -= step; 45 | if (pwm0 >= 2000000 || pwm0 <= 1000000 || pwm7 >= 2000000 || pwm7 <= 1000000) { 46 | step *= -1; 47 | usleep(1000000); 48 | } 49 | usleep(100); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /include/pruPWM.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2013 Owen McAree 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #ifndef pruPWM_h 22 | #define pruPWM_h 23 | 24 | #include 25 | #include "pru.h" 26 | 27 | 28 | class PRUPWM : private PRU { 29 | public: 30 | PRUPWM(unsigned int frequency = 50); 31 | void start(); 32 | using PRU::stop; 33 | 34 | void setFrequency(unsigned int frequency); 35 | void setChannelValue(unsigned int channel, unsigned long pwm_ns); 36 | void setFailsafeValue(unsigned int channel, unsigned long pwm_ns); 37 | void setFailsafeTimeout(unsigned int timeout_ms); 38 | private: 39 | void setPRUDuty(unsigned int channel, unsigned long pwm_ns); 40 | void updateFailsafe(); 41 | static const unsigned int nanosecondsPerCycle = 95; 42 | unsigned int pwmFrequency; 43 | unsigned int failsafeTimeout; 44 | }; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /include/pru.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2013 Owen McAree 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | // Class derived from node-pru Node.js module 22 | 23 | #ifndef pru_h 24 | #define pru_h 25 | 26 | // System headers 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | // PRU Driver headers 35 | #include 36 | #include 37 | 38 | class PRU { 39 | public: 40 | PRU(int number = 0); 41 | ~PRU(); 42 | 43 | void execute(const char * program); 44 | void stop(); 45 | void setSharedMemoryInt(unsigned int index, unsigned int value); 46 | void setSharedMemory(unsigned int * array, unsigned int start, unsigned int length); 47 | unsigned int getSharedMemoryInt(unsigned int index); 48 | void getSharedMemory(unsigned int * memory, unsigned int start, unsigned int length); 49 | 50 | private: 51 | int pruNumber; 52 | static unsigned int* memoryPtr; 53 | static const unsigned int OFFSET_SHAREDRAM = 2048; 54 | }; 55 | #endif -------------------------------------------------------------------------------- /src/pruPWM.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2013 Owen McAree 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #include "pruPWM.h" 22 | 23 | PRUPWM::PRUPWM(unsigned int frequency) : PRU(0) { 24 | this->stop(); 25 | this->setFrequency(frequency); 26 | this->setFailsafeTimeout(0); 27 | for (int i = 0; i < 8; i++) { 28 | this->setChannelValue(i, 1500000); 29 | this->setFailsafeValue(i, 1500000); 30 | } 31 | } 32 | 33 | void PRUPWM::start() { 34 | this->execute("pwm.bin"); 35 | } 36 | 37 | void PRUPWM::setFrequency(unsigned int frequency) { 38 | this->pwmFrequency = frequency; 39 | this->setSharedMemoryInt(0, 1000000000 / (PRUPWM::nanosecondsPerCycle*this->pwmFrequency)); 40 | } 41 | 42 | void PRUPWM::setChannelValue(unsigned int channel, unsigned long pwm_ns) { 43 | this->setPRUDuty(channel, pwm_ns); 44 | } 45 | void PRUPWM::setFailsafeValue(unsigned int channel, unsigned long pwm_ns) { 46 | this->setPRUDuty(channel+9, pwm_ns); 47 | } 48 | 49 | void PRUPWM::setFailsafeTimeout(unsigned int timeout_ms) { 50 | this->failsafeTimeout = timeout_ms; 51 | this->updateFailsafe(); 52 | } 53 | 54 | void PRUPWM::setPRUDuty(unsigned int channel, unsigned long pwm_ns) { 55 | this->setSharedMemoryInt(channel+1, (unsigned int)((unsigned long long)pwm_ns / PRUPWM::nanosecondsPerCycle)); 56 | this->updateFailsafe(); 57 | } 58 | void PRUPWM::updateFailsafe() { 59 | this->setSharedMemoryInt(9, this->failsafeTimeout * this->pwmFrequency / 1000); 60 | } 61 | -------------------------------------------------------------------------------- /include/pru.hp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Owen McAree 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | // this software and associated documentation files (the "Software"), to deal in 5 | // the Software without restriction, including without limitation the rights to 6 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | // the Software, and to permit persons to whom the Software is furnished to do so, 8 | // subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | // pru.hp 21 | #ifndef __PRU_HP__ 22 | #define __PRU_HP__ 23 | 24 | // Definitions 25 | 26 | // Refer to this mapping in the file - pruss_intc_mapping.h 27 | #define PRU0_PRU1_INTERRUPT 17 28 | #define PRU1_PRU0_INTERRUPT 18 29 | #define PRU0_ARM_INTERRUPT 19 30 | #define PRU1_ARM_INTERRUPT 20 31 | #define ARM_PRU0_INTERRUPT 21 32 | #define ARM_PRU1_INTERRUPT 22 33 | 34 | #define CONST_PRUCFG C4 35 | #define CONST_PRUDRAM C24 36 | #define CONST_PRUSHAREDRAM C28 37 | #define CONST_DDR C31 38 | 39 | // Address for the Constant table Block Index Register (CTBIR) 40 | #define CTBIR 0x22020 41 | 42 | // Address for the Constant table Programmable Pointer Register 0(CTPPR_0) 43 | #define CTPPR_0 0x22028 44 | 45 | // Address for the Constant table Programmable Pointer Register 1(CTPPR_1) 46 | #define CTPPR_1 0x2202C 47 | 48 | // Macros 49 | 50 | .macro LD32 51 | .mparam dst,src 52 | LBBO dst,src,#0x00,4 53 | .endm 54 | 55 | .macro LD16 56 | .mparam dst,src 57 | LBBO dst,src,#0x00,2 58 | .endm 59 | 60 | .macro LD8 61 | .mparam dst,src 62 | LBBO dst,src,#0x00,1 63 | .endm 64 | 65 | .macro ST32 66 | .mparam src,dst 67 | SBBO src,dst,#0x00,4 68 | .endm 69 | 70 | .macro ST16 71 | .mparam src,dst 72 | SBBO src,dst,#0x00,2 73 | .endm 74 | 75 | .macro ST8 76 | .mparam src,dst 77 | SBBO src,dst,#0x00,1 78 | .endm 79 | 80 | // *************************************** 81 | // * Global Structure Definitions * 82 | // *************************************** 83 | 84 | .struct Global 85 | .u32 regPointer 86 | .u32 regVal 87 | .ends 88 | 89 | 90 | // *************************************** 91 | // * Global Register Assignments * 92 | // *************************************** 93 | 94 | .assign Global, r2, *, global 95 | 96 | #endif // __PRU_HP__ 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /src/pwm.p: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Owen McAree 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | // this software and associated documentation files (the "Software"), to deal in 5 | // the Software without restriction, including without limitation the rights to 6 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | // the Software, and to permit persons to whom the Software is furnished to do so, 8 | // subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | .origin 0 21 | .entrypoint START 22 | #include "../include/pru.hp" 23 | 24 | START: 25 | // Preamble to set up OCP and shared RAM 26 | LBCO r0, CONST_PRUCFG, 4, 4 // Enable OCP master port 27 | CLR r0, r0, 4 // Clear SYSCFG[STANDBY_INIT] to enable OCP master port 28 | SBCO r0, CONST_PRUCFG, 4, 4 29 | MOV r0, 0x00000120 // Configure the programmable pointer register for PRU0 by setting c28_pointer[15:0] 30 | MOV r1, CTPPR_0 // field to 0x0120. This will make C28 point to 0x00012000 (PRU shared RAM). 31 | ST32 r0, r1 32 | // End of preamble 33 | 34 | // Shared memory registers 35 | // Int 0: Total PWM period (number of PRU cycles) 36 | // Int 1-8: Pulse width for each channel (number of PRU cycles) 37 | // Int 9: Failsafe timeout (number of PWM cycles) 38 | // e.g. for a PWM frequency of 50Hz, a value of 100 would be a 2s timeout 39 | // Must be reset everytime channels are updated 40 | // Set to 0 to disable failsafe feature 41 | // Int 10-17: Failsafe PWM periods (number of PRU cycles) 42 | LOOP1: // Outer loop repeats everything at PWM period 43 | LBCO r0, CONST_PRUSHAREDRAM, 0, 40 // Load in registers from shared memory 44 | QBEQ NO_FAILSAFE, r9, 0 // Check to see if failsafe is enabled 45 | QBEQ FAILSAFE, r9, 1 // Check to see if failsafe timeout has occured 46 | SUB r9, r9, 1 // If not timed out, decrement timeout counter 47 | SBCO r9, CONST_PRUSHAREDRAM, 36, 4 // Write timeout counter back to shared RAM 48 | QBA NO_FAILSAFE // Skip failsafe action 49 | FAILSAFE: 50 | LBCO r1, CONST_PRUSHAREDRAM, 40, 32 // Overwrite commanded positions with failsafe positions 51 | 52 | NO_FAILSAFE: 53 | MOV r30.b0, 0xFF // Turn on all output channels for start of cycle 54 | 55 | LOOP2: // Inner loop to handle channels 56 | SUB r0, r0, 1 // Subtract one from each register 57 | SUB r1, r1, 1 58 | SUB r2, r2, 1 59 | SUB r3, r3, 1 60 | SUB r4, r4, 1 61 | SUB r5, r5, 1 62 | SUB r6, r6, 1 63 | SUB r7, r7, 1 64 | SUB r8, r8, 1 65 | QBNE SKIP1, r1, 0 // Compare each register with zero 66 | CLR r30.t0 // If zero then turn off the corresponding channel 67 | SKIP1: // Otherwise skip that channel (leaving it high) 68 | QBNE SKIP2, r2, 0 69 | CLR r30.t1 70 | SKIP2: 71 | QBNE SKIP3, r3, 0 72 | CLR r30.t2 73 | SKIP3: 74 | QBNE SKIP4, r4, 0 75 | CLR r30.t3 76 | SKIP4: 77 | QBNE SKIP5, r5, 0 78 | CLR r30.t4 79 | SKIP5: 80 | QBNE SKIP6, r6, 0 81 | CLR r30.t5 82 | SKIP6: 83 | QBNE SKIP7, r7, 0 84 | CLR r30.t6 85 | SKIP7: 86 | QBNE SKIP8, r8, 0 87 | CLR r30.t7 88 | SKIP8: 89 | QBEQ LOOP1, r0, 0 // If cycle register is empty, restart 90 | QBA LOOP2 // Total of 19 statements per cycle, 95ns 91 | HALT // Halt the processor (although we will never get here...) 92 | -------------------------------------------------------------------------------- /src/pru.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2013 Owen McAree 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #include "pru.h" 22 | 23 | unsigned int* PRU::memoryPtr = NULL; 24 | 25 | PRU::PRU(int number) { 26 | // Store the PRU number (0 or 1) 27 | if (number < 2) { 28 | this->pruNumber = number; 29 | } else { 30 | this->pruNumber = 0; 31 | } 32 | 33 | // Initialise driver 34 | prussdrv_init (); 35 | 36 | // Open interrupt 37 | unsigned int ret = prussdrv_open(PRU_EVTOUT_0); 38 | if (ret) { 39 | printf("prussdrv_open open failed\n"); 40 | return; 41 | } 42 | 43 | //Initialise interrupt 44 | tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA; 45 | prussdrv_pruintc_init(&pruss_intc_initdata); 46 | 47 | // Allocate shared PRU memory 48 | if (!this->memoryPtr) { 49 | static void *sharedMem; 50 | prussdrv_map_prumem(PRUSS0_SHARED_DATARAM, &sharedMem); 51 | this->memoryPtr = (unsigned int*) sharedMem; 52 | } 53 | } 54 | 55 | PRU::~PRU() { 56 | this->stop(); 57 | prussdrv_exit (); 58 | }; 59 | 60 | void PRU::execute(const char * program) { 61 | prussdrv_exec_program (this->pruNumber, (char *)program); 62 | 63 | }; 64 | 65 | void PRU::stop() { 66 | prussdrv_pru_disable(this->pruNumber); 67 | } 68 | 69 | void PRU::setSharedMemoryInt(unsigned int index, unsigned int value) { 70 | this->memoryPtr[PRU::OFFSET_SHAREDRAM + index] = value; 71 | }; 72 | 73 | void PRU::setSharedMemory(unsigned int * array, unsigned int start, unsigned int length) { 74 | for (int i = 0; isetSharedMemoryInt(start + i, array[i]); 76 | } 77 | }; 78 | 79 | unsigned int PRU::getSharedMemoryInt(unsigned int index) { 80 | return this->memoryPtr[PRU::OFFSET_SHAREDRAM + index]; 81 | }; 82 | 83 | void PRU::getSharedMemory(unsigned int * memory, unsigned int start, unsigned int length) { 84 | for (unsigned int i = 0; igetSharedMemoryInt(start + i); 86 | } 87 | }; 88 | 89 | // TODO: Implement interrupt handling 90 | /*-------------------This is mostly copy/pasted from here: ---------------------*/ 91 | /*----------------http://kkaefer.github.io/node-cpp-modules/--------------------*/ 92 | /*struct Baton { 93 | uv_work_t request; 94 | Persistent callback; 95 | int error_code; 96 | std::string error_message; 97 | int32_t result; 98 | }; 99 | 100 | void AsyncWork(uv_work_t* req) { 101 | // Baton* baton = static_cast(req->data); 102 | prussdrv_pru_wait_event(PRU_EVTOUT_0); 103 | } 104 | 105 | void AsyncAfter(uv_work_t* req, int status) { 106 | HandleScope scope; 107 | Baton* baton = static_cast(req->data); 108 | baton->callback->Call(Context::GetCurrent()->Global(), 0, 0); 109 | baton->callback.Dispose(); 110 | delete baton; 111 | } 112 | 113 | Handle waitForInterrupt(const Arguments& args) { 114 | HandleScope scope; 115 | Local callback = Local::Cast(args[0]); 116 | 117 | Baton* baton = new Baton(); 118 | baton->request.data = baton; 119 | baton->callback = Persistent::New(callback); 120 | 121 | uv_queue_work(uv_default_loop(), &baton->request, AsyncWork, AsyncAfter); 122 | return scope.Close(Undefined()); 123 | } 124 | 125 | /*---------------------------Here ends the copy/pasting----------------------------*/ 126 | 127 | /* Clear Interrupt */ 128 | /*Handle clearInterrupt(const Arguments& args) { 129 | HandleScope scope; 130 | prussdrv_pru_clear_event(PRU0_ARM_INTERRUPT); 131 | return scope.Close(Undefined()); 132 | }; 133 | 134 | Handle interruptPRU(const Arguments& args) { 135 | HandleScope scope; 136 | prussdrv_pru_send_event(ARM_PRU0_INTERRUPT); 137 | return scope.Close(Undefined()); 138 | };*/ --------------------------------------------------------------------------------