Skip to content

Instantly share code, notes, and snippets.

@keybuk
Created December 13, 2016 02:13
Show Gist options
  • Save keybuk/faebfde6d858eeb8472b65421e583822 to your computer and use it in GitHub Desktop.
Save keybuk/faebfde6d858eeb8472b65421e583822 to your computer and use it in GitHub Desktop.
Raspberry PI PWM
#if os(Linux)
import Glibc
#else
import Darwin
let O_SYNC: Int32 = 0
#endif
import Dispatch
let peripheralAddressBase = 0x3f000000
let peripheralBlockSize = 0x1000
let clockRegistersAddress = peripheralAddressBase + 0x101000
let gpioRegistersAddress = peripheralAddressBase + 0x200000
let pwmRegistersAddress = peripheralAddressBase + 0x20c000
let pwmClockControlOffset = 0xa0
let pwmClockDivOffset = 0xa4
let pwmControlOffset = 0x00
let pwmRange1Offset = 0x010
let pwmData1Offset = 0x14
let memFd = open("/dev/mem", O_RDWR | O_SYNC)
guard memFd > 0 else { fatalError("Couldn't open /dev/mem") }
defer { close(memFd) }
guard let clockRegisters = mmap(nil, peripheralBlockSize, PROT_READ | PROT_WRITE, MAP_SHARED, memFd, off_t(clockRegistersAddress)) else { fatalError("Couldn't mmap clock registers") }
guard let gpioRegisters = mmap(nil, peripheralBlockSize, PROT_READ | PROT_WRITE, MAP_SHARED, memFd, off_t(gpioRegistersAddress)) else { fatalError("Couldn't mmap GPIO registers") }
guard let pwmRegisters = mmap(nil, peripheralBlockSize, PROT_READ | PROT_WRITE, MAP_SHARED, memFd, off_t(pwmRegistersAddress)) else { fatalError("Couldn't mmap PWM registers") }
// Begin by disabling the PWM while we configure it.
let pwmControl = pwmRegisters.advanced(by: pwmControlOffset).assumingMemoryBound(to: UInt32.self)
pwmControl.pointee = 0
usleep(10)
// Configure the PWM clock.
let pwmClockControl = clockRegisters.advanced(by: pwmClockControlOffset).assumingMemoryBound(to: UInt32.self)
let pwmClockDiv = clockRegisters.advanced(by: pwmClockDivOffset).assumingMemoryBound(to: UInt32.self)
// Disable, and set source to PLLD (500 Mhz).
pwmClockControl.pointee = 0x5a000006
usleep(100)
// Set the divisor to 500, giving us a 1MHz clock.
pwmClockDiv.pointee = 0x5a000000 | UInt32(500 << 12)
usleep(100)
// Enable, with the source still set to PLLD.
pwmClockControl.pointee = 0x5a000016
usleep(100)
// Set the range of the clock to 1,000 giving us 1KHz "bits", and the duty cycle to 25%.
let pwmRange1 = pwmRegisters.advanced(by: pwmRange1Offset).assumingMemoryBound(to: UInt32.self)
let pwmData1 = pwmRegisters.advanced(by: pwmData1Offset).assumingMemoryBound(to: UInt32.self)
pwmRange1.pointee = 1000
usleep(10)
pwmData1.pointee = 250
usleep(10)
// GPIO 18's alternate mode 5 (0b010) uses PWM1 as a source, select it.
let gpioPin = 18
let gpioFunctionSelect = gpioRegisters.assumingMemoryBound(to: UInt32.self).advanced(by: gpioPin / 10)
gpioFunctionSelect.pointee &= ~(0b111 << UInt32((gpioPin % 10) * 3))
gpioFunctionSelect.pointee |= 0b010 << UInt32((gpioPin % 10) * 3)
// Enable PWM1 in PWM mode, using the data register as a source, and the PWM algorithm.
pwmControl.pointee = 0b00000001
dispatchMain()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment