added posts up to 2022
51
_posts/2022-05-03-mmv3.md
Normal file
@ -0,0 +1,51 @@
|
||||
---
|
||||
title: MMv3
|
||||
date: 2022-05-03
|
||||
categories: projects school
|
||||
excerpt: It still can't do maze-solving (from lack of trying and resources), but it's much smaller, easier to assemble, and runs Python.
|
||||
header:
|
||||
teaser: /assets/img/2022/mmv3_2.jpg
|
||||
|
||||
gallery:
|
||||
- image_path: /assets/img/2022/mmv3_2.jpg
|
||||
- image_path: /assets/img/2022/mmv3_3.jpg
|
||||
|
||||
gallery2:
|
||||
- image_path: /assets/img/2022/mmv3_5.jpg
|
||||
- image_path: /assets/img/2022/mmv3_6.jpg
|
||||
- image_path: /assets/img/2022/mmv3_7.jpg
|
||||
- image_path: /assets/img/2022/mmv3_8.jpg
|
||||
- image_path: /assets/img/2022/mmv3_9.jpg
|
||||
---
|
||||
|
||||
<https://github.com/dragonlock2/MMv3>
|
||||
|
||||
Instead of spending the Fall 2021 semester teaching Micromouse, I developed a completely new robot and curriculum. The main problems I wanted to solve were twofold. First was the recurring issue with having students learn C++ from scratch. Second was improving mass manufacturability in case the course had to go remote again or for scalability. Considering the budget of the class, I also had to keep things under $40 per robot. Luckily, the release of the Pi Pico, which can run CircuitPython and only costs $4, made this entire project feasible.
|
||||
|
||||
## Design
|
||||
|
||||
The design ended up being pretty straightforward. I took inspiration from competition-winning mice and used the PCB as the chassis. The biggest electrical change I made was switching from ToF sensors to a differential IR sensor pair. To minimize the BOM, I removed things like decoupling capacitors and series/pull-down resistors on MOSFET gates. I used the simplest IR sensor circuit and even multiplexed them among the Pico’s 3 ADC pins without additional parts. With all the changes, the entire BOM except for motors could now be sourced from US distributors, relieving the issue of having to order parts months in advance. Since the schematic and layout will continue to evolve, I won’t attach any pictures here.
|
||||
|
||||
I finished the first iteration (MMv3) during the summer. Since I had the board area and pins available, I also added a 1-Wire EEPROM and LSM9DS1. Not realizing the EEPROM I bought only worked at overdrive speeds, I spent one too many hours modifying CircuitPython in non-portable ways to get the support added.
|
||||
|
||||
{% include figure image_path="/assets/img/2022/mmv3_1.jpg" %}
|
||||
|
||||
Once school started, I completed the next iteration (MMv3.1) which turned the design into something more complete. My favorite part was not needing a 9V cable and finally having a power switch.
|
||||
|
||||
{% include gallery %}
|
||||
|
||||
After having my officers assemble the previous design, I made several changes focused on manufacturability for what was supposed to be the final iteration (MMv3.2). Since I am a terrible leader when it comes to motivating other people, I ended up spending winter break assembling MMv3.2 and rewriting all of the labs around it.
|
||||
|
||||
{% include figure image_path="/assets/img/2022/mmv3_4.jpg" %}
|
||||
|
||||
As fate would have it, I made a slight mistake in the IR detector circuit. The reason I had to use such large pull-up resistors was that I had the photodiode in forward bias. In reverse bias, the current becomes much more predictable and larger, enabling smaller resistors. As has been a theme with reusing circuits I designed a decade ago, I used this circuit in my [coil gun](https://matthewtran.dev/2014/04/coil-gun-v2/) where I had IR sensitivity issues. Well, I finally had the iteration (MMv3.3) the Spring 2022 class would use.
|
||||
|
||||
## Course
|
||||
|
||||
When it came to running the course, everything went pretty well. It took me almost a decade to go from through-hole soldering to SMD soldering, so I made sure students got exposure to reflow soldering and industry manufacturing methods early. For their first time, the reflow results were surprisingly good and consistent. However, due to using $6 soldering irons, the hand soldering portions were not so great. After 3 lab sessions, office hours, much debugging, and many destroyed traces, all of the students had something working.
|
||||
|
||||
For firmware development, the familiarity of Python and having running new code be as simple as uploading a file to a flash drive made things run very smoothly. Definitely an improvement over Arduino, albeit with a performance hit.
|
||||
|
||||
{% include gallery id="gallery2" %}
|
||||
|
||||
There’s a laundry list of updates to make for the next iteration of the class, but I would consider MMv3 a success. My time at Berkeley has come to an end, but perhaps future generations can push the class towards actual maze-solving and even competing.
|
47
_posts/2022-05-04-6wire.md
Normal file
@ -0,0 +1,47 @@
|
||||
---
|
||||
title: 6wire
|
||||
date: 2022-05-04
|
||||
categories: projects
|
||||
excerpt: After learning about the 6 wire measurement technique in industry, I decided to make a board to try it out. It's also an auto-ranging 4-wire ohmmeter.
|
||||
header:
|
||||
teaser: /assets/img/2022/6wire_1.jpg
|
||||
|
||||
gallery:
|
||||
- image_path: /assets/img/2022/6wire_1.jpg
|
||||
- image_path: /assets/img/2022/6wire_2.jpg
|
||||
---
|
||||
|
||||
<https://github.com/dragonlock2/kicadboards/tree/main/projects/6wire>
|
||||
<https://github.com/dragonlock2/zephyrboards/tree/main/samples/6wire>
|
||||
|
||||
After being in industry for a bit, I learned about the [6-wire measurement technique](https://www.ni.com/en-us/support/documentation/supplemental/06/improve-resistance-measurement-accuracy-with-6-wire-technique.html#). The idea is clever yet simple and enables accurate measurement of components in-circuit despite it being in parallel with other components. It starts with a standard 4-wire (or 2-wire) meter placed across the DUT. For every alternate current path, we place a two wire “guard” at a node along that path that drives the voltage at that node to the same as one of the DUT leads (depending on architecture). Of course, we could also use one wire merging both feedback and current driving, but that comes with the same drawbacks as using a 2-wire over a 4-wire meter. Since both nodes share the same voltage, no current flows along that path and the 4-wire meter just sees the DUT. This idea can be easily extended to more alternate current paths.
|
||||
|
||||
## Design
|
||||
|
||||
Since I came up with this idea two years ago, I had already ordered many of the chips I would need in this project. I wanted it to be auto-ranging so I got several precision resistors and a MAX4639 analog switch. Thankfully I got the double-pole model which made the architecture viable.
|
||||
|
||||
An important factor in the 6-wire guard performance is the accuracy with which the nodes’ voltages match. This necessitates a low input offset voltage (and low input bias current for large resistance measurements) op-amp so I got the industry-leading MAX4238. Thankfully I got the one that was stable down to unity gain.
|
||||
|
||||
For the 4-wire meter, I decided to use the ADS122C04 24-bit ADC which can take differential measurements and reference voltages. I used the ratiometric ohmmeter architecture from the app note. Since I had reference resistors from 10Ω to 10MΩ, I needed to make sure that everything was within specification across that range.
|
||||
|
||||
To ensure the 10Ω 1/8W resistors I used wouldn’t overheat, I created a ~100mA current source with a standard op-amp and PMOS. I didn’t have any smaller resistors for current measurement, so used another 10Ω one. I initially set the current lower, but had to increase it due to an issue discussed later. While I could’ve done a low-side current sink with an NMOS, I didn’t due to the 10Ω reference resistor which reduced the voltage budget I needed to meet the other ADC input voltage requirements.
|
||||
|
||||
For maximum resolution across the range, I made sure to support the internal PGA in the ADC. The main constraint was that the analog inputs had to stay below a certain voltage (~2.7V for my use case). There’s also minimums, but I was well within those. As such, I effectively created a voltage regulator with a standard op-amp and PMOS. I was initially worried about PMOS leakage currents causing issues with the 10MΩ reference, but based on rough estimates it was going to be fine.
|
||||
|
||||
The layout ended up being pretty straightforward once I came up with an organization that separated the analog and digital domains cleanly. It was also my first time doing a Kelvin connection! I admit my analog noise design and layout skills aren’t great, so I’m sure there’s much to be improved. Based on the good but lower than expected ENOB measurements I ended up getting, this is definitely the case.
|
||||
|
||||
## Firmware
|
||||
|
||||
When writing the firmware, I ended up having quite a bit of fun messing around with the features of Zephyr. I really like interrupt or thread based drivers that don’t require the application code to constantly call a function. Thus, I made the ADC do continuous conversions and send GPIO interrupts that I would then use to increment a semaphore that enabled a thread to run. The auto-ranging algorithm was also interesting since I had to consider different reference resistors and gains while also making sure I converged on the right selection quickly due to the low 20Hz sample rate of the ADC (chosen for maximum ENOB).
|
||||
|
||||
Since I used a ATSAMD21G16B with 8KB of SRAM and 64KB of flash, I initially had issues with RAM usage. Zephyr is not exactly known for a small memory footprint. Everything was fine until I added the USB CDC serial driver, so I started by looking into its memory usage. It had some ring buffers which I shrank. Then I realized a lot of memory was potentially taken by stacks for various threads. After scouring the compiled Kconfig for stacks to shrink, I shrank each one until the code stopped functioning. At the end of the day, I barely fit everything within 8KB.
|
||||
|
||||
## Results
|
||||
|
||||
{% include gallery %}
|
||||
|
||||
Once the 4-wire ohmmeter section was working, I started testing the 6-wire guard feature. It initially didn’t work, with the measurements being thrown off despite the node voltages matching. After looking at the schematic again and pondering for awhile, I realized a crucial mistake. With the circuit using Vsense+ as the guard reference, the extra current it sources also flows through the reference resistor but not the DUT.
|
||||
|
||||
After even more thought and slight panic, I had a simple solution. While Vsense+ is used in the NI diagram, using Vsense- is just as valid and would be considered its “dual”. Rather than source current into the low side to equalize node voltages, the guard sinks current from the high side. This ensures that the DUT and reference resistor see equal current. Since I have a high-side current source, this would “steal” current from the DUT. As such, I increased the limit from 80mA to 100mA to minimize this effect.
|
||||
|
||||
Thankfully, the change could be realized with a trace cut and bodge wire. With that, everything worked!
|
12
_posts/2022-05-04-avrisp-mkii.md
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
title: AVRISP MKII
|
||||
date: 2022-05-04
|
||||
categories: projects
|
||||
excerpt: I might be a couple years late to the party, but here's a tiny board powered by LUFA's AVRISP MKII project.'
|
||||
header:
|
||||
teaser: /assets/img/2022/avrispmkii.jpg
|
||||
---
|
||||
|
||||
<https://github.com/dragonlock2/kicadboards/tree/main/projects/arvispmkii>
|
||||
|
||||
After using an AVRISP MKII created from an Arduino Uno for years, I decided to finally figure out where that custom firmware came from. Turns out it came from [LUFA](https://github.com/abcminiuser/lufa/tree/master/Projects/AVRISP-MKII). With that, I designed a board around it. I definitely didn’t want to buy new parts for this, so I used what I had. While the MPLAB Snap is preferred due to its wide protocol support, I will continue using the AVRISP MKII since it’s supported in Arduino IDE. After modifying the LUFA project a bit to match the clock speed, GPIO, and chip, I got it working! All of my programmers are now tiny and it’s beautiful.
|
22
_posts/2022-05-04-stm32-daplink.md
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
title: STM32 DAPLink
|
||||
date: 2022-05-04
|
||||
categories: projects
|
||||
excerpt: After parsing through much documentation, I learned enough to design a DAPLink probe based around the ever-popular STM32F103.
|
||||
header:
|
||||
teaser: /assets/img/2022/daplink_1.jpg
|
||||
|
||||
gallery:
|
||||
- image_path: /assets/img/2022/daplink_1.jpg
|
||||
- image_path: /assets/img/2022/daplink_2.jpg
|
||||
---
|
||||
|
||||
<https://github.com/dragonlock2/kicadboards/tree/main/projects/NOGPROG>
|
||||
|
||||
Wanting an alternative to the Black Magic Probe, I took a look into DAPLink which is backed by Mbed. It uses CMSIS-DAP which is supported by OpenOCD which means it is supported by virtually every IDE and framework. As with the Black Magic Probe and many popular projects, the STM32F103 is supported. It took some time to parse through the code, documentation, and existing designs, but I got a schematic together.
|
||||
|
||||
I usually post images of my schematic and layout, but I’m making it a point to throw everything on Github so that everyone has full access to my designs. Check it out!
|
||||
|
||||
Assembly was straightforward as always. I didn’t have all the right components so I had to make do. The part that finally made DAPLink a viable option was the GCC support. It was in development while I did this project, but now appears to be mainlined. After compiling from main and uploading the bootloader and main program, everything worked! Here it is programming a little board I designed for UR@B training.
|
||||
|
||||
{% include gallery %}
|
28
_posts/2022-05-14-2022-courses.md
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
title: 2022 Courses
|
||||
date: 2022-05-04
|
||||
categories: school
|
||||
excerpt: Finally graduated! The end of one amazing chapter and the start of another.
|
||||
header:
|
||||
teaser: /assets/img/2022/tapeout.png
|
||||
---
|
||||
|
||||
This last semester was definitely a fight to complete despite taking only 3 classes. I pulled more all-nighters and EECS’d it more times than all of my past semesters combined. For all of my hard work, this was certainly one of my most interesting semesters. While I am sad to be graduating, especially after all of the time lost to COVID, it will be exciting to see what the next chapter of my life has in store. That next chapter will start in July where I’ll be a Tesla firmware engineer! I’ll be taking a well deserved vacation in the interlude.
|
||||
|
||||
#### CS152 – Computer Architecture and Engineering
|
||||
|
||||
While I didn’t go to a single lecture or discussion past the first few, this was an eye-opening class in learning how modern CPUs are designed and how they can be so performant. Topics ranging from out-of-order processors, register renaming, branch prediction, to cache coherence all ended up being conceptually pretty simple and not just magic. My favorite part was finally implementing a Spectre attack in simulation and having it work perfectly. The workload was quite light and relatively rewarding. Much like CS161, it’s a class that doesn’t consume much time and fits well into any schedule.
|
||||
|
||||
#### EE142 – Integrated Circuits for Communications
|
||||
|
||||
Much like its prerequisite EE105, this was easily one of the most difficult classes I’ve ever taken. The workload was manageable, but the material went over my head more times than not. Taking the class for a grade really did not help things. While the initial material on transmission lines, waves, noise, and distortion wasn’t terrible to pick up, once we got to amplifiers, mixers, and oscillators I was completely out of my element. I really should’ve taken EE140 prior. Still, I learned a good amount in this class. The labs were really fun and rewarding and the TAs were amazing. I can’t say much about the lectures and discussions since I didn’t go to a single one past the first few, but I’d wager they were solid too. Highly recommended if you’re into analog circuits but this was not the class for me.
|
||||
|
||||
#### EE194 – Advanced Topics in Circuit Design (22nm SoC for IoT)
|
||||
|
||||
Mostly due to its open-ended nature, this was one of the heaviest but most rewarding classes I’ve ever taken. Not for the faint of heart, this class really opened up my eyes to the wonders of chip design and just how much Berkeley has contributed to the field. The goal of the class is to take a team of ~20 undergraduate and graduate students and do a chip “tapeout”. Since our class had ~40 students, we split into two teams, one doing an ML accelerator chip and one doing a mixed signal chip. In what ended up being a perfect match, I was on the RF digital team for the mixed signal chip which was tasked with validating and updating the past BLE module and adding 802.15.4 support.
|
||||
|
||||
Since the previous team’s work was understandably rushed and poorly documented, I ended up teaching myself how the entire thing was designed and implemented. Since I hate MATLAB, I wrote Python simulations to design the DSP pathway and CDR and figure out just how low an SNR we could get away with. Transferring that design to RTL in Chisel was daunting at first but ended up being pretty straightforward. At the end of the day, I was quite proud of the work I did.
|
||||
|
||||
I had so much fun with Chisel that I decided to spend a couple of days not studying for the EE142 final and instead developing a workflow for deploying Chisel on FPGAs in MacOS. Since I’m graduating and won’t have VMware anymore, I figured out how to load Vivado in a Docker image and compile the Chisel-generated Verilog there. Check it out!
|
||||
|
||||
<https://github.com/dragonlock2/chivado>
|
40
_posts/2022-07-06-jabi-just-another-bridge-interface.md
Normal file
@ -0,0 +1,40 @@
|
||||
---
|
||||
title: JABI (Just Another Bridge Interface)
|
||||
date: 2022-07-06
|
||||
categories: projects
|
||||
excerpt: Easily access any microcontroller's peripherals from anywhere! Supports all major operating systems and libraries are available for C++, Python, and gRPC.
|
||||
header:
|
||||
teaser: /assets/img/2022/jabi.jpg
|
||||
---
|
||||
|
||||
<https://github.com/dragonlock2/JABI>
|
||||
|
||||
After developing [stbridge](https://matthewtran.dev/2021/01/stbridge/) to bind STLINK-V3-BRIDGE to Python for easy usage, I was not satisfied. The hardware and firmware of the STLINK-V3 were both closed-source which meant I couldn’t fix any bugs I found. Even the library code was under ST’s weird license. Furthermore, my retrospectively poor decision to use Boost Python to create the bindings was absolute hell to get compiling and the reason I chose not to port it to Windows.
|
||||
|
||||
The motivation for JABI came from wanting to build something like stbridge but fully open-source and far easier to build and use. While it wasn’t conceptually difficult, the sheer number of moving parts and things I had to learn meant the whole project took me over two weeks to complete. At the end of the day, I’m quite proud of what I built. It is easy to use and most of all maintainable.
|
||||
|
||||
## Hardware
|
||||
|
||||
With a global chip shortage, the last thing I wanted to do was tie JABI to a specific microcontroller as with STLINK-V3 or Bus Pirate. While with a fixed microcontroller I could tailor the performance and features more easily, in this case flexibility is far more important. The only hard requirement for a microcontroller is that it can talk to a host OS over one of the supported interfaces (ex. USB, UART). The rest of the hardware is really up to the designer.
|
||||
|
||||
## Firmware
|
||||
|
||||
The key reason this whole project was even possible was Zephyr RTOS. Unlike Mbed or Arduino, Zephyr does an amazing job at cleanly separating application code from hardware specific factors. The same application can be compiled for a different board by simply naming it. If the microcontroller is already supported, porting a board using it to Zephyr – and thus JABI – takes minutes. If it’s not, it’ll take longer but the framework is set up to make it relatively straightforward.
|
||||
|
||||
Since Zephyr doesn’t have an RPC implementation built-in – although it appears Thrift support is being worked on – I had to design my own. The only real consideration here is the request and response packet formats which contain things like a function ID, return code, and variable length payload. To reduce the memory footprint and remove the need for dynamic memory allocation, there is also a configurable maximum length to each packet.
|
||||
|
||||
With that, each supported interface just needs to be able to send and receive the RPC packets. UART was relatively straightforward since it uses a semi-reliable byte stream. All I had to really consider was adding a timeout to ensure a bad short packet arriving now wouldn’t affect a good one later. USB was harder with its dizzying array of descriptors, but I eventually got them set up and reliable in-order bulk transfers to work.
|
||||
|
||||
Peripherals were even easier to add support for since for the most part they just called the underlying Zephyr API. The hardest part was balancing flexibility and ease of use when figuring out what parts of the Zephyr API to expose and how.
|
||||
|
||||
In order to support multiple interfaces and thus clients running concurrently, locks are added around peripheral accesses and are shared in case the same underlying peripheral instance is used. To avoid an adversarial client being able to crash the microcontroller or even worse hack it, I made sure to do a religious amount of error checking at all levels.
|
||||
|
||||
## Software
|
||||
|
||||
The most annoying part of writing the software libraries was getting them to compile across macOS, Windows, and Linux. The differences between Clang, GCC, and MSVC popped up time and time again with warnings and lack of compiler support for some C++ features. Eventually, I ironed all of them out and now have a clean dependency setup and build process for each.
|
||||
|
||||
The first and most important library to write was the C++ one. Baffled by how annoying STLINK-V3-BRIDGE was to use, I strived to keep the interface clean at all levels. Adding it to any application involves a simple CMake add\_subdirectory() call. Opening a device for use involves just calling the appropriate function from the desired interface. Accessing a peripheral is a simple function call and often times takes a single line of code. Exceptions are thrown only in fatal errors (except for maybe an I2C scan) to keep function definitions simple. Using smart pointers and locks, a device can be shared among multiple threads in case that’s needed.
|
||||
|
||||
While I could’ve written the Python library in pure Python, I wanted to reuse my C++ code and learn pybind11. It ended up being surprisingly easy to setup and get running and I could get bindings up in minutes. Since I didn’t want to have to be in a certain directory or manually copy files to install it, I made the Python library locally installable and manageable using pip. It even works in a Python virtual environment!
|
||||
|
||||
To bridge JABI to a network using an industry standard RPC framework, I decided to also write a gRPC server binding the C++ library. I even wrote an example client which provides the same interface as the C++ library but only depends on the protobuf file. The gRPC server may one day pave the road to mobile apps or Web interfaces.
|
31
_posts/2022-12-10-zephyr-lin-driver.md
Normal file
@ -0,0 +1,31 @@
|
||||
---
|
||||
title: Zephyr LIN Driver
|
||||
date: 2022-12-10
|
||||
categories: projects
|
||||
excerpt: Inspired by the CAN API, I designed a LIN API for Zephyr along with one implementation built on top of the UART driver.
|
||||
header:
|
||||
teaser: /assets/img/2022/zephyr_lin.jpg
|
||||
---
|
||||
|
||||
<https://github.com/dragonlock2/zephyrboards/blob/main/include/zephyrboards/drivers/lin.h>
|
||||
<https://github.com/dragonlock2/zephyrboards/tree/main/drivers/lin>
|
||||
|
||||
After hearing about it for so long, I finally had an opportunity to work with LIN while working in industry. Designed as a low-cost alternative to CAN, it’s actually quite simple. Virtually any microcontroller with a UART can become a LIN node when coupled with a LIN transceiver. Given this, I decided to add LIN support into Zephyr.
|
||||
|
||||
## LIN protocol
|
||||
|
||||
At the physical layer, LIN is a bus-based architecture with all nodes sharing the same LIN line. The LIN transceiver translates UART signals to the voltage required by LIN with TX driving the bus as needed and RX acting as feedback. This is quite similar to what a CAN transceiver does. In fact, while impractical, one could in theory use the CAN physical layer for LIN with a transceiver swap.
|
||||
|
||||
At the link layer, LIN operates on a commander-responder architecture. The commander initiates a packet by sending a break, sync byte, and PID. Then it sends or waits for a payload and checksum response as needed. One interesting caveat I noticed was that LIN packets don’t contain their length. That means there’s a non-zero chance that a random long packet will get received as a shorter one simply because one of the data bytes was also a valid checksum. After playing with a PeakCAN device, I realized the solution was just to know how long the packets you want to receive are.
|
||||
|
||||
## Zephyr
|
||||
|
||||
Designing the LIN API for Zephyr was relatively straightforward with most of the methods mimicking the CAN API. Since it’s possible for some microcontrollers to have a LIN hardware block, it’s important that the LIN API support several possible underlying implementations.
|
||||
|
||||
With the LIN API done, I proceeded with an implementation using the Zephyr UART API. In order to be non-blocking, everything would have to be interrupt based. Couple that with supporting a packet timeout and making sure LIN API calls don’t affect in-flight packets and I had a big synchronization problem on my hands. I dealt with simultaneous LIN API and UART ISR calls by encapsulating all LIN operations in a lock. Since I can’t guarantee the priorities of the UART and timeout ISRs, I had to trace all code paths to make sure either one interrupting the other at any point wouldn’t cause any issues.
|
||||
|
||||
One interesting quirk of UART peripherals I found was that changing the baud rate meant that the next byte sent out would be delayed by at least one byte time. Since I had to change the baud rate in order to send a break, it meant that my LIN packets would just barely comply with the LIN spec.
|
||||
|
||||
A bug I found in both the STM32 and NXP UART drivers was that the error check function blindly clears errors bits that aren’t even set. Typically this would also clear a pending received byte. Thankfully, Zephyr’s build system makes it relatively easy to temporarily apply patches to fix this behavior.
|
||||
|
||||
At the end of the day, everything worked! It’s certainly not fully LIN spec compliant especially with the API the spec defines, but those layers should be buildable if it ever became necessary.
|
33
_posts/2022-12-11-100base-t1-converter.md
Normal file
@ -0,0 +1,33 @@
|
||||
---
|
||||
title: 100BASE-T1 Converter
|
||||
date: 2022-12-11
|
||||
categories: projects
|
||||
excerpt: By connecting the RMII interfaces of two PHYs, this board bidirectionally converts between 100BASE-T1 and 100BASE-TX packets.
|
||||
header:
|
||||
teaser: /assets/img/2022/dp83tc811r.jpg
|
||||
---
|
||||
|
||||
<https://github.com/dragonlock2/kicadboards/tree/main/breakouts/dp83tc811r>
|
||||
<https://github.com/dragonlock2/miscboards/tree/main/psoc_creator/100BASE-T1.cydsn>
|
||||
|
||||
One of the interesting Ethernet variants is 100BASE-T1 which is used in the automotive space and offers full-duplex communication over a single unshielded twisted pair. The compromise is a far shorter maximum length at 15m. Since I had some 100BASE-TX PHYs leftover and Digi-Key had some 100BASE-T1 ones in stock, I decided to build a converter between them.
|
||||
|
||||
## Design
|
||||
|
||||
The hardware design was relatively straightforward with most of it coming from reference designs. I couldn’t find any designs with an RMII to RMII connection and had to infer based on the KSZ8091 back-to-back PHY configuration and MII to MII designs. As expected, the solution was to connect CRS\_DV and TX\_EN as well as the TX and RX signals together. They must also share a reference clock, although one PHY can be used to provide a master 50MHz clock for the other to reduce part count. For maximum configurability, I also made sure that all the strapping options were available. I also added a microcontroller to configure the PHYs over the MDIO bus.
|
||||
|
||||
## Bring-up Hell
|
||||
|
||||
The board bring-up process was absolute hell. Since I don’t have any 100BASE-T1 devices, I connected two boards together to act as a 100BASE-TX passthrough. At first only the 100BASE-TX link up was working but no packets transferred and the 100BASE-T1 link wasn’t establishing. To help the debug process, I talked to the PHYs over a bit-banged MDIO bus (not the same as I2C!). I eventually learned that 100BASE-T1 (and the other automotive variants) define a commander and responder during the link up process after which full duplex communication is possible. Thankfully, that’s configurable over MDIO (as well as straps) and I finally got the 100BASE-T1 link working. Packets were finally transferred but with a 30-70% drop rate (the horror!).
|
||||
|
||||
The debug process from there was far from smooth. Thankfully, the KSZ8091 and DP83TC811R both have loopback options among all parts of the communication pathway. To reliably check packet drop rate, I sent ping requests and checked which ones made it (in both directions) using Wireshark. I started with an analog loopback on 100BASE-TX and checking the 100BASE-T1 signal quality to make sure my electrical layout side was good. I then started moving between enabling all the other loopbacks to see where the packets were being lost. After probing the RMII lines, I realized that CRS_DV was toggling at the end of a packet which was certainly not good if it’s piped into TX_EN. Thankfully both PHYs allowed switching to RMII Revision 1.0 to turn off that behavior. Still the problem persisted.
|
||||
|
||||
Next using local and remote RMII loopback I learned that the last two bits of each packet was randomly being dropped. Correlating this with the CRS_DV signal, the hold time slack on it was marginal. Part of this was due to REF_CLK being delayed over the PCB trace which I attempted to solve with a register – only documented in the KSZ8091 errata – that adjusted clock skew. This did not work. After trying all the other registers that adjusted signal timing and even adjusting the crystal load capacitance to shift the resonant point, I gave up and nearly considered cutting traces and adding wires to fix the timing.
|
||||
|
||||
Thankfully, I held off for just a bit longer before cutting traces. I first tried tying TX_EN high to avoid any timing issues which didn’t work. Then I noticed that at the beginning of each packet, the DP83TC811R would assert CRS_DV early by a seemingly random amount of time (since CRS and RX_DV are combined signals in RMII). Coupled with the fact that tying TX_EN high doesn’t work and that 100BASE-TX uses 4B5B encoding, I correctly assumed that the timing of the TX_EN signal matters. Since CRS was causing TX_EN to go high early, this resulted in the 50% packet loss I was seeing!
|
||||
|
||||
Fixing this issue meant figuring out how to separate the CRS and RX_DV in RMII’s CRS_DV signal. Thankfully the KSZ8091 has a documented register that does exactly this, called “copper repeater” mode. The DP83TC811R took some more digging with me finding a TI forum post that showed how to do it on the DP83TC812R by modifying an undocumented register. Seeing that and noting the similarities between the part, I made another forum post asking if a similar register existed on my part. They answered and yes, it did exist!
|
||||
|
||||
<https://e2e.ti.com/support/interface-group/interface/f/interface-forum/1160107/dp83tc811r-q1-does-it-support-rmii-repeater-mode>
|
||||
|
||||
With that publicly undocumented register modified, packets were passing through with no drops. While it was frustrating, I learned quite a lot about RMII at a low level, enough to one day write HDL to support it. The last thing was to switch between commander and responder mode randomly until the 100BASE-T1 link goes up. Since the microcontroller had no RNG nor ADC to seed a PRNG, I used the on-chip low speed RC oscillator which didn’t oscillate at an even multiple of the main clock as a makeshift RNG. With that, it was all done!
|
15
_posts/2022-12-11-attiny10-lin-node.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: ATtiny10 LIN Node
|
||||
date: 2022-12-11
|
||||
categories: projects
|
||||
excerpt: Since LIN transceivers are glorified level translators and LIN is slow enough to bit-bang, I built a LIN node using an ATtiny10.
|
||||
header:
|
||||
teaser: /assets/img/2022/attiny10_lin.jpg
|
||||
---
|
||||
|
||||
<https://github.com/dragonlock2/kicadboards/tree/main/tests/attiny10_lin>
|
||||
<https://github.com/dragonlock2/miscboards/tree/main/microchip_studio/attiny10_lin/attiny10_lin>
|
||||
|
||||
After building my first [LIN device](https://matthewtran.dev/2022/12/jabican-usb-pro/), I decided to see how cheaply I could make a responder node. In the automotive industry, LIN is frequently used to control buttons and lights. Thus I did just that with my board. It’s quite a simple design with the most interesting part being using a NMOS to act as a level translator between the LIN line and the ATtiny10’s IO. It doesn’t have the same slope control or wake features more expensive LIN transceivers have, but it works quite well.
|
||||
|
||||
The firmware was also pretty straightforward with the oscilloscope helping a lot with checking my timing. While a more complex solution would have used interrupts and timers, I wanted to move quickly and stuck with polling. In order to use only one IO for LIN, I had to switch between input and output quickly (AVR doesn’t have open-drain mode). There’s not much more to say honestly; it really was a straightforward implementation.
|
18
_posts/2022-12-11-jabican-usb-pro.md
Normal file
@ -0,0 +1,18 @@
|
||||
---
|
||||
title: JABICAN-USB Pro
|
||||
date: 2022-12-11
|
||||
categories: projects
|
||||
excerpt: Designed as an open-source alternative to the PCAN-USB Pro, JABICAN-USB Pro runs JABI to provide isolated CAN and LIN access over USB.
|
||||
header:
|
||||
teaser: /assets/img/2022/jabican_usb_pro.jpg
|
||||
---
|
||||
|
||||
<https://github.com/dragonlock2/kicadboards/tree/main/projects/JABICAN-USB%20Pro>
|
||||
|
||||
After using the PCAN-USB Pro FD at work, I decided to build an open-source competitor to it. The PCAN-USB Pro FD costs over $500 which is certainly inaccessible to most hobbyists. Drawing from the software stack I built with [JABI](https://matthewtran.dev/2022/07/jabi-just-another-bridge-interface/), I simply needed to design hardware to take advantage of it. It costs over $50 and lacks USB HS and CAN FD support, but that’s mainly due to me trying to only use parts I had on hand.
|
||||
|
||||
The hardware design was pretty straightforward with the new challenge being the isolation. I was finally able to use the isolated CAN transceiver I got samples of awhile ago. It includes an auxiliary IO pin which was used to implement the switchable 5V CAN rail the PCAN-USB Pro has. I wanted to one-up the PCAN-USB Pro and added an isolated 12V LIN rail. It also has a switchable 1kΩ pull-up on the LIN line for LIN commander mode.
|
||||
|
||||
The firmware was absolute hell to get running fully. The MK22FN1M0AVLH12 I chose was a more featured version of the one supported by Zephyr which meant a lot of debugging to figure out exactly where they were different. I nearly gave up while getting USB running after checking everything from the register map and clocks to probing the USB data lines. In my final try, I did some googling and found that the enabled-by-default MPU was the issue. Adding CAN support was more straightforward and involved copying feature definition code over, connecting interrupts correctly, and setting up clocks.
|
||||
|
||||
At the end of the day, once the low-level Zephyr board support was finished, getting JABI up and running was easy. I still have to add GUI support to JABI for it to truly compete with the PCAN-USB Pro, but the majority of the work is done.
|
31
_posts/2022-12-12-nrf24-remote.md
Normal file
@ -0,0 +1,31 @@
|
||||
---
|
||||
title: nRF24 Remote
|
||||
date: 2022-12-12
|
||||
categories: projects
|
||||
excerpt: Transmits button, microphone, accelerometer, and magnetometer data when awake and sips 2.5uA while asleep.
|
||||
header:
|
||||
teaser: /assets/img/2022/nrf24_remote.jpg
|
||||
---
|
||||
|
||||
<https://github.com/dragonlock2/kicadboards/tree/main/projects/nrf24_remote>
|
||||
<https://github.com/dragonlock2/miscboards/tree/main/microchip_studio/nrf24_remote/nrf24_remote>
|
||||
|
||||
(Please excuse the lack of PCBA picture my phone’s camera broke at the most inopportune time.)
|
||||
|
||||
Inspired by my housemate’s constant asking of me to do things for them, I decided to build an ultra low-power remote using parts I had on hand. To reduce the need to walk or strain one’s voice, it includes live microphone streaming. I am yet to be encouraged into making the speaker receiver, but the hard part is done.
|
||||
|
||||
## Design
|
||||
|
||||
This design marked quite a few firsts. First is the low quiescent draw which at 2.5uA meant a charged battery can last literally years not counting natural depletion. I achieved this by locking everything except the microcontroller, resistor divider, and battery charger behind voltage regulators that I could turn off. I could’ve probably added FETs to switch the resistor divider and battery charger, but the gains would’ve been marginal at best. Second is the electret microphone amplifier which I sized with empirical measurements. The main issue was the HPF so that the DC bias doesn’t get amplified which I initially sized with a 10uF cap. This added a 3 second wake time so I later changed it to 1uF with only marginal sacrifice in audio quality (for my voice at least). Third is my first 1.8V device (LSM303AH) along with the appropriate voltage shifters. Last but not least, this was my first RF design. Wanting to maximize success, I did use Nordic’s recommended circuit and TI’s 2.4GHz PCB antenna.
|
||||
|
||||
While the digital layout was quite simple, the RF layout was interesting to say the least since I’ve never done it before. While optimizing heavily for board size, I made sure to keep the RF and digital circuits separate. A ring of vias surrounds the RF section to shield it from the digital section as well as providing a solid continuous RF ground. For traces that require controlled impedance, I made sure to add many ground vias surrounding them for shielding as well as maintaining that impedance. At the end of the day, all of my traces were so short it probably didn’t matter too much. The main mistake I made was routing the RF power trace underneath the microphone amplifier which produced audible but weak noise at the transmit rate.
|
||||
|
||||
## Firmware
|
||||
|
||||
Getting the firmware working provided a slight challenge. Running on an ATtiny1616, I didn’t even consider encryption (although looking back it may be barely possible with the right sacrifices). Microchip Studio was the first of my worries with the MCC Classic code generator UI being difficult to work with and even having a bug in the generated code. Thankfully, I had enough knowledge of AVR to figure out the issue.
|
||||
|
||||
Next was transmitting the audio samples in real time since doing the SPI transfer for a packet wouldn’t fit in between two samples especially with packet overhead. I eventually settled on collecting 22 samples to be transmitted every 1ms in one larger packet. Since samples would be collected in an interrupt, I made sure to use two packet buffers to ping-pong between so that one packet would be written to while the other transmitted.
|
||||
|
||||
Last was the sleep mode where I had to figure out why the quiescent current was so high (~130uA). I desoldered just about every part until I narrowed it down to the resistor divider. Since the current passing through 2MΩ @ 4.2V is quite small, it had to be the microcontroller. As it turns out, holding a microcontroller’s IO mid-rail even when it’s an input increases quiescent draw significantly. Since I still wanted to monitor the battery voltage, I enabled the pull-up before sleep to hold the pin near the battery voltage and finally got the quiescent draw down to a sweet 2.5uA.
|
||||
|
||||
Overall, this project was a resounding success. Definitely a few things to improve on for future projects, but I’m quite proud of it.
|
27
_posts/2022-12-12-stm32-esc.md
Normal file
@ -0,0 +1,27 @@
|
||||
---
|
||||
title: STM32 ESC
|
||||
date: 2022-12-12
|
||||
categories: projects
|
||||
excerpt: Wanting to demystify how commodity ESCs work, I built a trapezoidal sensorless BLDC driver. It doesn''t have FOC (yet) but it does have LIN.
|
||||
header:
|
||||
teaser: /assets/img/2022/stm32_esc.jpg
|
||||
---
|
||||
|
||||
<https://github.com/dragonlock2/kicadboards/tree/main/projects/stm32_esc>
|
||||
<https://github.com/dragonlock2/zephyrboards/tree/main/samples/stm32_esc>
|
||||
|
||||
After using off-the-shelf ESCs for so long, I decided it was about time I learned the algorithm and hardware design behind driving them. While sensored control is quite easy, I stuck with a sensorless design for cost. While I eventually want to try sensorless FOC, I made sure to start with the far simpler and common sensorless trapezoidal control scheme.
|
||||
|
||||
The power part of the hardware was relatively straightforward with only three PWM-able half bridges needed. By using an ATA6844 which has shoot-through protection as the gate drivers, I ensured that burning my board would be difficult. The sensing part was where things got interesting and quite unclear. Drawing from several open-source designs, the first method was using 3 comparators to compare each motor terminal against the average of them all to find the BEMF zero-crossing. The second method was cheaper in terms of parts and involved measuring the motor terminal voltages directly and computing the zero-crossing in software. The last method was for FOC and added current sense to two half-bridges (I did three for simplicity). Wanting to maximize success, I did all three methods.
|
||||
|
||||
There’s not much to say about the layout other than the hell I went through keeping everything in two layers and all the parts on one side. The board is a bit large, but I got it.
|
||||
|
||||
The firmware was surprisingly straightforward and I even got it working in Zephyr. After fixing the UART driver to get LIN working, I brought up the gate drivers. I started with open-loop control and just sequenced through the six steps of trapezoidal control. For closed-loop control, I made the important note that the zero-crossing edge direction we’re looking for depends on the previous state of that pin (either + =>; HiZ => – or – => HiZ => +). Before getting to try it, my hard drive motor broke leaving me with only a high speed/power BLDC. With a resistance down to the 10s of mΩ, messing up would likely result in a burned FET. While initially hesitant, I eventually took a leap of faith and just tried it. It worked!
|
||||
|
||||
After working out some open-loop parameters to get the motor spinning fast enough for closed-loop to take over, I moved everything to be interrupt based. One important thing to note is that going to the next trapezoidal step has to happen at 30° from the zero-crossing for optimal switching. This was accomplishable with a Zephyr timer (after increasing the resolution). After thinking about synchronization issues, I settled on the motor control loop running constantly even at zero PWM and new PWM/direction requests coming over a message queue. By adding a timeout to waiting for a BEMF zero-crossing that automatically switched between steps, motor startup became straightforward. Closed-loop was defined as seeing all the BEMF zero-crossing interrupts for at least a few revolutions. While it may not be the best way, it certainly worked.
|
||||
|
||||
Last but not least was adding a LIN interface. Based on the cycle times, the interrupt rate is pretty manageable, triggering at worst every 100us (makes sense based on the motor parameters).
|
||||
|
||||
Overall, this project was a success. In the future I may look into FOC or even removing the RC filters on the BEMF detection (VESC does this by synchronizing the ADC and PWM). I could even drive the FETs more directly, but then I’d have to worry about shoot-through and PWM dead time.
|
||||
|
||||
{% include figure image_path="/assets/img/2022/stm32_esc_2.jpg" %}
|
BIN
assets/img/2022/6wire_1.jpg
Normal file
After Width: | Height: | Size: 182 KiB |
BIN
assets/img/2022/6wire_2.jpg
Normal file
After Width: | Height: | Size: 114 KiB |
BIN
assets/img/2022/attiny10_lin.jpg
Normal file
After Width: | Height: | Size: 99 KiB |
BIN
assets/img/2022/avrispmkii.jpg
Normal file
After Width: | Height: | Size: 309 KiB |
BIN
assets/img/2022/daplink_1.jpg
Normal file
After Width: | Height: | Size: 377 KiB |
BIN
assets/img/2022/daplink_2.jpg
Normal file
After Width: | Height: | Size: 128 KiB |
BIN
assets/img/2022/dp83tc811r.jpg
Normal file
After Width: | Height: | Size: 114 KiB |
BIN
assets/img/2022/jabi.jpg
Normal file
After Width: | Height: | Size: 187 KiB |
BIN
assets/img/2022/jabican_usb_pro.jpg
Normal file
After Width: | Height: | Size: 127 KiB |
BIN
assets/img/2022/mmv3_1.jpg
Normal file
After Width: | Height: | Size: 104 KiB |
BIN
assets/img/2022/mmv3_2.jpg
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
assets/img/2022/mmv3_3.jpg
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
assets/img/2022/mmv3_4.jpg
Normal file
After Width: | Height: | Size: 104 KiB |
BIN
assets/img/2022/mmv3_5.jpg
Normal file
After Width: | Height: | Size: 98 KiB |
BIN
assets/img/2022/mmv3_6.jpg
Normal file
After Width: | Height: | Size: 79 KiB |
BIN
assets/img/2022/mmv3_7.jpg
Normal file
After Width: | Height: | Size: 94 KiB |
BIN
assets/img/2022/mmv3_8.jpg
Normal file
After Width: | Height: | Size: 193 KiB |
BIN
assets/img/2022/mmv3_9.jpg
Normal file
After Width: | Height: | Size: 83 KiB |
BIN
assets/img/2022/nrf24_remote.jpg
Normal file
After Width: | Height: | Size: 107 KiB |
BIN
assets/img/2022/stm32_esc.jpg
Normal file
After Width: | Height: | Size: 113 KiB |
BIN
assets/img/2022/stm32_esc_2.jpg
Normal file
After Width: | Height: | Size: 125 KiB |
BIN
assets/img/2022/tapeout.png
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
assets/img/2022/zephyr_lin.jpg
Normal file
After Width: | Height: | Size: 167 KiB |