diff --git a/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Package.mmp b/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Package.mmp new file mode 100644 index 0000000..8af5322 --- /dev/null +++ b/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Package.mmp @@ -0,0 +1,28 @@ +# This is a MadMachine project file in TOML format +# This file contains parameters that cannot be managed by SwiftPM +# Editing this file will alter the behavior of the build/download process +# Project files within dependent libraries will be IGNORED + +# Specify the board name below +# Supported boards are listed as follows +# "SwiftIOBoard" +# "SwiftIOMicro" +board = "SwiftIOMicro" + +# Specify the target triple below +# Supported architectures are listed as follows +# "thumbv7em-unknown-none-eabi" +# "thumbv7em-unknown-none-eabihf" +# "armv7em-none-none-eabi" +triple = "armv7em-none-none-eabi" + +# Enable or disable hardware floating-point support below +# If your code involves significant floating-point calculations, please set it to 'true' +hard-float = true + +# Enable or disable float register below +# If your code involves significant floating-point calculations, please set it to 'true' +float-abi = false + +# Reserved for future use +version = 1 \ No newline at end of file diff --git a/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Package.swift b/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Package.swift new file mode 100644 index 0000000..597b846 --- /dev/null +++ b/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Package.swift @@ -0,0 +1,26 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. +import PackageDescription + +let package = Package( + name: "DefaultApp", + dependencies: [ + // Dependencies declare other packages that this package depends on. + .package(url: "https://github.com/madmachineio/SwiftIO.git", branch: "develop"), + .package(url: "https://github.com/madmachineio/MadBoards.git", branch: "develop"), + .package(url: "https://github.com/madmachineio/MadDrivers.git", branch: "develop"), + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages this package depends on. + .executableTarget( + name: "DefaultApp", + dependencies: ["SwiftIO", + "MadBoards", + .product(name: "LIS3DH", package: "MadDrivers"), + .product(name: "PCF8563", package: "MadDrivers"), + .product(name: "SHT3x", package: "MadDrivers"), + .product(name: "ST7789", package: "MadDrivers"), + ]), + ] +) diff --git a/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Resources/Fonts/Roboto-Italic.ttf b/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Resources/Fonts/Roboto-Italic.ttf new file mode 100644 index 0000000..1b5eaa3 Binary files /dev/null and b/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Resources/Fonts/Roboto-Italic.ttf differ diff --git a/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Resources/Fonts/Roboto-Regular.ttf b/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Resources/Fonts/Roboto-Regular.ttf new file mode 100644 index 0000000..ddf4bfa Binary files /dev/null and b/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Resources/Fonts/Roboto-Regular.ttf differ diff --git a/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Sources/DefaultApp/DefaultApp.swift b/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Sources/DefaultApp/DefaultApp.swift new file mode 100644 index 0000000..3d94a83 --- /dev/null +++ b/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Sources/DefaultApp/DefaultApp.swift @@ -0,0 +1,317 @@ +import SwiftIO +import MadBoard +import ST7789 +import MadGraphics + + +let a0Module = 0 +let d1Module = 1 +let a11Module = 2 +let d19Module = 3 + +let temperature = 5 +let humidityKey = 6 + +let accValue = 8 + + +nonisolated(unsafe) var globalIOValue: [Int: Int] = [ + a0Module: -1, + d1Module: -1, + a11Module: -1, + d19Module: -1, +] +let ioLock = Mutex() + + +nonisolated(unsafe) var globalI2CValue: [Int: Int] = [ + temperature: -1, + humidityKey: -1, + accValue: -1, +] +let i2cLock = Mutex() + + +@main +public struct DefaultApp { + public static func main() { + sleep(ms: 500) + + let red = DigitalOut(Id.RED, value: true) + let green = DigitalOut(Id.GREEN, value: true) + let blue = DigitalOut(Id.BLUE, value: true) + + let led = PWMOut(Id.PWM4A) + led.set(frequency: 2000, dutycycle: 0.0) + let buzzer = PWMOut(Id.PWM5A) + + let spi = SPI(Id.SPI0, speed: 30_000_000) + + let bl = DigitalOut(Id.D2) + let rst = DigitalOut(Id.D12) + let dc = DigitalOut(Id.D13) + let cs = DigitalOut(Id.D5) + + let screen = ST7789(spi: spi, cs: cs, dc: dc, rst: rst, bl: bl, width: 240, rotation: .angle90) + var screenBuffer = [UInt16](repeating: 0, count: screen.width * screen.height) + + var frameBuffer = [UInt32](repeating: 0, count: screen.width * screen.height) + + let colors = [ + Color.black, + Color.gray, + Color.silver, + Color.red, + Color.pink, + Color.maroon, + Color.lime, + Color.green, + Color.olive, + Color.blue, + Color.navy, + Color.teal, + Color.cyan, + Color.aqua, + Color.purple, + Color.magenta, + Color.orange, + Color.yellow, + Color.white, + ] + var colorIndex = 0 + + var uptime = getSystemUptimeInMilliseconds() / 1000 + + let robotoFont = Font(path: "/lfs/Resources/Fonts/Roboto-Regular.ttf" , pointSize: 6, dpi: 220) + + let rootLayer = rootBackgroundInit(font: robotoFont) + rootLayer.render(into: &frameBuffer, output: &screenBuffer, transform: Color.getRGB565LE) { dirty, data in + screen.writeBitmap(x: dirty.x, y: dirty.y, width: dirty.width, height: dirty.height, data: data) + } + + + let temperatureText = TextLayer(at: Point(10, 12), anchorPoint: UnitPoint.zero, string: "°C", font: robotoFont, foregroundColor: Color(0x39E910)) + temperatureText.pointSize = 10 + rootLayer.append(temperatureText) + + let humidityText = TextLayer(at: Point(10, 48), anchorPoint: UnitPoint.zero, string: "%", font: robotoFont, foregroundColor: Color(0x1e9cf4)) + humidityText.pointSize = 10 + rootLayer.append(humidityText) + + let colorBar = Layer(at: Point(10, 185), anchorPoint: UnitPoint.zero, width: 100, height: 30, backgroundColor: Color.white) + rootLayer.append(colorBar) + + + let timeText = TextLayer(at: Point(124, 22), anchorPoint: UnitPoint.zero, string: " ", font: robotoFont, foregroundColor: Color.purple) + rootLayer.append(timeText) + + let soundState = TextLayer(at: Point(124, 42), anchorPoint: UnitPoint.zero, string: "Play sound", font: robotoFont, foregroundColor: Color.red) + rootLayer.append(soundState) + + let buzzerBar = Layer(at: Point(130, 106), anchorPoint: UnitPoint.zero, width: 1, height: 28, backgroundColor: Color(0xFFF568)) + rootLayer.append(buzzerBar) + + let accBar = Layer(at: Point(130, 186), anchorPoint: UnitPoint.zero, width: 5, height: 27, backgroundColor: Color.orange) + rootLayer.append(accBar) + + + + createThread( + name: "play_sound", + priority: 3, + stackSize: 1024 * 64, + soundThread + ) + sleep(ms: 10) + + createThread( + name: "normal_io", + priority: 4, + stackSize: 1024 * 64, + ioThread + ) + sleep(ms: 10) + + createThread( + name: "i2c_io", + priority: 5, + stackSize: 1024 * 64, + i2cIOThread + ) + sleep(ms: 10) + + while true { + sleep(ms: 5) + blue.toggle() + + ioLock.lock() + let ioSendState = globalIOValue.filter { (step, value) in + value >= 0 + } + ioLock.unlock() + + for (key, value) in ioSendState { + if key == a0Module { + let dutycycle = Float(value) / 100.0 + led.setDutycycle(dutycycle) + rootLayer.draw() { canvas in + canvas.fillRectangle(at: Point(x: 10, y: 106), width: 100, height: 28, data: Color.white.rawValue) + } + if value != 0 { + rootLayer.draw() { canvas in + canvas.fillRectangle(at: Point(x: 10, y: 106), width: value, height: 28, data: Color(0xFF5E5E).rawValue) + } + } + } + + if key == a11Module { + let fre = 400 + 40 * value + if value > 1 { + buzzer.set(frequency: fre, dutycycle: 0.5) + buzzerBar.frame.size.width = value + } else { + buzzer.set(frequency: fre, dutycycle: 0) + buzzerBar.frame.size.width = 1 + } + } + + if key == d1Module { + let color = colors[colorIndex] + colorBar.backgroundColor = color + colorIndex += 1 + if colorIndex >= colors.count { + colorIndex = 0 + } + } + + if key == d19Module && value == 2 { + soundState.foregroundColor = Color.lime + } + + if key == d19Module && value == 0 { + soundState.foregroundColor = Color.red + } + } + + ioLock.lock() + for (key, value) in ioSendState { + if key == d19Module { + if value == 2 || value == 0 { + let newValue = globalIOValue[key]! - 1 + globalIOValue[key] = newValue + } + } else { + globalIOValue[key] = -1 + } + } + ioLock.unlock() + + + i2cLock.lock() + let i2cSendState = globalI2CValue.filter { (step, value) in + value >= 0 + } + i2cLock.unlock() + + for (key, value) in i2cSendState { + if key == temperature { + temperatureText.string = String(value / 10) + "." + String(value % 10) + "°C" + } + + if key == humidityKey { + humidityText.string = String(value / 10) + "." + String(value % 10) + "%" + } + + if key == accValue { + let x = value & 0xFF + accBar.position.x = 130 + x + } + } + + i2cLock.lock() + for key in i2cSendState.keys { + globalI2CValue[key] = -1 + } + i2cLock.unlock() + + + let currentTime = getSystemUptimeInMilliseconds() / 1000 + if uptime != currentTime { + uptime = currentTime + let hour = uptime / 3600 + let minute = (uptime / 60) % 60 + let second = uptime % 60 + + let hourStr = (hour < 10 ? String(0) + String(hour) : String(hour)) + ":" + let minStr = (minute < 10 ? String(0) + String(minute) : String(minute)) + ":" + let secStr = (second < 10 ? String(0) + String(second) : String(second)) + + timeText.string = hourStr + minStr + secStr + } + + rootLayer.render(into: &frameBuffer, output: &screenBuffer, transform: Color.getRGB565LE) { dirty, data in + screen.writeBitmap(x: dirty.x, y: dirty.y, width: dirty.width, height: dirty.height, data: data) + } + } + } +} + + +func rootBackgroundInit(font: Font) -> Layer { + let layer = Layer(at: Point.zero, anchorPoint: UnitPoint.zero, width: 240, height: 240) + + layer.draw() { canvas in + canvas.fill(Color.white.rawValue) + canvas.drawLine(from: Point(x: 120, y: 0), to: Point(x: 120, y: 239), stroke: 2, data: Color.pink.rawValue) + canvas.drawLine(from: Point(x: 120, y: 40), to: Point(x: 239, y: 40), stroke: 2, data: Color.pink.rawValue) + canvas.drawLine(from: Point(x: 0, y: 80), to: Point(x: 239, y: 80), stroke: 2, data: Color.pink.rawValue) + canvas.drawLine(from: Point(x: 0, y: 160), to: Point(x: 239, y: 160), stroke: 2, data: Color.pink.rawValue) + } + + layer.draw() { canvas in + canvas.drawRectangle(at: Point(x: 10, y: 105), width: 100, height: 30, stroke: 3, data: Color(0x252525).rawValue) + canvas.drawRectangle(at: Point(x: 130, y: 105), width: 100, height: 30, stroke: 3, data: Color(0x252525).rawValue) + canvas.drawRectangle(at: Point(x: 130, y: 185), width: 100, height: 30, stroke: 2, data: Color(0x252525).rawValue) + } + + print("display init 0") + let time = TextLayer(at: Point(124, 2), anchorPoint: UnitPoint.zero, string: "Uptime", font: font, foregroundColor: Color.black) + layer.append(time) + + print("display init 1") + let soundAction = TextLayer(at: Point(124, 65), anchorPoint: UnitPoint.zero, string: "Press Button D19", font: font, foregroundColor: Color.gray) + soundAction.pointSize = 5 + layer.append(soundAction) + + + print("display init 2") + let led = TextLayer(at: Point(0, 82), anchorPoint: UnitPoint.zero, string: "LED brightness", font: font, foregroundColor: Color(0x252525)) + let ledAction = TextLayer(at: Point(0, 142), anchorPoint: UnitPoint.zero, string: "Rotate Knob A0", font: font, foregroundColor: Color.gray) + ledAction.pointSize = 5 + layer.append(led) + layer.append(ledAction) + + print("display init 3") + let buzzer = TextLayer(at: Point(124, 82), anchorPoint: UnitPoint.zero, string: "Buzzer pitch", font: font, foregroundColor: Color(0x252525)) + let buzzerAction = TextLayer(at: Point(124, 142), anchorPoint: UnitPoint.zero, string: "Rotate Knob A11", font: font, foregroundColor: Color.gray) + buzzerAction.pointSize = 5 + layer.append(buzzer) + layer.append(buzzerAction) + + + print("display init 4") + let color = TextLayer(at: Point(0, 165), anchorPoint: UnitPoint.zero, string: "Color picker", font: font, foregroundColor: Color(0x252525)) + let colorAction = TextLayer(at: Point(0, 225), anchorPoint: UnitPoint.zero, string: "Press Button D1", font: font, foregroundColor: Color.gray) + colorAction.pointSize = 5 + layer.append(color) + layer.append(colorAction) + + print("display init 5") + let acc = TextLayer(at: Point(124, 165), anchorPoint: UnitPoint.zero, string: "Acceleration", font: font, foregroundColor: Color(0x252525)) + let accAction = TextLayer(at: Point(124, 225), anchorPoint: UnitPoint.zero, string: "Tilt the board", font: font, foregroundColor: Color.gray) + accAction.pointSize = 5 + layer.append(acc) + layer.append(accAction) + + return layer.presentation +} \ No newline at end of file diff --git a/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Sources/DefaultApp/I2CThread.swift b/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Sources/DefaultApp/I2CThread.swift new file mode 100644 index 0000000..2bbdfb7 --- /dev/null +++ b/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Sources/DefaultApp/I2CThread.swift @@ -0,0 +1,88 @@ +import SwiftIO +import MadBoard + +import SHT3x +import LIS3DH +import PCF8563 + +func i2cIOThread(_ a: UnsafeMutableRawPointer?, _ b: UnsafeMutableRawPointer?, _ c: UnsafeMutableRawPointer?) -> () { + sleep(ms: 500) + + let i2c = I2C(Id.I2C0, speed: .fast) + sleep(ms: 10) + + let sht = SHT3x(i2c) + sleep(ms: 10) + var temp = 0 + var humidity = 0 + var tempCount = 0 + var humidityCount = 0 + var previousTemp = -1 + var previousHumidity = -1 + + + let acc = LIS3DH(i2c) + sleep(ms: 10) + var x = 50 + + var accCount = 0 + var previousX = 50 + + + + + + + + while true { + sleep(ms: 10) + + let ret = acc.readXYZ() + x = 100 - (Int(ret.x * 100.0) + 50) + x = min(93, x) + x = max(1, x) + + if x != previousX { + accCount += 1 + if accCount > 2 { + accCount = 0 + i2cLock.lock() + globalI2CValue[accValue] = (previousX << 8) | x + i2cLock.unlock() + previousX = x + } + } + + + + + temp = Int(sht.readCelsius() * 10) + humidity = Int(sht.readHumidity() * 10) + + if temp != previousTemp { + tempCount += 1 + + if tempCount > 5 { + i2cLock.lock() + globalI2CValue[temperature] = temp + i2cLock.unlock() + previousTemp = temp + tempCount = 0 + } + } + + if humidity != previousHumidity { + humidityCount += 1 + + if humidityCount > 5 { + i2cLock.lock() + globalI2CValue[humidityKey] = humidity + i2cLock.unlock() + previousHumidity = humidity + humidityCount = 0 + } + } + + } + +} \ No newline at end of file diff --git a/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Sources/DefaultApp/IOThread.swift b/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Sources/DefaultApp/IOThread.swift new file mode 100644 index 0000000..928620c --- /dev/null +++ b/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Sources/DefaultApp/IOThread.swift @@ -0,0 +1,92 @@ +import SwiftIO +import MadBoard + +func ioThread(_ a: UnsafeMutableRawPointer?, _ b: UnsafeMutableRawPointer?, _ c: UnsafeMutableRawPointer?) -> () { + sleep(ms: 100) + + let a0 = AnalogIn(Id.A0) + var a0Count = 0 + var a0PreviousValue = -1 + + let a11 = AnalogIn(Id.A11) + var a11Count = 0 + var a11PreviousValue = -1 + + let d1 = DigitalIn(Id.D1) + var d1PressCount = 0 + + let d19 = DigitalIn(Id.D19) + var d19PressCount = 0 + + + + while true { + sleep(ms: 4) + + + let a0Value = Int(a0.readPercentage() * 100) + if a0Value != a0PreviousValue { + a0Count += 1 + } else { + a0Count = 0 + } + + if a0Count > 5 { + a0Count = 0 + a0PreviousValue = a0Value + ioLock.lock() + globalIOValue[a0Module] = a0Value + ioLock.unlock() + } + + + + let a11Value = Int(a11.readPercentage() * 100) + if a11Value != a11PreviousValue { + a11Count += 1 + } else { + a11Count = 0 + } + + if a11Count > 5 { + a11Count = 0 + a11PreviousValue = a11Value + ioLock.lock() + globalIOValue[a11Module] = a11Value + ioLock.unlock() + } + + + + if d1.read() { + d1PressCount += 1 + } else { + d1PressCount -= 1 + d1PressCount = max(0, d1PressCount) + } + + if d1PressCount > 10 && !d1.read() { + d1PressCount = 0 + ioLock.lock() + globalIOValue[d1Module] = 1 + ioLock.unlock() + } + + + + if d19.read() { + d19PressCount += 1 + } else { + d19PressCount -= 1 + d19PressCount = max(0, d19PressCount) + } + if d19PressCount > 10 && !d19.read() { + d19PressCount = 0 + ioLock.lock() + if globalIOValue[d19Module]! < 0 { + globalIOValue[d19Module] = 3 + } + ioLock.unlock() + } + } +} \ No newline at end of file diff --git a/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Sources/DefaultApp/SoundThread.swift b/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Sources/DefaultApp/SoundThread.swift new file mode 100644 index 0000000..cd5ea92 --- /dev/null +++ b/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Sources/DefaultApp/SoundThread.swift @@ -0,0 +1,31 @@ +import SwiftIO +import MadBoard + +func soundThread(_ a: UnsafeMutableRawPointer?, _ b: UnsafeMutableRawPointer?, _ c: UnsafeMutableRawPointer?) -> () { + + +// BPM is beat count per minute. +// Timer signature specifies beats per bar and note value of a beat. +let player = Speaker() + + + +var startPlay = false + +while true { + sleep(ms: 100) + ioLock.lock() + if globalIOValue[d19Module]! == 3 { + globalIOValue[d19Module] = 2 + startPlay = true + } + ioLock.unlock() + if startPlay { + player.play() + startPlay = false + ioLock.lock() + globalIOValue[d19Module] = 0 + ioLock.unlock() + } +} +} \ No newline at end of file diff --git a/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Sources/DefaultApp/Speaker.swift b/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Sources/DefaultApp/Speaker.swift new file mode 100644 index 0000000..83ab4b5 --- /dev/null +++ b/Examples/SwiftIOPlayground/13MoreProjects/DefaultApp/Sources/DefaultApp/Speaker.swift @@ -0,0 +1,102 @@ +import SwiftIO +import MadBoard + + +public final class Speaker { + let speaker: I2S + // The frequencies of note C to B in octave 4. + let frequency: [Float] = [ + 261.626, + 293.665, + 329.628, + 349.228, + 391.995, + 440.000, + 493.883, + 523.250 + ] + + // Set the samples of the waveforms. + let sampleRate = 16_000 + let rawSampleLength = 1000 + var rawSamples: [Int16] + var amplitude: Int16 = 10_000 + + + public init() { + // Initialize the speaker using I2S communication. + // The default setting is 16k sample rate, 16bit sample bits. + speaker = I2S(Id.I2S0) + rawSamples = [Int16](repeating: 0, count: rawSampleLength) + } + + + public func play() { + + let duration: Float = 0.25 + + // Iterate through the frequencies from C to B to play a scale. + // The sound waveform is a square wave, so you will hear a buzzing sound. + // generateSquare(amplitude: amplitude, &rawSamples) + // for f in frequency { + // playWave(samples: rawSamples, frequency: f, duration: duration) + // } + // sleep(ms: 1000) + + // Iterate through the frequencies from C to B to play a scale. + // The sound waveform is a triangle wave, and the sound is much softer. + generateTriangle(amplitude: amplitude, &rawSamples) + for f in frequency { + playWave(samples: rawSamples, frequency: f, duration: duration) + } + + } + + // Generate samples for a square wave with a specified amplitude and store them in an array. + func generateSquare(amplitude: Int16, _ samples: inout [Int16]) { + let count = samples.count + for i in 0..