Skip to content

atmega: add SPI host HAL for the ATmega328P - #1076

Merged
mattnite merged 5 commits into
ZigEmbeddedGroup:mainfrom
matpala:atmega/spi
Oct 10, 2026
Merged

mattnite merged 5 commits into
ZigEmbeddedGroup:mainfrom
matpala:atmega/spi

Conversation

@matpala

@matpala matpala commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Adds a polling SPI host HAL for the ATmega328P and an RF-Nano V3.0 example that reads the nRF24L01+ STATUS and CONFIG registers.

Part of #1069.

Uses the wch HAL's instance pattern: instance.SPI0 exposes transfer methods, and Setup.instance defaults to it. Boards provide the CPU clock; applications configure SCK frequency, CPOL/CPHA and bit order.

The SCK divider is selected at compile time without exceeding the requested frequency. Requests below the minimum supported frequency cause a compile error. Host tests cover divider selection and rejection; testing the compile error itself remains a TODO (ziglang/zig#513).

apply configures SCK, MOSI and hardware SS as outputs. The caller controls chip select. Blocking transfers include writev_blocking and readv_blocking, return void, require exclusive peripheral access, and have no timeouts or WCOL reporting.

The HAL uses separate register pointers to work around the generated address-zero SPI pointer (#1072).

Tested on an RF-Nano V3.0: reading the radio returns STATUS=0x0E and CONFIG=0x08, its reset values, after both a warm and a cold start.

Comment thread port/microchip/atmega/src/hals/atmega328p/spi.zig Outdated
@Grazfather

Copy link
Copy Markdown
Collaborator

Will need to rebase now with the uart PR merged.

@matpala
matpala marked this pull request as ready for review October 9, 2026 12:32
@matpala

matpala commented Oct 9, 2026

Copy link
Copy Markdown
Contributor Author

Rebased on main and marked ready for review.

@Grazfather Grazfather left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great. Unfortunately I can't easily test it. I'll look into testing with some spi flash I have lying around.

Comment thread port/microchip/atmega/src/boards/arduino_nano.zig
Comment thread examples/microchip/atmega/src/spi_nrf24.zig Outdated
Comment thread port/microchip/atmega/src/hals/atmega328p/spi.zig
Comment thread port/microchip/atmega/src/hals/atmega328p/spi.zig Outdated
}
}

fn write_hex(byte: u8) void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use stdlib formatting, don't write your own.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't compile when I tried.

});

test_step.dependOn(&b.addRunArtifact(uart_tests).step);
for ([_][]const u8{ "uart", "spi" }) |name| {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason you didn't use the root of the hal for the tests? Then we wouldn't have to compile multiple.


/// SPI on PB5 (SCK), PB3 (MOSI) and PB4 (MISO). PB2 (D10) is set as an
/// output to keep the SPI in host mode.
pub const spi_setup: hal.spi.Setup = .{ .cpu_frequency = clock_frequencies.cpu };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Grazfather its goofy that the cpu frequency for the spi is being set in the board file.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CPU frequency is defined by the board. The 3v3 sets 8MHz and the 5v sets 16MHz on the pro mini, for example. It's just passed to the spi setup so that the correct clock divider can be configured.

The baud_rate is actually provided by the example.

/// is set as an output to keep the SPI in host mode.
pub const spi_setup: hal.spi.Setup = .{ .cpu_frequency = clock_frequencies.cpu };

pub const pin_map = .{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the pin map even used for anything? If not I'd like to delete it.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it is. This is basically the same in all atmega boards. I agree we should do something or trash it, but let's clean it up in a followup, since it touches a bunch of boards

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed in #1089. Nothing has read it since #800 removed experimental.zig (the Pin(spec) helper that looked names up in board.pin_map).

@Grazfather

Grazfather commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator

I ported my spi flash example from wch ch32v.

I will push in a followup PR, it is janky for now, since we don't have the UART logger and don't have proper timekeeping.

That said, I verified that this works well on my pro mini:

❯ tio /dev/tty.usbserial-1140 -b 57600
[09:02:59.855] tio 3.9
[09:02:59.856] Press ctrl-t q to quit
[09:02:59.865] Connected to /dev/tty.usbserial-1140
W25Q128 SPI Flash Test
======================

Initializing SPI...
SPI initialized

Test 1: Reading JEDEC ID...
  JEDEC ID: 0xEF4018
  Device: Winbond W25Q128 - VERIFIED

Test 2: Reading Status Register...
  Status: 0x00
  BUSY: false
  WEL:  false

Test 3: Erase/Write/Read Cycle...
  Erasing sector at 0x001000...
  Sector erased
  Writing 256 bytes...
  Page written
  Reading 256 bytes...
  Page read
  Verifying data...
  Data verified

All tests complete!

@mattnite
mattnite merged commit 1f131ad into ZigEmbeddedGroup:main Oct 10, 2026
37 checks passed
mattnite pushed a commit that referenced this pull request Oct 13, 2026
Follow-up to #1076, where @mattnite asked whether the pin map was used
and @Grazfather suggested cleaning it up in a follow-up.

Nothing in the repo reads `pin_map`, so this removes it from the six
atmega boards. Examples address pins with `hal.gpio.pin(.b, 5)`, and the
RF-Nano's doc comment already lists the radio and LED pins.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants