├── .gitignore ├── LICENSE ├── README.md ├── build.zig └── src ├── atmega328p.zig ├── gpio.zig ├── linker.ld ├── main.zig ├── start.zig ├── uart.zig └── uno.zig /.gitignore: -------------------------------------------------------------------------------- 1 | zig-cache/ 2 | zig-out/ 3 | build/ 4 | build-*/ 5 | docgen_tmp/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Timon Kruiper 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AVR Arduino Zig 2 | 3 | This project can build code that can be run on an Arduino Uno, using only Zig as its **only** dependency. 4 | 5 | Currently only `avrdude` is an external dependency that is needed to program the chip. 6 | 7 | ## Build instructions 8 | 9 | * `zig build` creates the executable. 10 | * `zig build upload -Dtty=/dev/ttyACM0` uploads the code to an Arduino connected to `/dev/ttyACM0`. 11 | * `zig build monitor -Dtty=/dev/ttyACM0` shows the serial monitor using `screen`. 12 | * `zig build objdump` shows the disassembly (`avr-objdump` has to be installed). -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const Builder = std.Build; 3 | 4 | pub fn build(b: *std.Build) !void { 5 | const uno = std.Target.Query{ 6 | .cpu_arch = .avr, 7 | .cpu_model = .{ .explicit = &std.Target.avr.cpu.atmega328p }, 8 | .os_tag = .freestanding, 9 | .abi = .none, 10 | }; 11 | 12 | const exe = b.addExecutable(.{ 13 | .name = "avr-arduino-zig", 14 | .root_source_file = b.path("src/start.zig"), 15 | .target = b.resolveTargetQuery(uno), 16 | .optimize = .ReleaseSafe, 17 | }); 18 | 19 | exe.setLinkerScriptPath(b.path("src/linker.ld")); 20 | 21 | b.installArtifact(exe); 22 | 23 | const tty = b.option( 24 | []const u8, 25 | "tty", 26 | "Specify the port to which the Arduino is connected (defaults to /dev/ttyACM0)", 27 | ) orelse "/dev/ttyACM0"; 28 | 29 | const bin_path = b.getInstallPath(.{ .custom = exe.installed_path orelse "./bin" }, exe.out_filename); 30 | 31 | const flash_command = blk: { 32 | var tmp = std.ArrayList(u8).init(b.allocator); 33 | try tmp.appendSlice("-Uflash:w:"); 34 | try tmp.appendSlice(bin_path); 35 | try tmp.appendSlice(":e"); 36 | break :blk try tmp.toOwnedSlice(); 37 | }; 38 | 39 | const upload = b.step("upload", "Upload the code to an Arduino device using avrdude"); 40 | const avrdude = b.addSystemCommand(&.{ 41 | "avrdude", 42 | "-carduino", 43 | "-patmega328p", 44 | "-D", 45 | "-P", 46 | tty, 47 | flash_command, 48 | }); 49 | upload.dependOn(&avrdude.step); 50 | avrdude.step.dependOn(&exe.step); 51 | 52 | const objdump = b.step("objdump", "Show dissassembly of the code using avr-objdump"); 53 | const avr_objdump = b.addSystemCommand(&.{ 54 | "avr-objdump", 55 | "-dh", 56 | bin_path, 57 | }); 58 | objdump.dependOn(&avr_objdump.step); 59 | avr_objdump.step.dependOn(&exe.step); 60 | 61 | const monitor = b.step("monitor", "Opens a monitor to the serial output"); 62 | const screen = b.addSystemCommand(&.{ 63 | "screen", 64 | tty, 65 | "115200", 66 | }); 67 | monitor.dependOn(&screen.step); 68 | 69 | b.default_step.dependOn(&exe.step); 70 | } 71 | -------------------------------------------------------------------------------- /src/atmega328p.zig: -------------------------------------------------------------------------------- 1 | // this file was generated by regz: https://github.com/ZigEmbeddedGroup/regz 2 | // commit: 6376709051af4d8920d5c8bb48945ca688af32ae 3 | // 4 | // vendor: Atmel 5 | // device: ATmega328P 6 | // cpu: AVR8 7 | 8 | pub const VectorTable = extern struct { 9 | /// External Pin, Power-on Reset, Brown-out Reset and Watchdog Reset 10 | RESET: InterruptVector = unhandled, 11 | /// External Interrupt Request 0 12 | INT0: InterruptVector = unhandled, 13 | /// External Interrupt Request 1 14 | INT1: InterruptVector = unhandled, 15 | /// Pin Change Interrupt Request 0 16 | PCINT0: InterruptVector = unhandled, 17 | /// Pin Change Interrupt Request 1 18 | PCINT1: InterruptVector = unhandled, 19 | /// Pin Change Interrupt Request 2 20 | PCINT2: InterruptVector = unhandled, 21 | /// Watchdog Time-out Interrupt 22 | WDT: InterruptVector = unhandled, 23 | /// Timer/Counter2 Compare Match A 24 | TIMER2_COMPA: InterruptVector = unhandled, 25 | /// Timer/Counter2 Compare Match B 26 | TIMER2_COMPB: InterruptVector = unhandled, 27 | /// Timer/Counter2 Overflow 28 | TIMER2_OVF: InterruptVector = unhandled, 29 | /// Timer/Counter1 Capture Event 30 | TIMER1_CAPT: InterruptVector = unhandled, 31 | /// Timer/Counter1 Compare Match A 32 | TIMER1_COMPA: InterruptVector = unhandled, 33 | /// Timer/Counter1 Compare Match B 34 | TIMER1_COMPB: InterruptVector = unhandled, 35 | /// Timer/Counter1 Overflow 36 | TIMER1_OVF: InterruptVector = unhandled, 37 | /// TimerCounter0 Compare Match A 38 | TIMER0_COMPA: InterruptVector = unhandled, 39 | /// TimerCounter0 Compare Match B 40 | TIMER0_COMPB: InterruptVector = unhandled, 41 | /// Timer/Couner0 Overflow 42 | TIMER0_OVF: InterruptVector = unhandled, 43 | /// SPI Serial Transfer Complete 44 | SPI_STC: InterruptVector = unhandled, 45 | /// USART Rx Complete 46 | USART_RX: InterruptVector = unhandled, 47 | /// USART, Data Register Empty 48 | USART_UDRE: InterruptVector = unhandled, 49 | /// USART Tx Complete 50 | USART_TX: InterruptVector = unhandled, 51 | /// ADC Conversion Complete 52 | ADC: InterruptVector = unhandled, 53 | /// EEPROM Ready 54 | EE_READY: InterruptVector = unhandled, 55 | /// Analog Comparator 56 | ANALOG_COMP: InterruptVector = unhandled, 57 | /// Two-wire Serial Interface 58 | TWI: InterruptVector = unhandled, 59 | /// Store Program Memory Read 60 | SPM_Ready: InterruptVector = unhandled, 61 | }; 62 | 63 | pub const registers = struct { 64 | /// Fuses 65 | pub const FUSE = struct { 66 | /// address: 0x2 67 | pub const EXTENDED = @as(*volatile Mmio(8, packed struct { 68 | /// Brown-out Detector trigger level 69 | /// 70 | /// 0x4: Brown-out detection at VCC=4.3 V 71 | /// 0x5: Brown-out detection at VCC=2.7 V 72 | /// 0x6: Brown-out detection at VCC=1.8 V 73 | /// 0x7: Brown-out detection disabled 74 | BODLEVEL: u3, 75 | padding0: u1, 76 | padding1: u1, 77 | padding2: u1, 78 | padding3: u1, 79 | padding4: u1, 80 | }), @ptrFromInt(0x2)); 81 | 82 | /// address: 0x1 83 | pub const HIGH = @as(*volatile Mmio(8, packed struct { 84 | /// Boot Reset vector Enabled 85 | BOOTRST: u1, 86 | /// Select boot size 87 | /// 88 | /// 0x0: Boot Flash size=2048 words start address=$3800 89 | /// 0x1: Boot Flash size=1024 words start address=$3C00 90 | /// 0x2: Boot Flash size=512 words start address=$3E00 91 | /// 0x3: Boot Flash size=256 words start address=$3F00 92 | BOOTSZ: u2, 93 | /// Preserve EEPROM through the Chip Erase cycle 94 | EESAVE: u1, 95 | /// Watch-dog Timer always on 96 | WDTON: u1, 97 | /// Serial program downloading (SPI) enabled 98 | SPIEN: u1, 99 | /// Debug Wire enable 100 | DWEN: u1, 101 | /// Reset Disabled (Enable PC6 as i/o pin) 102 | RSTDISBL: u1, 103 | }), @ptrFromInt(0x1)); 104 | 105 | /// address: 0x0 106 | pub const LOW = @as(*volatile Mmio(8, packed struct { 107 | /// Select Clock Source 108 | /// 109 | /// 0x0: Ext. Clock; Start-up time PWRDWN/RESET: 6 CK/14 CK + 0 ms 110 | /// 0x2: Int. RC Osc. 8 MHz; Start-up time PWRDWN/RESET: 6 CK/14 CK + 0 ms 111 | /// 0x3: Int. RC Osc. 128kHz; Start-up time PWRDWN/RESET: 6 CK/14 CK + 0 ms 112 | /// 0x4: Ext. Low-Freq. Crystal; Start-up time PWRDWN/RESET: 1K CK/14 CK + 0 ms 113 | /// 0x5: Ext. Low-Freq. Crystal; Start-up time PWRDWN/RESET: 32K CK/14 CK + 0 ms 114 | /// 0x6: Ext. Full-swing Crystal; Start-up time PWRDWN/RESET: 258 CK/14 CK + 4.1 ms 115 | /// 0x7: Ext. Full-swing Crystal; Start-up time PWRDWN/RESET: 1K CK /14 CK + 65 ms 116 | /// 0x8: Ext. Crystal Osc. 0.4-0.9 MHz; Start-up time PWRDWN/RESET: 258 CK/14 CK + 4.1 ms 117 | /// 0x9: Ext. Crystal Osc. 0.4-0.9 MHz; Start-up time PWRDWN/RESET: 1K CK /14 CK + 65 ms 118 | /// 0xa: Ext. Crystal Osc. 0.9-3.0 MHz; Start-up time PWRDWN/RESET: 258 CK/14 CK + 4.1 ms 119 | /// 0xb: Ext. Crystal Osc. 0.9-3.0 MHz; Start-up time PWRDWN/RESET: 1K CK /14 CK + 65 ms 120 | /// 0xc: Ext. Crystal Osc. 3.0-8.0 MHz; Start-up time PWRDWN/RESET: 258 CK/14 CK + 4.1 ms 121 | /// 0xd: Ext. Crystal Osc. 3.0-8.0 MHz; Start-up time PWRDWN/RESET: 1K CK /14 CK + 65 ms 122 | /// 0xe: Ext. Crystal Osc. 8.0- MHz; Start-up time PWRDWN/RESET: 258 CK/14 CK + 4.1 ms 123 | /// 0xf: Ext. Crystal Osc. 8.0- MHz; Start-up time PWRDWN/RESET: 1K CK /14 CK + 65 ms 124 | /// 0x10: Ext. Clock; Start-up time PWRDWN/RESET: 6 CK/14 CK + 4.1 ms 125 | /// 0x12: Int. RC Osc. 8 MHz; Start-up time PWRDWN/RESET: 6 CK/14 CK + 4.1 ms 126 | /// 0x13: Int. RC Osc. 128kHz; Start-up time PWRDWN/RESET: 6 CK/14 CK + 4.1 ms 127 | /// 0x14: Ext. Low-Freq. Crystal; Start-up time PWRDWN/RESET: 1K CK/14 CK + 4.1 ms 128 | /// 0x15: Ext. Low-Freq. Crystal; Start-up time PWRDWN/RESET: 32K CK/14 CK + 4.1 ms 129 | /// 0x16: Ext. Full-swing Crystal; Start-up time PWRDWN/RESET: 258 CK/14 CK + 65 ms 130 | /// 0x17: Ext. Full-swing Crystal; Start-up time PWRDWN/RESET: 16K CK/14 CK + 0 ms 131 | /// 0x18: Ext. Crystal Osc. 0.4-0.9 MHz; Start-up time PWRDWN/RESET: 258 CK/14 CK + 65 ms 132 | /// 0x19: Ext. Crystal Osc. 0.4-0.9 MHz; Start-up time PWRDWN/RESET: 16K CK/14 CK + 0 ms 133 | /// 0x1a: Ext. Crystal Osc. 0.9-3.0 MHz; Start-up time PWRDWN/RESET: 258 CK/14 CK + 65 ms 134 | /// 0x1b: Ext. Crystal Osc. 0.9-3.0 MHz; Start-up time PWRDWN/RESET: 16K CK/14 CK + 0 ms 135 | /// 0x1c: Ext. Crystal Osc. 3.0-8.0 MHz; Start-up time PWRDWN/RESET: 258 CK/14 CK + 65 ms 136 | /// 0x1d: Ext. Crystal Osc. 3.0-8.0 MHz; Start-up time PWRDWN/RESET: 16K CK/14 CK + 0 ms 137 | /// 0x1e: Ext. Crystal Osc. 8.0- MHz; Start-up time PWRDWN/RESET: 258 CK/14 CK + 65 ms 138 | /// 0x1f: Ext. Crystal Osc. 8.0- MHz; Start-up time PWRDWN/RESET: 16K CK/14 CK + 0 ms 139 | /// 0x20: Ext. Clock; Start-up time PWRDWN/RESET: 6 CK/14 CK + 65 ms 140 | /// 0x22: Int. RC Osc. 8 MHz; Start-up time PWRDWN/RESET: 6 CK/14 CK + 65 ms 141 | /// 0x23: Int. RC Osc. 128kHz; Start-up time PWRDWN/RESET: 6 CK/14 CK + 65 ms 142 | /// 0x24: Ext. Low-Freq. Crystal; Start-up time PWRDWN/RESET: 1K CK/14 CK + 65 ms 143 | /// 0x25: Ext. Low-Freq. Crystal; Start-up time PWRDWN/RESET: 32K CK/14 CK + 65 ms 144 | /// 0x26: Ext. Full-swing Crystal; Start-up time PWRDWN/RESET: 1K CK /14 CK + 0 ms 145 | /// 0x27: Ext. Full-swing Crystal; Start-up time PWRDWN/RESET: 16K CK/14 CK + 4.1 ms 146 | /// 0x28: Ext. Crystal Osc. 0.4-0.9 MHz; Start-up time PWRDWN/RESET: 1K CK /14 CK + 0 ms 147 | /// 0x29: Ext. Crystal Osc. 0.4-0.9 MHz; Start-up time PWRDWN/RESET: 16K CK/14 CK + 4.1 ms 148 | /// 0x2a: Ext. Crystal Osc. 0.9-3.0 MHz; Start-up time PWRDWN/RESET: 1K CK /14 CK + 0 ms 149 | /// 0x2b: Ext. Crystal Osc. 0.9-3.0 MHz; Start-up time PWRDWN/RESET: 16K CK/14 CK + 4.1 ms 150 | /// 0x2c: Ext. Crystal Osc. 3.0-8.0 MHz; Start-up time PWRDWN/RESET: 1K CK /14 CK + 0 ms 151 | /// 0x2d: Ext. Crystal Osc. 3.0-8.0 MHz; Start-up time PWRDWN/RESET: 16K CK/14 CK + 4.1 ms 152 | /// 0x2e: Ext. Crystal Osc. 8.0- MHz; Start-up time PWRDWN/RESET: 1K CK /14 CK + 0 ms 153 | /// 0x2f: Ext. Crystal Osc. 8.0- MHz; Start-up time PWRDWN/RESET: 16K CK/14 CK + 4.1 ms 154 | /// 0x36: Ext. Full-swing Crystal; Start-up time PWRDWN/RESET: 1K CK /14 CK + 4.1 ms 155 | /// 0x37: Ext. Full-swing Crystal; Start-up time PWRDWN/RESET: 16K CK/14 CK + 65 ms 156 | /// 0x38: Ext. Crystal Osc. 0.4-0.9 MHz; Start-up time PWRDWN/RESET: 1K CK /14 CK + 4.1 ms 157 | /// 0x39: Ext. Crystal Osc. 0.4-0.9 MHz; Start-up time PWRDWN/RESET: 16K CK/14 CK + 65 ms 158 | /// 0x3a: Ext. Crystal Osc. 0.9-3.0 MHz; Start-up time PWRDWN/RESET: 1K CK /14 CK + 4.1 ms 159 | /// 0x3b: Ext. Crystal Osc. 0.9-3.0 MHz; Start-up time PWRDWN/RESET: 16K CK/14 CK + 65 ms 160 | /// 0x3c: Ext. Crystal Osc. 3.0-8.0 MHz; Start-up time PWRDWN/RESET: 1K CK /14 CK + 4.1 ms 161 | /// 0x3d: Ext. Crystal Osc. 3.0-8.0 MHz; Start-up time PWRDWN/RESET: 16K CK/14 CK + 65 ms 162 | /// 0x3e: Ext. Crystal Osc. 8.0- MHz; Start-up time PWRDWN/RESET: 1K CK /14 CK + 4.1 ms 163 | /// 0x3f: Ext. Crystal Osc. 8.0- MHz; Start-up time PWRDWN/RESET: 16K CK/14 CK + 65 ms 164 | SUT_CKSEL: u6, 165 | /// Clock output on PORTB0 166 | CKOUT: u1, 167 | /// Divide clock by 8 internally 168 | CKDIV8: u1, 169 | }), @ptrFromInt(0x0)); 170 | }; 171 | 172 | /// Lockbits 173 | pub const LOCKBIT = struct { 174 | /// address: 0x0 175 | pub const LOCKBIT = @as(*volatile Mmio(8, packed struct { 176 | /// Memory Lock 177 | /// 178 | /// 0x0: Further programming and verification disabled 179 | /// 0x2: Further programming disabled 180 | /// 0x3: No memory lock features enabled 181 | LB: u2, 182 | /// Boot Loader Protection Mode 183 | /// 184 | /// 0x0: LPM and SPM prohibited in Application Section 185 | /// 0x1: LPM prohibited in Application Section 186 | /// 0x2: SPM prohibited in Application Section 187 | /// 0x3: No lock on SPM and LPM in Application Section 188 | BLB0: u2, 189 | /// Boot Loader Protection Mode 190 | /// 191 | /// 0x0: LPM and SPM prohibited in Boot Section 192 | /// 0x1: LPM prohibited in Boot Section 193 | /// 0x2: SPM prohibited in Boot Section 194 | /// 0x3: No lock on SPM and LPM in Boot Section 195 | BLB1: u2, 196 | padding0: u1, 197 | padding1: u1, 198 | }), @ptrFromInt(0x0)); 199 | }; 200 | 201 | /// USART 202 | pub const USART0 = struct { 203 | /// address: 0xc6 204 | /// USART I/O Data Register 205 | pub const UDR0 = @as(*volatile u8, @ptrFromInt(0xc6)); 206 | 207 | /// address: 0xc0 208 | /// USART Control and Status Register A 209 | pub const UCSR0A = @as(*volatile Mmio(8, packed struct { 210 | /// Multi-processor Communication Mode 211 | MPCM0: u1, 212 | /// Double the USART transmission speed 213 | U2X0: u1, 214 | /// Parity Error 215 | UPE0: u1, 216 | /// Data overRun 217 | DOR0: u1, 218 | /// Framing Error 219 | FE0: u1, 220 | /// USART Data Register Empty 221 | UDRE0: u1, 222 | /// USART Transmitt Complete 223 | TXC0: u1, 224 | /// USART Receive Complete 225 | RXC0: u1, 226 | }), @ptrFromInt(0xc0)); 227 | 228 | /// address: 0xc1 229 | /// USART Control and Status Register B 230 | pub const UCSR0B = @as(*volatile Mmio(8, packed struct { 231 | /// Transmit Data Bit 8 232 | TXB80: u1, 233 | /// Receive Data Bit 8 234 | RXB80: u1, 235 | /// Character Size - together with UCSZ0 in UCSR0C 236 | UCSZ02: u1, 237 | /// Transmitter Enable 238 | TXEN0: u1, 239 | /// Receiver Enable 240 | RXEN0: u1, 241 | /// USART Data register Empty Interrupt Enable 242 | UDRIE0: u1, 243 | /// TX Complete Interrupt Enable 244 | TXCIE0: u1, 245 | /// RX Complete Interrupt Enable 246 | RXCIE0: u1, 247 | }), @ptrFromInt(0xc1)); 248 | 249 | /// address: 0xc2 250 | /// USART Control and Status Register C 251 | pub const UCSR0C = @as(*volatile Mmio(8, packed struct { 252 | /// Clock Polarity 253 | UCPOL0: u1, 254 | /// Character Size - together with UCSZ2 in UCSR0B 255 | UCSZ0: u2, 256 | /// Stop Bit Select 257 | /// 258 | /// 0x0: 1-bit 259 | /// 0x1: 2-bit 260 | USBS0: u1, 261 | /// Parity Mode Bits 262 | /// 263 | /// 0x0: Disabled 264 | /// 0x1: Reserved 265 | /// 0x2: Enabled, Even Parity 266 | /// 0x3: Enabled, Odd Parity 267 | UPM0: u2, 268 | /// USART Mode Select 269 | /// 270 | /// 0x0: Asynchronous USART 271 | /// 0x1: Synchronous USART 272 | /// 0x3: Master SPI 273 | UMSEL0: u2, 274 | }), @ptrFromInt(0xc2)); 275 | 276 | /// address: 0xc4 277 | /// USART Baud Rate Register Bytes 278 | pub const UBRR0 = @as(*volatile u12, @ptrFromInt(0xc4)); 279 | }; 280 | 281 | /// Two Wire Serial Interface 282 | pub const TWI = struct { 283 | /// address: 0xbd 284 | /// TWI (Slave) Address Mask Register 285 | pub const TWAMR = @as(*volatile Mmio(8, packed struct { 286 | reserved0: u1, 287 | TWAM: u7, 288 | }), @ptrFromInt(0xbd)); 289 | 290 | /// address: 0xb8 291 | /// TWI Bit Rate register 292 | pub const TWBR = @as(*volatile u8, @ptrFromInt(0xb8)); 293 | 294 | /// address: 0xbc 295 | /// TWI Control Register 296 | pub const TWCR = @as(*volatile Mmio(8, packed struct { 297 | /// TWI Interrupt Enable 298 | TWIE: u1, 299 | reserved0: u1, 300 | /// TWI Enable Bit 301 | TWEN: u1, 302 | /// TWI Write Collition Flag 303 | TWWC: u1, 304 | /// TWI Stop Condition Bit 305 | TWSTO: u1, 306 | /// TWI Start Condition Bit 307 | TWSTA: u1, 308 | /// TWI Enable Acknowledge Bit 309 | TWEA: u1, 310 | /// TWI Interrupt Flag 311 | TWINT: u1, 312 | }), @ptrFromInt(0xbc)); 313 | 314 | /// address: 0xb9 315 | /// TWI Status Register 316 | pub const TWSR = @as(*volatile Mmio(8, packed struct { 317 | /// TWI Prescaler 318 | /// 319 | /// 0x0: 1 320 | /// 0x1: 4 321 | /// 0x2: 16 322 | /// 0x3: 64 323 | TWPS: u2, 324 | reserved0: u1, 325 | /// TWI Status 326 | TWS: u5, 327 | }), @ptrFromInt(0xb9)); 328 | 329 | /// address: 0xbb 330 | /// TWI Data register 331 | pub const TWDR = @as(*volatile u8, @ptrFromInt(0xbb)); 332 | 333 | /// address: 0xba 334 | /// TWI (Slave) Address register 335 | pub const TWAR = @as(*volatile Mmio(8, packed struct { 336 | /// TWI General Call Recognition Enable Bit 337 | TWGCE: u1, 338 | /// TWI (Slave) Address register Bits 339 | TWA: u7, 340 | }), @ptrFromInt(0xba)); 341 | }; 342 | 343 | /// Timer/Counter, 16-bit 344 | pub const TC1 = struct { 345 | /// address: 0x6f 346 | /// Timer/Counter Interrupt Mask Register 347 | pub const TIMSK1 = @as(*volatile Mmio(8, packed struct { 348 | /// Timer/Counter1 Overflow Interrupt Enable 349 | TOIE1: u1, 350 | /// Timer/Counter1 Output CompareA Match Interrupt Enable 351 | OCIE1A: u1, 352 | /// Timer/Counter1 Output CompareB Match Interrupt Enable 353 | OCIE1B: u1, 354 | reserved0: u1, 355 | reserved1: u1, 356 | /// Timer/Counter1 Input Capture Interrupt Enable 357 | ICIE1: u1, 358 | padding0: u1, 359 | padding1: u1, 360 | }), @ptrFromInt(0x6f)); 361 | 362 | /// address: 0x36 363 | /// Timer/Counter Interrupt Flag register 364 | pub const TIFR1 = @as(*volatile Mmio(8, packed struct { 365 | /// Timer/Counter1 Overflow Flag 366 | TOV1: u1, 367 | /// Output Compare Flag 1A 368 | OCF1A: u1, 369 | /// Output Compare Flag 1B 370 | OCF1B: u1, 371 | reserved0: u1, 372 | reserved1: u1, 373 | /// Input Capture Flag 1 374 | ICF1: u1, 375 | padding0: u1, 376 | padding1: u1, 377 | }), @ptrFromInt(0x36)); 378 | 379 | /// address: 0x80 380 | /// Timer/Counter1 Control Register A 381 | pub const TCCR1A = @as(*volatile Mmio(8, packed struct { 382 | /// Waveform Generation Mode 383 | WGM1: u2, 384 | reserved0: u1, 385 | reserved1: u1, 386 | /// Compare Output Mode 1B, bits 387 | COM1B: u2, 388 | /// Compare Output Mode 1A, bits 389 | COM1A: u2, 390 | }), @ptrFromInt(0x80)); 391 | 392 | /// address: 0x81 393 | /// Timer/Counter1 Control Register B 394 | pub const TCCR1B = @as(*volatile Mmio(8, packed struct { 395 | /// Prescaler source of Timer/Counter 1 396 | /// 397 | /// 0x0: No Clock Source (Stopped) 398 | /// 0x1: Running, No Prescaling 399 | /// 0x2: Running, CLK/8 400 | /// 0x3: Running, CLK/64 401 | /// 0x4: Running, CLK/256 402 | /// 0x5: Running, CLK/1024 403 | /// 0x6: Running, ExtClk Tn Falling Edge 404 | /// 0x7: Running, ExtClk Tn Rising Edge 405 | CS1: u3, 406 | /// Waveform Generation Mode 407 | WGM1: u2, 408 | reserved0: u1, 409 | /// Input Capture 1 Edge Select 410 | ICES1: u1, 411 | /// Input Capture 1 Noise Canceler 412 | ICNC1: u1, 413 | }), @ptrFromInt(0x81)); 414 | 415 | /// address: 0x82 416 | /// Timer/Counter1 Control Register C 417 | pub const TCCR1C = @as(*volatile Mmio(8, packed struct { 418 | reserved0: u1, 419 | reserved1: u1, 420 | reserved2: u1, 421 | reserved3: u1, 422 | reserved4: u1, 423 | reserved5: u1, 424 | FOC1B: u1, 425 | FOC1A: u1, 426 | }), @ptrFromInt(0x82)); 427 | 428 | /// address: 0x84 429 | /// Timer/Counter1 Bytes 430 | pub const TCNT1 = @as(*volatile u16, @ptrFromInt(0x84)); 431 | 432 | /// address: 0x88 433 | /// Timer/Counter1 Output Compare Register Bytes 434 | pub const OCR1A = @as(*volatile u16, @ptrFromInt(0x88)); 435 | 436 | /// address: 0x8a 437 | /// Timer/Counter1 Output Compare Register Bytes 438 | pub const OCR1B = @as(*volatile u16, @ptrFromInt(0x8a)); 439 | 440 | /// address: 0x86 441 | /// Timer/Counter1 Input Capture Register Bytes 442 | pub const ICR1 = @as(*volatile u16, @ptrFromInt(0x86)); 443 | 444 | /// address: 0x43 445 | /// General Timer/Counter Control Register 446 | pub const GTCCR = @as(*volatile Mmio(8, packed struct { 447 | /// Prescaler Reset Timer/Counter1 and Timer/Counter0 448 | PSRSYNC: u1, 449 | reserved0: u1, 450 | reserved1: u1, 451 | reserved2: u1, 452 | reserved3: u1, 453 | reserved4: u1, 454 | reserved5: u1, 455 | /// Timer/Counter Synchronization Mode 456 | TSM: u1, 457 | }), @ptrFromInt(0x43)); 458 | }; 459 | 460 | /// Timer/Counter, 8-bit Async 461 | pub const TC2 = struct { 462 | /// address: 0x70 463 | /// Timer/Counter Interrupt Mask register 464 | pub const TIMSK2 = @as(*volatile Mmio(8, packed struct { 465 | /// Timer/Counter2 Overflow Interrupt Enable 466 | TOIE2: u1, 467 | /// Timer/Counter2 Output Compare Match A Interrupt Enable 468 | OCIE2A: u1, 469 | /// Timer/Counter2 Output Compare Match B Interrupt Enable 470 | OCIE2B: u1, 471 | padding0: u1, 472 | padding1: u1, 473 | padding2: u1, 474 | padding3: u1, 475 | padding4: u1, 476 | }), @ptrFromInt(0x70)); 477 | 478 | /// address: 0x37 479 | /// Timer/Counter Interrupt Flag Register 480 | pub const TIFR2 = @as(*volatile Mmio(8, packed struct { 481 | /// Timer/Counter2 Overflow Flag 482 | TOV2: u1, 483 | /// Output Compare Flag 2A 484 | OCF2A: u1, 485 | /// Output Compare Flag 2B 486 | OCF2B: u1, 487 | padding0: u1, 488 | padding1: u1, 489 | padding2: u1, 490 | padding3: u1, 491 | padding4: u1, 492 | }), @ptrFromInt(0x37)); 493 | 494 | /// address: 0xb0 495 | /// Timer/Counter2 Control Register A 496 | pub const TCCR2A = @as(*volatile Mmio(8, packed struct { 497 | /// Waveform Genration Mode 498 | WGM2: u2, 499 | reserved0: u1, 500 | reserved1: u1, 501 | /// Compare Output Mode bits 502 | COM2B: u2, 503 | /// Compare Output Mode bits 504 | COM2A: u2, 505 | }), @ptrFromInt(0xb0)); 506 | 507 | /// address: 0xb1 508 | /// Timer/Counter2 Control Register B 509 | pub const TCCR2B = @as(*volatile Mmio(8, packed struct { 510 | /// Clock Select bits 511 | /// 512 | /// 0x0: No Clock Source (Stopped) 513 | /// 0x1: Running, No Prescaling 514 | /// 0x2: Running, CLK/8 515 | /// 0x3: Running, CLK/32 516 | /// 0x4: Running, CLK/64 517 | /// 0x5: Running, CLK/128 518 | /// 0x6: Running, CLK/256 519 | /// 0x7: Running, CLK/1024 520 | CS2: u3, 521 | /// Waveform Generation Mode 522 | WGM22: u1, 523 | reserved0: u1, 524 | reserved1: u1, 525 | /// Force Output Compare B 526 | FOC2B: u1, 527 | /// Force Output Compare A 528 | FOC2A: u1, 529 | }), @ptrFromInt(0xb1)); 530 | 531 | /// address: 0xb2 532 | /// Timer/Counter2 533 | pub const TCNT2 = @as(*volatile u8, @ptrFromInt(0xb2)); 534 | 535 | /// address: 0xb4 536 | /// Timer/Counter2 Output Compare Register B 537 | pub const OCR2B = @as(*volatile u8, @ptrFromInt(0xb4)); 538 | 539 | /// address: 0xb3 540 | /// Timer/Counter2 Output Compare Register A 541 | pub const OCR2A = @as(*volatile u8, @ptrFromInt(0xb3)); 542 | 543 | /// address: 0xb6 544 | /// Asynchronous Status Register 545 | pub const ASSR = @as(*volatile Mmio(8, packed struct { 546 | /// Timer/Counter Control Register2 Update Busy 547 | TCR2BUB: u1, 548 | /// Timer/Counter Control Register2 Update Busy 549 | TCR2AUB: u1, 550 | /// Output Compare Register 2 Update Busy 551 | OCR2BUB: u1, 552 | /// Output Compare Register2 Update Busy 553 | OCR2AUB: u1, 554 | /// Timer/Counter2 Update Busy 555 | TCN2UB: u1, 556 | /// Asynchronous Timer/Counter2 557 | AS2: u1, 558 | /// Enable External Clock Input 559 | EXCLK: u1, 560 | padding0: u1, 561 | }), @ptrFromInt(0xb6)); 562 | 563 | /// address: 0x43 564 | /// General Timer Counter Control register 565 | pub const GTCCR = @as(*volatile Mmio(8, packed struct { 566 | reserved0: u1, 567 | /// Prescaler Reset Timer/Counter2 568 | PSRASY: u1, 569 | reserved1: u1, 570 | reserved2: u1, 571 | reserved3: u1, 572 | reserved4: u1, 573 | reserved5: u1, 574 | /// Timer/Counter Synchronization Mode 575 | TSM: u1, 576 | }), @ptrFromInt(0x43)); 577 | }; 578 | 579 | /// Analog-to-Digital Converter 580 | pub const ADC = struct { 581 | /// address: 0x7c 582 | /// The ADC multiplexer Selection Register 583 | pub const ADMUX = @as(*volatile Mmio(8, packed struct { 584 | /// Analog Channel Selection Bits 585 | /// 586 | /// 0x0: ADC Single Ended Input pin 0 587 | /// 0x1: ADC Single Ended Input pin 1 588 | /// 0x2: ADC Single Ended Input pin 2 589 | /// 0x3: ADC Single Ended Input pin 3 590 | /// 0x4: ADC Single Ended Input pin 4 591 | /// 0x5: ADC Single Ended Input pin 5 592 | /// 0x6: ADC Single Ended Input pin 6 593 | /// 0x7: ADC Single Ended Input pin 7 594 | /// 0x8: Temperature sensor 595 | /// 0xe: Internal Reference (VBG) 596 | /// 0xf: 0V (GND) 597 | MUX: u4, 598 | reserved0: u1, 599 | /// Left Adjust Result 600 | ADLAR: u1, 601 | /// Reference Selection Bits 602 | /// 603 | /// 0x0: AREF, Internal Vref turned off 604 | /// 0x1: AVCC with external capacitor at AREF pin 605 | /// 0x2: Reserved 606 | /// 0x3: Internal 1.1V Voltage Reference with external capacitor at AREF pin 607 | REFS: u2, 608 | }), @ptrFromInt(0x7c)); 609 | 610 | /// address: 0x78 611 | /// ADC Data Register Bytes 612 | pub const ADC = @as(*volatile u16, @ptrFromInt(0x78)); 613 | 614 | /// address: 0x7a 615 | /// The ADC Control and Status register A 616 | pub const ADCSRA = @as(*volatile Mmio(8, packed struct { 617 | /// ADC Prescaler Select Bits 618 | /// 619 | /// 0x0: 2 620 | /// 0x1: 2 621 | /// 0x2: 4 622 | /// 0x3: 8 623 | /// 0x4: 16 624 | /// 0x5: 32 625 | /// 0x6: 64 626 | /// 0x7: 128 627 | ADPS: u3, 628 | /// ADC Interrupt Enable 629 | ADIE: u1, 630 | /// ADC Interrupt Flag 631 | ADIF: u1, 632 | /// ADC Auto Trigger Enable 633 | ADATE: u1, 634 | /// ADC Start Conversion 635 | ADSC: u1, 636 | /// ADC Enable 637 | ADEN: u1, 638 | }), @ptrFromInt(0x7a)); 639 | 640 | /// address: 0x7b 641 | /// The ADC Control and Status register B 642 | pub const ADCSRB = @as(*volatile Mmio(8, packed struct { 643 | /// ADC Auto Trigger Source bits 644 | /// 645 | /// 0x0: Free Running mode 646 | /// 0x1: Analog Comparator 647 | /// 0x2: External Interrupt Request 0 648 | /// 0x3: Timer/Counter0 Compare Match A 649 | /// 0x4: Timer/Counter0 Overflow 650 | /// 0x5: Timer/Counter1 Compare Match B 651 | /// 0x6: Timer/Counter1 Overflow 652 | /// 0x7: Timer/Counter1 Capture Event 653 | ADTS: u3, 654 | reserved0: u1, 655 | reserved1: u1, 656 | reserved2: u1, 657 | ACME: u1, 658 | padding0: u1, 659 | }), @ptrFromInt(0x7b)); 660 | 661 | /// address: 0x7e 662 | /// Digital Input Disable Register 663 | pub const DIDR0 = @as(*volatile Mmio(8, packed struct { 664 | ADC0D: u1, 665 | ADC1D: u1, 666 | ADC2D: u1, 667 | ADC3D: u1, 668 | ADC4D: u1, 669 | ADC5D: u1, 670 | padding0: u1, 671 | padding1: u1, 672 | }), @ptrFromInt(0x7e)); 673 | }; 674 | 675 | /// Analog Comparator 676 | pub const AC = struct { 677 | /// address: 0x50 678 | /// Analog Comparator Control And Status Register 679 | pub const ACSR = @as(*volatile Mmio(8, packed struct { 680 | /// Analog Comparator Interrupt Mode Select bits 681 | /// 682 | /// 0x0: Interrupt on Toggle 683 | /// 0x1: Reserved 684 | /// 0x2: Interrupt on Falling Edge 685 | /// 0x3: Interrupt on Rising Edge 686 | ACIS: u2, 687 | /// Analog Comparator Input Capture Enable 688 | ACIC: u1, 689 | /// Analog Comparator Interrupt Enable 690 | ACIE: u1, 691 | /// Analog Comparator Interrupt Flag 692 | ACI: u1, 693 | /// Analog Compare Output 694 | ACO: u1, 695 | /// Analog Comparator Bandgap Select 696 | ACBG: u1, 697 | /// Analog Comparator Disable 698 | ACD: u1, 699 | }), @ptrFromInt(0x50)); 700 | 701 | /// address: 0x7f 702 | /// Digital Input Disable Register 1 703 | pub const DIDR1 = @as(*volatile Mmio(8, packed struct { 704 | /// AIN0 Digital Input Disable 705 | AIN0D: u1, 706 | /// AIN1 Digital Input Disable 707 | AIN1D: u1, 708 | padding0: u1, 709 | padding1: u1, 710 | padding2: u1, 711 | padding3: u1, 712 | padding4: u1, 713 | padding5: u1, 714 | }), @ptrFromInt(0x7f)); 715 | }; 716 | 717 | /// I/O Port 718 | pub const PORTB = struct { 719 | /// address: 0x25 720 | /// Port B Data Register 721 | pub const PORTB = @as(*volatile u8, @ptrFromInt(0x25)); 722 | 723 | /// address: 0x24 724 | /// Port B Data Direction Register 725 | pub const DDRB = @as(*volatile u8, @ptrFromInt(0x24)); 726 | 727 | /// address: 0x23 728 | /// Port B Input Pins 729 | pub const PINB = @as(*volatile u8, @ptrFromInt(0x23)); 730 | }; 731 | 732 | /// I/O Port 733 | pub const PORTC = struct { 734 | /// address: 0x28 735 | /// Port C Data Register 736 | pub const PORTC = @as(*volatile u7, @ptrFromInt(0x28)); 737 | 738 | /// address: 0x27 739 | /// Port C Data Direction Register 740 | pub const DDRC = @as(*volatile u7, @ptrFromInt(0x27)); 741 | 742 | /// address: 0x26 743 | /// Port C Input Pins 744 | pub const PINC = @as(*volatile u7, @ptrFromInt(0x26)); 745 | }; 746 | 747 | /// I/O Port 748 | pub const PORTD = struct { 749 | /// address: 0x2b 750 | /// Port D Data Register 751 | pub const PORTD = @as(*volatile u8, @ptrFromInt(0x2b)); 752 | 753 | /// address: 0x2a 754 | /// Port D Data Direction Register 755 | pub const DDRD = @as(*volatile u8, @ptrFromInt(0x2a)); 756 | 757 | /// address: 0x29 758 | /// Port D Input Pins 759 | pub const PIND = @as(*volatile u8, @ptrFromInt(0x29)); 760 | }; 761 | 762 | /// Timer/Counter, 8-bit 763 | pub const TC0 = struct { 764 | /// address: 0x48 765 | /// Timer/Counter0 Output Compare Register 766 | pub const OCR0B = @as(*volatile u8, @ptrFromInt(0x48)); 767 | 768 | /// address: 0x47 769 | /// Timer/Counter0 Output Compare Register 770 | pub const OCR0A = @as(*volatile u8, @ptrFromInt(0x47)); 771 | 772 | /// address: 0x46 773 | /// Timer/Counter0 774 | pub const TCNT0 = @as(*volatile u8, @ptrFromInt(0x46)); 775 | 776 | /// address: 0x45 777 | /// Timer/Counter Control Register B 778 | pub const TCCR0B = @as(*volatile Mmio(8, packed struct { 779 | /// Clock Select 780 | /// 781 | /// 0x0: No Clock Source (Stopped) 782 | /// 0x1: Running, No Prescaling 783 | /// 0x2: Running, CLK/8 784 | /// 0x3: Running, CLK/64 785 | /// 0x4: Running, CLK/256 786 | /// 0x5: Running, CLK/1024 787 | /// 0x6: Running, ExtClk Tn Falling Edge 788 | /// 0x7: Running, ExtClk Tn Rising Edge 789 | CS0: u3, 790 | WGM02: u1, 791 | reserved0: u1, 792 | reserved1: u1, 793 | /// Force Output Compare B 794 | FOC0B: u1, 795 | /// Force Output Compare A 796 | FOC0A: u1, 797 | }), @ptrFromInt(0x45)); 798 | 799 | /// address: 0x44 800 | /// Timer/Counter Control Register A 801 | pub const TCCR0A = @as(*volatile Mmio(8, packed struct { 802 | /// Waveform Generation Mode 803 | WGM0: u2, 804 | reserved0: u1, 805 | reserved1: u1, 806 | /// Compare Output Mode, Fast PWm 807 | COM0B: u2, 808 | /// Compare Output Mode, Phase Correct PWM Mode 809 | COM0A: u2, 810 | }), @ptrFromInt(0x44)); 811 | 812 | /// address: 0x6e 813 | /// Timer/Counter0 Interrupt Mask Register 814 | pub const TIMSK0 = @as(*volatile Mmio(8, packed struct { 815 | /// Timer/Counter0 Overflow Interrupt Enable 816 | TOIE0: u1, 817 | /// Timer/Counter0 Output Compare Match A Interrupt Enable 818 | OCIE0A: u1, 819 | /// Timer/Counter0 Output Compare Match B Interrupt Enable 820 | OCIE0B: u1, 821 | padding0: u1, 822 | padding1: u1, 823 | padding2: u1, 824 | padding3: u1, 825 | padding4: u1, 826 | }), @ptrFromInt(0x6e)); 827 | 828 | /// address: 0x35 829 | /// Timer/Counter0 Interrupt Flag register 830 | pub const TIFR0 = @as(*volatile Mmio(8, packed struct { 831 | /// Timer/Counter0 Overflow Flag 832 | TOV0: u1, 833 | /// Timer/Counter0 Output Compare Flag 0A 834 | OCF0A: u1, 835 | /// Timer/Counter0 Output Compare Flag 0B 836 | OCF0B: u1, 837 | padding0: u1, 838 | padding1: u1, 839 | padding2: u1, 840 | padding3: u1, 841 | padding4: u1, 842 | }), @ptrFromInt(0x35)); 843 | 844 | /// address: 0x43 845 | /// General Timer/Counter Control Register 846 | pub const GTCCR = @as(*volatile Mmio(8, packed struct { 847 | /// Prescaler Reset Timer/Counter1 and Timer/Counter0 848 | PSRSYNC: u1, 849 | reserved0: u1, 850 | reserved1: u1, 851 | reserved2: u1, 852 | reserved3: u1, 853 | reserved4: u1, 854 | reserved5: u1, 855 | /// Timer/Counter Synchronization Mode 856 | TSM: u1, 857 | }), @ptrFromInt(0x43)); 858 | }; 859 | 860 | /// External Interrupts 861 | pub const EXINT = struct { 862 | /// address: 0x69 863 | /// External Interrupt Control Register 864 | pub const EICRA = @as(*volatile Mmio(8, packed struct { 865 | /// External Interrupt Sense Control 0 Bits 866 | /// 867 | /// 0x0: Low Level of INTX 868 | /// 0x1: Any Logical Change of INTX 869 | /// 0x2: Falling Edge of INTX 870 | /// 0x3: Rising Edge of INTX 871 | ISC0: u2, 872 | /// External Interrupt Sense Control 1 Bits 873 | /// 874 | /// 0x0: Low Level of INTX 875 | /// 0x1: Any Logical Change of INTX 876 | /// 0x2: Falling Edge of INTX 877 | /// 0x3: Rising Edge of INTX 878 | ISC1: u2, 879 | padding0: u1, 880 | padding1: u1, 881 | padding2: u1, 882 | padding3: u1, 883 | }), @ptrFromInt(0x69)); 884 | 885 | /// address: 0x3d 886 | /// External Interrupt Mask Register 887 | pub const EIMSK = @as(*volatile Mmio(8, packed struct { 888 | /// External Interrupt Request 1 Enable 889 | INT: u2, 890 | padding0: u1, 891 | padding1: u1, 892 | padding2: u1, 893 | padding3: u1, 894 | padding4: u1, 895 | padding5: u1, 896 | }), @ptrFromInt(0x3d)); 897 | 898 | /// address: 0x3c 899 | /// External Interrupt Flag Register 900 | pub const EIFR = @as(*volatile Mmio(8, packed struct { 901 | /// External Interrupt Flags 902 | INTF: u2, 903 | padding0: u1, 904 | padding1: u1, 905 | padding2: u1, 906 | padding3: u1, 907 | padding4: u1, 908 | padding5: u1, 909 | }), @ptrFromInt(0x3c)); 910 | 911 | /// address: 0x68 912 | /// Pin Change Interrupt Control Register 913 | pub const PCICR = @as(*volatile Mmio(8, packed struct { 914 | /// Pin Change Interrupt Enables 915 | PCIE: u3, 916 | padding0: u1, 917 | padding1: u1, 918 | padding2: u1, 919 | padding3: u1, 920 | padding4: u1, 921 | }), @ptrFromInt(0x68)); 922 | 923 | /// address: 0x6d 924 | /// Pin Change Mask Register 2 925 | pub const PCMSK2 = @as(*volatile Mmio(8, packed struct { 926 | /// Pin Change Enable Masks 927 | PCINT: u8, 928 | }), @ptrFromInt(0x6d)); 929 | 930 | /// address: 0x6c 931 | /// Pin Change Mask Register 1 932 | pub const PCMSK1 = @as(*volatile Mmio(8, packed struct { 933 | /// Pin Change Enable Masks 934 | PCINT: u7, 935 | padding0: u1, 936 | }), @ptrFromInt(0x6c)); 937 | 938 | /// address: 0x6b 939 | /// Pin Change Mask Register 0 940 | pub const PCMSK0 = @as(*volatile Mmio(8, packed struct { 941 | /// Pin Change Enable Masks 942 | PCINT: u8, 943 | }), @ptrFromInt(0x6b)); 944 | 945 | /// address: 0x3b 946 | /// Pin Change Interrupt Flag Register 947 | pub const PCIFR = @as(*volatile Mmio(8, packed struct { 948 | /// Pin Change Interrupt Flags 949 | PCIF: u3, 950 | padding0: u1, 951 | padding1: u1, 952 | padding2: u1, 953 | padding3: u1, 954 | padding4: u1, 955 | }), @ptrFromInt(0x3b)); 956 | }; 957 | 958 | /// Serial Peripheral Interface 959 | pub const SPI = struct { 960 | /// address: 0x4e 961 | /// SPI Data Register 962 | pub const SPDR = @as(*volatile u8, @ptrFromInt(0x4e)); 963 | 964 | /// address: 0x4d 965 | /// SPI Status Register 966 | pub const SPSR = @as(*volatile Mmio(8, packed struct { 967 | /// Double SPI Speed Bit 968 | SPI2X: u1, 969 | reserved0: u1, 970 | reserved1: u1, 971 | reserved2: u1, 972 | reserved3: u1, 973 | reserved4: u1, 974 | /// Write Collision Flag 975 | WCOL: u1, 976 | /// SPI Interrupt Flag 977 | SPIF: u1, 978 | }), @ptrFromInt(0x4d)); 979 | 980 | /// address: 0x4c 981 | /// SPI Control Register 982 | pub const SPCR = @as(*volatile Mmio(8, packed struct { 983 | /// SPI Clock Rate Selects 984 | /// 985 | /// 0x0: fosc/2 or fosc/4 986 | /// 0x1: fosc/8 or fosc/16 987 | /// 0x2: fosc/32 or fosc/64 988 | /// 0x3: fosc/64 or fosc/128 989 | SPR: u2, 990 | /// Clock Phase 991 | CPHA: u1, 992 | /// Clock polarity 993 | CPOL: u1, 994 | /// Master/Slave Select 995 | MSTR: u1, 996 | /// Data Order 997 | DORD: u1, 998 | /// SPI Enable 999 | SPE: u1, 1000 | /// SPI Interrupt Enable 1001 | SPIE: u1, 1002 | }), @ptrFromInt(0x4c)); 1003 | }; 1004 | 1005 | /// Watchdog Timer 1006 | pub const WDT = struct { 1007 | /// address: 0x60 1008 | /// Watchdog Timer Control Register 1009 | pub const WDTCSR = @as(*volatile Mmio(8, packed struct { 1010 | reserved0: u1, 1011 | reserved1: u1, 1012 | reserved2: u1, 1013 | /// Watch Dog Enable 1014 | WDE: u1, 1015 | /// Watchdog Change Enable 1016 | WDCE: u1, 1017 | reserved3: u1, 1018 | /// Watchdog Timeout Interrupt Enable 1019 | WDIE: u1, 1020 | /// Watchdog Timeout Interrupt Flag 1021 | WDIF: u1, 1022 | }), @ptrFromInt(0x60)); 1023 | }; 1024 | 1025 | /// CPU Registers 1026 | pub const CPU = struct { 1027 | /// address: 0x64 1028 | /// Power Reduction Register 1029 | pub const PRR = @as(*volatile Mmio(8, packed struct { 1030 | /// Power Reduction ADC 1031 | PRADC: u1, 1032 | /// Power Reduction USART 1033 | PRUSART0: u1, 1034 | /// Power Reduction Serial Peripheral Interface 1035 | PRSPI: u1, 1036 | /// Power Reduction Timer/Counter1 1037 | PRTIM1: u1, 1038 | reserved0: u1, 1039 | /// Power Reduction Timer/Counter0 1040 | PRTIM0: u1, 1041 | /// Power Reduction Timer/Counter2 1042 | PRTIM2: u1, 1043 | /// Power Reduction TWI 1044 | PRTWI: u1, 1045 | }), @ptrFromInt(0x64)); 1046 | 1047 | /// address: 0x66 1048 | /// Oscillator Calibration Value 1049 | pub const OSCCAL = @as(*volatile u8, @ptrFromInt(0x66)); 1050 | 1051 | /// address: 0x61 1052 | /// Clock Prescale Register 1053 | pub const CLKPR = @as(*volatile Mmio(8, packed struct { 1054 | /// Clock Prescaler Select Bits 1055 | /// 1056 | /// 0x0: 1 1057 | /// 0x1: 2 1058 | /// 0x2: 4 1059 | /// 0x3: 8 1060 | /// 0x4: 16 1061 | /// 0x5: 32 1062 | /// 0x6: 64 1063 | /// 0x7: 128 1064 | /// 0x8: 256 1065 | CLKPS: u4, 1066 | reserved0: u1, 1067 | reserved1: u1, 1068 | reserved2: u1, 1069 | /// Clock Prescaler Change Enable 1070 | CLKPCE: u1, 1071 | }), @ptrFromInt(0x61)); 1072 | 1073 | /// address: 0x5f 1074 | /// Status Register 1075 | pub const SREG = @as(*volatile Mmio(8, packed struct { 1076 | /// Carry Flag 1077 | C: u1, 1078 | /// Zero Flag 1079 | Z: u1, 1080 | /// Negative Flag 1081 | N: u1, 1082 | /// Two's Complement Overflow Flag 1083 | V: u1, 1084 | /// Sign Bit 1085 | S: u1, 1086 | /// Half Carry Flag 1087 | H: u1, 1088 | /// Bit Copy Storage 1089 | T: u1, 1090 | /// Global Interrupt Enable 1091 | I: u1, 1092 | }), @ptrFromInt(0x5f)); 1093 | 1094 | /// address: 0x5d 1095 | /// Stack Pointer 1096 | pub const SP = @as(*volatile u12, @ptrFromInt(0x5d)); 1097 | 1098 | /// address: 0x57 1099 | /// Store Program Memory Control and Status Register 1100 | pub const SPMCSR = @as(*volatile Mmio(8, packed struct { 1101 | /// Store Program Memory 1102 | SPMEN: u1, 1103 | /// Page Erase 1104 | PGERS: u1, 1105 | /// Page Write 1106 | PGWRT: u1, 1107 | /// Boot Lock Bit Set 1108 | BLBSET: u1, 1109 | /// Read-While-Write section read enable 1110 | RWWSRE: u1, 1111 | /// Signature Row Read 1112 | SIGRD: u1, 1113 | /// Read-While-Write Section Busy 1114 | RWWSB: u1, 1115 | /// SPM Interrupt Enable 1116 | SPMIE: u1, 1117 | }), @ptrFromInt(0x57)); 1118 | 1119 | /// address: 0x55 1120 | /// MCU Control Register 1121 | pub const MCUCR = @as(*volatile Mmio(8, packed struct { 1122 | IVCE: u1, 1123 | IVSEL: u1, 1124 | reserved0: u1, 1125 | reserved1: u1, 1126 | PUD: u1, 1127 | /// BOD Sleep Enable 1128 | BODSE: u1, 1129 | /// BOD Sleep 1130 | BODS: u1, 1131 | padding0: u1, 1132 | }), @ptrFromInt(0x55)); 1133 | 1134 | /// address: 0x54 1135 | /// MCU Status Register 1136 | pub const MCUSR = @as(*volatile Mmio(8, packed struct { 1137 | /// Power-on reset flag 1138 | PORF: u1, 1139 | /// External Reset Flag 1140 | EXTRF: u1, 1141 | /// Brown-out Reset Flag 1142 | BORF: u1, 1143 | /// Watchdog Reset Flag 1144 | WDRF: u1, 1145 | padding0: u1, 1146 | padding1: u1, 1147 | padding2: u1, 1148 | padding3: u1, 1149 | }), @ptrFromInt(0x54)); 1150 | 1151 | /// address: 0x53 1152 | /// Sleep Mode Control Register 1153 | pub const SMCR = @as(*volatile Mmio(8, packed struct { 1154 | /// Sleep Enable 1155 | SE: u1, 1156 | /// Sleep Mode Select Bits 1157 | /// 1158 | /// 0x0: Idle 1159 | /// 0x1: ADC Noise Reduction (If Available) 1160 | /// 0x2: Power Down 1161 | /// 0x3: Power Save 1162 | /// 0x4: Reserved 1163 | /// 0x5: Reserved 1164 | /// 0x6: Standby 1165 | /// 0x7: Extended Standby 1166 | SM: u3, 1167 | padding0: u1, 1168 | padding1: u1, 1169 | padding2: u1, 1170 | padding3: u1, 1171 | }), @ptrFromInt(0x53)); 1172 | 1173 | /// address: 0x4b 1174 | /// General Purpose I/O Register 2 1175 | pub const GPIOR2 = @as(*volatile u8, @ptrFromInt(0x4b)); 1176 | 1177 | /// address: 0x4a 1178 | /// General Purpose I/O Register 1 1179 | pub const GPIOR1 = @as(*volatile u8, @ptrFromInt(0x4a)); 1180 | 1181 | /// address: 0x3e 1182 | /// General Purpose I/O Register 0 1183 | pub const GPIOR0 = @as(*volatile u8, @ptrFromInt(0x3e)); 1184 | }; 1185 | 1186 | /// EEPROM 1187 | pub const EEPROM = struct { 1188 | /// address: 0x41 1189 | /// EEPROM Address Register Bytes 1190 | pub const EEAR = @as(*volatile u10, @ptrFromInt(0x41)); 1191 | 1192 | /// address: 0x40 1193 | /// EEPROM Data Register 1194 | pub const EEDR = @as(*volatile u8, @ptrFromInt(0x40)); 1195 | 1196 | /// address: 0x3f 1197 | /// EEPROM Control Register 1198 | pub const EECR = @as(*volatile Mmio(8, packed struct { 1199 | /// EEPROM Read Enable 1200 | EERE: u1, 1201 | /// EEPROM Write Enable 1202 | EEPE: u1, 1203 | /// EEPROM Master Write Enable 1204 | EEMPE: u1, 1205 | /// EEPROM Ready Interrupt Enable 1206 | EERIE: u1, 1207 | /// EEPROM Programming Mode Bits 1208 | /// 1209 | /// 0x0: Erase and Write in one operation 1210 | /// 0x1: Erase Only 1211 | /// 0x2: Write Only 1212 | EEPM: u2, 1213 | padding0: u1, 1214 | padding1: u1, 1215 | }), @ptrFromInt(0x3f)); 1216 | }; 1217 | }; 1218 | 1219 | const std = @import("std"); 1220 | 1221 | pub fn mmio(addr: usize, comptime size: u8, comptime PackedT: type) *volatile Mmio(size, PackedT) { 1222 | return @as(*volatile Mmio(size, PackedT), @ptrFromInt(addr)); 1223 | } 1224 | 1225 | pub fn Mmio(comptime size: u8, comptime PackedT: type) type { 1226 | if ((size % 8) != 0) 1227 | @compileError("size must be divisible by 8!"); 1228 | 1229 | if (!std.math.isPowerOfTwo(size / 8)) 1230 | @compileError("size must encode a power of two number of bytes!"); 1231 | 1232 | const IntT = std.meta.Int(.unsigned, size); 1233 | 1234 | if (@sizeOf(PackedT) != (size / 8)) 1235 | @compileError(std.fmt.comptimePrint("IntT and PackedT must have the same size!, they are {} and {} bytes respectively", .{ size / 8, @sizeOf(PackedT) })); 1236 | 1237 | return extern struct { 1238 | const Self = @This(); 1239 | 1240 | raw: IntT, 1241 | 1242 | pub const underlying_type = PackedT; 1243 | 1244 | pub inline fn read(addr: *volatile Self) PackedT { 1245 | return @as(PackedT, @bitCast(addr.raw)); 1246 | } 1247 | 1248 | pub inline fn write(addr: *volatile Self, val: PackedT) void { 1249 | // This is a workaround for a compiler bug related to miscompilation 1250 | // If the tmp var is not used, result location will fuck things up 1251 | const tmp = @as(IntT, @bitCast(val)); 1252 | addr.raw = tmp; 1253 | } 1254 | 1255 | pub inline fn modify(addr: *volatile Self, fields: anytype) void { 1256 | var val = read(addr); 1257 | inline for (@typeInfo(@TypeOf(fields)).Struct.fields) |field| { 1258 | @field(val, field.name) = @field(fields, field.name); 1259 | } 1260 | write(addr, val); 1261 | } 1262 | 1263 | pub inline fn toggle(addr: *volatile Self, fields: anytype) void { 1264 | var val = read(addr); 1265 | inline for (@typeInfo(@TypeOf(fields)).Struct.fields) |field| { 1266 | @field(val, @tagName(field.default_value.?)) = !@field(val, @tagName(field.default_value.?)); 1267 | } 1268 | write(addr, val); 1269 | } 1270 | }; 1271 | } 1272 | 1273 | pub fn MmioInt(comptime size: u8, comptime T: type) type { 1274 | return extern struct { 1275 | const Self = @This(); 1276 | 1277 | raw: std.meta.Int(.unsigned, size), 1278 | 1279 | pub inline fn read(addr: *volatile Self) T { 1280 | return @as(T, @truncate(addr.raw)); 1281 | } 1282 | 1283 | pub inline fn modify(addr: *volatile Self, val: T) void { 1284 | const Int = std.meta.Int(.unsigned, size); 1285 | const mask = ~@as(Int, (1 << @bitSizeOf(T)) - 1); 1286 | 1287 | const tmp = addr.raw; 1288 | addr.raw = (tmp & mask) | val; 1289 | } 1290 | }; 1291 | } 1292 | 1293 | pub fn mmioInt(addr: usize, comptime size: usize, comptime T: type) *volatile MmioInt(size, T) { 1294 | return @as(*volatile MmioInt(size, T), @ptrFromInt(addr)); 1295 | } 1296 | 1297 | const InterruptVector = extern union { 1298 | C: *const fn () callconv(.C) void, 1299 | Naked: *const fn () callconv(.Naked) void, 1300 | // Interrupt is not supported on arm 1301 | }; 1302 | 1303 | const unhandled = InterruptVector{ 1304 | .C = &(struct { 1305 | fn tmp() callconv(.C) noreturn { 1306 | @panic("unhandled interrupt"); 1307 | } 1308 | }.tmp), 1309 | }; 1310 | -------------------------------------------------------------------------------- /src/gpio.zig: -------------------------------------------------------------------------------- 1 | const regs = @import("atmega328p.zig").registers; 2 | 3 | const MODE = enum { in, out }; 4 | 5 | // PORTB: pins D8 to D13 6 | fn init_portb(pin: u3, comptime dir: MODE) void { 7 | regs.PORTB.DDRB.* = @as(u8, @intFromEnum(dir)) << pin; 8 | } 9 | 10 | pub fn toggle_portb(comptime pin: u3) void { 11 | var val = regs.PORTB.PORTB.*; 12 | val ^= 1 << pin; 13 | regs.PORTB.PORTB.* = val; 14 | } 15 | 16 | // PORTD: pins D0 TO D7 17 | fn init_portd(pin: u3, comptime dir: MODE) void { 18 | regs.PORTD.DDRD.* = @as(u8, @intFromEnum(dir)) << pin; 19 | } 20 | 21 | pub fn toggle_portd(comptime pin: u3) void { 22 | var val = regs.PORTD.PORTD.*; 23 | val ^= 1 << pin; 24 | regs.PORTD.PORTD.* = val; 25 | } 26 | 27 | // TODO: PORTC: analog pins 28 | 29 | pub const PIN = enum { 30 | D0, 31 | D1, 32 | D2, 33 | D3, 34 | D4, 35 | D5, 36 | D6, 37 | D7, 38 | D8, 39 | D9, 40 | D10, 41 | D11, 42 | D12, 43 | D13, 44 | A0, 45 | A1, 46 | A3, 47 | A4, 48 | A5, 49 | }; 50 | 51 | pub fn init(comptime pin: PIN, comptime dir: MODE) void { 52 | const i = @intFromEnum(pin); 53 | if (i <= 7) { 54 | init_portd(@as(u3, @intCast(i)), dir); 55 | } else if (i >= 8 and i <= 13) { 56 | init_portb(@as(u3, @intCast(i - 8)), dir); 57 | } 58 | } 59 | 60 | pub fn toggle(comptime pin: PIN) void { 61 | const i = comptime @intFromEnum(pin); 62 | if (i <= 7) { 63 | toggle_portd(@as(u3, @intCast(i))); 64 | } else if (i >= 8 and i <= 13) { 65 | toggle_portb(@as(u3, @intCast(i - 8))); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/linker.ld: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | flash (rx) : ORIGIN = 0, LENGTH = 32K 4 | ram (rw!x) : ORIGIN = 0x800100, LENGTH = 2K 5 | } 6 | 7 | SECTIONS 8 | { 9 | .text : 10 | { 11 | KEEP(*(.vectors)) 12 | 13 | *(.text*) 14 | } > flash 15 | 16 | .data : 17 | { 18 | __data_start = .; 19 | *(.rodata*) 20 | *(.data*) 21 | __data_end = .; 22 | } > ram AT> flash 23 | 24 | .bss (NOLOAD) : 25 | { 26 | __bss_start = .; 27 | *(.bss*) 28 | __bss_end = .; 29 | } > ram 30 | 31 | __data_load_start = LOADADDR(.data); 32 | } -------------------------------------------------------------------------------- /src/main.zig: -------------------------------------------------------------------------------- 1 | const uart = @import("uart.zig"); 2 | const gpio = @import("gpio.zig"); 3 | 4 | // This is put in the data section 5 | var ch: u8 = '!'; 6 | 7 | // This ends up in the bss section 8 | var bss_stuff: [9]u8 = .{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 9 | 10 | // Put public functions here named after interrupts to instantiate them as 11 | // interrupt handlers. If you name one incorrectly you'll get a compiler error 12 | // with the full list of options. 13 | pub const interrupts = struct { 14 | // Pin Change Interrupt Source 0 15 | // pub fn PCINT0() void {} 16 | }; 17 | 18 | pub fn main() void { 19 | uart.init(115200); 20 | uart.write("All your codebase are belong to us!\r\n\r\n"); 21 | 22 | if (bss_stuff[0] == 0) 23 | uart.write("Ahh its actually zero!\r\n"); 24 | 25 | bss_stuff = "\r\nhello\r\n".*; 26 | uart.write(&bss_stuff); 27 | 28 | // This will actually call our panic handler in start.zig when 29 | // uncommented. 30 | // var x: u8 = 255; 31 | // x += 1; 32 | 33 | gpio.init(.D13, .out); 34 | gpio.init(.D5, .out); 35 | 36 | while (true) { 37 | uart.write_ch(ch); 38 | if (ch < '~') { 39 | ch += 1; 40 | } else { 41 | ch = '!'; 42 | uart.write("\r\n"); 43 | } 44 | 45 | gpio.toggle(.D13); 46 | gpio.toggle(.D5); 47 | delay_cycles(50000); 48 | } 49 | } 50 | 51 | fn delay_cycles(cycles: u32) void { 52 | var count: u32 = 0; 53 | while (count < cycles) : (count += 1) { 54 | asm volatile ("nop"); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/start.zig: -------------------------------------------------------------------------------- 1 | const main = @import("main.zig"); 2 | const atmega328p = @import("atmega328p.zig"); 3 | const uart = @import("uart.zig"); 4 | const std = @import("std"); 5 | const builtin = std.builtin; 6 | 7 | comptime { 8 | std.debug.assert(std.mem.eql(u8, "RESET", std.meta.fields(atmega328p.VectorTable)[0].name)); 9 | var asm_str: []const u8 = ".section .vectors\njmp _start\n"; 10 | 11 | const has_interrupts = @hasDecl(main, "interrupts"); 12 | if (has_interrupts) { 13 | if (@hasDecl(main.interrupts, "RESET")) 14 | @compileError("Not allowed to overload the reset vector"); 15 | 16 | for (std.meta.declarations(main.interrupts)) |decl| { 17 | if (!@hasField(atmega328p.VectorTable, decl.name)) { 18 | var msg: []const u8 = "There is no such interrupt as '" ++ decl.name ++ "'. ISRs the 'interrupts' namespace must be one of:\n"; 19 | for (std.meta.fields(atmega328p.VectorTable)) |field| { 20 | if (!std.mem.eql(u8, "RESET", field.name)) { 21 | msg = msg ++ " " ++ field.name ++ "\n"; 22 | } 23 | } 24 | 25 | @compileError(msg); 26 | } 27 | } 28 | } 29 | 30 | for (std.meta.fields(atmega328p.VectorTable)[1..]) |field| { 31 | const new_insn = if (has_interrupts) overload: { 32 | if (@hasDecl(main.interrupts, field.name)) { 33 | const handler = @field(main.interrupts, field.name); 34 | const calling_convention = switch (@typeInfo(@TypeOf(@field(main.interrupts, field.name)))) { 35 | .Fn => |info| info.calling_convention, 36 | else => @compileError("Declarations in 'interrupts' namespace must all be functions. '" ++ field.name ++ "' is not a function"), 37 | }; 38 | 39 | const exported_fn = switch (calling_convention) { 40 | .Unspecified => struct { 41 | fn wrapper() callconv(.C) void { 42 | //if (calling_convention == .Unspecified) // TODO: workaround for some weird stage1 bug 43 | @call(.{ .modifier = .always_inline }, handler, .{}); 44 | } 45 | }.wrapper, 46 | else => @compileError("Just leave interrupt handlers with an unspecified calling convention"), 47 | }; 48 | 49 | const options = .{ .name = field.name, .linkage = .Strong }; 50 | @export(exported_fn, options); 51 | break :overload "jmp " ++ field.name; 52 | } else { 53 | break :overload "jmp _unhandled_vector"; 54 | } 55 | } else "jmp _unhandled_vector"; 56 | 57 | asm_str = asm_str ++ new_insn ++ "\n"; 58 | } 59 | asm (asm_str); 60 | } 61 | 62 | export fn _unhandled_vector() void { 63 | while (true) {} 64 | } 65 | 66 | pub export fn _start() noreturn { 67 | // At startup the stack pointer is at the end of RAM 68 | // so, no need to set it manually! 69 | 70 | copy_data_to_ram(); 71 | clear_bss(); 72 | 73 | main.main(); 74 | while (true) {} 75 | } 76 | 77 | fn copy_data_to_ram() void { 78 | asm volatile ( 79 | \\ ; load Z register with the address of the data in flash 80 | \\ ldi r30, lo8(__data_load_start) 81 | \\ ldi r31, hi8(__data_load_start) 82 | \\ ; load X register with address of the data in ram 83 | \\ ldi r26, lo8(__data_start) 84 | \\ ldi r27, hi8(__data_start) 85 | \\ ; load address of end of the data in ram 86 | \\ ldi r24, lo8(__data_end) 87 | \\ ldi r25, hi8(__data_end) 88 | \\ rjmp .L2 89 | \\ 90 | \\.L1: 91 | \\ lpm r18, Z+ ; copy from Z into r18 and increment Z 92 | \\ st X+, r18 ; store r18 at location X and increment X 93 | \\ 94 | \\.L2: 95 | \\ cp r26, r24 96 | \\ cpc r27, r25 ; check and branch if we are at the end of data 97 | \\ brne .L1 98 | ); 99 | // Probably a good idea to add clobbers here, but compiler doesn't seem to care 100 | } 101 | 102 | fn clear_bss() void { 103 | asm volatile ( 104 | \\ ; load X register with the beginning of bss section 105 | \\ ldi r26, lo8(__bss_start) 106 | \\ ldi r27, hi8(__bss_start) 107 | \\ ; load end of the bss in registers 108 | \\ ldi r24, lo8(__bss_end) 109 | \\ ldi r25, hi8(__bss_end) 110 | \\ ldi r18, 0x00 111 | \\ rjmp .L4 112 | \\ 113 | \\.L3: 114 | \\ st X+, r18 115 | \\ 116 | \\.L4: 117 | \\ cp r26, r24 118 | \\ cpc r27, r25 ; check and branch if we are at the end of bss 119 | \\ brne .L3 120 | ); 121 | // Probably a good idea to add clobbers here, but compiler doesn't seem to care 122 | } 123 | 124 | pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace, _: ?usize) noreturn { 125 | // Currently assumes that the uart is initialized in main(). 126 | uart.write("PANIC: "); 127 | uart.write(msg); 128 | 129 | // TODO: print stack trace (addresses), which can than be turned into actual source line 130 | // numbers on the connected machine. 131 | _ = error_return_trace; 132 | while (true) {} 133 | } 134 | -------------------------------------------------------------------------------- /src/uart.zig: -------------------------------------------------------------------------------- 1 | const uno = @import("uno.zig"); 2 | const regs = @import("atmega328p.zig").registers; 3 | 4 | pub fn init(comptime baud: comptime_int) void { 5 | // Set baudrate 6 | regs.USART0.UBRR0.* = (uno.CPU_FREQ / (8 * baud)) - 1; 7 | 8 | // Default uart settings are 8n1, so no need to change them! 9 | regs.USART0.UCSR0A.modify(.{ .U2X0 = 1 }); 10 | 11 | // Enable transmitter! 12 | regs.USART0.UCSR0B.modify(.{ .TXEN0 = 1 }); 13 | } 14 | 15 | pub fn write(data: []const u8) void { 16 | for (data) |ch| { 17 | write_ch(ch); 18 | } 19 | 20 | // Wait till we are actually done sending 21 | while (regs.USART0.UCSR0A.read().TXC0 != 1) {} 22 | } 23 | 24 | pub fn write_ch(ch: u8) void { 25 | // Wait till the transmit buffer is empty 26 | while (regs.USART0.UCSR0A.read().UDRE0 != 1) {} 27 | 28 | regs.USART0.UDR0.* = ch; 29 | } 30 | -------------------------------------------------------------------------------- /src/uno.zig: -------------------------------------------------------------------------------- 1 | pub const CPU_FREQ = 16000000; 2 | --------------------------------------------------------------------------------