added posts up to 2021
55
_posts/2021-01-02-custom-black-magic-probe.md
Normal file
@ -0,0 +1,55 @@
|
||||
---
|
||||
title: Custom Black Magic Probe
|
||||
date: 2021-01-02
|
||||
categories: projects
|
||||
excerpt: The Black Magic Probe is a pretty interesting open source ARM debugger project, so I decided to modify the existing design and make one myself.
|
||||
header:
|
||||
teaser: /assets/img/2021/bmp_1.jpg
|
||||
|
||||
gallery:
|
||||
- image_path: /assets/img/2021/bmp_5.jpg
|
||||
- image_path: /assets/img/2021/bmp_6.jpg
|
||||
- image_path: /assets/img/2021/bmp_9.jpg
|
||||
- image_path: /assets/img/2021/bmp_10.jpg
|
||||
- image_path: /assets/img/2021/bmp_11.jpg
|
||||
- image_path: /assets/img/2021/bmp_12.jpg
|
||||
|
||||
gallery2:
|
||||
- image_path: /assets/img/2021/bmp_7.jpg
|
||||
- image_path: /assets/img/2021/bmp_8.jpg
|
||||
|
||||
gallery3:
|
||||
- image_path: /assets/img/2021/bmp_3.jpg
|
||||
- image_path: /assets/img/2021/bmp_4.jpg
|
||||
- image_path: /assets/img/2021/bmp_6.jpg
|
||||
- image_path: /assets/img/2021/bmp_1.jpg
|
||||
- image_path: /assets/img/2021/bmp_2.jpg
|
||||
---
|
||||
|
||||
Continuing on the train of learning about ARM debuggers, I decided to take a look at one that I heard about for a long time and supported all the chips that I was interested in experimenting with next. However, the $60 price tag was a bit out of my budget. Since it’s an open source project, I took a look at the [schematics](https://github.com/blacksphere/blackmagic/wiki/Debugger-Hardware) and upon realizing it used an STM32F103 which I had some of, I decided to make one myself.
|
||||
|
||||
### Design
|
||||
|
||||
In order to keep the design compact and manufacturable with the limited components I had on hand, I changed a couple of things on the schematic. The USB TVS diodes and voltage translators were removed. The original design used a pair of P-channel MOSFETs to control target power, but I switched it out for an LDO instead. I connected the UART pins to a TRRS connector and added a Tag-Connect footprint for programming.
|
||||
|
||||
{% include figure image_path="/assets/img/2021/bmp_schem.jpg" %}
|
||||
|
||||
The PCB layout was relatively straightforward, but I had to use a 6.3 mil trace/space to keep everything small.
|
||||
|
||||
{% include figure image_path="/assets/img/2021/bmp_layout.jpg" %}
|
||||
|
||||
### Manufacture
|
||||
|
||||
Manufacturing the PCB was the difficult part. To avoid drilling holes and cutting out the PCB manually, I brought out my CNC router to help me out. Since the PCB was now the exact correct size, I couldn’t tape on risers to etch both sides at once, so I did one at a time. Having one side face down also caused differences in etching rates so this improved overall quality. This was all back in April which was before I built my PCB agitator, so I’m surprised the traces actually came out mostly fine. Even the solder mask was decent considering I hadn’t developed my vastly improved technique yet.
|
||||
|
||||
{% include gallery %}
|
||||
|
||||
### Flashing
|
||||
|
||||
Flashing the BMP firmware onto the STM32 was tricky since I accidentally destroyed the programming pads while soldering. I had to solder some thin wires onto the LQFP package, but it worked out in the end.
|
||||
|
||||
{% include gallery id="gallery2" %}
|
||||
|
||||
As always, I 3D-printed a case to protect the fragile PCB. I tried out JTAG for the first time and even programmed an ATSAMD21 breakout I made.
|
||||
|
||||
{% include gallery id="gallery3" %}
|
103
_posts/2021-01-04-tinkering-with-qtouch.md
Normal file
@ -0,0 +1,103 @@
|
||||
---
|
||||
title: Tinkering With QTouch
|
||||
date: 2021-01-04
|
||||
categories: how-to
|
||||
excerpt: Just a short little tutorial about getting a basic QTouch example up and running.
|
||||
header:
|
||||
teaser: /assets/img/2021/qtouch_7.jpg
|
||||
|
||||
gallery:
|
||||
- image_path: /assets/img/2021/qtouch_4.jpg
|
||||
- image_path: /assets/img/2021/qtouch_5.jpg
|
||||
|
||||
gallery2:
|
||||
- image_path: /assets/img/2021/qtouch_7.jpg
|
||||
- image_path: /assets/img/2021/qtouch_8.jpg
|
||||
- image_path: /assets/img/2021/qtouch_9.jpg
|
||||
- image_path: /assets/img/2021/qtouch_10.jpg
|
||||
- image_path: /assets/img/2021/qtouch_11.jpg
|
||||
- image_path: /assets/img/2021/qtouch_12.jpg
|
||||
|
||||
gallery3:
|
||||
- image_path: /assets/img/2021/qtouch_13.jpg
|
||||
- image_path: /assets/img/2021/qtouch_14.jpg
|
||||
- image_path: /assets/img/2021/qtouch_15.jpg
|
||||
---
|
||||
|
||||
After seeing QTouch in the Atmel datasheets countless times, I decided to take a closer look at it. It’s a really nice and simple way of working with capacitive touch sensors. I’m no expert at it, but this should be enough to get a basic example up and running.
|
||||
|
||||
I’ll be using an [ATtiny817 Xplained Mini](https://www.microchip.com/developmenttools/ProductDetails/attiny817-xmini) which I got for free from Arrow a few years ago. It’s a nifty little chip and the board has two QTouch buttons on it. I’ll also be using [Microchip Studio](https://www.microchip.com/en-us/development-tools-tools-and-software/microchip-studio-for-avr-and-sam-devices) which I prefer over [MPLAB X IDE](https://www.microchip.com/en-us/development-tools-tools-and-software/mplab-x-ide) mostly because of my experience with Atmel Studio.
|
||||
|
||||
## Atmel START
|
||||
|
||||
Similarly to STM32CubeMX, Atmel START provides a really easy way to quickly configure all the peripherals you need and start developing your solution in no time. You can do it within any browser, but you can also navigate to “File > New > Atmel Start Project” to do it within Microchip Studio. Once your chip/board is selected, it should look like this.
|
||||
|
||||
{% include figure image_path="/assets/img/2021/qtouch_1.jpg" %}
|
||||
|
||||
The ATtiny817 has a default prescaler on its clock so I disabled it in CLKCTRL. Next add the “QTouch Library” under “Add software component”. Open up the new QTouch tab and add in the sensors you want. It’ll ask to change the clock settings which you should accept. From what I can understand, the RTC is setup to periodically trigger some processing.
|
||||
|
||||
{% include figure image_path="/assets/img/2021/qtouch_2.jpg" %}
|
||||
|
||||
Next select the appropriate pins for your sensors. Also disable data streaming in the “Debug” tab unless you want to use the data visualizer which you can learn more about [here](https://microchipdeveloper.com/touch:visualize-touch-debug-data-using-data-visualizer).
|
||||
|
||||
{% include figure image_path="/assets/img/2021/qtouch_3.jpg" %}
|
||||
|
||||
There’s a lot of other settings in the configurator to make things work better but I won’t be going over those.
|
||||
|
||||
Next let’s add the onboard LED and some UART debugging. printf support takes up more memory and processing but it is quite convenient for debugging.
|
||||
|
||||
{% include gallery %}
|
||||
|
||||
Finally click “Generate Project” to create your project.
|
||||
|
||||
## Microchip Studio
|
||||
|
||||
Just about all of the code you need to look at to figure out how to use QTouch is in `examples/src/touch_example.c`. In fact, it looks like example code for other peripherals is also stored in this folder.
|
||||
|
||||
The following code I wrote in main.c lights up the LED when both buttons are pressed. The green “Start Without Debugging” button is basically just like Arduino’s “Upload” button.
|
||||
|
||||
{% highlight C %}
|
||||
#include <atmel_start.h>
|
||||
#include <stdio.h>
|
||||
|
||||
extern volatile uint8_t measurement_done_touch;
|
||||
|
||||
int main(void) {
|
||||
atmel_start_init();
|
||||
cpu_irq_enable();
|
||||
|
||||
while (1) {
|
||||
touch_process();
|
||||
if (measurement_done_touch == 1) {
|
||||
measurement_done_touch = 0;
|
||||
led_set_level((get_sensor_state(0) & KEY_TOUCHED_MASK) && (get_sensor_state(1) & KEY_TOUCHED_MASK));
|
||||
printf("Measurement done!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
{% endhighlight %}
|
||||
|
||||
{% include figure image_path="/assets/img/2021/qtouch_6.jpg" %}
|
||||
|
||||
## Other Sensors
|
||||
|
||||
Here’s the other sensor types that I experimented with. Here’s an [application note](http://ww1.microchip.com/downloads/en/appnotes/atmel-42094-qtouch-schematic-and-layout-checklist_applicationnote_at02259.pdf) about QTouch implementation.
|
||||
|
||||
### Slider/Wheel
|
||||
|
||||
Getting a hatched ground plane and capacitive slider footprint working in KiCad was difficult, but I was able to get a demo running. Altium, which I just realized has student licenses, apparently has builtin support for what I was doing so I’ll check that out later. It took me longer than I’d like to admit to realize that using the half electrode setup from the application note is actually a wheel.
|
||||
|
||||
{% include gallery id="gallery2" %}
|
||||
|
||||
As usual, the functions of interest are in `touch_example.c`.
|
||||
|
||||
{% highlight C %}
|
||||
get_scroller_state()
|
||||
get_scroller_position()
|
||||
{% endhighlight %}
|
||||
|
||||
### 2D Surface
|
||||
|
||||
For some reason I didn’t get any pictures from when I made a super rudimentary 2D Surface sensor from tape and copper wire, so I made another. The ATtiny817 doesn’t support it, so I used the ATmega328PB board I made for [Mission Possible](https://matthewtran.dev/2018/05/mission-possible-2018/) a few years back. I can say that it definitely doesn’t not work.
|
||||
|
||||
{% include gallery id="gallery3" %}
|
81
_posts/2021-01-05-pcb-agitator.md
Normal file
@ -0,0 +1,81 @@
|
||||
---
|
||||
title: PCB Agitator
|
||||
date: 2021-01-05
|
||||
categories: projects
|
||||
excerpt: This glorified camera slider halves my etch times and improves etching consistency all in an overkill but stylish form factor.
|
||||
header:
|
||||
teaser: /assets/img/2021/agitator_19.jpg
|
||||
|
||||
gallery:
|
||||
- image_path: /assets/img/2021/agitator_12.jpg
|
||||
- image_path: /assets/img/2021/agitator_14.jpg
|
||||
- image_path: /assets/img/2021/agitator_15.jpg
|
||||
- image_path: /assets/img/2021/agitator_16.jpg
|
||||
|
||||
gallery2:
|
||||
- image_path: /assets/img/2021/agitator_7.jpg
|
||||
- image_path: /assets/img/2021/agitator_8.jpg
|
||||
- image_path: /assets/img/2021/agitator_9.jpg
|
||||
- image_path: /assets/img/2021/agitator_10.jpg
|
||||
- image_path: /assets/img/2021/agitator_11.jpg
|
||||
- image_path: /assets/img/2021/agitator_13.jpg
|
||||
- image_path: /assets/img/2021/agitator_17.jpg
|
||||
- image_path: /assets/img/2021/agitator_18.jpg
|
||||
|
||||
gallery3:
|
||||
- image_path: /assets/img/2021/agitator_4.jpg
|
||||
- image_path: /assets/img/2021/agitator_5.jpg
|
||||
- image_path: /assets/img/2021/agitator_6.jpg
|
||||
---
|
||||
|
||||
One of the issues I was running into when etching PCBs was uneven etch rates and long etch times. The solution is to keep the etchant moving across the board, commonly known as agitation. While I could have just taped a vibration motor to my etching container, I decided to make something a bit fancier.
|
||||
|
||||
## Design
|
||||
|
||||
{% include figure image_path="/assets/img/2021/agitator_1.jpg" %}
|
||||
|
||||
As with most projects, the direction of my design was heavily dictated by the parts I had lying around. I went for a highly symmetric and clean design. Mechanically, it’s basically a camera slider. I wanted to implement some sort of closed loop control, so I added homing Hall effect sensors and a magnetic encoder similar to what I used in the [PCB Laminator](https://matthewtran.dev/2019/06/pcb-laminator/). The UI was trickier to implement since I wanted to use as few buttons as possible. To maximize flexibility, I still wanted to be able to set a start point, end point, acceleration, and max velocity. Seeing as I already had an encoder on the sliding mechanism, that became the slider to pick my settings. Pair it with one button and we are good to go.
|
||||
|
||||
## Electrical
|
||||
|
||||
The schematic was pretty straightforward. This was the first time I used an ATtiny1616 which is now my designated microcontroller for low power use cases. I even added input voltage monitoring because I have destroyed several LiPo packs from accidental over-discharge.
|
||||
|
||||
{% include figure image_path="/assets/img/2021/agitator_3.jpg" %}
|
||||
|
||||
The PCB layout was also pretty straightforward. Keeping up with the whole symmetry theme, I made it as symmetric as possible.
|
||||
|
||||
{% include figure image_path="/assets/img/2021/agitator_2.jpg" %}
|
||||
|
||||
As usual, the PCB came out pretty good.
|
||||
|
||||
{% include gallery %}
|
||||
|
||||
## Assembly
|
||||
|
||||
After several hours of 3D printing, CNC routing, and sanding, my parts were all ready for assembly.
|
||||
|
||||
{% include gallery id="gallery2" %}
|
||||
|
||||
Wanting a more integrated solution than using silicone oven mats to prevent the etching container from sliding around, I decided to cover the platform in hot glue. I tried using a heated bed for a 3D printer to smooth it out, but without a release agent it just made a mess. The end result isn’t smooth, but it’s got texture.
|
||||
|
||||
{% include gallery id="gallery3" %}
|
||||
|
||||
## Software
|
||||
|
||||
I developed the code using Atmel START and Atmel Studio. Getting the basics up and running was pretty easy but the control loop was where things got interesting. I started with an open loop system and after several iterations I settled on an Arduino [AccelStepper](https://www.airspayce.com/mikem/arduino/AccelStepper/) inspired algorithm. I had to switch to full stepping because the computation was too heavy so there’s definitely some optimizations to be made there.
|
||||
|
||||
Adding in the closed loop took a few iterations to get the behavior I liked. The resolution of the encoder is about 1mm, so it’s really only good enough to counter stalling. At first I tried incorporating the encoder data and restart the open loop trajectory upon stalling, but it just didn’t feel right. Eventually I settled upon what I like to call clopen loop control. Basically the encoder data is used only after finishing a full trajectory. If it stalls in the middle, it doesn’t care. Couple that with traveling to the farther endpoint each time and I had something that just felt right. It even stops moving when it runs into the homing sensors which is a plus.
|
||||
|
||||
There were a couple of other interesting bits like a custom variable microsecond level delay made by counting cycles, state machine, analog comparator interrupt, and more. I don’t think it’s good enough to throw on Github, so the full code can be downloaded [here](/assets/zip/Agitator.zip) as a zip.
|
||||
|
||||
## Demo
|
||||
|
||||
I have to say it works pretty well. It’s pretty surprising how much of a difference agitation makes to etching PCBs.
|
||||
|
||||
{% include video id="oFWEokCxA_s" provider="youtube" %}
|
||||
|
||||
{% include video id="WKkd1ORuvCs" provider="youtube" %}
|
||||
|
||||
{% include video id="vjOV97aMJiw" provider="youtube" %}
|
||||
|
||||
{% include video id="6LXCrswkLD8" provider="youtube" %}
|
34
_posts/2021-01-07-simple-eeprom-wear-leveling.md
Normal file
@ -0,0 +1,34 @@
|
||||
---
|
||||
title: Simple EEPROM Wear Leveling
|
||||
date: 2021-01-07
|
||||
categories: projects how-to
|
||||
excerpt: A one bit overhead wear leveling algorithm for storing fixed size data in EEPROM. Based on Danny Chouinard's work.
|
||||
header:
|
||||
teaser: /assets/img/2021/wlprom.jpg
|
||||
---
|
||||
|
||||
In order to deal with the limited write cycles on EEPROM, wear leveling algorithms are commonly used. Generally speaking, the most common use case is storing fixed size data like user settings. After some searching, I came across a really clever algorithm that uses only one extra bit on top of the stored data. It’s a solid read I’d recommend checking it out.
|
||||
|
||||
- <https://sites.google.com/site/dannychouinard/Home/atmel-avr-stuff/eeprom-longevity>
|
||||
|
||||
## Algorithm
|
||||
|
||||
Conceptually, we start by splitting the memory into evenly sized chunks that’s the size of the data we’d like to store plus one bit. Our goal is to write to a different chunk each time to spread out the writes. The extra bit, which we call a sentinel bit, is used to mark where the last written data is. We’ll write the sentinel bits in such a way that it’s a sequence of 1s followed by 0s or 0s followed by 1s.
|
||||
|
||||
Every time we want to read our data, we search for the location just before our bit changes which gives us the last written data. If the bit doesn’t change, it means the last index in our array of chunks is the last written data. Rather than searching linearly and comparing adjacent indices, we can use binary search and compare against the first index for that sweet O(log n) runtime.
|
||||
|
||||
Every time we want to write data, we simply write to the location after the last written data, keeping the same sentinel bit as the last written data. If we rollover, we flip the sentinel bit. This is how we maintain that the sentinel bit change marks our most recent data.
|
||||
|
||||
## Implementation
|
||||
|
||||
To test out the algorithm, I wrote it in C++ on Mbed. It interacts with a separate EEPROM library that provides a simple random byte-level read/write interface. My implementation when writing doesn’t read in the last written data to set the correct sentinel bit but rather assumes the passed in data has the correct sentinel bit. This has a slight performance improvement at the cost of needing to be careful not to modify the sentinel bit and having to read it in the first time before writing.
|
||||
|
||||
{% gist d97fd3b2cd98ce1b930640b9e46618cf %}
|
||||
|
||||
The EEPROM I tested on is the [BRCF016GWZ](https://fscdn.rohm.com/en/products/databook/datasheet/ic/memory/eeprom/brcf016gwz-3-e.pdf). I pretty much just wanted to try soldering a CSP package. It can only write a certain number of bytes at a time, so I had to convert that interface to one that can write any number of bytes.
|
||||
|
||||
{% gist c75fe401a6d6384656cea15f4d8a7f7a %}
|
||||
|
||||
## Extras
|
||||
|
||||
As a simple demonstration of this algorithm, the code works really well, but there’s definitely some features that could be added. We could add a CRC or checksum to detect errors and potentially fix them or revert to the last good write. Due to the nature of the algorithm, we also keep track of the past couple writes. Support for multiple instances or being able to split the memory up to write more than one kind of data could also be added. Even accounting for the memory itself and using a more optimized write pattern could be looked into.
|
147
_posts/2021-01-11-stm32-aprs.md
Normal file
@ -0,0 +1,147 @@
|
||||
---
|
||||
title: STM32 APRS
|
||||
date: 2021-01-11
|
||||
categories: projects
|
||||
excerpt: A low cost compact device that provides APRS messaging capabilities to cheap HAM radios.
|
||||
header:
|
||||
teaser: /assets/img/2021/aprs_25.jpg
|
||||
|
||||
gallery:
|
||||
- image_path: /assets/img/2021/aprs_1.jpg
|
||||
- image_path: /assets/img/2021/aprs_2.jpg
|
||||
|
||||
gallery2:
|
||||
- image_path: /assets/img/2021/aprs_7.jpg
|
||||
- image_path: /assets/img/2021/aprs_8.jpg
|
||||
- image_path: /assets/img/2021/aprs_9.jpg
|
||||
|
||||
gallery3:
|
||||
- image_path: /assets/img/2021/aprs_23.jpg
|
||||
- image_path: /assets/img/2021/aprs_24.jpg
|
||||
|
||||
gallery4:
|
||||
- image_path: /assets/img/2021/aprs_10.jpg
|
||||
- image_path: /assets/img/2021/aprs_11.jpg
|
||||
- image_path: /assets/img/2021/aprs_14.jpg
|
||||
- image_path: /assets/img/2021/aprs_15.jpg
|
||||
- image_path: /assets/img/2021/aprs_16.jpg
|
||||
- image_path: /assets/img/2021/aprs_17.jpg
|
||||
- image_path: /assets/img/2021/aprs_12.jpg
|
||||
- image_path: /assets/img/2021/aprs_13.jpg
|
||||
|
||||
gallery5:
|
||||
- image_path: /assets/img/2021/aprs_19.jpg
|
||||
- image_path: /assets/img/2021/aprs_20.jpg
|
||||
- image_path: /assets/img/2021/aprs_18.jpg
|
||||
- image_path: /assets/img/2021/aprs_21.jpg
|
||||
---
|
||||
|
||||
Back in July after having just gotten into HAM radio after taking EE123, I decided to build an attachment for my Baofeng UV-5R to allow it to transmit and receive APRS messages, something that’s usually reserved for more expensive radios. All of the code and design files are located in <https://github.com/dragonlock2/STM32_APRS>.
|
||||
|
||||
## Algorithm
|
||||
|
||||
The part that I focused most heavily on was the modulation and demodulation algorithms for APRS packets. For fast development, I did everything in an iPython notebook first and slowly made adjustments to make it more C-like.
|
||||
|
||||
### Modulation
|
||||
|
||||
We can split up the modulation of APRS packets into several parts. Let’s start with the packet format. All the information needed to specify an APRS packet are a destination address, source address, digipeater addresses, and up to 256 bytes of information to send. Addresses are specified as a callsign and SSID, a number from 0-15. The destination address is used to identify APRS packets and software version/application. The source address is your callsign and an SSID you pick. The digipeater addresses, up to 8, specify how far your packet can get repeated by stations around you, with WIDE1-1, WIDE2-1 being common.
|
||||
|
||||
To generate the bytes that need to be sent, we start by specifying how addresses are encoded, which you can find more info about [here](http://www.ax25.net/AX25.2.2-Jul%2098-2.pdf). Basically we need 7 bytes for each, 6 of which are for our callsign (padded with ASCII spaces if needed) and 1 for the SSID which occupies the bottom 4 bits of 0b00110000. We left bit shift each byte by 1 before we add it to our bytes to be sent. We add in our destination, source, and digipeater addresses all in that order. The LSB of the last byte we just added is set to 1 (the rest are 0 because of the bit shift) to mark the end of the addresses field. Then we add the control field 0x03, protocol ID 0xF0, and information bytes. Then we add in the 2 byte FCS field which is a CRC computed over all the bytes we just added. Finally, we prepend and append a couple of flags 0x7E that mark the start and end of the entire packet. Extra preflags give extra time to open up a receiver’s squelch and sync up the sample phase.
|
||||
|
||||
To generate the bits that will be AFSK modulated, we start by bit stuffing. For each byte to be sent, we add to our bitstream in LSB order and every time we see 5 ones, we add a 0. We specifically don’t bit stuff the flags which allows us to identify flags as anytime we see 6 ones in a row. The resulting bitstream is then NRZI encoded which basically means ones are encoded as keeping the same bit level while zeros toggle the bit.
|
||||
|
||||
Finally, we can AFSK modulate our final bitstream using Bell 202 AFSK. Basically, 1s use a 1200Hz tone while 0s use a 2200Hz tone. We transmit at 1200 baud, which means 1s get a full cycle while 0s get ~1.8 cycles. If we simply transmit a precomputed waveform for each bit without considering the bit that came before, we end up with phase discontinuities which increase our bandwidth. Since a different frequency just changes how fast the phase changes, we can keep track of the phase to compute the audio waveform and ensure it stays continuous.
|
||||
|
||||
{% include gallery %}
|
||||
|
||||
For implementation on a microcontroller, I pretty much just rewrote everything to be line for line convertible to C. I tested frequently to make sure I didn’t mess anything up. For playback, I used a DMA transfer to the DAC peripheral at 38400Hz and a pingpong buffer. Since I already did most of the work and major optimizations in the iPython notebook, transferring to a microcontroller was very straightforward with the hardest part being learning how to use DMA for the first time.
|
||||
|
||||
### Demodulation
|
||||
|
||||
To decode an APRS packet from the raw audio, let’s start with how we did it in EE123. I can’t seem to find the diagram we used in class but Stanford has a very similar [one](https://web.stanford.edu/class/ee179/labs/MF_aprs.png). Basically we pass one copy of the signal through a 1200Hz bandpass filter and another through a 2200Hz bandpass filter. Then we take the absolute value of both and subtract one from the other. We then sample at the right times to get the raw bits and then decode the bytes from there which I’ll go over later. This method appears to be the one used by [Dire Wolf](https://github.com/wb2osz/direwolf/blob/master/src/demod_afsk.c).
|
||||
|
||||
Since I was implementing this on a microcontroller, I was worried there wouldn’t be enough processing power to do it in real time. I came across the [MicroAPRS](https://github.com/markqvist/MicroAPRS/blob/master/hardware/AFSK.c) project which runs on AVR processors and has a completely different DSP algorithm. I found this [document](https://www.ti.com/lit/an/spra347/spra347.pdf) from TI that does a good job of explaining how it works. Essentially we multiply by a time delayed version of the raw audio signal. Doing the math (left as an exercise for the reader) for both 1200Hz and 2200Hz, we get both a constant term and a high frequency term. A delay of 180° for 1200Hz, or 4 samples at 9600Hz sample rate, maximizes the difference between the constant terms. We can then apply a 1st order IIR low pass filter with a cutoff frequency of 1200Hz to get rid of the high frequency terms and keep our bits. I tried designing my own with SciPy and ended up with the exact same one as MicroAPRS’s. I also double checked it was stable. In practice, this algorithm works exceptionally well at a fraction of the computation, helping to decode as many packets as the bandpass method.
|
||||
|
||||
After this AFSK demodulation, we still need to choose where to sample at to get our raw bitstream. Optimally, we’d sample right in the middle of each bit period to maximize our chances of receiving the packet. However, our input signal will arrive with some phase shift which means we need some way to sync up with it. To do this, we use a PLL. There was a really nice diagram from EE123 which I can’t seem to find, so I’ll try to explain it in words. We start with a signed counter that increments and overflows at 1200Hz. We want to sample a bit at every overflow which means bit transitions happen at zero crossings for our counter. To accomplish this, we nudge the counter back to 0 every time there’s a bit transition in our signal. If we nudge by too much, our phase tracks minute jittering in the input signal. If we nudge by too little, our phase doesn’t track the input signal phase well. Multiplying by 0.5 works pretty well in practice.
|
||||
|
||||
After applying the PLL, we have a raw bitstream that’s still NRZI encoded and bit stuffed. We can do the NRZI decoding pretty easily by just checking if the sampled bit matches the previous sampled bit. The bit unstuffing and gathering of the APRS packet bytes is done using a state machine. The state machine starts by looking for the bit sequence 0b1111110 which denotes a start flag. Then we switch to a gathering state and store each bit, ignoring stuffed 0s, until we hit an end flag, where can finally decode the packet if the number of bits gathered is the right size. Decoding the packet involves first checking the FCS, control field, and protocol ID and then grabbing the appropriate fields.
|
||||
|
||||
{% include figure image_path="/assets/img/2021/aprs_3.jpg" %}
|
||||
|
||||
For implementation on a microcontroller, it was again pretty straightforward as I wrote everything to be as C-like as possible. My favorite bit was in the LSB implementation of the BitFIFO which means I could literally grab the underlying uint8_t array for packet decoding once the bits were gathered. As usual, I did frequent testing while doing the Python to C-like Python conversion. The data acquisition was done using DMA from the ADC peripheral at 9600Hz and a pingpong buffer. I was initially worried the computation would be too heavy to do in real time, but it ended up consuming only 2.5% of the CPU.
|
||||
|
||||
## Software
|
||||
|
||||
Due to the sheer number of unknowns and firsts that I would need to accomplish with this project, this was the first time I actually completed most of the software before even designing a PCB. I’ll go over the hardware decisions in the hardware section, but this is pretty much what my desk looked like for the better part of a week or two.
|
||||
|
||||
{% include figure image_path="/assets/img/2021/aprs_4.jpg" %}
|
||||
|
||||
Here’s all the different modules and layers of abstraction I developed to get the whole thing working. The running theme was to use DMA and perform computation in interrupts as much as possible so that all the main function had to do was call some high level setup functions and then do whatever it wanted from there.
|
||||
|
||||
### BitFIFO/IntFIFO
|
||||
|
||||
This was the first thing I worked on since it was fundamental for the APRS algorithm. It’s pretty simple just a ring buffer implementation of a queue which was one of the first things we learned in CS61B. I attempted to do some kind of inheritance from a common interface since I needed one FIFO for bits and one for ints, but C doesn’t make it quite as straightforward.
|
||||
|
||||
### APRS/AFSK
|
||||
|
||||
I already went over the bulk of the APRS algorithm above, so I’ll just go over some more details in the microcontroller implementation. The AFSK module handles modulating and demodulating an AFSK signal using DMA. The idea of the pingpong buffer is that we perform our DMA transfer on one half of the buffer while performing computation on the other half. ST makes this really easy with its half transfer complete callbacks and a cyclical DMA transfer. The APRS module then handles converting to and from the useful data contained in each packet.
|
||||
|
||||
### SSD1306
|
||||
|
||||
Starting from [Adafruit’s SSD1306](https://github.com/adafruit/Adafruit_SSD1306) library and the [datasheet](https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf), I proceeded to build the driver for STM32. I wasn’t satisfied with just copying Adafruit’s library and instead strove to understand what each line did. My library isn’t quite as fully featured as the Adafruit one, but I’d say it’s good enough for the majority of use cases. When getting the DMA setup, I wondered why an interrupt based transfer wasn’t the same thing. After digging through the code, the interrupt transfer actually calls a callback after every byte is transferred to start the next byte which means it isn’t a CPU-free transfer. I should’ve used a pingpong buffer, but in practice the effects of writing to the frame buffer as it’s being displayed are barely noticeable.
|
||||
|
||||
To maximize the amount text I could display, I looked for the smallest legible font I could find. I eventually came across a 3×5 one (4×6 with spacing) that worked. It’s interesting to note how the Adafruit library stores fonts as bytes and as such can directly write each byte to the frame buffer rather than pixel by pixel. The tradeoff is that fonts have to be a multiple of 8 pixels tall. Since I wanted the smallest font possible, I had to write my font pixel by pixel.
|
||||
|
||||
{% include figure image_path="/assets/img/2021/aprs_5.jpg" %}
|
||||
|
||||
### APRSGUI
|
||||
|
||||
Designing a fully featured UI for such a tiny display was quite the challenge. Safe to say I have a lot of respect for people who do this kind of thing. I ended up with something that could display the message to be transmitted as well as the 8 most recent messages. Since the entire message can’t fit on one line, I added text scrolling as well as blinking cursors to indicate there’s more info to see. Lot’s of tweaking and debugging later, I had something that I’m pretty proud of.
|
||||
|
||||
At 24 FPS, rendering the UI takes up a whopping 50% of the CPU. Since I was doing the rendering inside an interrupt, I made sure to set its priority lower than the one for the APRS algorithm.
|
||||
|
||||
{% include figure image_path="/assets/img/2021/aprs_6.jpg" %}
|
||||
|
||||
### Keyboard
|
||||
|
||||
Since I gave myself the deadline of finishing the project before school started in August and I was still busy with an internship, I couldn’t source the BlackBerry keyboard in time which was what I originally wanted to use. With only buttons in my parts stock and quite large ones at that (PTS 810s were too thick for what I wanted), I eventually settled on a T9 style keyboard from the flip phones of days past. To be able to enter any ASCII key, I used the same method as flip phones where pressing a key multiple times in succession allowed entering different characters. I added multiple “pages” to keep the maximum length of these sequences short. I also tried to keep the mappings as intuitive as possible, to the point where I can somewhat type messages despite blank keys.
|
||||
|
||||
Interfacing the keyboard module with APRSGUI was simple since I designed APRSGUI to take in ASCII characters as commands. I did have to modify APRSGUI to be able to display the character about to be entered for some user feedback.
|
||||
|
||||
## Hardware
|
||||
|
||||
With the algorithm complete, I could start designing the actual hardware. I chose to develop on the STM32L433CCU6 simply because I had some samples of it and was initially worried I didn’t have enough computational power. In retrospect, a Cortex M0 would probably suffice.
|
||||
|
||||
Since I worked on the software first, I had to breadboard everything. I had so many issues when transmitting since the USB debugger kept resetting from all the noise. Measuring on the oscilloscope, the Baofeng spits out quite a bit of noise over its audio lines when transmitting. This reminded me of one of the labs in EE123 where even on low power with an attenuator and shielding the Pi inside a metal box, the audio chip kept glitching out. In hindsight, it was probably the excessive noise coming in through the audio lines that caused this issue.
|
||||
|
||||
Before I was comfortable transmitting on the national APRS frequency of 144.390MHz, I started on an unused channel. Funnily enough, I accidentally transmitted on one of the weather channels which is illegal for HAMs. Thankfully the FCC didn’t come after me. This is what happens when you start studying for your HAM exam the day of and after almost pulling an all nighter. I passed, but I forgot things.
|
||||
|
||||
Since this was the first time I would be using the raw SSD1306 OLED module in a project, I built a little breakout board first. KiCad didn’t want to do the slot drill, so I had to write a little script to attempt it.
|
||||
|
||||
{% include gallery id="gallery2" %}
|
||||
|
||||
Once that worked, I was finally ready to design the final PCB. Most of it was pretty straightforward and just involved throwing a bunch of standard components on. In hindsight, the SSD1306 supporting components or layout need adjustment since I can’t hit 1MHz I2C on it. I also need to adjust the decoupling caps or add more filtering since enough noise still gets through to distort the DAC, which was a huge issue I had during development. I don’t have much experience with analog frontends, but what I designed works pretty well.
|
||||
|
||||
{% include figure image_path="/assets/img/2021/aprs_22.jpg" %}
|
||||
|
||||
The PCB layout was pretty straightforward as usual. This was the first time I routed a differential trace in KiCad. It doesn’t have the right differential impedance, but it works. In hindsight, although a 6 mil trace width on the power lines is sufficient, I should’ve made it thicker because I accidentally shorted the battery through a trace in testing and burned it.
|
||||
|
||||
{% include gallery id="gallery3" %}
|
||||
|
||||
OSH Park, my new go-to PCB manufacturer, did a great job with the PCB. They actually failed to manufacture the super thin and long slot for the SSD1306 the first time around, but they refunded me and got it the second time. Since school was about to start, I couldn’t wait for the second board run and actually took a Dremel to the defective board to cut the slot and was able to get that to work.
|
||||
|
||||
{% include gallery id="gallery4" %}
|
||||
|
||||
This is what happens when you buy the cheapest TRRS plug on Digikey.
|
||||
|
||||
{% include gallery id="gallery5" layout="half" %}
|
||||
|
||||
As usual, toss on a custom 3D printed case and half an hour of sanding and it looks great!
|
||||
|
||||
{% include figure image_path="/assets/img/2021/aprs_25.jpg" %}
|
||||
|
||||
## Demo
|
||||
|
||||
{% include video id="dPAJH9Xz7w0" provider="youtube" %}
|
||||
|
||||
Overall, this project turned out pretty good. There’s definitely a good number of improvements to be made, in particular on the audio frontend. I can’t seem to get transmissions between handsets to work probably because there’s just too much noise and distortion. The IGates in LA and Berkeley pick it up fine though. If I ever get around to another revision of this project I’ll make sure to iron those bugs out.
|
40
_posts/2021-01-12-2020-courses.md
Normal file
@ -0,0 +1,40 @@
|
||||
---
|
||||
title: 2020 Courses
|
||||
date: 2021-01-12
|
||||
categories: school
|
||||
excerpt: It's a bit much to make a post for every single class I take, so here's all the major courses I took during Spring 2020 and Fall 2020.
|
||||
header:
|
||||
teaser: /assets/img/2021/2020courses.jpg
|
||||
---
|
||||
|
||||
It’s a bit much to make a post for every single class I take, so here’s all the major courses I took during Spring 2020 and Fall 2020.
|
||||
|
||||
#### CS188 – Introduction to Artificial Intelligence
|
||||
|
||||
It doesn’t go too deep into all the math, so it’s a class I think just about anyone can take. True to its name, it does a really good job of introducing different methods of AI, ending off with machine learning. The projects are especially rewarding, take about 3 hours each solo, and are for the most part straight up implementing equations learned in class. However, if you do not like Pac-Man, I do not recommend the class as Pac-Man is basically our mascot.
|
||||
|
||||
#### EE105 – Microelectronic Devices and Circuits
|
||||
|
||||
This was definitely the more difficult of the classes I have taken at Berkeley simply because it caught me off guard with all of the semiconductor physics. After taking the class, I definitely have a better understanding of how transistors work and better circuit analysis techniques. If you’re going deeper into the circuit design, this is the class for you. I might be more of a CS person than I’d like to admit.
|
||||
|
||||
#### EE123 – Digital Signal Processing
|
||||
|
||||
Absolutely amazing class and one of my favorites. I still have a lot to learn, but it does a great job of introducing the different aspects of DSP and its many applications. The labs are also really fun and were what got me into HAM radio. The professor is also just so passionate about what he does and it really came off well and made the class that much more interesting. Simply put, I’d recommend this class.
|
||||
|
||||
#### CS162 – Operating Systems and System Programming
|
||||
|
||||
Operating systems have always been this mysterious thing to me but this class did an amazing job of demystifying it all and breaking down the various parts that make them up. Like I feel like I could actually become a kernel developer one day. I don’t really understand why so many people dislike the class but I’m also the type of guy that loves C so I might just be weird. It’s important to note that you’re required to form a ~4 person group to work on projects and that you basically need to design the entire project before you get to code. However, if you do the design well enough, there’s little need to debug your code. Literally this was the first time I wrote sizable amounts of code that worked on the first try. Except for the last project where early in the process I was like not putting parentheses around this define will bite me in the back. Sure enough, a 3 hour debug session later and that was the problem.
|
||||
|
||||
#### EE106A – Introduction to Robotics
|
||||
|
||||
Another great class and a solid intro to the world of robotics. There’s quite a bit of linear algebra but you really get to see how applicable it all is. Having a good concept of 3d space and orientation definitely doesn’t hurt with all of the transformations we do. It also introduced me to ROS which I finally understand the use of. The final project was a 4 person thing and I can safely say this was the first course project after CS170 that legitimately needed a group. We ended up doing a discretized multi-robot CTF game. Just like CS170, I handled the majority of the repo setup, architectural design, and test scripts to make sure we had a simple interface to design algorithms with.
|
||||
|
||||
- <https://github.com/dragonlock2/AntBots_vs_BeeBots>
|
||||
|
||||
#### EE149 – Introduction to Embedded Systems
|
||||
|
||||
If you didn’t like state machines before this class, you will. It’s a good way to get started with practical electronics implementations if you didn’t have the experience before. Even with my experience, I still had a couple gaps in knowledge to be filled in particularly with a couple of gotchas that are harder to learn on your own. The theory side of things was the place in particular where I learned quite a bit. The final project was a 2 person thing and I feel like we were the only group to do a simulation based project. I would’ve done a hardware project but coordinating that remotely during COVID is not easy. We ended up doing a multi-robot search and object retrieval project. I did the majority of the architectural setup and object estimation and retrieval algorithms.
|
||||
|
||||
#### SpaceX Summer 2020 Build Reliability Internship
|
||||
|
||||
This really isn’t a course but I don’t really know where else to put this so here it is. Also can’t really spill any details so here’s my three sentence review. Absolutely amazing experience I really learned quite a lot and honestly I changed quite a bit as a person. It was both a humbling experience in the sense that there’s just so much out there that I still need and want to learn and also a confidence boosting experience in the sense that I feel like I could actually make it as an engineer now. 10/10 would recommend.
|
16
_posts/2021-01-12-stbridge.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
title: stbridge
|
||||
date: 2021-01-12
|
||||
categories: projects
|
||||
excerpt: My first legitimately useful open source project, stbridge is a Python wrapper for the STLINK-V3's bridge API.
|
||||
header:
|
||||
teaser: /assets/img/2021/stbridge.jpg
|
||||
---
|
||||
|
||||
Still on my quest of learning about non-shady ARM debuggers, I learned about the STLINK-V3MODS. It’s even got I2c, SPI, CAN, and GPIO interfaces all for $9 so I was hooked. After spending more hours than I’d like to admit getting the code to compile, I had a basic example working in C++. After spending even more hours than I’d like to admit getting a Python extension to compile, I also had a basic example working in Python. I even made my first open source contribution (yay!) in the form of bug fixes to a project that converted the bridge API library to use libusb. This conversion was necessary to make stbridge cross-platform compatible. Anyway, I’m a huge fan of self-documenting code so there isn’t much more to say.
|
||||
|
||||
- <https://github.com/dragonlock2/stbridge>
|
||||
|
||||
As far as a PCB breakout is concerned, I designed a KiCad footprint which is in my [extraparts](https://github.com/dragonlock2/extraparts) library. I’ve designed two breakouts, one of which I call NOGLINK but that one’s a little more specialized. There’s this person on [Hackaday](https://hackaday.io/project/171319-hw-mods) who also made a breakout but since OSH Park charges by area I had to design my own much smaller version. If people want the files for it I can throw it up somewhere.
|
||||
|
||||
After doing some probing, I confirmed what the person on Hackaday said about pin 22 providing 5v. It appears that the 5v passed through a STMPS2151 and the main processor turns it on after some time. As such, it can source maybe 500mA. However, it’s important to note that this can literally change at any time since the pin is technically reserved.
|
94
_posts/2021-08-18-getting-started-with-petalinux.md
Normal file
@ -0,0 +1,94 @@
|
||||
---
|
||||
title: Getting Started With PetaLinux
|
||||
date: 2021-08-18
|
||||
categories: projects how-to
|
||||
excerpt: A rough guide to getting Linux booted on Zynq using Vivado and PetaLinux written by someone taking their first steps into that wonderful world.
|
||||
header:
|
||||
teaser: /assets/img/2021/plnx_eth.png
|
||||
---
|
||||
|
||||
After receiving the $20 EBAZ4205 in the mail, I decided to learn the basic process for getting a Linux image booted from the SD card. It runs a low-end Zynq SoC which is essentially an ARM microprocessor combined with an FPGA. While I am still a beginner in the world of embedded Linux and Zynq, I hope I can help others take their first steps.
|
||||
|
||||
## Vivado
|
||||
|
||||
Everything starts with a Vivado project where we’ll configure options for the PS (Processing System) side containing the ARM processor and the PL (Programmable Logic) side containing the FPGA. From there, we can export to PetaLinux for Linux development or Vitis IDE for bare metal development. Let’s get started putting the block design together.
|
||||
|
||||
### Minimum Requirements
|
||||
|
||||
After creating the project – make sure to select “Project is an extensible Vitis platform” for easier exporting later – and selecting the right Zynq part, we need to add the PS to the block diagram.
|
||||
|
||||
{% include figure image_path="/assets/img/2021/plnx_ps.png" %}
|
||||
|
||||
According to [UG1144](https://www.xilinx.com/support/documentation/sw_manuals/xilinx2021_1/ug1144-petalinux-tools-reference-guide.pdf), the bare minimum components needed to boot Linux on a Zynq-7000 are one TTC, >32 MB RAM, UART, and non-volatile memory (ex. QSPI, SD/MMC). The TTC can be enabled under “MIO Configuration > Application Processor Unit > Timer 0”. The RAM can be configured under “DDR Configuration > DDR Controller Configuration” where you can select among a bunch of known working parts and if not you can define your own. The UART and non-volatile memory can be enabled under “MIO Configuration > I/O Peripherals”. Much like a microcontroller, most of the peripherals can be remapped to other MIO. More interestingly, we can also remap peripherals to EMIO that can be mapped to pins controlled by the PL. It’s important to note that you can’t boot from a peripheral connected to EMIO since the PS side is what sets that up in the first place.
|
||||
|
||||
{% include figure image_path="/assets/img/2021/plnx_green.png" %}
|
||||
|
||||
There’s a couple more things to set up. The input frequency can be configured under “Clock Configuration” but the default ended up being correct for me. You might also need to set the correct voltage standard for IO banks under “MIO Configuration”.
|
||||
|
||||
A “Processor System Reset” block is also required along with referencing it in the “Platform Setup > Clock” tab. A master AXI port also needs to be selected under “Platform Setup > AXI Port” along with its clock connected. An AXI device needs to be connected or else we get a “failed to find amba_pl node” error later due to some device tree errors, so I added an “AXI GPIO” block and ran connection automation to hook it up. Thankfully, Vivado’s “Run Connection Automation…” is pretty good and will connect most things correctly.
|
||||
|
||||
{% include figure image_path="/assets/img/2021/plnx_min.png" %}
|
||||
|
||||
### EBAZ4205 Ethernet
|
||||
|
||||
The EBAZ4205 maps all of the MII pins through EMIO so we’ll need to do that next. We start by enabling ENET 0 and the associated MDIO under “MIO Configuration > I/O Peripherals” and change its IO to EMIO. Since the EBAZ4205’s PHY is only 100 Mbps, we’ll need to configure that under “Clock Configuration > IO Peripheral Clocks”. Then we need to make the GMII and MDIO pins external in the block design. Since we’re using MII and not GMII, we don’t actually need all the pins in the GMII harness, but I was lazy and made it all external.
|
||||
|
||||
The EBAZ4205 I received didn’t have the PHY crystal soldered and instead sourced it from one of the PS PLLs. The PHY it uses requires a 25MHz clock, so we can configure that under “Clock Configuration > PL Fabric Clocks” and then make the corresponding pin external.
|
||||
|
||||
{% include figure image_path="/assets/img/2021/plnx_eth.png" %}
|
||||
|
||||
Finally, we can map all the EMIO to the correct pins in an XDC file. For some reason, Vivado doesn’t allow you to leave pins as a no connect so we have to map all those unused GMII pins to somewhere. One interesting gotcha I ran into was that initially all of my received Ethernet packets came in as error because I didn’t have a pulldown on the RX_ER signal.
|
||||
|
||||
[Constraints XDC File](/assets/txt/ps.txt)
|
||||
|
||||
### Export to PetaLinux
|
||||
|
||||
Finally, we can save our block design and run “Create HDL Wrapper...” to generate the top level HDL file. From there we can run “Generate Bitstream...” to generate the bitstream that we’ll load at boot. Finally, we can run “File > Export > Export Platform...” (make sure to include bitstream) and generate the XSA file we can import into PetaLinux.
|
||||
|
||||
## PetaLinux
|
||||
|
||||
Most places tell you to install PetaLinux using the normal installer, but I just used Xilinx’s Unified Installer which has the added benefit of being easily managed along with my Vivado installation. Don’t forget to source the settings64.sh file before starting to call your PetaLinux commands (I added it to my .bashrc). Let’s start by making a PetaLinux project.
|
||||
|
||||
{% highlight bash %}
|
||||
petalinux-create --type project --template zynq --name <proj folder name>
|
||||
petalinux-config --get-hw-description <path to XSA>
|
||||
{% endhighlight %}
|
||||
|
||||
Then we can configure things like kernel options, U-Boot options, Yocto recipes, etc.
|
||||
|
||||
{% highlight bash %}
|
||||
petalinux-config # PetaLinux settings
|
||||
petalinux-config -c kernel # kernel options
|
||||
petalinux-config -c rootfs # packages
|
||||
petalinux-config -c uboot # U-Boot options
|
||||
petalinux-config -c busybox # BusyBox options
|
||||
# there's probably more these are the ones I found
|
||||
{% endhighlight %}
|
||||
|
||||
The options that I changed from the default were using an EXT4 root filesystem and adding SD boot option to U-Boot.
|
||||
|
||||
Finally we can compile our image!
|
||||
|
||||
{% highlight bash %}
|
||||
petalinux-build
|
||||
{% endhighlight %}
|
||||
|
||||
Then we need to package a boot.bin which is what the Zynq boot ROM searches for and initially loads from the SD card (boot strap pins can change the default boot source). boot.bin contains the FSBL which loads the optional bitstream and U-Boot SPL which finally boots Linux.
|
||||
|
||||
{% highlight bash %}
|
||||
petalinux-package --boot --fsbl ./images/linux/zynq_fsbl.elf --fpga ./images/linux/system.bit --u-boot --force
|
||||
{% endhighlight %}
|
||||
|
||||
Next we need to create a FAT boot partition and EXT4 partition for our filesystem on our SD card. I use GParted. Copy over the BOOT.BIN, boot.scr, and uImage (image.ub doesn’t work for me for some reason) from the images/linux folder into the boot partition. Extract the the root filesystem over to the EXT4 partition.
|
||||
|
||||
{% highlight bash %}
|
||||
sudo tar xvf ./images/linux/rootfs.tar.gz -C <path to mounted ext4 partition>
|
||||
{% endhighlight %}
|
||||
|
||||
Finally we can load the SD card and test everything!
|
||||
|
||||
{% include figure image_path="/assets/img/2021/plnx_boot.png" %}
|
||||
|
||||
## Extra
|
||||
|
||||
For fun, let’s reuse the NAND flash onboard as UBIFS storage by following <https://www.xilinx.com/support/answers/69765.html>. First we need to enable the NAND peripheral in Vivado. Then we can follow the instructions in the link to enable kernel support and install UBIFS utils. Rebuild everything and install on the SD card. Finally, there’s a couple of commands to format the flash as UBIFS and make a new volume.
|
32
_posts/2021-08-19-ftdi-xilinx-jtag-programmer.md
Normal file
@ -0,0 +1,32 @@
|
||||
---
|
||||
title: FTDI Xilinx JTAG Programmer
|
||||
date: 2021-08-19
|
||||
categories: projects
|
||||
excerpt: Thanks to some netizens dumping FTDI EEPROMs, I was able to hack together my own Xilinx programming cables.
|
||||
header:
|
||||
teaser: /assets/img/2021/ft232h_real.jpg
|
||||
---
|
||||
|
||||
Literally the day before starting my summer internship, I decided to teach myself how to use Altium Designer. I decided to try something relatively simple and use up the FT232H chips that I bought almost a year ago. The FT232H, FT2232H, and FT4232H make up an interesting family of chips that appears to have a partial monopoly in the FPGA dev board world due to their MPSSE which can emulate a variety of interfaces. For my first board designed with a mouse in the past 3 years, the board came out pretty good.
|
||||
|
||||
## Schematic
|
||||
|
||||
The schematic is pretty simple, with the FT232H not requiring too many external components and the datasheet even providing example circuits. As is the theme with most of the boards I design at home, I ignored standard things like ferrite beads, TVS, and fuses due to board size and not having parts. In addition to standard decoupling, the FT232H needs a “precision” 12K resistor (I just put a 10K and 1Ks in series), a 12MHz crystal, and EEPROM.
|
||||
|
||||
{% include figure image_path="/assets/img/2021/ft232h_schematic.png" %}
|
||||
|
||||
## Layout
|
||||
|
||||
Altium’s layout software is pretty awesome. I really like the quick switching between 3D views which makes it really easy to find and correct things like silkscreen overlap. The live DRC pointing out part collisions is also nice. It can be a bit annoying to set up design rules and having to press two keys for everything instead of one for KiCad but overall, I like it. It didn’t take long before I had a completed layout.
|
||||
|
||||
{% include figure image_path="/assets/img/2021/ft232h_layout.png" %}
|
||||
|
||||
## Flashing EEPROMs
|
||||
|
||||
The secret sauce that turns an FTDI chip into a Xilinx programmer is just special values stored in its EEPROM which alerts Xilinx/Digilent drivers to configure it as such. While I would certainly recommend against recreating my project in a commercial capacity without legal agreements with Xilinx or Digilent, for the struggling college student or someone who opposes the partial monopoly on a moral basis, it just might be worth it.
|
||||
|
||||
I have to give credit to this [gist](https://gist.github.com/rikka0w0/24b58b54473227502fa0334bbe75c3c1) which was the only reason this project was possible (especially since I didn’t own any official programmers myself). I’ve compiled all of the EEPROM dumps along with some of my own in a central repo just in case the original gist disappears.
|
||||
|
||||
<https://github.com/dragonlock2/ftdi_dumps>
|
||||
|
||||
{% include figure image_path="/assets/img/2021/ft232h_real.jpg" %}
|
49
_posts/2021-08-19-k66f-breakout.md
Normal file
@ -0,0 +1,49 @@
|
||||
---
|
||||
title: K66F Breakout
|
||||
date: 2021-08-19
|
||||
categories: projects
|
||||
excerpt: This board marks quite a few firsts including using a BGA part, solder stencil, ethernet PHY, custom DAPLink, and Zephyr RTOS.
|
||||
header:
|
||||
teaser: /assets/img/2021/k66f_all.jpg
|
||||
|
||||
gallery:
|
||||
- image_path: /assets/img/2021/k66f_stencil.jpg
|
||||
- image_path: /assets/img/2021/k66f_stencil2.jpg
|
||||
- image_path: /assets/img/2021/k66f_test.jpg
|
||||
---
|
||||
|
||||
Having gotten my first taste of Altium, I decided to try my hand at something more complex. Digging through my box of samples, I came across the MK66FN2M0VMD18 which is in the same family of chip used in the FRDM-K66F and Teensy 3.6. Since this was my first time using Ethernet in a design, it was very helpful to be able use the FRDM-K66F schematics for reference and rely on Mbed for software support. At the end of the day, this board took a week to design from ideation to PCB order – a stark departure from my usual 4 hour process – but I ended up with something I’m quite proud of.
|
||||
|
||||
## Schematic
|
||||
|
||||
One of the things that I really like about Altium is just how well it does things like hierarchical designs and harnesses. With schematics no longer fitting on one page, I’ll link the PDF and show a picture of the top level sheet. The top level sheet basically shows the major components of the board in a way that encourages reuse.
|
||||
|
||||
[K66F_Breakout.pdf](/assets/pdf/K66F_Breakout.pdf)
|
||||
|
||||
{% include figure image_path="/assets/img/2021/k66f_top.png" %}
|
||||
|
||||
As for the details of the individual sheets, the design was driven by the goal of buying as few parts as possible and using things from my personal stock instead. Getting something working at home was more important than making it work in all environments which as usual meant removing things that weren’t absolutely necessary.
|
||||
|
||||
For the PHY, I actually bought a KSZ8091 instead of the KSZ8081 but since it has more features and appear to be in the same family, I correctly guessed I could just drop it in. Chances are any PHY would work depending on how the Mbed driver works, but better safe than sorry. The RMII signals usually need series termination to remove reflections, but given my traces were so short I omitted them.
|
||||
|
||||
## Layout
|
||||
|
||||
The layout was really quite the challenge, taking me the majority of my design time. This board marks my most expensive OSH Park order, but I’m pretty happy with how small I was able to squeeze it. BGAs are an interesting part to route and I can definitely see where some people are coming from when they say it’s easier than a QFN. With the traces so short, I didn’t do any kind of length matching or impedance control on the RMII lines. I did try playing with Altium’s differential routing for the USB and Ethernet lines.
|
||||
|
||||
{% include figure image_path="/assets/img/2021/k66f_layout.jpg" %}
|
||||
|
||||
## Assembly
|
||||
|
||||
As my first time using a solder stencil, I have to say it is well worth the money. Instead of spending over an hour squeezing paste, I can do the whole thing in a swipe. It took a few tries, but I got a working technique down. To avoid the stencil shifting and merging pads, my technique is to initially apply a mostly vertical force when moving the paste over the stencil. Then I alternate between that and a mostly horizontal force to squeeze the paste into all the crevices.
|
||||
|
||||
{% include gallery %}
|
||||
|
||||
## Zephyr RTOS
|
||||
|
||||
After testing the board using Mbed and confirming everything worked, I tried porting it over to Zephyr which I just learned about. Having used Mbed for a few years, I feel that it’s best suited for quickly bringing boards up with the same chip as a supported target but quickly gets messy when you try to push beyond that. My initial impressions of Zephyr are that it trades off higher initial overhead for a very Linux-y feel to everything which makes organizing and modifying things much easier. Adding new boards and managing multiple version controlled projects feel like something that was actively thought about.
|
||||
|
||||
Of course, Zephyr is not without its little quirks. While porting this board over, I learned that due to how the clocks are setup, I couldn’t make USB work without lowering the clock speed or modifying that setup. It looks like an NXP specific thing, but when defining pins I have to do it both in both the pinmux code and the DTS which seems redundant. In addition, like with Mbed, there isn’t a generic PHY framework but instead each vendor’s driver looks specific to the PHY they use in their dev boards. Likewise, it’s pretty clear looking at the DTS files which boards came first and which had support added later. Still, I really like Zephyr.
|
||||
|
||||
Anyway, it ended up working! Here’s the repo where I’ll be putting all of the boards I design and add Zephyr support for.
|
||||
|
||||
<https://github.com/dragonlock2/zephyrboards>
|
45
_posts/2021-08-19-spartan-7-breakout.md
Normal file
@ -0,0 +1,45 @@
|
||||
---
|
||||
title: Spartan 7 Breakout
|
||||
date: 2021-08-19
|
||||
categories: projects
|
||||
excerpt: During the great chip shortage of 2021, I decided it was about time I designed something with an FPGA on it.
|
||||
header:
|
||||
teaser: /assets/img/2021/spartan7_3.jpg
|
||||
|
||||
gallery:
|
||||
- image_path: /assets/img/2021/spartan7_1.jpg
|
||||
- image_path: /assets/img/2021/spartan7_2.jpg
|
||||
- image_path: /assets/img/2021/spartan7_4.jpg
|
||||
---
|
||||
|
||||
After seeing so many SpaceX designs with Xilinx parts and taking EE151, I decided to try my hand at building a breakout for one myself. I also wanted to play around with LVDS which most dev boards don’t break out. At the end of they day, the process felt very similar to what I usually do with microcontrollers except having to go through a bunch more documentation.
|
||||
|
||||
## Schematic
|
||||
|
||||
Given the whole chip shortage, the hardest part of this project was just finding parts that were in stock and were cheap enough that I could absorb development costs. I wanted to use a Spartan 7 which hits that balance of price and performance and bought an XC7S15-1CSGA225I. This is the first time I’ve purchased parts before even starting the schematic because my other choices went out of stock. For the programmer, the FT2232H everyone uses is out of stock so I used an FT4232H which strangely has a lot of stock. I guess companies tend not to need the 4 MPSSEs the FT4232H offers. I know I didn’t. Anyway, here’s the schematic PDF.
|
||||
|
||||
[Spartan7_Breakout.pdf](/assets/pdf/Spartan7_Breakout.pdf)
|
||||
|
||||
{% include figure image_path="/assets/img/2021/spartan7_top.png" %}
|
||||
|
||||
Much like the [K66F Breakout](https://matthewtran.dev/2021/08/k66f-breakout/), I tried to keep sheets organized for design reuse. For the connectors, I used 22 pin FFC for LVDS so I can use cables from Amazon and a JTAG header in case I want to experiment with programming Lattice chips. This is also the first time I’ve intentionally added TVS to a design.
|
||||
|
||||
7-series chips need power sequencing and after spending an hour looking for the smallest and cheapest solution, I settled on just sequencing the cheapest buck regulator on Digikey with power goods and enables. The datasheet for the regulator calculated the wrong recommended resistor values so this is the first time I’ve directly disobeyed a datasheet. Technically I didn’t obey the power off sequence requirements due to size but it hasn’t caused me issues yet. If I were doing this properly I’d probably invest time into learning how to use PMICs or trying out a power sequencer chip with a system monitor to kill things quickly on sudden power disconnect. For a future design I guess.
|
||||
|
||||
Hooking up the FPGA was relatively straightforward it was really just loads of documentation. Just like microcontrollers, you just need to hook up power, clocks, config pins (and flash in this case), JTAG, and any peripherals you want. I really like Xilinx’s pinout files since I can basically copy paste them into Altium to make a symbol. I didn’t follow decoupling suggestions to the letter due to size (100uF caps are huge) but it worked out. Sadly, the FT4232H doesn’t offer a clock output like the FT2232H does so this is the first time I added a MEMS oscillator to a design.
|
||||
|
||||
## Layout
|
||||
|
||||
I can safely say that layout was quite difficult. Due to size, I moved to 0402 and 0201 parts for everything. While I hate soldering QFPs, I have to say being able to route things underneath it makes life a lot easier. Getting the 0.8mm BGA part routed was a bit hard because I obeyed OSH Park’s 5mil trace/space rule which prevented routing traces between vias. For my next design I’ll definitely move to 4mil or 4.5mil traces which according to other peoples’ experiences is reliable. Overall, I’m pretty happy with how the layout turned out and how I fit it on a 4 layer board while maintaining size.
|
||||
|
||||
{% include figure image_path="/assets/img/2021/spartan7_layout.jpg" %}
|
||||
|
||||
## Assembly
|
||||
|
||||
Stencils really are quite awesome. This is the first time I’ve soldered a QFP chip without any pins bridging. I did have a bunch of tombstoning on one of the boards but a little bit of hot air got that fixed. My first board initially didn’t work and man was I stressed until I realized the decoupling cap on the input power didn’t reflow properly. Considering there were other decoupling caps nearby, I’m surprised by how much it mattered.
|
||||
|
||||
{% include gallery %}
|
||||
|
||||
Everything ended up working! Well I haven’t tested the LVDS nor the non-UART FTDI pins yet but I’ll get there when I do something with it. For the 5th board I’ve designed in the past 3 years with a mouse, I have to say I’m pretty proud of it.
|
||||
|
||||
{% include figure image_path="/assets/img/2021/spartan7_overall.jpg" %}
|
52
_posts/2021-12-20-2021-courses.md
Normal file
@ -0,0 +1,52 @@
|
||||
---
|
||||
title: 2021 Courses
|
||||
date: 2021-12-20
|
||||
categories: school
|
||||
excerpt: Another year, another bunch of courses.
|
||||
header:
|
||||
teaser: /assets/img/2021/silicon.jpg
|
||||
---
|
||||
|
||||
Another year, another set of classes under my belt. I had quite a bit of fun and explored just a little further into the EECS spectrum. I’m finally graduating next semester which is perfect timing since the wear and tear of 4 years at Berkeley is really hitting me. I’ve come a long way from the freshman that went to every lecture and did every practice midterm to the senior who skipped half the semester and studies only hours before an exam.
|
||||
|
||||
#### CS161 – Computer Security
|
||||
|
||||
Highly recommended class. It doesn’t go over the mathematical details of cryptographic algorithms but instead is about how to use existing algorithms to implement secure systems. The workload is quite light except for one large design project. I loved learning about the nuances between algorithms and various real world examples of security vulnerabilities. For those interested in blockchain and cryptocurrency, it does touch on the fundamental building blocks along with its current issues.
|
||||
|
||||
#### CS189 – Introduction to Machine Learning
|
||||
|
||||
For all that it can do, machine learning really just boils down to a bunch of math and data analysis. That is not to say that it is simple. When it comes to ML engineering, choosing algorithms and tuning hyperparameters to maximize performance can be quite difficult. This course offers a great intro into common ML algorithms and their applications. We derive most of the math ourselves and implement them in projects which offers great insight into what used to be a black box. There’s even class competitions where we compete for the highest test accuracy. The workload can be heavy, but it is worthwhile.
|
||||
|
||||
#### EE106B – Robotic Manipulation and Interaction
|
||||
|
||||
Honestly, the EE106A/B courses really made me question my passion for robotics. It took a good amount of reflection to realize that robotics isn’t just what these courses focus on and that the part I care about (hardware and low-level firmware) is an equally valid and important part. EE106B covers more of the high level algorithms and mathematics behind robotic systems. Certainly good to know, but not my cup of tea. To be blunt, with the remote setting, lack of physical labs, and poor organization, my experience wasn’t positive. The professors and TAs were awesome so I wouldn’t be surprised if it’s gotten better.
|
||||
|
||||
#### EE151 – Introduction to Digital Design and Integrated Circuits
|
||||
|
||||
Easily one of the best courses I’ve taken. I really wish I’d taken this course earlier but sadly it never fit into my schedule until now. I finally got to learn Verilog, work with FPGAs, and just figure out what digital design really is. In addition, I also learned about the physical limitations of chip design and how to balance things such as power and performance. The workload is pretty light for most of the semester except for the final project which is basically the 61C RISC-V project on steroids. Thankfully, we used Verilog instead of Logisim. I had quite a lot of fun in this course and would recommend it to anyone even remotely interested.
|
||||
|
||||
#### EE127 – Optimization Models in Engineering
|
||||
|
||||
All of the remaining interesting courses were being offered in the Spring, so I added EE127 as my fourth technical course. It’s a great introduction into how to think about and formulate optimization problems as well as solving them practically with software. The workload is decently heavy, especially with amount of linear algebra involved. Since I already took CS189, I didn’t learn very much new material and this class was more of a filler for the semester.
|
||||
|
||||
#### EE130 – Integrated-Circuit Devices
|
||||
|
||||
After getting destroyed in the semiconductor physics portion of EE105 which made me steer away from analog circuits, I finally took a class dedicated to it. It’s extremely well taught and the professor does an amazing job of explaining things clearly and providing in-depth notes for all the lectures that I did miss. The homework and exams are difficult, but fair and doable in the time allotted. I walked away from the class feeling like I had a fundamental understanding of common semiconductor devices. Basically, band diagrams are your friend.
|
||||
|
||||
#### EE143 – Microfabrication Technology
|
||||
|
||||
This class has an excellent reputation, but the new visiting professor for this semester did not do a great job. Ultimately, this is the one and only class that I’ve ever dropped in my entire undergraduate career. Between the poor lectures that didn’t really point out important topics and the lack of homework to reinforce learning, I found it difficult to have any motivation in this class. The first midterm was also the perfect example of an exam being completely different from the lecture material. I still remember the professor roasting the class for how poorly we did especially for Berkeley students. I believe the old professor is coming back to teach, so I’d still recommend this class for future generations.
|
||||
|
||||
On the other hand, the lab portion of the class was extremely well done and the TAs were amazing. We actually went through the whole process of fabricating MOSFETs and other devices on a silicon wafer. While the quality and yield of my devices was not great, I had a blast doing it.
|
||||
|
||||
#### CS164 – Programming Languages and Compilers
|
||||
|
||||
Following my theme of demystifying EECS, this class did not disappoint on making compilers understandable. As with operating systems, it’s mainly a huge logistics issue with lots of moving parts that need to work together. The professor was new, but did an excellent job teaching. The workload is pretty normal and the homework did a great job at expanding what was taught in lecture in a manageable way. It also offered my first taste of a real functional programming language, OCaml. This class also had the first final exam where I studied during the final since not attending most lectures meant not knowing the notation used.
|
||||
|
||||
#### SpaceX Summer 2021 Avionics Internship
|
||||
|
||||
This summer I was part of the Starship Test Engineering team working on various projects. Whereas my Build Reliability internship helped teach me how to design, this internship actually put those skills to the test. It also offered great insight into how test systems work and how formalized the testing process is. Having knowledge of both hardware and software was pretty invaluable in getting my projects done well. I learned quite a lot and even dabbled a bit into power electronics which I did not expect. This was the first summer I didn’t want to go back to school, which is a great sign I should graduate.
|
||||
|
||||
#### Full-Time Recruitment
|
||||
|
||||
It’s funny how life doesn’t really ever go exactly as planned. I originally intended on getting a 5th Year Master’s and applied for a couple internships. Then an Apple recruiter called me and convinced me to try full-time recruiting instead. As luck would have it, I got rejected by Apple but got offers from Tesla and Neuralink. Still figuring out where I want to end up, but I’ll pick one eventually.
|
BIN
assets/img/2021/2020courses.jpg
Normal file
After Width: | Height: | Size: 79 KiB |
BIN
assets/img/2021/agitator_1.jpg
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
assets/img/2021/agitator_10.jpg
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
assets/img/2021/agitator_11.jpg
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
assets/img/2021/agitator_12.jpg
Normal file
After Width: | Height: | Size: 78 KiB |
BIN
assets/img/2021/agitator_13.jpg
Normal file
After Width: | Height: | Size: 96 KiB |
BIN
assets/img/2021/agitator_14.jpg
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
assets/img/2021/agitator_15.jpg
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
assets/img/2021/agitator_16.jpg
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
assets/img/2021/agitator_17.jpg
Normal file
After Width: | Height: | Size: 61 KiB |
BIN
assets/img/2021/agitator_18.jpg
Normal file
After Width: | Height: | Size: 88 KiB |
BIN
assets/img/2021/agitator_19.jpg
Normal file
After Width: | Height: | Size: 136 KiB |
BIN
assets/img/2021/agitator_2.jpg
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
assets/img/2021/agitator_3.jpg
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
assets/img/2021/agitator_4.jpg
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
assets/img/2021/agitator_5.jpg
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
assets/img/2021/agitator_6.jpg
Normal file
After Width: | Height: | Size: 102 KiB |
BIN
assets/img/2021/agitator_7.jpg
Normal file
After Width: | Height: | Size: 71 KiB |
BIN
assets/img/2021/agitator_8.jpg
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
assets/img/2021/agitator_9.jpg
Normal file
After Width: | Height: | Size: 69 KiB |
BIN
assets/img/2021/aprs_1.jpg
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
assets/img/2021/aprs_10.jpg
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
assets/img/2021/aprs_11.jpg
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
assets/img/2021/aprs_12.jpg
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
assets/img/2021/aprs_13.jpg
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
assets/img/2021/aprs_14.jpg
Normal file
After Width: | Height: | Size: 49 KiB |
BIN
assets/img/2021/aprs_15.jpg
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
assets/img/2021/aprs_16.jpg
Normal file
After Width: | Height: | Size: 79 KiB |
BIN
assets/img/2021/aprs_17.jpg
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
assets/img/2021/aprs_18.jpg
Normal file
After Width: | Height: | Size: 69 KiB |
BIN
assets/img/2021/aprs_19.jpg
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
assets/img/2021/aprs_2.jpg
Normal file
After Width: | Height: | Size: 69 KiB |
BIN
assets/img/2021/aprs_20.jpg
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
assets/img/2021/aprs_21.jpg
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
assets/img/2021/aprs_22.jpg
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
assets/img/2021/aprs_23.jpg
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
assets/img/2021/aprs_24.jpg
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
assets/img/2021/aprs_25.jpg
Normal file
After Width: | Height: | Size: 135 KiB |
BIN
assets/img/2021/aprs_3.jpg
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
assets/img/2021/aprs_4.jpg
Normal file
After Width: | Height: | Size: 61 KiB |
BIN
assets/img/2021/aprs_5.jpg
Normal file
After Width: | Height: | Size: 88 KiB |
BIN
assets/img/2021/aprs_6.jpg
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
assets/img/2021/aprs_7.jpg
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
assets/img/2021/aprs_8.jpg
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
assets/img/2021/aprs_9.jpg
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
assets/img/2021/bmp_1.jpg
Normal file
After Width: | Height: | Size: 139 KiB |
BIN
assets/img/2021/bmp_10.jpg
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
assets/img/2021/bmp_11.jpg
Normal file
After Width: | Height: | Size: 77 KiB |
BIN
assets/img/2021/bmp_12.jpg
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
assets/img/2021/bmp_2.jpg
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
assets/img/2021/bmp_3.jpg
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
assets/img/2021/bmp_4.jpg
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
assets/img/2021/bmp_5.jpg
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
assets/img/2021/bmp_6.jpg
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
assets/img/2021/bmp_7.jpg
Normal file
After Width: | Height: | Size: 86 KiB |
BIN
assets/img/2021/bmp_8.jpg
Normal file
After Width: | Height: | Size: 102 KiB |
BIN
assets/img/2021/bmp_9.jpg
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
assets/img/2021/bmp_layout.jpg
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
assets/img/2021/bmp_schem.jpg
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
assets/img/2021/ft232h_layout.png
Normal file
After Width: | Height: | Size: 129 KiB |
BIN
assets/img/2021/ft232h_real.jpg
Normal file
After Width: | Height: | Size: 149 KiB |
BIN
assets/img/2021/ft232h_schematic.png
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
assets/img/2021/k66f_all.jpg
Normal file
After Width: | Height: | Size: 199 KiB |
BIN
assets/img/2021/k66f_layout.jpg
Normal file
After Width: | Height: | Size: 96 KiB |
BIN
assets/img/2021/k66f_stencil.jpg
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
assets/img/2021/k66f_stencil2.jpg
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
assets/img/2021/k66f_test.jpg
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
assets/img/2021/k66f_top.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
assets/img/2021/plnx_boot.png
Normal file
After Width: | Height: | Size: 65 KiB |
BIN
assets/img/2021/plnx_eth.png
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
assets/img/2021/plnx_green.png
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
assets/img/2021/plnx_min.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
assets/img/2021/plnx_ps.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
assets/img/2021/qtouch_1.jpg
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
assets/img/2021/qtouch_10.jpg
Normal file
After Width: | Height: | Size: 85 KiB |
BIN
assets/img/2021/qtouch_11.jpg
Normal file
After Width: | Height: | Size: 66 KiB |
BIN
assets/img/2021/qtouch_12.jpg
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
assets/img/2021/qtouch_13.jpg
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
assets/img/2021/qtouch_14.jpg
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
assets/img/2021/qtouch_15.jpg
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
assets/img/2021/qtouch_2.jpg
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
assets/img/2021/qtouch_3.jpg
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
assets/img/2021/qtouch_4.jpg
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
assets/img/2021/qtouch_5.jpg
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
assets/img/2021/qtouch_6.jpg
Normal file
After Width: | Height: | Size: 83 KiB |
BIN
assets/img/2021/qtouch_7.jpg
Normal file
After Width: | Height: | Size: 199 KiB |
BIN
assets/img/2021/qtouch_8.jpg
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
assets/img/2021/qtouch_9.jpg
Normal file
After Width: | Height: | Size: 60 KiB |