+

A good combination

In []:
01_Intro

BeagleBone

What is BeagleBone Black?

BeagleBone Black is a $45 MSRP community-supported development platform for developers and hobbyists. Boot Linux in under 10 seconds and get started on development in less than 5 minutes with just a single USB cable.


Software Compatibility

  • Ã…ngström Linux
  • Android
  • Ubuntu
  • Cloud9 IDE on Node.js w/ BoneScript library
  • plus much more


Hints

McASP = Multichannel Audio Serial Port

GPMC = General Purpose Memory Controler

Shipped a total 147,031 boards to date


References

http://beaglebone.org

http://elinux.org

spruh73c.pdf Caution: 4593 pages!

BBB_SRM.pdf

am335xPruReferenceGuide.pdf

Other cards

BBW -> no HDMI, 720MHz, less memory

Arduino Uno -> uController 16MHz, very easy to use

RaspberryPI -> HDMI HD

Broadcom BCM2835, a System-on-Chip that contains an ARM1176JZFS with floating point, running at 700Mhz, and a Videocore 4 GPU. The GPU provides Open GL ES 2.0, hardware-accelerated OpenVG, and 1080p30 H.264 high-profile decode and is capable of 1Gpixel/s, 1.5Gtexel/s or 24 GFLOPs of general purpose compute. What's that all mean? It means that if you plug the Raspberry Pi into your HDTV, you could watch BluRay quality video, using H.264 at 40MBits/s.

Zedboard

The zedboard Board is a single-board computer based on Xilinx's Zynq device family. It uses a Xilinx Zynq Z-7020 Zynq device (dual core ARM Cortex-A9 cores ~800MHz paired with a xilinx Artix 7 fpga). [Price is USD 299 academic , USD 395 commerical ].

Uses

  • Educational: i.e. develop linux drivers
  • Prototyping: i.e. prototype SPI bus, HD video streaming
  • DIY

Programming the BB SoC to access the HW

cross software paradigm has changed

  • as a microcontroller (without OS): starterware, micropython?
  • with linux distribution
    • bonescript
    • any language with OS support

Python

  • Just want to access the registers and the addresses. But DeviceTree
  • 4 methods
    • sysfs
    • mmap
    • combined with pruss
    • combined with kernel

Device Tree

  • new from kernel 3.6
  • puts order on kernel source
  • sysfs is a map of devicetree

Other useful references

In []:
02_blink_bonescript
This is bonescript code to blink LED. Connect a LED to P8_13
In [1]:
cat /home/ubuntu/bonesketches/blink/src/boneblink.js
var b = require('bonescript');

var ledPin = "P8_13";
var ledPin2 = "USR3";

b.pinMode(ledPin, b.OUTPUT);
b.pinMode(ledPin2, b.OUTPUT);

var state = b.LOW;
b.digitalWrite(ledPin, state);
b.digitalWrite(ledPin2, state);

setInterval(toggle, 1000);

function toggle() {
    if(state == b.LOW) state = b.HIGH;
    else state = b.LOW;
    b.digitalWrite(ledPin, state);
    b.digitalWrite(ledPin2, state);
}

In []:
!(cd /home/ubuntu/bonesketches/blink/src; node boneblink.js)
In [4]:
cat /sys/devices/bone_capemgr.9/slots
 0: 54:PF--- 
 1: 55:PF--- 
 2: 56:PF--- 
 3: 57:PF--- 
 4: ff:P-O-- Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G
 5: ff:P-O-- Bone-Black-HDMI,00A0,Texas Instrument,BB-BONELT-HDMI
 6: ff:P-O-- Bone-Black-HDMIN,00A0,Texas Instrument,BB-BONELT-HDMIN
 7: ff:P-O-L Override Board Name,00A0,Override Manuf,BB-UART5
 8: ff:P-O-L Override Board Name,00A0,Override Manuf,bspm_P8_13_f

In [5]:
!echo -8 > /sys/devices/bone_capemgr.9/slots
!cat /sys/devices/bone_capemgr.9/slots
 0: 54:PF--- 
 1: 55:PF--- 
 2: 56:PF--- 
 3: 57:PF--- 
 4: ff:P-O-- Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G
 5: ff:P-O-- Bone-Black-HDMI,00A0,Texas Instrument,BB-BONELT-HDMI
 6: ff:P-O-- Bone-Black-HDMIN,00A0,Texas Instrument,BB-BONELT-HDMIN
 7: ff:P-O-L Override Board Name,00A0,Override Manuf,BB-UART5

In [6]:
ls /lib/firmware/bs*
/lib/firmware/bspm_P8_13_f-00A0.dtbo   /lib/firmware/bspwm_P9_28_34-00A0.dts
/lib/firmware/bspm_P8_13_f-00A0.dts    /lib/firmware/bspwm_P9_28_4-00A0.dtbo
/lib/firmware/bspwm_P8_13_4-00A0.dtbo  /lib/firmware/bspwm_P9_28_4-00A0.dts
/lib/firmware/bspwm_P8_13_4-00A0.dts   /lib/firmware/bspwm_P9_42_0-00A0.dtbo
/lib/firmware/bspwm_P9_28_24-00A0.dts  /lib/firmware/bspwm_P9_42_0-00A0.dts

In [13]:
cat /lib/firmware/bspm_P8_13_f-00A0.dts
/*
 * This is a template-generated file from BoneScript
 */

/dts-v1/;
/plugin/;

/{
    compatible = "ti,beaglebone", "ti,beaglebone-black";
    part_number = "BS_PINMODE_P8_13_0xf";

    exclusive-use =
        "P8.13",
        "gpio0_23";

    fragment@0 {
        target = <&am33xx_pinmux>;
        __overlay__ {
            bs_pinmode_P8_13_0xf: pinmux_bs_pinmode_P8_13_0xf {
                pinctrl-single,pins = <0x024 0xf>;
            };
        };
    };

    fragment@1 {
        target = <&ocp>;
        __overlay__ {
            bs_pinmode_P8_13_0xf_pinmux {
                compatible = "bone-pinmux-helper";
                status = "okay";
                pinctrl-names = "default";
                pinctrl-0 = <&bs_pinmode_P8_13_0xf>;
            };
        };
    };
};

Puntos clave de este método

  • Es javascript
  • Familiar: llamadas parecidas a Arduino
  • Viene de fábrica con ello => en unos segundos estamos controlando la BB
  • Usan el entorno de desarrollo en la nube Cloud9 IDE
  • Se lo han currado: si ve que no lo tiene, se genera automaticamente el devicetree en /lib/firmware, incluso el .dts -no tenemos que preocuparnos del pinmux-
03_blinkAdafruit

Adafruit solution

Adafruit people made a library derived from the raspberry MIT one. In fact wraps calls to the OS in C.

