/pwm-sunxi-opi0

PWM driver for H2+ Sunxi SoC

Primary LanguageC

pwm-sunxi-opi0

Loadable Kernel Module to support PWM on Allwinner H3 / H2+ SoC (sun8i / sun8iw7p1). Works with Orange Pi Zero.

Tested on Kernel (4.14.X)

Provides access to PWM configuration parameters from userspace. Only exposes PWM0 (PWM1 will be added once H2+ SoC documentation is available). PWM0 output is on PA05, which is exposed as UART0_RX (middle pin on the UART header).

Installation

Remap pins in FEX:

; Disable debug UART0
[uart_para]
uart_debug_port = 0
uart_debug_tx = port:PA04<2><1>
uart_debug_rx = port:PA05<2><1>

; Enable PWM0 on PA05
[pwm0_para]
pwm_used = 1
pwm_positive = port:PA05<3><0>
> fex2bin orangepizero.fex orangepizero.bin
> shutdown -r now

After the board comes back up:

> git clone https://github.com/iboguslavsky/pwm-sunxi-opi0.git
> cd pwm-sunxi-opi0
> make
> insmod ./pwm-sunxi-opi0.ko

Once loaded, the following sysfs directory structure will be created:

/sys
  │
  └─ /class 
       │
       └─ /pwm-sunxi-opi0
	    │
	    └─ /pwm0
	         │
	         ├── run
	         ├── prescale
	         ├── entire_cycles
	         ├── active_cycles
	         ├── polarity
	         └── freqperiod

Positive Polarity PWM cadence diagram PWM Cadence

  • run (read / write) - NOTE: This needs to be run first before any other values are set

    Enable / disable PWM0

    Allowed values: 0, 1

    echo 1 > /sys/class/pwm-sunxi-opi0/pwm0/run
    echo 0 > /sys/class/pwm-sunxi-opi0/pwm0/run
    
  • prescale (read / write)

    Divide 24MHz PWM clock by a specified prescaler

    Allowed Values: hex value from the table below

    PRESCALE_DIV120  = 0x00, // Divide 24mhz clock by 120 => PWM clock: 200Khz, PWM single "cycle": 5us
    PRESCALE_DIV180  = 0x01,
    PRESCALE_DIV240  = 0x02,
    PRESCALE_DIV360  = 0x03,
    PRESCALE_DIV480  = 0x04,
    PRESCALE_INVx05  = 0x05, // Invalid prescaler setting
    PRESCALE_INVx06  = 0x06,
    PRESCALE_INVx07  = 0x07,
    PRESCALE_DIV12k  = 0x08,
    PRESCALE_DIV24k  = 0x09,
    PRESCALE_DIV36k  = 0x0a,
    PRESCALE_DIV48k  = 0x0b,
    PRESCALE_DIV72k  = 0x0c,
    PRESCALE_INVx0d  = 0x0d,
    PRESCALE_INVx0e  = 0x0e,
    PRESCALE_DIV_NO  = 0x0f
    
  • entire_cycles (read / write)

    Specify number of ticks in a complete PWM period

    Allowed values 0..65534

  • active_cycles (read / write)

    Specify number of active ticks in a PWM period

    Allowed values 0..65535

  • polarity (read / write)

    Specify polarity of the duty cycle (positive / negative)

    Allowed values: 0, 1

  • freqperiod (read only)

    Show a calculated frequency of the PWM cycle (accounting for PWM clock divider and specified PWM period)


Sample of frequency values

prescale (in decimal) entire_cycles Result in Hz (Prefix Hz) active_cycles (Duty%)
15 1 12000000 Hz (12 MHz) 1 (50%)
15 10 2400000 Hz (2.4 MHz) 5 (50%)
15 100 240000 Hz (240 kHz) 50 (50%)
15 1000 24000 Hz (24 kHz) 500 (50%)
15 10000 2400 Hz (2.4 kHz) 5000 (50%)
0 1 100000 Hz (100 kHz) 1 (50%)
0 10 20000 Hz (20 kHz) 5 (50%)
0 100 2000 Hz (2 kHz) 50 (50%)
0 1000 200 Hz (0.2 kHz) 500 (50%)
0 10000 20 Hz (0.02 kHz) 5000 (50%)

As you can see the active_cycles is always lower or equal to entire_cycles, and depends on the necessary duty/frequency you need is necessary to recalculate the prescale.

And be aware that the final result of the frequency may vary do to how things may get rounded.