https://elinux.org/api.php?action=feedcontributions&user=Popenhjc&feedformat=atomeLinux.org - User contributions [en]2024-03-29T08:27:40ZUser contributionsMediaWiki 1.31.0https://elinux.org/index.php?title=EBC_Exercise_08_Installing_Development_Tools_3.8&diff=188678EBC Exercise 08 Installing Development Tools 3.82012-11-05T19:28:21Z<p>Popenhjc: /* download */</p>
<hr />
<div>[[Category:ECE497]]<br />
{{YoderHead}}<br />
<br />
Early in the class most of the exercises we will do will all run on the BeagleBoard. You'll be able to edit ([http://projects.gnome.org/gedit/ gedit]), compile ([http://gcc.gnu.org/ gcc]) and run all on the Beagle. Later, when we start compiling the kernel [http://www.kernel.org/] or the boot loader, ([http://www.denx.de/wiki/U-Boot U-boot]) you will need to cross compile on a Linux machine and copy the results to the Beagle.<br />
<br />
The purpose of this exercise is to install all the tools needed for compiling on your host so they will be ready when you need them.<br />
<br />
Instructions for building Ångström are given [http://www.angstrom-distribution.org/building-angstrom here]; however there are a few changes you have to make. Here's what I did.<br />
<br />
'''Tip:''' Run this exercise using a wired connection if you can. The Ubuntu wireless driver can be finicky, and if it stops working you'll have to restart some of this.<br />
<br />
== The Kernel ==<br />
<br />
These instructions have been tested for the 3.2.25 kernel.<br />
<br />
=== download ===<br />
These are notes are based on [https://github.com/beagleboard/kernel/blob/6682025752d0b807119c1e363a0b1b9bfe2ab453/README.md Beagleboard kernel git site].<br />
<br />
First download the tools needed to compile the kernel. This took about 25 seconds.<br />
host$ '''sudo apt-get install -y git lzop gcc-arm-linux-gnueabi uboot-mkimage'''<br />
<br />
Next download the tools to get the kernel and the patches needed to make it run on the beagle. (2.5 seconds)<br />
host$ '''cd ~/BeagleBoard'''<br />
host$ '''git clone git://github.com/beagleboard/kernel.git'''<br />
host$ '''cd kernel'''<br />
host$ '''git checkout 6a7c4284a16fed3dae87f4aef78b59c902e4da84 -b beaglebone-3.2'''<br />
<br />
Next download the kernel and the patches. Before running '''./patch/sh''', take a look at it. Can you figure out what it's doing? Also look at '''patch_script.sh''', it's where the details are. The downloading/patching process takes some 39 minutes.<br />
host$ '''less patch.sh patch_script.sh'''<br />
host$ '''./patch.sh'''<br />
host$ '''cp patches/beaglebone/defconfig kernel/arch/arm/configs/beaglebone_defconfig<br />
host$ '''wget http://arago-project.org/git/projects/?p=am33x-cm3.git\;a=blob_plain\;f=bin/am335x-pm-firmware.bin\;hb=HEAD -O kernel/firmware/am335x-pm-firmware.bin'''<br />
host$ '''md5sum kernel/firmware/am335x-pm-firmware.bin''' <br />
17d6a4c24d3cb720aa9ed4522cb891fc kernel/firmware/am335x-pm-firmware.bin<br />
<br />
=== compile ===<br />
Once patched you are ready to compile the kernel. The first time takes a while. Mine tool 4 minutes, but I was running on 8 cores. Set the '''-j''X'' ''' to match the number of cores you have. '''uImage''' is the kernel!<br />
host$ '''cd kernel'''<br />
host$ '''make beaglebone_defconfig'''<br />
<br />
Now that you know it's working, let's compile it. First set the paths to find the cross-compiles. Put the following in a file, call it ~/.oe/'''crossCompile.sh'''.<br />
<br />
export ARCH=arm<br />
export CROSS_COMPILE=arm-linux-gnueabi-<br />
Do the above once<br />
<br />
Now ''source'' the file.<br />
host$ '''source ~/.oe/crossCompileEnv.sh'''<br />
Do the above once per terminal session.<br />
<br />
host$ '''make -j9'''<br />
host$ '''make uImage'''<br />
Do the above every time you recompile the kernel<br />
<br />
You also need all the kernel modules. Here we create a directory to install them in. (a few seconds)<br />
host$ '''mkdir ../rootfs'''<br />
host$ '''make INSTALL_MOD_PATH=../rootfs modules_install'''<br />
<br />
=== install ===<br />
Copy the kernel and the modules to the Beagle. (a minute or so)<br />
host$ '''cd ..'''<br />
host$ '''scp kernel/arch/arm/boot/uImage root@beagle:/boot/uImage-3.2.25+<br />
host$ '''cd rootfs'''<br />
host$ '''find -H -depth | cpio -o -H crc | ssh root@beagle 'cd /; cpio -id' '''<br />
<br />
Now log into the beagle and move some things around.<br />
host$ '''ssh root@beagle<br />
<br />
This will copy the kernel to the ext4 partition.<br />
beagle$ '''cd /boot'''<br />
beagle$ '''rm uImage'''<br />
beagle$ '''ln -s uImage-3.2.25+ uImage'''<br />
<br />
This will copy to the FAT partition.<br />
beagle$ '''mkdir /media/mmcblk0p1'''<br />
beagle$ '''mount /dev/mmcblk0p1 /media/mmcblk0p1'''<br />
beagle$ '''cp /boot/uImage-3.2.25+ /media/uImage'''<br />
<br />
=== reboot ===<br />
Make sure screen is running on your host so you can see the shutdown and boot processes.<br />
host$ '''screen /dev/ttyUSB1 115200'''<br />
<br />
If you get an error try changing making yourself the owner of /dev/ttyUSB1.<br />
host$ '''sudo chown ''yoder:yoder'' /dev/ttyUSB1'''<br />
host$ '''screen /dev/ttyUSB1 115200'''<br />
<br />
Then restart you beagle<br />
beagle$ '''shutdown -r now'''<br />
<br />
If all goes well you will boot into your new kernel.<br />
beagle$ '''uname -a'''<br />
Linux beaglebone 3.2.25+ #1 Fri Oct 19 11:05:28 EDT 2012 armv7l GNU/Linux<br />
<br />
== DAS U-boot ==<br />
These instructions came from [http://www.eewiki.net/display/linuxonarm/Home eewiki].<br />
<br />
=== download ===<br />
While we're at it, let's get the boot loader we'll be using. It takes some 3 minutes.<br />
<br />
host$ '''cd ~/BeagleBoard'''<br />
host$ '''git clone git://git.denx.de/u-boot.git'''<br />
host$ '''cd u-boot/'''<br />
host$ '''git checkout v2012.10-rc2 -b tmp'''<br />
<br />
=== compile ===<br />
Now configure and build. The first time takes some 4 minutes. After that it's only 5 seconds or so. (Replace '''am335x_evm_config''' with '''omap3_beagle_config''' if you are compiling for the xM.)<br />
host$ '''make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- am335x_evm_config'''<br />
host$ '''make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-'''<br />
<br />
=== install ===<br />
<br />
host$ '''scp u-boot.img root@beagle:.'''<br />
beagle$ '''mkdir /media/mmcblk0p1'''<br />
beagle$ '''mount /dev/mmcblk0p1 /media/mmcblk0p1'''<br />
beagle$ '''cd /media/mmcblk0p1'''<br />
beagle$ '''mv u-boot.img u-boot.img.orig''' # Save the working u-boot<br />
beagle$ '''cp ~/u-boot.img u-boot.img.new'''<br />
beagle$ '''cp u-boot.img.new u-boot.img'''<br />
Once installed you are ready for u-boot work.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=188654ECE497 BeagleBone PRU2012-11-05T17:27:30Z<p>Popenhjc: /* Work Breakdown */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
10 Highlights (Nice clean tone in 2nd video)<br />
07 Theory of Operation (Good overview. I'd like to see more comment in your PRU code)<br />
05 Work Breakdown (Need to include who did what)<br />
10 Future Work<br />
10 Conclusions<br />
00 Demo (I want to hear the clean sine before filling this in)<br />
10 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU, Programmable Realtime Unit, <span style="color:red">(DID THIS:Define PRU)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The PRU is a part of the processor that runs at 200MHz (5ns per instruction), and is separate from the operating system all together, making it more efficient at accessing I/O pins. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin. We were also able to produce an approximated sinusoidal output on the GPIO pin using pulse width modulation at a specific frequency. For each of these there is an example to follow describing how each part works, and listing any resources to look at to find out more.<br />
<br />
We were hoping to potentially look into reading an analog input and producing it as an approximated output using the pulse width modulator, but this ended up being to optimistic of a goal. This would be an interesting thing to explore for a project that expands upon this.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer. Also, if you do not do this instruction before following the rest of the instructions you may run into segmentation faults when trying to initialize the PRU.<br />
<br />
<span style="color:red">DID THIS:Where is uio_pruss? (Not a specific location, can be accessed anywhere on the BeagleBone)</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
beagle$ '''git clone git://github.com/millerap/AM335x_PRU_BeagleBone'''<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE=""'''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
beagle$ '''cd ../utils/pasm_source'''<br />
beagle$ '''./linuxbuild'''<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
beagle$ '''cd ../../'''<br />
beagle$ '''make CROSS_COMPILE=""'''<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
beagle$ '''cd bin'''<br />
beagle$ '''make'''<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
beagle$ '''./blinker'''<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't do the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(DID THIS:Note that this is /bin/blinker.p. WORKING ON THIS: Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
In the assembly file blinker.p:<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">DID THIS:This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The following information can be found on TI's PRU Linux Application Loader API Guide wiki:<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem C PRU instructions]<br />
<br />
This lays out every function that can be used in the C code as well as an explanation of its functionality.<br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS, Programmable Realtime Unit Subsystem or the entire system of two PRUs, an interrupt controller (INTC), and associated memory ([http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit_Subsystem PRUSS]),<span style="color:red">(DID THIS:What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) <span style="color:red">(Which header?)</span> through a speaker to ground. The output produced will be start as a 367Hz approximation and can be changed by putting a number into the terminal (67 to 66000) and pressing enter (NOTE:Text may mess it up. So, DON'T use text!). All of the sampling for 133kHz sampling is now taken care of for any frequency wave mentioned in the previous sentence.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
*[http://www.youtube.com/watch?v=aFep70xN2j4 PWM Sine Wave Approximation Example]<br />
<br />
<span style="color:red">The first video sounds clipped, but the second one sound very clean! Good job.</span><br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
<span style="color:red">Also include who did what.</span><br />
<br />
'''Milestones'''<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6-11/9: Presentation<br />
<br />
<br />
'''What was done and who did it'''<br />
<br />
Research and Documentation: Andrew, Bryan, James, Peter<br />
<br />
Simple Implementation of a blinking LED: Andrew, Bryan, James, Peter<br />
<br />
PWM Implementation: Andrew<br />
<br />
Sinusoidal Approximation: James<br />
<br />
Wiki Editing and Organization: Andrew, Bryan, James, Peter<br />
<br />
<br />
'''Research'''<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. <span style="color:red">(The 'C' code has this comment: /* Wait for event completion from PRU */. Does it wait for the PRU?)</span> <br />
So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a route you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=188642ECE497 BeagleBone PRU2012-11-05T17:23:32Z<p>Popenhjc: /* Work Breakdown */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
10 Highlights (Nice clean tone in 2nd video)<br />
07 Theory of Operation (Good overview. I'd like to see more comment in your PRU code)<br />
05 Work Breakdown (Need to include who did what)<br />
10 Future Work<br />
10 Conclusions<br />
00 Demo (I want to hear the clean sine before filling this in)<br />
10 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU, Programmable Realtime Unit, <span style="color:red">(DID THIS:Define PRU)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The PRU is a part of the processor that runs at 200MHz (5ns per instruction), and is separate from the operating system all together, making it more efficient at accessing I/O pins. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin. We were also able to produce an approximated sinusoidal output on the GPIO pin using pulse width modulation at a specific frequency. For each of these there is an example to follow describing how each part works, and listing any resources to look at to find out more.<br />
<br />
We were hoping to potentially look into reading an analog input and producing it as an approximated output using the pulse width modulator, but this ended up being to optimistic of a goal. This would be an interesting thing to explore for a project that expands upon this.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer. Also, if you do not do this instruction before following the rest of the instructions you may run into segmentation faults when trying to initialize the PRU.<br />
<br />
<span style="color:red">DID THIS:Where is uio_pruss? (Not a specific location, can be accessed anywhere on the BeagleBone)</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
beagle$ '''git clone git://github.com/millerap/AM335x_PRU_BeagleBone'''<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE=""'''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
beagle$ '''cd ../utils/pasm_source'''<br />
beagle$ '''./linuxbuild'''<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
beagle$ '''cd ../../'''<br />
beagle$ '''make CROSS_COMPILE=""'''<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
beagle$ '''cd bin'''<br />
beagle$ '''make'''<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
beagle$ '''./blinker'''<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't do the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(DID THIS:Note that this is /bin/blinker.p. WORKING ON THIS: Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
In the assembly file blinker.p:<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">DID THIS:This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The following information can be found on TI's PRU Linux Application Loader API Guide wiki:<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem C PRU instructions]<br />
<br />
This lays out every function that can be used in the C code as well as an explanation of its functionality.<br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS, Programmable Realtime Unit Subsystem or the entire system of two PRUs, an interrupt controller (INTC), and associated memory ([http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit_Subsystem PRUSS]),<span style="color:red">(DID THIS:What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) <span style="color:red">(Which header?)</span> through a speaker to ground. The output produced will be start as a 367Hz approximation and can be changed by putting a number into the terminal (67 to 66000) and pressing enter (NOTE:Text may mess it up. So, DON'T use text!). All of the sampling for 133kHz sampling is now taken care of for any frequency wave mentioned in the previous sentence.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
*[http://www.youtube.com/watch?v=aFep70xN2j4 PWM Sine Wave Approximation Example]<br />
<br />
<span style="color:red">The first video sounds clipped, but the second one sound very clean! Good job.</span><br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
<span style="color:red">Also include who did what.</span><br />
<br />
'''Milestones'''<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
<br />
'''What was done and who did it'''<br />
<br />
Research and Documentation: Andrew, Bryan, James, Peter<br />
<br />
Simple Implementation of a blinking LED: Andrew, Bryan, James, Peter<br />
<br />
PWM Implementation: Andrew<br />
<br />
Sinusoidal Approximation: James<br />
<br />
Wiki Editing and Organization: Andrew, Bryan, James, Peter<br />
<br />
<br />
'''Research'''<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. <span style="color:red">(The 'C' code has this comment: /* Wait for event completion from PRU */. Does it wait for the PRU?)</span> <br />
So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a route you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=188636ECE497 BeagleBone PRU2012-11-05T17:23:20Z<p>Popenhjc: /* Work Breakdown */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
10 Highlights (Nice clean tone in 2nd video)<br />
07 Theory of Operation (Good overview. I'd like to see more comment in your PRU code)<br />
05 Work Breakdown (Need to include who did what)<br />
10 Future Work<br />
10 Conclusions<br />
00 Demo (I want to hear the clean sine before filling this in)<br />
10 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU, Programmable Realtime Unit, <span style="color:red">(DID THIS:Define PRU)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The PRU is a part of the processor that runs at 200MHz (5ns per instruction), and is separate from the operating system all together, making it more efficient at accessing I/O pins. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin. We were also able to produce an approximated sinusoidal output on the GPIO pin using pulse width modulation at a specific frequency. For each of these there is an example to follow describing how each part works, and listing any resources to look at to find out more.<br />
<br />
We were hoping to potentially look into reading an analog input and producing it as an approximated output using the pulse width modulator, but this ended up being to optimistic of a goal. This would be an interesting thing to explore for a project that expands upon this.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer. Also, if you do not do this instruction before following the rest of the instructions you may run into segmentation faults when trying to initialize the PRU.<br />
<br />
<span style="color:red">DID THIS:Where is uio_pruss? (Not a specific location, can be accessed anywhere on the BeagleBone)</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
beagle$ '''git clone git://github.com/millerap/AM335x_PRU_BeagleBone'''<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE=""'''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
beagle$ '''cd ../utils/pasm_source'''<br />
beagle$ '''./linuxbuild'''<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
beagle$ '''cd ../../'''<br />
beagle$ '''make CROSS_COMPILE=""'''<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
beagle$ '''cd bin'''<br />
beagle$ '''make'''<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
beagle$ '''./blinker'''<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't do the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(DID THIS:Note that this is /bin/blinker.p. WORKING ON THIS: Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
In the assembly file blinker.p:<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">DID THIS:This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The following information can be found on TI's PRU Linux Application Loader API Guide wiki:<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem C PRU instructions]<br />
<br />
This lays out every function that can be used in the C code as well as an explanation of its functionality.<br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS, Programmable Realtime Unit Subsystem or the entire system of two PRUs, an interrupt controller (INTC), and associated memory ([http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit_Subsystem PRUSS]),<span style="color:red">(DID THIS:What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) <span style="color:red">(Which header?)</span> through a speaker to ground. The output produced will be start as a 367Hz approximation and can be changed by putting a number into the terminal (67 to 66000) and pressing enter (NOTE:Text may mess it up. So, DON'T use text!). All of the sampling for 133kHz sampling is now taken care of for any frequency wave mentioned in the previous sentence.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
*[http://www.youtube.com/watch?v=aFep70xN2j4 PWM Sine Wave Approximation Example]<br />
<br />
<span style="color:red">The first video sounds clipped, but the second one sound very clean! Good job.</span><br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
<span style="color:red">Also include who did what.</span><br />
<br />
'''Milestones'''<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
<br />
'''What was done and who did it'''<br />
<br />
Research and Documentation: Andrew, Bryan, James, Peter<br />
<br />
Simple Implementation of a blinking LED: Andrew, Bryan, James, Peter<br />
<br />
PWM Implementation: Andrew<br />
<br />
Sinusoidal Approximation: James<br />
<br />
Wiki Editing and Organization: Andrew, Bryan, James, Peter<br />
<br />
'''Research'''<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. <span style="color:red">(The 'C' code has this comment: /* Wait for event completion from PRU */. Does it wait for the PRU?)</span> <br />
So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a route you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=188618ECE497 BeagleBone PRU2012-11-05T17:18:05Z<p>Popenhjc: /* Executive Summary */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
10 Highlights (Nice clean tone in 2nd video)<br />
07 Theory of Operation (Good overview. I'd like to see more comment in your PRU code)<br />
05 Work Breakdown (Need to include who did what)<br />
10 Future Work<br />
10 Conclusions<br />
00 Demo (I want to hear the clean sine before filling this in)<br />
10 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU, Programmable Realtime Unit, <span style="color:red">(DID THIS:Define PRU)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The PRU is a part of the processor that runs at 200MHz (5ns per instruction), and is separate from the operating system all together, making it more efficient at accessing I/O pins. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin. We were also able to produce an approximated sinusoidal output on the GPIO pin using pulse width modulation at a specific frequency. For each of these there is an example to follow describing how each part works, and listing any resources to look at to find out more.<br />
<br />
We were hoping to potentially look into reading an analog input and producing it as an approximated output using the pulse width modulator, but this ended up being to optimistic of a goal. This would be an interesting thing to explore for a project that expands upon this.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer. Also, if you do not do this instruction before following the rest of the instructions you may run into segmentation faults when trying to initialize the PRU.<br />
<br />
<span style="color:red">DID THIS:Where is uio_pruss? (Not a specific location, can be accessed anywhere on the BeagleBone)</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
beagle$ '''git clone git://github.com/millerap/AM335x_PRU_BeagleBone'''<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE=""'''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
beagle$ '''cd ../utils/pasm_source'''<br />
beagle$ '''./linuxbuild'''<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
beagle$ '''cd ../../'''<br />
beagle$ '''make CROSS_COMPILE=""'''<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
beagle$ '''cd bin'''<br />
beagle$ '''make'''<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
beagle$ '''./blinker'''<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't do the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(DID THIS:Note that this is /bin/blinker.p. WORKING ON THIS: Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
In the assembly file blinker.p:<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">DID THIS:This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The following information can be found on TI's PRU Linux Application Loader API Guide wiki:<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem C PRU instructions]<br />
<br />
This lays out every function that can be used in the C code as well as an explanation of its functionality.<br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS, Programmable Realtime Unit Subsystem or the entire system of two PRUs, an interrupt controller (INTC), and associated memory ([http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit_Subsystem PRUSS]),<span style="color:red">(DID THIS:What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) <span style="color:red">(Which header?)</span> through a speaker to ground. The output produced will be start as a 367Hz approximation and can be changed by putting a number into the terminal (67 to 66000) and pressing enter (NOTE:Text may mess it up. So, DON'T use text!). All of the sampling for 133kHz sampling is now taken care of for any frequency wave mentioned in the previous sentence.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
*[http://www.youtube.com/watch?v=aFep70xN2j4 PWM Sine Wave Approximation Example]<br />
<br />
<span style="color:red">The first video sounds clipped, but the second one sound very clean! Good job.</span><br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
<span style="color:red">Also include who did what.</span><br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. <span style="color:red">(The 'C' code has this comment: /* Wait for event completion from PRU */. Does it wait for the PRU?)</span> <br />
So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a route you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=187322ECE497 BeagleBone PRU2012-11-03T02:11:45Z<p>Popenhjc: /* How the C Code Works */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
00 Highlights<br />
00 Theory of Operation<br />
00 Work Breakdown<br />
00 Future Work<br />
00 Conclusions<br />
00 Demo<br />
00 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU, Programmable Realtime Unit, <span style="color:red">(DID THIS:Define PRU)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The PRU is a part of the processor that runs at 200MHz (5ns per instruction), and is separate from the operating system all together, making it more efficient at accessing I/O pins. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer. Also, if you do not do this instruction before following the rest of the instructions you may run into segmentation faults when trying to initialize the PRU.<br />
<br />
<span style="color:red">DID THIS:Where is uio_pruss? (Not a specific location, can be accessed anywhere on the BeagleBone)</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
beagle$ '''git clone git://github.com/millerap/AM335x_PRU_BeagleBone'''<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE=""'''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
beagle$ '''cd ../utils/pasm_source'''<br />
beagle$ '''./linuxbuild'''<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
beagle$ '''cd ../../'''<br />
beagle$ '''make CROSS_COMPILE=""'''<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
beagle$ '''cd bin'''<br />
beagle$ '''make'''<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
beagle$ '''./blinker'''<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't do the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(DID THIS:Note that this is /bin/blinker.p. WORKING ON THIS: Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
In the assembly file blinker.p:<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">DID THIS:This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The following information can be found on TI's PRU Linux Application Loader API Guide wiki:<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem C PRU instructions]<br />
<br />
This lays out every function that can be used in the C code as well as an explanation of its functionality.<br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS, Programmable Realtime Unit Subsystem or the entire system of two PRUs, an interrupt controller (INTC), and associated memory ([http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit_Subsystem PRUSS]),<span style="color:red">(DID THIS:What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) through a speaker to ground. The output produced will be start as a 367Hz approximation and can be changed by putting a number into the terminal (67 to 66000) and pressing enter (NOTE:Text may mess it up. So, DON'T use text!). All of the sampling for 133kHz sampling is now taken care of for any frequency wave mentioned in the previous sentence.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
*[http://www.youtube.com/watch?v=aFep70xN2j4 PWM Sine Wave Approximation Example]<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a rout you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=187316ECE497 BeagleBone PRU2012-11-03T02:11:20Z<p>Popenhjc: /* How the C Code Works */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
00 Highlights<br />
00 Theory of Operation<br />
00 Work Breakdown<br />
00 Future Work<br />
00 Conclusions<br />
00 Demo<br />
00 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU, Programmable Realtime Unit, <span style="color:red">(DID THIS:Define PRU)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The PRU is a part of the processor that runs at 200MHz (5ns per instruction), and is separate from the operating system all together, making it more efficient at accessing I/O pins. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer. Also, if you do not do this instruction before following the rest of the instructions you may run into segmentation faults when trying to initialize the PRU.<br />
<br />
<span style="color:red">DID THIS:Where is uio_pruss? (Not a specific location, can be accessed anywhere on the BeagleBone)</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
beagle$ '''git clone git://github.com/millerap/AM335x_PRU_BeagleBone'''<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE=""'''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
beagle$ '''cd ../utils/pasm_source'''<br />
beagle$ '''./linuxbuild'''<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
beagle$ '''cd ../../'''<br />
beagle$ '''make CROSS_COMPILE=""'''<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
beagle$ '''cd bin'''<br />
beagle$ '''make'''<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
beagle$ '''./blinker'''<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't do the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(DID THIS:Note that this is /bin/blinker.p. WORKING ON THIS: Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
In the assembly file blinker.p:<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">DID THIS:This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The following information can be found on TI's PRU Linux Application Loader API Guide wiki:<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem C PRU instructions]<br />
<br />
This lays out every function that can be used in the C code as well as an explanation of its functionality.<br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS, Programmable Realtime Unit Subsystem or the entire system of two PRUs, and interrupt controller (INTC), and associated memory ([http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit_Subsystem PRUSS]),<span style="color:red">(DID THIS:What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) through a speaker to ground. The output produced will be start as a 367Hz approximation and can be changed by putting a number into the terminal (67 to 66000) and pressing enter (NOTE:Text may mess it up. So, DON'T use text!). All of the sampling for 133kHz sampling is now taken care of for any frequency wave mentioned in the previous sentence.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
*[http://www.youtube.com/watch?v=aFep70xN2j4 PWM Sine Wave Approximation Example]<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a rout you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=187310ECE497 BeagleBone PRU2012-11-03T02:08:09Z<p>Popenhjc: /* How the C Code Works */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
00 Highlights<br />
00 Theory of Operation<br />
00 Work Breakdown<br />
00 Future Work<br />
00 Conclusions<br />
00 Demo<br />
00 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU, Programmable Realtime Unit, <span style="color:red">(DID THIS:Define PRU)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The PRU is a part of the processor that runs at 200MHz (5ns per instruction), and is separate from the operating system all together, making it more efficient at accessing I/O pins. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer. Also, if you do not do this instruction before following the rest of the instructions you may run into segmentation faults when trying to initialize the PRU.<br />
<br />
<span style="color:red">DID THIS:Where is uio_pruss? (Not a specific location, can be accessed anywhere on the BeagleBone)</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
beagle$ '''git clone git://github.com/millerap/AM335x_PRU_BeagleBone'''<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE=""'''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
beagle$ '''cd ../utils/pasm_source'''<br />
beagle$ '''./linuxbuild'''<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
beagle$ '''cd ../../'''<br />
beagle$ '''make CROSS_COMPILE=""'''<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
beagle$ '''cd bin'''<br />
beagle$ '''make'''<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
beagle$ '''./blinker'''<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't do the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(DID THIS:Note that this is /bin/blinker.p. WORKING ON THIS: Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
In the assembly file blinker.p:<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">DID THIS:This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The following information can be found on TI's PRU Linux Application Loader API Guide wiki:<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem C PRU instructions]<br />
<br />
This lays out every function that can be used in the C code as well as an explanation of its functionality.<br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS, Programmable Realtime Unit Subsystem,<span style="color:red">(DID THIS:What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) through a speaker to ground. The output produced will be start as a 367Hz approximation and can be changed by putting a number into the terminal (67 to 66000) and pressing enter (NOTE:Text may mess it up. So, DON'T use text!). All of the sampling for 133kHz sampling is now taken care of for any frequency wave mentioned in the previous sentence.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
*[http://www.youtube.com/watch?v=aFep70xN2j4 PWM Sine Wave Approximation Example]<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a rout you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=187304ECE497 BeagleBone PRU2012-11-03T02:07:55Z<p>Popenhjc: /* How the C Code Works */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
00 Highlights<br />
00 Theory of Operation<br />
00 Work Breakdown<br />
00 Future Work<br />
00 Conclusions<br />
00 Demo<br />
00 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU, Programmable Realtime Unit, <span style="color:red">(DID THIS:Define PRU)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The PRU is a part of the processor that runs at 200MHz (5ns per instruction), and is separate from the operating system all together, making it more efficient at accessing I/O pins. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer. Also, if you do not do this instruction before following the rest of the instructions you may run into segmentation faults when trying to initialize the PRU.<br />
<br />
<span style="color:red">DID THIS:Where is uio_pruss? (Not a specific location, can be accessed anywhere on the BeagleBone)</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
beagle$ '''git clone git://github.com/millerap/AM335x_PRU_BeagleBone'''<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE=""'''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
beagle$ '''cd ../utils/pasm_source'''<br />
beagle$ '''./linuxbuild'''<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
beagle$ '''cd ../../'''<br />
beagle$ '''make CROSS_COMPILE=""'''<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
beagle$ '''cd bin'''<br />
beagle$ '''make'''<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
beagle$ '''./blinker'''<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't do the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(DID THIS:Note that this is /bin/blinker.p. WORKING ON THIS: Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
In the assembly file blinker.p:<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The following information can be found on TI's PRU Linux Application Loader API Guide wiki:<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem C PRU instructions]<br />
<br />
This lays out every function that can be used in the C code as well as an explanation of its functionality.<br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS, Programmable Realtime Unit Subsystem,<span style="color:red">(DID THIS:What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) through a speaker to ground. The output produced will be start as a 367Hz approximation and can be changed by putting a number into the terminal (67 to 66000) and pressing enter (NOTE:Text may mess it up. So, DON'T use text!). All of the sampling for 133kHz sampling is now taken care of for any frequency wave mentioned in the previous sentence.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
*[http://www.youtube.com/watch?v=aFep70xN2j4 PWM Sine Wave Approximation Example]<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a rout you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=187298ECE497 BeagleBone PRU2012-11-03T02:03:12Z<p>Popenhjc: /* User Instructions */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
00 Highlights<br />
00 Theory of Operation<br />
00 Work Breakdown<br />
00 Future Work<br />
00 Conclusions<br />
00 Demo<br />
00 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU, Programmable Realtime Unit, <span style="color:red">(DID THIS:Define PRU)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The PRU is a part of the processor that runs at 200MHz (5ns per instruction), and is separate from the operating system all together, making it more efficient at accessing I/O pins. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer. Also, if you do not do this instruction before following the rest of the instructions you may run into segmentation faults when trying to initialize the PRU.<br />
<br />
<span style="color:red">DID THIS:Where is uio_pruss? (Not a specific location, can be accessed anywhere on the BeagleBone)</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
beagle$ '''git clone git://github.com/millerap/AM335x_PRU_BeagleBone'''<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE=""'''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
beagle$ '''cd ../utils/pasm_source'''<br />
beagle$ '''./linuxbuild'''<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
beagle$ '''cd ../../'''<br />
beagle$ '''make CROSS_COMPILE=""'''<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
beagle$ '''cd bin'''<br />
beagle$ '''make'''<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
beagle$ '''./blinker'''<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't do the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(DID THIS:Note that this is /bin/blinker.p. WORKING ON THIS: Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
In the assembly file blinker.p:<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The following information can be found in the following references:<br />
*<br />
*<br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS, Programmable Realtime Unit Subsystem,<span style="color:red">(DID THIS:What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) through a speaker to ground. The output produced will be start as a 367Hz approximation and can be changed by putting a number into the terminal (67 to 66000) and pressing enter (NOTE:Text may mess it up. So, DON'T use text!). All of the sampling for 133kHz sampling is now taken care of for any frequency wave mentioned in the previous sentence.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
*[http://www.youtube.com/watch?v=aFep70xN2j4 PWM Sine Wave Approximation Example]<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a rout you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=187292ECE497 BeagleBone PRU2012-11-03T02:02:56Z<p>Popenhjc: /* User Instructions */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
00 Highlights<br />
00 Theory of Operation<br />
00 Work Breakdown<br />
00 Future Work<br />
00 Conclusions<br />
00 Demo<br />
00 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU, Programmable Realtime Unit, <span style="color:red">(DID THIS:Define PRU)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The PRU is a part of the processor that runs at 200MHz (5ns per instruction), and is separate from the operating system all together, making it more efficient at accessing I/O pins. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer. Also, if you do not do this instruction before following the rest of the instructions you may run into segmentation faults when trying to initialize the PRU.<br />
<br />
<span style="color:red">DID THIS:Where is uio_pruss? (Not a specific location, can be accessed anywhere)</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
beagle$ '''git clone git://github.com/millerap/AM335x_PRU_BeagleBone'''<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE=""'''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
beagle$ '''cd ../utils/pasm_source'''<br />
beagle$ '''./linuxbuild'''<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
beagle$ '''cd ../../'''<br />
beagle$ '''make CROSS_COMPILE=""'''<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
beagle$ '''cd bin'''<br />
beagle$ '''make'''<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
beagle$ '''./blinker'''<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't do the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(DID THIS:Note that this is /bin/blinker.p. WORKING ON THIS: Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
In the assembly file blinker.p:<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The following information can be found in the following references:<br />
*<br />
*<br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS, Programmable Realtime Unit Subsystem,<span style="color:red">(DID THIS:What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) through a speaker to ground. The output produced will be start as a 367Hz approximation and can be changed by putting a number into the terminal (67 to 66000) and pressing enter (NOTE:Text may mess it up. So, DON'T use text!). All of the sampling for 133kHz sampling is now taken care of for any frequency wave mentioned in the previous sentence.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
*[http://www.youtube.com/watch?v=aFep70xN2j4 PWM Sine Wave Approximation Example]<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a rout you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=187286ECE497 BeagleBone PRU2012-11-03T02:01:46Z<p>Popenhjc: /* How the C Code Works */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
00 Highlights<br />
00 Theory of Operation<br />
00 Work Breakdown<br />
00 Future Work<br />
00 Conclusions<br />
00 Demo<br />
00 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU, Programmable Realtime Unit, <span style="color:red">(DID THIS:Define PRU)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The PRU is a part of the processor that runs at 200MHz (5ns per instruction), and is separate from the operating system all together, making it more efficient at accessing I/O pins. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer. Also, if you do not do this instruction before following the rest of the instructions you may run into segmentation faults when trying to initialize the PRU.<br />
<br />
<span style="color:red">Where is uio_pruss?</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
beagle$ '''git clone git://github.com/millerap/AM335x_PRU_BeagleBone'''<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE=""'''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
beagle$ '''cd ../utils/pasm_source'''<br />
beagle$ '''./linuxbuild'''<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
beagle$ '''cd ../../'''<br />
beagle$ '''make CROSS_COMPILE=""'''<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
beagle$ '''cd bin'''<br />
beagle$ '''make'''<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
beagle$ '''./blinker'''<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't do the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(DID THIS:Note that this is /bin/blinker.p. WORKING ON THIS: Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
In the assembly file blinker.p:<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The following information can be found in the following references:<br />
*<br />
*<br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS, Programmable Realtime Unit Subsystem,<span style="color:red">(DID THIS:What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) through a speaker to ground. The output produced will be start as a 367Hz approximation and can be changed by putting a number into the terminal (67 to 66000) and pressing enter (NOTE:Text may mess it up. So, DON'T use text!). All of the sampling for 133kHz sampling is now taken care of for any frequency wave mentioned in the previous sentence.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
*[http://www.youtube.com/watch?v=aFep70xN2j4 PWM Sine Wave Approximation Example]<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a rout you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=187280ECE497 BeagleBone PRU2012-11-03T01:58:26Z<p>Popenhjc: /* Building and Running the Sin_Approximation Example */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
00 Highlights<br />
00 Theory of Operation<br />
00 Work Breakdown<br />
00 Future Work<br />
00 Conclusions<br />
00 Demo<br />
00 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU, Programmable Realtime Unit, <span style="color:red">(DID THIS:Define PRU)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The PRU is a part of the processor that runs at 200MHz (5ns per instruction), and is separate from the operating system all together, making it more efficient at accessing I/O pins. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer. Also, if you do not do this instruction before following the rest of the instructions you may run into segmentation faults when trying to initialize the PRU.<br />
<br />
<span style="color:red">Where is uio_pruss?</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
beagle$ '''git clone git://github.com/millerap/AM335x_PRU_BeagleBone'''<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE=""'''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
beagle$ '''cd ../utils/pasm_source'''<br />
beagle$ '''./linuxbuild'''<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
beagle$ '''cd ../../'''<br />
beagle$ '''make CROSS_COMPILE=""'''<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
beagle$ '''cd bin'''<br />
beagle$ '''make'''<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
beagle$ '''./blinker'''<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't do the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(DID THIS:Note that this is /bin/blinker.p. WORKING ON THIS: Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
In the assembly file blinker.p:<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS, Programmable Realtime Unit Subsystem,<span style="color:red">(DID THIS:What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) through a speaker to ground. The output produced will be start as a 367Hz approximation and can be changed by putting a number into the terminal (67 to 66000) and pressing enter (NOTE:Text may mess it up. So, DON'T use text!). All of the sampling for 133kHz sampling is now taken care of for any frequency wave mentioned in the previous sentence.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
*[http://www.youtube.com/watch?v=aFep70xN2j4 PWM Sine Wave Approximation Example]<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a rout you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=187274ECE497 BeagleBone PRU2012-11-03T01:48:38Z<p>Popenhjc: /* Highlights */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
00 Highlights<br />
00 Theory of Operation<br />
00 Work Breakdown<br />
00 Future Work<br />
00 Conclusions<br />
00 Demo<br />
00 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU, Programmable Realtime Unit, <span style="color:red">(DID THIS:Define PRU)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The PRU is a part of the processor that runs at 200MHz (5ns per instruction), and is separate from the operating system all together, making it more efficient at accessing I/O pins. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer. Also, if you do not do this instruction before following the rest of the instructions you may run into segmentation faults when trying to initialize the PRU.<br />
<br />
<span style="color:red">Where is uio_pruss?</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
beagle$ '''git clone git://github.com/millerap/AM335x_PRU_BeagleBone'''<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE=""'''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
beagle$ '''cd ../utils/pasm_source'''<br />
beagle$ '''./linuxbuild'''<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
beagle$ '''cd ../../'''<br />
beagle$ '''make CROSS_COMPILE=""'''<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
beagle$ '''cd bin'''<br />
beagle$ '''make'''<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
beagle$ '''./blinker'''<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't do the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(DID THIS:Note that this is /bin/blinker.p. WORKING ON THIS: Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
In the assembly file blinker.p:<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS, Programmable Realtime Unit Subsystem,<span style="color:red">(DID THIS:What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) through a speaker to ground. The output produced will be an 880Hz sin wave approximation.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
*[http://www.youtube.com/watch?v=aFep70xN2j4 PWM Sine Wave Approximation Example]<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a rout you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=187268ECE497 BeagleBone PRU2012-11-03T01:45:22Z<p>Popenhjc: /* How the C Code Works */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
00 Highlights<br />
00 Theory of Operation<br />
00 Work Breakdown<br />
00 Future Work<br />
00 Conclusions<br />
00 Demo<br />
00 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU, Programmable Realtime Unit, <span style="color:red">(DID THIS:Define PRU)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The PRU is a part of the processor that runs at 200MHz (5ns per instruction), and is separate from the operating system all together, making it more efficient at accessing I/O pins. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer. Also, if you do not do this instruction before following the rest of the instructions you may run into segmentation faults when trying to initialize the PRU.<br />
<br />
<span style="color:red">Where is uio_pruss?</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
beagle$ '''git clone git://github.com/millerap/AM335x_PRU_BeagleBone'''<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE=""'''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
beagle$ '''cd ../utils/pasm_source'''<br />
beagle$ '''./linuxbuild'''<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
beagle$ '''cd ../../'''<br />
beagle$ '''make CROSS_COMPILE=""'''<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
beagle$ '''cd bin'''<br />
beagle$ '''make'''<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
beagle$ '''./blinker'''<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't do the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(DID THIS:Note that this is /bin/blinker.p. WORKING ON THIS: Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
In the assembly file blinker.p:<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS, Programmable Realtime Unit Subsystem,<span style="color:red">(DID THIS:What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) through a speaker to ground. The output produced will be an 880Hz sin wave approximation.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a rout you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=187262ECE497 BeagleBone PRU2012-11-03T01:44:41Z<p>Popenhjc: /* Executive Summary */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
00 Highlights<br />
00 Theory of Operation<br />
00 Work Breakdown<br />
00 Future Work<br />
00 Conclusions<br />
00 Demo<br />
00 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU, Programmable Realtime Unit, <span style="color:red">(DID THIS:Define PRU)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The PRU is a part of the processor that runs at 200MHz (5ns per instruction), and is separate from the operating system all together, making it more efficient at accessing I/O pins. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer. Also, if you do not do this instruction before following the rest of the instructions you may run into segmentation faults when trying to initialize the PRU.<br />
<br />
<span style="color:red">Where is uio_pruss?</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
beagle$ '''git clone git://github.com/millerap/AM335x_PRU_BeagleBone'''<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE=""'''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
beagle$ '''cd ../utils/pasm_source'''<br />
beagle$ '''./linuxbuild'''<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
beagle$ '''cd ../../'''<br />
beagle$ '''make CROSS_COMPILE=""'''<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
beagle$ '''cd bin'''<br />
beagle$ '''make'''<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
beagle$ '''./blinker'''<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't do the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(DID THIS:Note that this is /bin/blinker.p. WORKING ON THIS: Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
In the assembly file blinker.p:<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS <span style="color:red">(What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) through a speaker to ground. The output produced will be an 880Hz sin wave approximation.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a rout you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=187256ECE497 BeagleBone PRU2012-11-03T01:42:35Z<p>Popenhjc: /* How the Assembly Code Works */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
00 Highlights<br />
00 Theory of Operation<br />
00 Work Breakdown<br />
00 Future Work<br />
00 Conclusions<br />
00 Demo<br />
00 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU, Processor Unit, <span style="color:red">(DID THIS:Define PRU)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The PRU is a part of the processor that runs at 200MHz (5ns per instruction), and is separate from the operating system all together, making it more efficient at accessing I/O pins. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer. Also, if you do not do this instruction before following the rest of the instructions you may run into segmentation faults when trying to initialize the PRU.<br />
<br />
<span style="color:red">Where is uio_pruss?</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
beagle$ '''git clone git://github.com/millerap/AM335x_PRU_BeagleBone'''<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE=""'''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
beagle$ '''cd ../utils/pasm_source'''<br />
beagle$ '''./linuxbuild'''<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
beagle$ '''cd ../../'''<br />
beagle$ '''make CROSS_COMPILE=""'''<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
beagle$ '''cd bin'''<br />
beagle$ '''make'''<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
beagle$ '''./blinker'''<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't do the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(DID THIS:Note that this is /bin/blinker.p. WORKING ON THIS: Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
In the assembly file blinker.p:<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS <span style="color:red">(What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) through a speaker to ground. The output produced will be an 880Hz sin wave approximation.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a rout you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=187250ECE497 BeagleBone PRU2012-11-03T01:41:56Z<p>Popenhjc: /* How the Assembly Code Works */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
00 Highlights<br />
00 Theory of Operation<br />
00 Work Breakdown<br />
00 Future Work<br />
00 Conclusions<br />
00 Demo<br />
00 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU, Processor Unit, <span style="color:red">(DID THIS:Define PRU)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The PRU is a part of the processor that runs at 200MHz (5ns per instruction), and is separate from the operating system all together, making it more efficient at accessing I/O pins. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer. Also, if you do not do this instruction before following the rest of the instructions you may run into segmentation faults when trying to initialize the PRU.<br />
<br />
<span style="color:red">Where is uio_pruss?</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
beagle$ '''git clone git://github.com/millerap/AM335x_PRU_BeagleBone'''<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE=""'''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
beagle$ '''cd ../utils/pasm_source'''<br />
beagle$ '''./linuxbuild'''<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
beagle$ '''cd ../../'''<br />
beagle$ '''make CROSS_COMPILE=""'''<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
beagle$ '''cd bin'''<br />
beagle$ '''make'''<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
beagle$ '''./blinker'''<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't do the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(Note that this is /bin/blinker.p. Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
In the assembly file blinker.p:<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS <span style="color:red">(What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) through a speaker to ground. The output produced will be an 880Hz sin wave approximation.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a rout you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=187244ECE497 BeagleBone PRU2012-11-03T01:38:24Z<p>Popenhjc: /* Building and Running the GPIO_PWM_PRU Example */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
00 Highlights<br />
00 Theory of Operation<br />
00 Work Breakdown<br />
00 Future Work<br />
00 Conclusions<br />
00 Demo<br />
00 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU, Processor Unit, <span style="color:red">(DID THIS:Define PRU)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The PRU is a part of the processor that runs at 200MHz (5ns per instruction), and is separate from the operating system all together, making it more efficient at accessing I/O pins. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer. Also, if you do not do this instruction before following the rest of the instructions you may run into segmentation faults when trying to initialize the PRU.<br />
<br />
<span style="color:red">Where is uio_pruss?</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
beagle$ '''git clone git://github.com/millerap/AM335x_PRU_BeagleBone'''<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE=""'''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
beagle$ '''cd ../utils/pasm_source'''<br />
beagle$ '''./linuxbuild'''<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
beagle$ '''cd ../../'''<br />
beagle$ '''make CROSS_COMPILE=""'''<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
beagle$ '''cd bin'''<br />
beagle$ '''make'''<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
beagle$ '''./blinker'''<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't do the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(Note that this is /bin/blinker.p. Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS <span style="color:red">(What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) through a speaker to ground. The output produced will be an 880Hz sin wave approximation.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a rout you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=187232ECE497 BeagleBone PRU2012-11-03T01:35:27Z<p>Popenhjc: /* Executive Summary */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
00 Highlights<br />
00 Theory of Operation<br />
00 Work Breakdown<br />
00 Future Work<br />
00 Conclusions<br />
00 Demo<br />
00 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU, Processor Unit, <span style="color:red">(DID THIS:Define PRU)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The PRU is a part of the processor that runs at 200MHz (5ns per instruction), and is separate from the operating system all together, making it more efficient at accessing I/O pins. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer. Also, if you do not do this instruction before following the rest of the instructions you may run into segmentation faults when trying to initialize the PRU.<br />
<br />
<span style="color:red">Where is uio_pruss?</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
git clone git://github.com/millerap/AM335x_PRU_BeagleBone<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE=""'''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
beagle$ '''cd ../utils/pasm_source'''<br />
beagle$ '''./linuxbuild'''<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
beagle$ '''cd ../../'''<br />
beagle$ '''make CROSS_COMPILE=""'''<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
beagle$ '''cd bin'''<br />
beagle$ '''make'''<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
beagle$ '''./blinker'''<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't do the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(Note that this is /bin/blinker.p. Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS <span style="color:red">(What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) through a speaker to ground. The output produced will be an 880Hz sin wave approximation.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a rout you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=187226ECE497 BeagleBone PRU2012-11-03T01:35:00Z<p>Popenhjc: /* Executive Summary */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
00 Highlights<br />
00 Theory of Operation<br />
00 Work Breakdown<br />
00 Future Work<br />
00 Conclusions<br />
00 Demo<br />
00 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU, Processor Unit, <span style="color:red">(Define PUR)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The PRU is a part of the processor that runs at 200MHz (5ns per instruction), and is separate from the operating system all together, making it more efficient at accessing I/O pins. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer. Also, if you do not do this instruction before following the rest of the instructions you may run into segmentation faults when trying to initialize the PRU.<br />
<br />
<span style="color:red">Where is uio_pruss?</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
git clone git://github.com/millerap/AM335x_PRU_BeagleBone<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE=""'''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
beagle$ '''cd ../utils/pasm_source'''<br />
beagle$ '''./linuxbuild'''<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
beagle$ '''cd ../../'''<br />
beagle$ '''make CROSS_COMPILE=""'''<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
beagle$ '''cd bin'''<br />
beagle$ '''make'''<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
beagle$ '''./blinker'''<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't do the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(Note that this is /bin/blinker.p. Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS <span style="color:red">(What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) through a speaker to ground. The output produced will be an 880Hz sin wave approximation.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a rout you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=187220ECE497 BeagleBone PRU2012-11-03T01:32:31Z<p>Popenhjc: /* User Instructions */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
00 Highlights<br />
00 Theory of Operation<br />
00 Work Breakdown<br />
00 Future Work<br />
00 Conclusions<br />
00 Demo<br />
00 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU <span style="color:red">(Define PUR)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer. Also, if you do not do this instruction before following the rest of the instructions you may run into segmentation faults when trying to initialize the PRU.<br />
<br />
<span style="color:red">Where is uio_pruss?</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
git clone git://github.com/millerap/AM335x_PRU_BeagleBone<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE=""'''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
beagle$ '''cd ../utils/pasm_source'''<br />
beagle$ '''./linuxbuild'''<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
beagle$ '''cd ../../'''<br />
beagle$ '''make CROSS_COMPILE=""'''<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
beagle$ '''cd bin'''<br />
beagle$ '''make'''<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
beagle$ '''./blinker'''<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't do the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(Note that this is /bin/blinker.p. Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS <span style="color:red">(What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) through a speaker to ground. The output produced will be an 880Hz sin wave approximation.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a rout you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=187214ECE497 BeagleBone PRU2012-11-03T01:30:11Z<p>Popenhjc: /* Building and Running the GPIO_PWM_PRU Example */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
00 Highlights<br />
00 Theory of Operation<br />
00 Work Breakdown<br />
00 Future Work<br />
00 Conclusions<br />
00 Demo<br />
00 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU <span style="color:red">(Define PUR)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer.<br />
<br />
<span style="color:red">Where is uio_pruss?</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
git clone git://github.com/millerap/AM335x_PRU_BeagleBone<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE=""'''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
beagle$ '''cd ../utils/pasm_source'''<br />
beagle$ '''./linuxbuild'''<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
beagle$ '''cd ../../'''<br />
beagle$ '''make CROSS_COMPILE=""'''<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
beagle$ '''cd bin'''<br />
beagle$ '''make'''<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
beagle$ '''./blinker'''<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't do the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(Note that this is /bin/blinker.p. Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS <span style="color:red">(What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) through a speaker to ground. The output produced will be an 880Hz sin wave approximation.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a rout you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=187208ECE497 BeagleBone PRU2012-11-03T01:25:28Z<p>Popenhjc: /* User Instructions */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Grading ==<br />
I'm using the following template to grade. Each slot is 10 points.<br />
0 = Missing, 5=OK, 10=Wow!<br />
<br />
<pre style="color:red"><br />
09 Executive Summary (looks good)<br />
05 Installation Instructions (Can't find uio_pruss)<br />
05 User Instructions (Segmentation fault)<br />
00 Highlights<br />
00 Theory of Operation<br />
00 Work Breakdown<br />
00 Future Work<br />
00 Conclusions<br />
00 Demo<br />
00 Late<br />
Comments:<br />
<br />
Score: 00/100<br />
</pre><br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU <span style="color:red">(Define PUR)</span> of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: Some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summing circuit which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This can be activated anywhere on the BeagleBone and activates the PRU module in the kernel so that its memory and all of its components are accessible.<br />
<br />
Note: modprobe uio_pruss is BeagleBone specific and will not be found on a host computer.<br />
<br />
<span style="color:red">Where is uio_pruss?</span><br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
git clone git://github.com/millerap/AM335x_PRU_BeagleBone<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
beagle$ '''cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface'''<br />
beagle$ '''export CROSS_COMPILE='''<br />
beagle$ '''make'''<br />
<br />
<span style="color:red">Make the rest follow the above format.</span><br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
cd ../utils/pasm_source<br />
./linuxbuild<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
cd ../../<br />
make CROSS_COMPILE=""<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
cd bin<br />
make<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
./blinker<br />
<br />
<span style="color:red">I get a Segmentation fault, but I think that's because I can't to the modprobe</span><br />
<br />
== How the Assembly Code Works ==<br />
<span style="color:red">(Note that this is /bin/blinker.p. Could you add<br />
some comments to the file explaining things?)</span><br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 stores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<span style="color:red">This is an interesting section. Could you note here where in the manuals<br />
you found your information?</span><br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS <span style="color:red">(What's the difference between PRUSS and PRU?)</span> by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. <br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 is found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<span style="color:red">Thanks for the reference. It's a big help.)</span><br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
To build this example, follow the same procedure as before, and wire GPIO7 (Pin 42) through a speaker to ground. The output produced will be an 880Hz sin wave approximation.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a rout you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=186248ECE497 BeagleBone PRU2012-11-02T01:54:27Z<p>Popenhjc: /* Conclusions */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
Give two sentences telling what isn't working.<br />
*Actual PWM implementation<br />
<br />
End with a two sentence conclusion.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: A push button for input and, though not necessarily required, some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summer circui which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This activates the PRU so that its memory and all of its components are accessible.<br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
git clone git://github.com/millerap/AM335x_PRU_BeagleBone<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface<br />
make CROSS_COMPILE=""<br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
cd ../utils/pasm_source<br />
./linuxbuild<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
cd ../../<br />
make CROSS_COMPILE=""<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
cd bin<br />
make<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
./blinker<br />
<br />
== How the Assembly Code Works ==<br />
<br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 sores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. There is more information about using interrupts available in the '''Interrupts''' section below.<br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 iis found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
So, if you need precise timing, or more rapid access to a certain GPIO pin, this is a rout you might want to look into. There are a few suggestions listed above that might be interesting to see come out of using the PRU. However, if you do not require precisely timed events or faster access to GPIO pins, you might want to consider just using C on the main processor. Much of the information needed to access certain parts of the PRU and the hardware from the PRU is either very vague, or very difficult to dig up, and because the PRU is not widely used, it is difficult to find people that can offer information on the topic.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=186236ECE497 BeagleBone PRU2012-11-02T01:43:44Z<p>Popenhjc: /* User Instructions */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
Give two sentences telling what isn't working.<br />
*Actual PWM implementation<br />
<br />
End with a two sentence conclusion.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: A push button for input and, though not necessarily required, some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summer circui which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
This activates the PRU so that its memory and all of its components are accessible.<br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
git clone git://github.com/millerap/AM335x_PRU_BeagleBone<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface<br />
make CROSS_COMPILE=""<br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
cd ../utils/pasm_source<br />
./linuxbuild<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
cd ../../<br />
make CROSS_COMPILE=""<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
cd bin<br />
make<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
./blinker<br />
<br />
== How the Assembly Code Works ==<br />
<br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 sores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. There is more information about using interrupts available in the '''Interrupts''' section below.<br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 iis found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
Give some concluding thoughts about the project. Suggest some future additions that could make it even more interesting.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=186230ECE497 BeagleBone PRU2012-11-02T01:42:05Z<p>Popenhjc: /* Theory of Operation */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
Give two sentences telling what isn't working.<br />
*Actual PWM implementation<br />
<br />
End with a two sentence conclusion.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: A push button for input and, though not necessarily required, some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summer circui which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
git clone git://github.com/millerap/AM335x_PRU_BeagleBone<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface<br />
make CROSS_COMPILE=""<br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
cd ../utils/pasm_source<br />
./linuxbuild<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
cd ../../<br />
make CROSS_COMPILE=""<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
cd bin<br />
make<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
./blinker<br />
<br />
== How the Assembly Code Works ==<br />
<br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 sores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. There is more information about using interrupts available in the '''Interrupts''' section below.<br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 iis found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building and Running Sin_Approximation for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
Give some concluding thoughts about the project. Suggest some future additions that could make it even more interesting.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=186224ECE497 BeagleBone PRU2012-11-02T01:41:27Z<p>Popenhjc: /* Theory of Operation */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
Give two sentences telling what isn't working.<br />
*Actual PWM implementation<br />
<br />
End with a two sentence conclusion.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: A push button for input and, though not necessarily required, some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summer circui which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
git clone git://github.com/millerap/AM335x_PRU_BeagleBone<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface<br />
make CROSS_COMPILE=""<br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
cd ../utils/pasm_source<br />
./linuxbuild<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
cd ../../<br />
make CROSS_COMPILE=""<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
cd bin<br />
make<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
./blinker<br />
<br />
== How the Assembly Code Works ==<br />
<br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 sores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. There is more information about using interrupts available in the '''Interrupts''' section below.<br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 iis found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
Refer to How the C Code Works, How the Assembly Code Works, and Building the Sine for more details.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
Give some concluding thoughts about the project. Suggest some future additions that could make it even more interesting.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=186218ECE497 BeagleBone PRU2012-11-02T01:39:44Z<p>Popenhjc: /* Theory of Operation */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
Give two sentences telling what isn't working.<br />
*Actual PWM implementation<br />
<br />
End with a two sentence conclusion.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: A push button for input and, though not necessarily required, some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summer circui which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
git clone git://github.com/millerap/AM335x_PRU_BeagleBone<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface<br />
make CROSS_COMPILE=""<br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
cd ../utils/pasm_source<br />
./linuxbuild<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
cd ../../<br />
make CROSS_COMPILE=""<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
cd bin<br />
make<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
./blinker<br />
<br />
== How the Assembly Code Works ==<br />
<br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 sores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. There is more information about using interrupts available in the '''Interrupts''' section below.<br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 iis found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
<br />
== Theory of Operation ==<br />
<br />
In the first examples, you can see that the GPIO can be toggled on and off simply by editing locations in memory from the PRU. You simply set how long you want the LED to be on and how long you want the LED to be off, and delay the time between on and off to create the desired latency.<br />
<br />
In the following example, the premise changes slightly. The operation of our code is simple, the PRU offers precise instruction delay of 5ns per instruction. With this we were able to create a delay of instructions that would be a sampling frequency, and in between this delay of instructions we were able to have a set amount of instructions for which the GPIO was on, and a set amount where the GPIO was off. Also, memory could be set from a C program, and then read by the PRU. This came in handy when approximating the sine wave because C offers the math.h header file that has the sin function included and can give approximate numbers to send to the PRU. The amount of delay on and off could be set dynamically every time the loop began by reading the next bit of data stored in memory, thus creating an average voltage that approximated a playable sine wave.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
Give some concluding thoughts about the project. Suggest some future additions that could make it even more interesting.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=186188ECE497 BeagleBone PRU2012-11-02T01:26:16Z<p>Popenhjc: /* Future Work */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
Give two sentences telling what isn't working.<br />
*Actual PWM implementation<br />
<br />
End with a two sentence conclusion.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: A push button for input and, though not necessarily required, some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summer circui which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
git clone git://github.com/millerap/AM335x_PRU_BeagleBone<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface<br />
make CROSS_COMPILE=""<br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
cd ../utils/pasm_source<br />
./linuxbuild<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
cd ../../<br />
make CROSS_COMPILE=""<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
cd bin<br />
make<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
./blinker<br />
<br />
== How the Assembly Code Works ==<br />
<br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 sores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. There is more information about using interrupts available in the '''Interrupts''' section below.<br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 iis found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
<br />
== Theory of Operation ==<br />
<br />
Give a high level overview of the structure of your software. Are you using GStreamer? Show a diagram of the pipeline. Are you running multiple tasks? Show what they do and how they interact.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
For future work there are a few interesting features that we were not able to get to due to time limit and the amount of research needed to begin with. First, we found difficulty in accessing things such as the PWM and analog in ports. These could be further explored given the documents that we have dug up, and some exploration on Google. Second, we wanted to read audio from the analog input and adjust the duty_cycle of the PWM accordingly to have approximate audio output which is the next step to what we have done here. Third, we wanted to explore interrupts on the PRU, but were unable to find enough documentation to get an example working. So, PWM, analog in, audio capabilities, and interrupts are the possible things to look into. Also, any other time critical operations can be explored further with the BeagleBone PRU because it has a delay of exactly 5ns for every instruction.<br />
<br />
== Conclusions ==<br />
<br />
Give some concluding thoughts about the project. Suggest some future additions that could make it even more interesting.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=186182ECE497 BeagleBone PRU2012-11-02T01:17:55Z<p>Popenhjc: /* Building and Running the Sin_Approximation Example */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
Give two sentences telling what isn't working.<br />
*Actual PWM implementation<br />
<br />
End with a two sentence conclusion.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: A push button for input and, though not necessarily required, some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summer circui which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
git clone git://github.com/millerap/AM335x_PRU_BeagleBone<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface<br />
make CROSS_COMPILE=""<br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
cd ../utils/pasm_source<br />
./linuxbuild<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
cd ../../<br />
make CROSS_COMPILE=""<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
cd bin<br />
make<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
./blinker<br />
<br />
== How the Assembly Code Works ==<br />
<br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 sores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. There is more information about using interrupts available in the '''Interrupts''' section below.<br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 iis found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 and turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t/fs) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t/fs)<br />
.<br />
duty_percent = sin(2*PI*f*t/fs)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated due to precise measuring for the sampling frequency, but because this is being run on PRU0, DRAM starts at 0x0. If you feel that you need to change the sampling frequency at any point here's a bit on how to calculate the amount of instructions you need to delay in total.<br />
<br />
sample_period = 1/sample_frequency<br />
.<br />
sample_period/(number_instructions_delay_loop*5ns) = total_number_instructions_to_delay_per_period<br />
.<br />
Then count the amount of instructions before or after the loop and nock off that many instructions to delay. This will of course<br />
need to be accounted for in the duty cycle, and some sampling frequencies may not offer all duty_percentages.<br />
.<br />
duty_percent = number_on_instruction_delay/number_off_instruction_delay<br />
.<br />
number_on_instruction_delay + number_off_instruction_delay = total_number_instructions_to_delay_per_period<br />
.<br />
->(duty_percent + 1)*total_number_instruction_to_delay_per_period = number_on_instruction_delay<br />
<br />
Of course a few other numbers will have to be adjusted, such as the number of samples to read from memory. Because it has to read in 4 bytes of data, this will end up being:<br />
total_number_instructions_to_delay_per_period*4+4<br />
This way the number will reset to 0 as soon as it goes over the limit of memory to be read.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
<br />
== Theory of Operation ==<br />
<br />
Give a high level overview of the structure of your software. Are you using GStreamer? Show a diagram of the pipeline. Are you running multiple tasks? Show what they do and how they interact.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
Suggest addition things that could be done with this project.<br />
<br />
== Conclusions ==<br />
<br />
Give some concluding thoughts about the project. Suggest some future additions that could make it even more interesting.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=186164ECE497 BeagleBone PRU2012-11-02T00:58:41Z<p>Popenhjc: /* Highlights */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
Give two sentences telling what isn't working.<br />
*Actual PWM implementation<br />
<br />
End with a two sentence conclusion.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: A push button for input and, though not necessarily required, some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summer circui which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
git clone git://github.com/millerap/AM335x_PRU_BeagleBone<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface<br />
make CROSS_COMPILE=""<br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
cd ../utils/pasm_source<br />
./linuxbuild<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
cd ../../<br />
make CROSS_COMPILE=""<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
cd bin<br />
make<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
./blinker<br />
<br />
== How the Assembly Code Works ==<br />
<br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 sores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<br />
The blinker.c file is a direct port of the PRU initialization code from TI. Putting the two side by side, the only difference between the two is the name of the bin file that is used for the exec function.<br />
<br />
The code first initializes the PRUSS by allocating memory for it using the prussdrv_init() function. It then initializes memory mapping for the PRU using the prussdrv_open() function. All of the intc functions are used for interrupt communication between the ARM and the PRU. This code is not utilized by the examples in this page. There is more information about using interrupts available in the '''Interrupts''' section below.<br />
<br />
Similar to the exec function in C, the prussdrv_exec_program () function overlays the IRAM (Instruction RAM) portion of the PRUSS with the bin file that was created from blinker.p. The first field of prussdrv_exec_program needs a PRU number, which is either 0 or 1 depending on which PRU core is being used. In this case, PRU0 is executing blinker.bin. The second field is the path to the bin that will be put into the PRU's IRAM.<br />
<br />
The next section waits on event 0 from the PRU to signal the C program that it has completed its execution. This, again, was not implemented, but writing the appropriate bit to the r31 register would cause the C program to continue. As it is, the program stalls at this point until SIGINT is received. <br />
<br />
If the correct event were received, the next function is used to halt the PRU's execution then it would release the PRUSS clocks and disable the prussdrv module.<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 iis found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<br />
== Building and Running the Sin_Approximation Example ==<br />
<br />
This example uses a modified version of the GPIO_PWM_PRU example to change the duty cycle every period such that the average voltage approximates a sin wave. Navigate to the pwm_sin directory and take a look at the C code. Its nearly identical to the previous code except for a few small differences. The first difference is that it opens and edits two files to export GPIO0_7 turn it into an output.<br />
<br />
The next change is that the prussdrv_pru_write_memory command discussed above is used to push an array containing duty cycles onto the DRAM for PRU0. The duty cycles are approximated using a the sin function from the math.h header file. Here is why:<br />
(VCC*(on_time)+0*(off_time)) <br />
---------------------------- = VCC*duty_percent<br />
period<br />
.<br />
VCC*duty_percent = Va<br />
.<br />
Va = VCC*Sin(2*PI*f*t) <br />
.<br />
VCC*duty_percent = VCC*sin(2*PI*f*t)<br />
.<br />
duty_percent = sin(2*PI*f*t)<br />
<br />
Looking at the assembly code, we see a similar PWM control as before, but this time it is reading the duty cycles from memory. The coding to do this is a little more complicated, but because this is being run on PRU0, DRAM starts at 0x0.<br />
<br />
== Highlights ==<br />
<br />
During the project we were able to get an approximated 880Hz sine wave to play by changing a pulse width modulation duty cycle to approximate a dc voltage output, as you would with an LED dimmer. You can view this in action along with a helpful tip in the youtube video: <br />
<br />
*[http://www.youtube.com/watch?v=6ytju0bkkiQ&feature=youtu.be PWM Demo].<br />
<br />
== Theory of Operation ==<br />
<br />
Give a high level overview of the structure of your software. Are you using GStreamer? Show a diagram of the pipeline. Are you running multiple tasks? Show what they do and how they interact.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
Suggest addition things that could be done with this project.<br />
<br />
== Conclusions ==<br />
<br />
Give some concluding thoughts about the project. Suggest some future additions that could make it even more interesting.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=185960ECE497 BeagleBone PRU2012-10-30T22:25:30Z<p>Popenhjc: /* Installation Instructions */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
Give two sentences telling what isn't working.<br />
*Actual PWM implementation<br />
<br />
End with a two sentence conclusion.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: A push button for input and, though not necessarily required, some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summer circui which will require an Op-Amp, a 2kohm and 1kohm resistor, and 2 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown where V1 is your pwm voltage, V2 is your -1.65V bias, and V3 is unused:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
git clone git://github.com/millerap/AM335x_PRU_BeagleBone<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface<br />
make CROSS_COMPILE=""<br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
cd ../utils/pasm_source<br />
./linuxbuild<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
cd ../../<br />
make CROSS_COMPILE=""<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
cd bin<br />
make<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
./blinker<br />
<br />
== How the Assembly Code Works ==<br />
<br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 sores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<br />
'''FILL THIS IN LATER'''<br />
<br />
== Sending an array to the PRU ==<br />
<br />
The initialization code provided by TI has a handy function for passing an array to the PRU. Each of the PRU cores have an 8kb data ram associated with them, and that data space can be populated from an external C program. The next example will make use of this function to pass different PWM duty cycles to the PRU. This will be largely based around the following function:<br />
<br />
int prussdrv_pru_write_memory (unsigned int pru_ram_id, unsigned int wordoffset, unsigned int *memarea, unsigned int bytelength); <br />
<br />
pru_ram_id can take on one of 4 values, and are as follows:<br />
<br />
PRUSS0_PRU0_DATARAM <br />
PRUSS0_PRU1_DATARAM <br />
PRUSS0_PRU0_IRAM <br />
PRUSS0_PRU1_IRAM<br />
<br />
Here, each of the PRUs have both an Instruction RAM and a DATARAM section. DATARAM for PRU0 is found in the memory locations 0x0 - 0x2000, and DATARAM for PRU1 iis found in the memory locations 0x2000 - 0x4000. <br />
<br />
wordoffset is an offset in words (4 bytes) from the base memory location, pru_ram_id. <br />
<br />
memarea is a pointer to an array of unsigned ints (also 4 bytes) that will be passed onto the PRU.<br />
<br />
bytelength is the number of bytes to write to the PRU.<br />
<br />
For more information on using C to initialize the PRU visit [http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem TI's PRU Linux Application Loader API Guide]<br />
<br />
== Highlights ==<br />
<br />
Here is where you brag about what your project can do.<br />
<br />
Include a [http://www.youtube.com/ YouTube] demo.<br />
<br />
== Theory of Operation ==<br />
<br />
Give a high level overview of the structure of your software. Are you using GStreamer? Show a diagram of the pipeline. Are you running multiple tasks? Show what they do and how they interact.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
Suggest addition things that could be done with this project.<br />
<br />
== Conclusions ==<br />
<br />
Give some concluding thoughts about the project. Suggest some future additions that could make it even more interesting.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=185726ECE497 BeagleBone PRU2012-10-30T03:44:28Z<p>Popenhjc: /* Finding Where to Access Things */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
Give two sentences telling what isn't working.<br />
*Actual PWM implementation<br />
<br />
End with a two sentence conclusion.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: A push button for input and, though not necessarily required, some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summer circui which will require an Op-Amp, a 2kohm and 1kohm resistor, and 3 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
memory location: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
memory location: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
memory location: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
git clone git://github.com/millerap/AM335x_PRU_BeagleBone<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface<br />
make CROSS_COMPILE=""<br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
cd ../utils/pasm_source<br />
./linuxbuild<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
cd ../../<br />
make CROSS_COMPILE=""<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
cd bin<br />
make<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
./blinker<br />
<br />
== How the Assembly Code Works ==<br />
<br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 sores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<br />
'''FILL THIS IN LATER'''<br />
<br />
== Highlights ==<br />
<br />
Here is where you brag about what your project can do.<br />
<br />
Include a [http://www.youtube.com/ YouTube] demo.<br />
<br />
== Theory of Operation ==<br />
<br />
Give a high level overview of the structure of your software. Are you using GStreamer? Show a diagram of the pipeline. Are you running multiple tasks? Show what they do and how they interact.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
Suggest addition things that could be done with this project.<br />
<br />
== Conclusions ==<br />
<br />
Give some concluding thoughts about the project. Suggest some future additions that could make it even more interesting.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=185720ECE497 BeagleBone PRU2012-10-30T03:42:36Z<p>Popenhjc: /* Finding Where to Access Things */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
Give two sentences telling what isn't working.<br />
*Actual PWM implementation<br />
<br />
End with a two sentence conclusion.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: A push button for input and, though not necessarily required, some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summer circui which will require an Op-Amp, a 2kohm and 1kohm resistor, and 3 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
== Finding Where to Access Things ==<br />
<br />
There are many locations in memory that are needed to access specific I/O pins on the BeagleBone. Some of these I/O pins can be found here:<br />
*[http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf SPRUH73f pdf]<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
name: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
name: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
name: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
git clone git://github.com/millerap/AM335x_PRU_BeagleBone<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface<br />
make CROSS_COMPILE=""<br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
cd ../utils/pasm_source<br />
./linuxbuild<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
cd ../../<br />
make CROSS_COMPILE=""<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
cd bin<br />
make<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
./blinker<br />
<br />
== How the Assembly Code Works ==<br />
<br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 sores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<br />
'''FILL THIS IN LATER'''<br />
<br />
== Highlights ==<br />
<br />
Here is where you brag about what your project can do.<br />
<br />
Include a [http://www.youtube.com/ YouTube] demo.<br />
<br />
== Theory of Operation ==<br />
<br />
Give a high level overview of the structure of your software. Are you using GStreamer? Show a diagram of the pipeline. Are you running multiple tasks? Show what they do and how they interact.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
Suggest addition things that could be done with this project.<br />
<br />
== Conclusions ==<br />
<br />
Give some concluding thoughts about the project. Suggest some future additions that could make it even more interesting.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=185714ECE497 BeagleBone PRU2012-10-30T03:24:52Z<p>Popenhjc: /* Installation Instructions */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
Give two sentences telling what isn't working.<br />
*Actual PWM implementation<br />
<br />
End with a two sentence conclusion.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: A push button for input and, though not necessarily required, some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summer circui which will require an Op-Amp, a 2kohm and 1kohm resistor, and 3 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
== Finding Where to Access Things ==<br />
<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
name: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
name: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
name: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
git clone git://github.com/millerap/AM335x_PRU_BeagleBone<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface<br />
make CROSS_COMPILE=""<br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
cd ../utils/pasm_source<br />
./linuxbuild<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
cd ../../<br />
make CROSS_COMPILE=""<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
cd bin<br />
make<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
./blinker<br />
<br />
== How the Assembly Code Works ==<br />
<br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 sores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<br />
'''FILL THIS IN LATER'''<br />
<br />
== Highlights ==<br />
<br />
Here is where you brag about what your project can do.<br />
<br />
Include a [http://www.youtube.com/ YouTube] demo.<br />
<br />
== Theory of Operation ==<br />
<br />
Give a high level overview of the structure of your software. Are you using GStreamer? Show a diagram of the pipeline. Are you running multiple tasks? Show what they do and how they interact.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
Suggest addition things that could be done with this project.<br />
<br />
== Conclusions ==<br />
<br />
Give some concluding thoughts about the project. Suggest some future additions that could make it even more interesting.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=File:SummerCicuit.png&diff=185708File:SummerCicuit.png2012-10-30T03:24:20Z<p>Popenhjc: </p>
<hr />
<div></div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=185702ECE497 BeagleBone PRU2012-10-30T03:24:08Z<p>Popenhjc: /* Installation Instructions */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
Give two sentences telling what isn't working.<br />
*Actual PWM implementation<br />
<br />
End with a two sentence conclusion.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: A push button for input and, though not necessarily required, some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summer circui which will require an Op-Amp, a 20kohm and 10kohm resistor, and 3 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown:<br />
<br />
[[File:SummerCicuit.png|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
== Finding Where to Access Things ==<br />
<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
name: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
name: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
name: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
git clone git://github.com/millerap/AM335x_PRU_BeagleBone<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface<br />
make CROSS_COMPILE=""<br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
cd ../utils/pasm_source<br />
./linuxbuild<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
cd ../../<br />
make CROSS_COMPILE=""<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
cd bin<br />
make<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
./blinker<br />
<br />
== How the Assembly Code Works ==<br />
<br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 sores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<br />
'''FILL THIS IN LATER'''<br />
<br />
== Highlights ==<br />
<br />
Here is where you brag about what your project can do.<br />
<br />
Include a [http://www.youtube.com/ YouTube] demo.<br />
<br />
== Theory of Operation ==<br />
<br />
Give a high level overview of the structure of your software. Are you using GStreamer? Show a diagram of the pipeline. Are you running multiple tasks? Show what they do and how they interact.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
Suggest addition things that could be done with this project.<br />
<br />
== Conclusions ==<br />
<br />
Give some concluding thoughts about the project. Suggest some future additions that could make it even more interesting.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=185696ECE497 BeagleBone PRU2012-10-30T03:23:09Z<p>Popenhjc: /* Installation Instructions */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
Give two sentences telling what isn't working.<br />
*Actual PWM implementation<br />
<br />
End with a two sentence conclusion.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
*https://github.com/millerap/AM335x_PRU_BeagleBone<br />
*Hardware: A push button for input and, though not necessarily required, some LEDs for messing with the GPIO pins and a speaker for listening to PWM approximated sine wave.<br />
<br />
Note: When implementing the pulse width modulation, you may want to bias the wave around 0V instead of 1.65V. If this is the case, you may want to use a summer circui which will require an Op-Amp, a 20kohm and 10kohm resistor, and 3 resistors of the same value (Higher values preferable for lower power consumption), which will need to be connected as shown:<br />
<br />
[[File:Non-Inverting Summer Cicuit|Non-Inverting Summer Circuit]]<br />
<br />
Unless you desire a louder output than capable with simple I/O pins, there is no additional hardware needed.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
== Finding Where to Access Things ==<br />
<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
name: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
name: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
name: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
git clone git://github.com/millerap/AM335x_PRU_BeagleBone<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface<br />
make CROSS_COMPILE=""<br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
cd ../utils/pasm_source<br />
./linuxbuild<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
cd ../../<br />
make CROSS_COMPILE=""<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
cd bin<br />
make<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
./blinker<br />
<br />
== How the Assembly Code Works ==<br />
<br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 sores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<br />
'''FILL THIS IN LATER'''<br />
<br />
== Highlights ==<br />
<br />
Here is where you brag about what your project can do.<br />
<br />
Include a [http://www.youtube.com/ YouTube] demo.<br />
<br />
== Theory of Operation ==<br />
<br />
Give a high level overview of the structure of your software. Are you using GStreamer? Show a diagram of the pipeline. Are you running multiple tasks? Show what they do and how they interact.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
Suggest addition things that could be done with this project.<br />
<br />
== Conclusions ==<br />
<br />
Give some concluding thoughts about the project. Suggest some future additions that could make it even more interesting.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=185690ECE497 BeagleBone PRU2012-10-30T03:01:14Z<p>Popenhjc: /* Executive Summary */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Executive Summary ==<br />
<br />
For this project, the objective is to explore the PRU of the BeagleBone, looking at both the limitations of implementation and how to implement tasks such as handling pulse width modulation. The project is more research intensive, as opposed to implementation intensive, and serves to bring together all of the sources found on the BeagleBone's PRU into one abbreviated document with examples of how to use it and the potential for extra projects. The ultimate goal here is to walk through step by step leading to the representation of a sinusoidal wave using pulse width modulation accessed from PRU and play the produced wave through a speaker.<br />
<br />
As of now we have gathered information about the PRU, found memory locations that can be edited on the PRU and in C so that we can interact with functions outside of the PRU's capabilities, and implemented code on the PRU that simulates a pulse width modulation on a GPIO pin.<br />
<br />
Give two sentences telling what isn't working.<br />
*Actual PWM implementation<br />
<br />
End with a two sentence conclusion.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
* [https://github.com/millerap/AM335x_PRU_BeagleBone].<br />
* Include any additional packages installed via '''opkg'''.<br />
* Include kernel mods.<br />
* If there is extra hardware needed, include links to where it can be obtained.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
== Finding Where to Access Things ==<br />
<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
name: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
name: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
name: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
git clone git://github.com/millerap/AM335x_PRU_BeagleBone<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface<br />
make CROSS_COMPILE=""<br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
cd ../utils/pasm_source<br />
./linuxbuild<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
cd ../../<br />
make CROSS_COMPILE=""<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
cd bin<br />
make<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
./blinker<br />
<br />
== How the Assembly Code Works ==<br />
<br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 sores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<br />
'''FILL THIS IN LATER'''<br />
<br />
== Highlights ==<br />
<br />
Here is where you brag about what your project can do.<br />
<br />
Include a [http://www.youtube.com/ YouTube] demo.<br />
<br />
== Theory of Operation ==<br />
<br />
Give a high level overview of the structure of your software. Are you using GStreamer? Show a diagram of the pipeline. Are you running multiple tasks? Show what they do and how they interact.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
Suggest addition things that could be done with this project.<br />
<br />
== Conclusions ==<br />
<br />
Give some concluding thoughts about the project. Suggest some future additions that could make it even more interesting.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=184838ECE497 BeagleBone PRU2012-10-26T18:02:38Z<p>Popenhjc: </p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Executive Summary ==<br />
<br />
Give two sentence intro to the project.<br />
<br />
Give two sentences telling what works.<br />
<br />
Give two sentences telling what isn't working.<br />
<br />
End with a two sentence conclusion.<br />
<br />
The sentence count is approximate and only to give an idea of the expected length.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
* [https://github.com/millerap/AM335x_PRU_BeagleBone].<br />
* Include any additional packages installed via '''opkg'''.<br />
* Include kernel mods.<br />
* If there is extra hardware needed, include links to where it can be obtained.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
== Finding Where to Access Things ==<br />
<br />
<br />
The following are not found in the file, but are good addresses to know when accessing MUXs:<br />
<br />
gpmc_a2:<br />
memory location: gpmc_a2.gpio1_18 (0x44e10848/0x848 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a2 | gmii2_txd3 | rgmii2_td3 | mmc2_dat1 | gpmc_a18 | pr1_mii1_txd2 | ehrpwm1A | gpio1_18<br />
<br />
gpmc_a3:<br />
name: gpmc_a3.gpio1_19 (0x44e1084c/0x84c = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_a3 | gmii2_txd2 | rgmii2_td2 | mmc2_dat2 | gpmc_a19 | pr1_mii1_txd1 | ehrpwm1B | gpio1_19<br />
<br />
gpmc_ad8:<br />
name: gpmc_ad8.gpio0_22 (0x44e10820/0x820 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad8 | lcd_data23 | mmc1_dat0 | mmc2_dat4 | ehrpwm2A | pr1_mii_mt0_clk | NA | gpio0_22<br />
<br />
gpmc_ad9:<br />
name: gpmc_ad9.gpio0_23 (0x44e10824/0x824 = 0x0027), b NA, t NA<br />
mode: OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE7<br />
signals: gpmc_ad9 | lcd_data22 | mmc1_dat1 | mmc2_dat5 | ehrpwm2B | pr1_mii0_col | NA | gpio0_23<br />
<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
git clone git://github.com/millerap/AM335x_PRU_BeagleBone<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface<br />
make CROSS_COMPILE=""<br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
cd ../utils/pasm_source<br />
./linuxbuild<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
cd ../../<br />
make CROSS_COMPILE=""<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
cd bin<br />
make<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
./blinker<br />
<br />
== How the Assembly Code Works ==<br />
<br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 sores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<br />
'''FILL THIS IN LATER'''<br />
<br />
== Highlights ==<br />
<br />
Here is where you brag about what your project can do.<br />
<br />
Include a [http://www.youtube.com/ YouTube] demo.<br />
<br />
== Theory of Operation ==<br />
<br />
Give a high level overview of the structure of your software. Are you using GStreamer? Show a diagram of the pipeline. Are you running multiple tasks? Show what they do and how they interact.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
Suggest addition things that could be done with this project.<br />
<br />
== Conclusions ==<br />
<br />
Give some concluding thoughts about the project. Suggest some future additions that could make it even more interesting.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=184832ECE497 BeagleBone PRU2012-10-26T17:37:35Z<p>Popenhjc: /* Installation Instructions */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Executive Summary ==<br />
<br />
Give two sentence intro to the project.<br />
<br />
Give two sentences telling what works.<br />
<br />
Give two sentences telling what isn't working.<br />
<br />
End with a two sentence conclusion.<br />
<br />
The sentence count is approximate and only to give an idea of the expected length.<br />
<br />
== Installation Instructions ==<br />
<br />
The Git Hub is on the following link: <br />
<br />
* [https://github.com/millerap/AM335x_PRU_BeagleBone].<br />
* Include any additional packages installed via '''opkg'''.<br />
* Include kernel mods.<br />
* If there is extra hardware needed, include links to where it can be obtained.<br />
<br />
== User Instructions ==<br />
<br />
'''Always''' run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
== Building and Running the GPIO_PWM_PRU Example ==<br />
<br />
This example is located in the GPIO_PWM_PRU directory in the AM335x_PRU_BeagleBone git repository, and can be pulled with the following:<br />
git clone git://github.com/millerap/AM335x_PRU_BeagleBone<br />
<br />
This example uses the gpio and delay loops to approximate a PWM using the user LEDs on the BeagleBone. It is based on an example provided by Lyren Brown and documented by boxysean at <br />
http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/<br />
<br />
In GPIO_PWM_PRU all of the complicated Makefiles and directories used to make a multitude of examples at once have been stripped away to allow the user to compile one individual program that will run on the PRU.<br />
<br />
The readme.txt file in the GPIO_PWM_PRU directory provides a walkthrough for compiling and running blinker on the BeagleBone.<br />
<br />
The first step to compiling a program for the PRU is to make sure prussdrv.c is made and up to date. This is the file provided by TI that contains all of the C functions that allow for communication with the PRU. To do this, do the following:<br />
cd <directory>/AM335x_PRU_BeagleBone/GPIO_PWM_PRU/interface<br />
make CROSS_COMPILE=""<br />
<br />
CROSS_COMPILE is specified as "" because this is running on the BeagleBone itself and the Makefile is setup to defaultly cross compile the code from another linux machine.<br />
<br />
Once this is completed, the pasm_source must be set for the BeagleBone's linux operating system:<br />
cd ../utils/pasm_source<br />
./linuxbuild<br />
<br />
'''Note: The above instructions need to be done for every time the BeagleBone boots up and these directories should be included with any code that you write for the PRU'''<br />
<br />
Now, the BeagleBone is ready to compile the example code. Navigate to the example's root directory again:<br />
cd ../../<br />
make CROSS_COMPILE=""<br />
<br />
This will compile the blinker.c file and output it to the bin folder. After this point, the assembly file needs to be compiled into a .bin file. This is done in the bin folder.<br />
cd bin<br />
make<br />
<br />
Now, there should be a blinker.bin file in the folder. running the blinker executabile will put the blinker.bin file on the PRU and start it running. Use the following:<br />
./blinker<br />
<br />
== How the Assembly Code Works ==<br />
<br />
//in the overview talk about the period being 5ns<br />
<br />
Registers r5 and r6 are the duty_cycle and period respectively. The duty_cycle is a number smaller than the period that the accumulator r4 counts up to before setting the output to zero. When the r4 = period, r4 resets and the output is set to 1. This gives the following for for OnTime and OffTime.<br />
<br />
SecondsPerCycle = 5*10^-9<br />
OnCycles = 2 + (duty_cycle)*3 + 2<br />
OffCycles = 2 + (period - duty_cycle)*3 - 1 + 2<br />
TotalCycles = 7 + (period)*3<br />
<br />
These equations can be used to create a very exact PWM output by setting duty_cycle and period to the values you wish to use. The code that was compiled and run above has a period of about a second and a duty cycle of about 50%.<br />
<br />
There are a few macros defined at the beginning of the program. These macros are the location of GPIO1's memory space, the location of its set registers and the location of its clear registers. The BeagleBone's GPIO pins must be turned off and on using these two different memory locations. Setting the set register to 0 does not turn off its respective GPIO pin.<br />
<br />
r2 stores the value that is going to be written to either set or clear gpio. r3 sores the address that r2 will be written to. within the first 3 lines of PWM_ON these values are set such that r2 will turn on the user LEDs. The instruction that actually turns it on is SBBO. This takes the value of r2 and writes it to memory location r3 with an offset of 0.<br />
<br />
Here is a complete guide to the [http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU's Assembly Instructions] from TI<br />
<br />
== How the C Code Works ==<br />
<br />
'''FILL THIS IN LATER'''<br />
<br />
== Highlights ==<br />
<br />
Here is where you brag about what your project can do.<br />
<br />
Include a [http://www.youtube.com/ YouTube] demo.<br />
<br />
== Theory of Operation ==<br />
<br />
Give a high level overview of the structure of your software. Are you using GStreamer? Show a diagram of the pipeline. Are you running multiple tasks? Show what they do and how they interact.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
Suggest addition things that could be done with this project.<br />
<br />
== Conclusions ==<br />
<br />
Give some concluding thoughts about the project. Suggest some future additions that could make it even more interesting.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=EBC_Exercise_16_git_Workshop_Version&diff=184550EBC Exercise 16 git Workshop Version2012-10-25T17:37:25Z<p>Popenhjc: /* Other Errors while using git */</p>
<hr />
<div>[[Category:ECE497]]<br />
{{YoderHead}}<br />
<br />
'''[[git]]''' is a distributed revision control system with an emphasis on being fast. It was initially designed and developed by Linus Torvalds for Linux kernel development. The purpose of this lab is to get hands on experience with git to learn how it works and how to use it.<br />
<br />
This is the workshop version of the exercise. It assumes git is already installed and the repository is already cloned.<br />
<br />
Much of the material here has come from [http://progit.org/book/ Pro Git]. We'll be using [https://github.com/ github] to practice '''git'''ting.<br />
<br />
== Update the git repository ==<br />
<br />
Always before you start work on a git project, make sure you have the current version from github.<br />
<br />
beagle$ '''cd ~/gitLearn'''<br />
beagle$ '''git pull'''<br />
<br />
== Play with git locally ==<br />
<br />
* Edit <code>helloWorld.c</code> and add a <code>printf</code> with your name on it.<br />
* stage and commit <code>helloWorld.c</code>. You may have to merge. Keep everyone else's name in the file.<br />
* Push it to the repository<br />
<br />
Here's how it is done.<br />
<br />
beagle$ '''gedit helloWorld.c'''<br />
beagle$ '''gcc helloWorld.c''' Make sure the file compiles and runs OK.<br />
beagle$ '''./a.out''<br />
beagle$ '''git status''' What's the status of helloWorld?<br />
beagle$ '''git add'''<br />
beagle$ '''git status''' What's the status now?<br />
beagle$ '''git commit -m '''"''Put your own comment here''"<br />
beagle$ '''git status''' What's the status now?<br />
beagle$ '''git push''' This will send your repository to gethub<br />
<br />
If the last '''git push''' give you error, it means someone else has changed the file on the repository and you need to get the new version before you can push. Do this<br />
beagle$ '''git pull'''<br />
<br />
git will try to merge the new version of the file with your version. It it's successful all you have to do is.<br />
<br />
beagle$ '''gedit helloWord.c''' Do this to be sure the file looks OK.<br />
beagle$ '''gcc helloWorld.c'''<br />
beagle$ '''./a.out''' If it doesn't compile edit and fix it.<br />
beagle$ '''git add'''<br />
beagle$ '''git commit -m "''Another descriptive comment here''"'''<br />
beagle$ '''git push'''<br />
<br />
If you aren't lucky, git won't be able to merge and it will tell you so. Edit the file as above and look for '''<<<<<<''' , this will mark the places git didn't know how to handle. Fix them and remove the lines with '''<<<<<<''' and repeat the steps above.<br />
<br />
If you are lucky, no one else has changed the file while you were working on it. If not, repeat the above steps.<br />
<br />
Wait a while and do a '''git pull''' to see if anyone else has changed the repository.<br />
<br />
== Moving from svn ==<br />
<br />
[http://yehudakatz.com/2010/05/13/common-git-workflows/ Here's] a nice article on a common git workflow for those who are moving from svn.<br />
<br />
== Other Errors while using git ==<br />
<br />
The BeagleBone might not be able to handle https requests so instead try using '''git@github.com:repositoryowner/repositoryname.git'''.<br />
<br />
If you run into:<br />
error: Problem with the SSL CA cert (path? access rights?) while accessing git@github.com:repositoryowner/repositoryname.git<br />
fatal HTTP request failed<br />
<br />
You might have to generate a key using '''ssh-keygen''' and add it to your SSH keys on your github. To install ssh-keygen do the following:<br />
beagle$ '''opkg install openssh-keygen'''<br />
<br />
Once it is installed or if it is already installed:<br />
beagle$ '''ssh-keygen'''<br />
<br />
It will ask for a file, passphrase, and same passphrase. I just pressed enter and left them blank for all three. This should show up:<br />
Generating public/private rsa key pair.<br />
Enter file in which to save the key (/home/root/.ssh/id_rsa): <br />
Enter passphrase (empty for no passphrase): <br />
Enter same passphrase again: <br />
Your identification has been saved in /home/root/.ssh/id_rsa.<br />
Your public key has been saved in /home/root/.ssh/id_rsa.pub.<br />
The key fingerprint is:<br />
a8:08:62:47:72:0a:c0:4f:22:0d:fb:46:59:46:8b:eb root@beaglebone<br />
The key's randomart image is:<br />
+--[ RSA 2048]----+<br />
|+o .+ |<br />
|o+.* . |<br />
|+.Bo. |<br />
|.o=o . |<br />
|oo+. . S |<br />
|o+.. . |<br />
| E . |<br />
| |<br />
| |<br />
+-----------------+<br />
<br />
To access the key in the file created by the keygen cat the file using the following command:<br />
beagle$ '''cat ~/.ssh/id_rsa.pub'''<br />
<br />
You should get a long string of characters starting with ssh-rsa and ending with root@beaglebone. Excluding root@beaglebone, this is what you will copy into your SSH-keys on github.com.<br />
<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=EBC_Exercise_16_git_Workshop_Version&diff=184514EBC Exercise 16 git Workshop Version2012-10-25T17:11:40Z<p>Popenhjc: /* Other Errors while using git */</p>
<hr />
<div>[[Category:ECE497]]<br />
{{YoderHead}}<br />
<br />
'''[[git]]''' is a distributed revision control system with an emphasis on being fast. It was initially designed and developed by Linus Torvalds for Linux kernel development. The purpose of this lab is to get hands on experience with git to learn how it works and how to use it.<br />
<br />
This is the workshop version of the exercise. It assumes git is already installed and the repository is already cloned.<br />
<br />
Much of the material here has come from [http://progit.org/book/ Pro Git]. We'll be using [https://github.com/ github] to practice '''git'''ting.<br />
<br />
== Update the git repository ==<br />
<br />
Always before you start work on a git project, make sure you have the current version from github.<br />
<br />
beagle$ '''cd ~/gitLearn'''<br />
beagle$ '''git pull'''<br />
<br />
== Play with git locally ==<br />
<br />
* Edit <code>helloWorld.c</code> and add a <code>printf</code> with your name on it.<br />
* stage and commit <code>helloWorld.c</code>. You may have to merge. Keep everyone else's name in the file.<br />
* Push it to the repository<br />
<br />
Here's how it is done.<br />
<br />
beagle$ '''gedit helloWorld.c'''<br />
beagle$ '''gcc helloWorld.c''' Make sure the file compiles and runs OK.<br />
beagle$ '''./a.out''<br />
beagle$ '''git status''' What's the status of helloWorld?<br />
beagle$ '''git add'''<br />
beagle$ '''git status''' What's the status now?<br />
beagle$ '''git commit -m '''"''Put your own comment here''"<br />
beagle$ '''git status''' What's the status now?<br />
beagle$ '''git push''' This will send your repository to gethub<br />
<br />
If the last '''git push''' give you error, it means someone else has changed the file on the repository and you need to get the new version before you can push. Do this<br />
beagle$ '''git pull'''<br />
<br />
git will try to merge the new version of the file with your version. It it's successful all you have to do is.<br />
<br />
beagle$ '''gedit helloWord.c''' Do this to be sure the file looks OK.<br />
beagle$ '''gcc helloWorld.c'''<br />
beagle$ '''./a.out''' If it doesn't compile edit and fix it.<br />
beagle$ '''git add'''<br />
beagle$ '''git commit -m "''Another descriptive comment here''"'''<br />
beagle$ '''git push'''<br />
<br />
If you aren't lucky, git won't be able to merge and it will tell you so. Edit the file as above and look for '''<<<<<<''' , this will mark the places git didn't know how to handle. Fix them and remove the lines with '''<<<<<<''' and repeat the steps above.<br />
<br />
If you are lucky, no one else has changed the file while you were working on it. If not, repeat the above steps.<br />
<br />
Wait a while and do a '''git pull''' to see if anyone else has changed the repository.<br />
<br />
== Moving from svn ==<br />
<br />
[http://yehudakatz.com/2010/05/13/common-git-workflows/ Here's] a nice article on a common git workflow for those who are moving from svn.<br />
<br />
== Other Errors while using git ==<br />
<br />
If you run into a "'''fatal HTTP request failed'''" error you might have to generate a key using '''ssh-keygen''' and add it to your SSH keys on your github. To install ssh-keygen do the following:<br />
beagle$ '''opkg install openssh-keygen'''<br />
<br />
Once it is installed or if it is already installed:<br />
beagle$ '''ssh-keygen'''<br />
<br />
It will ask for a file, passphrase, and same passphrase. I just pressed enter and left them blank for all three. This should show up:<br />
Generating public/private rsa key pair.<br />
Enter file in which to save the key (/home/root/.ssh/id_rsa): <br />
Enter passphrase (empty for no passphrase): <br />
Enter same passphrase again: <br />
Your identification has been saved in /home/root/.ssh/id_rsa.<br />
Your public key has been saved in /home/root/.ssh/id_rsa.pub.<br />
The key fingerprint is:<br />
a8:08:62:47:72:0a:c0:4f:22:0d:fb:46:59:46:8b:eb root@beaglebone<br />
The key's randomart image is:<br />
+--[ RSA 2048]----+<br />
|+o .+ |<br />
|o+.* . |<br />
|+.Bo. |<br />
|.o=o . |<br />
|oo+. . S |<br />
|o+.. . |<br />
| E . |<br />
| |<br />
| |<br />
+-----------------+<br />
<br />
To access the key in the file created by the keygen cat the file using the following command:<br />
beagle$ '''cat ~/.ssh/id_rsa.pub'''<br />
<br />
You should get a long string of characters starting with ssh-rsa and ending with root@beaglebone. Excluding root@beaglebone, this is what you will copy into your SSH-keys on github.com.<br />
<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=EBC_Exercise_16_git_Workshop_Version&diff=184502EBC Exercise 16 git Workshop Version2012-10-25T17:09:11Z<p>Popenhjc: /* Other Errors with using git */</p>
<hr />
<div>[[Category:ECE497]]<br />
{{YoderHead}}<br />
<br />
'''[[git]]''' is a distributed revision control system with an emphasis on being fast. It was initially designed and developed by Linus Torvalds for Linux kernel development. The purpose of this lab is to get hands on experience with git to learn how it works and how to use it.<br />
<br />
This is the workshop version of the exercise. It assumes git is already installed and the repository is already cloned.<br />
<br />
Much of the material here has come from [http://progit.org/book/ Pro Git]. We'll be using [https://github.com/ github] to practice '''git'''ting.<br />
<br />
== Update the git repository ==<br />
<br />
Always before you start work on a git project, make sure you have the current version from github.<br />
<br />
beagle$ '''cd ~/gitLearn'''<br />
beagle$ '''git pull'''<br />
<br />
== Play with git locally ==<br />
<br />
* Edit <code>helloWorld.c</code> and add a <code>printf</code> with your name on it.<br />
* stage and commit <code>helloWorld.c</code>. You may have to merge. Keep everyone else's name in the file.<br />
* Push it to the repository<br />
<br />
Here's how it is done.<br />
<br />
beagle$ '''gedit helloWorld.c'''<br />
beagle$ '''gcc helloWorld.c''' Make sure the file compiles and runs OK.<br />
beagle$ '''./a.out''<br />
beagle$ '''git status''' What's the status of helloWorld?<br />
beagle$ '''git add'''<br />
beagle$ '''git status''' What's the status now?<br />
beagle$ '''git commit -m '''"''Put your own comment here''"<br />
beagle$ '''git status''' What's the status now?<br />
beagle$ '''git push''' This will send your repository to gethub<br />
<br />
If the last '''git push''' give you error, it means someone else has changed the file on the repository and you need to get the new version before you can push. Do this<br />
beagle$ '''git pull'''<br />
<br />
git will try to merge the new version of the file with your version. It it's successful all you have to do is.<br />
<br />
beagle$ '''gedit helloWord.c''' Do this to be sure the file looks OK.<br />
beagle$ '''gcc helloWorld.c'''<br />
beagle$ '''./a.out''' If it doesn't compile edit and fix it.<br />
beagle$ '''git add'''<br />
beagle$ '''git commit -m "''Another descriptive comment here''"'''<br />
beagle$ '''git push'''<br />
<br />
If you aren't lucky, git won't be able to merge and it will tell you so. Edit the file as above and look for '''<<<<<<''' , this will mark the places git didn't know how to handle. Fix them and remove the lines with '''<<<<<<''' and repeat the steps above.<br />
<br />
If you are lucky, no one else has changed the file while you were working on it. If not, repeat the above steps.<br />
<br />
Wait a while and do a '''git pull''' to see if anyone else has changed the repository.<br />
<br />
== Moving from svn ==<br />
<br />
[http://yehudakatz.com/2010/05/13/common-git-workflows/ Here's] a nice article on a common git workflow for those who are moving from svn.<br />
<br />
== Other Errors while using git ==<br />
<br />
If you run into a "'''fatal HTTP request failed'''" error you might have to generate a key using '''ssh-keygen''' and add it to your SSH keys on your github. To install ssh-keygen do the following:<br />
beagle$ '''opkg install openssh-keygen'''<br />
<br />
Once it is installed or if it is already installed:<br />
beagle$ '''ssh-keygen'''<br />
<br />
It will ask for a file, passphrase, and same passphrase. I just pressed enter and left them blank for all three. This should show up:<br />
Generating public/private rsa key pair.<br />
Enter file in which to save the key (/home/root/.ssh/id_rsa): <br />
Enter passphrase (empty for no passphrase): <br />
Enter same passphrase again: <br />
Your identification has been saved in /home/root/.ssh/id_rsa.<br />
Your public key has been saved in /home/root/.ssh/id_rsa.pub.<br />
The key fingerprint is:<br />
a8:08:62:47:72:0a:c0:4f:22:0d:fb:46:59:46:8b:eb root@beaglebone<br />
The key's randomart image is:<br />
+--[ RSA 2048]----+<br />
|+o .+ |<br />
|o+.* . |<br />
|+.Bo. |<br />
|.o=o . |<br />
|oo+. . S |<br />
|o+.. . |<br />
| E . |<br />
| |<br />
| |<br />
+-----------------+<br />
<br />
To access the key in the file created by the keygen cat the file using the following command:<br />
beagle$ '''cat ~/.ssh/id_rsa.pub'''<br />
<br />
You should get a long string of characters between ssh-rsa and root@beaglebone. This is what you will copy into your SSH-keys on github.com.<br />
<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=EBC_Exercise_16_git_Workshop_Version&diff=184496EBC Exercise 16 git Workshop Version2012-10-25T17:08:46Z<p>Popenhjc: </p>
<hr />
<div>[[Category:ECE497]]<br />
{{YoderHead}}<br />
<br />
'''[[git]]''' is a distributed revision control system with an emphasis on being fast. It was initially designed and developed by Linus Torvalds for Linux kernel development. The purpose of this lab is to get hands on experience with git to learn how it works and how to use it.<br />
<br />
This is the workshop version of the exercise. It assumes git is already installed and the repository is already cloned.<br />
<br />
Much of the material here has come from [http://progit.org/book/ Pro Git]. We'll be using [https://github.com/ github] to practice '''git'''ting.<br />
<br />
== Update the git repository ==<br />
<br />
Always before you start work on a git project, make sure you have the current version from github.<br />
<br />
beagle$ '''cd ~/gitLearn'''<br />
beagle$ '''git pull'''<br />
<br />
== Play with git locally ==<br />
<br />
* Edit <code>helloWorld.c</code> and add a <code>printf</code> with your name on it.<br />
* stage and commit <code>helloWorld.c</code>. You may have to merge. Keep everyone else's name in the file.<br />
* Push it to the repository<br />
<br />
Here's how it is done.<br />
<br />
beagle$ '''gedit helloWorld.c'''<br />
beagle$ '''gcc helloWorld.c''' Make sure the file compiles and runs OK.<br />
beagle$ '''./a.out''<br />
beagle$ '''git status''' What's the status of helloWorld?<br />
beagle$ '''git add'''<br />
beagle$ '''git status''' What's the status now?<br />
beagle$ '''git commit -m '''"''Put your own comment here''"<br />
beagle$ '''git status''' What's the status now?<br />
beagle$ '''git push''' This will send your repository to gethub<br />
<br />
If the last '''git push''' give you error, it means someone else has changed the file on the repository and you need to get the new version before you can push. Do this<br />
beagle$ '''git pull'''<br />
<br />
git will try to merge the new version of the file with your version. It it's successful all you have to do is.<br />
<br />
beagle$ '''gedit helloWord.c''' Do this to be sure the file looks OK.<br />
beagle$ '''gcc helloWorld.c'''<br />
beagle$ '''./a.out''' If it doesn't compile edit and fix it.<br />
beagle$ '''git add'''<br />
beagle$ '''git commit -m "''Another descriptive comment here''"'''<br />
beagle$ '''git push'''<br />
<br />
If you aren't lucky, git won't be able to merge and it will tell you so. Edit the file as above and look for '''<<<<<<''' , this will mark the places git didn't know how to handle. Fix them and remove the lines with '''<<<<<<''' and repeat the steps above.<br />
<br />
If you are lucky, no one else has changed the file while you were working on it. If not, repeat the above steps.<br />
<br />
Wait a while and do a '''git pull''' to see if anyone else has changed the repository.<br />
<br />
== Moving from svn ==<br />
<br />
[http://yehudakatz.com/2010/05/13/common-git-workflows/ Here's] a nice article on a common git workflow for those who are moving from svn.<br />
<br />
== Other Errors with using git ==<br />
<br />
If you run into a "'''fatal HTTP request failed'''" error you might have to generate a key using '''ssh-keygen''' and add it to your SSH keys on your github. To install ssh-keygen do the following:<br />
beagle$ '''opkg install openssh-keygen'''<br />
<br />
Once it is installed or if it is already installed:<br />
beagle$ '''ssh-keygen'''<br />
<br />
It will ask for a file, passphrase, and same passphrase. I just pressed enter and left them blank for all three. This should show up:<br />
Generating public/private rsa key pair.<br />
Enter file in which to save the key (/home/root/.ssh/id_rsa): <br />
Enter passphrase (empty for no passphrase): <br />
Enter same passphrase again: <br />
Your identification has been saved in /home/root/.ssh/id_rsa.<br />
Your public key has been saved in /home/root/.ssh/id_rsa.pub.<br />
The key fingerprint is:<br />
a8:08:62:47:72:0a:c0:4f:22:0d:fb:46:59:46:8b:eb root@beaglebone<br />
The key's randomart image is:<br />
+--[ RSA 2048]----+<br />
|+o .+ |<br />
|o+.* . |<br />
|+.Bo. |<br />
|.o=o . |<br />
|oo+. . S |<br />
|o+.. . |<br />
| E . |<br />
| |<br />
| |<br />
+-----------------+<br />
<br />
To access the key in the file created by the keygen cat the file using the following command:<br />
beagle$ '''cat ~/.ssh/id_rsa.pub'''<br />
<br />
You should get a long string of characters between ssh-rsa and root@beaglebone. This is what you will copy into your SSH-keys on github.com.<br />
<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=EBC_Exercise_16_git_Workshop_Version&diff=184442EBC Exercise 16 git Workshop Version2012-10-25T16:38:57Z<p>Popenhjc: /* Play with git locally */</p>
<hr />
<div>[[Category:ECE497]]<br />
{{YoderHead}}<br />
<br />
'''[[git]]''' is a distributed revision control system with an emphasis on being fast. It was initially designed and developed by Linus Torvalds for Linux kernel development. The purpose of this lab is to get hands on experience with git to learn how it works and how to use it.<br />
<br />
This is the workshop version of the exercise. It assumes git is already installed and the repository is already cloned.<br />
<br />
Much of the material here has come from [http://progit.org/book/ Pro Git]. We'll be using [https://github.com/ github] to practice '''git'''ting.<br />
<br />
== Update the git repository ==<br />
<br />
Always before you start work on a git project, make sure you have the current version from github.<br />
<br />
beagle$ '''cd ~/gitLearn'''<br />
beagle$ '''git pull'''<br />
<br />
== Play with git locally ==<br />
<br />
* Edit <code>helloWorld.c</code> and add a <code>printf</code> with your name on it.<br />
* stage and commit <code>helloWorld.c</code>. You may have to merge. Keep everyone else's name in the file.<br />
* Push it to the repository<br />
<br />
Here's how it is done.<br />
<br />
beagle$ '''gedit helloWorld.c'''<br />
beagle$ '''gcc helloWorld.c''' Make sure the file compiles and runs OK.<br />
beagle$ '''./a.out''<br />
beagle$ '''git status''' What's the status of helloWorld?<br />
beagle$ '''git add'''<br />
beagle$ '''git status''' What's the status now?<br />
beagle$ '''git commit -m '''"''Put your own comment here''"<br />
beagle$ '''git status''' What's the status now?<br />
beagle$ '''git push''' This will send your repository to gethub<br />
<br />
If the last '''git push''' give you error, it means someone else has changed the file on the repository and you need to get the new version before you can push. Do this<br />
beagle$ '''git pull'''<br />
<br />
If you run into an '''fatal HTTP request failed''' error you might have generate a key using '''ssh-keygen''' and add it to you SSH keys on your github. To install ssh-keygen do the following:<br />
beagle$ '''opkg install openssh-keygen'''<br />
<br />
To access the file created by the keygen cat the file using the following command<br />
beagle$<br />
<br />
<br />
git will try to merge the new version of the file with your version. It it's successful all you have to do is.<br />
<br />
beagle$ '''gedit helloWord.c''' Do this to be sure the file looks OK.<br />
beagle$ '''gcc helloWorld.c'''<br />
beagle$ '''./a.out''' If it doesn't compile edit and fix it.<br />
beagle$ '''git add'''<br />
beagle$ '''git commit -m "''Another descriptive comment here''"'''<br />
beagle$ '''git push'''<br />
<br />
If you aren't lucky, git won't be able to merge and it will tell you so. Edit the file as above and look for '''<<<<<<''' , this will mark the places git didn't know how to handle. Fix them and remove the lines with '''<<<<<<''' and repeat the steps above.<br />
<br />
If you are lucky, no one else has changed the file while you were working on it. If not, repeat the above steps.<br />
<br />
Wait a while and do a '''git pull''' to see if anyone else has changed the repository.<br />
<br />
== Moving from svn ==<br />
<br />
[http://yehudakatz.com/2010/05/13/common-git-workflows/ Here's] a nice article on a common git workflow for those who are moving from svn.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=184286ECE497 BeagleBone PRU2012-10-24T23:23:28Z<p>Popenhjc: /* User Instructions */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Executive Summary ==<br />
<br />
Give two sentence intro to the project.<br />
<br />
Give two sentences telling what works.<br />
<br />
Give two sentences telling what isn't working.<br />
<br />
End with a two sentence conclusion.<br />
<br />
The sentence count is approximate and only to give an idea of the expected length.<br />
<br />
== Installation Instructions ==<br />
<br />
Give step by step instructions on how to install your project on the SPEd2 image. <br />
<br />
* Include your [https://github.com/ github] path as a link like this: [https://github.com/MarkAYoder/gitLearn https://github.com/MarkAYoder/gitLearn]. <br />
* Include any additional packages installed via '''opkg'''.<br />
* Include kernel mods.<br />
* If there is extra hardware needed, include links to where it can be obtained.<br />
<br />
== User Instructions ==<br />
<br />
Always run the following before doing anything with the PRU:<br />
beagle$ '''modprobe uio_pruss'''<br />
<br />
== Highlights ==<br />
<br />
Here is where you brag about what your project can do.<br />
<br />
Include a [http://www.youtube.com/ YouTube] demo.<br />
<br />
== Theory of Operation ==<br />
<br />
Give a high level overview of the structure of your software. Are you using GStreamer? Show a diagram of the pipeline. Are you running multiple tasks? Show what they do and how they interact.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
Suggest addition things that could be done with this project.<br />
<br />
== Conclusions ==<br />
<br />
Give some concluding thoughts about the project. Suggest some future additions that could make it even more interesting.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=183368ECE497 BeagleBone PRU2012-10-22T16:12:51Z<p>Popenhjc: /* Work Breakdown */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Executive Summary ==<br />
<br />
Give two sentence intro to the project.<br />
<br />
Give two sentences telling what works.<br />
<br />
Give two sentences telling what isn't working.<br />
<br />
End with a two sentence conclusion.<br />
<br />
The sentence count is approximate and only to give an idea of the expected length.<br />
<br />
== Installation Instructions ==<br />
<br />
Give step by step instructions on how to install your project on the SPEd2 image. <br />
<br />
* Include your [https://github.com/ github] path as a link like this: [https://github.com/MarkAYoder/gitLearn https://github.com/MarkAYoder/gitLearn]. <br />
* Include any additional packages installed via '''opkg'''.<br />
* Include kernel mods.<br />
* If there is extra hardware needed, include links to where it can be obtained.<br />
<br />
== User Instructions ==<br />
<br />
Once everything is installed, how do you use the program? Give details here, so if you have a long user manual, link to it here.<br />
<br />
== Highlights ==<br />
<br />
Here is where you brag about what your project can do.<br />
<br />
Include a [http://www.youtube.com/ YouTube] demo.<br />
<br />
== Theory of Operation ==<br />
<br />
Give a high level overview of the structure of your software. Are you using GStreamer? Show a diagram of the pipeline. Are you running multiple tasks? Show what they do and how they interact.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
Research<br />
<br />
Most of our research has come from internet resources listed below:<br />
*[http://elinux.org/Ti_AM33XX_PRUSSv2 TI PRU Resources]<br />
*[http://blog.boxysean.com/2012/08/12/first-steps-with-the-beaglebone-pru/ Example for Running Code on the PRU]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions PRU Assembly Instructions]<br />
*[http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide#prussdrv_map_prumem Initializing PRU in C]<br />
*[http://www.ti.com/lit/ds/symlink/am3358.pdf AM335X Datasheet]<br />
<br />
== Future Work ==<br />
<br />
Suggest addition things that could be done with this project.<br />
<br />
== Conclusions ==<br />
<br />
Give some concluding thoughts about the project. Suggest some future additions that could make it even more interesting.<br />
<br />
{{YoderFoot}}</div>Popenhjchttps://elinux.org/index.php?title=ECE497_BeagleBone_PRU&diff=181790ECE497 BeagleBone PRU2012-10-16T16:27:55Z<p>Popenhjc: /* Work Breakdown */</p>
<hr />
<div>[[Category:ECE497 |Project]]<br />
{{YoderHead}}<br />
<br />
Team members: [[user:Yoder|Mark A. Yoder]], [[user:Correlbn|Bryan Correll]], [[user:Millerap|Andrew Miller]], [[user:Ngop|Peter Ngo]], [[user:Popenhjc|James Popenhagen]]<br />
<br />
== Executive Summary ==<br />
<br />
Give two sentence intro to the project.<br />
<br />
Give two sentences telling what works.<br />
<br />
Give two sentences telling what isn't working.<br />
<br />
End with a two sentence conclusion.<br />
<br />
The sentence count is approximate and only to give an idea of the expected length.<br />
<br />
== Installation Instructions ==<br />
<br />
Give step by step instructions on how to install your project on the SPEd2 image. <br />
<br />
* Include your [https://github.com/ github] path as a link like this: [https://github.com/MarkAYoder/gitLearn https://github.com/MarkAYoder/gitLearn]. <br />
* Include any additional packages installed via '''opkg'''.<br />
* Include kernel mods.<br />
* If there is extra hardware needed, include links to where it can be obtained.<br />
<br />
== User Instructions ==<br />
<br />
Once everything is installed, how do you use the program? Give details here, so if you have a long user manual, link to it here.<br />
<br />
== Highlights ==<br />
<br />
Here is where you brag about what your project can do.<br />
<br />
Include a [http://www.youtube.com/ YouTube] demo.<br />
<br />
== Theory of Operation ==<br />
<br />
Give a high level overview of the structure of your software. Are you using GStreamer? Show a diagram of the pipeline. Are you running multiple tasks? Show what they do and how they interact.<br />
<br />
== Work Breakdown ==<br />
<br />
Milestones<br />
<br />
10/22: We should have all research done. Update documentation with every Milestone.<br />
<br />
10/26: We should be able to show something, an example or simple implementation.<br />
<br />
10/29: Ability to send different lengths to turn on an LEDs.<br />
<br />
10/31: Ability to send different lengths to multiple LEDs.<br />
<br />
11/2: We should be able to demo our overall work, possibly have some things to fix before presentation.<br />
<br />
11/4: Finalize presentation<br />
<br />
11/6: Presentation<br />
<br />
== Future Work ==<br />
<br />
Suggest addition things that could be done with this project.<br />
<br />
== Conclusions ==<br />
<br />
Give some concluding thoughts about the project. Suggest some future additions that could make it even more interesting.<br />
<br />
{{YoderFoot}}</div>Popenhjc