python2 only :(

https://github.com/adafruit/adafruit-beaglebone-io-python/>

In [1]:
!(cd ../bonesketches/blink/src; cat blinkAdafruit.py)
import Adafruit_BBIO.GPIO as GPIO
import time
GPIO.setup("P8_13", GPIO.OUT)
GPIO.setup("P8_3", GPIO.OUT)
for i in range(100):
    GPIO.output("P8_13", GPIO.HIGH)
    GPIO.output("P8_3", GPIO.HIGH)
    time.sleep(0.2)
    GPIO.output("P8_13", GPIO.LOW)
    GPIO.output("P8_3", GPIO.LOW)
    time.sleep(0.2)

In [2]:
!(cd ../bonesketches/blink/src; python2 blinkAdafruit.py)

Puntos clave de este método

  • Es python 2
  • Es derivado de Raspberry
  • Es un wrapper a llamadas al OS -lo vemos en el siguiente método-
In []:
04_blink_OS
blinking the led with python using OS facilities - sysfs

BeagleBone white

In [1]:
import time

# put Port 8 Pin 3 into mode 7 (GPIO). P8_3 = GPIO1_6 => 32+6 = 38, gpio38 gpmc_ad6 is mode 0, see SRM
open('/sys/kernel/debug/omap_mux/gpmc_ad6', 'wb').write("%X" % 7)

try:
   # check to see if the pin is already exported
   open('/sys/class/gpio/gpio38/direction').read()
except:
   # it isn't, so export it
   print("exporting GPIO 38")
   open('/sys/class/gpio/export', 'w').write('38')

# set Port 8 Pin 3 for output
open('/sys/class/gpio/gpio38/direction', 'w').write('out')

for i in range(1000):
   # turn on USR1 and external LED
   open('/sys/class/gpio/gpio38/value', 'w').write("1")
   open("/sys/devices/platform/leds-gpio/leds/beaglebone::usr1/brightness", 'w').write("1")
   # turn off USR2
   open("/sys/devices/platform/leds-gpio/leds/beaglebone::usr2/brightness", 'w').write("0")

   time.sleep(1)

   # turn off USR1 and external LED
   open('/sys/class/gpio/gpio38/value', 'w').write("0")
   open("/sys/devices/platform/leds-gpio/leds/beaglebone::usr1/brightness", 'w').write("0")
   # turn on USR2
   open("/sys/devices/platform/leds-gpio/leds/beaglebone::usr2/brightness", 'w').write("1")

   time.sleep(1)
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
<ipython-input-1-55578c180526> in <module>()
      2 
      3 # put Port 8 Pin 3 into mode 7 (GPIO). P8_3 = GPIO1_6 => 32+6 = 38, gpio38 gpmc_ad6 is mode 0, see SRM
----> 4 open('/sys/kernel/debug/omap_mux/gpmc_ad6', 'wb').write("%X" % 7)
      5 
      6 try:

FileNotFoundError: [Errno 2] No such file or directory: '/sys/kernel/debug/omap_mux/gpmc_ad6'

BeagleBone Black

Connect LEDS to P8_13 and P8_3
In [2]:
!cat /sys/kernel/debug/pinctrl/44e10800.pinmux/pins | grep "pin 23" 
!cat /sys/kernel/debug/pinctrl/44e10800.pinmux/pins | grep "pin 38"  
pin 23 (44e1085c) 00000007 pinctrl-single 
pin 38 (44e10898) 00000037 pinctrl-single 

In [1]:
import time

# Port 8 Pin 13 is in mode 7 (GPIO). P8_13 = GPIO0_23 => gpio23 
# Port 8 Pin 3 is in mode 7 (GPIO). P8_3 = GPIO1_6 => gpio38 

try:
   # check to see if the pin is already exported
   open('/sys/class/gpio/gpio23/direction').read()
except:
   # it isn't, so export it
   print("exporting GPIO 23")
   open('/sys/class/gpio/export', 'w').write('23')

try:
   # check to see if the pin is already exported
   open('/sys/class/gpio/gpio38/direction').read()
except:
   # it isn't, so export it
   print("exporting GPIO 38")
   open('/sys/class/gpio/export', 'w').write('38')
    
# set Port 8 Pin 13 for output
open('/sys/class/gpio/gpio23/direction', 'w').write('out')    
# set Port 8 Pin 3 for output
open('/sys/class/gpio/gpio38/direction', 'w').write('out')    

for i in range(20):
   open('/sys/class/gpio/gpio23/value', 'w').write("1")
   open('/sys/class/gpio/gpio38/value', 'w').write("0")
   open("/sys/class/leds/beaglebone:green:usr1/brightness", 'w').write("1")
   open("/sys/class/leds/beaglebone:green:usr2/brightness", 'w').write("0")

   time.sleep(0.2)

   open('/sys/class/gpio/gpio23/value', 'w').write("0")
   open('/sys/class/gpio/gpio38/value', 'w').write("1")
   open("/sys/class/leds/beaglebone:green:usr1/brightness", 'w').write("0")
   open("/sys/class/leds/beaglebone:green:usr2/brightness", 'w').write("1")

   time.sleep(0.2)
open('/sys/class/gpio/gpio38/value', 'w').write("0")    
exporting GPIO 38

Out[1]:
1
In [3]:
!ls /sys/class/gpio/
export	gpio23	gpio38	gpiochip0  gpiochip32  gpiochip64  gpiochip96  unexport

En BBW:

sudo cat /sys/kernel/debug/omap_mux/gpmc_ad6 [sudo] password for ubuntu: name: gpmc_ad6.gpio1_6 (0x44e10818/0x818 = 0x0037), b NA, t NA mode: OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE7 signals: gpmc_ad6 | mmc1_dat6 | NA | NA | NA | NA | NA | gpio1_6

Puntos clave de este método

  • Es python pero el acceso al OS (sysfs), podría hacerse en cualquier lenguaje o desde el shell
  • Con este método en la BBW se puede reprogramar el pinmux, con la BBB no
  • De los ejemplos anteriores se deriva que hay dependencia a como se implemente el sysfs desde el kernel
  • No es muy didáctico un open() sin un close()
05_blink_mmap
In [1]:
import os
import mmap
import time
import struct

GPIOMEM = (0x44E07000, 0x1000)   #(addr, size) GPIO0, pag 210 spruh73c.pdf
GPIO_OE = 0x134                  # pag 4517
GPIO_CLEARDATAOUT = 0x190
GPIO_SETDATAOUT = 0x194
PIN = 1 << 23                    #GPIO0_23 = P8_13
        
#access to GPIO0 registers
fdm = os.open("/dev/mem", os.O_RDWR | os.O_SYNC)
mm = mmap.mmap(fdm, GPIOMEM[1], mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE, 0, GPIOMEM[0])

mm[GPIO_OE:GPIO_OE+4] = struct.pack("<L", ~PIN & 0xffffffff)

for i in range(20):
    mm[GPIO_SETDATAOUT:GPIO_SETDATAOUT+4] = struct.pack("<L", PIN)
    time.sleep(0.2)
    mm[GPIO_CLEARDATAOUT:GPIO_CLEARDATAOUT+4] = struct.pack("<L", PIN)
    time.sleep(0.2)

mm.close()
os.close(fdm)
In [2]:
import os
import mmap
import time
import struct

GPIOMEM0 = (0x44E07000, 0x1000)   #(addr, size) GPIO0, pag 158 spruh73c.pdf
GPIOMEM1 = (0x4804C000, 0x1000)   #(addr, size) GPIO1, pag 159 spruh73c.pdf
GPIO_OE = 0x134                  # pag 4517
GPIO_CLEARDATAOUT = 0x190
GPIO_SETDATAOUT = 0x194
PIN0 = 1 << 23                    #GPIO0_23 = P8_13
PIN1 = 1 << 6                     #GPIO1_6 = P8_3       
#access to GPIO0 registers
fdm = os.open("/dev/mem", os.O_RDWR | os.O_SYNC)
mm0 = mmap.mmap(fdm, GPIOMEM0[1], mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE, 0, GPIOMEM0[0])
mm1 = mmap.mmap(fdm, GPIOMEM1[1], mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE, 0, GPIOMEM1[0])

mm0[GPIO_OE:GPIO_OE+4] = struct.pack("<L", ~PIN0 & 0xffffffff)
mm1[GPIO_OE:GPIO_OE+4] = struct.pack("<L", ~PIN1 & 0xffffffff)

for i in range(20):
    mm0[GPIO_SETDATAOUT:GPIO_SETDATAOUT+4] = struct.pack("<L", PIN0)
    mm1[GPIO_CLEARDATAOUT:GPIO_CLEARDATAOUT+4] = struct.pack("<L", PIN1)
    time.sleep(0.2)
    mm0[GPIO_CLEARDATAOUT:GPIO_CLEARDATAOUT+4] = struct.pack("<L", PIN0)
    mm1[GPIO_SETDATAOUT:GPIO_SETDATAOUT+4] = struct.pack("<L", PIN1)
    time.sleep(0.2)

mm0.close()
mm1.close()
os.close(fdm)

devmem2

In [3]:
!devmem2

Usage:	devmem2 { address } [ type [ data ] ]
	address : memory address to act upon
	type    : access operation type : [b]yte, [h]alfword, [w]ord
	data    : data to be written


In [4]:
#goal: set P9_22 in mode 1 as input: 0x21
#address to act: 0x44e10000 (ctrl module) + 0x950 (conf_spi0_sclk)
!devmem2 0x44e10950 w
/dev/mem opened.
Memory mapped at address 0xb6fd8000.
Value at address 0x44E10950 (0xb6fd8950): 0x20

In [5]:
!devmem2 0x44e10950 w 0x21
/dev/mem opened.
Memory mapped at address 0xb6f5e000.
Value at address 0x44E10950 (0xb6f5e950): 0x20
Written 0x21; readback 0x21

In [6]:
!devmem2 0x44e10950 w
/dev/mem opened.
Memory mapped at address 0xb6fb7000.
Value at address 0x44E10950 (0xb6fb7950): 0x20

¿Qué ha ocurrido?

Puntos clave de este método

  • Es python. Con python3 el mm.write_byte() es mejor
  • Acceso al hardware siguiendo la especificación del fabricante
  • No hace falta ninguna libreria "ajena"
  • Menor dependencia del sysfs que con el método anterior
  • devmem2 es una utilidad de linux que hace abre el /dev/mem y hace el mmap
  • Hay zonas de memoria a las que solamente se puede acceder desde el kernel space
In []:
06_blinkPruss

PRUSS = Programmable Real-time Unit Subsystem
See page 234 of spruh73c.pdf
IEP = Industrial Ethernet Port
MII_RT = Ethernet Media Independent Interface
SPAD = Three scratch pad banks of 30, 32-bit registers (R29:0)
MAC = Multiplier with optional acummulation
eGPIO = 28 shift register + 16parallel + MII_RT

In [1]:
import os
import sys 
import mmap
import struct
import time

PRU_ID = 1   #PRU identifier, 0 or 1
PATHBIN = '/home/ubuntu/bonesketches/blink/bin/blinkBBB.bin'  #assembled code to load

PRUSSMEM = (0x4a300000, 0x80000)     #(addr,size) pag 215 spruh73c.pdf
CTRLREG  = [0x00022000, 0x00024000]  #pag 19 am335xPruReferenceGuide.pdf
IRAM     = [0x00034000, 0x00038000]  #pag 19 am335xPruReferenceGuide.pdf                

#access to PRUSS memory
fdm = os.open("/dev/mem", os.O_RDWR | os.O_SYNC)
mm = mmap.mmap(fdm, PRUSSMEM[1], mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE, 0, PRUSSMEM[0])

#disable
mm.seek(CTRLREG[PRU_ID])
mm.write_byte(0)    #disable & reset, pag. 69 am335xPruReferenceGuide.pdf

#load binary file
fdbin = open(PATHBIN, 'rb')
ba = fdbin.read()
fdbin.close()
mm.seek(IRAM[PRU_ID])
mm.write(ba)

#enable/run
mm.seek(CTRLREG[PRU_ID])
mm.write_byte(3)    #enable, pag. 69 am335xPruReferenceGuide.pdf

time.sleep(10.8)

#disable
mm.seek(CTRLREG[PRU_ID])
mm.write_byte(1)    #disable, pag. 69 am335xPruReferenceGuide.pdf

debugging

In [2]:
REGISTER = [0x00022400, 0x00024400]  #pag 19 am335xPruReferenceGuide.pdf 

#display registers
mm.seek(REGISTER[PRU_ID])
for i in range(32):
    print("r%d = %02X %02X %02X %02X"%(i, mm.read_byte(),mm.read_byte(),mm.read_byte(),mm.read_byte()))

#display constants
mm.seek(REGISTER[PRU_ID]+0x80)
for i in range(32):
    print("c%d = %02X %02X %02X %02X"%(i, mm.read_byte(),mm.read_byte(),mm.read_byte(),mm.read_byte()))
    
#display cfg
mm.seek(0x26000)
for i in range(13):
    print("cfg%d = %02X %02X %02X %02X"%(i, mm.read_byte(),mm.read_byte(),mm.read_byte(),mm.read_byte()))    

#display ctrlregs
mm.seek(CTRLREG[PRU_ID])
for i in range(5):
    print("%02X %02X %02X %02X"%(mm.read_byte(),mm.read_byte(),mm.read_byte(),mm.read_byte()))

#close
mm.close()
os.close(fdm)
r0 = BF FF FF FF
r1 = 34 C1 04 48
r2 = 45 D5 53 4E
r3 = 37 45 32 EE
r4 = F3 F2 FD 87
r5 = 7F F5 FB 9E
r6 = 64 A3 BA 68
r7 = E1 7A 43 AD
r8 = 6E 59 F8 EA
r9 = F5 94 FF 24
r10 = FE BD CF AF
r11 = F8 FF 1D 84
r12 = FC B1 FB E5
r13 = 3D 20 DF F6
r14 = 18 B7 75 27
r15 = F0 53 FF E2
r16 = AD 7D D5 35
r17 = 97 4A FC FF
r18 = 55 EF 6F DF
r19 = C5 DE F3 7A
r20 = 90 C1 04 48
r21 = 40 00 00 00
r22 = E7 8D C7 BB
r23 = D1 CA DD 99
r24 = 7F 8F 21 01
r25 = D9 14 33 D0
r26 = BB 97 3D F7
r27 = 48 85 D7 3B
r28 = A1 CF 77 4F
r29 = FF D8 D1 FB
r30 = 16 00 9F FF
r31 = 00 00 00 00
c0 = 00 00 02 00
c1 = 00 00 04 48
c2 = 00 A0 02 48
c3 = 00 00 03 00
c4 = 00 60 02 00
c5 = 00 00 06 48
c6 = 00 00 03 48
c7 = 00 80 02 00
c8 = 00 00 00 46
c9 = 00 00 10 4A
c10 = 00 80 31 48
c11 = 00 20 02 48
c12 = 00 40 02 48
c13 = 00 00 31 48
c14 = 00 C0 1C 48
c15 = 00 00 1D 48
c16 = 00 00 1A 48
c17 = 00 C0 19 48
c18 = 00 00 30 48
c19 = 00 20 30 48
c20 = 00 40 30 48
c21 = 00 24 03 00
c22 = 00 80 0C 48
c23 = 00 A0 0C 48
c24 = 00 00 00 00
c25 = 00 20 00 00
c26 = 00 E0 02 00
c27 = 00 20 03 00
c28 = 00 00 00 00
c29 = 00 00 00 49
c30 = 00 00 00 40
c31 = 00 00 00 80
cfg0 = 00 00 00 47
cfg1 = 0A 00 00 00
cfg2 = 00 00 00 02
cfg3 = 00 00 00 02
cfg4 = 24 49 02 00
cfg5 = 00 00 00 00
cfg6 = 00 00 00 00
cfg7 = 00 00 00 00
cfg8 = 00 00 00 00
cfg9 = 4B A8 0A 00
cfg10 = 00 00 00 00
cfg11 = 01 00 00 00
cfg12 = 00 00 00 00
01 00 00 00
1A 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00

In [3]:
!cat /home/ubuntu/bonesketches/blink/src/blinkBBB.p
.origin 0
.entrypoint BLINK

#define PIN 6     //P8_3 = GPIO1_6
#define GPIO1_REGS 0x4804C000
#define GPIO1_OUTPIN 1<<PIN 
#define GPIO1_CLEARDATAOUT GPIO1_REGS | 0x190
#define GPIO1_SETDATAOUT GPIO1_REGS | 0x194
#define GPIO1_OE GPIO1_REGS | 0x134

BLINK:
    LBCO    r0, C4, 4, 4
    CLR     r0, r0, 4         // Clear SYSCFG[STANDBY_INIT] to enable OCP master port
    SBCO    r0, C4, 4, 4 
    MOV     r1, GPIO1_OE
    LBBO    r0, r1, 0, 4
    CLR     r0, r0, PIN
    SBBO    r0, r1, 0, 1   // revert with cleared bit
CYCLE:
    MOV     r20, GPIO1_SETDATAOUT
    MOV     r21, GPIO1_OUTPIN
    SBBO    r21, r20, 0, 4
    MOV     r24, 0x05f5e100    
    CALL    DELAY_TICKS
    MOV     r20, GPIO1_CLEARDATAOUT
    MOV     r21, GPIO1_OUTPIN
    SBBO    r21, r20, 0, 4
    MOV     r24, 0x05f5e100    
    CALL    DELAY_TICKS
    JMP     CYCLE

DELAY_TICKS:
    //r24->num ticks
    LSR       r24, r24, 1   // divide by 2
    SUB       r24, r24, 3   // 3 instructions to substract
WAIT_TICKS:
    SUB       r24, r24, 1
    QBLT      WAIT_TICKS, r24, 1
    LDI       r24,0xff  // needed for adjusting time exactly
    RET

In [5]:
#time in set or in zero
float(0x05f5e100)/200e6
Out[5]:
0.5

Puntos clave de este método

  • El SOC, además del procesador de 1GHz alberga dos microcontroladores a 200MHz
  • Estos micros están interconectados con el host
  • Esto es Python + ensamblador (es un RISC sencillo)
  • Podemos gobernar el micro desde python: cargar software, ejecutar, parar, ver registros
  • No hace falta ninguna librería "ajena"
  • Hace falta el ensamblador pasm, lo vemos más adelante
In []:
07_blink_PWM
The SOC has a subsystem for PWM. 203 pages, see page 1970

Program the DeviceTree

We need to activate the clocks and program pinmux for P8_13 to mode 4

In []:
!reboot
In [6]:
!cat /sys/devices/bone_capemgr.9/slots                     #this shows loaded DTs
 0: 54:PF--- 
 1: 55:PF--- 
 2: 56:PF--- 
 3: 57:PF--- 
 4: ff:P-O-- Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G
 5: ff:P-O-- Bone-Black-HDMI,00A0,Texas Instrument,BB-BONELT-HDMI
 6: ff:P-O-- Bone-Black-HDMIN,00A0,Texas Instrument,BB-BONELT-HDMIN
 7: ff:P-O-L Override Board Name,00A0,Override Manuf,BB-UART5

In [5]:
!echo -8 > /sys/devices/bone_capemgr.9/slots
In [8]:
!echo am33xx_pwm > /sys/devices/bone_capemgr.9/slots       #this activates clocks
In [7]:
!echo bspwm_P8_13_4 > /sys/devices/bone_capemgr.9/slots    #this puts mode4 on P8_13
In [9]:
!cat /sys/devices/bone_capemgr.9/slots                     #this shows loaded DTs
 0: 54:PF--- 
 1: 55:PF--- 
 2: 56:PF--- 
 3: 57:PF--- 
 4: ff:P-O-- Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G
 5: ff:P-O-- Bone-Black-HDMI,00A0,Texas Instrument,BB-BONELT-HDMI
 6: ff:P-O-- Bone-Black-HDMIN,00A0,Texas Instrument,BB-BONELT-HDMIN
 7: ff:P-O-L Override Board Name,00A0,Override Manuf,BB-UART5
 9: ff:P-O-L Override Board Name,00A0,Override Manuf,bspwm_P8_13_4
10: ff:P-O-L Override Board Name,00A0,Override Manuf,am33xx_pwm

Program the PWM registers of ePWM2 in python

In [10]:
import os
import mmap
import struct

#pwm ss 2
PWM_SUBSYSTEM2 =    (0x48304000, 0x260)
ePWM2 = 0x200
TBCTL = ePWM2+0
AQCTLB = ePWM2+0x18
TBPRD = ePWM2+0x0a
CMPB = ePWM2+0x14

#access to pwm subsystem 2
fdm = os.open("/dev/mem", os.O_RDWR | os.O_SYNC)
mm = mmap.mmap(fdm, PWM_SUBSYSTEM2[1], mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE, 0, PWM_SUBSYSTEM2[0])

mm.seek(AQCTLB)                     #Action Qualifier
mm.write(struct.pack('<H', 0x010a))  

mm.seek(TBPRD)
mm.write(struct.pack('<H', 32552))   # 65104 => 1 second

mm.seek(TBCTL)
mm.write(struct.pack('<H', 0xdf00))   # free run /128  /12 => 65104.16 hz (SYSCLK = 100MHz)

mm.seek(CMPB)
mm.write(struct.pack('<H', 32552//3))

mm.close()
os.close(fdm)

bonescript creates bspwm_P8_13_4-00A0.dtbo file for us

In []:
#this generates .dtbo & .dts if they does not exist
!node -e "require('bonescript').analogWrite('P8_13', 0.5, 1, console.log);"
In [6]:
!(ls /lib/firmware | egrep '^bs')
bspm_P8_13_f-00A0.dtbo
bspm_P8_13_f-00A0.dts
bspwm_P8_13_4-00A0.dtbo
bspwm_P8_13_4-00A0.dts
bspwm_P9_28_24-00A0.dts
bspwm_P9_28_34-00A0.dts
bspwm_P9_28_4-00A0.dtbo
bspwm_P9_28_4-00A0.dts
bspwm_P9_42_0-00A0.dtbo
bspwm_P9_42_0-00A0.dts

Puntos clave de este método

  • En este ejemplo cambiamos el pinmux y activamos el reloj haciendo uso de los ficheros DT que tenemos en /lib/firmware
  • Usamos el método mmap
  • El SOC tiene bastante hardware para hacer PWM
  • Para hacer el parpadeo no estamos usando CPU, lo hace el hardware
  • Como truco, generamos desde bonescript los ficheros DT
In []:
08_buzz_PWM

Buzzer with a PWM output

In [1]:
!echo am33xx_pwm > /sys/devices/bone_capemgr.9/slots
!echo bspwm_P8_13_4 > /sys/devices/bone_capemgr.9/slots
!cat /sys/devices/bone_capemgr.9/slots
 0: 54:PF--- 
 1: 55:PF--- 
 2: 56:PF--- 
 3: 57:PF--- 
 4: ff:P-O-- Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G
 5: ff:P-O-- Bone-Black-HDMI,00A0,Texas Instrument,BB-BONELT-HDMI
 6: ff:P-O-- Bone-Black-HDMIN,00A0,Texas Instrument,BB-BONELT-HDMIN
 7: ff:P-O-L Override Board Name,00A0,Override Manuf,BB-UART5
 8: ff:P-O-L Override Board Name,00A0,Override Manuf,am33xx_pwm
 9: ff:P-O-L Override Board Name,00A0,Override Manuf,bspwm_P8_13_4

In [1]:
import os
import mmap
import struct
import time

#pwm ss 2
PWM_SUBSYSTEM2 =    (0x48304000, 0x260)
ePWM2 = 0x200
TBCTL = ePWM2+0
AQCTLB = ePWM2+0x18
TBPRD = ePWM2+0x0a
CMPB = ePWM2+0x14

#access to pwm subsystem 2
fdm = os.open("/dev/mem", os.O_RDWR | os.O_SYNC)
mm = mmap.mmap(fdm, PWM_SUBSYSTEM2[1], mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE, 0, PWM_SUBSYSTEM2[0])

u = 19  # aprox 3.5Khz (3426.53 Hz to be precise)

mm.seek(AQCTLB)                     #Action Qualifier
mm.write(struct.pack('<H', 0x010a))  

mm.seek(TBPRD)
mm.write(struct.pack('<H', u))   # 65104 => 1 second

mm.seek(CMPB)
mm.write(struct.pack('<H', u//2))

for i in range(100):
    mm.seek(TBCTL)
    mm.write(struct.pack('<H', 0xdf00))   # start, free run /128  /12 => 65104.16 hz (SYSCLK = 100MHz)
    
    time.sleep(0.04)

    mm.seek(TBCTL)
    mm.write(struct.pack('<H', 0xdf03))   # stop
    
    time.sleep(0.1)

mm.close()
os.close(fdm)

Puntos clave de este método

  • En este ejemplo cambiamos el pinmux y activamos el reloj haciendo uso de los ficheros DT que tenemos en /lib/firmware
  • Usamos el método mmap
  • El SOC tiene bastante hardware para hacer PWM
  • Para generar los 3.5Khz de la especificación no estamos usando CPU, lo hace el hardware
In []:
09_time

Precise time measures and delays

Each ucontroller has a counter that is controlled by the control register. We can compare the counter by the desired delay time or we can count ourselves the ticks.
In [1]:
!cat /home/ubuntu/bonesketches/tiempos/src/tiempos.p
.origin 0
.entrypoint TIEMPOS

TIEMPOS:
    // r5 must be loaded with control register 
    // r24 must be loaded with # of ticks to be delayed
    LBBO      r6, r5, 0, 4      //Load control register for counter in r6
    CLR       r16, r6.b0, 3     // for pausing counter 
    SET       r17, r6.b0, 3     // for resuming counter 
    SBBO      r17, r5, 0, 4     // activate counter
    SBCO      r24, C24, 8, 4    //store r24 (debug) 
    SBBO      r16, r5, 0x0c, 4  // counter to 0
    CALL      DELAY_TICKS
    SBBO      r16, r5, 0, 4     //stop counter
    LBBO      r7, r5, 0x0c, 4   //read counter
    SBCO      r7, C24, 4, 4     //store counter (debug) 

    // Send notification to Host for program completion
    SBCO      r3, C24, 0, 1
    HALT

DELAY_TICKS:
    //r24->num ticks
    LSR       r24, r24, 1
    SUB       r24, r24, 3
WAIT_TICKS:
    SUB       r24, r24, 1
    QBLT      WAIT_TICKS, r24, 1
    LDI       r24,0xff  // needed for adjusting time exactly
    RET 

In [2]:
!cat /home/ubuntu/bonesketches/tiempos/src/haz.sh
ls *.p | xargs -i pasm -b {}
echo generated: `ls *.bin`
mv -f *.bin ../bin

In [3]:
# Where is pasm?
!(cd /home/ubuntu/am335x_pru_package; git remote -v)
origin	https://github.com/beagleboard/am335x_pru_package (fetch)
origin	https://github.com/beagleboard/am335x_pru_package (push)

In [4]:
!(cd /home/ubuntu/bonesketches/tiempos/src; ./haz.sh)


PRU Assembler Version 0.84
Copyright (C) 2005-2013 by Texas Instruments Inc.


Pass 2 : 0 Error(s), 0 Warning(s)

Writing Code Image of 18 word(s)



PRU Assembler Version 0.84
Copyright (C) 2005-2013 by Texas Instruments Inc.


Pass 2 : 0 Error(s), 0 Warning(s)

Writing Code Image of 47 word(s)



PRU Assembler Version 0.84
Copyright (C) 2005-2013 by Texas Instruments Inc.


Pass 2 : 0 Error(s), 0 Warning(s)

Writing Code Image of 83 word(s)

generated: tiempos.bin tiemposInterrupt.bin tiemposInterruptMultiPru.bin

In [1]:
import os
import sys 
import mmap
import time
import struct

PRU_ID = 1   #PRU identifier, 0 or 1
PATHBIN = '/home/ubuntu/bonesketches/tiempos/bin/tiempos.bin'  #assembled code to load
TICKSTODELAY = 0x00142CA

#addresses
PRUSSMEM = (0x4a300000, 0x80000)     #(addr,size) pag 215 spruh73c.pdf
CTRLREG  = [0x00022000, 0x00024000]  #pag 19 am335xPruReferenceGuide.pdf
IRAM     = [0x00034000, 0x00038000]  #pag 19 am335xPruReferenceGuide.pdf
DATARAM  = [0x00000000, 0x00002000]  #pag 19 am335xPruReferenceGuide.pdf
SHARRAM  = (0x10000, 0x3000)         #(addr,size) pag 19 am335xPruReferenceGuide.pdf
REGISTER = [0x00022400, 0x00024400]  #pag 19 am335xPruReferenceGuide.pdf

#requirements
if sys.version_info[0] != 3:
    sys.exit("python3 required")
if os.geteuid() != 0:
    sys.exit("You need to have root privileges")

def write_reg(val, nreg):
    """write val to register rnreg"""
    mm.seek(0)
    mm[REGISTER[PRU_ID]+nreg*4:REGISTER[PRU_ID]+nreg*4+4] = struct.pack("<L", val)

#access to PRUSS memory
fdm = os.open("/dev/mem", os.O_RDWR | os.O_SYNC)
mm = mmap.mmap(fdm, PRUSSMEM[1], mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE, 0, PRUSSMEM[0])

#read binary file to memory
fdbin = open(PATHBIN, 'rb')
ba = fdbin.read()
fdbin.close()

#disable
mm.seek(CTRLREG[PRU_ID])
mm.write_byte(1)    #disable, pag. 69 am335xPruReferenceGuide.pdf

#load binary file
mm.seek(IRAM[PRU_ID])
mm.write(ba)

#clear beginning of memory
mm.seek(DATARAM[PRU_ID])
mm.write(b'\0'*16)

#clear registers
mm.seek(REGISTER[PRU_ID])
mm.write(b'\0'*32*4)

#pass CTRLREG to r5, this way, assembler code is agnostic of which PRU is executing
write_reg(CTRLREG[PRU_ID], 5)

#pass ticks to delay to r24
write_reg(TICKSTODELAY, 24)

# running indicator   1->running    0->halted
mm.seek(DATARAM[PRU_ID])
mm.write_byte(1)

#enable/run
mm.seek(CTRLREG[PRU_ID])
mm.write_byte(2)    #enable, pag. 69 am335xPruReferenceGuide.pdf

#wait for PRU to halt (polling method)
while True:
    mm.seek(DATARAM[PRU_ID])
    if mm.read_byte() == 0:
        break
    time.sleep(0.1)
    
#display beginning memory
mm.seek(DATARAM[PRU_ID])
for i in range(8):
    print("%02X "%(mm.read_byte()), end="")
print()
for i in range(8):
    print("%02X "%(mm.read_byte()), end="")
print()

#display registers
mm.seek(REGISTER[PRU_ID])
for i in range(32):
    print("r%d = %02X %02X %02X %02X"%(i, mm.read_byte(),mm.read_byte(),mm.read_byte(),mm.read_byte()))

#close
mm.close()
os.close(fdm)
    
00 00 00 00 CA 42 01 00 
CA 42 01 00 00 00 00 00 
r0 = 00 00 00 00
r1 = 00 00 00 00
r2 = 00 00 00 00
r3 = 00 00 00 00
r4 = 00 00 00 00
r5 = 00 40 02 00
r6 = 03 80 00 00
r7 = CA 42 01 00
r8 = 00 00 00 00
r9 = 00 00 00 00
r10 = 00 00 00 00
r11 = 00 00 00 00
r12 = 00 00 00 00
r13 = 00 00 00 00
r14 = 00 00 00 00
r15 = 00 00 00 00
r16 = 03 00 00 00
r17 = 0B 00 00 00
r18 = 00 00 00 00
r19 = 00 00 00 00
r20 = 00 00 00 00
r21 = 00 00 00 00
r22 = 00 00 00 00
r23 = 00 00 00 00
r24 = FF 00 00 00
r25 = 00 00 00 00
r26 = 00 00 00 00
r27 = 00 00 00 00
r28 = 00 00 00 00
r29 = 00 00 00 00
r30 = 07 00 00 00
r31 = 00 00 00 00

Puntos clave de este método

  • Casi todas las instrucciones RISC usan un solo tick de reloj de 200MHz
  • Decrementar + saltar => dos ticks
  • Existe un contador en cada micro que ya cuenta por nosotros
  • El fin del programa por polling
  • Python + Assembler (pasm)
In []:
0A_timeInterrupt

Capture interrupt PRUSS->Host

In []:
import os
import sys 
import mmap
import time

PRU_ID = 1   #PRU identifier, 0 or 1
PATHBIN = '/home/ubuntu/bonesketches/tiempos/bin/tiemposInterrupt.bin'  #assembled code to load
TICKSTODELAY = 0x00142CA

#addresses
PRUSSMEM = (0x4a300000, 0x80000)     #(addr,size) pag 215 spruh73c.pdf
CTRLREG  = [0x00022000, 0x00024000]  #pag 19 am335xPruReferenceGuide.pdf
IRAM     = [0x00034000, 0x00038000]  #pag 19 am335xPruReferenceGuide.pdf
DATARAM  = [0x00000000, 0x00002000]  #pag 19 am335xPruReferenceGuide.pdf
SHARRAM  = (0x10000, 0x3000)         #(addr,size) pag 19 am335xPruReferenceGuide.pdf
REGISTER = [0x00022400, 0x00024400]  #pag 19 am335xPruReferenceGuide.pdf

#requirements
if sys.version_info[0] != 3:
    sys.exit("python3 required")
if os.geteuid() != 0:
    sys.exit("You need to have root privileges")

def write_reg(val, nreg):
    """write val to register rnreg"""
    mm.seek(REGISTER[PRU_ID]+nreg*4)
    for i in range(0,32,8):
        mm.write_byte((val >> i) & 0xff)

#access to PRUSS memory
fdm = os.open("/dev/mem", os.O_RDWR | os.O_SYNC)
mm = mmap.mmap(fdm, PRUSSMEM[1], mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE, 0, PRUSSMEM[0])

#read binary file to memory
fdbin = open(PATHBIN, 'rb')
ba = fdbin.read()
fdbin.close()

#disable
mm.seek(CTRLREG[PRU_ID])
mm.write_byte(1)    #disable, pag. 69 am335xPruReferenceGuide.pdf

#load binary file
mm.seek(IRAM[PRU_ID])
mm.write(ba)

#clear beginning of memory
mm.seek(DATARAM[PRU_ID])
mm.write(b'\0'*16)

#clear registers
mm.seek(REGISTER[PRU_ID])
mm.write(b'\0'*29*4)

#pass CTRLREG to r5, this way, assembler code is agnostic of which PRU is executing
write_reg(CTRLREG[PRU_ID], 5)

#pass ticks to delay to r24
write_reg(TICKSTODELAY, 24)

#for capturing events PRU->host
fdi = os.open("/dev/uio1", os.O_RDWR | os.O_SYNC)

#enable/run
mm.seek(CTRLREG[PRU_ID])
mm.write_byte(2)    #enable, pag. 69 am335xPruReferenceGuide.pdf

#wait for PRU to halt (interrupt method)
os.read(fdi, 4)
os.close(fdi)

#display beginning memory
mm.seek(DATARAM[PRU_ID])
for i in range(8):
    print("%02X "%(mm.read_byte()), end="")
print()
for i in range(8):
    print("%02X "%(mm.read_byte()), end="")
print()

#display registers
mm.seek(REGISTER[PRU_ID])
for i in range(32):
    print("r%d = %02X %02X %02X %02X"%(i, mm.read_byte(),mm.read_byte(),mm.read_byte(),mm.read_byte()))

#close
mm.close()
os.close(fdm)
In [1]:
!cat ../bonesketches/tiempos/src/tiemposInterrupt.p
.origin 0
.entrypoint TIEMPOS

// see am335xPruReferenceGuide.pdf
#define INTC 0x20000
#define SIPR0 INTC+0xd00
#define SIPR1 INTC+0xd04
#define CMR0  INTC+0x400
#define CMR1  INTC+0x404
#define CMR2  INTC+0x408
#define CMR3  INTC+0x40C
#define CMR4  INTC+0x410
#define CMR5  INTC+0x414
#define CMR6  INTC+0x418
#define CMR7  INTC+0x41C
#define CMR8  INTC+0x420
#define CMR9  INTC+0x424
#define CMR10 INTC+0x428
#define CMR11 INTC+0x42C
#define CMR12 INTC+0x430
#define CMR13 INTC+0x434
#define CMR14 INTC+0x438
#define CMR15 INTC+0x43C
#define HMR0  INTC+0x800
#define HMR1  INTC+0x804
#define HMR2  INTC+0x808
#define SITR0 INTC+0xD80
#define SITR1 INTC+0xD84
#define ESR0  INTC+0x300
#define ESR1  INTC+0x304
#define SECR0 INTC+0x280
#define SECR1 INTC+0x284
#define HIEISR INTC+0x34
#define GER   INTC+0x010
#define REVID INTC+0x000

TIEMPOS:
    // r5 must be loaded with control register 
    // r24 must be loaded with # of ticks to be delayed
    LBBO      r6, r5, 0, 4      //Load control register for counter in r6
    CLR       r16, r6.b0, 3     // for pausing counter 
    SET       r17, r6.b0, 3     // for resuming counter 
    SBBO      r17, r5, 0, 4     // activate counter
    JMP       CONF_INTERRUPT
CONT:
    SBCO      r24, C24, 8, 4    //store r24 (debug) 
    SBBO      r16, r5, 0x0c, 4  // counter to 0
    CALL      DELAY_TICKS
    SBBO      r16, r5, 0, 4     //stop counter
    LBBO      r7, r5, 0x0c, 4   //read counter
    SBCO      r7, C24, 4, 4     //store counter (debug) 
    // Send notification to Host for program completion
    MOV       r31.b0, 0x24     // 1<<5 + 4 => sys interrupt 20
    HALT

DELAY_TICKS:
    //r24->num ticks
    LSR       r24, r24, 1
    SUB       r24, r24, 3
WAIT_TICKS:
    SUB       r24, r24, 1
    QBLT      WAIT_TICKS, r24, 1
    LDI       r24,0xff  // needed for adjusting time exactly
    RET 

// channel 3 , sysevent 20, host 3
CONF_INTERRUPT:
    MOV    r30, CMR5
    MOV    r0, 0x03
    SBBO   r0, r30, 0, 4
    MOV    r30, HMR0
    MOV    r0, 0x03000000
    SBBO   r0, r30, 0, 4
    MOV    r30, ESR0
    MOV    r0, 1<<20
    SBBO   r0, r30, 0, 4
    MOV    r30, SECR0
    MOV    r0, 1<<20
    SBBO   r0, r30, 0, 4
    MOV    r30, HIEISR
    MOV    r0, 0x3 
    SBBO   r0, r30, 0, 4
    MOV    r30, GER 
    MOV    r0, 0x01
    SBBO   r0, r30, 0, 4
    JMP    CONT

In []:
!lsmod | grep uio
In []:
!cat /proc/interrupts

uio

uio = userspace I/O
This interface allows the ability to write the majority of a driver in userspace with only very shell of a driver in the kernel itself. It uses a char device and sysfs to interact with a userspace process to process interrupts and control memory accesses. (Quoted from Greg Kroah-Hartman's log)

Puntos clave de este método

  • Hay que configurar el INTC: channel, sysevent, host
  • Se requiere un módulo del kernel: uio_pruss
  • Dicho módulo nos crea unos dispositivos en /dev, /dev/uio1 es el que usamos
  • Desde python nos basta hacer un read() bloqueante a dicho dispositivo
  • Desde pruss generamos la interrupción escribiendo en r31
In [2]:
!lsmod
Module                  Size  Used by
pwm_test                3690  0 
g_multi                47200  0 
libcomposite           13915  1 g_multi
uio_pruss               3822  0 

In []:
0B_timeInterruptMultiPru

Capture interrupt PRUSS->Host: different interrupt per PRU

In []:
import os
import sys 
import mmap

PRU_ID = 0   #PRU identifier, 0 or 1
PATHBIN = '/home/ubuntu/bonesketches/tiempos/bin/tiemposInterruptMultiPru.bin'  #assembled code to load
TICKSTODELAY = 0x00142CA

#addresses
PRUSSMEM = (0x4a300000, 0x80000)     #(addr,size) pag 215 spruh73c.pdf
CTRLREG  = [0x00022000, 0x00024000]  #pag 19 am335xPruReferenceGuide.pdf
IRAM     = [0x00034000, 0x00038000]  #pag 19 am335xPruReferenceGuide.pdf
DATARAM  = [0x00000000, 0x00002000]  #pag 19 am335xPruReferenceGuide.pdf
SHARRAM  = (0x10000, 0x3000)         #(addr,size) pag 19 am335xPruReferenceGuide.pdf
REGISTER = [0x00022400, 0x00024400]  #pag 19 am335xPruReferenceGuide.pdf

#requirements
if sys.version_info[0] != 3:
    sys.exit("python3 required")
if os.geteuid() != 0:
    sys.exit("You need to have root privileges")

def write_reg(val, nreg):
    """write val to register rnreg"""
    mm.seek(REGISTER[PRU_ID]+nreg*4)
    for i in range(0,32,8):
        mm.write_byte((val >> i) & 0xff)

#access to PRUSS memory
fdm = os.open("/dev/mem", os.O_RDWR | os.O_SYNC)
mm = mmap.mmap(fdm, PRUSSMEM[1], mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE, 0, PRUSSMEM[0])

#read binary file to memory
fdbin = open(PATHBIN, 'rb')
ba = fdbin.read()
fdbin.close()

#disable
mm.seek(CTRLREG[PRU_ID])
mm.write_byte(1)    #disable, pag. 69 am335xPruReferenceGuide.pdf

#load binary file
mm.seek(IRAM[PRU_ID])
mm.write(ba)

#clear beginning of memory
mm.seek(DATARAM[PRU_ID])
mm.write(b'\0'*16)

#clear registers
mm.seek(REGISTER[PRU_ID])
mm.write(b'\0'*29*4)

#pass PRU_ID to r5, this way, assembler code is agnostic of which PRU is executing
write_reg(PRU_ID, 5)

#pass ticks to delay to r24
write_reg(TICKSTODELAY, 24)

#for capturing events PRU->host
fdi = os.open("/dev/uio"+str(PRU_ID), os.O_RDWR | os.O_SYNC)

#enable/run
mm.seek(CTRLREG[PRU_ID])
mm.write_byte(2)    #enable, pag. 69 am335xPruReferenceGuide.pdf

#wait for PRU to halt (interrupt method)$
os.read(fdi, 4)
os.close(fdi)

#display beginning memory
mm.seek(DATARAM[PRU_ID])
for i in range(8):
    print("%02X "%(mm.read_byte()), end="")
print()
for i in range(8):
    print("%02X "%(mm.read_byte()), end="")
print()

#display registers
mm.seek(REGISTER[PRU_ID])
for i in range(32):
    print("r%d = %02X %02X %02X %02X"%(i, mm.read_byte(),mm.read_byte(),mm.read_byte(),mm.read_byte()))

#close
mm.close()
os.close(fdm)
In [1]:
!cat ../bonesketches/tiempos/src/tiemposInterruptMultiPru.p
.origin 0
.entrypoint TIEMPOS

// see am335xPruReferenceGuide.pdf
#define INTC 0x20000
#define SIPR0 INTC+0xd00
#define SIPR1 INTC+0xd04
#define CMR0  INTC+0x400
#define CMR1  INTC+0x404
#define CMR2  INTC+0x408
#define CMR3  INTC+0x40C
#define CMR4  INTC+0x410
#define CMR5  INTC+0x414
#define CMR6  INTC+0x418
#define CMR7  INTC+0x41C
#define CMR8  INTC+0x420
#define CMR9  INTC+0x424
#define CMR10 INTC+0x428
#define CMR11 INTC+0x42C
#define CMR12 INTC+0x430
#define CMR13 INTC+0x434
#define CMR14 INTC+0x438
#define CMR15 INTC+0x43C
#define HMR0  INTC+0x800
#define HMR1  INTC+0x804
#define HMR2  INTC+0x808
#define SITR0 INTC+0xD80
#define SITR1 INTC+0xD84
#define ESR0  INTC+0x300
#define ESR1  INTC+0x304
#define SECR0 INTC+0x280
#define SECR1 INTC+0x284
#define HIEISR INTC+0x34
#define GER   INTC+0x010
#define REVID INTC+0x000
#define PRU0CTRL 0x022000
#define PRU1CTRL 0x024000


TIEMPOS:
    // r5 must be loaded with PRU_ID (0 or 1)
    // r24 must be loaded with # of ticks to be delayed
    QBEQ      CONF_INTERRUPT_PRU0, r5, 0
    JMP       CONF_INTERRUPT_PRU1 
CONT:
    LBBO      r6, r5, 0, 4      //Load control register for counter in r6
    CLR       r16, r6.b0, 3     // for pausing counter 
    SET       r17, r6.b0, 3     // for resuming counter 
    SBBO      r17, r5, 0, 4     // activate counter
    SBCO      r24, C24, 8, 4    //store r24 (debug) 
    SBBO      r16, r5, 0x0c, 4  // counter to 0
    CALL      DELAY_TICKS
    SBBO      r16, r5, 0, 4     //stop counter
    LBBO      r7, r5, 0x0c, 4   //read counter
    SBCO      r7, C24, 4, 4     //store counter (debug) 
    // Send notification to Host for program completion
    MOV       r31.b0, r2.b0
    HALT

DELAY_TICKS:
    //r24->num ticks
    LSR       r24, r24, 1
    SUB       r24, r24, 3
WAIT_TICKS:
    SUB       r24, r24, 1
    QBLT      WAIT_TICKS, r24, 1
    LDI       r24,0xff  // needed for adjusting time exactly
    RET 

// channel 3 , sysevent 20, host 3
CONF_INTERRUPT_PRU1:
    MOV    r30, CMR5
    MOV    r0, 0x03
    SBBO   r0, r30, 0, 4
    MOV    r30, HMR0
    MOV    r0, 0x03000000
    SBBO   r0, r30, 0, 4
    MOV    r30, ESR0
    MOV    r0, 1<<20
    SBBO   r0, r30, 0, 4
    MOV    r30, SECR0
    MOV    r0, 1<<20
    SBBO   r0, r30, 0, 4
    MOV    r30, HIEISR
    MOV    r0, 0x3 
    SBBO   r0, r30, 0, 4
    MOV    r30, GER 
    MOV    r0, 0x01
    SBBO   r0, r30, 0, 4
    MOV    r5, PRU1CTRL // r5 <- address of PRU control
    MOV    r2, (1 << 5) + 4  // r2 <- trigger sys interrupt 20
    JMP    CONT

// channel 2 , sysevent 19, host 2
CONF_INTERRUPT_PRU0:
    MOV    r30, CMR4
    MOV    r0, 0x02000000
    SBBO   r0, r30, 0, 4
    MOV    r30, HMR0
    MOV    r0, 0x00020000
    SBBO   r0, r30, 0, 4
    MOV    r30, ESR0
    MOV    r0, 1<<19
    SBBO   r0, r30, 0, 4
    MOV    r30, SECR0
    MOV    r0, 1<<19
    SBBO   r0, r30, 0, 4
    MOV    r30, HIEISR
    MOV    r0, 0x2 
    SBBO   r0, r30, 0, 4
    MOV    r30, GER 
    MOV    r0, 0x01
    SBBO   r0, r30, 0, 4
    MOV    r5, PRU0CTRL // r5 <- address of PRU control
    MOV    r2, (1 << 5) + 3  // r2 <- trigger sys interrupt 19
    JMP    CONT

Puntos clave de este método

  • Hay que configurar el INTC: channel, sysevent, host de forma diferente para cada PRU
  • Se requiere un módulo del kernel: uio_pruss
  • Dicho módulo nos crea unos dispositivos en /dev, /dev/uio[0-1] es el que usamos
  • Desde python nos basta hacer un read() bloqueante a dicho dispositivo
  • Desde pruss generamos la interrupción escribiendo en r31
In []:
0C_weather

Measurement of humidity & temperature

am2302 has one wire interface: AM2302.pdf

In []:
import os
import sys 
import mmap
import time
import struct

PRU_ID = 0   #PRU identifier, 0 or 1
PATHBIN = '../bin/temp.bin'  #assembled code to load
PATHRES = '../bin/result.txt'  #file for weather result

#addresses
PRUSSMEM = (0x4a300000, 0x80000)     #(addr,size) pag 215 spruh73c.pdf
CTRLREG  = [0x00022000, 0x00024000]  #pag 19 am335xPruReferenceGuide.pdf
IRAM     = [0x00034000, 0x00038000]  #pag 19 am335xPruReferenceGuide.pdf
DATARAM  = [0x00000000, 0x00002000]  #pag 19 am335xPruReferenceGuide.pdf
SHARRAM  = (0x10000, 0x3000)         #(addr,size) pag 19 am335xPruReferenceGuide.pdf
REGISTER = [0x00022400, 0x00024400]  #pag 19 am335xPruReferenceGuide.pdf

#requirements
if sys.version_info[0] != 3:
    sys.exit("python3 required")
if os.geteuid() != 0:
    sys.exit("You need to have root privileges")

#write val to register rnreg
def write_reg(val, nreg):
    mm.seek(0)
    mm[REGISTER[PRU_ID]+nreg*4:REGISTER[PRU_ID]+nreg*4+4] = struct.pack("<L", val)

#stored in memoruy are the durations of pulses, convert to '1' or '0' and then combine the two bytes
def read2bytes():
    cad = ''
    for i in range(8):
        val = mm.read_byte() + (mm.read_byte() << 8)
        if  val < 10000:
            cad = cad + '0'
        else:
            cad = cad + '1'
    negativo = cad[0] == '1'
    if negativo:
        cad[0] = '0'
    toReturn = int(cad,2)
    cad = ''
    for i in range(8):
        val = mm.read_byte() + (mm.read_byte() << 8)
        if  val < 10000:
            cad = cad + '0'
        else:
            cad = cad + '1'
    toReturn = (toReturn * 256.0 + int(cad,2))/10.0
    if negativo:
        toReturn = -toReturn
    return toReturn

#access to PRUSS memory
fdm = os.open("/dev/mem", os.O_RDWR | os.O_SYNC)
mm = mmap.mmap(fdm, PRUSSMEM[1], mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE, 0, PRUSSMEM[0])

#read binary file to memory
fdbin = open(PATHBIN, 'rb')
ba = fdbin.read()
fdbin.close()

#disable
mm.seek(CTRLREG[PRU_ID])
mm.write_byte(1)    #disable, pag. 69 am335xPruReferenceGuide.pdf

#load binary file
mm.seek(IRAM[PRU_ID])
mm.write(ba)

#clear beginning of memory
mm.seek(DATARAM[PRU_ID])
mm.write(b'\0'*80)

#clear registers
mm.seek(REGISTER[PRU_ID])
mm.write(b'\0'*32*4)

#pass CTRLREG to r5, this way, assembler code is agnostic of which PRU is executing
write_reg(CTRLREG[PRU_ID], 5)

# running indicator   1->running    0->halted
mm.seek(DATARAM[PRU_ID])
mm.write_byte(1)

#enable/run
mm.seek(CTRLREG[PRU_ID])
mm.write_byte(2)    #enable, pag. 69 am335xPruReferenceGuide.pdf

#wait for PRU to halt (polling method)
while True:
    mm.seek(DATARAM[PRU_ID])
    if mm.read_byte() == 0:
        break
    time.sleep(0.1)

#create binary result
mm.seek(DATARAM[PRU_ID]+8)
hum = read2bytes()
tem = read2bytes()
print("hum:%.2f temp:%.2f deg"%(hum,tem))
f = open(PATHRES, 'a')
print('%.2f %.2f '%(hum,tem), end="", file=f)
f.close()
In []:
.origin 0
.entrypoint TEMP

#define GPIO1_REGS 0x4804C000
#define MUX 0x44e10838
#define GPIO1_DATAIN GPIO1_REGS | 0x138
#define GPIO1_INPUTPIN 14
#define GPIO1_BUZZEPIN 15
#define GPIO1_USERLED3 24
#define GPIO1_AM_OUT 0x004000
#define GPIO1_CLEARDATAOUT GPIO1_REGS | 0x190
#define GPIO1_SETDATAOUT GPIO1_REGS | 0x194
#define GPIO1_OE GPIO1_REGS | 0x134
#define TIMEOUT45US 9000
#define TIMEOUT85US 17000
#define TIMEOUT70US 14000
#define TIMEOUT75US 15000

TEMP:
    // r5 must be loaded with control register 
    //Load control register for counter in r6
    LBBO      r6, r5, 0, 4
    LDI       r16, 0          // for resetting
    SET       r17, r6.b0, 3   // for resuming
    SBBO      r17, r5, 0, 4   // activate counter

    // STANDBY_INIT
    LBCO      r8, C4, 4, 4
    CLR       r8, r8, 4
    SBCO      r8, C4, 4, 4

    //Inicio variables
    LDI       r2, GPIO1_INPUTPIN
    LDI       r3, GPIO1_USERLED3
    MOV       r9, GPIO1_OE
    LBBO      r10, r9, 0, 4 // r10 <= valor OE de GPIO1
    SET       r10, r2    // como input en reposo a 1
    CLR       r10, r3    // userled3 como salida
    SET       r4, r3   //para encender / apagar led user3
    LDI       r3, GPIO1_BUZZEPIN
    CLR       r10, r3    // buzzer como salida
    SET       r28, r3   //para buzzer
    SBBO      r10, r9, 0, 4
    MOV       r11, GPIO1_SETDATAOUT
    MOV       r20, GPIO1_DATAIN //r20=>addres for DATAIN
    MOV       r12, MUX
    LDI       r18, 0x0f  // as output
    LDI       r19, 0x2f  // as input
    SBBO      r19, r12, 0, 4
    LDI       r29, 8  // indice memoria para guardar tiempos
    LDI       r13, 40  //numero de bits a leer

    SBBO      r4, r11, 0, 4  // enciende led
    // paso a output
    CLR       r10, r2
    SBBO      r10, r9, 0, 4
    SBBO      r18, r12, 0, 4
    // pull the pin high and wait 250 milliseconds
    MOV       r14, GPIO1_SETDATAOUT
    MOV       r15, GPIO1_AM_OUT
    SBBO      r15, r14, 0, 4
    MOV       r24, 50000000
    CALL      DELAY_TICKS
    //Host pulls low 1.5 ms
    MOV       r14, GPIO1_CLEARDATAOUT
    MOV       r15, GPIO1_AM_OUT
    SBBO      r15, r14, 0, 4
    MOV       r24, 300000
    CALL      DELAY_TICKS
    //Host pulls up 40us
    MOV       r14, GPIO1_SETDATAOUT
    MOV       r15, GPIO1_AM_OUT
    SBBO      r15, r14, 0, 4
    MOV       r24, 8000
    CALL      DELAY_TICKS
    //paso a Input
    SET       r10, r2
    SBBO      r10, r9, 0, 4
    SBBO      r19, r12, 0, 4
    MOV       r22, TIMEOUT45US
STILL_HIGH:
    // read input
    LBBO      r21, r20, 0, 4
    //is low?
    QBBC      IS_LOW, r21, GPIO1_INPUTPIN
    //is timeout?
    LBBO      r7, r5, 0x0c, 4
    QBGE      IS_TIMEOUT45US, r22, r7
    JMP       STILL_HIGH
IS_LOW: // sensor pulls low 80us
    SBBO      r16, r5, 0x0c, 4   // counter to 0
    MOV       r22, TIMEOUT85US
STILL_LOW:
    // read input
    LBBO      r21, r20, 0, 4
    // is high?
    QBBS      IS_HIGH, r21, GPIO1_INPUTPIN
    // is timeout?
    LBBO      r7, r5, 0x0c, 4
    QBGE      IS_TIMEOUT85US, r22, r7
    JMP       STILL_LOW
IS_HIGH: // sensor pulls up 80us:
    SBBO      r16, r5, 0x0c, 4   // counter to 0
    MOV       r22, TIMEOUT85US
STILL_HIGH2:
    // read input
    LBBO      r21, r20, 0, 4
    // is high?
    QBBC      IS_LOW2, r21, GPIO1_INPUTPIN
    // is timeout?
    LBBO      r7, r5, 0x0c, 4
    QBGE      IS_TIMEOUT85US, r22, r7
    JMP       STILL_HIGH2
    // Vamos leyendo los bits y almacenamos su duracion
IS_LOW2:
    CALL      START_TRANSMIT
    CALL      READBIT
    SUB       r13, r13, 1
    QBGT      IS_LOW2, r16, r13

    MOV       r11, GPIO1_SETDATAOUT
    MOV       r14, GPIO1_CLEARDATAOUT
    LDI       r13, 200
BUZZER:
    SBBO      r28, r11, 0, 4 
    MOV       r24, 25000 // 125us
    CALL      DELAY_TICKS
    SBBO      r28, r14, 0, 4 
    MOV       r24, 25000 // 125us
    CALL      DELAY_TICKS
    SUB       r13, r13, 1
    QBGT      BUZZER, r16, r13

USCITA:
    MOV       r14, GPIO1_CLEARDATAOUT
    SBBO      r4, r14, 0, 4  // apaga led
    // Send notification to Host for program completion
    LDI       r3, 0
    SBCO      r3, C24, 0, 1
    HALT

IS_TIMEOUT45US:
    MOV       r23, 0xfffffffe   //error timeout 45us
    SBCO      r23, C24, 4, 4
    JMP       USCITA
IS_TIMEOUT70US:
    MOV       r23, 0xfffffffd   //error timeout 70us
    SBCO      r23, C24, 4, 4
    JMP       USCITA
IS_TIMEOUT75US:
    MOV       r23, 0xfffffffc   //error timeout 75us
    SBCO      r23, C24, 4, 4
    JMP       USCITA
IS_TIMEOUT85US:
    MOV       r23, 0xfffffffb   //error timeout 85us
    SBCO      r23, C24, 4, 4
    JMP       USCITA
IS_SHORT:
    MOV       r23, 0xfffffffa   //error is short
    SBCO      r23, C24, 4, 4
    JMP       USCITA

READBIT:
    SBBO      r16, r5, 0x0c, 4   // counter to 0
    MOV       r22, TIMEOUT75US
STILL_HIGH4:
    LBBO      r21, r20, 0, 4  // read input
    QBBC      IS_LOW4, r21, GPIO1_INPUTPIN  // is low?
    LBBO      r7, r5, 0x0c, 4
    QBGE      IS_TIMEOUT75US, r22, r7
    JMP       STILL_HIGH4
IS_LOW4:
    LBBO      r7, r5, 0x0c, 4
    SBCO      r7, C24, r29, 2
    ADD       r29, r29, 2
    RET

START_TRANSMIT:
    SBBO      r16, r5, 0x0c, 4   // counter to 0
    MOV       r22, TIMEOUT70US
STILL_LOW3:
    LBBO      r21, r20, 0, 4  // read input
    QBBS      IS_HIGH3, r21, GPIO1_INPUTPIN  // is high?
    LBBO      r7, r5, 0x0c, 4  // is timeout?
    QBGE      IS_TIMEOUT70US, r22, r7
    JMP       STILL_LOW3
IS_HIGH3:
    RET

DELAY_TICKS:
    //r24->num ticks
    SUB       r24, r24, 7
    LSR       r24, r24, 1
WAIT_TICKS:
    SUB       r24, r24, 1
    QBLT      WAIT_TICKS, r24, 1
    RET

Puntos clave de este método

  • En One Wire tenemos que poner el mismo hilo como salida y luego como entrada
  • En este ejemplo tomamos las medidas de tiempos en pruss y las almacenamos
  • Cuando termina una medida, desde python calculamos los bytes de informacion: bits -> bytes
In []:
0D_ecapInterrupt

Objetivo: crear un driver para leer el am2302 con ecap0 con interrupciones

¿No estaría mucho mejor hacer un read() de /dev/am2302 y que ya nos diera la humedad y la temperatura? Aprovechemos el eCAP = Enhanced Capturer. Programemos el eCAP para que nos interrumpa en los flancos con la medida ya hecha por el HW. Evidentemente, un driver de linux es en C pero usaremos las ventajas de python de prototipar la solución y luego lo pasaremos al "kernel space"

Paso 0: un poco de housekeeping

In [1]:
!reboot
In []:
!lsmod
In []:
!cat /sys/devices/bone_capemgr.9/slots

Unloading various overlays can cause a kernel panic, and cause you to lose your ssh session, along with making the capemgr unpredictable. It's recommended to just restart your system to unload overlays until that issue is resolved. https://learn.adafruit.com/introduction-to-the-beaglebone-black-device-tree/exporting-and-unexporting-an-overlay

Paso 1: poner la multiplexacion correcta del pin ecap0

Programemos la multiplexacion mediante el kernel. Según BBB_SRM, ecap0 está en P9_42.
In [1]:
 !cat /home/ubuntu/bonesketches/kernel/ponMux/ponMux.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <asm/io.h>

#define CONTROL_MODULE 0x44e10000
#define PINMUX_ECAP0 CONTROL_MODULE+0x964      //P9_42
#define PINMUX_EHRPWM2B CONTROL_MODULE+0x824   //P8_13
#define PINMUX_ECAP2 CONTROL_MODULE+0x99c      //P9_28

static void pon(unsigned long addr, unsigned char val) 
{
  void *ptr = ioremap(addr, 4);
  unsigned char val_actual = ioread8(ptr);
  if (val_actual != val) {
    iowrite8(val, ptr);   
    printk(KERN_ALERT "%08lX mode now is %02X\n",addr,val);
  }
  iounmap(ptr);  
}

static int __init ponMux_init(void)
{

  pon(PINMUX_ECAP0, 0x20); // mode 0 input
  pon(PINMUX_EHRPWM2B, 0x04); // mode 4 output
  pon(PINMUX_ECAP2, 0x24); // mode 4 input

  return 0;
}

static void __exit ponMux_exit(void)
{
  printk(KERN_ALERT "Goodbye, cruel world\n");
}

module_init(ponMux_init);
module_exit(ponMux_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Josep Danti <Josep.Danti@gmail.com>");

In [2]:
!insmod /home/ubuntu/bonesketches/kernel/ponMux/ponMux.ko
In [3]:
!dmesg | tail
[   17.693339] hub 2-0:1.0: enabling power on all ports
[   17.793546] hub 2-0:1.0: state 7 ports 1 chg 0000 evt 0000
[   17.793610] hub 2-0:1.0: hub_suspend
[   17.793639] usb usb2: bus auto-suspend, wakeup 1
[   17.916374] CAUTION: musb: Babble Interrupt Occurred
[   18.070833]  gadget: high-speed config #1: Multifunction with RNDIS
[   23.460292] init: plymouth-upstart-bridge main process ended, respawning
[   98.990866] 44E10964 mode now is 20
[   98.994777] 44E10824 mode now is 04
[   98.998556] 44E1099C mode now is 24

Paso 2: Activar el reloj PWM de ecap0

Por defecto el reloj está desactivado por razones de consumo.
In [4]:
import os
import mmap
import struct

CM_PER = (0x44E00000, 0x400)
CM_PER_EPWMSS2_CLKCTRL = 0xd8 #for debugging
CM_PER_EPWMSS0_CLKCTRL = 0xd4

#access clock PWMSS2 (for debugging)
fdm = os.open("/dev/mem", os.O_RDWR | os.O_SYNC)
mm = mmap.mmap(fdm, CM_PER[1], mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE, 0, CM_PER[0])
mm.seek(CM_PER_EPWMSS2_CLKCTRL)
disabled = struct.unpack('<L', mm.read(4))[0] == 0x00030000
if (disabled):
    mm.seek(CM_PER_EPWMSS2_CLKCTRL)
    mm.write(struct.pack('<L', 2))
    print("clock enabled for EPWMSS2")
mm.close()
os.close(fdm)

#access clock PWMSS0
fdm = os.open("/dev/mem", os.O_RDWR | os.O_SYNC)
mm = mmap.mmap(fdm, CM_PER[1], mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE, 0, CM_PER[0])
mm.seek(CM_PER_EPWMSS0_CLKCTRL)
disabled = struct.unpack('<L', mm.read(4))[0] == 0x00030000
if (disabled):
    mm.seek(CM_PER_EPWMSS0_CLKCTRL)
    mm.write(struct.pack('<L', 2))
    print("clock enabled for EPWMSS0")
mm.close()
os.close(fdm)
clock enabled for EPWMSS2
clock enabled for EPWMSS0

Paso 3: Un par de funciones para activar/desactivar ecap0

In [5]:
import os
import mmap
import struct

#para programar el PWMSS0
PWM_SUBSYSTEM0 = (0x48300000, 0x260)
eCAP0 = 0x100
ECCTL1 = eCAP0 +  0x28
ECCTL2 = eCAP0 +  0x2a
CAP1 = eCAP0 + 0x08
CAP2 = eCAP0 + 0x0c
CAP3 = eCAP0 + 0x10
CAP4 = eCAP0 + 0x14
CTRPHS = eCAP0 + 0x04
ECCLR = eCAP0 + 0x30
ECEINT = eCAP0 + 0x2c
ECFLAG = eCAP0 + 0x2e

def getMem(tupl):
    fdm_0 = os.open("/dev/mem", os.O_RDWR | os.O_SYNC)
    mm_0 = mmap.mmap(fdm_0, tupl[1], mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE, 0, tupl[0])    
    return (fdm_0, mm_0)

def stopAndClear():
    mem_pwmss0 = getMem(PWM_SUBSYSTEM0)
    
    #stop ecap
    mem_pwmss0[1].seek(ECEINT) # no interrupts
    mem_pwmss0[1].write(struct.pack('<H', 0)) 
    mem_pwmss0[1].seek(ECCLR) # clear flags
    mem_pwmss0[1].write(struct.pack('<H', 0xffff))
    mem_pwmss0[1].seek(ECCTL2) # stop
    mem_pwmss0[1].write(struct.pack('<H', 0x00))  
    mem_pwmss0[1].seek(CAP1)
    mem_pwmss0[1].write(b'\0'*16)
    
    mem_pwmss0[1].close()
    os.close(mem_pwmss0[0])

def configureAndEnable():
    mem_pwmss0 = getMem(PWM_SUBSYSTEM0)

    # configure peripheral  
    mem_pwmss0[1].seek(CTRPHS) # phase
    mem_pwmss0[1].write(struct.pack('<L', 0))
    mem_pwmss0[1].seek(ECCTL1) # program for deltas
    mem_pwmss0[1].write(struct.pack('<H', 0x1ee))
    mem_pwmss0[1].seek(ECCTL2) # activate
    mem_pwmss0[1].write(struct.pack('<H', 0xd6))

    #enable interrupts coming from ecap0
    mem_pwmss0[1].seek(ECEINT) # ecap0 interrupts for 4 events
    mem_pwmss0[1].write(struct.pack('<H', 0x1e))

    mem_pwmss0[1].close()
    os.close(mem_pwmss0[0])
In [6]:
#lo ponemos desactivado
stopAndClear()

Paso 4. (Debug only) Activamos señal de salida para debug en PWMSS2

Hay que conectar la salida de PWM2SS (P8_13) a la entrada de ecap0 (P9_42)
In [7]:
import os
import mmap
import struct

#pwm ss 2
PWM_SUBSYSTEM2 =    (0x48304000, 0x260)
ePWM2 = 0x200
TBCTL = ePWM2+0
AQCTLB = ePWM2+0x18
TBPRD = ePWM2+0x0a
CMPB = ePWM2+0x14

#access to pwm subsystem 2
fdm = os.open("/dev/mem", os.O_RDWR | os.O_SYNC)
mm = mmap.mmap(fdm, PWM_SUBSYSTEM2[1], mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE, 0, PWM_SUBSYSTEM2[0])

mm.seek(AQCTLB)                     #Action Qualifier
mm.write(struct.pack('<H', 0x010a))  

mm.seek(TBPRD)
mm.write(struct.pack('<H', 32552))   # 65104 => 1 second

mm.seek(TBCTL)
mm.write(struct.pack('<H', 0xdf00))   # free run /128  /12 => 65104.16 hz (SYSCLK = 100MHz)

mm.seek(CMPB)
mm.write(struct.pack('<H', 32552//3))

mm.close()
os.close(fdm)

Paso 5. Instalamos la rutina de interrupción

Está integrado con scull para crear las 4 pipas necesarias para cada tipo de evento de ecap0. Scull es uno de los ejemplos del libro "Linux Device Drivers" de O'Reilly
In [8]:
!cat /home/ubuntu/kernel/examples/scull/ecap0.c
#include "scull.h"

#define ECAP0IRQ (31+16)
#define ILR31 0x48200000 + 0x17c
#define INTC_CONTROL    0x48200000+0x48
#define INTC_MIR_CLEAR0 0x48200000+0x88
#define INTC_MIR_SET0   0x48200000+0x8C

#define PWM_SUBSYSTEM0 0x48300000
#define eCAP0 (0x100 +  PWM_SUBSYSTEM0)
#define ECCLR (eCAP0 + 0x30)
#define ECFLAG (eCAP0 + 0x2e)
#define CAP1 (eCAP0 + 0x08)

static void *ptr_CAP1;  //allocated memory for handling on interrupts

static void calculate_flags_and_notify(unsigned short flags, unsigned short *nflags, int which)
{
  unsigned long toWrite = 0xdeadbeef;
  if (flags & (1<<(which+1))) {
    *nflags |= (1<<(which+1));
    if (which < 4) {
      toWrite = ioread32(ptr_CAP1+(which<<2));
    }
    scull_p_write_from_ecap0(which, toWrite);  // write to the pipe
  }
}

static irqreturn_t ecap0irqhandler(int irq, void *p)
{
  void *ptr;
  unsigned short flags, nflags = 1;
  int i;

  ptr = ioremap(ECFLAG,2);  /* get flags that caused interruption */
  flags = ioread16(ptr);
  iounmap(ptr);
  for (i = 0; i < 7; i++) {
    calculate_flags_and_notify(flags, &nflags, i);
  }
  ptr = ioremap(ECCLR,2);   /* clear flags that caused interruption */
  iowrite16(nflags, ptr);
  iounmap(ptr);
  return IRQ_HANDLED;
}

static void stopAndClear(void)
{
  void *ptr;
  /* disable globalinterrupts from ecap0 */
  ptr = ioremap(INTC_MIR_SET0,4);
  iowrite32(1<<31, ptr);
  iounmap(ptr);
}

int ecap0_init(void)
{
  void *ptr;
  int ret;

  /* Install interrupt handler */
  ret = request_irq(ECAP0IRQ, ecap0irqhandler, 0, "ecap0", NULL);
  if (ret!=0) {
    printk("ERROR: Cannot request ECAP0IRQ %d", ECAP0IRQ);
    printk(" - code %d , EIO %d , EINVAL %d\n", ret, EIO, EINVAL);
  }

  stopAndClear();
  ptr_CAP1 = ioremap(CAP1, 16);

  /* enable interrupts from ecap0 */
  ptr= ioremap(ILR31,4);  // IRQ (not FIQ), medium priority
  iowrite32(0xc0, ptr);
  iounmap(ptr);
  ptr= ioremap(INTC_MIR_CLEAR0,4);  // clear mask
  iowrite32(1<<31, ptr);
  iounmap(ptr);
  printk(KERN_ALERT "ecap0 interrupts enabled!\n");

  return 0;
}

void ecap0_exit(void)
{
  stopAndClear();
  free_irq(ECAP0IRQ,NULL);
  iounmap(ptr_CAP1);
}

In []:
!cat /home/ubuntu/kernel/examples/scull/pipe.c
In [9]:
!cat /home/ubuntu/kernel/examples/scull/scull_load
#!/bin/sh
# $Id: scull_load,v 1.4 2004/11/03 06:19:49 rubini Exp $
module="scull"
device="scull"
#mode="400"
mode="666"

# Group: since distributions do it differently, look for wheel or use staff
if grep -q '^staff:' /etc/group; then
    group="staff"
else
    group="wheel"
fi

# invoke insmod with all arguments we got
# and use a pathname, as insmod doesn't look in . by default
/sbin/insmod ./$module.ko $* || exit 1

# retrieve major number
major=$(awk "\$2==\"$module\" {print \$1}" /proc/devices)

# Remove stale nodes and replace them, then give gid and perms
# Usually the script is shorter, it's scull that has several devices in it.

rm -f /dev/${device}pipe[0-6]
mknod /dev/${device}pipe0 c $major 4
mknod /dev/${device}pipe1 c $major 5
mknod /dev/${device}pipe2 c $major 6
mknod /dev/${device}pipe3 c $major 7
mknod /dev/${device}pipe4 c $major 8
mknod /dev/${device}pipe5 c $major 9
mknod /dev/${device}pipe6 c $major 10
#ln -sf ${device}pipe0 /dev/${device}pipe
chgrp $group /dev/${device}pipe[0-6] 
chmod $mode  /dev/${device}pipe[0-6]

In [10]:
!(cd /home/ubuntu/kernel/examples/scull; ./scull_load)

Paso 6. (debug only) empezar y atender a las interrupciones

In [11]:
import mmap
import os

fifos = [open("/dev/scullpipe"+str(i), "rb") for i in range(4)]
In [12]:
import mmap
import os
import struct 

PWM_SUBSYSTEM0 =    (0x48300000, 0x260)
eCAP0 = 0x100
ECCTL1 = eCAP0 +  0x28
ECCTL2 = eCAP0 +  0x2a
CAP1 = eCAP0 + 0x08
CAP2 = eCAP0 + 0x0c
CAP3 = eCAP0 + 0x10
CAP4 = eCAP0 + 0x14
CTRPHS = eCAP0 + 0x04
ECCLR = eCAP0 + 0x30
ECEINT = eCAP0 + 0x2c

configureAndEnable()

#access to eCAP0
fdm = os.open("/dev/mem", os.O_RDWR | os.O_SYNC)
mm = mmap.mmap(fdm, PWM_SUBSYSTEM0[1], mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE, 0, PWM_SUBSYSTEM0[0])

for k in range(16):
    print(struct.unpack("<L", fifos[k % 4].read(4))[0])

[i.close() for i in fifos]
mm.close()
os.close(fdm)

stopAndClear()
34164814
16667135
33334271
16667135
33334271
16667135
33334271
16667135
33334271
16667135
33334271
16667135
33334271
16667135
33334271
16667135

In [13]:
#podemos ver las interrupciones que ha generado el ecap0:
!cat /proc/interrupts
           CPU0       
 23:          0      INTC  tps65217
 28:       4689      INTC  edma
 30:          0      INTC  edma_error
 34:       5559      INTC  musb-hdrc.0.auto
 35:          1      INTC  musb-hdrc.1.auto
 36:          0      INTC  pruss_evt0
 37:          0      INTC  pruss_evt1
 38:          0      INTC  pruss_evt2
 39:          0      INTC  pruss_evt3
 40:          0      INTC  pruss_evt4
 41:          0      INTC  pruss_evt5
 42:          0      INTC  pruss_evt6
 43:          0      INTC  pruss_evt7
 46:         93      INTC  4819c000.i2c
 47:         17      INTC  ecap0
 56:          0      INTC  4a100000.ethernet
 57:          0      INTC  4a100000.ethernet
 58:          0      INTC  4a100000.ethernet
 59:          0      INTC  4a100000.ethernet
 80:      15147      INTC  mmc0
 83:      31209      INTC  gp_timer
 86:       2381      INTC  44e0b000.i2c
 88:        470      INTC  OMAP UART0
 91:          0      INTC  rtc0
 92:          1      INTC  rtc0
125:          0      INTC  53100000.sham
150:          0      GPIO  mmc0
IPI0:          0  CPU wakeup interrupts
IPI1:          0  Timer broadcast interrupts
IPI2:          0  Rescheduling interrupts
IPI3:          0  Function call interrupts
IPI4:          0  Single function call interrupts
IPI5:          0  CPU stop interrupts
Err:          0

Paso 7. (Debug only) Controlar salida PWM con interrupciones

Hay que conectar el ecap0 (P9_42) a un LED
In [14]:
import os
import mmap
import struct
import time

PWM_SUBSYSTEM0 =    (0x48300000, 0x260)
eCAP0 = 0x100
ECCTL1 = eCAP0 +  0x28
ECCTL2 = eCAP0 +  0x2a
CAP1 = eCAP0 + 0x08
CAP2 = eCAP0 + 0x0c
CAP3 = eCAP0 + 0x10
CAP4 = eCAP0 + 0x14
CTRPHS = eCAP0 + 0x04
ECCLR = eCAP0 + 0x30
ECEINT = eCAP0 + 0x2c

fifos = [open("/dev/scullpipe"+str(i), "rb") for i in range(7)]
stopAndClear()

#access to eCAP0
fdm = os.open("/dev/mem", os.O_RDWR | os.O_SYNC)
mm = mmap.mmap(fdm, PWM_SUBSYSTEM0[1], mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE, 0, PWM_SUBSYSTEM0[0])

mm.seek(CAP3)  # period
mm.write(struct.pack('<L', int(250e6)))

mm.seek(CAP4)  #duty
mm.write(struct.pack('<L', int(200e6))) #tiempo high

mm.seek(CTRPHS)  #phase
mm.write(struct.pack('<L', int(0))) # con 0 empieza en high

mm.seek(ECCTL1) # program for PWM
mm.write(struct.pack('<H', 0x0))

mm.seek(ECCTL2) # activate
mm.write(struct.pack('<H', 0x03b0))

#enable interrupts coming from ecap0
mm.seek(ECEINT) # ecap0 interrupts for events
mm.write(struct.pack('<H', 0xc0))

time_old = time.time()
fifos[6].read(4)  #ctr = cmp
time_new = time.time()
dif1 = time_new-time_old
time_old = time_new

fifos[5].read(4)  #ctr = prd
time_new = time.time()
dif2 = time_new-time_old
time_old = time_new

mm.seek(CAP2)  #duty
mm.write(struct.pack('<L', int(100e6))) #tiempo high

time_old = time.time()
fifos[6].read(4)  #ctr = cmp
time_new = time.time()
dif3 = time_new-time_old
time_old = time_new


mm.seek(ECCTL2) # stop
mm.write(struct.pack('<H', 0x00))    

time_new = time.time()
dif4 = time_new-time_old
time_old = time_new

print("dif ", end = "")
print(dif1, end='')
print (" ", end='')
print(dif2, end='')
print (" ", end='')
print(dif3, end='')
print (" ", end='')
print(dif4)

    
[i.close() for i in fifos]
mm.close()
os.close(fdm)
stopAndClear()
dif 2.000514030456543 0.5001356601715088 0.9943082332611084 0.0058782100677490234

Paso 8. Conectar el sensor a P9_42, ¡One Wire!

In []:
stopAndClear()
!rmmod scull

Paso 9. Pasar a am2302.c lo aprendido anteriormente

In [15]:
!cat /home/ubuntu/kernel/am2302/am2302.c
#include "pipe.h"

//#define _DEBUG_

#define CONTROL_MODULE 0x44e10000
#define CONTROL_MODULE_SIZE 0x2000
#define PINMUX_ECAP0 0x964      //P9_42

#define INTC  0x48200000
#define INTC_SIZE 0x1000
#define ECAP0IRQ (31+16)
#define ILR31 0x17c
#define INTC_CONTROL    0x48
#define INTC_MIR_CLEAR0 0x88
#define INTC_MIR_SET0   0x8C

#define CM_PER 0x44E00000
#define CM_PER_SIZE 0x400
#define CM_PER_EPWMSS0_CLKCTRL 0xd4

#define PWM_SUBSYSTEM0 0x48300000
#define PWM_SUBSYSTEM0_SIZE 0x260
#define eCAP0 0x100
#define ECCLR (eCAP0 + 0x30)
#define ECFLAG (eCAP0 + 0x2e)
#define CAP1 (eCAP0 + 0x08)
#define CAP2 (eCAP0 + 0x0c)
#define CAP3 (eCAP0 + 0x10)
#define CAP4 (eCAP0 + 0x14)
#define ECCTL1 (eCAP0 +  0x28)
#define ECCTL2 (eCAP0 +  0x2a)
#define CTRPHS (eCAP0 + 0x04)
#define ECEINT (eCAP0 + 0x2c)
#ifdef _DEBUG_
typedef struct { //for debugging
  unsigned long delta;
  unsigned long ht;
  unsigned short bit;
  unsigned short cap;
  int state;
} t_delta;
static t_delta tempo[100]; //for debugging
static int itempo; //for debugging
#endif
static void *ptr_pwmss0;  //allocated memory for handling pwmss0
static void *ptr_intc;  //allocated memory for interrupt controller
static int indexBit = 0;  // index to the bit that is in process
static int state = 0; // 0 -> idle    1 -> host start     2 -> host start last high    3 -> sensor start    4 -> receiving bits    5 -> receiving checksum
static unsigned long ht = 0;  // it will hold the sensor output value of humidity & temperature 

void am2302_startMeasure() {
  iowrite32(25150000, ptr_pwmss0 + CAP3);  // period 250ms high + 1.5ms low
  iowrite32(25000000, ptr_pwmss0 + CAP4);  // duty: 250ms high
  iowrite32(0, ptr_pwmss0 + CTRPHS);  // phase 0 => starts with high
  iowrite16(0, ptr_pwmss0 + ECCTL1);  // program for PWM
  iowrite16(0x03b0, ptr_pwmss0 + ECCTL2);  // activate
  iowrite16(0xc0, ptr_pwmss0 + ECEINT);  // enable interrupts coming from ecap0
  state = 1; // host start
}

static void invalid_state(void) {
  iowrite32(1<<31, ptr_intc+INTC_MIR_SET0);  // disable interrupts
  printk(KERN_ERR "am2302 invalid state\n");
}

static void calculate_flags_and_notify(unsigned short flags, unsigned short *nflags, int which)
{
  unsigned long toWrite = 0xdeadbeef;
  unsigned char bit;
  if (flags & (1<<(which+1))) {
    *nflags |= (1<<(which+1));
    switch (state) {
      case 0:  // idle
        break;
      case 1: // host start
        if (which == 5) {  //ctr = prd  sube a high
          iowrite32(4000, ptr_pwmss0 + CAP2);  // high time 40us
          state++;
        }
        break;
      case 2: // host start last high
        if (which == 6) {  // ctr = cmp  ==> set input mode
          iowrite16(0, ptr_pwmss0 + ECCTL2);  // deactivate
          //iowrite32(0, ptr_pwmss0 + CTRPHS); // phase
          iowrite16(0x1ee, ptr_pwmss0 + ECCTL1);   // program for deltas
          iowrite16(0xd6, ptr_pwmss0 + ECCTL2);    // activate
          iowrite16(0x1e, ptr_pwmss0 + ECEINT);    // ecap0 interrupts for 4 events
          state++;
        }
        break;
      case 3:  // sensor start
#ifdef _DEBUG_
        toWrite = ioread32(ptr_pwmss0 + CAP1 + (which<<2));
        tempo[itempo].delta = toWrite;
        tempo[itempo].cap = which;
        tempo[itempo].bit = (toWrite > 4850)?1:0;
        tempo[itempo].ht = 0;
        tempo[itempo].state = state;
        itempo++;
        itempo = itempo % 100;
#endif
        if (which & 1) {  // looking for sensor high 80us
          toWrite = ioread32(ptr_pwmss0 + CAP1 + (which<<2));
          if ((toWrite < 8400) && (toWrite > 7600)) {
            indexBit = 0;
            state++;
            ht = 0;
          }
        }
        break;
      case 4: // receiving 32 bits
        if (which & 1) {   // just interested in the odd events, corresponding to high values in input
          toWrite = ioread32(ptr_pwmss0 + CAP1 + (which<<2));
          bit = (toWrite > 4850)?1:0;
          ht |= bit;
          if (++indexBit >= 32) {
            state++;
          }
          else {
            ht = ht << 1;
          }
        }
#ifdef _DEBUG_
        toWrite = ioread32(ptr_pwmss0 + CAP1 + (which<<2));
        tempo[itempo].delta = toWrite;
        tempo[itempo].cap = which;
        tempo[itempo].bit = (toWrite > 4850)?1:0;
        tempo[itempo].ht = ht;
        tempo[itempo].state = state;
        itempo++;
        itempo = itempo % 100;
#endif
        break;
      case 5: // receiving checksum
#ifdef _DEBUG_
        toWrite = ioread32(ptr_pwmss0 + CAP1 + (which<<2));
        tempo[itempo].delta = toWrite;
        tempo[itempo].cap = which;
        tempo[itempo].bit = (toWrite > 4850)?1:0;
        tempo[itempo].ht = 0;
        tempo[itempo].state = state;
        itempo++;
        itempo = itempo % 100;
#endif
        if (which & 1) {   // just interested in the odd events, corresponding to high values in input
          if (++indexBit >= 40) {
#ifdef _DEBUG_
            itempo = 0;
#endif
            indexBit = 0;
            state = 0;
            iowrite16(0, ptr_pwmss0 + ECEINT); //no interrupts
            iowrite16(0xffff, ptr_pwmss0 + ECCLR); //clear flags
            iowrite16(0, ptr_pwmss0 + ECCTL2); //stop
            iowrite32(0, ptr_pwmss0 + CAP1);
            iowrite32(0, ptr_pwmss0 + CAP2);
            iowrite32(0, ptr_pwmss0 + CAP3);
            iowrite32(0, ptr_pwmss0 + CAP4);
            write_from_am2302(ht);
          }
        }
        break;
      default:
        invalid_state();
        state = 0;
    }
  }
}

static irqreturn_t am2302irqhandler(int irq, void *p)
{
  unsigned short flags, nflags = 1;
  int i;

  flags = ioread16(ptr_pwmss0+ECFLAG);
  for (i = 0; i < 7; i++) {
    calculate_flags_and_notify(flags, &nflags, i);
  }
  iowrite16(nflags, ptr_pwmss0+ECCLR);
  return IRQ_HANDLED;
}

int am2302_init(void)
{
  int ret;
  void *ptr_clk, *ptr_mux;

  ptr_pwmss0 = ioremap(PWM_SUBSYSTEM0, PWM_SUBSYSTEM0_SIZE);
  ptr_intc = ioremap(INTC, INTC_SIZE);
  ptr_clk = ioremap(CM_PER, CM_PER_SIZE);
  ptr_mux = ioremap(CONTROL_MODULE, CONTROL_MODULE_SIZE);

  /* pinmux correct? */
  iowrite32(0x20, ptr_mux+PINMUX_ECAP0);  // mode 0 input
  iounmap(ptr_mux);

  /* clock enabled? */
  iowrite32(2, ptr_clk+CM_PER_EPWMSS0_CLKCTRL);
  iounmap(ptr_clk);

  /* Install interrupt handler */
  ret = request_irq(ECAP0IRQ, am2302irqhandler, 0, "am2302", NULL);

  /* enable interrupts from ecap0 */
  iowrite32(1<<31, ptr_intc+INTC_MIR_SET0); //  disable interrupts
  //iowrite32(0xc0, ptr_intc + ILR31); // IRQ (not FIQ), medium priority
  iowrite32(0x00, ptr_intc + ILR31);
  iowrite32(1<<31, ptr_intc + INTC_MIR_CLEAR0); // clear mask = enable interrupts
  printk(KERN_ALERT "am2302 interrupts enabled!\n");


  //DEBUG ONLY
  //am2302_startMeasure(); 
  return 0;
}

void am2302_exit(void)
{
#ifdef _DEBUG_
  int i;
#endif
  /* disable globalinterrupts from ecap0 */
  iowrite32(1<<31, ptr_intc+INTC_MIR_SET0);
  free_irq(ECAP0IRQ,NULL);
  iounmap(ptr_pwmss0);
  iounmap(ptr_intc);
#ifdef _DEBUG_
  printk(KERN_ALERT "am2302 state: %d %d %08lX\n", state, indexBit, ht);
  for (i = 0; i < 83; i++) {
    printk(KERN_ALERT "am2302 %d %d %ld %d %d %08lX\n", i, tempo[i].cap, tempo[i].delta, tempo[i].bit, tempo[i].state, tempo[i].ht);
  }
#endif
}


Paso 10. Conseguir hacer un open y read a /dev/am2302 y que ya nos de el valor

In []:
!cat /home/ubuntu/kernel/am2302/pipe.c

Paso 11. Compilar, cargar y probar

In [16]:
!(cd /home/ubuntu/bonesketches/kernel/am2302; make)
make -C /lib/modules/3.8.13-bone40/build M=/home/ubuntu/bonesketches/kernel/am2302 modules
make[1]: Entering directory `/usr/src/linux-headers-3.8.13-bone40'
  Building modules, stage 2.
  MODPOST 1 modules
make[1]: Leaving directory `/usr/src/linux-headers-3.8.13-bone40'

In []:
!(cd /home/ubuntu/bonesketches/kernel/am2302; ./ecap0_am2302_load)
!(dmesg | tail)
!lsmod
In [18]:
import struct
def process(u16):
   negativo = u16 & 0x8000 
   val = u16
   val = val & 0x7fff
   toReturn = float(val)/10.0
   if negativo:
       toReturn = -toReturn
   return toReturn

f = open("/dev/am2302","rb")
dat = f.read(4)
f.close()
dat = struct.unpack("<L",dat)[0]
hum = dat >> 16
hum = process(hum)
tem = dat & 0x0000ffff
tem = process(tem)
print("Hum: %.2f  Temp: %.2f"%(hum,tem))
---------------------------------------------------------------------------
OSError                                   Traceback (most recent call last)
<ipython-input-18-54c6b34c911a> in <module>()
     10 
     11 f = open("/dev/am2302","rb")
---> 12 dat = f.read(4)
     13 f.close()
     14 dat = struct.unpack("<L",dat)[0]

OSError: [Errno 62] Timer expired

Puntos clave de este método

  • Qué fácil es cambiar el pinmux desde el kernel ¿verdad?
  • Probamos con LEDS y luego pasamos al kernel cambiando los tiempos
  • Interrupciones HW solamente se pueden atender desde el kernel
  • Usamos el ejemplo scull de partida
  • Comprobamos con una señal del PWMSS2 que capturamos las interrupciones y leemos los tiempos correctamente desde ecap0
  • Implementamos el open(), close() y read() en la parte del kernel
  • Construimos la palabra humedad+temperatura conforme nos van interrumpiendo en el kernel
  • Separamos y decodficamos la humedad y la temperatura en python
In []:
0E_finalRemarks

Conclusiones / limitaciones

  • Para el uso sencillo de las líneas de entrada/salida, cualquiera de los métodos presentados vale
  • Si se necesita control riguroso de los tiempos, hay que irse al pruss o a interrupciones
  • La BB está basada en un SOC muy potente: solamente hemos visto algunas de las muchas capacidades que tiene
  • El devicetree es la forma oficial de definir el Hardware
  • La información del devicetree es escasa
  • La información y los ejemplos de uio también son escasos

+

A good combination

In []:
0F_weatherResults
In []:
%pylab inline
In [6]:
u = loadtxt('result.txt')
In [7]:
trasp=u.T
hum=trasp[0]
temp=trasp[1]
tiem=trasp[2]
In [8]:
import datetime, time
def conv(en):
    lo = long(en)
    s = str(lo)
    d = datetime.datetime.strptime(s, '%y%m%d%H%M')
    return time.mktime(d.timetuple())
tiem2 = [conv(i) for i in tiem]
In [9]:
plot(tiem2,temp, 'o')
plot(tiem2,hum, 'o')
coefficients = polyfit(tiem2, temp, 6)
polynomial = poly1d(coefficients)
ys = polynomial(tiem2)
plot(tiem2,ys)
coefficients = polyfit(tiem2, hum, 6)
polynomial = poly1d(coefficients)
ys = polynomial(tiem2)
plot(tiem2,ys)
/usr/local/lib/python3.4/site-packages/numpy-1.8.0-py3.4-linux-armv7l.egg/numpy/lib/polynomial.py:587: RankWarning: Polyfit may be poorly conditioned
  warnings.warn(msg, RankWarning)
/usr/local/lib/python3.4/site-packages/numpy-1.8.0-py3.4-linux-armv7l.egg/numpy/lib/polynomial.py:587: RankWarning: Polyfit may be poorly conditioned
  warnings.warn(msg, RankWarning)

Out[9]:
[<matplotlib.lines.Line2D at 0xb14f79f0>]
In [8]:
close()