<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>http://wiki.nuclearman.technology/mediawiki//api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Nuclear</id>
	<title>Nuclear's Documentation Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.nuclearman.technology/mediawiki//api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Nuclear"/>
	<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php/Special:Contributions/Nuclear"/>
	<updated>2026-05-06T18:16:10Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.35.13</generator>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=RA8875_Parallel_VGA_Card&amp;diff=80</id>
		<title>RA8875 Parallel VGA Card</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=RA8875_Parallel_VGA_Card&amp;diff=80"/>
		<updated>2024-09-03T17:56:05Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: smaller images&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The RA8875 Parallel VGA Card is a small card which provides 800x480 pixel VGA output, using an 8-bit, 5v-compatible 8080-style data bus.  It uses the RA8875, which comes with many useful features for drawing, and includes built-in fonts, hardware scrolling, multiple layers, etc.&lt;br /&gt;
&lt;br /&gt;
See the hackaday project for schematics and updates: https://hackaday.io/project/197178-ra8875-vga&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
* Up to 480x800 pixel display&lt;br /&gt;
* 16-bit color (RGB565)&lt;br /&gt;
** Optionally, 8-bit color can be selected to use two drawing layers, and to improve bitmap draw time&lt;br /&gt;
* Video modes can be selected or designed&lt;br /&gt;
** RA8875 allows configuring of all VGA regions, as well as hsync and vsync polarity.  Multiple resolutions and pixel clocks may be selected&lt;br /&gt;
** Recommended settings use a 40Mhz pixel clock&lt;br /&gt;
* 8-bit parallel data bus for fast writing&lt;br /&gt;
** Overclocked to over 167.9Mbps with stable writing in tests&lt;br /&gt;
** Minimum cycle time of 55ns (see RA8875 datasheet)&lt;br /&gt;
** 5v compatible&lt;br /&gt;
** 8080-style control signals: nWAIT, nWR, nRD, nSEL&lt;br /&gt;
** Z80 and 8080 compatible design&lt;br /&gt;
&lt;br /&gt;
This board is designed to work with the [[Teensy_4.1_Computer]], and is a convenient way of adding video output to the Teensy computer.&lt;br /&gt;
&lt;br /&gt;
== External Connection ==&lt;br /&gt;
&lt;br /&gt;
[[File:RA8875_VGA_Card_Pinouts.png|thumb|Board pinout|600px]]&lt;br /&gt;
&lt;br /&gt;
[[File:Expansion_port_pinout_on_RA8875_card_(any_variant).png|thumb|Expansion port pinout|600px]]&lt;br /&gt;
[[File:Teensy4.1_computer_external_headers_1.png|thumb|Teensy 4.1 Computer connector schematics, excluding the card connector, for comparison|400px]]&lt;br /&gt;
&lt;br /&gt;
If you are on the Teensy 4.1 Computer board or any 5v microcontroller, you probably want to use the card connector.  Otherwise, I recommend soldering your connections to the large side expansion port.  You can read more on each in the sections below.&lt;br /&gt;
The rest of this section will be devoted to more nitty-gritty technical details.&lt;br /&gt;
&lt;br /&gt;
There are two main connectors for interfacing to the RA8875, both share most of their pins and both are compatible with a respective connector on the [[Teensy 4.1 computer]].&lt;br /&gt;
The card connector is a simpler, smaller header with pre-soldered headers, while the larger expansion port comes unsoldered and has more pins.&lt;br /&gt;
&lt;br /&gt;
Both ports share the parallel pins WR, RD, and WAIT, as well as the busses ADDR and DATA.  WR, RD, and WAIT are all active low, and it is expected that all select lines also be active low.  The address bus (ADDR, shown on the board as A0, A1, and A2) has three pins, allowing for three addresses per device, but the RA8875 only uses addresses 0 and 1.&lt;br /&gt;
&lt;br /&gt;
=== Card Connector ===&lt;br /&gt;
&lt;br /&gt;
This connector has all the needed lines to communicate with the RA8875, as well as level-shifting logic to make it completely 5v-compatible.  A pinout is provided above.&lt;br /&gt;
&lt;br /&gt;
The card connector was originally designed to interface with cards for the Z80 computer I previously built, and has a compatible pinout, as well as a compatible electrical specification.&lt;br /&gt;
&lt;br /&gt;
=== Side expansion port ===&lt;br /&gt;
&lt;br /&gt;
This is a larger 3.3v-compatible port.&lt;br /&gt;
&lt;br /&gt;
== Software and Programming ==&lt;br /&gt;
&lt;br /&gt;
An Arduino library is available on my gitlab: https://git.nuclaer-servers.com/Nuclaer/ra8875-parallel-vga-library&lt;br /&gt;
&lt;br /&gt;
The library has example code to reference.  Currently it supports:&lt;br /&gt;
* Multiple resolutions, including changing display resolution while in use&lt;br /&gt;
* Basic shapes, like rectangles, circles, lines&lt;br /&gt;
* Text&lt;br /&gt;
** Color, background color, or background transparency&lt;br /&gt;
** Size, both vertically and horizontally&lt;br /&gt;
* Bitmaps&lt;br /&gt;
** Extremely fast draw times&lt;br /&gt;
* Multiple parallel bus interface options&lt;br /&gt;
** Dedicated memory interface&lt;br /&gt;
** Raw IO pins (Arduino digitalRead/digitalWrite)&lt;br /&gt;
** Teensy 4.1 direct port manipulation&lt;br /&gt;
&lt;br /&gt;
For code optimized for more complex and demanding video games, my Teensy Minecraft is a good reference: https://git.nuclaer-servers.com/Nuclaer/teensy-minecraft&lt;br /&gt;
&lt;br /&gt;
NTIOS also has a driver for this card, and may be fun to play with or reference.  The OS drivers and such are in this repo: https://git.nuclaer-servers.com/Nuclaer/ntios-2020   And here is an Arduino sketch that uses the driver, running on Teensy 4.1: https://git.nuclaer-servers.com/Nuclaer/teensy-ntios&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=RA8875_Parallel_VGA_Card&amp;diff=79</id>
		<title>RA8875 Parallel VGA Card</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=RA8875_Parallel_VGA_Card&amp;diff=79"/>
		<updated>2024-09-03T17:55:11Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Add links and software info&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The RA8875 Parallel VGA Card is a small card which provides 800x480 pixel VGA output, using an 8-bit, 5v-compatible 8080-style data bus.  It uses the RA8875, which comes with many useful features for drawing, and includes built-in fonts, hardware scrolling, multiple layers, etc.&lt;br /&gt;
&lt;br /&gt;
See the hackaday project for schematics and updates: https://hackaday.io/project/197178-ra8875-vga&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
* Up to 480x800 pixel display&lt;br /&gt;
* 16-bit color (RGB565)&lt;br /&gt;
** Optionally, 8-bit color can be selected to use two drawing layers, and to improve bitmap draw time&lt;br /&gt;
* Video modes can be selected or designed&lt;br /&gt;
** RA8875 allows configuring of all VGA regions, as well as hsync and vsync polarity.  Multiple resolutions and pixel clocks may be selected&lt;br /&gt;
** Recommended settings use a 40Mhz pixel clock&lt;br /&gt;
* 8-bit parallel data bus for fast writing&lt;br /&gt;
** Overclocked to over 167.9Mbps with stable writing in tests&lt;br /&gt;
** Minimum cycle time of 55ns (see RA8875 datasheet)&lt;br /&gt;
** 5v compatible&lt;br /&gt;
** 8080-style control signals: nWAIT, nWR, nRD, nSEL&lt;br /&gt;
** Z80 and 8080 compatible design&lt;br /&gt;
&lt;br /&gt;
This board is designed to work with the [[Teensy_4.1_Computer]], and is a convenient way of adding video output to the Teensy computer.&lt;br /&gt;
&lt;br /&gt;
== External Connection ==&lt;br /&gt;
&lt;br /&gt;
[[File:RA8875_VGA_Card_Pinouts.png|thumb|Board pinout|800px]]&lt;br /&gt;
&lt;br /&gt;
[[File:Expansion_port_pinout_on_RA8875_card_(any_variant).png|thumb|Expansion port pinout|800px]]&lt;br /&gt;
[[File:Teensy4.1_computer_external_headers_1.png|thumb|Teensy 4.1 Computer connector schematics, excluding the card connector, for comparison|800px]]&lt;br /&gt;
&lt;br /&gt;
If you are on the Teensy 4.1 Computer board or any 5v microcontroller, you probably want to use the card connector.  Otherwise, I recommend soldering your connections to the large side expansion port.  You can read more on each in the sections below.&lt;br /&gt;
The rest of this section will be devoted to more nitty-gritty technical details.&lt;br /&gt;
&lt;br /&gt;
There are two main connectors for interfacing to the RA8875, both share most of their pins and both are compatible with a respective connector on the [[Teensy 4.1 computer]].&lt;br /&gt;
The card connector is a simpler, smaller header with pre-soldered headers, while the larger expansion port comes unsoldered and has more pins.&lt;br /&gt;
&lt;br /&gt;
Both ports share the parallel pins WR, RD, and WAIT, as well as the busses ADDR and DATA.  WR, RD, and WAIT are all active low, and it is expected that all select lines also be active low.  The address bus (ADDR, shown on the board as A0, A1, and A2) has three pins, allowing for three addresses per device, but the RA8875 only uses addresses 0 and 1.&lt;br /&gt;
&lt;br /&gt;
=== Card Connector ===&lt;br /&gt;
&lt;br /&gt;
This connector has all the needed lines to communicate with the RA8875, as well as level-shifting logic to make it completely 5v-compatible.  A pinout is provided above.&lt;br /&gt;
&lt;br /&gt;
The card connector was originally designed to interface with cards for the Z80 computer I previously built, and has a compatible pinout, as well as a compatible electrical specification.&lt;br /&gt;
&lt;br /&gt;
=== Side expansion port ===&lt;br /&gt;
&lt;br /&gt;
This is a larger 3.3v-compatible port.&lt;br /&gt;
&lt;br /&gt;
== Software and Programming ==&lt;br /&gt;
&lt;br /&gt;
An Arduino library is available on my gitlab: https://git.nuclaer-servers.com/Nuclaer/ra8875-parallel-vga-library&lt;br /&gt;
&lt;br /&gt;
The library has example code to reference.  Currently it supports:&lt;br /&gt;
* Multiple resolutions, including changing display resolution while in use&lt;br /&gt;
* Basic shapes, like rectangles, circles, lines&lt;br /&gt;
* Text&lt;br /&gt;
** Color, background color, or background transparency&lt;br /&gt;
** Size, both vertically and horizontally&lt;br /&gt;
* Bitmaps&lt;br /&gt;
** Extremely fast draw times&lt;br /&gt;
* Multiple parallel bus interface options&lt;br /&gt;
** Dedicated memory interface&lt;br /&gt;
** Raw IO pins (Arduino digitalRead/digitalWrite)&lt;br /&gt;
** Teensy 4.1 direct port manipulation&lt;br /&gt;
&lt;br /&gt;
For code optimized for more complex and demanding video games, my Teensy Minecraft is a good reference: https://git.nuclaer-servers.com/Nuclaer/teensy-minecraft&lt;br /&gt;
&lt;br /&gt;
NTIOS also has a driver for this card, and may be fun to play with or reference.  The OS drivers and such are in this repo: https://git.nuclaer-servers.com/Nuclaer/ntios-2020   And here is an Arduino sketch that uses the driver, running on Teensy 4.1: https://git.nuclaer-servers.com/Nuclaer/teensy-ntios&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=RA8875_Parallel_VGA_Card&amp;diff=78</id>
		<title>RA8875 Parallel VGA Card</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=RA8875_Parallel_VGA_Card&amp;diff=78"/>
		<updated>2024-09-03T17:41:07Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: improve images&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The RA8875 Parallel VGA Card is a small card which provides 800x480 pixel VGA output, using an 8-bit, 5v-compatible 8080-style data bus.  It uses the RA8875, which comes with many useful features for drawing, and includes built-in fonts, hardware scrolling, multiple layers, etc.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
* Up to 480x800 pixel display&lt;br /&gt;
* 16-bit color (RGB565)&lt;br /&gt;
** Optionally, 8-bit color can be selected to use two drawing layers, and to improve bitmap draw time&lt;br /&gt;
* Video modes can be selected or designed&lt;br /&gt;
** RA8875 allows configuring of all VGA regions, as well as hsync and vsync polarity.  Multiple resolutions and pixel clocks may be selected&lt;br /&gt;
** Recommended settings use a 40Mhz pixel clock&lt;br /&gt;
* 8-bit parallel data bus for fast writing&lt;br /&gt;
** Overclocked to over 167.9Mbps with stable writing in tests&lt;br /&gt;
** Minimum cycle time of 55ns (see RA8875 datasheet)&lt;br /&gt;
** 5v compatible&lt;br /&gt;
** 8080-style control signals: nWAIT, nWR, nRD, nSEL&lt;br /&gt;
** Z80 and 8080 compatible design&lt;br /&gt;
&lt;br /&gt;
This board is designed to work with the [[Teensy_4.1_Computer]], and is a convenient way of adding video output to the Teensy computer.&lt;br /&gt;
&lt;br /&gt;
== External Connection ==&lt;br /&gt;
&lt;br /&gt;
[[File:RA8875_VGA_Card_Pinouts.png|thumb|Board pinout|800px]]&lt;br /&gt;
&lt;br /&gt;
[[File:Card_Connector_Pinout.png|thumb|Card connector pinout|800px]]&lt;br /&gt;
&lt;br /&gt;
[[File:Expansion_port_pinout_on_RA8875_card_(any_variant).png|thumb|Expansion port pinout|800px]]&lt;br /&gt;
[[File:Teensy4.1_computer_external_headers_1.png|thumb|Teensy 4.1 Computer connector schematics, excluding the card connector, for comparison|800px]]&lt;br /&gt;
&lt;br /&gt;
If you are on the Teensy 4.1 Computer board or any 5v microcontroller, you probably want to use the card connector.  Otherwise, I recommend soldering your connections to the large side expansion port.  You can read more on each in the sections below.&lt;br /&gt;
The rest of this section will be devoted to more nitty-gritty technical details.&lt;br /&gt;
&lt;br /&gt;
There are two main connectors for interfacing to the RA8875, both share most of their pins and both are compatible with a respective connector on the [[Teensy 4.1 computer]].&lt;br /&gt;
The card connector is a simpler, smaller header with pre-soldered headers, while the larger expansion port comes unsoldered and has more pins.&lt;br /&gt;
&lt;br /&gt;
Both ports share the parallel pins WR, RD, and WAIT, as well as the busses ADDR and DATA.  WR, RD, and WAIT are all active low, and it is expected that all select lines also be active low.  The address bus (ADDR, shown on the board as A0, A1, and A2) has three pins, allowing for three addresses per device, but the RA8875 only uses addresses 0 and 1.&lt;br /&gt;
&lt;br /&gt;
=== Card Connector ===&lt;br /&gt;
&lt;br /&gt;
This connector has all the needed lines to communicate with the RA8875, as well as level-shifting logic to make it completely 5v-compatible.  A pinout is provided above.&lt;br /&gt;
&lt;br /&gt;
The card connector was originally designed to interface with cards for the Z80 computer I previously built, and has a compatible pinout, as well as a compatible electrical specification.&lt;br /&gt;
&lt;br /&gt;
=== Side expansion port ===&lt;br /&gt;
&lt;br /&gt;
This is a larger 3.3v-compatible port.&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=File:RA8875_VGA_Card_Pinouts.png&amp;diff=77</id>
		<title>File:RA8875 VGA Card Pinouts.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=File:RA8875_VGA_Card_Pinouts.png&amp;diff=77"/>
		<updated>2024-09-03T17:32:02Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Pinouts for the RA8875 VGA Card.  Note that this pinout is not the same for other board variants.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Pinouts for the RA8875 VGA Card.  Note that this pinout is not the same for other board variants.&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=RA8875_Parallel_VGA_Card&amp;diff=76</id>
		<title>RA8875 Parallel VGA Card</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=RA8875_Parallel_VGA_Card&amp;diff=76"/>
		<updated>2024-09-03T17:22:33Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Checkpoint to save work&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The RA8875 Parallel VGA Card is a small card which provides 800x480 pixel VGA output, using an 8-bit, 5v-compatible 8080-style data bus.  It uses the RA8875, which comes with many useful features for drawing, and includes built-in fonts, hardware scrolling, multiple layers, etc.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
* Up to 480x800 pixel display&lt;br /&gt;
* 16-bit color (RGB565)&lt;br /&gt;
** Optionally, 8-bit color can be selected to use two drawing layers, and to improve bitmap draw time&lt;br /&gt;
* Video modes can be selected or designed&lt;br /&gt;
** RA8875 allows configuring of all VGA regions, as well as hsync and vsync polarity.  Multiple resolutions and pixel clocks may be selected&lt;br /&gt;
** Recommended settings use a 40Mhz pixel clock&lt;br /&gt;
* 8-bit parallel data bus for fast writing&lt;br /&gt;
** Overclocked to over 167.9Mbps with stable writing in tests&lt;br /&gt;
** Minimum cycle time of 55ns (see RA8875 datasheet)&lt;br /&gt;
** 5v compatible&lt;br /&gt;
** 8080-style control signals: nWAIT, nWR, nRD, nSEL&lt;br /&gt;
** Z80 and 8080 compatible design&lt;br /&gt;
&lt;br /&gt;
This board is designed to work with the [[Teensy_4.1_Computer]], and is a convenient way of adding video output to the Teensy computer.&lt;br /&gt;
&lt;br /&gt;
== External Connection ==&lt;br /&gt;
&lt;br /&gt;
If you are on the Teensy 4.1 Computer board or any 5v microcontroller, you probably want to use the card connector.  Otherwise, I recommend soldering your connections to the large side expansion port.  You can read more on each in the sections below.&lt;br /&gt;
The rest of this section will be devoted to more nitty-gritty technical details.&lt;br /&gt;
&lt;br /&gt;
There are two main connectors for interfacing to the RA8875, both share most of their pins and both are compatible with a respective connector on the [[Teensy 4.1 computer]].&lt;br /&gt;
The card connector is a simpler, smaller header with pre-soldered headers, while the larger expansion port comes unsoldered and has more pins.&lt;br /&gt;
&lt;br /&gt;
Both ports share the parallel pins WR, RD, and WAIT, as well as the busses ADDR and DATA.  WR, RD, and WAIT are all active low, and it is expected that all select lines also be active low.  The address bus (ADDR, shown on the board as A0, A1, and A2) has three pins, allowing for three addresses per device, but the RA8875 only uses addresses 0 and 1.&lt;br /&gt;
&lt;br /&gt;
=== Card Connector ===&lt;br /&gt;
&lt;br /&gt;
[[File:Card_Connector_Pinout.png|thumb|Card connector pinout|800px]]&lt;br /&gt;
&lt;br /&gt;
This connector has all the needed lines to communicate with the RA8875, as well as level-shifting logic to make it completely 5v-compatible.  A pinout is provided above.&lt;br /&gt;
&lt;br /&gt;
The card connector was originally designed to interface with cards for the Z80 computer I previously built, and has a compatible pinout, as well as a compatible electrical specification.&lt;br /&gt;
&lt;br /&gt;
=== Side expansion port ===&lt;br /&gt;
&lt;br /&gt;
[[File:Expansion_port_pinout_on_RA8875_card_(any_variant).png|thumb|Expansion port pinout|800px]]&lt;br /&gt;
[[File:Teensy4.1_computer_external_headers_1.png|thumb|Teensy 4.1 Computer connector schematics, excluding the card connector, for comparison|800px]]&lt;br /&gt;
&lt;br /&gt;
This is a larger 3.3v-compatible port.&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=File:Expansion_port_pinout_on_RA8875_card_(any_variant).png&amp;diff=75</id>
		<title>File:Expansion port pinout on RA8875 card (any variant).png</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=File:Expansion_port_pinout_on_RA8875_card_(any_variant).png&amp;diff=75"/>
		<updated>2024-09-03T16:44:55Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Taken from the schematic.  This connector is pin compatible with J12 on the Teensy 4.1 computer.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Taken from the schematic.  This connector is pin compatible with J12 on the Teensy 4.1 computer.&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=File:Card_Connector_Pinout.png&amp;diff=74</id>
		<title>File:Card Connector Pinout.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=File:Card_Connector_Pinout.png&amp;diff=74"/>
		<updated>2024-09-03T16:39:51Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Taken from the RA8875 VGA graphics card, but the pinout is standard, and applies to all devices on the standard.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Taken from the RA8875 VGA graphics card, but the pinout is standard, and applies to all devices on the standard.&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=RA8875_Parallel_VGA_Card&amp;diff=73</id>
		<title>RA8875 Parallel VGA Card</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=RA8875_Parallel_VGA_Card&amp;diff=73"/>
		<updated>2024-08-31T15:22:42Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The RA8875 Parallel VGA Card is a small card which provides 800x480 pixel VGA output, using an 8-bit, 5v-compatible 8080-style data bus.  It uses the RA8875, which comes with many useful features for drawing, and includes built-in fonts, hardware scrolling, multiple layers, etc.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
* Up to 480x800 pixel display&lt;br /&gt;
* 16-bit color (RGB565)&lt;br /&gt;
** Optionally, 8-bit color can be selected to use two drawing layers, and to improve bitmap draw time&lt;br /&gt;
* Video modes can be selected or designed&lt;br /&gt;
** RA8875 allows configuring of all VGA regions, as well as hsync and vsync polarity.  Multiple resolutions and pixel clocks may be selected&lt;br /&gt;
** Recommended settings use a 40Mhz pixel clock&lt;br /&gt;
* 8-bit parallel data bus for fast writing&lt;br /&gt;
** Overclocked to over 167.9Mbps with stable writing in tests&lt;br /&gt;
** Minimum cycle time of 55ns (see RA8875 datasheet)&lt;br /&gt;
** 5v compatible&lt;br /&gt;
** 8080-style control signals: nWAIT, nWR, nRD, nSEL&lt;br /&gt;
** Z80 and 8080 compatible design&lt;br /&gt;
&lt;br /&gt;
This board is designed to work with the [[Teensy_4.1_Computer]], and is a convenient way of adding video output to the Teensy computer.&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=RA8875_Parallel_VGA_Card&amp;diff=72</id>
		<title>RA8875 Parallel VGA Card</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=RA8875_Parallel_VGA_Card&amp;diff=72"/>
		<updated>2024-08-28T21:05:06Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The RA8875 Parallel VGA Card is a small card which provides 800x480 pixel VGA output, using an 8-bit, 5v-compatible 8080-style data bus.  It uses the RA8875, which comes with many useful features for drawing, and includes built-in fonts, hardware scrolling, multiple layers, etc.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
* Up to 480x800 pixel display&lt;br /&gt;
* 16-bit color (RGB565)&lt;br /&gt;
** Optionally, 8-bit color can be selected to use two drawing layers, and to improve bitmap draw time&lt;br /&gt;
* Video modes can be selected or designed&lt;br /&gt;
** RA8875 allows configuring of all VGA regions, as well as hsync and vsync polarity.  Multiple resolutions and pixel clocks may be selected&lt;br /&gt;
** Recommended settings use a 40Mhz pixel clock&lt;br /&gt;
* 8-bit parallel data bus for fast writing&lt;br /&gt;
** Overclocked to over 167.9Mbps with stable writing in tests&lt;br /&gt;
** Minimum cycle time of 55ns (see RA8875 datasheet)&lt;br /&gt;
** 5v compatible&lt;br /&gt;
** 8080-style control signals: nWAIT, nWR, nRD, nSEL&lt;br /&gt;
** Z80 and 8080 compatible design&lt;br /&gt;
&lt;br /&gt;
This board is designed to work with the [[Teensy_4.1_Computer]], and is a convenient way of adding video output to the Teensy computer.&lt;br /&gt;
&lt;br /&gt;
== Overall Design ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy41_Computer_Audio_circuit.png|thumb|Teensy 4.1 Computer audio circuit|600px]]&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=Main_Page&amp;diff=71</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=Main_Page&amp;diff=71"/>
		<updated>2024-08-28T20:26:58Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Add RA8875_Parallel_VGA_Card link&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;strong&amp;gt;Important Documentation Pages:&amp;lt;/strong&amp;gt;&lt;br /&gt;
You are probably looking for one of these:&lt;br /&gt;
&lt;br /&gt;
* [[Teensy_4.1_Computer]]&lt;br /&gt;
* [[NGT31]]&lt;br /&gt;
* [[RA8875_Parallel_VGA_Card]]&lt;br /&gt;
&lt;br /&gt;
Other documentation will be added as needed :)&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=RA8875_Parallel_VGA_Card&amp;diff=70</id>
		<title>RA8875 Parallel VGA Card</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=RA8875_Parallel_VGA_Card&amp;diff=70"/>
		<updated>2024-08-28T20:24:05Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Fill in specifications section&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The RA8875 Parallel VGA Card is a small card which provides 800x480 pixel VGA output, using an 8-bit, 5v-compatible 8080-style data bus.  It uses the RA8875, which comes with many useful features for drawing, and includes built-in fonts, hardware scrolling, multiple layers, etc.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
* Up to 480x800 pixel display&lt;br /&gt;
* 16-bit color (RGB565)&lt;br /&gt;
** Optionally, 8-bit color can be selected to use two drawing layers, and to improve bitmap draw time&lt;br /&gt;
* Video modes can be selected or designed&lt;br /&gt;
** RA8875 allows configuring of all VGA regions, as well as hsync and vsync polarity.  Multiple resolutions and pixel clocks may be selected&lt;br /&gt;
** Recommended settings use a 40Mhz pixel clock&lt;br /&gt;
* 8-bit parallel data bus for fast writing&lt;br /&gt;
** Overclocked up to 167.9Mbps with stable writing in tests&lt;br /&gt;
** Minimum cycle time of 55ns (see RA8875 datasheet)&lt;br /&gt;
** 5v compatible&lt;br /&gt;
** 8080-style control signals: nWAIT, nWR, nRD, nSEL&lt;br /&gt;
** Z80 and 8080 compatible design&lt;br /&gt;
&lt;br /&gt;
== Overall Design ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy41_Computer_Audio_circuit.png|thumb|Teensy 4.1 Computer audio circuit|600px]]&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=RA8875_Parallel_VGA_Card&amp;diff=69</id>
		<title>RA8875 Parallel VGA Card</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=RA8875_Parallel_VGA_Card&amp;diff=69"/>
		<updated>2024-08-28T20:10:15Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: placeholder text&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Est li na svete,&lt;br /&gt;
tsveti chto nye ?&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=Main_Page&amp;diff=68</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=Main_Page&amp;diff=68"/>
		<updated>2024-08-07T17:08:07Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;strong&amp;gt;Important Documentation Pages:&amp;lt;/strong&amp;gt;&lt;br /&gt;
You are probably looking for one of these:&lt;br /&gt;
&lt;br /&gt;
* [[Teensy_4.1_Computer]]&lt;br /&gt;
* [[NGT31]]&lt;br /&gt;
&lt;br /&gt;
Other documentation will be added as needed :)&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=67</id>
		<title>Teensy 4.1 Computer</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=67"/>
		<updated>2024-07-30T15:18:01Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Fix mechanical measurements header size&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Teensy 4.1 Computer is more or less described by its name: Its a carrier board for the Teensy 4.1 which provides four USB ports, an Ethernet port, Hi-Fi audio, and several convenient expansion ports.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
'''USB:'''&lt;br /&gt;
&lt;br /&gt;
* TUSB2046 USB Hub, providing 4 USB A ports&lt;br /&gt;
* Each port has a maximum of 1.5A output - the port will shutdown due to overcurrent above this&lt;br /&gt;
* Board power provides a maximum of 2A to the 5v supply&lt;br /&gt;
** one should be aware of this when considering the power used by USB devices&lt;br /&gt;
&lt;br /&gt;
'''Audio:'''&lt;br /&gt;
&lt;br /&gt;
* I2S control connected to Teensy I2S 2&lt;br /&gt;
* PCM5102 ADC for providing stereo sound&lt;br /&gt;
* OPA1688 for the amplifier&lt;br /&gt;
* 3.5mm audio jack, maximum 75mA per channel&lt;br /&gt;
* Separate ground, connected to main ground through a ferrite bead&lt;br /&gt;
* Separate 3.3v and 5v supplies from linear regulators to reduce noise&lt;br /&gt;
** Note: 5v line will come from main 5v rail if only powered from USB&lt;br /&gt;
&lt;br /&gt;
'''Power:'''&lt;br /&gt;
&lt;br /&gt;
* 2A 5v buck regulator (Exact part is the VR20S05)&lt;br /&gt;
* PMOS switches to prevent regulator backfeeding from USB and to control power flow (U16, U17, and U18 on the PCB)&lt;br /&gt;
* External VIN accepted from 8v to 20v&lt;br /&gt;
* Separate 5v and 3.3v supplies for audio circutry&lt;br /&gt;
* Barrel jack and screw terminal connections for VIN&lt;br /&gt;
&lt;br /&gt;
'''Expansion Ports'''&lt;br /&gt;
&lt;br /&gt;
* I2S, SPI, I2C, and Serial expansion ports&lt;br /&gt;
* 5v-capable 8-bit parallel bus port, with 3-bit address, WR, RD, select, and WAIT lines&lt;br /&gt;
* Two larger expansion ports, each providing a '''3.3v compatible''' 8-bit parallel bus, various protocols like I2C and SPI, power, and GPIO&lt;br /&gt;
&lt;br /&gt;
== The Audio Circuit ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy41_Computer_Audio_circuit.png|thumb|Teensy 4.1 Computer audio circuit|600px]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1 is connected to the audio circuit via the Teensy's I2S 2 port.  This connection goes to the PCM5102 ADC.  As is recommended in the PCM5102's datasheet, filtering is added to the output, however the filter used on this board is a bit stronger, using a 4.7nF capacitor instead of the 2.2nF capacitor recommended in the datasheet.  The filter cutoff is still far above human perception, at 72kHz, and should filter high frequency noise a bit better.  The connection to the OPA1688 is DC, not AC, coupled, but this is according to the recommendations in the OPA1688 datasheet.  The gain of the amplifier is configured to be 1, so the output voltage of the amp should match the ADC's output voltage, and be in the range of 0v to 3.3v.  A voltage divider is used to activate the PCM5102's soft mute function when the 5v line drops, which is designed to prevent any pops in the audio when the power is disconnected.&lt;br /&gt;
&lt;br /&gt;
The circuit was designed with audio quality, but not energy efficiency, in mind.  The circuit may sink as much as 400mA from VIN, and an additional 40mA from the main 5v line.  However, when the audio circuit is idle, it should be less than 50mA all together.  The 3.5mm audio jack can provide a maximum of 75mA per channel.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;Audio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;SD.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// GUItool: begin automatically generated code&lt;br /&gt;
AudioPlaySdWav           playSdWav1;     //xy=719,389&lt;br /&gt;
AudioOutputI2S2          i2s2_1;         //xy=892,388&lt;br /&gt;
AudioConnection          patchCord1(playSdWav1, 0, i2s2_1, 0);&lt;br /&gt;
AudioConnection          patchCord2(playSdWav1, 1, i2s2_1, 1);&lt;br /&gt;
// GUItool: end automatically generated code&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  AudioMemory(10);&lt;br /&gt;
&lt;br /&gt;
  if (!(SD.begin(BUILTIN_SDCARD))) {&lt;br /&gt;
    // stop here, but print a message repetitively&lt;br /&gt;
    while (1) {&lt;br /&gt;
      Serial.println(&amp;quot;Unable to access the SD card&amp;quot;);&lt;br /&gt;
      delay(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Serial.println(&amp;quot;Playing audio.wav...&amp;quot;);&lt;br /&gt;
  playSdWav1.play(&amp;quot;audio.wav&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // Do nothing forever&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== USB Ports ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_USB_schematic.png|thumb|Teensy 4.1 Computer USB circuit|600px]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1's builtin USB port is connected directly to a TUSB2046 IC, which is a USB hub and provides four downstream ports.  Each port has overcurrent protection, with a maximum allowed current of 1.5A.  There are also TVS diodes on all USB data lines on the downstream ports, provided by three TPD3E001DRLR ICs.  Other than that, each downstream USB port has two 15k pulldowns - one on each data line.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&lt;br /&gt;
The best example is found in the Mouse_KeyboardBT example from the USBHost_t36 library, which is included in the TeensyDuino package.  For convenience, I have included a stripped down version of it below:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;quot;USBHost_t36.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
USBHost myusb;&lt;br /&gt;
USBHub hub1(myusb);&lt;br /&gt;
USBHub hub2(myusb);&lt;br /&gt;
KeyboardController keyboard1(myusb);&lt;br /&gt;
KeyboardController keyboard2(myusb);&lt;br /&gt;
USBHIDParser hid1(myusb);&lt;br /&gt;
USBHIDParser hid2(myusb);&lt;br /&gt;
USBHIDParser hid3(myusb);&lt;br /&gt;
USBHIDParser hid4(myusb);&lt;br /&gt;
USBHIDParser hid5(myusb);&lt;br /&gt;
MouseController mouse1(myusb);&lt;br /&gt;
&lt;br /&gt;
BluetoothController bluet(myusb);   // version assumes it already was paired&lt;br /&gt;
&lt;br /&gt;
USBDriver *drivers[] = {&amp;amp;hub1, &amp;amp;hub2, &amp;amp;keyboard1, &amp;amp;keyboard2, &amp;amp;bluet, &amp;amp;hid1, &amp;amp;hid2, &amp;amp;hid3, &amp;amp;hid4, &amp;amp;hid5};&lt;br /&gt;
&lt;br /&gt;
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))&lt;br /&gt;
const char * driver_names[CNT_DEVICES] = {&amp;quot;Hub1&amp;quot;, &amp;quot;Hub2&amp;quot;, &amp;quot;KB1&amp;quot;, &amp;quot;KB2&amp;quot;, &amp;quot;Bluet&amp;quot;, &amp;quot;HID1&amp;quot; , &amp;quot;HID2&amp;quot;, &amp;quot;HID3&amp;quot;, &amp;quot;HID4&amp;quot;, &amp;quot;HID5&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool driver_active[CNT_DEVICES] = {false, false, false, false, false, false, false, false, false, false};&lt;br /&gt;
&lt;br /&gt;
// Lets also look at HID Input devices&lt;br /&gt;
USBHIDInput *hiddrivers[] = {&amp;amp;mouse1};&lt;br /&gt;
&lt;br /&gt;
#define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0]))&lt;br /&gt;
const char * hid_driver_names[CNT_HIDDEVICES] = {&amp;quot;Mouse1&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool hid_driver_active[CNT_HIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
BTHIDInput *bthiddrivers[] = {&amp;amp;keyboard1, &amp;amp;mouse1};&lt;br /&gt;
#define CNT_BTHIDDEVICES (sizeof(bthiddrivers)/sizeof(bthiddrivers[0]))&lt;br /&gt;
const char * bthid_driver_names[CNT_BTHIDDEVICES] = {&amp;quot;Keyboard(BT)&amp;quot;, &amp;quot;Mouse(BT)&amp;quot;};&lt;br /&gt;
bool bthid_driver_active[CNT_BTHIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
bool show_changed_only = false;&lt;br /&gt;
&lt;br /&gt;
uint8_t joystick_left_trigger_value = 0;&lt;br /&gt;
uint8_t joystick_right_trigger_value = 0;&lt;br /&gt;
uint64_t joystick_full_notify_mask = (uint64_t) - 1;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  while (!Serial); // wait for Arduino Serial Monitor&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  myusb.begin();&lt;br /&gt;
  keyboard1.attachPress(OnPress);&lt;br /&gt;
  keyboard2.attachPress(OnPress);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // check to see if the device list has changed:&lt;br /&gt;
  UpdateActiveDeviceInfo();&lt;br /&gt;
&lt;br /&gt;
  myusb.Task();&lt;br /&gt;
&lt;br /&gt;
  if (mouse1.available()) {&lt;br /&gt;
    Serial.print(&amp;quot;Mouse: buttons = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getButtons());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseX = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseX());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseY = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseY());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheel = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheel());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheelH = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheelH());&lt;br /&gt;
    Serial.println();&lt;br /&gt;
    mouse1.mouseDataClear();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void OnPress(int key)&lt;br /&gt;
{&lt;br /&gt;
  Serial.print(&amp;quot;key '&amp;quot;);&lt;br /&gt;
  switch (key) {&lt;br /&gt;
    case KEYD_UP       : Serial.print(&amp;quot;UP&amp;quot;); break;&lt;br /&gt;
    case KEYD_DOWN     : Serial.print(&amp;quot;DN&amp;quot;); break;&lt;br /&gt;
    case KEYD_LEFT     : Serial.print(&amp;quot;LEFT&amp;quot;); break;&lt;br /&gt;
    case KEYD_RIGHT    : Serial.print(&amp;quot;RIGHT&amp;quot;); break;&lt;br /&gt;
    case KEYD_INSERT   : Serial.print(&amp;quot;Ins&amp;quot;); break;&lt;br /&gt;
    case KEYD_DELETE   : Serial.print(&amp;quot;Del&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_UP  : Serial.print(&amp;quot;PUP&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_DOWN: Serial.print(&amp;quot;PDN&amp;quot;); break;&lt;br /&gt;
    case KEYD_HOME     : Serial.print(&amp;quot;HOME&amp;quot;); break;&lt;br /&gt;
    case KEYD_END      : Serial.print(&amp;quot;END&amp;quot;); break;&lt;br /&gt;
    case KEYD_F1       : Serial.print(&amp;quot;F1&amp;quot;); break;&lt;br /&gt;
    case KEYD_F2       : Serial.print(&amp;quot;F2&amp;quot;); break;&lt;br /&gt;
    case KEYD_F3       : Serial.print(&amp;quot;F3&amp;quot;); break;&lt;br /&gt;
    case KEYD_F4       : Serial.print(&amp;quot;F4&amp;quot;); break;&lt;br /&gt;
    case KEYD_F5       : Serial.print(&amp;quot;F5&amp;quot;); break;&lt;br /&gt;
    case KEYD_F6       : Serial.print(&amp;quot;F6&amp;quot;); break;&lt;br /&gt;
    case KEYD_F7       : Serial.print(&amp;quot;F7&amp;quot;); break;&lt;br /&gt;
    case KEYD_F8       : Serial.print(&amp;quot;F8&amp;quot;); break;&lt;br /&gt;
    case KEYD_F9       : Serial.print(&amp;quot;F9&amp;quot;); break;&lt;br /&gt;
    case KEYD_F10      : Serial.print(&amp;quot;F10&amp;quot;); break;&lt;br /&gt;
    case KEYD_F11      : Serial.print(&amp;quot;F11&amp;quot;); break;&lt;br /&gt;
    case KEYD_F12      : Serial.print(&amp;quot;F12&amp;quot;); break;&lt;br /&gt;
    default: Serial.print((char)key); break;&lt;br /&gt;
  }&lt;br /&gt;
  Serial.print(&amp;quot;'  &amp;quot;);&lt;br /&gt;
  Serial.println(key);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//=============================================================================&lt;br /&gt;
// UpdateActiveDeviceInfo&lt;br /&gt;
//=============================================================================&lt;br /&gt;
void UpdateActiveDeviceInfo() {&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_DEVICES; i++) {&lt;br /&gt;
    if (*drivers[i] != driver_active[i]) {&lt;br /&gt;
      if (driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s - disconnected ***\n&amp;quot;, driver_names[i]);&lt;br /&gt;
        driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s %x:%x - connected ***\n&amp;quot;, driver_names[i], drivers[i]-&amp;gt;idVendor(), drivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = drivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
&lt;br /&gt;
        if (drivers[i] == &amp;amp;bluet) {&lt;br /&gt;
          const uint8_t *bdaddr = bluet.myBDAddr();&lt;br /&gt;
          // remember it...&lt;br /&gt;
          Serial.printf(&amp;quot;  BDADDR: %x:%x:%x:%x:%x:%x\n&amp;quot;, bdaddr[0], bdaddr[1], bdaddr[2], bdaddr[3], bdaddr[4], bdaddr[5]);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_HIDDEVICES; i++) {&lt;br /&gt;
    if (*hiddrivers[i] != hid_driver_active[i]) {&lt;br /&gt;
      if (hid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s - disconnected ***\n&amp;quot;, hid_driver_names[i]);&lt;br /&gt;
        hid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s %x:%x - connected ***\n&amp;quot;, hid_driver_names[i], hiddrivers[i]-&amp;gt;idVendor(), hiddrivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        hid_driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = hiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  // Then Bluetooth devices&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_BTHIDDEVICES; i++) {&lt;br /&gt;
    if (*bthiddrivers[i] != bthid_driver_active[i]) {&lt;br /&gt;
      if (bthid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - disconnected ***\n&amp;quot;, bthid_driver_names[i]);&lt;br /&gt;
        bthid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - connected ***\n&amp;quot;, bthid_driver_names[i]); Serial.flush();&lt;br /&gt;
        bthid_driver_active[i] = true;&lt;br /&gt;
        #if 0&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = bthiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
    #endif  &lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Known Issues:'''&lt;br /&gt;
* Version 1.1 of the board leaves TUSB2046's reset line floating.  This can be remedied by connecting the TUSB2046's reset line to some other circuit, although this is error prone.&lt;br /&gt;
&lt;br /&gt;
== Power ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_power_circuit.png|thumb|Teensy 4.1 Computer power circuitry|500px]]&lt;br /&gt;
&lt;br /&gt;
The computer's power supply accepts input from four primary sources:&lt;br /&gt;
* Barrel jack DC input, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Screw terminal block, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Teensy 4.1 micro USB port, 5v input, current limit depends on USB power source&lt;br /&gt;
* Expansion ports&lt;br /&gt;
** Direct to 5v line, only limited by source current.  This allows one to surpass the typical 2A limit on the 5v line.&lt;br /&gt;
** Direct to VIN, accepts 8v to 20v.  5v line limited to 2A.&lt;br /&gt;
** Direct to 3.3v line, only limited by source current.  This can be used to relieve stress from the Teensy 4.1's 3.3v regulator, if needed.&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_audio_power.png|thumb|Teensy 4.1 Computer audio power circuits|800px]]&lt;br /&gt;
There are a few different power planes on the board:&lt;br /&gt;
* VIN, 8v to 20v input&lt;br /&gt;
** Feeds power to system 5v and audio 5v&lt;br /&gt;
** Audio 5v will come from system 5v if VIN is not provided&lt;br /&gt;
* System 5v line&lt;br /&gt;
** Provides power to the system 3.3v line and the audio 3.3v line&lt;br /&gt;
** If coming from VIN, this line is provided by a 2A switching regulator, which may have some noise.&lt;br /&gt;
** Is fed through PMOS power switches to downstream USB devices, functions as VUSB.&lt;br /&gt;
* System 3.3v line&lt;br /&gt;
** Provides power to the Teensy 4.1 and the TUSB&lt;br /&gt;
** Provided by the Teensy 4.1's internal 3.3v regulator from the system 5v line&lt;br /&gt;
* System Ground&lt;br /&gt;
** Connected to all circutry, with the exception of some of the audio circutry, which uses the audio ground instead&lt;br /&gt;
* Audio 5v line&lt;br /&gt;
** Provided by either a 5v linear regulator from VIN, or from the system 5v line if VIN is not provided&lt;br /&gt;
** Powers the OPA1688 audio amplifier&lt;br /&gt;
** Is not broken out to the rest of the board&lt;br /&gt;
* Audio 3.3v line&lt;br /&gt;
** Regulated by a linear regulator, getting input from the system 5v line through a ferrite bead&lt;br /&gt;
** Powers the PCM5102 audio ADC&lt;br /&gt;
** Is not broken out elsewhere on the board&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** Connected to System Ground through a ferrite bead&lt;br /&gt;
** Connected to audio circutry&lt;br /&gt;
** Provides the ground used for the 3.5mm audio jack&lt;br /&gt;
** Not broken out elsewhere on the board&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When providing power to the board, a few things should be kept in mind.  First, for high quality audio, either VIN must be provided, or the system 5v line must be clean.  The latter is generally much harder to do, so it is recommended to provide VIN if possible.  For a clean system 5v line, generally it is to provide the system 5v line though a linear regulator instead.  Adding more ceramic capacitors to the system 5v line can help as well.  If providing power from an exteral board through an expansion connector, providing power to both the 5v and VIN lines can boost the maximum current of the system 5v line above 2A.  If more  than 2A is needed, for example for more current-hungry USB devices, then this may be desirable.  Finally, there is a limit on the safe capacitence to be connected to the system 5v line for the 2A switching regulator to work normally.  It is unadvisable to connect more than 800uF of external capacitence to the system 5v line, as it may interfere with that regulator.  Although, there really usually is no reason to connect to much capacitence to that line.&lt;br /&gt;
&lt;br /&gt;
If providing power to the system 5v line from an external source and the Teensy 4.1's USB port simultaneously, it is a good idea to include an ideal diode or similar circuit to prevent backflow from the USB port into your regulator, or vice versa, depending on your regulators characteristics.  For this purpose LM66100 ICs can be used, as are used to protect the onboard switching regulator from the Teensy 4.1's USB port.&lt;br /&gt;
&lt;br /&gt;
'''Capacitence by Power Plane:'''&lt;br /&gt;
&lt;br /&gt;
* VIN:&lt;br /&gt;
** 22uF electrolytic&lt;br /&gt;
** 4.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 26.7uF total&lt;br /&gt;
* System 5v&lt;br /&gt;
** 2.2uF on Teensy 4.1, ceramic&lt;br /&gt;
** 10uF onboard, ceramic&lt;br /&gt;
** Specific to onboard switching regulator: 22uF electrolytic&lt;br /&gt;
** 12.2uF total when external 5v is provided (Teensy 4.1 micro USB, expansion slots directly to 5v rail)&lt;br /&gt;
** 34.2uF total when onboard regulator is used&lt;br /&gt;
* System 3.3v&lt;br /&gt;
** 7.13uF from Teensy 4.1, ceramic&lt;br /&gt;
** 0.3uF onboard for decoupling, ceramic&lt;br /&gt;
** 7.43uF total&lt;br /&gt;
* System Ground&lt;br /&gt;
** 10.3uF ceramic&lt;br /&gt;
** 14.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 44uF electrolytic&lt;br /&gt;
** Approximately 25.13uF on Teensy 4.1, ceramic&lt;br /&gt;
** 94.13uF total&lt;br /&gt;
* Audio 5v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio 3.3v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** 27.2uF ceramic&lt;br /&gt;
** Connected to system ground through ferrite bead - technically connected to those capacitors as well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Expansion Ports ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_expansion_ports.png|thumb|Expansion port layout, not including the card connector port|800px]]&lt;br /&gt;
[[File:Teensy4.1_computer_external_headers_1.png|thumb|Expansion port schematics, excluding the card connector|800px]]&lt;br /&gt;
&lt;br /&gt;
There are three large expansion ports on the Teensy 4.1 computer board, and 5 smaller ports.  Here is a list, describing what each one breaks out:&lt;br /&gt;
* Largest expansion port on the back of the board (J11)&lt;br /&gt;
** Designed for large expansions to the computer system&lt;br /&gt;
** Exposes all power pins, including VIN, 5v, 3.3v, GND, and even the Teensy's VBAT line&lt;br /&gt;
** Numerous Teensy 4.1 pins: 0, 1, 11, 12, 13, 18, 19, 28, 29, 40, 41&lt;br /&gt;
*** UART1&lt;br /&gt;
*** UART7&lt;br /&gt;
*** SPI0&lt;br /&gt;
*** I2C0&lt;br /&gt;
*** Analog inputs: A4, A5, A16, A17&lt;br /&gt;
*** CRX2 and CTX2&lt;br /&gt;
** '''3.3v-compatible''' 8-bit parallel bus&lt;br /&gt;
*** Particularly useful for LCD displays and LCD display controllers, or any other parallel peripheral, for very high speeds&lt;br /&gt;
* Right-side expansion port (J12 - reference not shown on board revision 1.1)&lt;br /&gt;
** Designed for ATX-compatible expansions to the board&lt;br /&gt;
** Exposes 5v and 3.3v lines&lt;br /&gt;
** '''3.3v-compatible''' 8-bit parallel bus&lt;br /&gt;
** Several Teensy 4.1 pins: 6, 7, 8, 9, 18, 19, 32&lt;br /&gt;
*** UART2&lt;br /&gt;
*** I2C0&lt;br /&gt;
*** Analog inputs: A4, A5&lt;br /&gt;
* Card Expansion Port&lt;br /&gt;
** Designed to provide a card slot to fit in an ATX case.  The card may provide IO to the front.&lt;br /&gt;
** Provides a '''5v-compatible''' 8-bit parallel port.  The pins are shared with the other parallel ports.&lt;br /&gt;
** The direction of all data pins are controlled by the control lines.  '''Cannot use data line as input and output at the same time'''&lt;br /&gt;
*** Controlled by write, read, and select lines.  Select pin is pin 9, pulling it low will enable communication.&lt;br /&gt;
** Provides only a 5v line.&lt;br /&gt;
* I2S Expansion Port (J10)&lt;br /&gt;
** Provides an alternative to the 3.5mm audio output, and allows one to attach a microphone&lt;br /&gt;
** Breaks out I2S port 2&lt;br /&gt;
** Can be used to add a microphone alongside use of the 3.5mm audio jack&lt;br /&gt;
** Provides all power rails: VIN, 5v and 3.3v.&lt;br /&gt;
** Breaks out pins 16 and 17&lt;br /&gt;
*** I2C1&lt;br /&gt;
*** Analog pins A2 and A3&lt;br /&gt;
*** UART4&lt;br /&gt;
* SPI0 Port (J9)&lt;br /&gt;
** Provided for SPI devices.  Designed so that a card can be inserted to provide IO to the front of the board.&lt;br /&gt;
** Breaks out pins 10-13&lt;br /&gt;
** Provides 3.3v, but not 5v&lt;br /&gt;
** Breaks out SPI0&lt;br /&gt;
* UART6 Port&lt;br /&gt;
** This is a small port breaking out pins 24 and 25&lt;br /&gt;
*** UART6&lt;br /&gt;
*** Analog pins A10 and A11&lt;br /&gt;
*** I2C2&lt;br /&gt;
** The port is designed so another board can provide IO to the front.&lt;br /&gt;
** The pinout is designed to be compatible with Sparkfun devices and serial pinouts&lt;br /&gt;
** Can be used also as an additional I2C port, and has the necessary 4.7k resistors connected&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
* I2C1 Port&lt;br /&gt;
** This is a small port breaking out pins 16 and 17&lt;br /&gt;
*** UART4&lt;br /&gt;
*** Analog pins A2 and A3&lt;br /&gt;
*** I2C1&lt;br /&gt;
** Has the necessary 4.7k resistors connected&lt;br /&gt;
** The port is designed for internal devices, and is not designed to provide front-facing IO.&lt;br /&gt;
** Can be used also as an additional UART port&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
* UART3 Port&lt;br /&gt;
** This is a small port breaking out pins 14 and 15&lt;br /&gt;
*** UART3&lt;br /&gt;
*** Analog pins A0 and A1&lt;br /&gt;
** The port is designed for internal devices, and is not designed to provide front-facing IO.&lt;br /&gt;
** The pinout is designed to be compatible with Sparkfun devices and serial pinouts&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
&lt;br /&gt;
=== About the Parallel Port ===&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_card_connector_diagram.png|thumb|Card Connector circuit diagram and pinout|500px]]&lt;br /&gt;
&lt;br /&gt;
All three ports that provide parallel share the parallel pins WR, RD, and WAIT, as well as the busses ADDR and DATA.  Each connected parallel device should use a separate select pin, however, with pin 9 being reserved as the card connector's select pin.  WR, RD, and WAIT are all active low, and it is expected that all select lines also be active low.  The address bus (ADDR, shown on the board as A0, A1, and A2) has three pins, allowing for three addresses per device.  A parallel port is important to have because of the high bandwidth it provides.  The Teensy can write this port extremely fast, thus allowing better framerates and draw times for connected graphics devices.  I imagine there are other uses for this port as well.&lt;br /&gt;
&lt;br /&gt;
As ADDR, WR, RD, and any select pins are all output-only from the Teensy, '''there is no level-shifting logic for them.'''  For the WAIT line, there is a simple resistor voltage divider to take the 5v from the card connector down to 3.3v.  There is only level-shifting logic provided for data lines on the card connector, so that 5v TTL devices and levels can be used with the card connector out of the box.&lt;br /&gt;
&lt;br /&gt;
The card connector was designed to interface with cards for the Z80 computer I previously built, and has a compatible pinout.&lt;br /&gt;
&lt;br /&gt;
== Mechanical Measurements ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1 desktop measurements.png|Board measurements|854px]]&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=66</id>
		<title>Teensy 4.1 Computer</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=66"/>
		<updated>2024-07-29T14:05:26Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Change size of mechanical measurements image&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Teensy 4.1 Computer is more or less described by its name: Its a carrier board for the Teensy 4.1 which provides four USB ports, an Ethernet port, Hi-Fi audio, and several convenient expansion ports.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
'''USB:'''&lt;br /&gt;
&lt;br /&gt;
* TUSB2046 USB Hub, providing 4 USB A ports&lt;br /&gt;
* Each port has a maximum of 1.5A output - the port will shutdown due to overcurrent above this&lt;br /&gt;
* Board power provides a maximum of 2A to the 5v supply&lt;br /&gt;
** one should be aware of this when considering the power used by USB devices&lt;br /&gt;
&lt;br /&gt;
'''Audio:'''&lt;br /&gt;
&lt;br /&gt;
* I2S control connected to Teensy I2S 2&lt;br /&gt;
* PCM5102 ADC for providing stereo sound&lt;br /&gt;
* OPA1688 for the amplifier&lt;br /&gt;
* 3.5mm audio jack, maximum 75mA per channel&lt;br /&gt;
* Separate ground, connected to main ground through a ferrite bead&lt;br /&gt;
* Separate 3.3v and 5v supplies from linear regulators to reduce noise&lt;br /&gt;
** Note: 5v line will come from main 5v rail if only powered from USB&lt;br /&gt;
&lt;br /&gt;
'''Power:'''&lt;br /&gt;
&lt;br /&gt;
* 2A 5v buck regulator (Exact part is the VR20S05)&lt;br /&gt;
* PMOS switches to prevent regulator backfeeding from USB and to control power flow (U16, U17, and U18 on the PCB)&lt;br /&gt;
* External VIN accepted from 8v to 20v&lt;br /&gt;
* Separate 5v and 3.3v supplies for audio circutry&lt;br /&gt;
* Barrel jack and screw terminal connections for VIN&lt;br /&gt;
&lt;br /&gt;
'''Expansion Ports'''&lt;br /&gt;
&lt;br /&gt;
* I2S, SPI, I2C, and Serial expansion ports&lt;br /&gt;
* 5v-capable 8-bit parallel bus port, with 3-bit address, WR, RD, select, and WAIT lines&lt;br /&gt;
* Two larger expansion ports, each providing a '''3.3v compatible''' 8-bit parallel bus, various protocols like I2C and SPI, power, and GPIO&lt;br /&gt;
&lt;br /&gt;
== The Audio Circuit ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy41_Computer_Audio_circuit.png|thumb|Teensy 4.1 Computer audio circuit|600px]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1 is connected to the audio circuit via the Teensy's I2S 2 port.  This connection goes to the PCM5102 ADC.  As is recommended in the PCM5102's datasheet, filtering is added to the output, however the filter used on this board is a bit stronger, using a 4.7nF capacitor instead of the 2.2nF capacitor recommended in the datasheet.  The filter cutoff is still far above human perception, at 72kHz, and should filter high frequency noise a bit better.  The connection to the OPA1688 is DC, not AC, coupled, but this is according to the recommendations in the OPA1688 datasheet.  The gain of the amplifier is configured to be 1, so the output voltage of the amp should match the ADC's output voltage, and be in the range of 0v to 3.3v.  A voltage divider is used to activate the PCM5102's soft mute function when the 5v line drops, which is designed to prevent any pops in the audio when the power is disconnected.&lt;br /&gt;
&lt;br /&gt;
The circuit was designed with audio quality, but not energy efficiency, in mind.  The circuit may sink as much as 400mA from VIN, and an additional 40mA from the main 5v line.  However, when the audio circuit is idle, it should be less than 50mA all together.  The 3.5mm audio jack can provide a maximum of 75mA per channel.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;Audio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;SD.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// GUItool: begin automatically generated code&lt;br /&gt;
AudioPlaySdWav           playSdWav1;     //xy=719,389&lt;br /&gt;
AudioOutputI2S2          i2s2_1;         //xy=892,388&lt;br /&gt;
AudioConnection          patchCord1(playSdWav1, 0, i2s2_1, 0);&lt;br /&gt;
AudioConnection          patchCord2(playSdWav1, 1, i2s2_1, 1);&lt;br /&gt;
// GUItool: end automatically generated code&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  AudioMemory(10);&lt;br /&gt;
&lt;br /&gt;
  if (!(SD.begin(BUILTIN_SDCARD))) {&lt;br /&gt;
    // stop here, but print a message repetitively&lt;br /&gt;
    while (1) {&lt;br /&gt;
      Serial.println(&amp;quot;Unable to access the SD card&amp;quot;);&lt;br /&gt;
      delay(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Serial.println(&amp;quot;Playing audio.wav...&amp;quot;);&lt;br /&gt;
  playSdWav1.play(&amp;quot;audio.wav&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // Do nothing forever&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== USB Ports ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_USB_schematic.png|thumb|Teensy 4.1 Computer USB circuit|600px]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1's builtin USB port is connected directly to a TUSB2046 IC, which is a USB hub and provides four downstream ports.  Each port has overcurrent protection, with a maximum allowed current of 1.5A.  There are also TVS diodes on all USB data lines on the downstream ports, provided by three TPD3E001DRLR ICs.  Other than that, each downstream USB port has two 15k pulldowns - one on each data line.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&lt;br /&gt;
The best example is found in the Mouse_KeyboardBT example from the USBHost_t36 library, which is included in the TeensyDuino package.  For convenience, I have included a stripped down version of it below:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;quot;USBHost_t36.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
USBHost myusb;&lt;br /&gt;
USBHub hub1(myusb);&lt;br /&gt;
USBHub hub2(myusb);&lt;br /&gt;
KeyboardController keyboard1(myusb);&lt;br /&gt;
KeyboardController keyboard2(myusb);&lt;br /&gt;
USBHIDParser hid1(myusb);&lt;br /&gt;
USBHIDParser hid2(myusb);&lt;br /&gt;
USBHIDParser hid3(myusb);&lt;br /&gt;
USBHIDParser hid4(myusb);&lt;br /&gt;
USBHIDParser hid5(myusb);&lt;br /&gt;
MouseController mouse1(myusb);&lt;br /&gt;
&lt;br /&gt;
BluetoothController bluet(myusb);   // version assumes it already was paired&lt;br /&gt;
&lt;br /&gt;
USBDriver *drivers[] = {&amp;amp;hub1, &amp;amp;hub2, &amp;amp;keyboard1, &amp;amp;keyboard2, &amp;amp;bluet, &amp;amp;hid1, &amp;amp;hid2, &amp;amp;hid3, &amp;amp;hid4, &amp;amp;hid5};&lt;br /&gt;
&lt;br /&gt;
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))&lt;br /&gt;
const char * driver_names[CNT_DEVICES] = {&amp;quot;Hub1&amp;quot;, &amp;quot;Hub2&amp;quot;, &amp;quot;KB1&amp;quot;, &amp;quot;KB2&amp;quot;, &amp;quot;Bluet&amp;quot;, &amp;quot;HID1&amp;quot; , &amp;quot;HID2&amp;quot;, &amp;quot;HID3&amp;quot;, &amp;quot;HID4&amp;quot;, &amp;quot;HID5&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool driver_active[CNT_DEVICES] = {false, false, false, false, false, false, false, false, false, false};&lt;br /&gt;
&lt;br /&gt;
// Lets also look at HID Input devices&lt;br /&gt;
USBHIDInput *hiddrivers[] = {&amp;amp;mouse1};&lt;br /&gt;
&lt;br /&gt;
#define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0]))&lt;br /&gt;
const char * hid_driver_names[CNT_HIDDEVICES] = {&amp;quot;Mouse1&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool hid_driver_active[CNT_HIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
BTHIDInput *bthiddrivers[] = {&amp;amp;keyboard1, &amp;amp;mouse1};&lt;br /&gt;
#define CNT_BTHIDDEVICES (sizeof(bthiddrivers)/sizeof(bthiddrivers[0]))&lt;br /&gt;
const char * bthid_driver_names[CNT_BTHIDDEVICES] = {&amp;quot;Keyboard(BT)&amp;quot;, &amp;quot;Mouse(BT)&amp;quot;};&lt;br /&gt;
bool bthid_driver_active[CNT_BTHIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
bool show_changed_only = false;&lt;br /&gt;
&lt;br /&gt;
uint8_t joystick_left_trigger_value = 0;&lt;br /&gt;
uint8_t joystick_right_trigger_value = 0;&lt;br /&gt;
uint64_t joystick_full_notify_mask = (uint64_t) - 1;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  while (!Serial); // wait for Arduino Serial Monitor&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  myusb.begin();&lt;br /&gt;
  keyboard1.attachPress(OnPress);&lt;br /&gt;
  keyboard2.attachPress(OnPress);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // check to see if the device list has changed:&lt;br /&gt;
  UpdateActiveDeviceInfo();&lt;br /&gt;
&lt;br /&gt;
  myusb.Task();&lt;br /&gt;
&lt;br /&gt;
  if (mouse1.available()) {&lt;br /&gt;
    Serial.print(&amp;quot;Mouse: buttons = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getButtons());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseX = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseX());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseY = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseY());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheel = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheel());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheelH = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheelH());&lt;br /&gt;
    Serial.println();&lt;br /&gt;
    mouse1.mouseDataClear();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void OnPress(int key)&lt;br /&gt;
{&lt;br /&gt;
  Serial.print(&amp;quot;key '&amp;quot;);&lt;br /&gt;
  switch (key) {&lt;br /&gt;
    case KEYD_UP       : Serial.print(&amp;quot;UP&amp;quot;); break;&lt;br /&gt;
    case KEYD_DOWN     : Serial.print(&amp;quot;DN&amp;quot;); break;&lt;br /&gt;
    case KEYD_LEFT     : Serial.print(&amp;quot;LEFT&amp;quot;); break;&lt;br /&gt;
    case KEYD_RIGHT    : Serial.print(&amp;quot;RIGHT&amp;quot;); break;&lt;br /&gt;
    case KEYD_INSERT   : Serial.print(&amp;quot;Ins&amp;quot;); break;&lt;br /&gt;
    case KEYD_DELETE   : Serial.print(&amp;quot;Del&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_UP  : Serial.print(&amp;quot;PUP&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_DOWN: Serial.print(&amp;quot;PDN&amp;quot;); break;&lt;br /&gt;
    case KEYD_HOME     : Serial.print(&amp;quot;HOME&amp;quot;); break;&lt;br /&gt;
    case KEYD_END      : Serial.print(&amp;quot;END&amp;quot;); break;&lt;br /&gt;
    case KEYD_F1       : Serial.print(&amp;quot;F1&amp;quot;); break;&lt;br /&gt;
    case KEYD_F2       : Serial.print(&amp;quot;F2&amp;quot;); break;&lt;br /&gt;
    case KEYD_F3       : Serial.print(&amp;quot;F3&amp;quot;); break;&lt;br /&gt;
    case KEYD_F4       : Serial.print(&amp;quot;F4&amp;quot;); break;&lt;br /&gt;
    case KEYD_F5       : Serial.print(&amp;quot;F5&amp;quot;); break;&lt;br /&gt;
    case KEYD_F6       : Serial.print(&amp;quot;F6&amp;quot;); break;&lt;br /&gt;
    case KEYD_F7       : Serial.print(&amp;quot;F7&amp;quot;); break;&lt;br /&gt;
    case KEYD_F8       : Serial.print(&amp;quot;F8&amp;quot;); break;&lt;br /&gt;
    case KEYD_F9       : Serial.print(&amp;quot;F9&amp;quot;); break;&lt;br /&gt;
    case KEYD_F10      : Serial.print(&amp;quot;F10&amp;quot;); break;&lt;br /&gt;
    case KEYD_F11      : Serial.print(&amp;quot;F11&amp;quot;); break;&lt;br /&gt;
    case KEYD_F12      : Serial.print(&amp;quot;F12&amp;quot;); break;&lt;br /&gt;
    default: Serial.print((char)key); break;&lt;br /&gt;
  }&lt;br /&gt;
  Serial.print(&amp;quot;'  &amp;quot;);&lt;br /&gt;
  Serial.println(key);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//=============================================================================&lt;br /&gt;
// UpdateActiveDeviceInfo&lt;br /&gt;
//=============================================================================&lt;br /&gt;
void UpdateActiveDeviceInfo() {&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_DEVICES; i++) {&lt;br /&gt;
    if (*drivers[i] != driver_active[i]) {&lt;br /&gt;
      if (driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s - disconnected ***\n&amp;quot;, driver_names[i]);&lt;br /&gt;
        driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s %x:%x - connected ***\n&amp;quot;, driver_names[i], drivers[i]-&amp;gt;idVendor(), drivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = drivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
&lt;br /&gt;
        if (drivers[i] == &amp;amp;bluet) {&lt;br /&gt;
          const uint8_t *bdaddr = bluet.myBDAddr();&lt;br /&gt;
          // remember it...&lt;br /&gt;
          Serial.printf(&amp;quot;  BDADDR: %x:%x:%x:%x:%x:%x\n&amp;quot;, bdaddr[0], bdaddr[1], bdaddr[2], bdaddr[3], bdaddr[4], bdaddr[5]);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_HIDDEVICES; i++) {&lt;br /&gt;
    if (*hiddrivers[i] != hid_driver_active[i]) {&lt;br /&gt;
      if (hid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s - disconnected ***\n&amp;quot;, hid_driver_names[i]);&lt;br /&gt;
        hid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s %x:%x - connected ***\n&amp;quot;, hid_driver_names[i], hiddrivers[i]-&amp;gt;idVendor(), hiddrivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        hid_driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = hiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  // Then Bluetooth devices&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_BTHIDDEVICES; i++) {&lt;br /&gt;
    if (*bthiddrivers[i] != bthid_driver_active[i]) {&lt;br /&gt;
      if (bthid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - disconnected ***\n&amp;quot;, bthid_driver_names[i]);&lt;br /&gt;
        bthid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - connected ***\n&amp;quot;, bthid_driver_names[i]); Serial.flush();&lt;br /&gt;
        bthid_driver_active[i] = true;&lt;br /&gt;
        #if 0&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = bthiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
    #endif  &lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Known Issues:'''&lt;br /&gt;
* Version 1.1 of the board leaves TUSB2046's reset line floating.  This can be remedied by connecting the TUSB2046's reset line to some other circuit, although this is error prone.&lt;br /&gt;
&lt;br /&gt;
== Power ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_power_circuit.png|thumb|Teensy 4.1 Computer power circuitry|500px]]&lt;br /&gt;
&lt;br /&gt;
The computer's power supply accepts input from four primary sources:&lt;br /&gt;
* Barrel jack DC input, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Screw terminal block, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Teensy 4.1 micro USB port, 5v input, current limit depends on USB power source&lt;br /&gt;
* Expansion ports&lt;br /&gt;
** Direct to 5v line, only limited by source current.  This allows one to surpass the typical 2A limit on the 5v line.&lt;br /&gt;
** Direct to VIN, accepts 8v to 20v.  5v line limited to 2A.&lt;br /&gt;
** Direct to 3.3v line, only limited by source current.  This can be used to relieve stress from the Teensy 4.1's 3.3v regulator, if needed.&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_audio_power.png|thumb|Teensy 4.1 Computer audio power circuits|800px]]&lt;br /&gt;
There are a few different power planes on the board:&lt;br /&gt;
* VIN, 8v to 20v input&lt;br /&gt;
** Feeds power to system 5v and audio 5v&lt;br /&gt;
** Audio 5v will come from system 5v if VIN is not provided&lt;br /&gt;
* System 5v line&lt;br /&gt;
** Provides power to the system 3.3v line and the audio 3.3v line&lt;br /&gt;
** If coming from VIN, this line is provided by a 2A switching regulator, which may have some noise.&lt;br /&gt;
** Is fed through PMOS power switches to downstream USB devices, functions as VUSB.&lt;br /&gt;
* System 3.3v line&lt;br /&gt;
** Provides power to the Teensy 4.1 and the TUSB&lt;br /&gt;
** Provided by the Teensy 4.1's internal 3.3v regulator from the system 5v line&lt;br /&gt;
* System Ground&lt;br /&gt;
** Connected to all circutry, with the exception of some of the audio circutry, which uses the audio ground instead&lt;br /&gt;
* Audio 5v line&lt;br /&gt;
** Provided by either a 5v linear regulator from VIN, or from the system 5v line if VIN is not provided&lt;br /&gt;
** Powers the OPA1688 audio amplifier&lt;br /&gt;
** Is not broken out to the rest of the board&lt;br /&gt;
* Audio 3.3v line&lt;br /&gt;
** Regulated by a linear regulator, getting input from the system 5v line through a ferrite bead&lt;br /&gt;
** Powers the PCM5102 audio ADC&lt;br /&gt;
** Is not broken out elsewhere on the board&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** Connected to System Ground through a ferrite bead&lt;br /&gt;
** Connected to audio circutry&lt;br /&gt;
** Provides the ground used for the 3.5mm audio jack&lt;br /&gt;
** Not broken out elsewhere on the board&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When providing power to the board, a few things should be kept in mind.  First, for high quality audio, either VIN must be provided, or the system 5v line must be clean.  The latter is generally much harder to do, so it is recommended to provide VIN if possible.  For a clean system 5v line, generally it is to provide the system 5v line though a linear regulator instead.  Adding more ceramic capacitors to the system 5v line can help as well.  If providing power from an exteral board through an expansion connector, providing power to both the 5v and VIN lines can boost the maximum current of the system 5v line above 2A.  If more  than 2A is needed, for example for more current-hungry USB devices, then this may be desirable.  Finally, there is a limit on the safe capacitence to be connected to the system 5v line for the 2A switching regulator to work normally.  It is unadvisable to connect more than 800uF of external capacitence to the system 5v line, as it may interfere with that regulator.  Although, there really usually is no reason to connect to much capacitence to that line.&lt;br /&gt;
&lt;br /&gt;
If providing power to the system 5v line from an external source and the Teensy 4.1's USB port simultaneously, it is a good idea to include an ideal diode or similar circuit to prevent backflow from the USB port into your regulator, or vice versa, depending on your regulators characteristics.  For this purpose LM66100 ICs can be used, as are used to protect the onboard switching regulator from the Teensy 4.1's USB port.&lt;br /&gt;
&lt;br /&gt;
'''Capacitence by Power Plane:'''&lt;br /&gt;
&lt;br /&gt;
* VIN:&lt;br /&gt;
** 22uF electrolytic&lt;br /&gt;
** 4.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 26.7uF total&lt;br /&gt;
* System 5v&lt;br /&gt;
** 2.2uF on Teensy 4.1, ceramic&lt;br /&gt;
** 10uF onboard, ceramic&lt;br /&gt;
** Specific to onboard switching regulator: 22uF electrolytic&lt;br /&gt;
** 12.2uF total when external 5v is provided (Teensy 4.1 micro USB, expansion slots directly to 5v rail)&lt;br /&gt;
** 34.2uF total when onboard regulator is used&lt;br /&gt;
* System 3.3v&lt;br /&gt;
** 7.13uF from Teensy 4.1, ceramic&lt;br /&gt;
** 0.3uF onboard for decoupling, ceramic&lt;br /&gt;
** 7.43uF total&lt;br /&gt;
* System Ground&lt;br /&gt;
** 10.3uF ceramic&lt;br /&gt;
** 14.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 44uF electrolytic&lt;br /&gt;
** Approximately 25.13uF on Teensy 4.1, ceramic&lt;br /&gt;
** 94.13uF total&lt;br /&gt;
* Audio 5v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio 3.3v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** 27.2uF ceramic&lt;br /&gt;
** Connected to system ground through ferrite bead - technically connected to those capacitors as well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Expansion Ports ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_expansion_ports.png|thumb|Expansion port layout, not including the card connector port|800px]]&lt;br /&gt;
[[File:Teensy4.1_computer_external_headers_1.png|thumb|Expansion port schematics, excluding the card connector|800px]]&lt;br /&gt;
&lt;br /&gt;
There are three large expansion ports on the Teensy 4.1 computer board, and 5 smaller ports.  Here is a list, describing what each one breaks out:&lt;br /&gt;
* Largest expansion port on the back of the board (J11)&lt;br /&gt;
** Designed for large expansions to the computer system&lt;br /&gt;
** Exposes all power pins, including VIN, 5v, 3.3v, GND, and even the Teensy's VBAT line&lt;br /&gt;
** Numerous Teensy 4.1 pins: 0, 1, 11, 12, 13, 18, 19, 28, 29, 40, 41&lt;br /&gt;
*** UART1&lt;br /&gt;
*** UART7&lt;br /&gt;
*** SPI0&lt;br /&gt;
*** I2C0&lt;br /&gt;
*** Analog inputs: A4, A5, A16, A17&lt;br /&gt;
*** CRX2 and CTX2&lt;br /&gt;
** '''3.3v-compatible''' 8-bit parallel bus&lt;br /&gt;
*** Particularly useful for LCD displays and LCD display controllers, or any other parallel peripheral, for very high speeds&lt;br /&gt;
* Right-side expansion port (J12 - reference not shown on board revision 1.1)&lt;br /&gt;
** Designed for ATX-compatible expansions to the board&lt;br /&gt;
** Exposes 5v and 3.3v lines&lt;br /&gt;
** '''3.3v-compatible''' 8-bit parallel bus&lt;br /&gt;
** Several Teensy 4.1 pins: 6, 7, 8, 9, 18, 19, 32&lt;br /&gt;
*** UART2&lt;br /&gt;
*** I2C0&lt;br /&gt;
*** Analog inputs: A4, A5&lt;br /&gt;
* Card Expansion Port&lt;br /&gt;
** Designed to provide a card slot to fit in an ATX case.  The card may provide IO to the front.&lt;br /&gt;
** Provides a '''5v-compatible''' 8-bit parallel port.  The pins are shared with the other parallel ports.&lt;br /&gt;
** The direction of all data pins are controlled by the control lines.  '''Cannot use data line as input and output at the same time'''&lt;br /&gt;
*** Controlled by write, read, and select lines.  Select pin is pin 9, pulling it low will enable communication.&lt;br /&gt;
** Provides only a 5v line.&lt;br /&gt;
* I2S Expansion Port (J10)&lt;br /&gt;
** Provides an alternative to the 3.5mm audio output, and allows one to attach a microphone&lt;br /&gt;
** Breaks out I2S port 2&lt;br /&gt;
** Can be used to add a microphone alongside use of the 3.5mm audio jack&lt;br /&gt;
** Provides all power rails: VIN, 5v and 3.3v.&lt;br /&gt;
** Breaks out pins 16 and 17&lt;br /&gt;
*** I2C1&lt;br /&gt;
*** Analog pins A2 and A3&lt;br /&gt;
*** UART4&lt;br /&gt;
* SPI0 Port (J9)&lt;br /&gt;
** Provided for SPI devices.  Designed so that a card can be inserted to provide IO to the front of the board.&lt;br /&gt;
** Breaks out pins 10-13&lt;br /&gt;
** Provides 3.3v, but not 5v&lt;br /&gt;
** Breaks out SPI0&lt;br /&gt;
* UART6 Port&lt;br /&gt;
** This is a small port breaking out pins 24 and 25&lt;br /&gt;
*** UART6&lt;br /&gt;
*** Analog pins A10 and A11&lt;br /&gt;
*** I2C2&lt;br /&gt;
** The port is designed so another board can provide IO to the front.&lt;br /&gt;
** The pinout is designed to be compatible with Sparkfun devices and serial pinouts&lt;br /&gt;
** Can be used also as an additional I2C port, and has the necessary 4.7k resistors connected&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
* I2C1 Port&lt;br /&gt;
** This is a small port breaking out pins 16 and 17&lt;br /&gt;
*** UART4&lt;br /&gt;
*** Analog pins A2 and A3&lt;br /&gt;
*** I2C1&lt;br /&gt;
** Has the necessary 4.7k resistors connected&lt;br /&gt;
** The port is designed for internal devices, and is not designed to provide front-facing IO.&lt;br /&gt;
** Can be used also as an additional UART port&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
* UART3 Port&lt;br /&gt;
** This is a small port breaking out pins 14 and 15&lt;br /&gt;
*** UART3&lt;br /&gt;
*** Analog pins A0 and A1&lt;br /&gt;
** The port is designed for internal devices, and is not designed to provide front-facing IO.&lt;br /&gt;
** The pinout is designed to be compatible with Sparkfun devices and serial pinouts&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
&lt;br /&gt;
=== About the Parallel Port ===&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_card_connector_diagram.png|thumb|Card Connector circuit diagram and pinout|500px]]&lt;br /&gt;
&lt;br /&gt;
All three ports that provide parallel share the parallel pins WR, RD, and WAIT, as well as the busses ADDR and DATA.  Each connected parallel device should use a separate select pin, however, with pin 9 being reserved as the card connector's select pin.  WR, RD, and WAIT are all active low, and it is expected that all select lines also be active low.  The address bus (ADDR, shown on the board as A0, A1, and A2) has three pins, allowing for three addresses per device.  A parallel port is important to have because of the high bandwidth it provides.  The Teensy can write this port extremely fast, thus allowing better framerates and draw times for connected graphics devices.  I imagine there are other uses for this port as well.&lt;br /&gt;
&lt;br /&gt;
As ADDR, WR, RD, and any select pins are all output-only from the Teensy, '''there is no level-shifting logic for them.'''  For the WAIT line, there is a simple resistor voltage divider to take the 5v from the card connector down to 3.3v.  There is only level-shifting logic provided for data lines on the card connector, so that 5v TTL devices and levels can be used with the card connector out of the box.&lt;br /&gt;
&lt;br /&gt;
The card connector was designed to interface with cards for the Z80 computer I previously built, and has a compatible pinout.&lt;br /&gt;
&lt;br /&gt;
=== Mechanical Measurements ===&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1 desktop measurements.png|Board measurements|854px]]&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=65</id>
		<title>Teensy 4.1 Computer</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=65"/>
		<updated>2024-07-29T14:05:02Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Change size of mechanical measurements image&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Teensy 4.1 Computer is more or less described by its name: Its a carrier board for the Teensy 4.1 which provides four USB ports, an Ethernet port, Hi-Fi audio, and several convenient expansion ports.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
'''USB:'''&lt;br /&gt;
&lt;br /&gt;
* TUSB2046 USB Hub, providing 4 USB A ports&lt;br /&gt;
* Each port has a maximum of 1.5A output - the port will shutdown due to overcurrent above this&lt;br /&gt;
* Board power provides a maximum of 2A to the 5v supply&lt;br /&gt;
** one should be aware of this when considering the power used by USB devices&lt;br /&gt;
&lt;br /&gt;
'''Audio:'''&lt;br /&gt;
&lt;br /&gt;
* I2S control connected to Teensy I2S 2&lt;br /&gt;
* PCM5102 ADC for providing stereo sound&lt;br /&gt;
* OPA1688 for the amplifier&lt;br /&gt;
* 3.5mm audio jack, maximum 75mA per channel&lt;br /&gt;
* Separate ground, connected to main ground through a ferrite bead&lt;br /&gt;
* Separate 3.3v and 5v supplies from linear regulators to reduce noise&lt;br /&gt;
** Note: 5v line will come from main 5v rail if only powered from USB&lt;br /&gt;
&lt;br /&gt;
'''Power:'''&lt;br /&gt;
&lt;br /&gt;
* 2A 5v buck regulator (Exact part is the VR20S05)&lt;br /&gt;
* PMOS switches to prevent regulator backfeeding from USB and to control power flow (U16, U17, and U18 on the PCB)&lt;br /&gt;
* External VIN accepted from 8v to 20v&lt;br /&gt;
* Separate 5v and 3.3v supplies for audio circutry&lt;br /&gt;
* Barrel jack and screw terminal connections for VIN&lt;br /&gt;
&lt;br /&gt;
'''Expansion Ports'''&lt;br /&gt;
&lt;br /&gt;
* I2S, SPI, I2C, and Serial expansion ports&lt;br /&gt;
* 5v-capable 8-bit parallel bus port, with 3-bit address, WR, RD, select, and WAIT lines&lt;br /&gt;
* Two larger expansion ports, each providing a '''3.3v compatible''' 8-bit parallel bus, various protocols like I2C and SPI, power, and GPIO&lt;br /&gt;
&lt;br /&gt;
== The Audio Circuit ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy41_Computer_Audio_circuit.png|thumb|Teensy 4.1 Computer audio circuit|600px]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1 is connected to the audio circuit via the Teensy's I2S 2 port.  This connection goes to the PCM5102 ADC.  As is recommended in the PCM5102's datasheet, filtering is added to the output, however the filter used on this board is a bit stronger, using a 4.7nF capacitor instead of the 2.2nF capacitor recommended in the datasheet.  The filter cutoff is still far above human perception, at 72kHz, and should filter high frequency noise a bit better.  The connection to the OPA1688 is DC, not AC, coupled, but this is according to the recommendations in the OPA1688 datasheet.  The gain of the amplifier is configured to be 1, so the output voltage of the amp should match the ADC's output voltage, and be in the range of 0v to 3.3v.  A voltage divider is used to activate the PCM5102's soft mute function when the 5v line drops, which is designed to prevent any pops in the audio when the power is disconnected.&lt;br /&gt;
&lt;br /&gt;
The circuit was designed with audio quality, but not energy efficiency, in mind.  The circuit may sink as much as 400mA from VIN, and an additional 40mA from the main 5v line.  However, when the audio circuit is idle, it should be less than 50mA all together.  The 3.5mm audio jack can provide a maximum of 75mA per channel.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;Audio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;SD.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// GUItool: begin automatically generated code&lt;br /&gt;
AudioPlaySdWav           playSdWav1;     //xy=719,389&lt;br /&gt;
AudioOutputI2S2          i2s2_1;         //xy=892,388&lt;br /&gt;
AudioConnection          patchCord1(playSdWav1, 0, i2s2_1, 0);&lt;br /&gt;
AudioConnection          patchCord2(playSdWav1, 1, i2s2_1, 1);&lt;br /&gt;
// GUItool: end automatically generated code&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  AudioMemory(10);&lt;br /&gt;
&lt;br /&gt;
  if (!(SD.begin(BUILTIN_SDCARD))) {&lt;br /&gt;
    // stop here, but print a message repetitively&lt;br /&gt;
    while (1) {&lt;br /&gt;
      Serial.println(&amp;quot;Unable to access the SD card&amp;quot;);&lt;br /&gt;
      delay(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Serial.println(&amp;quot;Playing audio.wav...&amp;quot;);&lt;br /&gt;
  playSdWav1.play(&amp;quot;audio.wav&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // Do nothing forever&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== USB Ports ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_USB_schematic.png|thumb|Teensy 4.1 Computer USB circuit|600px]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1's builtin USB port is connected directly to a TUSB2046 IC, which is a USB hub and provides four downstream ports.  Each port has overcurrent protection, with a maximum allowed current of 1.5A.  There are also TVS diodes on all USB data lines on the downstream ports, provided by three TPD3E001DRLR ICs.  Other than that, each downstream USB port has two 15k pulldowns - one on each data line.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&lt;br /&gt;
The best example is found in the Mouse_KeyboardBT example from the USBHost_t36 library, which is included in the TeensyDuino package.  For convenience, I have included a stripped down version of it below:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;quot;USBHost_t36.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
USBHost myusb;&lt;br /&gt;
USBHub hub1(myusb);&lt;br /&gt;
USBHub hub2(myusb);&lt;br /&gt;
KeyboardController keyboard1(myusb);&lt;br /&gt;
KeyboardController keyboard2(myusb);&lt;br /&gt;
USBHIDParser hid1(myusb);&lt;br /&gt;
USBHIDParser hid2(myusb);&lt;br /&gt;
USBHIDParser hid3(myusb);&lt;br /&gt;
USBHIDParser hid4(myusb);&lt;br /&gt;
USBHIDParser hid5(myusb);&lt;br /&gt;
MouseController mouse1(myusb);&lt;br /&gt;
&lt;br /&gt;
BluetoothController bluet(myusb);   // version assumes it already was paired&lt;br /&gt;
&lt;br /&gt;
USBDriver *drivers[] = {&amp;amp;hub1, &amp;amp;hub2, &amp;amp;keyboard1, &amp;amp;keyboard2, &amp;amp;bluet, &amp;amp;hid1, &amp;amp;hid2, &amp;amp;hid3, &amp;amp;hid4, &amp;amp;hid5};&lt;br /&gt;
&lt;br /&gt;
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))&lt;br /&gt;
const char * driver_names[CNT_DEVICES] = {&amp;quot;Hub1&amp;quot;, &amp;quot;Hub2&amp;quot;, &amp;quot;KB1&amp;quot;, &amp;quot;KB2&amp;quot;, &amp;quot;Bluet&amp;quot;, &amp;quot;HID1&amp;quot; , &amp;quot;HID2&amp;quot;, &amp;quot;HID3&amp;quot;, &amp;quot;HID4&amp;quot;, &amp;quot;HID5&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool driver_active[CNT_DEVICES] = {false, false, false, false, false, false, false, false, false, false};&lt;br /&gt;
&lt;br /&gt;
// Lets also look at HID Input devices&lt;br /&gt;
USBHIDInput *hiddrivers[] = {&amp;amp;mouse1};&lt;br /&gt;
&lt;br /&gt;
#define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0]))&lt;br /&gt;
const char * hid_driver_names[CNT_HIDDEVICES] = {&amp;quot;Mouse1&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool hid_driver_active[CNT_HIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
BTHIDInput *bthiddrivers[] = {&amp;amp;keyboard1, &amp;amp;mouse1};&lt;br /&gt;
#define CNT_BTHIDDEVICES (sizeof(bthiddrivers)/sizeof(bthiddrivers[0]))&lt;br /&gt;
const char * bthid_driver_names[CNT_BTHIDDEVICES] = {&amp;quot;Keyboard(BT)&amp;quot;, &amp;quot;Mouse(BT)&amp;quot;};&lt;br /&gt;
bool bthid_driver_active[CNT_BTHIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
bool show_changed_only = false;&lt;br /&gt;
&lt;br /&gt;
uint8_t joystick_left_trigger_value = 0;&lt;br /&gt;
uint8_t joystick_right_trigger_value = 0;&lt;br /&gt;
uint64_t joystick_full_notify_mask = (uint64_t) - 1;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  while (!Serial); // wait for Arduino Serial Monitor&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  myusb.begin();&lt;br /&gt;
  keyboard1.attachPress(OnPress);&lt;br /&gt;
  keyboard2.attachPress(OnPress);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // check to see if the device list has changed:&lt;br /&gt;
  UpdateActiveDeviceInfo();&lt;br /&gt;
&lt;br /&gt;
  myusb.Task();&lt;br /&gt;
&lt;br /&gt;
  if (mouse1.available()) {&lt;br /&gt;
    Serial.print(&amp;quot;Mouse: buttons = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getButtons());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseX = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseX());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseY = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseY());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheel = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheel());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheelH = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheelH());&lt;br /&gt;
    Serial.println();&lt;br /&gt;
    mouse1.mouseDataClear();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void OnPress(int key)&lt;br /&gt;
{&lt;br /&gt;
  Serial.print(&amp;quot;key '&amp;quot;);&lt;br /&gt;
  switch (key) {&lt;br /&gt;
    case KEYD_UP       : Serial.print(&amp;quot;UP&amp;quot;); break;&lt;br /&gt;
    case KEYD_DOWN     : Serial.print(&amp;quot;DN&amp;quot;); break;&lt;br /&gt;
    case KEYD_LEFT     : Serial.print(&amp;quot;LEFT&amp;quot;); break;&lt;br /&gt;
    case KEYD_RIGHT    : Serial.print(&amp;quot;RIGHT&amp;quot;); break;&lt;br /&gt;
    case KEYD_INSERT   : Serial.print(&amp;quot;Ins&amp;quot;); break;&lt;br /&gt;
    case KEYD_DELETE   : Serial.print(&amp;quot;Del&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_UP  : Serial.print(&amp;quot;PUP&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_DOWN: Serial.print(&amp;quot;PDN&amp;quot;); break;&lt;br /&gt;
    case KEYD_HOME     : Serial.print(&amp;quot;HOME&amp;quot;); break;&lt;br /&gt;
    case KEYD_END      : Serial.print(&amp;quot;END&amp;quot;); break;&lt;br /&gt;
    case KEYD_F1       : Serial.print(&amp;quot;F1&amp;quot;); break;&lt;br /&gt;
    case KEYD_F2       : Serial.print(&amp;quot;F2&amp;quot;); break;&lt;br /&gt;
    case KEYD_F3       : Serial.print(&amp;quot;F3&amp;quot;); break;&lt;br /&gt;
    case KEYD_F4       : Serial.print(&amp;quot;F4&amp;quot;); break;&lt;br /&gt;
    case KEYD_F5       : Serial.print(&amp;quot;F5&amp;quot;); break;&lt;br /&gt;
    case KEYD_F6       : Serial.print(&amp;quot;F6&amp;quot;); break;&lt;br /&gt;
    case KEYD_F7       : Serial.print(&amp;quot;F7&amp;quot;); break;&lt;br /&gt;
    case KEYD_F8       : Serial.print(&amp;quot;F8&amp;quot;); break;&lt;br /&gt;
    case KEYD_F9       : Serial.print(&amp;quot;F9&amp;quot;); break;&lt;br /&gt;
    case KEYD_F10      : Serial.print(&amp;quot;F10&amp;quot;); break;&lt;br /&gt;
    case KEYD_F11      : Serial.print(&amp;quot;F11&amp;quot;); break;&lt;br /&gt;
    case KEYD_F12      : Serial.print(&amp;quot;F12&amp;quot;); break;&lt;br /&gt;
    default: Serial.print((char)key); break;&lt;br /&gt;
  }&lt;br /&gt;
  Serial.print(&amp;quot;'  &amp;quot;);&lt;br /&gt;
  Serial.println(key);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//=============================================================================&lt;br /&gt;
// UpdateActiveDeviceInfo&lt;br /&gt;
//=============================================================================&lt;br /&gt;
void UpdateActiveDeviceInfo() {&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_DEVICES; i++) {&lt;br /&gt;
    if (*drivers[i] != driver_active[i]) {&lt;br /&gt;
      if (driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s - disconnected ***\n&amp;quot;, driver_names[i]);&lt;br /&gt;
        driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s %x:%x - connected ***\n&amp;quot;, driver_names[i], drivers[i]-&amp;gt;idVendor(), drivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = drivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
&lt;br /&gt;
        if (drivers[i] == &amp;amp;bluet) {&lt;br /&gt;
          const uint8_t *bdaddr = bluet.myBDAddr();&lt;br /&gt;
          // remember it...&lt;br /&gt;
          Serial.printf(&amp;quot;  BDADDR: %x:%x:%x:%x:%x:%x\n&amp;quot;, bdaddr[0], bdaddr[1], bdaddr[2], bdaddr[3], bdaddr[4], bdaddr[5]);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_HIDDEVICES; i++) {&lt;br /&gt;
    if (*hiddrivers[i] != hid_driver_active[i]) {&lt;br /&gt;
      if (hid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s - disconnected ***\n&amp;quot;, hid_driver_names[i]);&lt;br /&gt;
        hid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s %x:%x - connected ***\n&amp;quot;, hid_driver_names[i], hiddrivers[i]-&amp;gt;idVendor(), hiddrivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        hid_driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = hiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  // Then Bluetooth devices&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_BTHIDDEVICES; i++) {&lt;br /&gt;
    if (*bthiddrivers[i] != bthid_driver_active[i]) {&lt;br /&gt;
      if (bthid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - disconnected ***\n&amp;quot;, bthid_driver_names[i]);&lt;br /&gt;
        bthid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - connected ***\n&amp;quot;, bthid_driver_names[i]); Serial.flush();&lt;br /&gt;
        bthid_driver_active[i] = true;&lt;br /&gt;
        #if 0&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = bthiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
    #endif  &lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Known Issues:'''&lt;br /&gt;
* Version 1.1 of the board leaves TUSB2046's reset line floating.  This can be remedied by connecting the TUSB2046's reset line to some other circuit, although this is error prone.&lt;br /&gt;
&lt;br /&gt;
== Power ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_power_circuit.png|thumb|Teensy 4.1 Computer power circuitry|500px]]&lt;br /&gt;
&lt;br /&gt;
The computer's power supply accepts input from four primary sources:&lt;br /&gt;
* Barrel jack DC input, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Screw terminal block, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Teensy 4.1 micro USB port, 5v input, current limit depends on USB power source&lt;br /&gt;
* Expansion ports&lt;br /&gt;
** Direct to 5v line, only limited by source current.  This allows one to surpass the typical 2A limit on the 5v line.&lt;br /&gt;
** Direct to VIN, accepts 8v to 20v.  5v line limited to 2A.&lt;br /&gt;
** Direct to 3.3v line, only limited by source current.  This can be used to relieve stress from the Teensy 4.1's 3.3v regulator, if needed.&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_audio_power.png|thumb|Teensy 4.1 Computer audio power circuits|800px]]&lt;br /&gt;
There are a few different power planes on the board:&lt;br /&gt;
* VIN, 8v to 20v input&lt;br /&gt;
** Feeds power to system 5v and audio 5v&lt;br /&gt;
** Audio 5v will come from system 5v if VIN is not provided&lt;br /&gt;
* System 5v line&lt;br /&gt;
** Provides power to the system 3.3v line and the audio 3.3v line&lt;br /&gt;
** If coming from VIN, this line is provided by a 2A switching regulator, which may have some noise.&lt;br /&gt;
** Is fed through PMOS power switches to downstream USB devices, functions as VUSB.&lt;br /&gt;
* System 3.3v line&lt;br /&gt;
** Provides power to the Teensy 4.1 and the TUSB&lt;br /&gt;
** Provided by the Teensy 4.1's internal 3.3v regulator from the system 5v line&lt;br /&gt;
* System Ground&lt;br /&gt;
** Connected to all circutry, with the exception of some of the audio circutry, which uses the audio ground instead&lt;br /&gt;
* Audio 5v line&lt;br /&gt;
** Provided by either a 5v linear regulator from VIN, or from the system 5v line if VIN is not provided&lt;br /&gt;
** Powers the OPA1688 audio amplifier&lt;br /&gt;
** Is not broken out to the rest of the board&lt;br /&gt;
* Audio 3.3v line&lt;br /&gt;
** Regulated by a linear regulator, getting input from the system 5v line through a ferrite bead&lt;br /&gt;
** Powers the PCM5102 audio ADC&lt;br /&gt;
** Is not broken out elsewhere on the board&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** Connected to System Ground through a ferrite bead&lt;br /&gt;
** Connected to audio circutry&lt;br /&gt;
** Provides the ground used for the 3.5mm audio jack&lt;br /&gt;
** Not broken out elsewhere on the board&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When providing power to the board, a few things should be kept in mind.  First, for high quality audio, either VIN must be provided, or the system 5v line must be clean.  The latter is generally much harder to do, so it is recommended to provide VIN if possible.  For a clean system 5v line, generally it is to provide the system 5v line though a linear regulator instead.  Adding more ceramic capacitors to the system 5v line can help as well.  If providing power from an exteral board through an expansion connector, providing power to both the 5v and VIN lines can boost the maximum current of the system 5v line above 2A.  If more  than 2A is needed, for example for more current-hungry USB devices, then this may be desirable.  Finally, there is a limit on the safe capacitence to be connected to the system 5v line for the 2A switching regulator to work normally.  It is unadvisable to connect more than 800uF of external capacitence to the system 5v line, as it may interfere with that regulator.  Although, there really usually is no reason to connect to much capacitence to that line.&lt;br /&gt;
&lt;br /&gt;
If providing power to the system 5v line from an external source and the Teensy 4.1's USB port simultaneously, it is a good idea to include an ideal diode or similar circuit to prevent backflow from the USB port into your regulator, or vice versa, depending on your regulators characteristics.  For this purpose LM66100 ICs can be used, as are used to protect the onboard switching regulator from the Teensy 4.1's USB port.&lt;br /&gt;
&lt;br /&gt;
'''Capacitence by Power Plane:'''&lt;br /&gt;
&lt;br /&gt;
* VIN:&lt;br /&gt;
** 22uF electrolytic&lt;br /&gt;
** 4.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 26.7uF total&lt;br /&gt;
* System 5v&lt;br /&gt;
** 2.2uF on Teensy 4.1, ceramic&lt;br /&gt;
** 10uF onboard, ceramic&lt;br /&gt;
** Specific to onboard switching regulator: 22uF electrolytic&lt;br /&gt;
** 12.2uF total when external 5v is provided (Teensy 4.1 micro USB, expansion slots directly to 5v rail)&lt;br /&gt;
** 34.2uF total when onboard regulator is used&lt;br /&gt;
* System 3.3v&lt;br /&gt;
** 7.13uF from Teensy 4.1, ceramic&lt;br /&gt;
** 0.3uF onboard for decoupling, ceramic&lt;br /&gt;
** 7.43uF total&lt;br /&gt;
* System Ground&lt;br /&gt;
** 10.3uF ceramic&lt;br /&gt;
** 14.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 44uF electrolytic&lt;br /&gt;
** Approximately 25.13uF on Teensy 4.1, ceramic&lt;br /&gt;
** 94.13uF total&lt;br /&gt;
* Audio 5v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio 3.3v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** 27.2uF ceramic&lt;br /&gt;
** Connected to system ground through ferrite bead - technically connected to those capacitors as well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Expansion Ports ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_expansion_ports.png|thumb|Expansion port layout, not including the card connector port|800px]]&lt;br /&gt;
[[File:Teensy4.1_computer_external_headers_1.png|thumb|Expansion port schematics, excluding the card connector|800px]]&lt;br /&gt;
&lt;br /&gt;
There are three large expansion ports on the Teensy 4.1 computer board, and 5 smaller ports.  Here is a list, describing what each one breaks out:&lt;br /&gt;
* Largest expansion port on the back of the board (J11)&lt;br /&gt;
** Designed for large expansions to the computer system&lt;br /&gt;
** Exposes all power pins, including VIN, 5v, 3.3v, GND, and even the Teensy's VBAT line&lt;br /&gt;
** Numerous Teensy 4.1 pins: 0, 1, 11, 12, 13, 18, 19, 28, 29, 40, 41&lt;br /&gt;
*** UART1&lt;br /&gt;
*** UART7&lt;br /&gt;
*** SPI0&lt;br /&gt;
*** I2C0&lt;br /&gt;
*** Analog inputs: A4, A5, A16, A17&lt;br /&gt;
*** CRX2 and CTX2&lt;br /&gt;
** '''3.3v-compatible''' 8-bit parallel bus&lt;br /&gt;
*** Particularly useful for LCD displays and LCD display controllers, or any other parallel peripheral, for very high speeds&lt;br /&gt;
* Right-side expansion port (J12 - reference not shown on board revision 1.1)&lt;br /&gt;
** Designed for ATX-compatible expansions to the board&lt;br /&gt;
** Exposes 5v and 3.3v lines&lt;br /&gt;
** '''3.3v-compatible''' 8-bit parallel bus&lt;br /&gt;
** Several Teensy 4.1 pins: 6, 7, 8, 9, 18, 19, 32&lt;br /&gt;
*** UART2&lt;br /&gt;
*** I2C0&lt;br /&gt;
*** Analog inputs: A4, A5&lt;br /&gt;
* Card Expansion Port&lt;br /&gt;
** Designed to provide a card slot to fit in an ATX case.  The card may provide IO to the front.&lt;br /&gt;
** Provides a '''5v-compatible''' 8-bit parallel port.  The pins are shared with the other parallel ports.&lt;br /&gt;
** The direction of all data pins are controlled by the control lines.  '''Cannot use data line as input and output at the same time'''&lt;br /&gt;
*** Controlled by write, read, and select lines.  Select pin is pin 9, pulling it low will enable communication.&lt;br /&gt;
** Provides only a 5v line.&lt;br /&gt;
* I2S Expansion Port (J10)&lt;br /&gt;
** Provides an alternative to the 3.5mm audio output, and allows one to attach a microphone&lt;br /&gt;
** Breaks out I2S port 2&lt;br /&gt;
** Can be used to add a microphone alongside use of the 3.5mm audio jack&lt;br /&gt;
** Provides all power rails: VIN, 5v and 3.3v.&lt;br /&gt;
** Breaks out pins 16 and 17&lt;br /&gt;
*** I2C1&lt;br /&gt;
*** Analog pins A2 and A3&lt;br /&gt;
*** UART4&lt;br /&gt;
* SPI0 Port (J9)&lt;br /&gt;
** Provided for SPI devices.  Designed so that a card can be inserted to provide IO to the front of the board.&lt;br /&gt;
** Breaks out pins 10-13&lt;br /&gt;
** Provides 3.3v, but not 5v&lt;br /&gt;
** Breaks out SPI0&lt;br /&gt;
* UART6 Port&lt;br /&gt;
** This is a small port breaking out pins 24 and 25&lt;br /&gt;
*** UART6&lt;br /&gt;
*** Analog pins A10 and A11&lt;br /&gt;
*** I2C2&lt;br /&gt;
** The port is designed so another board can provide IO to the front.&lt;br /&gt;
** The pinout is designed to be compatible with Sparkfun devices and serial pinouts&lt;br /&gt;
** Can be used also as an additional I2C port, and has the necessary 4.7k resistors connected&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
* I2C1 Port&lt;br /&gt;
** This is a small port breaking out pins 16 and 17&lt;br /&gt;
*** UART4&lt;br /&gt;
*** Analog pins A2 and A3&lt;br /&gt;
*** I2C1&lt;br /&gt;
** Has the necessary 4.7k resistors connected&lt;br /&gt;
** The port is designed for internal devices, and is not designed to provide front-facing IO.&lt;br /&gt;
** Can be used also as an additional UART port&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
* UART3 Port&lt;br /&gt;
** This is a small port breaking out pins 14 and 15&lt;br /&gt;
*** UART3&lt;br /&gt;
*** Analog pins A0 and A1&lt;br /&gt;
** The port is designed for internal devices, and is not designed to provide front-facing IO.&lt;br /&gt;
** The pinout is designed to be compatible with Sparkfun devices and serial pinouts&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
&lt;br /&gt;
=== About the Parallel Port ===&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_card_connector_diagram.png|thumb|Card Connector circuit diagram and pinout|500px]]&lt;br /&gt;
&lt;br /&gt;
All three ports that provide parallel share the parallel pins WR, RD, and WAIT, as well as the busses ADDR and DATA.  Each connected parallel device should use a separate select pin, however, with pin 9 being reserved as the card connector's select pin.  WR, RD, and WAIT are all active low, and it is expected that all select lines also be active low.  The address bus (ADDR, shown on the board as A0, A1, and A2) has three pins, allowing for three addresses per device.  A parallel port is important to have because of the high bandwidth it provides.  The Teensy can write this port extremely fast, thus allowing better framerates and draw times for connected graphics devices.  I imagine there are other uses for this port as well.&lt;br /&gt;
&lt;br /&gt;
As ADDR, WR, RD, and any select pins are all output-only from the Teensy, '''there is no level-shifting logic for them.'''  For the WAIT line, there is a simple resistor voltage divider to take the 5v from the card connector down to 3.3v.  There is only level-shifting logic provided for data lines on the card connector, so that 5v TTL devices and levels can be used with the card connector out of the box.&lt;br /&gt;
&lt;br /&gt;
The card connector was designed to interface with cards for the Z80 computer I previously built, and has a compatible pinout.&lt;br /&gt;
&lt;br /&gt;
=== Mechanical Measurements ===&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1 desktop measurements.png|Board measurements|860px]]&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=64</id>
		<title>Teensy 4.1 Computer</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=64"/>
		<updated>2024-07-29T14:04:18Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Change size of mechanical measurements image&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Teensy 4.1 Computer is more or less described by its name: Its a carrier board for the Teensy 4.1 which provides four USB ports, an Ethernet port, Hi-Fi audio, and several convenient expansion ports.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
'''USB:'''&lt;br /&gt;
&lt;br /&gt;
* TUSB2046 USB Hub, providing 4 USB A ports&lt;br /&gt;
* Each port has a maximum of 1.5A output - the port will shutdown due to overcurrent above this&lt;br /&gt;
* Board power provides a maximum of 2A to the 5v supply&lt;br /&gt;
** one should be aware of this when considering the power used by USB devices&lt;br /&gt;
&lt;br /&gt;
'''Audio:'''&lt;br /&gt;
&lt;br /&gt;
* I2S control connected to Teensy I2S 2&lt;br /&gt;
* PCM5102 ADC for providing stereo sound&lt;br /&gt;
* OPA1688 for the amplifier&lt;br /&gt;
* 3.5mm audio jack, maximum 75mA per channel&lt;br /&gt;
* Separate ground, connected to main ground through a ferrite bead&lt;br /&gt;
* Separate 3.3v and 5v supplies from linear regulators to reduce noise&lt;br /&gt;
** Note: 5v line will come from main 5v rail if only powered from USB&lt;br /&gt;
&lt;br /&gt;
'''Power:'''&lt;br /&gt;
&lt;br /&gt;
* 2A 5v buck regulator (Exact part is the VR20S05)&lt;br /&gt;
* PMOS switches to prevent regulator backfeeding from USB and to control power flow (U16, U17, and U18 on the PCB)&lt;br /&gt;
* External VIN accepted from 8v to 20v&lt;br /&gt;
* Separate 5v and 3.3v supplies for audio circutry&lt;br /&gt;
* Barrel jack and screw terminal connections for VIN&lt;br /&gt;
&lt;br /&gt;
'''Expansion Ports'''&lt;br /&gt;
&lt;br /&gt;
* I2S, SPI, I2C, and Serial expansion ports&lt;br /&gt;
* 5v-capable 8-bit parallel bus port, with 3-bit address, WR, RD, select, and WAIT lines&lt;br /&gt;
* Two larger expansion ports, each providing a '''3.3v compatible''' 8-bit parallel bus, various protocols like I2C and SPI, power, and GPIO&lt;br /&gt;
&lt;br /&gt;
== The Audio Circuit ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy41_Computer_Audio_circuit.png|thumb|Teensy 4.1 Computer audio circuit|600px]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1 is connected to the audio circuit via the Teensy's I2S 2 port.  This connection goes to the PCM5102 ADC.  As is recommended in the PCM5102's datasheet, filtering is added to the output, however the filter used on this board is a bit stronger, using a 4.7nF capacitor instead of the 2.2nF capacitor recommended in the datasheet.  The filter cutoff is still far above human perception, at 72kHz, and should filter high frequency noise a bit better.  The connection to the OPA1688 is DC, not AC, coupled, but this is according to the recommendations in the OPA1688 datasheet.  The gain of the amplifier is configured to be 1, so the output voltage of the amp should match the ADC's output voltage, and be in the range of 0v to 3.3v.  A voltage divider is used to activate the PCM5102's soft mute function when the 5v line drops, which is designed to prevent any pops in the audio when the power is disconnected.&lt;br /&gt;
&lt;br /&gt;
The circuit was designed with audio quality, but not energy efficiency, in mind.  The circuit may sink as much as 400mA from VIN, and an additional 40mA from the main 5v line.  However, when the audio circuit is idle, it should be less than 50mA all together.  The 3.5mm audio jack can provide a maximum of 75mA per channel.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;Audio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;SD.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// GUItool: begin automatically generated code&lt;br /&gt;
AudioPlaySdWav           playSdWav1;     //xy=719,389&lt;br /&gt;
AudioOutputI2S2          i2s2_1;         //xy=892,388&lt;br /&gt;
AudioConnection          patchCord1(playSdWav1, 0, i2s2_1, 0);&lt;br /&gt;
AudioConnection          patchCord2(playSdWav1, 1, i2s2_1, 1);&lt;br /&gt;
// GUItool: end automatically generated code&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  AudioMemory(10);&lt;br /&gt;
&lt;br /&gt;
  if (!(SD.begin(BUILTIN_SDCARD))) {&lt;br /&gt;
    // stop here, but print a message repetitively&lt;br /&gt;
    while (1) {&lt;br /&gt;
      Serial.println(&amp;quot;Unable to access the SD card&amp;quot;);&lt;br /&gt;
      delay(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Serial.println(&amp;quot;Playing audio.wav...&amp;quot;);&lt;br /&gt;
  playSdWav1.play(&amp;quot;audio.wav&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // Do nothing forever&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== USB Ports ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_USB_schematic.png|thumb|Teensy 4.1 Computer USB circuit|600px]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1's builtin USB port is connected directly to a TUSB2046 IC, which is a USB hub and provides four downstream ports.  Each port has overcurrent protection, with a maximum allowed current of 1.5A.  There are also TVS diodes on all USB data lines on the downstream ports, provided by three TPD3E001DRLR ICs.  Other than that, each downstream USB port has two 15k pulldowns - one on each data line.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&lt;br /&gt;
The best example is found in the Mouse_KeyboardBT example from the USBHost_t36 library, which is included in the TeensyDuino package.  For convenience, I have included a stripped down version of it below:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;quot;USBHost_t36.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
USBHost myusb;&lt;br /&gt;
USBHub hub1(myusb);&lt;br /&gt;
USBHub hub2(myusb);&lt;br /&gt;
KeyboardController keyboard1(myusb);&lt;br /&gt;
KeyboardController keyboard2(myusb);&lt;br /&gt;
USBHIDParser hid1(myusb);&lt;br /&gt;
USBHIDParser hid2(myusb);&lt;br /&gt;
USBHIDParser hid3(myusb);&lt;br /&gt;
USBHIDParser hid4(myusb);&lt;br /&gt;
USBHIDParser hid5(myusb);&lt;br /&gt;
MouseController mouse1(myusb);&lt;br /&gt;
&lt;br /&gt;
BluetoothController bluet(myusb);   // version assumes it already was paired&lt;br /&gt;
&lt;br /&gt;
USBDriver *drivers[] = {&amp;amp;hub1, &amp;amp;hub2, &amp;amp;keyboard1, &amp;amp;keyboard2, &amp;amp;bluet, &amp;amp;hid1, &amp;amp;hid2, &amp;amp;hid3, &amp;amp;hid4, &amp;amp;hid5};&lt;br /&gt;
&lt;br /&gt;
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))&lt;br /&gt;
const char * driver_names[CNT_DEVICES] = {&amp;quot;Hub1&amp;quot;, &amp;quot;Hub2&amp;quot;, &amp;quot;KB1&amp;quot;, &amp;quot;KB2&amp;quot;, &amp;quot;Bluet&amp;quot;, &amp;quot;HID1&amp;quot; , &amp;quot;HID2&amp;quot;, &amp;quot;HID3&amp;quot;, &amp;quot;HID4&amp;quot;, &amp;quot;HID5&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool driver_active[CNT_DEVICES] = {false, false, false, false, false, false, false, false, false, false};&lt;br /&gt;
&lt;br /&gt;
// Lets also look at HID Input devices&lt;br /&gt;
USBHIDInput *hiddrivers[] = {&amp;amp;mouse1};&lt;br /&gt;
&lt;br /&gt;
#define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0]))&lt;br /&gt;
const char * hid_driver_names[CNT_HIDDEVICES] = {&amp;quot;Mouse1&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool hid_driver_active[CNT_HIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
BTHIDInput *bthiddrivers[] = {&amp;amp;keyboard1, &amp;amp;mouse1};&lt;br /&gt;
#define CNT_BTHIDDEVICES (sizeof(bthiddrivers)/sizeof(bthiddrivers[0]))&lt;br /&gt;
const char * bthid_driver_names[CNT_BTHIDDEVICES] = {&amp;quot;Keyboard(BT)&amp;quot;, &amp;quot;Mouse(BT)&amp;quot;};&lt;br /&gt;
bool bthid_driver_active[CNT_BTHIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
bool show_changed_only = false;&lt;br /&gt;
&lt;br /&gt;
uint8_t joystick_left_trigger_value = 0;&lt;br /&gt;
uint8_t joystick_right_trigger_value = 0;&lt;br /&gt;
uint64_t joystick_full_notify_mask = (uint64_t) - 1;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  while (!Serial); // wait for Arduino Serial Monitor&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  myusb.begin();&lt;br /&gt;
  keyboard1.attachPress(OnPress);&lt;br /&gt;
  keyboard2.attachPress(OnPress);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // check to see if the device list has changed:&lt;br /&gt;
  UpdateActiveDeviceInfo();&lt;br /&gt;
&lt;br /&gt;
  myusb.Task();&lt;br /&gt;
&lt;br /&gt;
  if (mouse1.available()) {&lt;br /&gt;
    Serial.print(&amp;quot;Mouse: buttons = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getButtons());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseX = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseX());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseY = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseY());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheel = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheel());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheelH = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheelH());&lt;br /&gt;
    Serial.println();&lt;br /&gt;
    mouse1.mouseDataClear();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void OnPress(int key)&lt;br /&gt;
{&lt;br /&gt;
  Serial.print(&amp;quot;key '&amp;quot;);&lt;br /&gt;
  switch (key) {&lt;br /&gt;
    case KEYD_UP       : Serial.print(&amp;quot;UP&amp;quot;); break;&lt;br /&gt;
    case KEYD_DOWN     : Serial.print(&amp;quot;DN&amp;quot;); break;&lt;br /&gt;
    case KEYD_LEFT     : Serial.print(&amp;quot;LEFT&amp;quot;); break;&lt;br /&gt;
    case KEYD_RIGHT    : Serial.print(&amp;quot;RIGHT&amp;quot;); break;&lt;br /&gt;
    case KEYD_INSERT   : Serial.print(&amp;quot;Ins&amp;quot;); break;&lt;br /&gt;
    case KEYD_DELETE   : Serial.print(&amp;quot;Del&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_UP  : Serial.print(&amp;quot;PUP&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_DOWN: Serial.print(&amp;quot;PDN&amp;quot;); break;&lt;br /&gt;
    case KEYD_HOME     : Serial.print(&amp;quot;HOME&amp;quot;); break;&lt;br /&gt;
    case KEYD_END      : Serial.print(&amp;quot;END&amp;quot;); break;&lt;br /&gt;
    case KEYD_F1       : Serial.print(&amp;quot;F1&amp;quot;); break;&lt;br /&gt;
    case KEYD_F2       : Serial.print(&amp;quot;F2&amp;quot;); break;&lt;br /&gt;
    case KEYD_F3       : Serial.print(&amp;quot;F3&amp;quot;); break;&lt;br /&gt;
    case KEYD_F4       : Serial.print(&amp;quot;F4&amp;quot;); break;&lt;br /&gt;
    case KEYD_F5       : Serial.print(&amp;quot;F5&amp;quot;); break;&lt;br /&gt;
    case KEYD_F6       : Serial.print(&amp;quot;F6&amp;quot;); break;&lt;br /&gt;
    case KEYD_F7       : Serial.print(&amp;quot;F7&amp;quot;); break;&lt;br /&gt;
    case KEYD_F8       : Serial.print(&amp;quot;F8&amp;quot;); break;&lt;br /&gt;
    case KEYD_F9       : Serial.print(&amp;quot;F9&amp;quot;); break;&lt;br /&gt;
    case KEYD_F10      : Serial.print(&amp;quot;F10&amp;quot;); break;&lt;br /&gt;
    case KEYD_F11      : Serial.print(&amp;quot;F11&amp;quot;); break;&lt;br /&gt;
    case KEYD_F12      : Serial.print(&amp;quot;F12&amp;quot;); break;&lt;br /&gt;
    default: Serial.print((char)key); break;&lt;br /&gt;
  }&lt;br /&gt;
  Serial.print(&amp;quot;'  &amp;quot;);&lt;br /&gt;
  Serial.println(key);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//=============================================================================&lt;br /&gt;
// UpdateActiveDeviceInfo&lt;br /&gt;
//=============================================================================&lt;br /&gt;
void UpdateActiveDeviceInfo() {&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_DEVICES; i++) {&lt;br /&gt;
    if (*drivers[i] != driver_active[i]) {&lt;br /&gt;
      if (driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s - disconnected ***\n&amp;quot;, driver_names[i]);&lt;br /&gt;
        driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s %x:%x - connected ***\n&amp;quot;, driver_names[i], drivers[i]-&amp;gt;idVendor(), drivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = drivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
&lt;br /&gt;
        if (drivers[i] == &amp;amp;bluet) {&lt;br /&gt;
          const uint8_t *bdaddr = bluet.myBDAddr();&lt;br /&gt;
          // remember it...&lt;br /&gt;
          Serial.printf(&amp;quot;  BDADDR: %x:%x:%x:%x:%x:%x\n&amp;quot;, bdaddr[0], bdaddr[1], bdaddr[2], bdaddr[3], bdaddr[4], bdaddr[5]);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_HIDDEVICES; i++) {&lt;br /&gt;
    if (*hiddrivers[i] != hid_driver_active[i]) {&lt;br /&gt;
      if (hid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s - disconnected ***\n&amp;quot;, hid_driver_names[i]);&lt;br /&gt;
        hid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s %x:%x - connected ***\n&amp;quot;, hid_driver_names[i], hiddrivers[i]-&amp;gt;idVendor(), hiddrivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        hid_driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = hiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  // Then Bluetooth devices&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_BTHIDDEVICES; i++) {&lt;br /&gt;
    if (*bthiddrivers[i] != bthid_driver_active[i]) {&lt;br /&gt;
      if (bthid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - disconnected ***\n&amp;quot;, bthid_driver_names[i]);&lt;br /&gt;
        bthid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - connected ***\n&amp;quot;, bthid_driver_names[i]); Serial.flush();&lt;br /&gt;
        bthid_driver_active[i] = true;&lt;br /&gt;
        #if 0&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = bthiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
    #endif  &lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Known Issues:'''&lt;br /&gt;
* Version 1.1 of the board leaves TUSB2046's reset line floating.  This can be remedied by connecting the TUSB2046's reset line to some other circuit, although this is error prone.&lt;br /&gt;
&lt;br /&gt;
== Power ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_power_circuit.png|thumb|Teensy 4.1 Computer power circuitry|500px]]&lt;br /&gt;
&lt;br /&gt;
The computer's power supply accepts input from four primary sources:&lt;br /&gt;
* Barrel jack DC input, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Screw terminal block, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Teensy 4.1 micro USB port, 5v input, current limit depends on USB power source&lt;br /&gt;
* Expansion ports&lt;br /&gt;
** Direct to 5v line, only limited by source current.  This allows one to surpass the typical 2A limit on the 5v line.&lt;br /&gt;
** Direct to VIN, accepts 8v to 20v.  5v line limited to 2A.&lt;br /&gt;
** Direct to 3.3v line, only limited by source current.  This can be used to relieve stress from the Teensy 4.1's 3.3v regulator, if needed.&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_audio_power.png|thumb|Teensy 4.1 Computer audio power circuits|800px]]&lt;br /&gt;
There are a few different power planes on the board:&lt;br /&gt;
* VIN, 8v to 20v input&lt;br /&gt;
** Feeds power to system 5v and audio 5v&lt;br /&gt;
** Audio 5v will come from system 5v if VIN is not provided&lt;br /&gt;
* System 5v line&lt;br /&gt;
** Provides power to the system 3.3v line and the audio 3.3v line&lt;br /&gt;
** If coming from VIN, this line is provided by a 2A switching regulator, which may have some noise.&lt;br /&gt;
** Is fed through PMOS power switches to downstream USB devices, functions as VUSB.&lt;br /&gt;
* System 3.3v line&lt;br /&gt;
** Provides power to the Teensy 4.1 and the TUSB&lt;br /&gt;
** Provided by the Teensy 4.1's internal 3.3v regulator from the system 5v line&lt;br /&gt;
* System Ground&lt;br /&gt;
** Connected to all circutry, with the exception of some of the audio circutry, which uses the audio ground instead&lt;br /&gt;
* Audio 5v line&lt;br /&gt;
** Provided by either a 5v linear regulator from VIN, or from the system 5v line if VIN is not provided&lt;br /&gt;
** Powers the OPA1688 audio amplifier&lt;br /&gt;
** Is not broken out to the rest of the board&lt;br /&gt;
* Audio 3.3v line&lt;br /&gt;
** Regulated by a linear regulator, getting input from the system 5v line through a ferrite bead&lt;br /&gt;
** Powers the PCM5102 audio ADC&lt;br /&gt;
** Is not broken out elsewhere on the board&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** Connected to System Ground through a ferrite bead&lt;br /&gt;
** Connected to audio circutry&lt;br /&gt;
** Provides the ground used for the 3.5mm audio jack&lt;br /&gt;
** Not broken out elsewhere on the board&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When providing power to the board, a few things should be kept in mind.  First, for high quality audio, either VIN must be provided, or the system 5v line must be clean.  The latter is generally much harder to do, so it is recommended to provide VIN if possible.  For a clean system 5v line, generally it is to provide the system 5v line though a linear regulator instead.  Adding more ceramic capacitors to the system 5v line can help as well.  If providing power from an exteral board through an expansion connector, providing power to both the 5v and VIN lines can boost the maximum current of the system 5v line above 2A.  If more  than 2A is needed, for example for more current-hungry USB devices, then this may be desirable.  Finally, there is a limit on the safe capacitence to be connected to the system 5v line for the 2A switching regulator to work normally.  It is unadvisable to connect more than 800uF of external capacitence to the system 5v line, as it may interfere with that regulator.  Although, there really usually is no reason to connect to much capacitence to that line.&lt;br /&gt;
&lt;br /&gt;
If providing power to the system 5v line from an external source and the Teensy 4.1's USB port simultaneously, it is a good idea to include an ideal diode or similar circuit to prevent backflow from the USB port into your regulator, or vice versa, depending on your regulators characteristics.  For this purpose LM66100 ICs can be used, as are used to protect the onboard switching regulator from the Teensy 4.1's USB port.&lt;br /&gt;
&lt;br /&gt;
'''Capacitence by Power Plane:'''&lt;br /&gt;
&lt;br /&gt;
* VIN:&lt;br /&gt;
** 22uF electrolytic&lt;br /&gt;
** 4.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 26.7uF total&lt;br /&gt;
* System 5v&lt;br /&gt;
** 2.2uF on Teensy 4.1, ceramic&lt;br /&gt;
** 10uF onboard, ceramic&lt;br /&gt;
** Specific to onboard switching regulator: 22uF electrolytic&lt;br /&gt;
** 12.2uF total when external 5v is provided (Teensy 4.1 micro USB, expansion slots directly to 5v rail)&lt;br /&gt;
** 34.2uF total when onboard regulator is used&lt;br /&gt;
* System 3.3v&lt;br /&gt;
** 7.13uF from Teensy 4.1, ceramic&lt;br /&gt;
** 0.3uF onboard for decoupling, ceramic&lt;br /&gt;
** 7.43uF total&lt;br /&gt;
* System Ground&lt;br /&gt;
** 10.3uF ceramic&lt;br /&gt;
** 14.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 44uF electrolytic&lt;br /&gt;
** Approximately 25.13uF on Teensy 4.1, ceramic&lt;br /&gt;
** 94.13uF total&lt;br /&gt;
* Audio 5v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio 3.3v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** 27.2uF ceramic&lt;br /&gt;
** Connected to system ground through ferrite bead - technically connected to those capacitors as well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Expansion Ports ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_expansion_ports.png|thumb|Expansion port layout, not including the card connector port|800px]]&lt;br /&gt;
[[File:Teensy4.1_computer_external_headers_1.png|thumb|Expansion port schematics, excluding the card connector|800px]]&lt;br /&gt;
&lt;br /&gt;
There are three large expansion ports on the Teensy 4.1 computer board, and 5 smaller ports.  Here is a list, describing what each one breaks out:&lt;br /&gt;
* Largest expansion port on the back of the board (J11)&lt;br /&gt;
** Designed for large expansions to the computer system&lt;br /&gt;
** Exposes all power pins, including VIN, 5v, 3.3v, GND, and even the Teensy's VBAT line&lt;br /&gt;
** Numerous Teensy 4.1 pins: 0, 1, 11, 12, 13, 18, 19, 28, 29, 40, 41&lt;br /&gt;
*** UART1&lt;br /&gt;
*** UART7&lt;br /&gt;
*** SPI0&lt;br /&gt;
*** I2C0&lt;br /&gt;
*** Analog inputs: A4, A5, A16, A17&lt;br /&gt;
*** CRX2 and CTX2&lt;br /&gt;
** '''3.3v-compatible''' 8-bit parallel bus&lt;br /&gt;
*** Particularly useful for LCD displays and LCD display controllers, or any other parallel peripheral, for very high speeds&lt;br /&gt;
* Right-side expansion port (J12 - reference not shown on board revision 1.1)&lt;br /&gt;
** Designed for ATX-compatible expansions to the board&lt;br /&gt;
** Exposes 5v and 3.3v lines&lt;br /&gt;
** '''3.3v-compatible''' 8-bit parallel bus&lt;br /&gt;
** Several Teensy 4.1 pins: 6, 7, 8, 9, 18, 19, 32&lt;br /&gt;
*** UART2&lt;br /&gt;
*** I2C0&lt;br /&gt;
*** Analog inputs: A4, A5&lt;br /&gt;
* Card Expansion Port&lt;br /&gt;
** Designed to provide a card slot to fit in an ATX case.  The card may provide IO to the front.&lt;br /&gt;
** Provides a '''5v-compatible''' 8-bit parallel port.  The pins are shared with the other parallel ports.&lt;br /&gt;
** The direction of all data pins are controlled by the control lines.  '''Cannot use data line as input and output at the same time'''&lt;br /&gt;
*** Controlled by write, read, and select lines.  Select pin is pin 9, pulling it low will enable communication.&lt;br /&gt;
** Provides only a 5v line.&lt;br /&gt;
* I2S Expansion Port (J10)&lt;br /&gt;
** Provides an alternative to the 3.5mm audio output, and allows one to attach a microphone&lt;br /&gt;
** Breaks out I2S port 2&lt;br /&gt;
** Can be used to add a microphone alongside use of the 3.5mm audio jack&lt;br /&gt;
** Provides all power rails: VIN, 5v and 3.3v.&lt;br /&gt;
** Breaks out pins 16 and 17&lt;br /&gt;
*** I2C1&lt;br /&gt;
*** Analog pins A2 and A3&lt;br /&gt;
*** UART4&lt;br /&gt;
* SPI0 Port (J9)&lt;br /&gt;
** Provided for SPI devices.  Designed so that a card can be inserted to provide IO to the front of the board.&lt;br /&gt;
** Breaks out pins 10-13&lt;br /&gt;
** Provides 3.3v, but not 5v&lt;br /&gt;
** Breaks out SPI0&lt;br /&gt;
* UART6 Port&lt;br /&gt;
** This is a small port breaking out pins 24 and 25&lt;br /&gt;
*** UART6&lt;br /&gt;
*** Analog pins A10 and A11&lt;br /&gt;
*** I2C2&lt;br /&gt;
** The port is designed so another board can provide IO to the front.&lt;br /&gt;
** The pinout is designed to be compatible with Sparkfun devices and serial pinouts&lt;br /&gt;
** Can be used also as an additional I2C port, and has the necessary 4.7k resistors connected&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
* I2C1 Port&lt;br /&gt;
** This is a small port breaking out pins 16 and 17&lt;br /&gt;
*** UART4&lt;br /&gt;
*** Analog pins A2 and A3&lt;br /&gt;
*** I2C1&lt;br /&gt;
** Has the necessary 4.7k resistors connected&lt;br /&gt;
** The port is designed for internal devices, and is not designed to provide front-facing IO.&lt;br /&gt;
** Can be used also as an additional UART port&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
* UART3 Port&lt;br /&gt;
** This is a small port breaking out pins 14 and 15&lt;br /&gt;
*** UART3&lt;br /&gt;
*** Analog pins A0 and A1&lt;br /&gt;
** The port is designed for internal devices, and is not designed to provide front-facing IO.&lt;br /&gt;
** The pinout is designed to be compatible with Sparkfun devices and serial pinouts&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
&lt;br /&gt;
=== About the Parallel Port ===&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_card_connector_diagram.png|thumb|Card Connector circuit diagram and pinout|500px]]&lt;br /&gt;
&lt;br /&gt;
All three ports that provide parallel share the parallel pins WR, RD, and WAIT, as well as the busses ADDR and DATA.  Each connected parallel device should use a separate select pin, however, with pin 9 being reserved as the card connector's select pin.  WR, RD, and WAIT are all active low, and it is expected that all select lines also be active low.  The address bus (ADDR, shown on the board as A0, A1, and A2) has three pins, allowing for three addresses per device.  A parallel port is important to have because of the high bandwidth it provides.  The Teensy can write this port extremely fast, thus allowing better framerates and draw times for connected graphics devices.  I imagine there are other uses for this port as well.&lt;br /&gt;
&lt;br /&gt;
As ADDR, WR, RD, and any select pins are all output-only from the Teensy, '''there is no level-shifting logic for them.'''  For the WAIT line, there is a simple resistor voltage divider to take the 5v from the card connector down to 3.3v.  There is only level-shifting logic provided for data lines on the card connector, so that 5v TTL devices and levels can be used with the card connector out of the box.&lt;br /&gt;
&lt;br /&gt;
The card connector was designed to interface with cards for the Z80 computer I previously built, and has a compatible pinout.&lt;br /&gt;
&lt;br /&gt;
=== Mechanical Measurements ===&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1 desktop measurements.png|Board measurements|900px]]&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=63</id>
		<title>Teensy 4.1 Computer</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=63"/>
		<updated>2024-07-29T14:03:10Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Teensy 4.1 Computer is more or less described by its name: Its a carrier board for the Teensy 4.1 which provides four USB ports, an Ethernet port, Hi-Fi audio, and several convenient expansion ports.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
'''USB:'''&lt;br /&gt;
&lt;br /&gt;
* TUSB2046 USB Hub, providing 4 USB A ports&lt;br /&gt;
* Each port has a maximum of 1.5A output - the port will shutdown due to overcurrent above this&lt;br /&gt;
* Board power provides a maximum of 2A to the 5v supply&lt;br /&gt;
** one should be aware of this when considering the power used by USB devices&lt;br /&gt;
&lt;br /&gt;
'''Audio:'''&lt;br /&gt;
&lt;br /&gt;
* I2S control connected to Teensy I2S 2&lt;br /&gt;
* PCM5102 ADC for providing stereo sound&lt;br /&gt;
* OPA1688 for the amplifier&lt;br /&gt;
* 3.5mm audio jack, maximum 75mA per channel&lt;br /&gt;
* Separate ground, connected to main ground through a ferrite bead&lt;br /&gt;
* Separate 3.3v and 5v supplies from linear regulators to reduce noise&lt;br /&gt;
** Note: 5v line will come from main 5v rail if only powered from USB&lt;br /&gt;
&lt;br /&gt;
'''Power:'''&lt;br /&gt;
&lt;br /&gt;
* 2A 5v buck regulator (Exact part is the VR20S05)&lt;br /&gt;
* PMOS switches to prevent regulator backfeeding from USB and to control power flow (U16, U17, and U18 on the PCB)&lt;br /&gt;
* External VIN accepted from 8v to 20v&lt;br /&gt;
* Separate 5v and 3.3v supplies for audio circutry&lt;br /&gt;
* Barrel jack and screw terminal connections for VIN&lt;br /&gt;
&lt;br /&gt;
'''Expansion Ports'''&lt;br /&gt;
&lt;br /&gt;
* I2S, SPI, I2C, and Serial expansion ports&lt;br /&gt;
* 5v-capable 8-bit parallel bus port, with 3-bit address, WR, RD, select, and WAIT lines&lt;br /&gt;
* Two larger expansion ports, each providing a '''3.3v compatible''' 8-bit parallel bus, various protocols like I2C and SPI, power, and GPIO&lt;br /&gt;
&lt;br /&gt;
== The Audio Circuit ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy41_Computer_Audio_circuit.png|thumb|Teensy 4.1 Computer audio circuit|600px]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1 is connected to the audio circuit via the Teensy's I2S 2 port.  This connection goes to the PCM5102 ADC.  As is recommended in the PCM5102's datasheet, filtering is added to the output, however the filter used on this board is a bit stronger, using a 4.7nF capacitor instead of the 2.2nF capacitor recommended in the datasheet.  The filter cutoff is still far above human perception, at 72kHz, and should filter high frequency noise a bit better.  The connection to the OPA1688 is DC, not AC, coupled, but this is according to the recommendations in the OPA1688 datasheet.  The gain of the amplifier is configured to be 1, so the output voltage of the amp should match the ADC's output voltage, and be in the range of 0v to 3.3v.  A voltage divider is used to activate the PCM5102's soft mute function when the 5v line drops, which is designed to prevent any pops in the audio when the power is disconnected.&lt;br /&gt;
&lt;br /&gt;
The circuit was designed with audio quality, but not energy efficiency, in mind.  The circuit may sink as much as 400mA from VIN, and an additional 40mA from the main 5v line.  However, when the audio circuit is idle, it should be less than 50mA all together.  The 3.5mm audio jack can provide a maximum of 75mA per channel.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;Audio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;SD.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// GUItool: begin automatically generated code&lt;br /&gt;
AudioPlaySdWav           playSdWav1;     //xy=719,389&lt;br /&gt;
AudioOutputI2S2          i2s2_1;         //xy=892,388&lt;br /&gt;
AudioConnection          patchCord1(playSdWav1, 0, i2s2_1, 0);&lt;br /&gt;
AudioConnection          patchCord2(playSdWav1, 1, i2s2_1, 1);&lt;br /&gt;
// GUItool: end automatically generated code&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  AudioMemory(10);&lt;br /&gt;
&lt;br /&gt;
  if (!(SD.begin(BUILTIN_SDCARD))) {&lt;br /&gt;
    // stop here, but print a message repetitively&lt;br /&gt;
    while (1) {&lt;br /&gt;
      Serial.println(&amp;quot;Unable to access the SD card&amp;quot;);&lt;br /&gt;
      delay(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Serial.println(&amp;quot;Playing audio.wav...&amp;quot;);&lt;br /&gt;
  playSdWav1.play(&amp;quot;audio.wav&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // Do nothing forever&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== USB Ports ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_USB_schematic.png|thumb|Teensy 4.1 Computer USB circuit|600px]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1's builtin USB port is connected directly to a TUSB2046 IC, which is a USB hub and provides four downstream ports.  Each port has overcurrent protection, with a maximum allowed current of 1.5A.  There are also TVS diodes on all USB data lines on the downstream ports, provided by three TPD3E001DRLR ICs.  Other than that, each downstream USB port has two 15k pulldowns - one on each data line.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&lt;br /&gt;
The best example is found in the Mouse_KeyboardBT example from the USBHost_t36 library, which is included in the TeensyDuino package.  For convenience, I have included a stripped down version of it below:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;quot;USBHost_t36.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
USBHost myusb;&lt;br /&gt;
USBHub hub1(myusb);&lt;br /&gt;
USBHub hub2(myusb);&lt;br /&gt;
KeyboardController keyboard1(myusb);&lt;br /&gt;
KeyboardController keyboard2(myusb);&lt;br /&gt;
USBHIDParser hid1(myusb);&lt;br /&gt;
USBHIDParser hid2(myusb);&lt;br /&gt;
USBHIDParser hid3(myusb);&lt;br /&gt;
USBHIDParser hid4(myusb);&lt;br /&gt;
USBHIDParser hid5(myusb);&lt;br /&gt;
MouseController mouse1(myusb);&lt;br /&gt;
&lt;br /&gt;
BluetoothController bluet(myusb);   // version assumes it already was paired&lt;br /&gt;
&lt;br /&gt;
USBDriver *drivers[] = {&amp;amp;hub1, &amp;amp;hub2, &amp;amp;keyboard1, &amp;amp;keyboard2, &amp;amp;bluet, &amp;amp;hid1, &amp;amp;hid2, &amp;amp;hid3, &amp;amp;hid4, &amp;amp;hid5};&lt;br /&gt;
&lt;br /&gt;
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))&lt;br /&gt;
const char * driver_names[CNT_DEVICES] = {&amp;quot;Hub1&amp;quot;, &amp;quot;Hub2&amp;quot;, &amp;quot;KB1&amp;quot;, &amp;quot;KB2&amp;quot;, &amp;quot;Bluet&amp;quot;, &amp;quot;HID1&amp;quot; , &amp;quot;HID2&amp;quot;, &amp;quot;HID3&amp;quot;, &amp;quot;HID4&amp;quot;, &amp;quot;HID5&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool driver_active[CNT_DEVICES] = {false, false, false, false, false, false, false, false, false, false};&lt;br /&gt;
&lt;br /&gt;
// Lets also look at HID Input devices&lt;br /&gt;
USBHIDInput *hiddrivers[] = {&amp;amp;mouse1};&lt;br /&gt;
&lt;br /&gt;
#define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0]))&lt;br /&gt;
const char * hid_driver_names[CNT_HIDDEVICES] = {&amp;quot;Mouse1&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool hid_driver_active[CNT_HIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
BTHIDInput *bthiddrivers[] = {&amp;amp;keyboard1, &amp;amp;mouse1};&lt;br /&gt;
#define CNT_BTHIDDEVICES (sizeof(bthiddrivers)/sizeof(bthiddrivers[0]))&lt;br /&gt;
const char * bthid_driver_names[CNT_BTHIDDEVICES] = {&amp;quot;Keyboard(BT)&amp;quot;, &amp;quot;Mouse(BT)&amp;quot;};&lt;br /&gt;
bool bthid_driver_active[CNT_BTHIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
bool show_changed_only = false;&lt;br /&gt;
&lt;br /&gt;
uint8_t joystick_left_trigger_value = 0;&lt;br /&gt;
uint8_t joystick_right_trigger_value = 0;&lt;br /&gt;
uint64_t joystick_full_notify_mask = (uint64_t) - 1;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  while (!Serial); // wait for Arduino Serial Monitor&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  myusb.begin();&lt;br /&gt;
  keyboard1.attachPress(OnPress);&lt;br /&gt;
  keyboard2.attachPress(OnPress);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // check to see if the device list has changed:&lt;br /&gt;
  UpdateActiveDeviceInfo();&lt;br /&gt;
&lt;br /&gt;
  myusb.Task();&lt;br /&gt;
&lt;br /&gt;
  if (mouse1.available()) {&lt;br /&gt;
    Serial.print(&amp;quot;Mouse: buttons = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getButtons());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseX = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseX());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseY = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseY());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheel = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheel());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheelH = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheelH());&lt;br /&gt;
    Serial.println();&lt;br /&gt;
    mouse1.mouseDataClear();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void OnPress(int key)&lt;br /&gt;
{&lt;br /&gt;
  Serial.print(&amp;quot;key '&amp;quot;);&lt;br /&gt;
  switch (key) {&lt;br /&gt;
    case KEYD_UP       : Serial.print(&amp;quot;UP&amp;quot;); break;&lt;br /&gt;
    case KEYD_DOWN     : Serial.print(&amp;quot;DN&amp;quot;); break;&lt;br /&gt;
    case KEYD_LEFT     : Serial.print(&amp;quot;LEFT&amp;quot;); break;&lt;br /&gt;
    case KEYD_RIGHT    : Serial.print(&amp;quot;RIGHT&amp;quot;); break;&lt;br /&gt;
    case KEYD_INSERT   : Serial.print(&amp;quot;Ins&amp;quot;); break;&lt;br /&gt;
    case KEYD_DELETE   : Serial.print(&amp;quot;Del&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_UP  : Serial.print(&amp;quot;PUP&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_DOWN: Serial.print(&amp;quot;PDN&amp;quot;); break;&lt;br /&gt;
    case KEYD_HOME     : Serial.print(&amp;quot;HOME&amp;quot;); break;&lt;br /&gt;
    case KEYD_END      : Serial.print(&amp;quot;END&amp;quot;); break;&lt;br /&gt;
    case KEYD_F1       : Serial.print(&amp;quot;F1&amp;quot;); break;&lt;br /&gt;
    case KEYD_F2       : Serial.print(&amp;quot;F2&amp;quot;); break;&lt;br /&gt;
    case KEYD_F3       : Serial.print(&amp;quot;F3&amp;quot;); break;&lt;br /&gt;
    case KEYD_F4       : Serial.print(&amp;quot;F4&amp;quot;); break;&lt;br /&gt;
    case KEYD_F5       : Serial.print(&amp;quot;F5&amp;quot;); break;&lt;br /&gt;
    case KEYD_F6       : Serial.print(&amp;quot;F6&amp;quot;); break;&lt;br /&gt;
    case KEYD_F7       : Serial.print(&amp;quot;F7&amp;quot;); break;&lt;br /&gt;
    case KEYD_F8       : Serial.print(&amp;quot;F8&amp;quot;); break;&lt;br /&gt;
    case KEYD_F9       : Serial.print(&amp;quot;F9&amp;quot;); break;&lt;br /&gt;
    case KEYD_F10      : Serial.print(&amp;quot;F10&amp;quot;); break;&lt;br /&gt;
    case KEYD_F11      : Serial.print(&amp;quot;F11&amp;quot;); break;&lt;br /&gt;
    case KEYD_F12      : Serial.print(&amp;quot;F12&amp;quot;); break;&lt;br /&gt;
    default: Serial.print((char)key); break;&lt;br /&gt;
  }&lt;br /&gt;
  Serial.print(&amp;quot;'  &amp;quot;);&lt;br /&gt;
  Serial.println(key);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//=============================================================================&lt;br /&gt;
// UpdateActiveDeviceInfo&lt;br /&gt;
//=============================================================================&lt;br /&gt;
void UpdateActiveDeviceInfo() {&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_DEVICES; i++) {&lt;br /&gt;
    if (*drivers[i] != driver_active[i]) {&lt;br /&gt;
      if (driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s - disconnected ***\n&amp;quot;, driver_names[i]);&lt;br /&gt;
        driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s %x:%x - connected ***\n&amp;quot;, driver_names[i], drivers[i]-&amp;gt;idVendor(), drivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = drivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
&lt;br /&gt;
        if (drivers[i] == &amp;amp;bluet) {&lt;br /&gt;
          const uint8_t *bdaddr = bluet.myBDAddr();&lt;br /&gt;
          // remember it...&lt;br /&gt;
          Serial.printf(&amp;quot;  BDADDR: %x:%x:%x:%x:%x:%x\n&amp;quot;, bdaddr[0], bdaddr[1], bdaddr[2], bdaddr[3], bdaddr[4], bdaddr[5]);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_HIDDEVICES; i++) {&lt;br /&gt;
    if (*hiddrivers[i] != hid_driver_active[i]) {&lt;br /&gt;
      if (hid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s - disconnected ***\n&amp;quot;, hid_driver_names[i]);&lt;br /&gt;
        hid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s %x:%x - connected ***\n&amp;quot;, hid_driver_names[i], hiddrivers[i]-&amp;gt;idVendor(), hiddrivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        hid_driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = hiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  // Then Bluetooth devices&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_BTHIDDEVICES; i++) {&lt;br /&gt;
    if (*bthiddrivers[i] != bthid_driver_active[i]) {&lt;br /&gt;
      if (bthid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - disconnected ***\n&amp;quot;, bthid_driver_names[i]);&lt;br /&gt;
        bthid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - connected ***\n&amp;quot;, bthid_driver_names[i]); Serial.flush();&lt;br /&gt;
        bthid_driver_active[i] = true;&lt;br /&gt;
        #if 0&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = bthiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
    #endif  &lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Known Issues:'''&lt;br /&gt;
* Version 1.1 of the board leaves TUSB2046's reset line floating.  This can be remedied by connecting the TUSB2046's reset line to some other circuit, although this is error prone.&lt;br /&gt;
&lt;br /&gt;
== Power ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_power_circuit.png|thumb|Teensy 4.1 Computer power circuitry|500px]]&lt;br /&gt;
&lt;br /&gt;
The computer's power supply accepts input from four primary sources:&lt;br /&gt;
* Barrel jack DC input, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Screw terminal block, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Teensy 4.1 micro USB port, 5v input, current limit depends on USB power source&lt;br /&gt;
* Expansion ports&lt;br /&gt;
** Direct to 5v line, only limited by source current.  This allows one to surpass the typical 2A limit on the 5v line.&lt;br /&gt;
** Direct to VIN, accepts 8v to 20v.  5v line limited to 2A.&lt;br /&gt;
** Direct to 3.3v line, only limited by source current.  This can be used to relieve stress from the Teensy 4.1's 3.3v regulator, if needed.&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_audio_power.png|thumb|Teensy 4.1 Computer audio power circuits|800px]]&lt;br /&gt;
There are a few different power planes on the board:&lt;br /&gt;
* VIN, 8v to 20v input&lt;br /&gt;
** Feeds power to system 5v and audio 5v&lt;br /&gt;
** Audio 5v will come from system 5v if VIN is not provided&lt;br /&gt;
* System 5v line&lt;br /&gt;
** Provides power to the system 3.3v line and the audio 3.3v line&lt;br /&gt;
** If coming from VIN, this line is provided by a 2A switching regulator, which may have some noise.&lt;br /&gt;
** Is fed through PMOS power switches to downstream USB devices, functions as VUSB.&lt;br /&gt;
* System 3.3v line&lt;br /&gt;
** Provides power to the Teensy 4.1 and the TUSB&lt;br /&gt;
** Provided by the Teensy 4.1's internal 3.3v regulator from the system 5v line&lt;br /&gt;
* System Ground&lt;br /&gt;
** Connected to all circutry, with the exception of some of the audio circutry, which uses the audio ground instead&lt;br /&gt;
* Audio 5v line&lt;br /&gt;
** Provided by either a 5v linear regulator from VIN, or from the system 5v line if VIN is not provided&lt;br /&gt;
** Powers the OPA1688 audio amplifier&lt;br /&gt;
** Is not broken out to the rest of the board&lt;br /&gt;
* Audio 3.3v line&lt;br /&gt;
** Regulated by a linear regulator, getting input from the system 5v line through a ferrite bead&lt;br /&gt;
** Powers the PCM5102 audio ADC&lt;br /&gt;
** Is not broken out elsewhere on the board&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** Connected to System Ground through a ferrite bead&lt;br /&gt;
** Connected to audio circutry&lt;br /&gt;
** Provides the ground used for the 3.5mm audio jack&lt;br /&gt;
** Not broken out elsewhere on the board&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When providing power to the board, a few things should be kept in mind.  First, for high quality audio, either VIN must be provided, or the system 5v line must be clean.  The latter is generally much harder to do, so it is recommended to provide VIN if possible.  For a clean system 5v line, generally it is to provide the system 5v line though a linear regulator instead.  Adding more ceramic capacitors to the system 5v line can help as well.  If providing power from an exteral board through an expansion connector, providing power to both the 5v and VIN lines can boost the maximum current of the system 5v line above 2A.  If more  than 2A is needed, for example for more current-hungry USB devices, then this may be desirable.  Finally, there is a limit on the safe capacitence to be connected to the system 5v line for the 2A switching regulator to work normally.  It is unadvisable to connect more than 800uF of external capacitence to the system 5v line, as it may interfere with that regulator.  Although, there really usually is no reason to connect to much capacitence to that line.&lt;br /&gt;
&lt;br /&gt;
If providing power to the system 5v line from an external source and the Teensy 4.1's USB port simultaneously, it is a good idea to include an ideal diode or similar circuit to prevent backflow from the USB port into your regulator, or vice versa, depending on your regulators characteristics.  For this purpose LM66100 ICs can be used, as are used to protect the onboard switching regulator from the Teensy 4.1's USB port.&lt;br /&gt;
&lt;br /&gt;
'''Capacitence by Power Plane:'''&lt;br /&gt;
&lt;br /&gt;
* VIN:&lt;br /&gt;
** 22uF electrolytic&lt;br /&gt;
** 4.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 26.7uF total&lt;br /&gt;
* System 5v&lt;br /&gt;
** 2.2uF on Teensy 4.1, ceramic&lt;br /&gt;
** 10uF onboard, ceramic&lt;br /&gt;
** Specific to onboard switching regulator: 22uF electrolytic&lt;br /&gt;
** 12.2uF total when external 5v is provided (Teensy 4.1 micro USB, expansion slots directly to 5v rail)&lt;br /&gt;
** 34.2uF total when onboard regulator is used&lt;br /&gt;
* System 3.3v&lt;br /&gt;
** 7.13uF from Teensy 4.1, ceramic&lt;br /&gt;
** 0.3uF onboard for decoupling, ceramic&lt;br /&gt;
** 7.43uF total&lt;br /&gt;
* System Ground&lt;br /&gt;
** 10.3uF ceramic&lt;br /&gt;
** 14.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 44uF electrolytic&lt;br /&gt;
** Approximately 25.13uF on Teensy 4.1, ceramic&lt;br /&gt;
** 94.13uF total&lt;br /&gt;
* Audio 5v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio 3.3v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** 27.2uF ceramic&lt;br /&gt;
** Connected to system ground through ferrite bead - technically connected to those capacitors as well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Expansion Ports ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_expansion_ports.png|thumb|Expansion port layout, not including the card connector port|800px]]&lt;br /&gt;
[[File:Teensy4.1_computer_external_headers_1.png|thumb|Expansion port schematics, excluding the card connector|800px]]&lt;br /&gt;
&lt;br /&gt;
There are three large expansion ports on the Teensy 4.1 computer board, and 5 smaller ports.  Here is a list, describing what each one breaks out:&lt;br /&gt;
* Largest expansion port on the back of the board (J11)&lt;br /&gt;
** Designed for large expansions to the computer system&lt;br /&gt;
** Exposes all power pins, including VIN, 5v, 3.3v, GND, and even the Teensy's VBAT line&lt;br /&gt;
** Numerous Teensy 4.1 pins: 0, 1, 11, 12, 13, 18, 19, 28, 29, 40, 41&lt;br /&gt;
*** UART1&lt;br /&gt;
*** UART7&lt;br /&gt;
*** SPI0&lt;br /&gt;
*** I2C0&lt;br /&gt;
*** Analog inputs: A4, A5, A16, A17&lt;br /&gt;
*** CRX2 and CTX2&lt;br /&gt;
** '''3.3v-compatible''' 8-bit parallel bus&lt;br /&gt;
*** Particularly useful for LCD displays and LCD display controllers, or any other parallel peripheral, for very high speeds&lt;br /&gt;
* Right-side expansion port (J12 - reference not shown on board revision 1.1)&lt;br /&gt;
** Designed for ATX-compatible expansions to the board&lt;br /&gt;
** Exposes 5v and 3.3v lines&lt;br /&gt;
** '''3.3v-compatible''' 8-bit parallel bus&lt;br /&gt;
** Several Teensy 4.1 pins: 6, 7, 8, 9, 18, 19, 32&lt;br /&gt;
*** UART2&lt;br /&gt;
*** I2C0&lt;br /&gt;
*** Analog inputs: A4, A5&lt;br /&gt;
* Card Expansion Port&lt;br /&gt;
** Designed to provide a card slot to fit in an ATX case.  The card may provide IO to the front.&lt;br /&gt;
** Provides a '''5v-compatible''' 8-bit parallel port.  The pins are shared with the other parallel ports.&lt;br /&gt;
** The direction of all data pins are controlled by the control lines.  '''Cannot use data line as input and output at the same time'''&lt;br /&gt;
*** Controlled by write, read, and select lines.  Select pin is pin 9, pulling it low will enable communication.&lt;br /&gt;
** Provides only a 5v line.&lt;br /&gt;
* I2S Expansion Port (J10)&lt;br /&gt;
** Provides an alternative to the 3.5mm audio output, and allows one to attach a microphone&lt;br /&gt;
** Breaks out I2S port 2&lt;br /&gt;
** Can be used to add a microphone alongside use of the 3.5mm audio jack&lt;br /&gt;
** Provides all power rails: VIN, 5v and 3.3v.&lt;br /&gt;
** Breaks out pins 16 and 17&lt;br /&gt;
*** I2C1&lt;br /&gt;
*** Analog pins A2 and A3&lt;br /&gt;
*** UART4&lt;br /&gt;
* SPI0 Port (J9)&lt;br /&gt;
** Provided for SPI devices.  Designed so that a card can be inserted to provide IO to the front of the board.&lt;br /&gt;
** Breaks out pins 10-13&lt;br /&gt;
** Provides 3.3v, but not 5v&lt;br /&gt;
** Breaks out SPI0&lt;br /&gt;
* UART6 Port&lt;br /&gt;
** This is a small port breaking out pins 24 and 25&lt;br /&gt;
*** UART6&lt;br /&gt;
*** Analog pins A10 and A11&lt;br /&gt;
*** I2C2&lt;br /&gt;
** The port is designed so another board can provide IO to the front.&lt;br /&gt;
** The pinout is designed to be compatible with Sparkfun devices and serial pinouts&lt;br /&gt;
** Can be used also as an additional I2C port, and has the necessary 4.7k resistors connected&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
* I2C1 Port&lt;br /&gt;
** This is a small port breaking out pins 16 and 17&lt;br /&gt;
*** UART4&lt;br /&gt;
*** Analog pins A2 and A3&lt;br /&gt;
*** I2C1&lt;br /&gt;
** Has the necessary 4.7k resistors connected&lt;br /&gt;
** The port is designed for internal devices, and is not designed to provide front-facing IO.&lt;br /&gt;
** Can be used also as an additional UART port&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
* UART3 Port&lt;br /&gt;
** This is a small port breaking out pins 14 and 15&lt;br /&gt;
*** UART3&lt;br /&gt;
*** Analog pins A0 and A1&lt;br /&gt;
** The port is designed for internal devices, and is not designed to provide front-facing IO.&lt;br /&gt;
** The pinout is designed to be compatible with Sparkfun devices and serial pinouts&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
&lt;br /&gt;
=== About the Parallel Port ===&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_card_connector_diagram.png|thumb|Card Connector circuit diagram and pinout|500px]]&lt;br /&gt;
&lt;br /&gt;
All three ports that provide parallel share the parallel pins WR, RD, and WAIT, as well as the busses ADDR and DATA.  Each connected parallel device should use a separate select pin, however, with pin 9 being reserved as the card connector's select pin.  WR, RD, and WAIT are all active low, and it is expected that all select lines also be active low.  The address bus (ADDR, shown on the board as A0, A1, and A2) has three pins, allowing for three addresses per device.  A parallel port is important to have because of the high bandwidth it provides.  The Teensy can write this port extremely fast, thus allowing better framerates and draw times for connected graphics devices.  I imagine there are other uses for this port as well.&lt;br /&gt;
&lt;br /&gt;
As ADDR, WR, RD, and any select pins are all output-only from the Teensy, '''there is no level-shifting logic for them.'''  For the WAIT line, there is a simple resistor voltage divider to take the 5v from the card connector down to 3.3v.  There is only level-shifting logic provided for data lines on the card connector, so that 5v TTL devices and levels can be used with the card connector out of the box.&lt;br /&gt;
&lt;br /&gt;
The card connector was designed to interface with cards for the Z80 computer I previously built, and has a compatible pinout.&lt;br /&gt;
&lt;br /&gt;
=== Mechanical Measurements ===&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1 desktop measurements.png|Board measurements|750px]]&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=File:Teensy4.1_desktop_measurements.png&amp;diff=62</id>
		<title>File:Teensy4.1 desktop measurements.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=File:Teensy4.1_desktop_measurements.png&amp;diff=62"/>
		<updated>2024-07-29T14:01:02Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: CAD design measurements of the Teensy 4.1 Computer, showing screwhole locations, board edge, and component courtyards.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
CAD design measurements of the Teensy 4.1 Computer, showing screwhole locations, board edge, and component courtyards.&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=Ground_Robot_Control_Board&amp;diff=61</id>
		<title>Ground Robot Control Board</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=Ground_Robot_Control_Board&amp;diff=61"/>
		<updated>2024-07-16T18:13:50Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This board is designed as an easy and clean solution to building ground robots.  It requires minimal external components:&lt;br /&gt;
&lt;br /&gt;
* Battery or other power source&lt;br /&gt;
* Motors&lt;br /&gt;
* Optionally, regulators may be used to control motor voltage&lt;br /&gt;
* Sensors, easily connected via QWIIC&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=Ground_Robot_Control_Board&amp;diff=60</id>
		<title>Ground Robot Control Board</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=Ground_Robot_Control_Board&amp;diff=60"/>
		<updated>2024-07-16T18:12:02Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Create page for Ground Robot Control Board so I can get the link&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This board is designed&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=NGT31&amp;diff=59</id>
		<title>NGT31</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=NGT31&amp;diff=59"/>
		<updated>2024-06-22T00:29:32Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Add more code comments to the 3D section&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The NGT31 is a Parallax Propeller based VGA driver and graphics device, provided as an Arduino shield.  It uses a serial connection to the host device for communication, up to 250,000 baud with current firmware.&lt;br /&gt;
&lt;br /&gt;
This device provides many useful graphics functions, including accelerated sprite drawing, text, and even drawing lines in 3D.&lt;br /&gt;
&lt;br /&gt;
Firmware: https://git.nuclaer-servers.com/Nuclaer/ngt_firmware/-/tree/master&lt;br /&gt;
&lt;br /&gt;
Arduino Library: https://git.nuclaer-servers.com/Nuclaer/nmt_gfx/-/tree/master&lt;br /&gt;
&lt;br /&gt;
== Example Code ==&lt;br /&gt;
&lt;br /&gt;
Here is the demo included in the NMT_GFX library (https://git.nuclaer-servers.com/Nuclaer/nmt_gfx/-/blob/master/Examples/NGT31/NGT31_demo/NGT31_demo.ino):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;NMT_GFX.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
byte tree[] = { 1,8,0,0,&lt;br /&gt;
  0b01010100,0b00010101,&lt;br /&gt;
  0b01010101,0b01010101,&lt;br /&gt;
  0b01010101,0b01010101,&lt;br /&gt;
  0b01010101,0b01010101,&lt;br /&gt;
  0b10100011,0b00001010,&lt;br /&gt;
  0b10100000,0b00001010,&lt;br /&gt;
  0b10100000,0b00001010,&lt;br /&gt;
  0b10101000,0b00101010&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
unsigned short cat[] = { 0, 0,&lt;br /&gt;
  0xaaa,0x0000, 0xffff, 0xffff, 0xc000,&lt;br /&gt;
  0xaaa,0xaaa3, 0xf555, 0x5557, 0xf000,&lt;br /&gt;
  0xaaa,0xaaa3, 0xd555, 0x5555, 0xf000,&lt;br /&gt;
  0xaaa,0xaaa3, 0x5555, 0x5555, 0x7000,&lt;br /&gt;
  0xaaa,0xaaa3, 0x5555, 0x4155, 0x7000,&lt;br /&gt;
  0xaaa,0xaaa3, 0x5555, 0x3c55, 0x70f0,&lt;br /&gt;
  0xaaa,0x00a3, 0x5555, 0x3f15, 0x73f0,&lt;br /&gt;
  0xaaa,0x3c23, 0x5555, 0x3fc0, 0x0ff0,&lt;br /&gt;
  0xaaa,0x0f03, 0x5555, 0x3fff, 0xfff0,&lt;br /&gt;
  0xaaa,0x83c3, 0x5554, 0xffff, 0xfffc,&lt;br /&gt;
  0xaaa,0xa0f3, 0x5554, 0xfe3f, 0xfe3c,&lt;br /&gt;
  0xaaa,0xa803, 0x5554, 0xfc3f, 0xcc3c,&lt;br /&gt;
  0xaaa,0xaa83, 0x5554, 0xd7ff, 0xffd4,&lt;br /&gt;
  0xaaa,0xaaa3, 0xd554, 0xd73f, 0x3cd4,&lt;br /&gt;
  0xaaa,0xaaa3, 0xf555, 0x3f00, 0x00f0,&lt;br /&gt;
  0xaaa,0x0000, 0xffff, 0xcfff, 0xffc0,&lt;br /&gt;
  0xaaa,0x003f, 0x0000, 0x0000, 0x0000,&lt;br /&gt;
  0xaaa,0x003c, 0x03c0, 0x00f0, 0x3c00&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
NMT_GFX ngt;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  ngt.begin(11,10);&lt;br /&gt;
  ngt.print(&amp;quot;Hello!\nThis is an arduino writing\non screen with a &amp;quot;);&lt;br /&gt;
  ngt.println(ngt.get_card_ver());&lt;br /&gt;
&lt;br /&gt;
  delay(1200);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;Look, I can even do colors!&amp;quot;);&lt;br /&gt;
  ngt.set_color(3);&lt;br /&gt;
  ngt.print(&amp;quot;green &amp;quot;);&lt;br /&gt;
  ngt.set_color(2);&lt;br /&gt;
  ngt.print(&amp;quot;red &amp;quot;);&lt;br /&gt;
  ngt.set_color(1);&lt;br /&gt;
  ngt.println(&amp;quot;and white&amp;quot;);&lt;br /&gt;
  ngt.println(&amp;quot;(plus much more)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  delay(1400);&lt;br /&gt;
&lt;br /&gt;
  ngt.fill(2);&lt;br /&gt;
  ngt.println(&amp;quot;Screen fills can be done very fast.&amp;quot;);&lt;br /&gt;
  delay(1700);&lt;br /&gt;
&lt;br /&gt;
  ngt.clear();&lt;br /&gt;
  ngt.println(&amp;quot;I dont only do text though, so&amp;quot;);&lt;br /&gt;
  ngt.println(&amp;quot;let me load my graphics&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  // This line simply sets the size and center of the sprite.&lt;br /&gt;
  // It modifies the 'cat' byte data, so that when it is&lt;br /&gt;
  // uploaded, it will be the correct size, and centered.&lt;br /&gt;
  // We don't do this for the tree, as this data is specified directly&lt;br /&gt;
  // in the initialization of the 'tree' array.  Either way&lt;br /&gt;
  // of initializing it works perfectly fine.&lt;br /&gt;
  Sprite catSprite((byte*)cat, 33, 17, 16, 16);&lt;br /&gt;
&lt;br /&gt;
  int treeHandle = ngt.uploadSprite(tree);&lt;br /&gt;
  int catHandle = ngt.uploadSprite((byte*)cat);&lt;br /&gt;
&lt;br /&gt;
  delay(750);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;I can do lines and sprites efficiently too.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  byte x_tiles=ngt.x_tiles();              // Get tiles for x and y&lt;br /&gt;
  byte y_tiles=ngt.y_tiles();&lt;br /&gt;
  ngt.block_color(64+63,0b001100);&lt;br /&gt;
  ngt.block_color(128+63,0b011000);        // define tree colors&lt;br /&gt;
  ngt.block_color(192+63,0b010001);&lt;br /&gt;
  for(byte i=0;i&amp;lt;50;i++){    // make all tiles use text coloring&lt;br /&gt;
    ngt.tile_color(i,0);&lt;br /&gt;
  }&lt;br /&gt;
  for(byte i=x_tiles-1;i&amp;lt;50;i+=x_tiles){   // Make bottom tiles use tree coloring&lt;br /&gt;
    ngt.tile_color(i,63);&lt;br /&gt;
  }&lt;br /&gt;
  ngt.sprite(4, 180, 0, treeHandle);&lt;br /&gt;
  ngt.sprite(13, 180, 0, treeHandle);&lt;br /&gt;
  ngt.sprite(48, 180, 2, treeHandle);&lt;br /&gt;
  ngt.sprite(68, 180, 0, catHandle);&lt;br /&gt;
  ngt.sprite(28, 180, 1, treeHandle);&lt;br /&gt;
  ngt.line(0, 150, 30, 130);&lt;br /&gt;
  ngt.fast(60, 150);&lt;br /&gt;
  ngt.fast(90, 130);&lt;br /&gt;
  ngt.fast(120, 150);&lt;br /&gt;
  ngt.fast(150, 130);&lt;br /&gt;
&lt;br /&gt;
  delay(1000);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(' ');&lt;br /&gt;
  ngt.set_color(1);&lt;br /&gt;
  ngt.println(&amp;quot;The trees are drawn as sprites: an image of one is stored and copied to the screen using only one command, instead of resending the entire tree over serial.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  delay(6000);&lt;br /&gt;
&lt;br /&gt;
  ngt.set_cursor_pos(10, 190);&lt;br /&gt;
  ngt.println(&amp;quot;Now I'll show you a moving rainbow sprite.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  delay(1750);&lt;br /&gt;
&lt;br /&gt;
  ngt.clear();&lt;br /&gt;
&lt;br /&gt;
  // This sets up the colors for each tile&lt;br /&gt;
  for(byte i=0;i&amp;lt;64;i++){&lt;br /&gt;
    ngt.block_color(128+i,i); // RAINBOW&lt;br /&gt;
    ngt.block_color(64+i,25); // PINK for Nyan Cat&lt;br /&gt;
    ngt.block_color(192+i,0b101010); // GREY for Nyan Cat&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // We only need to call these once.  Instead of clearing the screen each frame,&lt;br /&gt;
  // we will erase the old sprite right before drawing a new one.  This allows faster&lt;br /&gt;
  // rendering, and frees us from the need to write a message over and over again.&lt;br /&gt;
  ngt.clear();&lt;br /&gt;
  ngt.set_color(3);&lt;br /&gt;
  ngt.println(&amp;quot;Moving rainbow sprite!&amp;quot;);&lt;br /&gt;
  ngt.set_color(0);&lt;br /&gt;
&lt;br /&gt;
  // This tells the NGT31 to draw only when nothing is being displayed.&lt;br /&gt;
  // Doing this reduces visual artifacts and makes the video data look cleaner.&lt;br /&gt;
  // This only really affects moving objects and changing images: static&lt;br /&gt;
  // text is not affected.&lt;br /&gt;
  // 'B' stands for 'Blanking' - it only draws when no video data is being sent to the monitor.&lt;br /&gt;
  ngt.set_draw_mode('B');&lt;br /&gt;
&lt;br /&gt;
  int lastX = 0;&lt;br /&gt;
  int lastY = 40;&lt;br /&gt;
  int vx = 1, vy = 1;&lt;br /&gt;
&lt;br /&gt;
  while(true) {&lt;br /&gt;
    long ls = millis();&lt;br /&gt;
&lt;br /&gt;
    int x = lastX + vx;&lt;br /&gt;
    int y = lastY + vy;&lt;br /&gt;
    ngt.moveSprite(lastX, lastY, x, y, 0, catHandle);&lt;br /&gt;
&lt;br /&gt;
    lastX = x;&lt;br /&gt;
    lastY = y;&lt;br /&gt;
    if (x &amp;gt;= 300)&lt;br /&gt;
      vx = -1;&lt;br /&gt;
    else if (x &amp;lt;= 0)&lt;br /&gt;
      vx = 1;&lt;br /&gt;
    if (y &amp;gt;= 240)&lt;br /&gt;
      vy = -1;&lt;br /&gt;
    else if (y &amp;lt;= 30)&lt;br /&gt;
      vy = 1;&lt;br /&gt;
&lt;br /&gt;
    while(ls + 16 &amp;gt; millis());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 3D Example and Explanation ===&lt;br /&gt;
&lt;br /&gt;
Drawing 3D lines, as part of a very simple 3D videogame demonstration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;NMT_GFX.h&amp;gt;&lt;br /&gt;
#include &amp;lt;PS2Keyboard.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
NMT_GFX ngt;&lt;br /&gt;
&lt;br /&gt;
PS2Keyboard keyboard;&lt;br /&gt;
&lt;br /&gt;
// This buffer is used to build the world command data before sending it to VRAM&lt;br /&gt;
uint16_t buffer[256];&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  keyboard.begin(4, 2);&lt;br /&gt;
  ngt.begin(11,10);&lt;br /&gt;
  ngt.println(&amp;quot;Loading world...&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  // This object functions as a tool to build the command data, and later send it to the NGT31.&lt;br /&gt;
  Object3D world(ngt, buffer);&lt;br /&gt;
  world.addLine(-1000,-100,-1000,1000,-100,-1000,2);        &lt;br /&gt;
  world.addLine(1000,-100,-1000,1000,100,-1000,2);               &lt;br /&gt;
  world.addLine(1000,100,-1000,1000,100,1000,2);                 &lt;br /&gt;
  world.addLine(1000,100,1000,-1000,100,1000,2);              &lt;br /&gt;
  world.addLine(-1000,100,1000,-1000,-100,1000,2);              &lt;br /&gt;
  world.addLine(-1000,-100,1000,-1000,-100,-1000,2);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(-1000,-100,1000,1000,-100,1000,2);            &lt;br /&gt;
  world.addLine(1000,-100,-1000,1000,-100,1000,2);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(-1000,-100,-1000,-1000,100,-1000,2);&lt;br /&gt;
  world.addLine(1000,-100,1000,1000,100,1000,2);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(-1000,100,-1000,1000,100,-1000,2);&lt;br /&gt;
  world.addLine(-1000,100,-1000,-1000,100,1000,2);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(10,10,10,30,10,10,3);        &lt;br /&gt;
  world.addLine(30,10,10,30,30,10,3);               &lt;br /&gt;
  world.addLine(30,30,10,30,30,30,3);                 &lt;br /&gt;
  world.addLine(30,30,30,10,30,30,3);              &lt;br /&gt;
  world.addLine(10,30,30,10,10,30,3);              &lt;br /&gt;
  world.addLine(10,10,30,10,10,10,3);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(10,10,30,30,10,30,3);            &lt;br /&gt;
  world.addLine(30,10,10,30,10,30,3);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(10,10,10,10,30,10,3);&lt;br /&gt;
  world.addLine(30,10,30,30,30,30,3);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(10,30,10,30,30,10,3);&lt;br /&gt;
  world.addLine(10,30,10,10,30,30,3);&lt;br /&gt;
&lt;br /&gt;
  // Flush to VRAM.  After this the buffer is no longer needed, and can be&lt;br /&gt;
  // used for something else or deallocated.&lt;br /&gt;
  world.upload();&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;Setting up colors...&amp;quot;);&lt;br /&gt;
  for(byte i=0;i&amp;lt;64;i++) {&lt;br /&gt;
    ngt.block_color(128 + i, 0b001011);&lt;br /&gt;
    ngt.block_color(192 + i, 0b111111);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Supported on newer firmware - reduces flicker&lt;br /&gt;
  ngt.set_draw_mode('B');&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;Ready to play...&amp;quot;);&lt;br /&gt;
  delay(500);&lt;br /&gt;
  double x=0,z=0;&lt;br /&gt;
  int rot=0;&lt;br /&gt;
  while(true) {&lt;br /&gt;
    int32_t time=millis();&lt;br /&gt;
    if (keyboard.available()) {&lt;br /&gt;
&lt;br /&gt;
      // read the next key&lt;br /&gt;
      char c = keyboard.read();&lt;br /&gt;
      if (c == PS2_LEFTARROW) {&lt;br /&gt;
        rot+=3;&lt;br /&gt;
      } else if (c == PS2_RIGHTARROW) {&lt;br /&gt;
        rot-=3;&lt;br /&gt;
      } else if (c == PS2_UPARROW) {&lt;br /&gt;
        x-=cos(rot*PI/180);&lt;br /&gt;
        z+=sin(rot*PI/180);&lt;br /&gt;
      } else if (c == PS2_DOWNARROW) {&lt;br /&gt;
        x+=cos(rot*PI/180);&lt;br /&gt;
        z-=sin(rot*PI/180);&lt;br /&gt;
      }&lt;br /&gt;
      ngt.rotatef(-rot,0);&lt;br /&gt;
      ngt.translatef((int32_t)x,0,(int32_t)z);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Clear the display of the old world image&lt;br /&gt;
    ngt.clear();&lt;br /&gt;
&lt;br /&gt;
    // Draw the world in one call, based on the data loaded into VRAM&lt;br /&gt;
    world.draw();&lt;br /&gt;
&lt;br /&gt;
    // Set maximum framerate to 60FPS&lt;br /&gt;
    delay(max(1,(16+time)-(int32_t)millis()));&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here, an &amp;lt;code&amp;gt;Object3D&amp;lt;/code&amp;gt; is used to construct a bytecode of the 3D world.  This bytecode can be rendered significantly faster than if individual commands were issued for each line or shape in the world.&lt;br /&gt;
Due to the speed limitations of the serial port, issuing this many commands per frame would cut the FPS to less than 25 - and that does not include draw time.  By using &amp;lt;code&amp;gt;Object3D::draw()&amp;lt;/code&amp;gt;, the&lt;br /&gt;
serial communication overhead is limited to less than 0.5ms for each draw.&lt;br /&gt;
&lt;br /&gt;
The same Object3D may also be drawn multiple times at different locations by calling &amp;lt;code&amp;gt;draw()&amp;lt;/code&amp;gt; multiple times, and calling &amp;lt;code&amp;gt;translatef()&amp;lt;/code&amp;gt; in-between to change the position each time.  Multiple&lt;br /&gt;
Object3Ds may be loaded as well, and drawn as needed.  This way each object can be kept in VRAM, and quickly drawn each time it needs to be.&lt;br /&gt;
&lt;br /&gt;
= History =&lt;br /&gt;
&lt;br /&gt;
The below text is based on / copied from an old store listing, written years ago, which details the evolution of this line of graphics devices.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
###TVout&lt;br /&gt;
Good ole TVout.  Get two resistors, a breadboard, and an NTSC output port and you had monochrome video.  Looking back, this sucked.  I mean, TVout is great, but for me the circuits were unreliable, hard to transport, and delicate.  If I knew about the propeller chip at that point at least I would have had colors.  I made some games but it wasn’t what I really wanted in the end.&lt;br /&gt;
&lt;br /&gt;
###Keyboard cable stripping&lt;br /&gt;
I didn’t have PS2 ports to plug keyboards into, so I cut the end off the cable and connected the wires to the arduino.  That was a crude design.  Worked, but also ruined the keyboard.&lt;br /&gt;
&lt;br /&gt;
###First propeller days&lt;br /&gt;
The propeller was nice because it gave me color NTSC (and later VGA).  I also built the propeller into permanent circuits capable of NTSC output, which evolved into a standard for my graphics technology.  This even powered graphics for my early FPGA designs.&lt;br /&gt;
&lt;br /&gt;
###NGT20&lt;br /&gt;
Finally: a single, solid, reliable device for video output.  Bulky, but had almost all the bells and whistles.  No SMD yet, no keyboard yet, but it was the first clean design.  It was also the first design with VGA output.  I still used the keyboards with stripped cables at this point.&lt;br /&gt;
&lt;br /&gt;
###NGT30&lt;br /&gt;
All the bells and whistles.  Like the NGT20, it is solid and reliable, but it isn’t bulky and also features 3D graphics and a keyboard port. One shield, one keyboard, one arduino, and one monitor.  Nothing else is needed.  No hanging resistors.  No loose wires.  No more stripped cables.  No more crude designs.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=NGT31&amp;diff=58</id>
		<title>NGT31</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=NGT31&amp;diff=58"/>
		<updated>2024-06-21T22:19:43Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Update formatting of history section&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The NGT31 is a Parallax Propeller based VGA driver and graphics device, provided as an Arduino shield.  It uses a serial connection to the host device for communication, up to 250,000 baud with current firmware.&lt;br /&gt;
&lt;br /&gt;
This device provides many useful graphics functions, including accelerated sprite drawing, text, and even drawing lines in 3D.&lt;br /&gt;
&lt;br /&gt;
Firmware: https://git.nuclaer-servers.com/Nuclaer/ngt_firmware/-/tree/master&lt;br /&gt;
&lt;br /&gt;
Arduino Library: https://git.nuclaer-servers.com/Nuclaer/nmt_gfx/-/tree/master&lt;br /&gt;
&lt;br /&gt;
== Example Code ==&lt;br /&gt;
&lt;br /&gt;
Here is the demo included in the NMT_GFX library (https://git.nuclaer-servers.com/Nuclaer/nmt_gfx/-/blob/master/Examples/NGT31/NGT31_demo/NGT31_demo.ino):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;NMT_GFX.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
byte tree[] = { 1,8,0,0,&lt;br /&gt;
  0b01010100,0b00010101,&lt;br /&gt;
  0b01010101,0b01010101,&lt;br /&gt;
  0b01010101,0b01010101,&lt;br /&gt;
  0b01010101,0b01010101,&lt;br /&gt;
  0b10100011,0b00001010,&lt;br /&gt;
  0b10100000,0b00001010,&lt;br /&gt;
  0b10100000,0b00001010,&lt;br /&gt;
  0b10101000,0b00101010&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
unsigned short cat[] = { 0, 0,&lt;br /&gt;
  0xaaa,0x0000, 0xffff, 0xffff, 0xc000,&lt;br /&gt;
  0xaaa,0xaaa3, 0xf555, 0x5557, 0xf000,&lt;br /&gt;
  0xaaa,0xaaa3, 0xd555, 0x5555, 0xf000,&lt;br /&gt;
  0xaaa,0xaaa3, 0x5555, 0x5555, 0x7000,&lt;br /&gt;
  0xaaa,0xaaa3, 0x5555, 0x4155, 0x7000,&lt;br /&gt;
  0xaaa,0xaaa3, 0x5555, 0x3c55, 0x70f0,&lt;br /&gt;
  0xaaa,0x00a3, 0x5555, 0x3f15, 0x73f0,&lt;br /&gt;
  0xaaa,0x3c23, 0x5555, 0x3fc0, 0x0ff0,&lt;br /&gt;
  0xaaa,0x0f03, 0x5555, 0x3fff, 0xfff0,&lt;br /&gt;
  0xaaa,0x83c3, 0x5554, 0xffff, 0xfffc,&lt;br /&gt;
  0xaaa,0xa0f3, 0x5554, 0xfe3f, 0xfe3c,&lt;br /&gt;
  0xaaa,0xa803, 0x5554, 0xfc3f, 0xcc3c,&lt;br /&gt;
  0xaaa,0xaa83, 0x5554, 0xd7ff, 0xffd4,&lt;br /&gt;
  0xaaa,0xaaa3, 0xd554, 0xd73f, 0x3cd4,&lt;br /&gt;
  0xaaa,0xaaa3, 0xf555, 0x3f00, 0x00f0,&lt;br /&gt;
  0xaaa,0x0000, 0xffff, 0xcfff, 0xffc0,&lt;br /&gt;
  0xaaa,0x003f, 0x0000, 0x0000, 0x0000,&lt;br /&gt;
  0xaaa,0x003c, 0x03c0, 0x00f0, 0x3c00&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
NMT_GFX ngt;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  ngt.begin(11,10);&lt;br /&gt;
  ngt.print(&amp;quot;Hello!\nThis is an arduino writing\non screen with a &amp;quot;);&lt;br /&gt;
  ngt.println(ngt.get_card_ver());&lt;br /&gt;
&lt;br /&gt;
  delay(1200);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;Look, I can even do colors!&amp;quot;);&lt;br /&gt;
  ngt.set_color(3);&lt;br /&gt;
  ngt.print(&amp;quot;green &amp;quot;);&lt;br /&gt;
  ngt.set_color(2);&lt;br /&gt;
  ngt.print(&amp;quot;red &amp;quot;);&lt;br /&gt;
  ngt.set_color(1);&lt;br /&gt;
  ngt.println(&amp;quot;and white&amp;quot;);&lt;br /&gt;
  ngt.println(&amp;quot;(plus much more)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  delay(1400);&lt;br /&gt;
&lt;br /&gt;
  ngt.fill(2);&lt;br /&gt;
  ngt.println(&amp;quot;Screen fills can be done very fast.&amp;quot;);&lt;br /&gt;
  delay(1700);&lt;br /&gt;
&lt;br /&gt;
  ngt.clear();&lt;br /&gt;
  ngt.println(&amp;quot;I dont only do text though, so&amp;quot;);&lt;br /&gt;
  ngt.println(&amp;quot;let me load my graphics&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  // This line simply sets the size and center of the sprite.&lt;br /&gt;
  // It modifies the 'cat' byte data, so that when it is&lt;br /&gt;
  // uploaded, it will be the correct size, and centered.&lt;br /&gt;
  // We don't do this for the tree, as this data is specified directly&lt;br /&gt;
  // in the initialization of the 'tree' array.  Either way&lt;br /&gt;
  // of initializing it works perfectly fine.&lt;br /&gt;
  Sprite catSprite((byte*)cat, 33, 17, 16, 16);&lt;br /&gt;
&lt;br /&gt;
  int treeHandle = ngt.uploadSprite(tree);&lt;br /&gt;
  int catHandle = ngt.uploadSprite((byte*)cat);&lt;br /&gt;
&lt;br /&gt;
  delay(750);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;I can do lines and sprites efficiently too.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  byte x_tiles=ngt.x_tiles();              // Get tiles for x and y&lt;br /&gt;
  byte y_tiles=ngt.y_tiles();&lt;br /&gt;
  ngt.block_color(64+63,0b001100);&lt;br /&gt;
  ngt.block_color(128+63,0b011000);        // define tree colors&lt;br /&gt;
  ngt.block_color(192+63,0b010001);&lt;br /&gt;
  for(byte i=0;i&amp;lt;50;i++){    // make all tiles use text coloring&lt;br /&gt;
    ngt.tile_color(i,0);&lt;br /&gt;
  }&lt;br /&gt;
  for(byte i=x_tiles-1;i&amp;lt;50;i+=x_tiles){   // Make bottom tiles use tree coloring&lt;br /&gt;
    ngt.tile_color(i,63);&lt;br /&gt;
  }&lt;br /&gt;
  ngt.sprite(4, 180, 0, treeHandle);&lt;br /&gt;
  ngt.sprite(13, 180, 0, treeHandle);&lt;br /&gt;
  ngt.sprite(48, 180, 2, treeHandle);&lt;br /&gt;
  ngt.sprite(68, 180, 0, catHandle);&lt;br /&gt;
  ngt.sprite(28, 180, 1, treeHandle);&lt;br /&gt;
  ngt.line(0, 150, 30, 130);&lt;br /&gt;
  ngt.fast(60, 150);&lt;br /&gt;
  ngt.fast(90, 130);&lt;br /&gt;
  ngt.fast(120, 150);&lt;br /&gt;
  ngt.fast(150, 130);&lt;br /&gt;
&lt;br /&gt;
  delay(1000);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(' ');&lt;br /&gt;
  ngt.set_color(1);&lt;br /&gt;
  ngt.println(&amp;quot;The trees are drawn as sprites: an image of one is stored and copied to the screen using only one command, instead of resending the entire tree over serial.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  delay(6000);&lt;br /&gt;
&lt;br /&gt;
  ngt.set_cursor_pos(10, 190);&lt;br /&gt;
  ngt.println(&amp;quot;Now I'll show you a moving rainbow sprite.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  delay(1750);&lt;br /&gt;
&lt;br /&gt;
  ngt.clear();&lt;br /&gt;
&lt;br /&gt;
  // This sets up the colors for each tile&lt;br /&gt;
  for(byte i=0;i&amp;lt;64;i++){&lt;br /&gt;
    ngt.block_color(128+i,i); // RAINBOW&lt;br /&gt;
    ngt.block_color(64+i,25); // PINK for Nyan Cat&lt;br /&gt;
    ngt.block_color(192+i,0b101010); // GREY for Nyan Cat&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // We only need to call these once.  Instead of clearing the screen each frame,&lt;br /&gt;
  // we will erase the old sprite right before drawing a new one.  This allows faster&lt;br /&gt;
  // rendering, and frees us from the need to write a message over and over again.&lt;br /&gt;
  ngt.clear();&lt;br /&gt;
  ngt.set_color(3);&lt;br /&gt;
  ngt.println(&amp;quot;Moving rainbow sprite!&amp;quot;);&lt;br /&gt;
  ngt.set_color(0);&lt;br /&gt;
&lt;br /&gt;
  // This tells the NGT31 to draw only when nothing is being displayed.&lt;br /&gt;
  // Doing this reduces visual artifacts and makes the video data look cleaner.&lt;br /&gt;
  // This only really affects moving objects and changing images: static&lt;br /&gt;
  // text is not affected.&lt;br /&gt;
  // 'B' stands for 'Blanking' - it only draws when no video data is being sent to the monitor.&lt;br /&gt;
  ngt.set_draw_mode('B');&lt;br /&gt;
&lt;br /&gt;
  int lastX = 0;&lt;br /&gt;
  int lastY = 40;&lt;br /&gt;
  int vx = 1, vy = 1;&lt;br /&gt;
&lt;br /&gt;
  while(true) {&lt;br /&gt;
    long ls = millis();&lt;br /&gt;
&lt;br /&gt;
    int x = lastX + vx;&lt;br /&gt;
    int y = lastY + vy;&lt;br /&gt;
    ngt.moveSprite(lastX, lastY, x, y, 0, catHandle);&lt;br /&gt;
&lt;br /&gt;
    lastX = x;&lt;br /&gt;
    lastY = y;&lt;br /&gt;
    if (x &amp;gt;= 300)&lt;br /&gt;
      vx = -1;&lt;br /&gt;
    else if (x &amp;lt;= 0)&lt;br /&gt;
      vx = 1;&lt;br /&gt;
    if (y &amp;gt;= 240)&lt;br /&gt;
      vy = -1;&lt;br /&gt;
    else if (y &amp;lt;= 30)&lt;br /&gt;
      vy = 1;&lt;br /&gt;
&lt;br /&gt;
    while(ls + 16 &amp;gt; millis());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 3D Example and Explanation ===&lt;br /&gt;
&lt;br /&gt;
Drawing 3D lines, as part of a very simple 3D videogame demonstration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;NMT_GFX.h&amp;gt;&lt;br /&gt;
#include &amp;lt;PS2Keyboard.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
NMT_GFX ngt;&lt;br /&gt;
&lt;br /&gt;
PS2Keyboard keyboard;&lt;br /&gt;
&lt;br /&gt;
uint16_t buffer[256];&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  keyboard.begin(4, 2);&lt;br /&gt;
  ngt.begin(11,10);&lt;br /&gt;
  ngt.println(&amp;quot;Loading world...&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  Object3D world(ngt, buffer);&lt;br /&gt;
  world.addLine(-1000,-100,-1000,1000,-100,-1000,2);        &lt;br /&gt;
  world.addLine(1000,-100,-1000,1000,100,-1000,2);               &lt;br /&gt;
  world.addLine(1000,100,-1000,1000,100,1000,2);                 &lt;br /&gt;
  world.addLine(1000,100,1000,-1000,100,1000,2);              &lt;br /&gt;
  world.addLine(-1000,100,1000,-1000,-100,1000,2);              &lt;br /&gt;
  world.addLine(-1000,-100,1000,-1000,-100,-1000,2);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(-1000,-100,1000,1000,-100,1000,2);            &lt;br /&gt;
  world.addLine(1000,-100,-1000,1000,-100,1000,2);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(-1000,-100,-1000,-1000,100,-1000,2);&lt;br /&gt;
  world.addLine(1000,-100,1000,1000,100,1000,2);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(-1000,100,-1000,1000,100,-1000,2);&lt;br /&gt;
  world.addLine(-1000,100,-1000,-1000,100,1000,2);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(10,10,10,30,10,10,3);        &lt;br /&gt;
  world.addLine(30,10,10,30,30,10,3);               &lt;br /&gt;
  world.addLine(30,30,10,30,30,30,3);                 &lt;br /&gt;
  world.addLine(30,30,30,10,30,30,3);              &lt;br /&gt;
  world.addLine(10,30,30,10,10,30,3);              &lt;br /&gt;
  world.addLine(10,10,30,10,10,10,3);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(10,10,30,30,10,30,3);            &lt;br /&gt;
  world.addLine(30,10,10,30,10,30,3);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(10,10,10,10,30,10,3);&lt;br /&gt;
  world.addLine(30,10,30,30,30,30,3);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(10,30,10,30,30,10,3);&lt;br /&gt;
  world.addLine(10,30,10,10,30,30,3);&lt;br /&gt;
&lt;br /&gt;
  world.upload();&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;Setting up colors...&amp;quot;);&lt;br /&gt;
  for(byte i=0;i&amp;lt;64;i++) {&lt;br /&gt;
    ngt.block_color(128 + i, 0b001011);&lt;br /&gt;
    ngt.block_color(192 + i, 0b111111);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Supported on newer firmware - reduces flicker&lt;br /&gt;
  ngt.set_draw_mode('B');&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;Ready to play...&amp;quot;);&lt;br /&gt;
  delay(500);&lt;br /&gt;
  double x=0,z=0;&lt;br /&gt;
  int rot=0;&lt;br /&gt;
  while(true) {&lt;br /&gt;
    int32_t time=millis();&lt;br /&gt;
    if (keyboard.available()) {&lt;br /&gt;
&lt;br /&gt;
      // read the next key&lt;br /&gt;
      char c = keyboard.read();&lt;br /&gt;
      if (c == PS2_LEFTARROW) {&lt;br /&gt;
        rot+=3;&lt;br /&gt;
      } else if (c == PS2_RIGHTARROW) {&lt;br /&gt;
        rot-=3;&lt;br /&gt;
      } else if (c == PS2_UPARROW) {&lt;br /&gt;
        x-=cos(rot*PI/180);&lt;br /&gt;
        z+=sin(rot*PI/180);&lt;br /&gt;
      } else if (c == PS2_DOWNARROW) {&lt;br /&gt;
        x+=cos(rot*PI/180);&lt;br /&gt;
        z-=sin(rot*PI/180);&lt;br /&gt;
      }&lt;br /&gt;
      ngt.rotatef(-rot,0);&lt;br /&gt;
      ngt.translatef((int32_t)x,0,(int32_t)z);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //ngt.clear();&lt;br /&gt;
    long timer = millis();&lt;br /&gt;
    world.draw();&lt;br /&gt;
    //Serial.println(millis() - timer);&lt;br /&gt;
&lt;br /&gt;
    delay(max(1,(16+time)-(int32_t)millis()));&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here, an &amp;lt;code&amp;gt;Object3D&amp;lt;/code&amp;gt; is used to construct a bytecode of the 3D world.  This bytecode can be rendered significantly faster than if individual commands were issued for each line or shape in the world.&lt;br /&gt;
Due to the speed limitations of the serial port, issuing this many commands per frame would cut the FPS to less than 25 - and that does not include draw time.  By using &amp;lt;code&amp;gt;Object3D::draw()&amp;lt;/code&amp;gt;, the&lt;br /&gt;
serial communication overhead is limited to less than 0.5ms for each draw.&lt;br /&gt;
&lt;br /&gt;
The same Object3D may also be drawn multiple times at different locations by calling &amp;lt;code&amp;gt;draw()&amp;lt;/code&amp;gt; multiple times, and calling &amp;lt;code&amp;gt;translatef()&amp;lt;/code&amp;gt; in-between to change the position each time.  Multiple&lt;br /&gt;
Object3Ds may be loaded as well, and drawn as needed.  This way each object can be kept in VRAM, and quickly drawn each time it needs to be.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= History =&lt;br /&gt;
&lt;br /&gt;
The below text is based on / copied from an old store listing, written years ago, which details the evolution of this line of graphics devices.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
###TVout&lt;br /&gt;
Good ole TVout.  Get two resistors, a breadboard, and an NTSC output port and you had monochrome video.  Looking back, this sucked.  I mean, TVout is great, but for me the circuits were unreliable, hard to transport, and delicate.  If I knew about the propeller chip at that point at least I would have had colors.  I made some games but it wasn’t what I really wanted in the end.&lt;br /&gt;
&lt;br /&gt;
###Keyboard cable stripping&lt;br /&gt;
I didn’t have PS2 ports to plug keyboards into, so I cut the end off the cable and connected the wires to the arduino.  That was a crude design.  Worked, but also ruined the keyboard.&lt;br /&gt;
&lt;br /&gt;
###First propeller days&lt;br /&gt;
The propeller was nice because it gave me color NTSC (and later VGA).  I also built the propeller into permanent circuits capable of NTSC output, which evolved into a standard for my graphics technology.  This even powered graphics for my early FPGA designs.&lt;br /&gt;
&lt;br /&gt;
###NGT20&lt;br /&gt;
Finally: a single, solid, reliable device for video output.  Bulky, but had almost all the bells and whistles.  No SMD yet, no keyboard yet, but it was the first clean design.  It was also the first design with VGA output.  I still used the keyboards with stripped cables at this point.&lt;br /&gt;
&lt;br /&gt;
###NGT30&lt;br /&gt;
All the bells and whistles.  Like the NGT20, it is solid and reliable, but it isn’t bulky and also features 3D graphics and a keyboard port. One shield, one keyboard, one arduino, and one monitor.  Nothing else is needed.  No hanging resistors.  No loose wires.  No more stripped cables.  No more crude designs.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=NGT31&amp;diff=57</id>
		<title>NGT31</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=NGT31&amp;diff=57"/>
		<updated>2024-06-21T22:18:57Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Add history section.  It needs a lot of cleanup.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The NGT31 is a Parallax Propeller based VGA driver and graphics device, provided as an Arduino shield.  It uses a serial connection to the host device for communication, up to 250,000 baud with current firmware.&lt;br /&gt;
&lt;br /&gt;
This device provides many useful graphics functions, including accelerated sprite drawing, text, and even drawing lines in 3D.&lt;br /&gt;
&lt;br /&gt;
Firmware: https://git.nuclaer-servers.com/Nuclaer/ngt_firmware/-/tree/master&lt;br /&gt;
&lt;br /&gt;
Arduino Library: https://git.nuclaer-servers.com/Nuclaer/nmt_gfx/-/tree/master&lt;br /&gt;
&lt;br /&gt;
== Example Code ==&lt;br /&gt;
&lt;br /&gt;
Here is the demo included in the NMT_GFX library (https://git.nuclaer-servers.com/Nuclaer/nmt_gfx/-/blob/master/Examples/NGT31/NGT31_demo/NGT31_demo.ino):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;NMT_GFX.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
byte tree[] = { 1,8,0,0,&lt;br /&gt;
  0b01010100,0b00010101,&lt;br /&gt;
  0b01010101,0b01010101,&lt;br /&gt;
  0b01010101,0b01010101,&lt;br /&gt;
  0b01010101,0b01010101,&lt;br /&gt;
  0b10100011,0b00001010,&lt;br /&gt;
  0b10100000,0b00001010,&lt;br /&gt;
  0b10100000,0b00001010,&lt;br /&gt;
  0b10101000,0b00101010&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
unsigned short cat[] = { 0, 0,&lt;br /&gt;
  0xaaa,0x0000, 0xffff, 0xffff, 0xc000,&lt;br /&gt;
  0xaaa,0xaaa3, 0xf555, 0x5557, 0xf000,&lt;br /&gt;
  0xaaa,0xaaa3, 0xd555, 0x5555, 0xf000,&lt;br /&gt;
  0xaaa,0xaaa3, 0x5555, 0x5555, 0x7000,&lt;br /&gt;
  0xaaa,0xaaa3, 0x5555, 0x4155, 0x7000,&lt;br /&gt;
  0xaaa,0xaaa3, 0x5555, 0x3c55, 0x70f0,&lt;br /&gt;
  0xaaa,0x00a3, 0x5555, 0x3f15, 0x73f0,&lt;br /&gt;
  0xaaa,0x3c23, 0x5555, 0x3fc0, 0x0ff0,&lt;br /&gt;
  0xaaa,0x0f03, 0x5555, 0x3fff, 0xfff0,&lt;br /&gt;
  0xaaa,0x83c3, 0x5554, 0xffff, 0xfffc,&lt;br /&gt;
  0xaaa,0xa0f3, 0x5554, 0xfe3f, 0xfe3c,&lt;br /&gt;
  0xaaa,0xa803, 0x5554, 0xfc3f, 0xcc3c,&lt;br /&gt;
  0xaaa,0xaa83, 0x5554, 0xd7ff, 0xffd4,&lt;br /&gt;
  0xaaa,0xaaa3, 0xd554, 0xd73f, 0x3cd4,&lt;br /&gt;
  0xaaa,0xaaa3, 0xf555, 0x3f00, 0x00f0,&lt;br /&gt;
  0xaaa,0x0000, 0xffff, 0xcfff, 0xffc0,&lt;br /&gt;
  0xaaa,0x003f, 0x0000, 0x0000, 0x0000,&lt;br /&gt;
  0xaaa,0x003c, 0x03c0, 0x00f0, 0x3c00&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
NMT_GFX ngt;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  ngt.begin(11,10);&lt;br /&gt;
  ngt.print(&amp;quot;Hello!\nThis is an arduino writing\non screen with a &amp;quot;);&lt;br /&gt;
  ngt.println(ngt.get_card_ver());&lt;br /&gt;
&lt;br /&gt;
  delay(1200);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;Look, I can even do colors!&amp;quot;);&lt;br /&gt;
  ngt.set_color(3);&lt;br /&gt;
  ngt.print(&amp;quot;green &amp;quot;);&lt;br /&gt;
  ngt.set_color(2);&lt;br /&gt;
  ngt.print(&amp;quot;red &amp;quot;);&lt;br /&gt;
  ngt.set_color(1);&lt;br /&gt;
  ngt.println(&amp;quot;and white&amp;quot;);&lt;br /&gt;
  ngt.println(&amp;quot;(plus much more)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  delay(1400);&lt;br /&gt;
&lt;br /&gt;
  ngt.fill(2);&lt;br /&gt;
  ngt.println(&amp;quot;Screen fills can be done very fast.&amp;quot;);&lt;br /&gt;
  delay(1700);&lt;br /&gt;
&lt;br /&gt;
  ngt.clear();&lt;br /&gt;
  ngt.println(&amp;quot;I dont only do text though, so&amp;quot;);&lt;br /&gt;
  ngt.println(&amp;quot;let me load my graphics&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  // This line simply sets the size and center of the sprite.&lt;br /&gt;
  // It modifies the 'cat' byte data, so that when it is&lt;br /&gt;
  // uploaded, it will be the correct size, and centered.&lt;br /&gt;
  // We don't do this for the tree, as this data is specified directly&lt;br /&gt;
  // in the initialization of the 'tree' array.  Either way&lt;br /&gt;
  // of initializing it works perfectly fine.&lt;br /&gt;
  Sprite catSprite((byte*)cat, 33, 17, 16, 16);&lt;br /&gt;
&lt;br /&gt;
  int treeHandle = ngt.uploadSprite(tree);&lt;br /&gt;
  int catHandle = ngt.uploadSprite((byte*)cat);&lt;br /&gt;
&lt;br /&gt;
  delay(750);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;I can do lines and sprites efficiently too.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  byte x_tiles=ngt.x_tiles();              // Get tiles for x and y&lt;br /&gt;
  byte y_tiles=ngt.y_tiles();&lt;br /&gt;
  ngt.block_color(64+63,0b001100);&lt;br /&gt;
  ngt.block_color(128+63,0b011000);        // define tree colors&lt;br /&gt;
  ngt.block_color(192+63,0b010001);&lt;br /&gt;
  for(byte i=0;i&amp;lt;50;i++){    // make all tiles use text coloring&lt;br /&gt;
    ngt.tile_color(i,0);&lt;br /&gt;
  }&lt;br /&gt;
  for(byte i=x_tiles-1;i&amp;lt;50;i+=x_tiles){   // Make bottom tiles use tree coloring&lt;br /&gt;
    ngt.tile_color(i,63);&lt;br /&gt;
  }&lt;br /&gt;
  ngt.sprite(4, 180, 0, treeHandle);&lt;br /&gt;
  ngt.sprite(13, 180, 0, treeHandle);&lt;br /&gt;
  ngt.sprite(48, 180, 2, treeHandle);&lt;br /&gt;
  ngt.sprite(68, 180, 0, catHandle);&lt;br /&gt;
  ngt.sprite(28, 180, 1, treeHandle);&lt;br /&gt;
  ngt.line(0, 150, 30, 130);&lt;br /&gt;
  ngt.fast(60, 150);&lt;br /&gt;
  ngt.fast(90, 130);&lt;br /&gt;
  ngt.fast(120, 150);&lt;br /&gt;
  ngt.fast(150, 130);&lt;br /&gt;
&lt;br /&gt;
  delay(1000);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(' ');&lt;br /&gt;
  ngt.set_color(1);&lt;br /&gt;
  ngt.println(&amp;quot;The trees are drawn as sprites: an image of one is stored and copied to the screen using only one command, instead of resending the entire tree over serial.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  delay(6000);&lt;br /&gt;
&lt;br /&gt;
  ngt.set_cursor_pos(10, 190);&lt;br /&gt;
  ngt.println(&amp;quot;Now I'll show you a moving rainbow sprite.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  delay(1750);&lt;br /&gt;
&lt;br /&gt;
  ngt.clear();&lt;br /&gt;
&lt;br /&gt;
  // This sets up the colors for each tile&lt;br /&gt;
  for(byte i=0;i&amp;lt;64;i++){&lt;br /&gt;
    ngt.block_color(128+i,i); // RAINBOW&lt;br /&gt;
    ngt.block_color(64+i,25); // PINK for Nyan Cat&lt;br /&gt;
    ngt.block_color(192+i,0b101010); // GREY for Nyan Cat&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // We only need to call these once.  Instead of clearing the screen each frame,&lt;br /&gt;
  // we will erase the old sprite right before drawing a new one.  This allows faster&lt;br /&gt;
  // rendering, and frees us from the need to write a message over and over again.&lt;br /&gt;
  ngt.clear();&lt;br /&gt;
  ngt.set_color(3);&lt;br /&gt;
  ngt.println(&amp;quot;Moving rainbow sprite!&amp;quot;);&lt;br /&gt;
  ngt.set_color(0);&lt;br /&gt;
&lt;br /&gt;
  // This tells the NGT31 to draw only when nothing is being displayed.&lt;br /&gt;
  // Doing this reduces visual artifacts and makes the video data look cleaner.&lt;br /&gt;
  // This only really affects moving objects and changing images: static&lt;br /&gt;
  // text is not affected.&lt;br /&gt;
  // 'B' stands for 'Blanking' - it only draws when no video data is being sent to the monitor.&lt;br /&gt;
  ngt.set_draw_mode('B');&lt;br /&gt;
&lt;br /&gt;
  int lastX = 0;&lt;br /&gt;
  int lastY = 40;&lt;br /&gt;
  int vx = 1, vy = 1;&lt;br /&gt;
&lt;br /&gt;
  while(true) {&lt;br /&gt;
    long ls = millis();&lt;br /&gt;
&lt;br /&gt;
    int x = lastX + vx;&lt;br /&gt;
    int y = lastY + vy;&lt;br /&gt;
    ngt.moveSprite(lastX, lastY, x, y, 0, catHandle);&lt;br /&gt;
&lt;br /&gt;
    lastX = x;&lt;br /&gt;
    lastY = y;&lt;br /&gt;
    if (x &amp;gt;= 300)&lt;br /&gt;
      vx = -1;&lt;br /&gt;
    else if (x &amp;lt;= 0)&lt;br /&gt;
      vx = 1;&lt;br /&gt;
    if (y &amp;gt;= 240)&lt;br /&gt;
      vy = -1;&lt;br /&gt;
    else if (y &amp;lt;= 30)&lt;br /&gt;
      vy = 1;&lt;br /&gt;
&lt;br /&gt;
    while(ls + 16 &amp;gt; millis());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 3D Example and Explanation ===&lt;br /&gt;
&lt;br /&gt;
Drawing 3D lines, as part of a very simple 3D videogame demonstration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;NMT_GFX.h&amp;gt;&lt;br /&gt;
#include &amp;lt;PS2Keyboard.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
NMT_GFX ngt;&lt;br /&gt;
&lt;br /&gt;
PS2Keyboard keyboard;&lt;br /&gt;
&lt;br /&gt;
uint16_t buffer[256];&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  keyboard.begin(4, 2);&lt;br /&gt;
  ngt.begin(11,10);&lt;br /&gt;
  ngt.println(&amp;quot;Loading world...&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  Object3D world(ngt, buffer);&lt;br /&gt;
  world.addLine(-1000,-100,-1000,1000,-100,-1000,2);        &lt;br /&gt;
  world.addLine(1000,-100,-1000,1000,100,-1000,2);               &lt;br /&gt;
  world.addLine(1000,100,-1000,1000,100,1000,2);                 &lt;br /&gt;
  world.addLine(1000,100,1000,-1000,100,1000,2);              &lt;br /&gt;
  world.addLine(-1000,100,1000,-1000,-100,1000,2);              &lt;br /&gt;
  world.addLine(-1000,-100,1000,-1000,-100,-1000,2);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(-1000,-100,1000,1000,-100,1000,2);            &lt;br /&gt;
  world.addLine(1000,-100,-1000,1000,-100,1000,2);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(-1000,-100,-1000,-1000,100,-1000,2);&lt;br /&gt;
  world.addLine(1000,-100,1000,1000,100,1000,2);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(-1000,100,-1000,1000,100,-1000,2);&lt;br /&gt;
  world.addLine(-1000,100,-1000,-1000,100,1000,2);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(10,10,10,30,10,10,3);        &lt;br /&gt;
  world.addLine(30,10,10,30,30,10,3);               &lt;br /&gt;
  world.addLine(30,30,10,30,30,30,3);                 &lt;br /&gt;
  world.addLine(30,30,30,10,30,30,3);              &lt;br /&gt;
  world.addLine(10,30,30,10,10,30,3);              &lt;br /&gt;
  world.addLine(10,10,30,10,10,10,3);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(10,10,30,30,10,30,3);            &lt;br /&gt;
  world.addLine(30,10,10,30,10,30,3);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(10,10,10,10,30,10,3);&lt;br /&gt;
  world.addLine(30,10,30,30,30,30,3);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(10,30,10,30,30,10,3);&lt;br /&gt;
  world.addLine(10,30,10,10,30,30,3);&lt;br /&gt;
&lt;br /&gt;
  world.upload();&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;Setting up colors...&amp;quot;);&lt;br /&gt;
  for(byte i=0;i&amp;lt;64;i++) {&lt;br /&gt;
    ngt.block_color(128 + i, 0b001011);&lt;br /&gt;
    ngt.block_color(192 + i, 0b111111);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Supported on newer firmware - reduces flicker&lt;br /&gt;
  ngt.set_draw_mode('B');&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;Ready to play...&amp;quot;);&lt;br /&gt;
  delay(500);&lt;br /&gt;
  double x=0,z=0;&lt;br /&gt;
  int rot=0;&lt;br /&gt;
  while(true) {&lt;br /&gt;
    int32_t time=millis();&lt;br /&gt;
    if (keyboard.available()) {&lt;br /&gt;
&lt;br /&gt;
      // read the next key&lt;br /&gt;
      char c = keyboard.read();&lt;br /&gt;
      if (c == PS2_LEFTARROW) {&lt;br /&gt;
        rot+=3;&lt;br /&gt;
      } else if (c == PS2_RIGHTARROW) {&lt;br /&gt;
        rot-=3;&lt;br /&gt;
      } else if (c == PS2_UPARROW) {&lt;br /&gt;
        x-=cos(rot*PI/180);&lt;br /&gt;
        z+=sin(rot*PI/180);&lt;br /&gt;
      } else if (c == PS2_DOWNARROW) {&lt;br /&gt;
        x+=cos(rot*PI/180);&lt;br /&gt;
        z-=sin(rot*PI/180);&lt;br /&gt;
      }&lt;br /&gt;
      ngt.rotatef(-rot,0);&lt;br /&gt;
      ngt.translatef((int32_t)x,0,(int32_t)z);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //ngt.clear();&lt;br /&gt;
    long timer = millis();&lt;br /&gt;
    world.draw();&lt;br /&gt;
    //Serial.println(millis() - timer);&lt;br /&gt;
&lt;br /&gt;
    delay(max(1,(16+time)-(int32_t)millis()));&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here, an &amp;lt;code&amp;gt;Object3D&amp;lt;/code&amp;gt; is used to construct a bytecode of the 3D world.  This bytecode can be rendered significantly faster than if individual commands were issued for each line or shape in the world.&lt;br /&gt;
Due to the speed limitations of the serial port, issuing this many commands per frame would cut the FPS to less than 25 - and that does not include draw time.  By using &amp;lt;code&amp;gt;Object3D::draw()&amp;lt;/code&amp;gt;, the&lt;br /&gt;
serial communication overhead is limited to less than 0.5ms for each draw.&lt;br /&gt;
&lt;br /&gt;
The same Object3D may also be drawn multiple times at different locations by calling &amp;lt;code&amp;gt;draw()&amp;lt;/code&amp;gt; multiple times, and calling &amp;lt;code&amp;gt;translatef()&amp;lt;/code&amp;gt; in-between to change the position each time.  Multiple&lt;br /&gt;
Object3Ds may be loaded as well, and drawn as needed.  This way each object can be kept in VRAM, and quickly drawn each time it needs to be.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= History =&lt;br /&gt;
&lt;br /&gt;
The below text is based on / copied from an old store listing, written years ago, which details the evolution of this line of graphics devices.&lt;br /&gt;
&lt;br /&gt;
###TVout&lt;br /&gt;
Good ole TVout.  Get two resistors, a breadboard, and an NTSC output port and you had monochrome video.  Looking back, this sucked.  I mean, TVout is great, but for me the circuits were unreliable, hard to transport, and delicate.  If I knew about the propeller chip at that point at least I would have had colors.  I made some games but it wasn’t what I really wanted in the end.&lt;br /&gt;
&lt;br /&gt;
###Keyboard cable stripping&lt;br /&gt;
I didn’t have PS2 ports to plug keyboards into, so I cut the end off the cable and connected the wires to the arduino.  That was a crude design.  Worked, but also ruined the keyboard.&lt;br /&gt;
&lt;br /&gt;
###First propeller days&lt;br /&gt;
The propeller was nice because it gave me color NTSC (and later VGA).  I also built the propeller into permanent circuits capable of NTSC output, which evolved into a standard for my graphics technology.  This even powered graphics for my early FPGA designs.&lt;br /&gt;
&lt;br /&gt;
###NGT20&lt;br /&gt;
Finally: a single, solid, reliable device for video output.  Bulky, but had almost all the bells and whistles.  No SMD yet, no keyboard yet, but it was the first clean design.  It was also the first design with VGA output.  I still used the keyboards with stripped cables at this point.&lt;br /&gt;
&lt;br /&gt;
###NGT30&lt;br /&gt;
All the bells and whistles.  Like the NGT20, it is solid and reliable, but it isn’t bulky and also features 3D graphics and a keyboard port. One shield, one keyboard, one arduino, and one monitor.  Nothing else is needed.  No hanging resistors.  No loose wires.  No more stripped cables.  No more crude designs.&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=NGT31&amp;diff=56</id>
		<title>NGT31</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=NGT31&amp;diff=56"/>
		<updated>2024-06-21T09:13:28Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Fix formatting&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The NGT31 is a Parallax Propeller based VGA driver and graphics device, provided as an Arduino shield.  It uses a serial connection to the host device for communication, up to 250,000 baud with current firmware.&lt;br /&gt;
&lt;br /&gt;
This device provides many useful graphics functions, including accelerated sprite drawing, text, and even drawing lines in 3D.&lt;br /&gt;
&lt;br /&gt;
Firmware: https://git.nuclaer-servers.com/Nuclaer/ngt_firmware/-/tree/master&lt;br /&gt;
&lt;br /&gt;
Arduino Library: https://git.nuclaer-servers.com/Nuclaer/nmt_gfx/-/tree/master&lt;br /&gt;
&lt;br /&gt;
== Example Code ==&lt;br /&gt;
&lt;br /&gt;
Here is the demo included in the NMT_GFX library (https://git.nuclaer-servers.com/Nuclaer/nmt_gfx/-/blob/master/Examples/NGT31/NGT31_demo/NGT31_demo.ino):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;NMT_GFX.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
byte tree[] = { 1,8,0,0,&lt;br /&gt;
  0b01010100,0b00010101,&lt;br /&gt;
  0b01010101,0b01010101,&lt;br /&gt;
  0b01010101,0b01010101,&lt;br /&gt;
  0b01010101,0b01010101,&lt;br /&gt;
  0b10100011,0b00001010,&lt;br /&gt;
  0b10100000,0b00001010,&lt;br /&gt;
  0b10100000,0b00001010,&lt;br /&gt;
  0b10101000,0b00101010&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
unsigned short cat[] = { 0, 0,&lt;br /&gt;
  0xaaa,0x0000, 0xffff, 0xffff, 0xc000,&lt;br /&gt;
  0xaaa,0xaaa3, 0xf555, 0x5557, 0xf000,&lt;br /&gt;
  0xaaa,0xaaa3, 0xd555, 0x5555, 0xf000,&lt;br /&gt;
  0xaaa,0xaaa3, 0x5555, 0x5555, 0x7000,&lt;br /&gt;
  0xaaa,0xaaa3, 0x5555, 0x4155, 0x7000,&lt;br /&gt;
  0xaaa,0xaaa3, 0x5555, 0x3c55, 0x70f0,&lt;br /&gt;
  0xaaa,0x00a3, 0x5555, 0x3f15, 0x73f0,&lt;br /&gt;
  0xaaa,0x3c23, 0x5555, 0x3fc0, 0x0ff0,&lt;br /&gt;
  0xaaa,0x0f03, 0x5555, 0x3fff, 0xfff0,&lt;br /&gt;
  0xaaa,0x83c3, 0x5554, 0xffff, 0xfffc,&lt;br /&gt;
  0xaaa,0xa0f3, 0x5554, 0xfe3f, 0xfe3c,&lt;br /&gt;
  0xaaa,0xa803, 0x5554, 0xfc3f, 0xcc3c,&lt;br /&gt;
  0xaaa,0xaa83, 0x5554, 0xd7ff, 0xffd4,&lt;br /&gt;
  0xaaa,0xaaa3, 0xd554, 0xd73f, 0x3cd4,&lt;br /&gt;
  0xaaa,0xaaa3, 0xf555, 0x3f00, 0x00f0,&lt;br /&gt;
  0xaaa,0x0000, 0xffff, 0xcfff, 0xffc0,&lt;br /&gt;
  0xaaa,0x003f, 0x0000, 0x0000, 0x0000,&lt;br /&gt;
  0xaaa,0x003c, 0x03c0, 0x00f0, 0x3c00&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
NMT_GFX ngt;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  ngt.begin(11,10);&lt;br /&gt;
  ngt.print(&amp;quot;Hello!\nThis is an arduino writing\non screen with a &amp;quot;);&lt;br /&gt;
  ngt.println(ngt.get_card_ver());&lt;br /&gt;
&lt;br /&gt;
  delay(1200);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;Look, I can even do colors!&amp;quot;);&lt;br /&gt;
  ngt.set_color(3);&lt;br /&gt;
  ngt.print(&amp;quot;green &amp;quot;);&lt;br /&gt;
  ngt.set_color(2);&lt;br /&gt;
  ngt.print(&amp;quot;red &amp;quot;);&lt;br /&gt;
  ngt.set_color(1);&lt;br /&gt;
  ngt.println(&amp;quot;and white&amp;quot;);&lt;br /&gt;
  ngt.println(&amp;quot;(plus much more)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  delay(1400);&lt;br /&gt;
&lt;br /&gt;
  ngt.fill(2);&lt;br /&gt;
  ngt.println(&amp;quot;Screen fills can be done very fast.&amp;quot;);&lt;br /&gt;
  delay(1700);&lt;br /&gt;
&lt;br /&gt;
  ngt.clear();&lt;br /&gt;
  ngt.println(&amp;quot;I dont only do text though, so&amp;quot;);&lt;br /&gt;
  ngt.println(&amp;quot;let me load my graphics&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  // This line simply sets the size and center of the sprite.&lt;br /&gt;
  // It modifies the 'cat' byte data, so that when it is&lt;br /&gt;
  // uploaded, it will be the correct size, and centered.&lt;br /&gt;
  // We don't do this for the tree, as this data is specified directly&lt;br /&gt;
  // in the initialization of the 'tree' array.  Either way&lt;br /&gt;
  // of initializing it works perfectly fine.&lt;br /&gt;
  Sprite catSprite((byte*)cat, 33, 17, 16, 16);&lt;br /&gt;
&lt;br /&gt;
  int treeHandle = ngt.uploadSprite(tree);&lt;br /&gt;
  int catHandle = ngt.uploadSprite((byte*)cat);&lt;br /&gt;
&lt;br /&gt;
  delay(750);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;I can do lines and sprites efficiently too.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  byte x_tiles=ngt.x_tiles();              // Get tiles for x and y&lt;br /&gt;
  byte y_tiles=ngt.y_tiles();&lt;br /&gt;
  ngt.block_color(64+63,0b001100);&lt;br /&gt;
  ngt.block_color(128+63,0b011000);        // define tree colors&lt;br /&gt;
  ngt.block_color(192+63,0b010001);&lt;br /&gt;
  for(byte i=0;i&amp;lt;50;i++){    // make all tiles use text coloring&lt;br /&gt;
    ngt.tile_color(i,0);&lt;br /&gt;
  }&lt;br /&gt;
  for(byte i=x_tiles-1;i&amp;lt;50;i+=x_tiles){   // Make bottom tiles use tree coloring&lt;br /&gt;
    ngt.tile_color(i,63);&lt;br /&gt;
  }&lt;br /&gt;
  ngt.sprite(4, 180, 0, treeHandle);&lt;br /&gt;
  ngt.sprite(13, 180, 0, treeHandle);&lt;br /&gt;
  ngt.sprite(48, 180, 2, treeHandle);&lt;br /&gt;
  ngt.sprite(68, 180, 0, catHandle);&lt;br /&gt;
  ngt.sprite(28, 180, 1, treeHandle);&lt;br /&gt;
  ngt.line(0, 150, 30, 130);&lt;br /&gt;
  ngt.fast(60, 150);&lt;br /&gt;
  ngt.fast(90, 130);&lt;br /&gt;
  ngt.fast(120, 150);&lt;br /&gt;
  ngt.fast(150, 130);&lt;br /&gt;
&lt;br /&gt;
  delay(1000);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(' ');&lt;br /&gt;
  ngt.set_color(1);&lt;br /&gt;
  ngt.println(&amp;quot;The trees are drawn as sprites: an image of one is stored and copied to the screen using only one command, instead of resending the entire tree over serial.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  delay(6000);&lt;br /&gt;
&lt;br /&gt;
  ngt.set_cursor_pos(10, 190);&lt;br /&gt;
  ngt.println(&amp;quot;Now I'll show you a moving rainbow sprite.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  delay(1750);&lt;br /&gt;
&lt;br /&gt;
  ngt.clear();&lt;br /&gt;
&lt;br /&gt;
  // This sets up the colors for each tile&lt;br /&gt;
  for(byte i=0;i&amp;lt;64;i++){&lt;br /&gt;
    ngt.block_color(128+i,i); // RAINBOW&lt;br /&gt;
    ngt.block_color(64+i,25); // PINK for Nyan Cat&lt;br /&gt;
    ngt.block_color(192+i,0b101010); // GREY for Nyan Cat&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // We only need to call these once.  Instead of clearing the screen each frame,&lt;br /&gt;
  // we will erase the old sprite right before drawing a new one.  This allows faster&lt;br /&gt;
  // rendering, and frees us from the need to write a message over and over again.&lt;br /&gt;
  ngt.clear();&lt;br /&gt;
  ngt.set_color(3);&lt;br /&gt;
  ngt.println(&amp;quot;Moving rainbow sprite!&amp;quot;);&lt;br /&gt;
  ngt.set_color(0);&lt;br /&gt;
&lt;br /&gt;
  // This tells the NGT31 to draw only when nothing is being displayed.&lt;br /&gt;
  // Doing this reduces visual artifacts and makes the video data look cleaner.&lt;br /&gt;
  // This only really affects moving objects and changing images: static&lt;br /&gt;
  // text is not affected.&lt;br /&gt;
  // 'B' stands for 'Blanking' - it only draws when no video data is being sent to the monitor.&lt;br /&gt;
  ngt.set_draw_mode('B');&lt;br /&gt;
&lt;br /&gt;
  int lastX = 0;&lt;br /&gt;
  int lastY = 40;&lt;br /&gt;
  int vx = 1, vy = 1;&lt;br /&gt;
&lt;br /&gt;
  while(true) {&lt;br /&gt;
    long ls = millis();&lt;br /&gt;
&lt;br /&gt;
    int x = lastX + vx;&lt;br /&gt;
    int y = lastY + vy;&lt;br /&gt;
    ngt.moveSprite(lastX, lastY, x, y, 0, catHandle);&lt;br /&gt;
&lt;br /&gt;
    lastX = x;&lt;br /&gt;
    lastY = y;&lt;br /&gt;
    if (x &amp;gt;= 300)&lt;br /&gt;
      vx = -1;&lt;br /&gt;
    else if (x &amp;lt;= 0)&lt;br /&gt;
      vx = 1;&lt;br /&gt;
    if (y &amp;gt;= 240)&lt;br /&gt;
      vy = -1;&lt;br /&gt;
    else if (y &amp;lt;= 30)&lt;br /&gt;
      vy = 1;&lt;br /&gt;
&lt;br /&gt;
    while(ls + 16 &amp;gt; millis());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 3D Example and Explanation ===&lt;br /&gt;
&lt;br /&gt;
Drawing 3D lines, as part of a very simple 3D videogame demonstration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;NMT_GFX.h&amp;gt;&lt;br /&gt;
#include &amp;lt;PS2Keyboard.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
NMT_GFX ngt;&lt;br /&gt;
&lt;br /&gt;
PS2Keyboard keyboard;&lt;br /&gt;
&lt;br /&gt;
uint16_t buffer[256];&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  keyboard.begin(4, 2);&lt;br /&gt;
  ngt.begin(11,10);&lt;br /&gt;
  ngt.println(&amp;quot;Loading world...&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  Object3D world(ngt, buffer);&lt;br /&gt;
  world.addLine(-1000,-100,-1000,1000,-100,-1000,2);        &lt;br /&gt;
  world.addLine(1000,-100,-1000,1000,100,-1000,2);               &lt;br /&gt;
  world.addLine(1000,100,-1000,1000,100,1000,2);                 &lt;br /&gt;
  world.addLine(1000,100,1000,-1000,100,1000,2);              &lt;br /&gt;
  world.addLine(-1000,100,1000,-1000,-100,1000,2);              &lt;br /&gt;
  world.addLine(-1000,-100,1000,-1000,-100,-1000,2);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(-1000,-100,1000,1000,-100,1000,2);            &lt;br /&gt;
  world.addLine(1000,-100,-1000,1000,-100,1000,2);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(-1000,-100,-1000,-1000,100,-1000,2);&lt;br /&gt;
  world.addLine(1000,-100,1000,1000,100,1000,2);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(-1000,100,-1000,1000,100,-1000,2);&lt;br /&gt;
  world.addLine(-1000,100,-1000,-1000,100,1000,2);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(10,10,10,30,10,10,3);        &lt;br /&gt;
  world.addLine(30,10,10,30,30,10,3);               &lt;br /&gt;
  world.addLine(30,30,10,30,30,30,3);                 &lt;br /&gt;
  world.addLine(30,30,30,10,30,30,3);              &lt;br /&gt;
  world.addLine(10,30,30,10,10,30,3);              &lt;br /&gt;
  world.addLine(10,10,30,10,10,10,3);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(10,10,30,30,10,30,3);            &lt;br /&gt;
  world.addLine(30,10,10,30,10,30,3);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(10,10,10,10,30,10,3);&lt;br /&gt;
  world.addLine(30,10,30,30,30,30,3);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(10,30,10,30,30,10,3);&lt;br /&gt;
  world.addLine(10,30,10,10,30,30,3);&lt;br /&gt;
&lt;br /&gt;
  world.upload();&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;Setting up colors...&amp;quot;);&lt;br /&gt;
  for(byte i=0;i&amp;lt;64;i++) {&lt;br /&gt;
    ngt.block_color(128 + i, 0b001011);&lt;br /&gt;
    ngt.block_color(192 + i, 0b111111);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Supported on newer firmware - reduces flicker&lt;br /&gt;
  ngt.set_draw_mode('B');&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;Ready to play...&amp;quot;);&lt;br /&gt;
  delay(500);&lt;br /&gt;
  double x=0,z=0;&lt;br /&gt;
  int rot=0;&lt;br /&gt;
  while(true) {&lt;br /&gt;
    int32_t time=millis();&lt;br /&gt;
    if (keyboard.available()) {&lt;br /&gt;
&lt;br /&gt;
      // read the next key&lt;br /&gt;
      char c = keyboard.read();&lt;br /&gt;
      if (c == PS2_LEFTARROW) {&lt;br /&gt;
        rot+=3;&lt;br /&gt;
      } else if (c == PS2_RIGHTARROW) {&lt;br /&gt;
        rot-=3;&lt;br /&gt;
      } else if (c == PS2_UPARROW) {&lt;br /&gt;
        x-=cos(rot*PI/180);&lt;br /&gt;
        z+=sin(rot*PI/180);&lt;br /&gt;
      } else if (c == PS2_DOWNARROW) {&lt;br /&gt;
        x+=cos(rot*PI/180);&lt;br /&gt;
        z-=sin(rot*PI/180);&lt;br /&gt;
      }&lt;br /&gt;
      ngt.rotatef(-rot,0);&lt;br /&gt;
      ngt.translatef((int32_t)x,0,(int32_t)z);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //ngt.clear();&lt;br /&gt;
    long timer = millis();&lt;br /&gt;
    world.draw();&lt;br /&gt;
    //Serial.println(millis() - timer);&lt;br /&gt;
&lt;br /&gt;
    delay(max(1,(16+time)-(int32_t)millis()));&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here, an &amp;lt;code&amp;gt;Object3D&amp;lt;/code&amp;gt; is used to construct a bytecode of the 3D world.  This bytecode can be rendered significantly faster than if individual commands were issued for each line or shape in the world.&lt;br /&gt;
Due to the speed limitations of the serial port, issuing this many commands per frame would cut the FPS to less than 25 - and that does not include draw time.  By using &amp;lt;code&amp;gt;Object3D::draw()&amp;lt;/code&amp;gt;, the&lt;br /&gt;
serial communication overhead is limited to less than 0.5ms for each draw.&lt;br /&gt;
&lt;br /&gt;
The same Object3D may also be drawn multiple times at different locations by calling &amp;lt;code&amp;gt;draw()&amp;lt;/code&amp;gt; multiple times, and calling &amp;lt;code&amp;gt;translatef()&amp;lt;/code&amp;gt; in-between to change the position each time.  Multiple&lt;br /&gt;
Object3Ds may be loaded as well, and drawn as needed.  This way each object can be kept in VRAM, and quickly drawn each time it needs to be.&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=NGT31&amp;diff=55</id>
		<title>NGT31</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=NGT31&amp;diff=55"/>
		<updated>2024-06-21T09:12:50Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Update 3D example and add more info&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The NGT31 is a Parallax Propeller based VGA driver and graphics device, provided as an Arduino shield.  It uses a serial connection to the host device for communication, up to 250,000 baud with current firmware.&lt;br /&gt;
&lt;br /&gt;
This device provides many useful graphics functions, including accelerated sprite drawing, text, and even drawing lines in 3D.&lt;br /&gt;
&lt;br /&gt;
Firmware: https://git.nuclaer-servers.com/Nuclaer/ngt_firmware/-/tree/master&lt;br /&gt;
&lt;br /&gt;
Arduino Library: https://git.nuclaer-servers.com/Nuclaer/nmt_gfx/-/tree/master&lt;br /&gt;
&lt;br /&gt;
== Example Code ==&lt;br /&gt;
&lt;br /&gt;
Here is the demo included in the NMT_GFX library (https://git.nuclaer-servers.com/Nuclaer/nmt_gfx/-/blob/master/Examples/NGT31/NGT31_demo/NGT31_demo.ino):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;NMT_GFX.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
byte tree[] = { 1,8,0,0,&lt;br /&gt;
  0b01010100,0b00010101,&lt;br /&gt;
  0b01010101,0b01010101,&lt;br /&gt;
  0b01010101,0b01010101,&lt;br /&gt;
  0b01010101,0b01010101,&lt;br /&gt;
  0b10100011,0b00001010,&lt;br /&gt;
  0b10100000,0b00001010,&lt;br /&gt;
  0b10100000,0b00001010,&lt;br /&gt;
  0b10101000,0b00101010&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
unsigned short cat[] = { 0, 0,&lt;br /&gt;
  0xaaa,0x0000, 0xffff, 0xffff, 0xc000,&lt;br /&gt;
  0xaaa,0xaaa3, 0xf555, 0x5557, 0xf000,&lt;br /&gt;
  0xaaa,0xaaa3, 0xd555, 0x5555, 0xf000,&lt;br /&gt;
  0xaaa,0xaaa3, 0x5555, 0x5555, 0x7000,&lt;br /&gt;
  0xaaa,0xaaa3, 0x5555, 0x4155, 0x7000,&lt;br /&gt;
  0xaaa,0xaaa3, 0x5555, 0x3c55, 0x70f0,&lt;br /&gt;
  0xaaa,0x00a3, 0x5555, 0x3f15, 0x73f0,&lt;br /&gt;
  0xaaa,0x3c23, 0x5555, 0x3fc0, 0x0ff0,&lt;br /&gt;
  0xaaa,0x0f03, 0x5555, 0x3fff, 0xfff0,&lt;br /&gt;
  0xaaa,0x83c3, 0x5554, 0xffff, 0xfffc,&lt;br /&gt;
  0xaaa,0xa0f3, 0x5554, 0xfe3f, 0xfe3c,&lt;br /&gt;
  0xaaa,0xa803, 0x5554, 0xfc3f, 0xcc3c,&lt;br /&gt;
  0xaaa,0xaa83, 0x5554, 0xd7ff, 0xffd4,&lt;br /&gt;
  0xaaa,0xaaa3, 0xd554, 0xd73f, 0x3cd4,&lt;br /&gt;
  0xaaa,0xaaa3, 0xf555, 0x3f00, 0x00f0,&lt;br /&gt;
  0xaaa,0x0000, 0xffff, 0xcfff, 0xffc0,&lt;br /&gt;
  0xaaa,0x003f, 0x0000, 0x0000, 0x0000,&lt;br /&gt;
  0xaaa,0x003c, 0x03c0, 0x00f0, 0x3c00&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
NMT_GFX ngt;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  ngt.begin(11,10);&lt;br /&gt;
  ngt.print(&amp;quot;Hello!\nThis is an arduino writing\non screen with a &amp;quot;);&lt;br /&gt;
  ngt.println(ngt.get_card_ver());&lt;br /&gt;
&lt;br /&gt;
  delay(1200);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;Look, I can even do colors!&amp;quot;);&lt;br /&gt;
  ngt.set_color(3);&lt;br /&gt;
  ngt.print(&amp;quot;green &amp;quot;);&lt;br /&gt;
  ngt.set_color(2);&lt;br /&gt;
  ngt.print(&amp;quot;red &amp;quot;);&lt;br /&gt;
  ngt.set_color(1);&lt;br /&gt;
  ngt.println(&amp;quot;and white&amp;quot;);&lt;br /&gt;
  ngt.println(&amp;quot;(plus much more)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  delay(1400);&lt;br /&gt;
&lt;br /&gt;
  ngt.fill(2);&lt;br /&gt;
  ngt.println(&amp;quot;Screen fills can be done very fast.&amp;quot;);&lt;br /&gt;
  delay(1700);&lt;br /&gt;
&lt;br /&gt;
  ngt.clear();&lt;br /&gt;
  ngt.println(&amp;quot;I dont only do text though, so&amp;quot;);&lt;br /&gt;
  ngt.println(&amp;quot;let me load my graphics&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  // This line simply sets the size and center of the sprite.&lt;br /&gt;
  // It modifies the 'cat' byte data, so that when it is&lt;br /&gt;
  // uploaded, it will be the correct size, and centered.&lt;br /&gt;
  // We don't do this for the tree, as this data is specified directly&lt;br /&gt;
  // in the initialization of the 'tree' array.  Either way&lt;br /&gt;
  // of initializing it works perfectly fine.&lt;br /&gt;
  Sprite catSprite((byte*)cat, 33, 17, 16, 16);&lt;br /&gt;
&lt;br /&gt;
  int treeHandle = ngt.uploadSprite(tree);&lt;br /&gt;
  int catHandle = ngt.uploadSprite((byte*)cat);&lt;br /&gt;
&lt;br /&gt;
  delay(750);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;I can do lines and sprites efficiently too.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  byte x_tiles=ngt.x_tiles();              // Get tiles for x and y&lt;br /&gt;
  byte y_tiles=ngt.y_tiles();&lt;br /&gt;
  ngt.block_color(64+63,0b001100);&lt;br /&gt;
  ngt.block_color(128+63,0b011000);        // define tree colors&lt;br /&gt;
  ngt.block_color(192+63,0b010001);&lt;br /&gt;
  for(byte i=0;i&amp;lt;50;i++){    // make all tiles use text coloring&lt;br /&gt;
    ngt.tile_color(i,0);&lt;br /&gt;
  }&lt;br /&gt;
  for(byte i=x_tiles-1;i&amp;lt;50;i+=x_tiles){   // Make bottom tiles use tree coloring&lt;br /&gt;
    ngt.tile_color(i,63);&lt;br /&gt;
  }&lt;br /&gt;
  ngt.sprite(4, 180, 0, treeHandle);&lt;br /&gt;
  ngt.sprite(13, 180, 0, treeHandle);&lt;br /&gt;
  ngt.sprite(48, 180, 2, treeHandle);&lt;br /&gt;
  ngt.sprite(68, 180, 0, catHandle);&lt;br /&gt;
  ngt.sprite(28, 180, 1, treeHandle);&lt;br /&gt;
  ngt.line(0, 150, 30, 130);&lt;br /&gt;
  ngt.fast(60, 150);&lt;br /&gt;
  ngt.fast(90, 130);&lt;br /&gt;
  ngt.fast(120, 150);&lt;br /&gt;
  ngt.fast(150, 130);&lt;br /&gt;
&lt;br /&gt;
  delay(1000);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(' ');&lt;br /&gt;
  ngt.set_color(1);&lt;br /&gt;
  ngt.println(&amp;quot;The trees are drawn as sprites: an image of one is stored and copied to the screen using only one command, instead of resending the entire tree over serial.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  delay(6000);&lt;br /&gt;
&lt;br /&gt;
  ngt.set_cursor_pos(10, 190);&lt;br /&gt;
  ngt.println(&amp;quot;Now I'll show you a moving rainbow sprite.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  delay(1750);&lt;br /&gt;
&lt;br /&gt;
  ngt.clear();&lt;br /&gt;
&lt;br /&gt;
  // This sets up the colors for each tile&lt;br /&gt;
  for(byte i=0;i&amp;lt;64;i++){&lt;br /&gt;
    ngt.block_color(128+i,i); // RAINBOW&lt;br /&gt;
    ngt.block_color(64+i,25); // PINK for Nyan Cat&lt;br /&gt;
    ngt.block_color(192+i,0b101010); // GREY for Nyan Cat&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // We only need to call these once.  Instead of clearing the screen each frame,&lt;br /&gt;
  // we will erase the old sprite right before drawing a new one.  This allows faster&lt;br /&gt;
  // rendering, and frees us from the need to write a message over and over again.&lt;br /&gt;
  ngt.clear();&lt;br /&gt;
  ngt.set_color(3);&lt;br /&gt;
  ngt.println(&amp;quot;Moving rainbow sprite!&amp;quot;);&lt;br /&gt;
  ngt.set_color(0);&lt;br /&gt;
&lt;br /&gt;
  // This tells the NGT31 to draw only when nothing is being displayed.&lt;br /&gt;
  // Doing this reduces visual artifacts and makes the video data look cleaner.&lt;br /&gt;
  // This only really affects moving objects and changing images: static&lt;br /&gt;
  // text is not affected.&lt;br /&gt;
  // 'B' stands for 'Blanking' - it only draws when no video data is being sent to the monitor.&lt;br /&gt;
  ngt.set_draw_mode('B');&lt;br /&gt;
&lt;br /&gt;
  int lastX = 0;&lt;br /&gt;
  int lastY = 40;&lt;br /&gt;
  int vx = 1, vy = 1;&lt;br /&gt;
&lt;br /&gt;
  while(true) {&lt;br /&gt;
    long ls = millis();&lt;br /&gt;
&lt;br /&gt;
    int x = lastX + vx;&lt;br /&gt;
    int y = lastY + vy;&lt;br /&gt;
    ngt.moveSprite(lastX, lastY, x, y, 0, catHandle);&lt;br /&gt;
&lt;br /&gt;
    lastX = x;&lt;br /&gt;
    lastY = y;&lt;br /&gt;
    if (x &amp;gt;= 300)&lt;br /&gt;
      vx = -1;&lt;br /&gt;
    else if (x &amp;lt;= 0)&lt;br /&gt;
      vx = 1;&lt;br /&gt;
    if (y &amp;gt;= 240)&lt;br /&gt;
      vy = -1;&lt;br /&gt;
    else if (y &amp;lt;= 30)&lt;br /&gt;
      vy = 1;&lt;br /&gt;
&lt;br /&gt;
    while(ls + 16 &amp;gt; millis());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 3D Example and Explanation ===&lt;br /&gt;
&lt;br /&gt;
Drawing 3D lines, as part of a very simple 3D videogame demonstration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;NMT_GFX.h&amp;gt;&lt;br /&gt;
#include &amp;lt;PS2Keyboard.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
NMT_GFX ngt;&lt;br /&gt;
&lt;br /&gt;
PS2Keyboard keyboard;&lt;br /&gt;
&lt;br /&gt;
uint16_t buffer[256];&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  keyboard.begin(4, 2);&lt;br /&gt;
  ngt.begin(11,10);&lt;br /&gt;
  ngt.println(&amp;quot;Loading world...&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  Object3D world(ngt, buffer);&lt;br /&gt;
  world.addLine(-1000,-100,-1000,1000,-100,-1000,2);        &lt;br /&gt;
  world.addLine(1000,-100,-1000,1000,100,-1000,2);               &lt;br /&gt;
  world.addLine(1000,100,-1000,1000,100,1000,2);                 &lt;br /&gt;
  world.addLine(1000,100,1000,-1000,100,1000,2);              &lt;br /&gt;
  world.addLine(-1000,100,1000,-1000,-100,1000,2);              &lt;br /&gt;
  world.addLine(-1000,-100,1000,-1000,-100,-1000,2);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(-1000,-100,1000,1000,-100,1000,2);            &lt;br /&gt;
  world.addLine(1000,-100,-1000,1000,-100,1000,2);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(-1000,-100,-1000,-1000,100,-1000,2);&lt;br /&gt;
  world.addLine(1000,-100,1000,1000,100,1000,2);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(-1000,100,-1000,1000,100,-1000,2);&lt;br /&gt;
  world.addLine(-1000,100,-1000,-1000,100,1000,2);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(10,10,10,30,10,10,3);        &lt;br /&gt;
  world.addLine(30,10,10,30,30,10,3);               &lt;br /&gt;
  world.addLine(30,30,10,30,30,30,3);                 &lt;br /&gt;
  world.addLine(30,30,30,10,30,30,3);              &lt;br /&gt;
  world.addLine(10,30,30,10,10,30,3);              &lt;br /&gt;
  world.addLine(10,10,30,10,10,10,3);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(10,10,30,30,10,30,3);            &lt;br /&gt;
  world.addLine(30,10,10,30,10,30,3);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(10,10,10,10,30,10,3);&lt;br /&gt;
  world.addLine(30,10,30,30,30,30,3);&lt;br /&gt;
&lt;br /&gt;
  world.addLine(10,30,10,30,30,10,3);&lt;br /&gt;
  world.addLine(10,30,10,10,30,30,3);&lt;br /&gt;
&lt;br /&gt;
  world.upload();&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;Setting up colors...&amp;quot;);&lt;br /&gt;
  for(byte i=0;i&amp;lt;64;i++) {&lt;br /&gt;
    ngt.block_color(128 + i, 0b001011);&lt;br /&gt;
    ngt.block_color(192 + i, 0b111111);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Supported on newer firmware - reduces flicker&lt;br /&gt;
  ngt.set_draw_mode('B');&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;Ready to play...&amp;quot;);&lt;br /&gt;
  delay(500);&lt;br /&gt;
  double x=0,z=0;&lt;br /&gt;
  int rot=0;&lt;br /&gt;
  while(true) {&lt;br /&gt;
    int32_t time=millis();&lt;br /&gt;
    if (keyboard.available()) {&lt;br /&gt;
&lt;br /&gt;
      // read the next key&lt;br /&gt;
      char c = keyboard.read();&lt;br /&gt;
      if (c == PS2_LEFTARROW) {&lt;br /&gt;
        rot+=3;&lt;br /&gt;
      } else if (c == PS2_RIGHTARROW) {&lt;br /&gt;
        rot-=3;&lt;br /&gt;
      } else if (c == PS2_UPARROW) {&lt;br /&gt;
        x-=cos(rot*PI/180);&lt;br /&gt;
        z+=sin(rot*PI/180);&lt;br /&gt;
      } else if (c == PS2_DOWNARROW) {&lt;br /&gt;
        x+=cos(rot*PI/180);&lt;br /&gt;
        z-=sin(rot*PI/180);&lt;br /&gt;
      }&lt;br /&gt;
      ngt.rotatef(-rot,0);&lt;br /&gt;
      ngt.translatef((int32_t)x,0,(int32_t)z);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //ngt.clear();&lt;br /&gt;
    long timer = millis();&lt;br /&gt;
    world.draw();&lt;br /&gt;
    //Serial.println(millis() - timer);&lt;br /&gt;
&lt;br /&gt;
    delay(max(1,(16+time)-(int32_t)millis()));&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here, an &amp;lt;pre&amp;gt;Object3D&amp;lt;/pre&amp;gt; is used to construct a bytecode of the 3D world.  This bytecode can be rendered significantly faster than if individual commands were issued for each line or shape in the world.&lt;br /&gt;
Due to the speed limitations of the serial port, issuing this many commands per frame would cut the FPS to less than 25 - and that does not include draw time.  By using &amp;lt;pre&amp;gt;Object3D::draw()&amp;lt;/pre&amp;gt;, the&lt;br /&gt;
serial communication overhead is limited to less than 0.5ms for each draw.&lt;br /&gt;
&lt;br /&gt;
The same Object3D may also be drawn multiple times at different locations by calling &amp;lt;pre&amp;gt;draw()&amp;lt;/pre&amp;gt; multiple times, and calling &amp;lt;pre&amp;gt;translatef()&amp;lt;/pre&amp;gt; in-between to change the position each time.  Multiple&lt;br /&gt;
Object3Ds may be loaded as well, and drawn as needed.  This way each object can be kept in VRAM, and quickly drawn each time it needs to be.&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=NGT31&amp;diff=54</id>
		<title>NGT31</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=NGT31&amp;diff=54"/>
		<updated>2024-06-20T05:59:30Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Add links for firmware and libraries&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The NGT31 is a Parallax Propeller based VGA driver and graphics device, provided as an Arduino shield.  It uses a serial connection to the host device for communication, up to 250,000 baud with current firmware.&lt;br /&gt;
&lt;br /&gt;
This device provides many useful graphics functions, including accelerated sprite drawing, text, and even drawing lines in 3D.&lt;br /&gt;
&lt;br /&gt;
Firmware: https://git.nuclaer-servers.com/Nuclaer/ngt_firmware/-/tree/master&lt;br /&gt;
&lt;br /&gt;
Arduino Library: https://git.nuclaer-servers.com/Nuclaer/nmt_gfx/-/tree/master&lt;br /&gt;
&lt;br /&gt;
== Example Code ==&lt;br /&gt;
&lt;br /&gt;
Here is the demo included in the NMT_GFX library (https://git.nuclaer-servers.com/Nuclaer/nmt_gfx/-/blob/master/Examples/NGT31/NGT31_demo/NGT31_demo.ino):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;NMT_GFX.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
byte tree[] = { 1,8,0,0,&lt;br /&gt;
  0b01010100,0b00010101,&lt;br /&gt;
  0b01010101,0b01010101,&lt;br /&gt;
  0b01010101,0b01010101,&lt;br /&gt;
  0b01010101,0b01010101,&lt;br /&gt;
  0b10100011,0b00001010,&lt;br /&gt;
  0b10100000,0b00001010,&lt;br /&gt;
  0b10100000,0b00001010,&lt;br /&gt;
  0b10101000,0b00101010&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
unsigned short cat[] = { 0, 0,&lt;br /&gt;
  0xaaa,0x0000, 0xffff, 0xffff, 0xc000,&lt;br /&gt;
  0xaaa,0xaaa3, 0xf555, 0x5557, 0xf000,&lt;br /&gt;
  0xaaa,0xaaa3, 0xd555, 0x5555, 0xf000,&lt;br /&gt;
  0xaaa,0xaaa3, 0x5555, 0x5555, 0x7000,&lt;br /&gt;
  0xaaa,0xaaa3, 0x5555, 0x4155, 0x7000,&lt;br /&gt;
  0xaaa,0xaaa3, 0x5555, 0x3c55, 0x70f0,&lt;br /&gt;
  0xaaa,0x00a3, 0x5555, 0x3f15, 0x73f0,&lt;br /&gt;
  0xaaa,0x3c23, 0x5555, 0x3fc0, 0x0ff0,&lt;br /&gt;
  0xaaa,0x0f03, 0x5555, 0x3fff, 0xfff0,&lt;br /&gt;
  0xaaa,0x83c3, 0x5554, 0xffff, 0xfffc,&lt;br /&gt;
  0xaaa,0xa0f3, 0x5554, 0xfe3f, 0xfe3c,&lt;br /&gt;
  0xaaa,0xa803, 0x5554, 0xfc3f, 0xcc3c,&lt;br /&gt;
  0xaaa,0xaa83, 0x5554, 0xd7ff, 0xffd4,&lt;br /&gt;
  0xaaa,0xaaa3, 0xd554, 0xd73f, 0x3cd4,&lt;br /&gt;
  0xaaa,0xaaa3, 0xf555, 0x3f00, 0x00f0,&lt;br /&gt;
  0xaaa,0x0000, 0xffff, 0xcfff, 0xffc0,&lt;br /&gt;
  0xaaa,0x003f, 0x0000, 0x0000, 0x0000,&lt;br /&gt;
  0xaaa,0x003c, 0x03c0, 0x00f0, 0x3c00&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
NMT_GFX ngt;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  ngt.begin(11,10);&lt;br /&gt;
  ngt.print(&amp;quot;Hello!\nThis is an arduino writing\non screen with a &amp;quot;);&lt;br /&gt;
  ngt.println(ngt.get_card_ver());&lt;br /&gt;
&lt;br /&gt;
  delay(1200);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;Look, I can even do colors!&amp;quot;);&lt;br /&gt;
  ngt.set_color(3);&lt;br /&gt;
  ngt.print(&amp;quot;green &amp;quot;);&lt;br /&gt;
  ngt.set_color(2);&lt;br /&gt;
  ngt.print(&amp;quot;red &amp;quot;);&lt;br /&gt;
  ngt.set_color(1);&lt;br /&gt;
  ngt.println(&amp;quot;and white&amp;quot;);&lt;br /&gt;
  ngt.println(&amp;quot;(plus much more)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  delay(1400);&lt;br /&gt;
&lt;br /&gt;
  ngt.fill(2);&lt;br /&gt;
  ngt.println(&amp;quot;Screen fills can be done very fast.&amp;quot;);&lt;br /&gt;
  delay(1700);&lt;br /&gt;
&lt;br /&gt;
  ngt.clear();&lt;br /&gt;
  ngt.println(&amp;quot;I dont only do text though, so&amp;quot;);&lt;br /&gt;
  ngt.println(&amp;quot;let me load my graphics&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  // This line simply sets the size and center of the sprite.&lt;br /&gt;
  // It modifies the 'cat' byte data, so that when it is&lt;br /&gt;
  // uploaded, it will be the correct size, and centered.&lt;br /&gt;
  // We don't do this for the tree, as this data is specified directly&lt;br /&gt;
  // in the initialization of the 'tree' array.  Either way&lt;br /&gt;
  // of initializing it works perfectly fine.&lt;br /&gt;
  Sprite catSprite((byte*)cat, 33, 17, 16, 16);&lt;br /&gt;
&lt;br /&gt;
  int treeHandle = ngt.uploadSprite(tree);&lt;br /&gt;
  int catHandle = ngt.uploadSprite((byte*)cat);&lt;br /&gt;
&lt;br /&gt;
  delay(750);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;I can do lines and sprites efficiently too.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  byte x_tiles=ngt.x_tiles();              // Get tiles for x and y&lt;br /&gt;
  byte y_tiles=ngt.y_tiles();&lt;br /&gt;
  ngt.block_color(64+63,0b001100);&lt;br /&gt;
  ngt.block_color(128+63,0b011000);        // define tree colors&lt;br /&gt;
  ngt.block_color(192+63,0b010001);&lt;br /&gt;
  for(byte i=0;i&amp;lt;50;i++){    // make all tiles use text coloring&lt;br /&gt;
    ngt.tile_color(i,0);&lt;br /&gt;
  }&lt;br /&gt;
  for(byte i=x_tiles-1;i&amp;lt;50;i+=x_tiles){   // Make bottom tiles use tree coloring&lt;br /&gt;
    ngt.tile_color(i,63);&lt;br /&gt;
  }&lt;br /&gt;
  ngt.sprite(4, 180, 0, treeHandle);&lt;br /&gt;
  ngt.sprite(13, 180, 0, treeHandle);&lt;br /&gt;
  ngt.sprite(48, 180, 2, treeHandle);&lt;br /&gt;
  ngt.sprite(68, 180, 0, catHandle);&lt;br /&gt;
  ngt.sprite(28, 180, 1, treeHandle);&lt;br /&gt;
  ngt.line(0, 150, 30, 130);&lt;br /&gt;
  ngt.fast(60, 150);&lt;br /&gt;
  ngt.fast(90, 130);&lt;br /&gt;
  ngt.fast(120, 150);&lt;br /&gt;
  ngt.fast(150, 130);&lt;br /&gt;
&lt;br /&gt;
  delay(1000);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(' ');&lt;br /&gt;
  ngt.set_color(1);&lt;br /&gt;
  ngt.println(&amp;quot;The trees are drawn as sprites: an image of one is stored and copied to the screen using only one command, instead of resending the entire tree over serial.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  delay(6000);&lt;br /&gt;
&lt;br /&gt;
  ngt.set_cursor_pos(10, 190);&lt;br /&gt;
  ngt.println(&amp;quot;Now I'll show you a moving rainbow sprite.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  delay(1750);&lt;br /&gt;
&lt;br /&gt;
  ngt.clear();&lt;br /&gt;
&lt;br /&gt;
  // This sets up the colors for each tile&lt;br /&gt;
  for(byte i=0;i&amp;lt;64;i++){&lt;br /&gt;
    ngt.block_color(128+i,i); // RAINBOW&lt;br /&gt;
    ngt.block_color(64+i,25); // PINK for Nyan Cat&lt;br /&gt;
    ngt.block_color(192+i,0b101010); // GREY for Nyan Cat&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // We only need to call these once.  Instead of clearing the screen each frame,&lt;br /&gt;
  // we will erase the old sprite right before drawing a new one.  This allows faster&lt;br /&gt;
  // rendering, and frees us from the need to write a message over and over again.&lt;br /&gt;
  ngt.clear();&lt;br /&gt;
  ngt.set_color(3);&lt;br /&gt;
  ngt.println(&amp;quot;Moving rainbow sprite!&amp;quot;);&lt;br /&gt;
  ngt.set_color(0);&lt;br /&gt;
&lt;br /&gt;
  // This tells the NGT31 to draw only when nothing is being displayed.&lt;br /&gt;
  // Doing this reduces visual artifacts and makes the video data look cleaner.&lt;br /&gt;
  // This only really affects moving objects and changing images: static&lt;br /&gt;
  // text is not affected.&lt;br /&gt;
  // 'B' stands for 'Blanking' - it only draws when no video data is being sent to the monitor.&lt;br /&gt;
  ngt.set_draw_mode('B');&lt;br /&gt;
&lt;br /&gt;
  int lastX = 0;&lt;br /&gt;
  int lastY = 40;&lt;br /&gt;
  int vx = 1, vy = 1;&lt;br /&gt;
&lt;br /&gt;
  while(true) {&lt;br /&gt;
    long ls = millis();&lt;br /&gt;
&lt;br /&gt;
    int x = lastX + vx;&lt;br /&gt;
    int y = lastY + vy;&lt;br /&gt;
    ngt.moveSprite(lastX, lastY, x, y, 0, catHandle);&lt;br /&gt;
&lt;br /&gt;
    lastX = x;&lt;br /&gt;
    lastY = y;&lt;br /&gt;
    if (x &amp;gt;= 300)&lt;br /&gt;
      vx = -1;&lt;br /&gt;
    else if (x &amp;lt;= 0)&lt;br /&gt;
      vx = 1;&lt;br /&gt;
    if (y &amp;gt;= 240)&lt;br /&gt;
      vy = -1;&lt;br /&gt;
    else if (y &amp;lt;= 30)&lt;br /&gt;
      vy = 1;&lt;br /&gt;
&lt;br /&gt;
    while(ls + 16 &amp;gt; millis());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Drawing 3D lines, as part of a very simple 3D videogame demonstration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;NMT_GFX.h&amp;gt;&lt;br /&gt;
#include &amp;lt;PS2Keyboard.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
NMT_GFX ngt;&lt;br /&gt;
&lt;br /&gt;
PS2Keyboard keyboard;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  keyboard.begin(4, 2);&lt;br /&gt;
  ngt.begin(11,10);&lt;br /&gt;
  ngt.println(&amp;quot;Loading world...&amp;quot;);&lt;br /&gt;
  ngt.add_line(-1000,-100,-1000,1000,-100,-1000,2);        &lt;br /&gt;
  ngt.add_line(1000,-100,-1000,1000,100,-1000,2);               &lt;br /&gt;
  ngt.add_line(1000,100,-1000,1000,100,1000,2);                 &lt;br /&gt;
  ngt.add_line(1000,100,1000,-1000,100,1000,2);              &lt;br /&gt;
  ngt.add_line(-1000,100,1000,-1000,-100,1000,2);              &lt;br /&gt;
  ngt.add_line(-1000,-100,1000,-1000,-100,-1000,2);&lt;br /&gt;
&lt;br /&gt;
  ngt.add_line(-1000,-100,1000,1000,-100,1000,2);            &lt;br /&gt;
  ngt.add_line(1000,-100,-1000,1000,-100,1000,2);&lt;br /&gt;
&lt;br /&gt;
  ngt.add_line(-1000,-100,-1000,-1000,100,-1000,2);&lt;br /&gt;
  ngt.add_line(1000,-100,1000,1000,100,1000,2);&lt;br /&gt;
&lt;br /&gt;
  ngt.add_line(-1000,100,-1000,1000,100,-1000,2);&lt;br /&gt;
  ngt.add_line(-1000,100,-1000,-1000,100,1000,2);&lt;br /&gt;
&lt;br /&gt;
  ngt.add_line(10,10,10,30,10,10,3);        &lt;br /&gt;
  ngt.add_line(30,10,10,30,30,10,3);               &lt;br /&gt;
  ngt.add_line(30,30,10,30,30,30,3);                 &lt;br /&gt;
  ngt.add_line(30,30,30,10,30,30,3);              &lt;br /&gt;
  ngt.add_line(10,30,30,10,10,30,3);              &lt;br /&gt;
  ngt.add_line(10,10,30,10,10,10,3);&lt;br /&gt;
&lt;br /&gt;
  ngt.add_line(10,10,30,30,10,30,3);            &lt;br /&gt;
  ngt.add_line(30,10,10,30,10,30,3);&lt;br /&gt;
&lt;br /&gt;
  ngt.add_line(10,10,10,10,30,10,3);&lt;br /&gt;
  ngt.add_line(30,10,30,30,30,30,3);&lt;br /&gt;
&lt;br /&gt;
  ngt.add_line(10,30,10,30,30,10,3);&lt;br /&gt;
  ngt.add_line(10,30,10,10,30,30,3);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;Setting up colors...&amp;quot;);&lt;br /&gt;
  for(byte i=0;i&amp;lt;64;i++) {&lt;br /&gt;
    ngt.block_color(128 + i, 0b001011);&lt;br /&gt;
    ngt.block_color(192 + i, 0b111111);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Supported on newer firmware - reduces flicker&lt;br /&gt;
  ngt.set_draw_mode('B');&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;Ready to play...&amp;quot;);&lt;br /&gt;
  delay(500);&lt;br /&gt;
  double x=0,z=0;&lt;br /&gt;
  int rot=0;&lt;br /&gt;
  while(true) {&lt;br /&gt;
    int32_t time=millis();&lt;br /&gt;
    if (keyboard.available()) {&lt;br /&gt;
&lt;br /&gt;
      // read the next key&lt;br /&gt;
      char c = keyboard.read();&lt;br /&gt;
      if (c == PS2_LEFTARROW) {&lt;br /&gt;
        rot+=3;&lt;br /&gt;
      } else if (c == PS2_RIGHTARROW) {&lt;br /&gt;
        rot-=3;&lt;br /&gt;
      } else if (c == PS2_UPARROW) {&lt;br /&gt;
        x-=cos(rot*PI/180);&lt;br /&gt;
        z+=sin(rot*PI/180);&lt;br /&gt;
      } else if (c == PS2_DOWNARROW) {&lt;br /&gt;
        x+=cos(rot*PI/180);&lt;br /&gt;
        z-=sin(rot*PI/180);&lt;br /&gt;
      }&lt;br /&gt;
      ngt.rotatef(rot,0);&lt;br /&gt;
      ngt.translatef((int32_t)x,0,(int32_t)z);&lt;br /&gt;
    }&lt;br /&gt;
    ngt.frame_3d();&lt;br /&gt;
    delay(max(1,(16+time)-(int32_t)millis()));&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=NGT31&amp;diff=53</id>
		<title>NGT31</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=NGT31&amp;diff=53"/>
		<updated>2024-06-20T05:47:34Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Add basic NGT31 page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The NGT31 is a Parallax Propeller based VGA driver and graphics device, provided as an Arduino shield.  It uses a serial connection to the host device for communication, up to 250,000 baud with current firmware.&lt;br /&gt;
&lt;br /&gt;
This device provides many useful graphics functions, including accelerated sprite drawing, text, and even drawing lines in 3D.&lt;br /&gt;
&lt;br /&gt;
== Example Code ==&lt;br /&gt;
&lt;br /&gt;
Here is the demo included in the NMT_GFX library (https://git.nuclaer-servers.com/Nuclaer/nmt_gfx/-/blob/master/Examples/NGT31/NGT31_demo/NGT31_demo.ino):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;NMT_GFX.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
byte tree[] = { 1,8,0,0,&lt;br /&gt;
  0b01010100,0b00010101,&lt;br /&gt;
  0b01010101,0b01010101,&lt;br /&gt;
  0b01010101,0b01010101,&lt;br /&gt;
  0b01010101,0b01010101,&lt;br /&gt;
  0b10100011,0b00001010,&lt;br /&gt;
  0b10100000,0b00001010,&lt;br /&gt;
  0b10100000,0b00001010,&lt;br /&gt;
  0b10101000,0b00101010&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
unsigned short cat[] = { 0, 0,&lt;br /&gt;
  0xaaa,0x0000, 0xffff, 0xffff, 0xc000,&lt;br /&gt;
  0xaaa,0xaaa3, 0xf555, 0x5557, 0xf000,&lt;br /&gt;
  0xaaa,0xaaa3, 0xd555, 0x5555, 0xf000,&lt;br /&gt;
  0xaaa,0xaaa3, 0x5555, 0x5555, 0x7000,&lt;br /&gt;
  0xaaa,0xaaa3, 0x5555, 0x4155, 0x7000,&lt;br /&gt;
  0xaaa,0xaaa3, 0x5555, 0x3c55, 0x70f0,&lt;br /&gt;
  0xaaa,0x00a3, 0x5555, 0x3f15, 0x73f0,&lt;br /&gt;
  0xaaa,0x3c23, 0x5555, 0x3fc0, 0x0ff0,&lt;br /&gt;
  0xaaa,0x0f03, 0x5555, 0x3fff, 0xfff0,&lt;br /&gt;
  0xaaa,0x83c3, 0x5554, 0xffff, 0xfffc,&lt;br /&gt;
  0xaaa,0xa0f3, 0x5554, 0xfe3f, 0xfe3c,&lt;br /&gt;
  0xaaa,0xa803, 0x5554, 0xfc3f, 0xcc3c,&lt;br /&gt;
  0xaaa,0xaa83, 0x5554, 0xd7ff, 0xffd4,&lt;br /&gt;
  0xaaa,0xaaa3, 0xd554, 0xd73f, 0x3cd4,&lt;br /&gt;
  0xaaa,0xaaa3, 0xf555, 0x3f00, 0x00f0,&lt;br /&gt;
  0xaaa,0x0000, 0xffff, 0xcfff, 0xffc0,&lt;br /&gt;
  0xaaa,0x003f, 0x0000, 0x0000, 0x0000,&lt;br /&gt;
  0xaaa,0x003c, 0x03c0, 0x00f0, 0x3c00&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
NMT_GFX ngt;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  ngt.begin(11,10);&lt;br /&gt;
  ngt.print(&amp;quot;Hello!\nThis is an arduino writing\non screen with a &amp;quot;);&lt;br /&gt;
  ngt.println(ngt.get_card_ver());&lt;br /&gt;
&lt;br /&gt;
  delay(1200);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;Look, I can even do colors!&amp;quot;);&lt;br /&gt;
  ngt.set_color(3);&lt;br /&gt;
  ngt.print(&amp;quot;green &amp;quot;);&lt;br /&gt;
  ngt.set_color(2);&lt;br /&gt;
  ngt.print(&amp;quot;red &amp;quot;);&lt;br /&gt;
  ngt.set_color(1);&lt;br /&gt;
  ngt.println(&amp;quot;and white&amp;quot;);&lt;br /&gt;
  ngt.println(&amp;quot;(plus much more)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  delay(1400);&lt;br /&gt;
&lt;br /&gt;
  ngt.fill(2);&lt;br /&gt;
  ngt.println(&amp;quot;Screen fills can be done very fast.&amp;quot;);&lt;br /&gt;
  delay(1700);&lt;br /&gt;
&lt;br /&gt;
  ngt.clear();&lt;br /&gt;
  ngt.println(&amp;quot;I dont only do text though, so&amp;quot;);&lt;br /&gt;
  ngt.println(&amp;quot;let me load my graphics&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  // This line simply sets the size and center of the sprite.&lt;br /&gt;
  // It modifies the 'cat' byte data, so that when it is&lt;br /&gt;
  // uploaded, it will be the correct size, and centered.&lt;br /&gt;
  // We don't do this for the tree, as this data is specified directly&lt;br /&gt;
  // in the initialization of the 'tree' array.  Either way&lt;br /&gt;
  // of initializing it works perfectly fine.&lt;br /&gt;
  Sprite catSprite((byte*)cat, 33, 17, 16, 16);&lt;br /&gt;
&lt;br /&gt;
  int treeHandle = ngt.uploadSprite(tree);&lt;br /&gt;
  int catHandle = ngt.uploadSprite((byte*)cat);&lt;br /&gt;
&lt;br /&gt;
  delay(750);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;I can do lines and sprites efficiently too.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  byte x_tiles=ngt.x_tiles();              // Get tiles for x and y&lt;br /&gt;
  byte y_tiles=ngt.y_tiles();&lt;br /&gt;
  ngt.block_color(64+63,0b001100);&lt;br /&gt;
  ngt.block_color(128+63,0b011000);        // define tree colors&lt;br /&gt;
  ngt.block_color(192+63,0b010001);&lt;br /&gt;
  for(byte i=0;i&amp;lt;50;i++){    // make all tiles use text coloring&lt;br /&gt;
    ngt.tile_color(i,0);&lt;br /&gt;
  }&lt;br /&gt;
  for(byte i=x_tiles-1;i&amp;lt;50;i+=x_tiles){   // Make bottom tiles use tree coloring&lt;br /&gt;
    ngt.tile_color(i,63);&lt;br /&gt;
  }&lt;br /&gt;
  ngt.sprite(4, 180, 0, treeHandle);&lt;br /&gt;
  ngt.sprite(13, 180, 0, treeHandle);&lt;br /&gt;
  ngt.sprite(48, 180, 2, treeHandle);&lt;br /&gt;
  ngt.sprite(68, 180, 0, catHandle);&lt;br /&gt;
  ngt.sprite(28, 180, 1, treeHandle);&lt;br /&gt;
  ngt.line(0, 150, 30, 130);&lt;br /&gt;
  ngt.fast(60, 150);&lt;br /&gt;
  ngt.fast(90, 130);&lt;br /&gt;
  ngt.fast(120, 150);&lt;br /&gt;
  ngt.fast(150, 130);&lt;br /&gt;
&lt;br /&gt;
  delay(1000);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(' ');&lt;br /&gt;
  ngt.set_color(1);&lt;br /&gt;
  ngt.println(&amp;quot;The trees are drawn as sprites: an image of one is stored and copied to the screen using only one command, instead of resending the entire tree over serial.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  delay(6000);&lt;br /&gt;
&lt;br /&gt;
  ngt.set_cursor_pos(10, 190);&lt;br /&gt;
  ngt.println(&amp;quot;Now I'll show you a moving rainbow sprite.&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  delay(1750);&lt;br /&gt;
&lt;br /&gt;
  ngt.clear();&lt;br /&gt;
&lt;br /&gt;
  // This sets up the colors for each tile&lt;br /&gt;
  for(byte i=0;i&amp;lt;64;i++){&lt;br /&gt;
    ngt.block_color(128+i,i); // RAINBOW&lt;br /&gt;
    ngt.block_color(64+i,25); // PINK for Nyan Cat&lt;br /&gt;
    ngt.block_color(192+i,0b101010); // GREY for Nyan Cat&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // We only need to call these once.  Instead of clearing the screen each frame,&lt;br /&gt;
  // we will erase the old sprite right before drawing a new one.  This allows faster&lt;br /&gt;
  // rendering, and frees us from the need to write a message over and over again.&lt;br /&gt;
  ngt.clear();&lt;br /&gt;
  ngt.set_color(3);&lt;br /&gt;
  ngt.println(&amp;quot;Moving rainbow sprite!&amp;quot;);&lt;br /&gt;
  ngt.set_color(0);&lt;br /&gt;
&lt;br /&gt;
  // This tells the NGT31 to draw only when nothing is being displayed.&lt;br /&gt;
  // Doing this reduces visual artifacts and makes the video data look cleaner.&lt;br /&gt;
  // This only really affects moving objects and changing images: static&lt;br /&gt;
  // text is not affected.&lt;br /&gt;
  // 'B' stands for 'Blanking' - it only draws when no video data is being sent to the monitor.&lt;br /&gt;
  ngt.set_draw_mode('B');&lt;br /&gt;
&lt;br /&gt;
  int lastX = 0;&lt;br /&gt;
  int lastY = 40;&lt;br /&gt;
  int vx = 1, vy = 1;&lt;br /&gt;
&lt;br /&gt;
  while(true) {&lt;br /&gt;
    long ls = millis();&lt;br /&gt;
&lt;br /&gt;
    int x = lastX + vx;&lt;br /&gt;
    int y = lastY + vy;&lt;br /&gt;
    ngt.moveSprite(lastX, lastY, x, y, 0, catHandle);&lt;br /&gt;
&lt;br /&gt;
    lastX = x;&lt;br /&gt;
    lastY = y;&lt;br /&gt;
    if (x &amp;gt;= 300)&lt;br /&gt;
      vx = -1;&lt;br /&gt;
    else if (x &amp;lt;= 0)&lt;br /&gt;
      vx = 1;&lt;br /&gt;
    if (y &amp;gt;= 240)&lt;br /&gt;
      vy = -1;&lt;br /&gt;
    else if (y &amp;lt;= 30)&lt;br /&gt;
      vy = 1;&lt;br /&gt;
&lt;br /&gt;
    while(ls + 16 &amp;gt; millis());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Drawing 3D lines, as part of a very simple 3D videogame demonstration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;NMT_GFX.h&amp;gt;&lt;br /&gt;
#include &amp;lt;PS2Keyboard.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
NMT_GFX ngt;&lt;br /&gt;
&lt;br /&gt;
PS2Keyboard keyboard;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  keyboard.begin(4, 2);&lt;br /&gt;
  ngt.begin(11,10);&lt;br /&gt;
  ngt.println(&amp;quot;Loading world...&amp;quot;);&lt;br /&gt;
  ngt.add_line(-1000,-100,-1000,1000,-100,-1000,2);        &lt;br /&gt;
  ngt.add_line(1000,-100,-1000,1000,100,-1000,2);               &lt;br /&gt;
  ngt.add_line(1000,100,-1000,1000,100,1000,2);                 &lt;br /&gt;
  ngt.add_line(1000,100,1000,-1000,100,1000,2);              &lt;br /&gt;
  ngt.add_line(-1000,100,1000,-1000,-100,1000,2);              &lt;br /&gt;
  ngt.add_line(-1000,-100,1000,-1000,-100,-1000,2);&lt;br /&gt;
&lt;br /&gt;
  ngt.add_line(-1000,-100,1000,1000,-100,1000,2);            &lt;br /&gt;
  ngt.add_line(1000,-100,-1000,1000,-100,1000,2);&lt;br /&gt;
&lt;br /&gt;
  ngt.add_line(-1000,-100,-1000,-1000,100,-1000,2);&lt;br /&gt;
  ngt.add_line(1000,-100,1000,1000,100,1000,2);&lt;br /&gt;
&lt;br /&gt;
  ngt.add_line(-1000,100,-1000,1000,100,-1000,2);&lt;br /&gt;
  ngt.add_line(-1000,100,-1000,-1000,100,1000,2);&lt;br /&gt;
&lt;br /&gt;
  ngt.add_line(10,10,10,30,10,10,3);        &lt;br /&gt;
  ngt.add_line(30,10,10,30,30,10,3);               &lt;br /&gt;
  ngt.add_line(30,30,10,30,30,30,3);                 &lt;br /&gt;
  ngt.add_line(30,30,30,10,30,30,3);              &lt;br /&gt;
  ngt.add_line(10,30,30,10,10,30,3);              &lt;br /&gt;
  ngt.add_line(10,10,30,10,10,10,3);&lt;br /&gt;
&lt;br /&gt;
  ngt.add_line(10,10,30,30,10,30,3);            &lt;br /&gt;
  ngt.add_line(30,10,10,30,10,30,3);&lt;br /&gt;
&lt;br /&gt;
  ngt.add_line(10,10,10,10,30,10,3);&lt;br /&gt;
  ngt.add_line(30,10,30,30,30,30,3);&lt;br /&gt;
&lt;br /&gt;
  ngt.add_line(10,30,10,30,30,10,3);&lt;br /&gt;
  ngt.add_line(10,30,10,10,30,30,3);&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;Setting up colors...&amp;quot;);&lt;br /&gt;
  for(byte i=0;i&amp;lt;64;i++) {&lt;br /&gt;
    ngt.block_color(128 + i, 0b001011);&lt;br /&gt;
    ngt.block_color(192 + i, 0b111111);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Supported on newer firmware - reduces flicker&lt;br /&gt;
  ngt.set_draw_mode('B');&lt;br /&gt;
&lt;br /&gt;
  ngt.println(&amp;quot;Ready to play...&amp;quot;);&lt;br /&gt;
  delay(500);&lt;br /&gt;
  double x=0,z=0;&lt;br /&gt;
  int rot=0;&lt;br /&gt;
  while(true) {&lt;br /&gt;
    int32_t time=millis();&lt;br /&gt;
    if (keyboard.available()) {&lt;br /&gt;
&lt;br /&gt;
      // read the next key&lt;br /&gt;
      char c = keyboard.read();&lt;br /&gt;
      if (c == PS2_LEFTARROW) {&lt;br /&gt;
        rot+=3;&lt;br /&gt;
      } else if (c == PS2_RIGHTARROW) {&lt;br /&gt;
        rot-=3;&lt;br /&gt;
      } else if (c == PS2_UPARROW) {&lt;br /&gt;
        x-=cos(rot*PI/180);&lt;br /&gt;
        z+=sin(rot*PI/180);&lt;br /&gt;
      } else if (c == PS2_DOWNARROW) {&lt;br /&gt;
        x+=cos(rot*PI/180);&lt;br /&gt;
        z-=sin(rot*PI/180);&lt;br /&gt;
      }&lt;br /&gt;
      ngt.rotatef(rot,0);&lt;br /&gt;
      ngt.translatef((int32_t)x,0,(int32_t)z);&lt;br /&gt;
    }&lt;br /&gt;
    ngt.frame_3d();&lt;br /&gt;
    delay(max(1,(16+time)-(int32_t)millis()));&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=UPD7220_Video_Card_(Revision_2)&amp;diff=52</id>
		<title>UPD7220 Video Card (Revision 2)</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=UPD7220_Video_Card_(Revision_2)&amp;diff=52"/>
		<updated>2024-06-14T04:49:19Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Formatting fixes&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
The uPD7220 Video Card revision 2 is a video card with 128KB of VRAM using the uPD7220 GDC or its variants.  It operates at a maximum clock speed of 32Mhz.  The card is based on the original uPD7220 card I (Dylan Brophy) made, which had half the VRAM and generally was far simpler.  This card solves several issues with the older card, at the cost of greater size and complexity.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
* 16-color VGA output&lt;br /&gt;
* 128KB of RAM&lt;br /&gt;
** 4 RAM chips, 32KB each, 25ns access and write time&lt;br /&gt;
** 16 bit bus (8 bits per chip, two chips per byte)&lt;br /&gt;
** 4 color bits per pixel, 4 pixels per word in RAM&lt;br /&gt;
*** Maximum 262144 pixels (hardware double buffering disabled)&lt;br /&gt;
**** Suggested resolutions: 544x480, 640x408&lt;br /&gt;
*** Maximum 131072 pixels (hardware double buffering enabled)&lt;br /&gt;
**** Suggested resolutions: 448x288, 400x300&lt;br /&gt;
* uPD7220 at up to 24Mhz, Z7220 at up to 32Mhz&lt;br /&gt;
* uPD7220 wide mode supported&lt;br /&gt;
** Allows higher pixel clock frequencies, for higher resolutions and lower visible display times&lt;br /&gt;
** Accesses two VRAM words per uPD7220 clock cycle&lt;br /&gt;
* Direct access to VRAM is provided to the host CPU&lt;br /&gt;
&lt;br /&gt;
== Video Modes ==&lt;br /&gt;
&lt;br /&gt;
It is assumed that the reader is familiar with VGA display timings.  If not, then it is recommended to study it, or to use the video timing calculator and to leave the card in wide mode.&lt;br /&gt;
&lt;br /&gt;
The uPD7220 was invented before the VGA standard, and due to this, was not designed to output a VGA signal.  Since VGA was designed to drive CRT monitors, as was the uPD7220, it turns out that&lt;br /&gt;
they are fairly compatible.  VGA timings work perfectly fine, but to compute them one must keep in mind the pixel and uPD7220 clocks.  The uPD7220 clock is always one fourth of the card's clock&lt;br /&gt;
speed.&lt;br /&gt;
&lt;br /&gt;
For common VGA signal timings, I would recommend looking at this very useful site: [http://tinyvga.com/vga-timing TinyVGA signal timings]&lt;br /&gt;
&lt;br /&gt;
One must take the timings and convert them to the correct resolution.  For an example, let's convert the 640x480 VGA standard timings to a 400x400 resolution.  Here are the video timings from [http://tinyvga.com/vga-timing/640x480@60Hz TinyVGA's 640x480 60hz page]:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Horizontal Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Pixels !! Microseconds&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 640 || 25.4220&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 16 || 0.6356&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 96 || 3.8133&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 48 || 1.9067&lt;br /&gt;
|-&lt;br /&gt;
| Whole line || 800 || 31.778&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vertical Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Lines&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 480&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 10&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 2&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 33&lt;br /&gt;
|-&lt;br /&gt;
| Whole frame || 525&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
First, we need to decide on a clock frequency for our card.  24Mhz is slower than the 25.175Mhz recommended, and this will make our pixels wider by about 5%.  However, a 24Mhz clock will run our uPD7220 at 6Mhz,&lt;br /&gt;
right at the maximum frequency, and 25.175Mhz would overclock it.  We will select 24Mhz, as the 5% elongation of pixels is too small to care about, and the timings should be compatible with the uPD7220 IC.  We&lt;br /&gt;
will enable wide mode, so our pixel clock is set at 24Mhz (the same as the card - without wide mode it would be 12Mhz, or half).  This keeps the pixel width close to the intended width.  Importantly, this change in clock speed will change the necessary timings for the video signal.  Since each uPD7220 clock cycle is 166.7ns at 6Mhz, and each word is two cycles, we will be dividing the horizontal porch and sync widths&lt;br /&gt;
by 333.3ns to get the number of words for each of the regions.  We cannot compute this yet though, because the visible region still needs to be adjusted for our new resolution.&lt;br /&gt;
&lt;br /&gt;
For our application, we need less pixels, but still need to meet the timing spec.  To achieve this, we can increase the back porch and reduce the visible region until we have the resolution we want.  We do this&lt;br /&gt;
on both the x and y axes.  This works because, during the back porch, the video signal is sent a black signal.  On older monitors, this corresponds to a dark box around the image, and newer monitors will treat it&lt;br /&gt;
as a blank region.  Fundamentally, the monitor will still generally treat it as a 640x480, standard VGA signal, because we haven't encroached on the porches, and we haven't changed/moved the sync signals.  To&lt;br /&gt;
compute the width of the new visible area, we multiply the number of horizontal pixels by the pixel clock period: 41.67ns * 400 pixels = 16.67us.  From this, we can calculate the new horizontal front porch, and&lt;br /&gt;
then we can recompute the lines per vertical video region:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Updated Horizontal Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Microseconds&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 16.67&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 0.6356&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 3.8133&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 10.6620&lt;br /&gt;
|-&lt;br /&gt;
| Whole line || 31.7776&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The back porch becomes much wider.  All we did was move the time over from the visible area to the back porch, such that the visible area has the correct number of pixels at our clock frequency.  The vertical&lt;br /&gt;
regions are adjusted similarly, but the math is much simpler, because we are only trying to keep constant the number of lines:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vertical Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Lines&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 400&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 60&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 2&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 63&lt;br /&gt;
|-&lt;br /&gt;
| Whole frame || 525&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
You will notice that I added lines to both the front and back vertical porches.  This is because the uPD7220 cannot output more than 63 of each porch, so I needed to add to both to obey this limitation.  For&lt;br /&gt;
lower resolutions, it is usually acceptable to reduce the total number of lines to obey these limitations.  It is important, however, that in such a case the horizontal lines are made longer to keep the refresh rate at 60hz, and that the video settings are tested to work on the monitor.&lt;br /&gt;
&lt;br /&gt;
At this point we have the description of the mode we want, now we can convert it into the numbers used by the card.  The vertical timings are already in the correct format, but the horizontal timing still must&lt;br /&gt;
be converted.  To do this, we need to get the number of memory cycles (not uPD7220 clock cycles) for each horizontal region, which is the time for two uPD7220 clock cycles.  The exception is the visible region,&lt;br /&gt;
which is computed from the amount of memory used to represent the data (4 pixels per word, one word accessed per uPD7220 cycle in wide mode).  For the visible region, we divide the number of pixels by 4, and&lt;br /&gt;
for the other regions, we divide the time by 333.3ns and round to the nearest whole number.  Here is the result:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ uPD7220 timing settings&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Words&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 100&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 1.907 =&amp;gt; 2&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 11.4399 =&amp;gt; 11&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 31.986 =&amp;gt; 32&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Any VGA monitor should accept these signal timings for a 400x400 resolution.  For video memory consumtion, this needs 400 * 400 / 4 = 40000 words of memory.  The card has 65536 words total, but only&lt;br /&gt;
32768 if automatic double buffering is enabled, so we cannot enable automatic double buffering for this resolution.  So, we must enable wide mode and disable double buffering for this video mode.&lt;br /&gt;
&lt;br /&gt;
'''Card Specific Details: Wide Mode'''&lt;br /&gt;
&lt;br /&gt;
This card can reach quite high clock speeds for the uPD7220, and to maintain this high speed for such slow chips, wide mode is used.  This allows twice as many pixels to be output per uPD7220 clock cycle.  This can be used either to increase the maximum resolution, or to reduce the display time.  Reducing the display time gives the uPD7220 and host processor more time to draw to the screen.  On the other hand, due to&lt;br /&gt;
the high speeds used in wide mode, there is also typically more data that must be written to the display per frame, thus putting more pressure on the host processor.  The balance in display characteristics depends on the application.&lt;br /&gt;
&lt;br /&gt;
Due to wide mode support, the card's clock is 4x the uPD7220's clock.  When doing calculations for video, this must be kept in mind.  If wide mode is disabled, then the pixel clock is only half the card's clock.  If the video timing math does not account for this, then the video signal timing will be incorrect.&lt;br /&gt;
&lt;br /&gt;
== Communication with the Host Processor ==&lt;br /&gt;
&lt;br /&gt;
The hardware connection uses a fairly simple 8-bit parallel bus for data exchange.  Once connected, the software can read from and write to several registers and addresses on the card to control it.&lt;br /&gt;
&lt;br /&gt;
=== Hardware Protocol ===&lt;br /&gt;
&lt;br /&gt;
Here is a pinout of the card:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Card Pinout&lt;br /&gt;
|-&lt;br /&gt;
! Pin !! Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || A0 || Address pin 0 (LSB)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || A1 || Address pin 1&lt;br /&gt;
|-&lt;br /&gt;
| 3 || A2 || Address pin 2 (MSB)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || NC || No connection: this is the key pin and should be blocked&lt;br /&gt;
|-&lt;br /&gt;
| 5 || GND || Ground connection&lt;br /&gt;
|-&lt;br /&gt;
| 6 || D0 || Data pin 0 (LSB)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || D1 || Data pin 1&lt;br /&gt;
|-&lt;br /&gt;
| 8 || D2 || Data pin 2&lt;br /&gt;
|-&lt;br /&gt;
| 9 || D3 || Data pin 3&lt;br /&gt;
|-&lt;br /&gt;
| 10 || D4 || Data pin 4&lt;br /&gt;
|-&lt;br /&gt;
| 11 || D5 || Data pin 5&lt;br /&gt;
|-&lt;br /&gt;
| 12 || D6 || Data pin 6&lt;br /&gt;
|-&lt;br /&gt;
| 13 || D7 || Data pin 7 (MSB)&lt;br /&gt;
|-&lt;br /&gt;
| 14 || SEL || Select pin (active low input) - selects the card for communication&lt;br /&gt;
|-&lt;br /&gt;
| 15 || 5v || +5V rail to power the card&lt;br /&gt;
|-&lt;br /&gt;
| 16 || RD || Read enable pin (active low input)&lt;br /&gt;
|-&lt;br /&gt;
| 17 || WR || Write enable pin (active low input)&lt;br /&gt;
|-&lt;br /&gt;
| 18 || WAIT || Wait indication (active low output)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When trying to access VRAM, if the VRAM is being used by the card, then WAIT will be pulled low to tell the CPU not to end the current operation until after the WAIT pin goes high again.  After WAIT goes high, wait at least 31.25ns for the read/write operation to complete before releasing any control lines or changing any data/address lines.  Never read data while WAIT is low, and wait 31.25ns after WAIT goes high before reading the data to the CPU.  WAIT only becomes low when trying to read from or write to VRAM; otherwise WAIT is always high.&lt;br /&gt;
&lt;br /&gt;
Each access cycle should last at least 31.25ns, and SEL should be de-asserted between cycles.  Any conventional processor like Z80, 8086, 6502, etc, should have no issue with WAIT or access cycles below an operating frequency of 32Mhz, regardless of the speed of the card.  There may be exceptions to this, but there are no known exceptions.&lt;br /&gt;
&lt;br /&gt;
=== Software Protocol ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Card Registers&lt;br /&gt;
|-&lt;br /&gt;
! Register !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| 0 || uPD7220 command register (uPD7220 A0 = 0)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || uPD7220 parameter register (uPD7220 A0 = 1)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Card configuration register (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Direct VRAM access (accessing this may cause WAIT to go low)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || VRAM Address bits 0-7 (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Reserved - for now same as register 4&lt;br /&gt;
|-&lt;br /&gt;
| 6 || VRAM Address bits 8-15 (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || Reserved - for now same as register 6&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For this card, a driver should first configure register 2 for the selected video mode and card interface configuration.  This register has various bits to tell the card what to do:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ Configuration Register Bits&lt;br /&gt;
|-&lt;br /&gt;
! Bit !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Enables automatic counting of the VRAM address registers if set.  If reset, then the VRAM address registers do not change when VRAM is written.  More on this later.&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Setting this bit enables the card's wide mode circutry.  When reset, one pixel will be output every other clock cycle.  This should match the setting on the uPD7220 IC.&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Enables automatic double buffering.  When set, draws to VRAM from the host CPU or the uPD7220 will happen on the opposite buffer as the one displaying.  Setting this halfs the available VRAM for displaying.&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 6 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 7 || Controls which buffer is displaying.  Changing this bit will swap the currently displaying buffer.  Only works in automatic double buffer mode.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It is recommended to keep track of what buffer is currently displaying, and perhaps this entire configuration register, so that the selected buffer can be switched.  This register is important for controlling how the CPU accesses VRAM, how smooth the display is, and the way the image data is displayed.  Generally, automatic double buffering is recommended if the resolution demands less than half of the available VRAM, as&lt;br /&gt;
double buffering makes the display smoother by removing visual artifacts due to drawing, but doubles VRAM demand. &lt;br /&gt;
&lt;br /&gt;
You can read the [http://www.vintagecomputer.net/fjkraan/comp/qx10/doc/nec7220.pdf programming manual] to get a detailed understanding of how to program the uPD7220, but I will also provide an abridged&lt;br /&gt;
version here.  Once the card's configuration register is set, the uPD7220 must be initialized.  The uPD7220 requires a sequence of commands to be sent to set it up:&lt;br /&gt;
&lt;br /&gt;
# A reset command (write 0x00 to address 0x01)&lt;br /&gt;
#* This must be followed by parameter bytes, described below&lt;br /&gt;
# Setting the video mode to master (write 0x6f to address 0x01)&lt;br /&gt;
# Configuring the pitch&lt;br /&gt;
# Initializing the PRAM&lt;br /&gt;
#* PRAM is some internal memory in the uPD7220, which configures how it accesses memory, and probably some other things&lt;br /&gt;
# CCHAR command to configure the character characteristics&lt;br /&gt;
# Disabling zoom (write 0x46 to 0x01, then 0x00 to 0x00)&lt;br /&gt;
# Starting the display (write 0x6B to 0x01)&lt;br /&gt;
# Disable blanking (write 0x0D to 0x01)&lt;br /&gt;
&lt;br /&gt;
My driver also seems to issue a FIGS command at the end of startup, but this may not be necessary.&lt;br /&gt;
&lt;br /&gt;
Each command starts with a write to address 0x01, and for each parameter a write to address 0x00 follows.  Each command I describe will proceed this way, with the first byte being the command byte written to&lt;br /&gt;
address 0x01, and all following bytes being parameters written to address 0x00.  At the end I will have some example code in C which provides as both an example and an additional explanation.&lt;br /&gt;
&lt;br /&gt;
The first command is the reset command; here is the format:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ The Reset Command&lt;br /&gt;
|-&lt;br /&gt;
! Address !! Value / Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0x00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Operation mode.  Discussed below.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Horizontal display words minus two.  Must be an even number.  This is equal to ((horizontal_pixels / 4) - 2).  Wide mode does not affect this.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Lower 5 bits are the HSync width minus 1, in words.  Upper 3 bits are the lower 3 bits of the VSync width.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Lower 2 bits are the upper 2 bits of the VSync width.  Upper 6 bits are the horizontal front porch width minus 1.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Horizontal back porch width minus one.  Upper two bits must be zero.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Vertical front porch width minus one.  Upper two bits must be zero.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Number of display lines per frame, lower 8 bits.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Lower 2 bits are the upper 2 bits of the number of display lines per frame.  Upper 6 bits are the vertical back porch length.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note that the vertical field sizes do not have numbers subtracted from them, but the horizontal field sizes have one or two subtracted from them.  If this description is confusing, the uPD7220 manual provided above does contain a nice illustration you may consider looking at.&lt;br /&gt;
&lt;br /&gt;
The operation mode is a bitmask, which controls interlacing, character or graphic mode, as well as DRAM refresh and if drawing is allowed during an active display cycle.  Here are some of the options that&lt;br /&gt;
one can OR together to control the operation mode:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ Operation control bits&lt;br /&gt;
|-&lt;br /&gt;
! Bitmask !! Selected Option&lt;br /&gt;
|-&lt;br /&gt;
| 0x00 || Leave bits 5 and 1 zero to have mixed character and graphics mode.&lt;br /&gt;
|-&lt;br /&gt;
| 0x02 || Enables just graphics mode&lt;br /&gt;
|-&lt;br /&gt;
| 0x20 || Enables just character mode&lt;br /&gt;
|-&lt;br /&gt;
| 0x00 || Leave bits 3 and 0 zero to disable interlacing&lt;br /&gt;
|-&lt;br /&gt;
| 0x08 || Enables interlacing&lt;br /&gt;
|-&lt;br /&gt;
| 0x09 || Enables &amp;quot;Interlaced repeat field for character displays&amp;quot;.  Read the manual for more information.&lt;br /&gt;
|-&lt;br /&gt;
| 0x04 || Enables DRAM refresh cycles - not used on this uPD7220 card, as static RAM is used.&lt;br /&gt;
|-&lt;br /&gt;
| 0x10 || Disables drawing during active display time.  Enabling this is recommended.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Generally 0x12 is a good setting for this card, unless you intend on using character mode.  0x10 will also work as a good mode.&lt;br /&gt;
&lt;br /&gt;
The second command one must send is 0x6f, again written to address 0x01, and it does not take any parameters.  The third command is the PITCH command:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ The Pitch Command&lt;br /&gt;
|-&lt;br /&gt;
! Address !! Value / Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0x47&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Number of memory words per line.  Usually the same as the number of display words per line.  Generally equal to (horizontal_pixels / 4).  The only reason it would differ, is if the &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The fourth command is a PRAM command, which writes data to the uPD7220 parameter RAM.  Here is a recommended usage of the command (although other usages are possible, again read the manual for more info):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ The PRAM Command&lt;br /&gt;
|-&lt;br /&gt;
! Address !! Value / Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0x70 (starts PRAM write at PRAM byte 0)&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Display partition 1 starting address (lower 8 bits) - it is recommended to leave this as zero.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Display partition 1 starting address (bits 8-16) - it is recommended to leave this as zero.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Lower 2 bits are the display partition 1 starting address (upper 2 bits).  Upper 4 bits are the lower 4 bits of the display partition length, in horizontal lines (NOT words).  Bits 2 and 3 should be left as zeros.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Lower 6 bits are the upper 6 bits of the image partition length, in horizontal lines (NOT words).  Bit 6 (0x40) indicates it is a graphics area, otherwise it is a character area if in mixed mode.  Bit 7 (0x80) enables wide mode - ensure to set this bit if you are using wide mode.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The parameter RAM has more variables, but only these really need to be specified.  There is another display partition that one can use, and configuration options for drawing and characters, later in the PRAM.&lt;br /&gt;
&lt;br /&gt;
If your display mode uses wide mode, then be sure to set the MSB of the fourth parameter byte of this command, otherwise the memory access will be incorrect for wide mode.&lt;br /&gt;
&lt;br /&gt;
Next, the CCHAR command is used to define the cursor and character characteristics:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ The CCHAR Command&lt;br /&gt;
|-&lt;br /&gt;
! Address !! Value / Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0x4B&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Setting the MSB will enable the cursor in character mode.  The lower 5 bits are the number of lines per character row minus one.  My driver leaves this byte as zero.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Lower 5 bits define the cursor top line number in the row.  Bit 5 makes the cursor steady if set, or blinking if reset.  Upper two bits are the lower two bits of the blink rate.  My driver leaves this byte as 0xC0.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is sufficient for non-interlaced modes, but interlaced modes require this command to get a third byte.&lt;br /&gt;
&lt;br /&gt;
Zoom must be disabled with a 0x46 write to address 0x01, followed by writing 0x00 to address 0x00.  At this point we can finally enable the display and disable blanking by writing commands 0x6B, then 0x0D, to address 0x01.&lt;br /&gt;
&lt;br /&gt;
My driver executes a FIGS command last, although this probably is not necessary.  Just in case, here is the command sequence:&lt;br /&gt;
&lt;br /&gt;
# 0x4C to address 0x01&lt;br /&gt;
# 0x02 to address 0x00&lt;br /&gt;
# 0x00 to address 0x00&lt;br /&gt;
# 0x00 to address 0x00&lt;br /&gt;
&lt;br /&gt;
Here is my code which executes this exact sequence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void setup_upd7220() {&lt;br /&gt;
  // Reset command&lt;br /&gt;
  write(1, 0);&lt;br /&gt;
  write(0, 0x10); // operation mode: draw only during blanking&lt;br /&gt;
  write(0, HORIZ_WORDS - 2);&lt;br /&gt;
  write(0, (VERT_SYNC &amp;lt;&amp;lt; 5) | (HORIZ_SYNC - 1));&lt;br /&gt;
  write(0, ((HORIZ_FRONT_PORCH - 1) &amp;lt;&amp;lt; 2) | (VERT_SYNC &amp;gt;&amp;gt; 3));&lt;br /&gt;
  write(0, (HORIZ_BACK_PORCH - 1) &amp;amp; 63);&lt;br /&gt;
  write(0, VERT_FRONT_PORCH &amp;amp; 63);&lt;br /&gt;
  write(0, VERT_PIXELS &amp;amp; 255);&lt;br /&gt;
  write(0, (VERT_BACK_PORCH &amp;lt;&amp;lt; 2) | (VERT_PIXELS &amp;gt;&amp;gt; 8));&lt;br /&gt;
&lt;br /&gt;
  // set master video mode&lt;br /&gt;
  write(1, 0x6f);&lt;br /&gt;
&lt;br /&gt;
  // set pitch&lt;br /&gt;
  write(1, 0x47);&lt;br /&gt;
  write(0, HORIZ_WORDS);&lt;br /&gt;
&lt;br /&gt;
  // set some PRAM values&lt;br /&gt;
  write(1, 0x70);&lt;br /&gt;
  write(0, 0x00);&lt;br /&gt;
  write(0, 0x00);&lt;br /&gt;
  write(0, (VERT_PIXELS &amp;lt;&amp;lt; 4) &amp;amp; 255);&lt;br /&gt;
  write(0, (VERT_PIXELS &amp;gt;&amp;gt; 4) | (WIDE_MODE ? 8 : 0));&lt;br /&gt;
&lt;br /&gt;
  // Set character and cursor characteristics&lt;br /&gt;
  write(1, 0x4B);&lt;br /&gt;
  write(0, 0x00);&lt;br /&gt;
  write(0, 0xC0);&lt;br /&gt;
&lt;br /&gt;
  // set zoom&lt;br /&gt;
  write(1, 0x46);&lt;br /&gt;
  write(0, 0x00);&lt;br /&gt;
&lt;br /&gt;
  // Enable the display&lt;br /&gt;
  write(1, 0x6b);&lt;br /&gt;
&lt;br /&gt;
  // Disable blanking&lt;br /&gt;
  write(1, 0x0D);&lt;br /&gt;
&lt;br /&gt;
  // This is probably not necessary&lt;br /&gt;
  write(1, 0x4c);&lt;br /&gt;
  write(0, 0x02);&lt;br /&gt;
  write(0, 0x00);&lt;br /&gt;
  write(0, 0x00);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=UPD7220_Video_Card_(Revision_2)&amp;diff=51</id>
		<title>UPD7220 Video Card (Revision 2)</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=UPD7220_Video_Card_(Revision_2)&amp;diff=51"/>
		<updated>2024-06-14T04:41:44Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Removed a weird comment from the example code&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
The uPD7220 Video Card revision 2 is a video card with 128KB of VRAM using the uPD7220 GDC or its variants.  It operates at a maximum clock speed of 32Mhz.  The card is based on the original uPD7220 card I (Dylan Brophy) made, which had half the VRAM and generally was far simpler.  This card solves several issues with the older card, at the cost of greater size and complexity.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
* 16-color VGA output&lt;br /&gt;
* 128KB of RAM&lt;br /&gt;
** 4 RAM chips, 32KB each, 25ns access and write time&lt;br /&gt;
** 16 bit bus (8 bits per chip, two chips per byte)&lt;br /&gt;
** 4 color bits per pixel, 4 pixels per word in RAM&lt;br /&gt;
*** Maximum 262144 pixels (hardware double buffering disabled)&lt;br /&gt;
**** Suggested resolutions: 544x480, 640x408&lt;br /&gt;
*** Maximum 131072 pixels (hardware double buffering enabled)&lt;br /&gt;
**** Suggested resolutions: 448x288, 400x300&lt;br /&gt;
* uPD7220 at up to 24Mhz, Z7220 at up to 32Mhz&lt;br /&gt;
* uPD7220 wide mode supported&lt;br /&gt;
** Allows higher pixel clock frequencies, for higher resolutions and lower visible display times&lt;br /&gt;
** Accesses two VRAM words per uPD7220 clock cycle&lt;br /&gt;
* Direct access to VRAM is provided to the host CPU&lt;br /&gt;
&lt;br /&gt;
== Video Modes ==&lt;br /&gt;
&lt;br /&gt;
It is assumed that the reader is familiar with VGA display timings.  If not, then it is recommended to study it, or to use the video timing calculator and to leave the card in wide mode.&lt;br /&gt;
&lt;br /&gt;
The uPD7220 was invented before the VGA standard, and due to this, was not designed to output a VGA signal.  Since VGA was designed to drive CRT monitors, as was the uPD7220, it turns out that&lt;br /&gt;
they are fairly compatible.  VGA timings work perfectly fine, but to compute them one must keep in mind the pixel and uPD7220 clocks.  The uPD7220 clock is always one fourth of the card's clock&lt;br /&gt;
speed.&lt;br /&gt;
&lt;br /&gt;
For common VGA signal timings, I would recommend looking at this very useful site: [http://tinyvga.com/vga-timing TinyVGA signal timings]&lt;br /&gt;
&lt;br /&gt;
One must take the timings and convert them to the correct resolution.  For an example, let's convert the 640x480 VGA standard timings to a 400x400 resolution.  Here are the video timings from [http://tinyvga.com/vga-timing/640x480@60Hz TinyVGA's 640x480 60hz page]:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Horizontal Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Pixels !! Microseconds&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 640 || 25.4220&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 16 || 0.6356&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 96 || 3.8133&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 48 || 1.9067&lt;br /&gt;
|-&lt;br /&gt;
| Whole line || 800 || 31.778&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vertical Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Lines&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 480&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 10&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 2&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 33&lt;br /&gt;
|-&lt;br /&gt;
| Whole frame || 525&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
First, we need to decide on a clock frequency for our card.  24Mhz is slower than the 25.175Mhz recommended, and this will make our pixels wider by about 5%.  However, a 24Mhz clock will run our uPD7220 at 6Mhz,&lt;br /&gt;
right at the maximum frequency, and 25.175Mhz would overclock it.  We will select 24Mhz, as the 5% elongation of pixels is too small to care about, and the timings should be compatible with the uPD7220 IC.  We&lt;br /&gt;
will enable wide mode, so our pixel clock is set at 24Mhz (the same as the card - without wide mode it would be 12Mhz, or half).  This keeps the pixel width close to the intended width.  Importantly, this change in clock speed will change the necessary timings for the video signal.  Since each uPD7220 clock cycle is 166.7ns at 6Mhz, and each word is two cycles, we will be dividing the horizontal porch and sync widths&lt;br /&gt;
by 333.3ns to get the number of words for each of the regions.  We cannot compute this yet though, because the visible region still needs to be adjusted for our new resolution.&lt;br /&gt;
&lt;br /&gt;
For our application, we need less pixels, but still need to meet the timing spec.  To achieve this, we can increase the back porch and reduce the visible region until we have the resolution we want.  We do this&lt;br /&gt;
on both the x and y axes.  This works because, during the back porch, the video signal is sent a black signal.  On older monitors, this corresponds to a dark box around the image, and newer monitors will treat it&lt;br /&gt;
as a blank region.  Fundamentally, the monitor will still generally treat it as a 640x480, standard VGA signal, because we haven't encroached on the porches, and we haven't changed/moved the sync signals.  To&lt;br /&gt;
compute the width of the new visible area, we multiply the number of horizontal pixels by the pixel clock period: 41.67ns * 400 pixels = 16.67us.  From this, we can calculate the new horizontal front porch, and&lt;br /&gt;
then we can recompute the lines per vertical video region:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Updated Horizontal Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Microseconds&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 16.67&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 0.6356&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 3.8133&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 10.6620&lt;br /&gt;
|-&lt;br /&gt;
| Whole line || 31.7776&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The back porch becomes much wider.  All we did was move the time over from the visible area to the back porch, such that the visible area has the correct number of pixels at our clock frequency.  The vertical&lt;br /&gt;
regions are adjusted similarly, but the math is much simpler, because we are only trying to keep constant the number of lines:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vertical Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Lines&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 400&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 60&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 2&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 63&lt;br /&gt;
|-&lt;br /&gt;
| Whole frame || 525&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
You will notice that I added lines to both the front and back vertical porches.  This is because the uPD7220 cannot output more than 63 of each porch, so I needed to add to both to obey this limitation.  For&lt;br /&gt;
lower resolutions, it is usually acceptable to reduce the total number of lines to obey these limitations.  It is important, however, that in such a case the horizontal lines are made longer to keep the refresh rate at 60hz, and that the video settings are tested to work on the monitor.&lt;br /&gt;
&lt;br /&gt;
At this point we have the description of the mode we want, now we can convert it into the numbers used by the card.  The vertical timings are already in the correct format, but the horizontal timing still must&lt;br /&gt;
be converted.  To do this, we need to get the number of memory cycles (not uPD7220 clock cycles) for each horizontal region, which is the time for two uPD7220 clock cycles.  The exception is the visible region,&lt;br /&gt;
which is computed from the amount of memory used to represent the data (4 pixels per word, one word accessed per uPD7220 cycle in wide mode).  For the visible region, we divide the number of pixels by 4, and&lt;br /&gt;
for the other regions, we divide the time by 333.3ns and round to the nearest whole number.  Here is the result:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ uPD7220 timing settings&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Words&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 100&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 1.907 =&amp;gt; 2&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 11.4399 =&amp;gt; 11&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 31.986 =&amp;gt; 32&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Any VGA monitor should accept these signal timings for a 400x400 resolution.  For video memory consumtion, this needs 400 * 400 / 4 = 40000 words of memory.  The card has 65536 words total, but only&lt;br /&gt;
32768 if automatic double buffering is enabled, so we cannot enable automatic double buffering for this resolution.  So, we must enable wide mode and disable double buffering for this video mode.&lt;br /&gt;
&lt;br /&gt;
'''Card Specific Details: Wide Mode'''&lt;br /&gt;
&lt;br /&gt;
This card can reach quite high clock speeds for the uPD7220, and to maintain this high speed for such slow chips, wide mode is used.  This allows twice as many pixels to be output per uPD7220 clock cycle.  This can be used either to increase the maximum resolution, or to reduce the display time.  Reducing the display time gives the uPD7220 and host processor more time to draw to the screen.  On the other hand, due to&lt;br /&gt;
the high speeds used in wide mode, there is also typically more data that must be written to the display per frame, thus putting more pressure on the host processor.  The balance in display characteristics depends on the application.&lt;br /&gt;
&lt;br /&gt;
Due to wide mode support, the card's clock is 4x the uPD7220's clock.  When doing calculations for video, this must be kept in mind.  If wide mode is disabled, then the pixel clock is only half the card's clock.  If the video timing math does not account for this, then the video signal timing will be incorrect.&lt;br /&gt;
&lt;br /&gt;
== Communication with the Host Processor ==&lt;br /&gt;
&lt;br /&gt;
The hardware connection uses a fairly simple 8-bit parallel bus for data exchange.  Once connected, the software can read from and write to several registers and addresses on the card to control it.&lt;br /&gt;
&lt;br /&gt;
=== Hardware Protocol ===&lt;br /&gt;
&lt;br /&gt;
Here is a pinout of the card:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Card Pinout&lt;br /&gt;
|-&lt;br /&gt;
! Pin !! Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || A0 || Address pin 0 (LSB)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || A1 || Address pin 1&lt;br /&gt;
|-&lt;br /&gt;
| 3 || A2 || Address pin 2 (MSB)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || NC || No connection: this is the key pin and should be blocked&lt;br /&gt;
|-&lt;br /&gt;
| 5 || GND || Ground connection&lt;br /&gt;
|-&lt;br /&gt;
| 6 || D0 || Data pin 0 (LSB)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || D1 || Data pin 1&lt;br /&gt;
|-&lt;br /&gt;
| 8 || D2 || Data pin 2&lt;br /&gt;
|-&lt;br /&gt;
| 9 || D3 || Data pin 3&lt;br /&gt;
|-&lt;br /&gt;
| 10 || D4 || Data pin 4&lt;br /&gt;
|-&lt;br /&gt;
| 11 || D5 || Data pin 5&lt;br /&gt;
|-&lt;br /&gt;
| 12 || D6 || Data pin 6&lt;br /&gt;
|-&lt;br /&gt;
| 13 || D7 || Data pin 7 (MSB)&lt;br /&gt;
|-&lt;br /&gt;
| 14 || SEL || Select pin (active low input) - selects the card for communication&lt;br /&gt;
|-&lt;br /&gt;
| 15 || 5v || +5V rail to power the card&lt;br /&gt;
|-&lt;br /&gt;
| 16 || RD || Read enable pin (active low input)&lt;br /&gt;
|-&lt;br /&gt;
| 17 || WR || Write enable pin (active low input)&lt;br /&gt;
|-&lt;br /&gt;
| 18 || WAIT || Wait indication (active low output)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When trying to access VRAM, if the VRAM is being used by the card, then WAIT will be pulled low to tell the CPU not to end the current operation until after the WAIT pin goes high again.  After WAIT goes high, wait at least 31.25ns for the read/write operation to complete before releasing any control lines or changing any data/address lines.  Never read data while WAIT is low, and wait 31.25ns after WAIT goes high before reading the data to the CPU.  WAIT only becomes low when trying to read from or write to VRAM; otherwise WAIT is always high.&lt;br /&gt;
&lt;br /&gt;
Each access cycle should last at least 31.25ns, and SEL should be de-asserted between cycles.  Any conventional processor like Z80, 8086, 6502, etc, should have no issue with WAIT or access cycles below an operating frequency of 32Mhz, regardless of the speed of the card.  There may be exceptions to this, but there are no known exceptions.&lt;br /&gt;
&lt;br /&gt;
=== Software Protocol ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Card Registers&lt;br /&gt;
|-&lt;br /&gt;
! Register !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| 0 || uPD7220 command register (uPD7220 A0 = 0)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || uPD7220 parameter register (uPD7220 A0 = 1)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Card configuration register (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Direct VRAM access (accessing this may cause WAIT to go low)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || VRAM Address bits 0-7 (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Reserved - for now same as register 4&lt;br /&gt;
|-&lt;br /&gt;
| 6 || VRAM Address bits 8-15 (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || Reserved - for now same as register 6&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For this card, a driver should first configure register 2 for the selected video mode and card interface configuration.  This register has various bits to tell the card what to do:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ Configuration Register Bits&lt;br /&gt;
|-&lt;br /&gt;
! Bit !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Enables automatic counting of the VRAM address registers if set.  If reset, then the VRAM address registers do not change when VRAM is written.  More on this later.&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Setting this bit enables the card's wide mode circutry.  When reset, one pixel will be output every other clock cycle.  This should match the setting on the uPD7220 IC.&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Enables automatic double buffering.  When set, draws to VRAM from the host CPU or the uPD7220 will happen on the opposite buffer as the one displaying.  Setting this halfs the available VRAM for displaying.&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 6 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 7 || Controls which buffer is displaying.  Changing this bit will swap the currently displaying buffer.  Only works in automatic double buffer mode.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It is recommended to keep track of what buffer is currently displaying, and perhaps this entire configuration register, so that the selected buffer can be switched.  This register is important for controlling how the CPU accesses VRAM, how smooth the display is, and the way the image data is displayed.  Generally, automatic double buffering is recommended if the resolution demands less than half of the available VRAM, as&lt;br /&gt;
double buffering makes the display smoother by removing visual artifacts due to drawing, but doubles VRAM demand. &lt;br /&gt;
&lt;br /&gt;
You can read the [http://www.vintagecomputer.net/fjkraan/comp/qx10/doc/nec7220.pdf programming manual] to get a detailed understanding of how to program the uPD7220, but I will also provide an abridged&lt;br /&gt;
version here.  Once the card's configuration register is set, the uPD7220 must be initialized.  The uPD7220 requires a sequence of commands to be sent to set it up:&lt;br /&gt;
1. A reset command (write 0x00 to address 0x01)&lt;br /&gt;
** This must be followed by parameter bytes, described below&lt;br /&gt;
2. Setting the video mode to master (write 0x6f to address 0x01)&lt;br /&gt;
3. Configuring the pitch&lt;br /&gt;
4. Initializing the PRAM&lt;br /&gt;
** PRAM is some internal memory in the uPD7220, which configures how it accesses memory, and probably some other things&lt;br /&gt;
5. CCHAR command to configure the character characteristics&lt;br /&gt;
6. Disabling zoom (write 0x46 to 0x01, then 0x00 to 0x00)&lt;br /&gt;
7. Starting the display (write 0x6B to 0x01)&lt;br /&gt;
8. Disable blanking (write 0x0D to 0x01)&lt;br /&gt;
&lt;br /&gt;
My driver also seems to issue a FIGS command at the end of startup, but this may not be necessary.&lt;br /&gt;
&lt;br /&gt;
Each command starts with a write to address 0x01, and for each parameter a write to address 0x00 follows.  Each command I describe will proceed this way, with the first byte being the command byte written to&lt;br /&gt;
address 0x01, and all following bytes being parameters written to address 0x00.  At the end I will have some example code in C which provides as both an example and an additional explanation.&lt;br /&gt;
&lt;br /&gt;
The first command is the reset command; here is the format:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ The Reset Command&lt;br /&gt;
|-&lt;br /&gt;
! Address !! Value / Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0x00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Operation mode.  Discussed below.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Horizontal display words minus two.  Must be an even number.  This is equal to ((horizontal_pixels / 4) - 2).  Wide mode does not affect this.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Lower 5 bits are the HSync width minus 1, in words.  Upper 3 bits are the lower 3 bits of the VSync width.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Lower 2 bits are the upper 2 bits of the VSync width.  Upper 6 bits are the horizontal front porch width minus 1.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Horizontal back porch width minus one.  Upper two bits must be zero.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Vertical front porch width minus one.  Upper two bits must be zero.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Number of display lines per frame, lower 8 bits.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Lower 2 bits are the upper 2 bits of the number of display lines per frame.  Upper 6 bits are the vertical back porch length.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note that the vertical field sizes do not have numbers subtracted from them, but the horizontal field sizes have one or two subtracted from them.  If this description is confusing, the uPD7220 manual provided above does contain a nice illustration you may consider looking at.&lt;br /&gt;
&lt;br /&gt;
The operation mode is a bitmask, which controls interlacing, character or graphic mode, as well as DRAM refresh and if drawing is allowed during an active display cycle.  Here are some of the options that&lt;br /&gt;
one can OR together to control the operation mode:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ Operation control bits&lt;br /&gt;
|-&lt;br /&gt;
! Bitmask !! Selected Option&lt;br /&gt;
|-&lt;br /&gt;
| 0x00 || Leave bits 5 and 1 zero to have mixed character and graphics mode.&lt;br /&gt;
|-&lt;br /&gt;
| 0x02 || Enables just graphics mode&lt;br /&gt;
|-&lt;br /&gt;
| 0x20 || Enables just character mode&lt;br /&gt;
|-&lt;br /&gt;
| 0x00 || Leave bits 3 and 0 zero to disable interlacing&lt;br /&gt;
|-&lt;br /&gt;
| 0x08 || Enables interlacing&lt;br /&gt;
|-&lt;br /&gt;
| 0x09 || Enables &amp;quot;Interlaced repeat field for character displays&amp;quot;.  Read the manual for more information.&lt;br /&gt;
|-&lt;br /&gt;
| 0x04 || Enables DRAM refresh cycles - not used on this uPD7220 card, as static RAM is used.&lt;br /&gt;
|-&lt;br /&gt;
| 0x10 || Disables drawing during active display time.  Enabling this is recommended.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Generally 0x12 is a good setting for this card, unless you intend on using character mode.  0x10 will also work as a good mode.&lt;br /&gt;
&lt;br /&gt;
The second command one must send is 0x6f, again written to address 0x01, and it does not take any parameters.  The third command is the PITCH command:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ The Pitch Command&lt;br /&gt;
|-&lt;br /&gt;
! Address !! Value / Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0x47&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Number of memory words per line.  Usually the same as the number of display words per line.  Generally equal to (horizontal_pixels / 4).  The only reason it would differ, is if the &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The fourth command is a PRAM command, which writes data to the uPD7220 parameter RAM.  Here is a recommended usage of the command (although other usages are possible, again read the manual for more info):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ The PRAM Command&lt;br /&gt;
|-&lt;br /&gt;
! Address !! Value / Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0x70 (starts PRAM write at PRAM byte 0)&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Display partition 1 starting address (lower 8 bits) - it is recommended to leave this as zero.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Display partition 1 starting address (bits 8-16) - it is recommended to leave this as zero.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Lower 2 bits are the display partition 1 starting address (upper 2 bits).  Upper 4 bits are the lower 4 bits of the display partition length, in horizontal lines (NOT words).  Bits 2 and 3 should be left as zeros.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Lower 6 bits are the upper 6 bits of the image partition length, in horizontal lines (NOT words).  Bit 6 (0x40) indicates it is a graphics area, otherwise it is a character area if in mixed mode.  Bit 7 (0x80) enables wide mode - ensure to set this bit if you are using wide mode.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The parameter RAM has more variables, but only these really need to be specified.  There is another display partition that one can use, and configuration options for drawing and characters, later in the PRAM.&lt;br /&gt;
&lt;br /&gt;
If your display mode uses wide mode, then be sure to set the MSB of the fourth parameter byte of this command, otherwise the memory access will be incorrect for wide mode.&lt;br /&gt;
&lt;br /&gt;
Next, the CCHAR command is used to define the cursor and character characteristics:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ The CCHAR Command&lt;br /&gt;
|-&lt;br /&gt;
! Address !! Value / Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0x4B&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Setting the MSB will enable the cursor in character mode.  The lower 5 bits are the number of lines per character row minus one.  My driver leaves this byte as zero.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Lower 5 bits define the cursor top line number in the row.  Bit 5 makes the cursor steady if set, or blinking if reset.  Upper two bits are the lower two bits of the blink rate.  My driver leaves this byte as 0xC0.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is sufficient for non-interlaced modes, but interlaced modes require this command to get a third byte.&lt;br /&gt;
&lt;br /&gt;
Zoom must be disabled with a 0x46 write to address 0x01, followed by writing 0x00 to address 0x00.  At this point we can finally enable the display and disable blanking by writing commands 0x6B, then 0x0D, to address 0x01.&lt;br /&gt;
&lt;br /&gt;
My driver executes a FIGS command last, although this probably is not necessary.  Just in case, here is the command sequence:&lt;br /&gt;
1. 0x4C to address 0x01&lt;br /&gt;
2. 0x02 to address 0x00&lt;br /&gt;
3. 0x00 to address 0x00&lt;br /&gt;
4. 0x00 to address 0x00&lt;br /&gt;
&lt;br /&gt;
Here is my code which executes this exact sequence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void setup_upd7220() {&lt;br /&gt;
  // Reset command&lt;br /&gt;
  write(1, 0);&lt;br /&gt;
  write(0, 0x10); // operation mode: draw only during blanking&lt;br /&gt;
  write(0, HORIZ_WORDS - 2);&lt;br /&gt;
  write(0, (VERT_SYNC &amp;lt;&amp;lt; 5) | (HORIZ_SYNC - 1));&lt;br /&gt;
  write(0, ((HORIZ_FRONT_PORCH - 1) &amp;lt;&amp;lt; 2) | (VERT_SYNC &amp;gt;&amp;gt; 3));&lt;br /&gt;
  write(0, (HORIZ_BACK_PORCH - 1) &amp;amp; 63);&lt;br /&gt;
  write(0, VERT_FRONT_PORCH &amp;amp; 63);&lt;br /&gt;
  write(0, VERT_PIXELS &amp;amp; 255);&lt;br /&gt;
  write(0, (VERT_BACK_PORCH &amp;lt;&amp;lt; 2) | (VERT_PIXELS &amp;gt;&amp;gt; 8));&lt;br /&gt;
&lt;br /&gt;
  // set master video mode&lt;br /&gt;
  write(1, 0x6f);&lt;br /&gt;
&lt;br /&gt;
  // set pitch&lt;br /&gt;
  write(1, 0x47);&lt;br /&gt;
  write(0, HORIZ_WORDS);&lt;br /&gt;
&lt;br /&gt;
  // set some PRAM values&lt;br /&gt;
  write(1, 0x70);&lt;br /&gt;
  write(0, 0x00);&lt;br /&gt;
  write(0, 0x00);&lt;br /&gt;
  write(0, (VERT_PIXELS &amp;lt;&amp;lt; 4) &amp;amp; 255);&lt;br /&gt;
  write(0, (VERT_PIXELS &amp;gt;&amp;gt; 4) | (WIDE_MODE ? 8 : 0));&lt;br /&gt;
&lt;br /&gt;
  // Set character and cursor characteristics&lt;br /&gt;
  write(1, 0x4B);&lt;br /&gt;
  write(0, 0x00);&lt;br /&gt;
  write(0, 0xC0);&lt;br /&gt;
&lt;br /&gt;
  // set zoom&lt;br /&gt;
  write(1, 0x46);&lt;br /&gt;
  write(0, 0x00);&lt;br /&gt;
&lt;br /&gt;
  // Enable the display&lt;br /&gt;
  write(1, 0x6b);&lt;br /&gt;
&lt;br /&gt;
  // Disable blanking&lt;br /&gt;
  write(1, 0x0D);&lt;br /&gt;
&lt;br /&gt;
  // This is probably not necessary&lt;br /&gt;
  write(1, 0x4c);&lt;br /&gt;
  write(0, 0x02);&lt;br /&gt;
  write(0, 0x00);&lt;br /&gt;
  write(0, 0x00);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=UPD7220_Video_Card_(Revision_2)&amp;diff=50</id>
		<title>UPD7220 Video Card (Revision 2)</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=UPD7220_Video_Card_(Revision_2)&amp;diff=50"/>
		<updated>2024-06-14T04:37:58Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Checkpoint: finished the initialization section at last!&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
The uPD7220 Video Card revision 2 is a video card with 128KB of VRAM using the uPD7220 GDC or its variants.  It operates at a maximum clock speed of 32Mhz.  The card is based on the original uPD7220 card I (Dylan Brophy) made, which had half the VRAM and generally was far simpler.  This card solves several issues with the older card, at the cost of greater size and complexity.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
* 16-color VGA output&lt;br /&gt;
* 128KB of RAM&lt;br /&gt;
** 4 RAM chips, 32KB each, 25ns access and write time&lt;br /&gt;
** 16 bit bus (8 bits per chip, two chips per byte)&lt;br /&gt;
** 4 color bits per pixel, 4 pixels per word in RAM&lt;br /&gt;
*** Maximum 262144 pixels (hardware double buffering disabled)&lt;br /&gt;
**** Suggested resolutions: 544x480, 640x408&lt;br /&gt;
*** Maximum 131072 pixels (hardware double buffering enabled)&lt;br /&gt;
**** Suggested resolutions: 448x288, 400x300&lt;br /&gt;
* uPD7220 at up to 24Mhz, Z7220 at up to 32Mhz&lt;br /&gt;
* uPD7220 wide mode supported&lt;br /&gt;
** Allows higher pixel clock frequencies, for higher resolutions and lower visible display times&lt;br /&gt;
** Accesses two VRAM words per uPD7220 clock cycle&lt;br /&gt;
* Direct access to VRAM is provided to the host CPU&lt;br /&gt;
&lt;br /&gt;
== Video Modes ==&lt;br /&gt;
&lt;br /&gt;
It is assumed that the reader is familiar with VGA display timings.  If not, then it is recommended to study it, or to use the video timing calculator and to leave the card in wide mode.&lt;br /&gt;
&lt;br /&gt;
The uPD7220 was invented before the VGA standard, and due to this, was not designed to output a VGA signal.  Since VGA was designed to drive CRT monitors, as was the uPD7220, it turns out that&lt;br /&gt;
they are fairly compatible.  VGA timings work perfectly fine, but to compute them one must keep in mind the pixel and uPD7220 clocks.  The uPD7220 clock is always one fourth of the card's clock&lt;br /&gt;
speed.&lt;br /&gt;
&lt;br /&gt;
For common VGA signal timings, I would recommend looking at this very useful site: [http://tinyvga.com/vga-timing TinyVGA signal timings]&lt;br /&gt;
&lt;br /&gt;
One must take the timings and convert them to the correct resolution.  For an example, let's convert the 640x480 VGA standard timings to a 400x400 resolution.  Here are the video timings from [http://tinyvga.com/vga-timing/640x480@60Hz TinyVGA's 640x480 60hz page]:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Horizontal Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Pixels !! Microseconds&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 640 || 25.4220&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 16 || 0.6356&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 96 || 3.8133&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 48 || 1.9067&lt;br /&gt;
|-&lt;br /&gt;
| Whole line || 800 || 31.778&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vertical Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Lines&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 480&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 10&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 2&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 33&lt;br /&gt;
|-&lt;br /&gt;
| Whole frame || 525&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
First, we need to decide on a clock frequency for our card.  24Mhz is slower than the 25.175Mhz recommended, and this will make our pixels wider by about 5%.  However, a 24Mhz clock will run our uPD7220 at 6Mhz,&lt;br /&gt;
right at the maximum frequency, and 25.175Mhz would overclock it.  We will select 24Mhz, as the 5% elongation of pixels is too small to care about, and the timings should be compatible with the uPD7220 IC.  We&lt;br /&gt;
will enable wide mode, so our pixel clock is set at 24Mhz (the same as the card - without wide mode it would be 12Mhz, or half).  This keeps the pixel width close to the intended width.  Importantly, this change in clock speed will change the necessary timings for the video signal.  Since each uPD7220 clock cycle is 166.7ns at 6Mhz, and each word is two cycles, we will be dividing the horizontal porch and sync widths&lt;br /&gt;
by 333.3ns to get the number of words for each of the regions.  We cannot compute this yet though, because the visible region still needs to be adjusted for our new resolution.&lt;br /&gt;
&lt;br /&gt;
For our application, we need less pixels, but still need to meet the timing spec.  To achieve this, we can increase the back porch and reduce the visible region until we have the resolution we want.  We do this&lt;br /&gt;
on both the x and y axes.  This works because, during the back porch, the video signal is sent a black signal.  On older monitors, this corresponds to a dark box around the image, and newer monitors will treat it&lt;br /&gt;
as a blank region.  Fundamentally, the monitor will still generally treat it as a 640x480, standard VGA signal, because we haven't encroached on the porches, and we haven't changed/moved the sync signals.  To&lt;br /&gt;
compute the width of the new visible area, we multiply the number of horizontal pixels by the pixel clock period: 41.67ns * 400 pixels = 16.67us.  From this, we can calculate the new horizontal front porch, and&lt;br /&gt;
then we can recompute the lines per vertical video region:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Updated Horizontal Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Microseconds&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 16.67&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 0.6356&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 3.8133&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 10.6620&lt;br /&gt;
|-&lt;br /&gt;
| Whole line || 31.7776&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The back porch becomes much wider.  All we did was move the time over from the visible area to the back porch, such that the visible area has the correct number of pixels at our clock frequency.  The vertical&lt;br /&gt;
regions are adjusted similarly, but the math is much simpler, because we are only trying to keep constant the number of lines:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vertical Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Lines&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 400&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 60&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 2&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 63&lt;br /&gt;
|-&lt;br /&gt;
| Whole frame || 525&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
You will notice that I added lines to both the front and back vertical porches.  This is because the uPD7220 cannot output more than 63 of each porch, so I needed to add to both to obey this limitation.  For&lt;br /&gt;
lower resolutions, it is usually acceptable to reduce the total number of lines to obey these limitations.  It is important, however, that in such a case the horizontal lines are made longer to keep the refresh rate at 60hz, and that the video settings are tested to work on the monitor.&lt;br /&gt;
&lt;br /&gt;
At this point we have the description of the mode we want, now we can convert it into the numbers used by the card.  The vertical timings are already in the correct format, but the horizontal timing still must&lt;br /&gt;
be converted.  To do this, we need to get the number of memory cycles (not uPD7220 clock cycles) for each horizontal region, which is the time for two uPD7220 clock cycles.  The exception is the visible region,&lt;br /&gt;
which is computed from the amount of memory used to represent the data (4 pixels per word, one word accessed per uPD7220 cycle in wide mode).  For the visible region, we divide the number of pixels by 4, and&lt;br /&gt;
for the other regions, we divide the time by 333.3ns and round to the nearest whole number.  Here is the result:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ uPD7220 timing settings&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Words&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 100&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 1.907 =&amp;gt; 2&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 11.4399 =&amp;gt; 11&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 31.986 =&amp;gt; 32&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Any VGA monitor should accept these signal timings for a 400x400 resolution.  For video memory consumtion, this needs 400 * 400 / 4 = 40000 words of memory.  The card has 65536 words total, but only&lt;br /&gt;
32768 if automatic double buffering is enabled, so we cannot enable automatic double buffering for this resolution.  So, we must enable wide mode and disable double buffering for this video mode.&lt;br /&gt;
&lt;br /&gt;
'''Card Specific Details: Wide Mode'''&lt;br /&gt;
&lt;br /&gt;
This card can reach quite high clock speeds for the uPD7220, and to maintain this high speed for such slow chips, wide mode is used.  This allows twice as many pixels to be output per uPD7220 clock cycle.  This can be used either to increase the maximum resolution, or to reduce the display time.  Reducing the display time gives the uPD7220 and host processor more time to draw to the screen.  On the other hand, due to&lt;br /&gt;
the high speeds used in wide mode, there is also typically more data that must be written to the display per frame, thus putting more pressure on the host processor.  The balance in display characteristics depends on the application.&lt;br /&gt;
&lt;br /&gt;
Due to wide mode support, the card's clock is 4x the uPD7220's clock.  When doing calculations for video, this must be kept in mind.  If wide mode is disabled, then the pixel clock is only half the card's clock.  If the video timing math does not account for this, then the video signal timing will be incorrect.&lt;br /&gt;
&lt;br /&gt;
== Communication with the Host Processor ==&lt;br /&gt;
&lt;br /&gt;
The hardware connection uses a fairly simple 8-bit parallel bus for data exchange.  Once connected, the software can read from and write to several registers and addresses on the card to control it.&lt;br /&gt;
&lt;br /&gt;
=== Hardware Protocol ===&lt;br /&gt;
&lt;br /&gt;
Here is a pinout of the card:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Card Pinout&lt;br /&gt;
|-&lt;br /&gt;
! Pin !! Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || A0 || Address pin 0 (LSB)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || A1 || Address pin 1&lt;br /&gt;
|-&lt;br /&gt;
| 3 || A2 || Address pin 2 (MSB)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || NC || No connection: this is the key pin and should be blocked&lt;br /&gt;
|-&lt;br /&gt;
| 5 || GND || Ground connection&lt;br /&gt;
|-&lt;br /&gt;
| 6 || D0 || Data pin 0 (LSB)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || D1 || Data pin 1&lt;br /&gt;
|-&lt;br /&gt;
| 8 || D2 || Data pin 2&lt;br /&gt;
|-&lt;br /&gt;
| 9 || D3 || Data pin 3&lt;br /&gt;
|-&lt;br /&gt;
| 10 || D4 || Data pin 4&lt;br /&gt;
|-&lt;br /&gt;
| 11 || D5 || Data pin 5&lt;br /&gt;
|-&lt;br /&gt;
| 12 || D6 || Data pin 6&lt;br /&gt;
|-&lt;br /&gt;
| 13 || D7 || Data pin 7 (MSB)&lt;br /&gt;
|-&lt;br /&gt;
| 14 || SEL || Select pin (active low input) - selects the card for communication&lt;br /&gt;
|-&lt;br /&gt;
| 15 || 5v || +5V rail to power the card&lt;br /&gt;
|-&lt;br /&gt;
| 16 || RD || Read enable pin (active low input)&lt;br /&gt;
|-&lt;br /&gt;
| 17 || WR || Write enable pin (active low input)&lt;br /&gt;
|-&lt;br /&gt;
| 18 || WAIT || Wait indication (active low output)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When trying to access VRAM, if the VRAM is being used by the card, then WAIT will be pulled low to tell the CPU not to end the current operation until after the WAIT pin goes high again.  After WAIT goes high, wait at least 31.25ns for the read/write operation to complete before releasing any control lines or changing any data/address lines.  Never read data while WAIT is low, and wait 31.25ns after WAIT goes high before reading the data to the CPU.  WAIT only becomes low when trying to read from or write to VRAM; otherwise WAIT is always high.&lt;br /&gt;
&lt;br /&gt;
Each access cycle should last at least 31.25ns, and SEL should be de-asserted between cycles.  Any conventional processor like Z80, 8086, 6502, etc, should have no issue with WAIT or access cycles below an operating frequency of 32Mhz, regardless of the speed of the card.  There may be exceptions to this, but there are no known exceptions.&lt;br /&gt;
&lt;br /&gt;
=== Software Protocol ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Card Registers&lt;br /&gt;
|-&lt;br /&gt;
! Register !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| 0 || uPD7220 command register (uPD7220 A0 = 0)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || uPD7220 parameter register (uPD7220 A0 = 1)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Card configuration register (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Direct VRAM access (accessing this may cause WAIT to go low)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || VRAM Address bits 0-7 (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Reserved - for now same as register 4&lt;br /&gt;
|-&lt;br /&gt;
| 6 || VRAM Address bits 8-15 (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || Reserved - for now same as register 6&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For this card, a driver should first configure register 2 for the selected video mode and card interface configuration.  This register has various bits to tell the card what to do:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ Configuration Register Bits&lt;br /&gt;
|-&lt;br /&gt;
! Bit !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Enables automatic counting of the VRAM address registers if set.  If reset, then the VRAM address registers do not change when VRAM is written.  More on this later.&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Setting this bit enables the card's wide mode circutry.  When reset, one pixel will be output every other clock cycle.  This should match the setting on the uPD7220 IC.&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Enables automatic double buffering.  When set, draws to VRAM from the host CPU or the uPD7220 will happen on the opposite buffer as the one displaying.  Setting this halfs the available VRAM for displaying.&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 6 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 7 || Controls which buffer is displaying.  Changing this bit will swap the currently displaying buffer.  Only works in automatic double buffer mode.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It is recommended to keep track of what buffer is currently displaying, and perhaps this entire configuration register, so that the selected buffer can be switched.  This register is important for controlling how the CPU accesses VRAM, how smooth the display is, and the way the image data is displayed.  Generally, automatic double buffering is recommended if the resolution demands less than half of the available VRAM, as&lt;br /&gt;
double buffering makes the display smoother by removing visual artifacts due to drawing, but doubles VRAM demand. &lt;br /&gt;
&lt;br /&gt;
You can read the [http://www.vintagecomputer.net/fjkraan/comp/qx10/doc/nec7220.pdf programming manual] to get a detailed understanding of how to program the uPD7220, but I will also provide an abridged&lt;br /&gt;
version here.  Once the card's configuration register is set, the uPD7220 must be initialized.  The uPD7220 requires a sequence of commands to be sent to set it up:&lt;br /&gt;
1. A reset command (write 0x00 to address 0x01)&lt;br /&gt;
** This must be followed by parameter bytes, described below&lt;br /&gt;
2. Setting the video mode to master (write 0x6f to address 0x01)&lt;br /&gt;
3. Configuring the pitch&lt;br /&gt;
4. Initializing the PRAM&lt;br /&gt;
** PRAM is some internal memory in the uPD7220, which configures how it accesses memory, and probably some other things&lt;br /&gt;
5. CCHAR command to configure the character characteristics&lt;br /&gt;
6. Disabling zoom (write 0x46 to 0x01, then 0x00 to 0x00)&lt;br /&gt;
7. Starting the display (write 0x6B to 0x01)&lt;br /&gt;
8. Disable blanking (write 0x0D to 0x01)&lt;br /&gt;
&lt;br /&gt;
My driver also seems to issue a FIGS command at the end of startup, but this may not be necessary.&lt;br /&gt;
&lt;br /&gt;
Each command starts with a write to address 0x01, and for each parameter a write to address 0x00 follows.  Each command I describe will proceed this way, with the first byte being the command byte written to&lt;br /&gt;
address 0x01, and all following bytes being parameters written to address 0x00.  At the end I will have some example code in C which provides as both an example and an additional explanation.&lt;br /&gt;
&lt;br /&gt;
The first command is the reset command; here is the format:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ The Reset Command&lt;br /&gt;
|-&lt;br /&gt;
! Address !! Value / Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0x00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Operation mode.  Discussed below.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Horizontal display words minus two.  Must be an even number.  This is equal to ((horizontal_pixels / 4) - 2).  Wide mode does not affect this.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Lower 5 bits are the HSync width minus 1, in words.  Upper 3 bits are the lower 3 bits of the VSync width.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Lower 2 bits are the upper 2 bits of the VSync width.  Upper 6 bits are the horizontal front porch width minus 1.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Horizontal back porch width minus one.  Upper two bits must be zero.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Vertical front porch width minus one.  Upper two bits must be zero.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Number of display lines per frame, lower 8 bits.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Lower 2 bits are the upper 2 bits of the number of display lines per frame.  Upper 6 bits are the vertical back porch length.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note that the vertical field sizes do not have numbers subtracted from them, but the horizontal field sizes have one or two subtracted from them.  If this description is confusing, the uPD7220 manual provided above does contain a nice illustration you may consider looking at.&lt;br /&gt;
&lt;br /&gt;
The operation mode is a bitmask, which controls interlacing, character or graphic mode, as well as DRAM refresh and if drawing is allowed during an active display cycle.  Here are some of the options that&lt;br /&gt;
one can OR together to control the operation mode:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ Operation control bits&lt;br /&gt;
|-&lt;br /&gt;
! Bitmask !! Selected Option&lt;br /&gt;
|-&lt;br /&gt;
| 0x00 || Leave bits 5 and 1 zero to have mixed character and graphics mode.&lt;br /&gt;
|-&lt;br /&gt;
| 0x02 || Enables just graphics mode&lt;br /&gt;
|-&lt;br /&gt;
| 0x20 || Enables just character mode&lt;br /&gt;
|-&lt;br /&gt;
| 0x00 || Leave bits 3 and 0 zero to disable interlacing&lt;br /&gt;
|-&lt;br /&gt;
| 0x08 || Enables interlacing&lt;br /&gt;
|-&lt;br /&gt;
| 0x09 || Enables &amp;quot;Interlaced repeat field for character displays&amp;quot;.  Read the manual for more information.&lt;br /&gt;
|-&lt;br /&gt;
| 0x04 || Enables DRAM refresh cycles - not used on this uPD7220 card, as static RAM is used.&lt;br /&gt;
|-&lt;br /&gt;
| 0x10 || Disables drawing during active display time.  Enabling this is recommended.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Generally 0x12 is a good setting for this card, unless you intend on using character mode.  0x10 will also work as a good mode.&lt;br /&gt;
&lt;br /&gt;
The second command one must send is 0x6f, again written to address 0x01, and it does not take any parameters.  The third command is the PITCH command:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ The Pitch Command&lt;br /&gt;
|-&lt;br /&gt;
! Address !! Value / Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0x47&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Number of memory words per line.  Usually the same as the number of display words per line.  Generally equal to (horizontal_pixels / 4).  The only reason it would differ, is if the &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The fourth command is a PRAM command, which writes data to the uPD7220 parameter RAM.  Here is a recommended usage of the command (although other usages are possible, again read the manual for more info):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ The PRAM Command&lt;br /&gt;
|-&lt;br /&gt;
! Address !! Value / Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0x70 (starts PRAM write at PRAM byte 0)&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Display partition 1 starting address (lower 8 bits) - it is recommended to leave this as zero.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Display partition 1 starting address (bits 8-16) - it is recommended to leave this as zero.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Lower 2 bits are the display partition 1 starting address (upper 2 bits).  Upper 4 bits are the lower 4 bits of the display partition length, in horizontal lines (NOT words).  Bits 2 and 3 should be left as zeros.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Lower 6 bits are the upper 6 bits of the image partition length, in horizontal lines (NOT words).  Bit 6 (0x40) indicates it is a graphics area, otherwise it is a character area if in mixed mode.  Bit 7 (0x80) enables wide mode - ensure to set this bit if you are using wide mode.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The parameter RAM has more variables, but only these really need to be specified.  There is another display partition that one can use, and configuration options for drawing and characters, later in the PRAM.&lt;br /&gt;
&lt;br /&gt;
If your display mode uses wide mode, then be sure to set the MSB of the fourth parameter byte of this command, otherwise the memory access will be incorrect for wide mode.&lt;br /&gt;
&lt;br /&gt;
Next, the CCHAR command is used to define the cursor and character characteristics:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ The CCHAR Command&lt;br /&gt;
|-&lt;br /&gt;
! Address !! Value / Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0x4B&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Setting the MSB will enable the cursor in character mode.  The lower 5 bits are the number of lines per character row minus one.  My driver leaves this byte as zero.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Lower 5 bits define the cursor top line number in the row.  Bit 5 makes the cursor steady if set, or blinking if reset.  Upper two bits are the lower two bits of the blink rate.  My driver leaves this byte as 0xC0.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is sufficient for non-interlaced modes, but interlaced modes require this command to get a third byte.&lt;br /&gt;
&lt;br /&gt;
Zoom must be disabled with a 0x46 write to address 0x01, followed by writing 0x00 to address 0x00.  At this point we can finally enable the display and disable blanking by writing commands 0x6B, then 0x0D, to address 0x01.&lt;br /&gt;
&lt;br /&gt;
My driver executes a FIGS command last, although this probably is not necessary.  Just in case, here is the command sequence:&lt;br /&gt;
1. 0x4C to address 0x01&lt;br /&gt;
2. 0x02 to address 0x00&lt;br /&gt;
3. 0x00 to address 0x00&lt;br /&gt;
4. 0x00 to address 0x00&lt;br /&gt;
&lt;br /&gt;
Here is my code which executes this exact sequence:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void setup_upd7220() {&lt;br /&gt;
  // Reset command&lt;br /&gt;
  write(1, 0);&lt;br /&gt;
  write(0, 0x10); // operation mode: draw only during blanking&lt;br /&gt;
  write(0, HORIZ_WORDS - 2);&lt;br /&gt;
  write(0, (VERT_SYNC &amp;lt;&amp;lt; 5) | (HORIZ_SYNC - 1));&lt;br /&gt;
  write(0, ((HORIZ_FRONT_PORCH - 1) &amp;lt;&amp;lt; 2) | (VERT_SYNC &amp;gt;&amp;gt; 3));&lt;br /&gt;
  write(0, (HORIZ_BACK_PORCH - 1) &amp;amp; 63);&lt;br /&gt;
  write(0, VERT_FRONT_PORCH &amp;amp; 63);&lt;br /&gt;
  write(0, VERT_PIXELS &amp;amp; 255);&lt;br /&gt;
  write(0, (VERT_BACK_PORCH &amp;lt;&amp;lt; 2) | (VERT_PIXELS &amp;gt;&amp;gt; 8));&lt;br /&gt;
&lt;br /&gt;
  // set master video mode&lt;br /&gt;
  write(1, 0x6f);&lt;br /&gt;
&lt;br /&gt;
  // set pitch&lt;br /&gt;
  write(1, 0x47);&lt;br /&gt;
  write(0, HORIZ_WORDS);&lt;br /&gt;
&lt;br /&gt;
  // set some PRAM values&lt;br /&gt;
  write(1, 0x70);&lt;br /&gt;
  write(0, 0x00);&lt;br /&gt;
  write(0, 0x00);&lt;br /&gt;
  write(0, (VERT_PIXELS &amp;lt;&amp;lt; 4) &amp;amp; 255);&lt;br /&gt;
  write(0, (VERT_PIXELS &amp;gt;&amp;gt; 4) | (WIDE_MODE ? 8 : 0));//*/&lt;br /&gt;
&lt;br /&gt;
  // Set character and cursor characteristics&lt;br /&gt;
  write(1, 0x4B);&lt;br /&gt;
  write(0, 0x00);&lt;br /&gt;
  write(0, 0xC0);&lt;br /&gt;
&lt;br /&gt;
  // set zoom&lt;br /&gt;
  write(1, 0x46);&lt;br /&gt;
  write(0, 0x00);&lt;br /&gt;
&lt;br /&gt;
  // Enable the display&lt;br /&gt;
  write(1, 0x6b);&lt;br /&gt;
&lt;br /&gt;
  // Disable blanking&lt;br /&gt;
  write(1, 0x0D);&lt;br /&gt;
&lt;br /&gt;
  // This is probably not necessary&lt;br /&gt;
  write(1, 0x4c);&lt;br /&gt;
  write(0, 0x02);&lt;br /&gt;
  write(0, 0x00);&lt;br /&gt;
  write(0, 0x00);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=UPD7220_Video_Card_(Revision_2)&amp;diff=49</id>
		<title>UPD7220 Video Card (Revision 2)</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=UPD7220_Video_Card_(Revision_2)&amp;diff=49"/>
		<updated>2024-06-14T04:17:08Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Software protocol checkpoint&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
The uPD7220 Video Card revision 2 is a video card with 128KB of VRAM using the uPD7220 GDC or its variants.  It operates at a maximum clock speed of 32Mhz.  The card is based on the original uPD7220 card I (Dylan Brophy) made, which had half the VRAM and generally was far simpler.  This card solves several issues with the older card, at the cost of greater size and complexity.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
* 16-color VGA output&lt;br /&gt;
* 128KB of RAM&lt;br /&gt;
** 4 RAM chips, 32KB each, 25ns access and write time&lt;br /&gt;
** 16 bit bus (8 bits per chip, two chips per byte)&lt;br /&gt;
** 4 color bits per pixel, 4 pixels per word in RAM&lt;br /&gt;
*** Maximum 262144 pixels (hardware double buffering disabled)&lt;br /&gt;
**** Suggested resolutions: 544x480, 640x408&lt;br /&gt;
*** Maximum 131072 pixels (hardware double buffering enabled)&lt;br /&gt;
**** Suggested resolutions: 448x288, 400x300&lt;br /&gt;
* uPD7220 at up to 24Mhz, Z7220 at up to 32Mhz&lt;br /&gt;
* uPD7220 wide mode supported&lt;br /&gt;
** Allows higher pixel clock frequencies, for higher resolutions and lower visible display times&lt;br /&gt;
** Accesses two VRAM words per uPD7220 clock cycle&lt;br /&gt;
* Direct access to VRAM is provided to the host CPU&lt;br /&gt;
&lt;br /&gt;
== Video Modes ==&lt;br /&gt;
&lt;br /&gt;
It is assumed that the reader is familiar with VGA display timings.  If not, then it is recommended to study it, or to use the video timing calculator and to leave the card in wide mode.&lt;br /&gt;
&lt;br /&gt;
The uPD7220 was invented before the VGA standard, and due to this, was not designed to output a VGA signal.  Since VGA was designed to drive CRT monitors, as was the uPD7220, it turns out that&lt;br /&gt;
they are fairly compatible.  VGA timings work perfectly fine, but to compute them one must keep in mind the pixel and uPD7220 clocks.  The uPD7220 clock is always one fourth of the card's clock&lt;br /&gt;
speed.&lt;br /&gt;
&lt;br /&gt;
For common VGA signal timings, I would recommend looking at this very useful site: [http://tinyvga.com/vga-timing TinyVGA signal timings]&lt;br /&gt;
&lt;br /&gt;
One must take the timings and convert them to the correct resolution.  For an example, let's convert the 640x480 VGA standard timings to a 400x400 resolution.  Here are the video timings from [http://tinyvga.com/vga-timing/640x480@60Hz TinyVGA's 640x480 60hz page]:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Horizontal Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Pixels !! Microseconds&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 640 || 25.4220&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 16 || 0.6356&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 96 || 3.8133&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 48 || 1.9067&lt;br /&gt;
|-&lt;br /&gt;
| Whole line || 800 || 31.778&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vertical Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Lines&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 480&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 10&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 2&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 33&lt;br /&gt;
|-&lt;br /&gt;
| Whole frame || 525&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
First, we need to decide on a clock frequency for our card.  24Mhz is slower than the 25.175Mhz recommended, and this will make our pixels wider by about 5%.  However, a 24Mhz clock will run our uPD7220 at 6Mhz,&lt;br /&gt;
right at the maximum frequency, and 25.175Mhz would overclock it.  We will select 24Mhz, as the 5% elongation of pixels is too small to care about, and the timings should be compatible with the uPD7220 IC.  We&lt;br /&gt;
will enable wide mode, so our pixel clock is set at 24Mhz (the same as the card - without wide mode it would be 12Mhz, or half).  This keeps the pixel width close to the intended width.  Importantly, this change in clock speed will change the necessary timings for the video signal.  Since each uPD7220 clock cycle is 166.7ns at 6Mhz, and each word is two cycles, we will be dividing the horizontal porch and sync widths&lt;br /&gt;
by 333.3ns to get the number of words for each of the regions.  We cannot compute this yet though, because the visible region still needs to be adjusted for our new resolution.&lt;br /&gt;
&lt;br /&gt;
For our application, we need less pixels, but still need to meet the timing spec.  To achieve this, we can increase the back porch and reduce the visible region until we have the resolution we want.  We do this&lt;br /&gt;
on both the x and y axes.  This works because, during the back porch, the video signal is sent a black signal.  On older monitors, this corresponds to a dark box around the image, and newer monitors will treat it&lt;br /&gt;
as a blank region.  Fundamentally, the monitor will still generally treat it as a 640x480, standard VGA signal, because we haven't encroached on the porches, and we haven't changed/moved the sync signals.  To&lt;br /&gt;
compute the width of the new visible area, we multiply the number of horizontal pixels by the pixel clock period: 41.67ns * 400 pixels = 16.67us.  From this, we can calculate the new horizontal front porch, and&lt;br /&gt;
then we can recompute the lines per vertical video region:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Updated Horizontal Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Microseconds&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 16.67&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 0.6356&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 3.8133&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 10.6620&lt;br /&gt;
|-&lt;br /&gt;
| Whole line || 31.7776&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The back porch becomes much wider.  All we did was move the time over from the visible area to the back porch, such that the visible area has the correct number of pixels at our clock frequency.  The vertical&lt;br /&gt;
regions are adjusted similarly, but the math is much simpler, because we are only trying to keep constant the number of lines:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vertical Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Lines&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 400&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 60&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 2&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 63&lt;br /&gt;
|-&lt;br /&gt;
| Whole frame || 525&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
You will notice that I added lines to both the front and back vertical porches.  This is because the uPD7220 cannot output more than 63 of each porch, so I needed to add to both to obey this limitation.  For&lt;br /&gt;
lower resolutions, it is usually acceptable to reduce the total number of lines to obey these limitations.  It is important, however, that in such a case the horizontal lines are made longer to keep the refresh rate at 60hz, and that the video settings are tested to work on the monitor.&lt;br /&gt;
&lt;br /&gt;
At this point we have the description of the mode we want, now we can convert it into the numbers used by the card.  The vertical timings are already in the correct format, but the horizontal timing still must&lt;br /&gt;
be converted.  To do this, we need to get the number of memory cycles (not uPD7220 clock cycles) for each horizontal region, which is the time for two uPD7220 clock cycles.  The exception is the visible region,&lt;br /&gt;
which is computed from the amount of memory used to represent the data (4 pixels per word, one word accessed per uPD7220 cycle in wide mode).  For the visible region, we divide the number of pixels by 4, and&lt;br /&gt;
for the other regions, we divide the time by 333.3ns and round to the nearest whole number.  Here is the result:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ uPD7220 timing settings&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Words&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 100&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 1.907 =&amp;gt; 2&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 11.4399 =&amp;gt; 11&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 31.986 =&amp;gt; 32&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Any VGA monitor should accept these signal timings for a 400x400 resolution.  For video memory consumtion, this needs 400 * 400 / 4 = 40000 words of memory.  The card has 65536 words total, but only&lt;br /&gt;
32768 if automatic double buffering is enabled, so we cannot enable automatic double buffering for this resolution.  So, we must enable wide mode and disable double buffering for this video mode.&lt;br /&gt;
&lt;br /&gt;
'''Card Specific Details: Wide Mode'''&lt;br /&gt;
&lt;br /&gt;
This card can reach quite high clock speeds for the uPD7220, and to maintain this high speed for such slow chips, wide mode is used.  This allows twice as many pixels to be output per uPD7220 clock cycle.  This can be used either to increase the maximum resolution, or to reduce the display time.  Reducing the display time gives the uPD7220 and host processor more time to draw to the screen.  On the other hand, due to&lt;br /&gt;
the high speeds used in wide mode, there is also typically more data that must be written to the display per frame, thus putting more pressure on the host processor.  The balance in display characteristics depends on the application.&lt;br /&gt;
&lt;br /&gt;
Due to wide mode support, the card's clock is 4x the uPD7220's clock.  When doing calculations for video, this must be kept in mind.  If wide mode is disabled, then the pixel clock is only half the card's clock.  If the video timing math does not account for this, then the video signal timing will be incorrect.&lt;br /&gt;
&lt;br /&gt;
== Communication with the Host Processor ==&lt;br /&gt;
&lt;br /&gt;
The hardware connection uses a fairly simple 8-bit parallel bus for data exchange.  Once connected, the software can read from and write to several registers and addresses on the card to control it.&lt;br /&gt;
&lt;br /&gt;
=== Hardware Protocol ===&lt;br /&gt;
&lt;br /&gt;
Here is a pinout of the card:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Card Pinout&lt;br /&gt;
|-&lt;br /&gt;
! Pin !! Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || A0 || Address pin 0 (LSB)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || A1 || Address pin 1&lt;br /&gt;
|-&lt;br /&gt;
| 3 || A2 || Address pin 2 (MSB)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || NC || No connection: this is the key pin and should be blocked&lt;br /&gt;
|-&lt;br /&gt;
| 5 || GND || Ground connection&lt;br /&gt;
|-&lt;br /&gt;
| 6 || D0 || Data pin 0 (LSB)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || D1 || Data pin 1&lt;br /&gt;
|-&lt;br /&gt;
| 8 || D2 || Data pin 2&lt;br /&gt;
|-&lt;br /&gt;
| 9 || D3 || Data pin 3&lt;br /&gt;
|-&lt;br /&gt;
| 10 || D4 || Data pin 4&lt;br /&gt;
|-&lt;br /&gt;
| 11 || D5 || Data pin 5&lt;br /&gt;
|-&lt;br /&gt;
| 12 || D6 || Data pin 6&lt;br /&gt;
|-&lt;br /&gt;
| 13 || D7 || Data pin 7 (MSB)&lt;br /&gt;
|-&lt;br /&gt;
| 14 || SEL || Select pin (active low input) - selects the card for communication&lt;br /&gt;
|-&lt;br /&gt;
| 15 || 5v || +5V rail to power the card&lt;br /&gt;
|-&lt;br /&gt;
| 16 || RD || Read enable pin (active low input)&lt;br /&gt;
|-&lt;br /&gt;
| 17 || WR || Write enable pin (active low input)&lt;br /&gt;
|-&lt;br /&gt;
| 18 || WAIT || Wait indication (active low output)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When trying to access VRAM, if the VRAM is being used by the card, then WAIT will be pulled low to tell the CPU not to end the current operation until after the WAIT pin goes high again.  After WAIT goes high, wait at least 31.25ns for the read/write operation to complete before releasing any control lines or changing any data/address lines.  Never read data while WAIT is low, and wait 31.25ns after WAIT goes high before reading the data to the CPU.  WAIT only becomes low when trying to read from or write to VRAM; otherwise WAIT is always high.&lt;br /&gt;
&lt;br /&gt;
Each access cycle should last at least 31.25ns, and SEL should be de-asserted between cycles.  Any conventional processor like Z80, 8086, 6502, etc, should have no issue with WAIT or access cycles below an operating frequency of 32Mhz, regardless of the speed of the card.  There may be exceptions to this, but there are no known exceptions.&lt;br /&gt;
&lt;br /&gt;
=== Software Protocol ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Card Registers&lt;br /&gt;
|-&lt;br /&gt;
! Register !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| 0 || uPD7220 command register (uPD7220 A0 = 0)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || uPD7220 parameter register (uPD7220 A0 = 1)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Card configuration register (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Direct VRAM access (accessing this may cause WAIT to go low)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || VRAM Address bits 0-7 (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Reserved - for now same as register 4&lt;br /&gt;
|-&lt;br /&gt;
| 6 || VRAM Address bits 8-15 (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || Reserved - for now same as register 6&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For this card, a driver should first configure register 2 for the selected video mode and card interface configuration.  This register has various bits to tell the card what to do:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ Configuration Register Bits&lt;br /&gt;
|-&lt;br /&gt;
! Bit !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Enables automatic counting of the VRAM address registers if set.  If reset, then the VRAM address registers do not change when VRAM is written.  More on this later.&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Setting this bit enables the card's wide mode circutry.  When reset, one pixel will be output every other clock cycle.  This should match the setting on the uPD7220 IC.&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Enables automatic double buffering.  When set, draws to VRAM from the host CPU or the uPD7220 will happen on the opposite buffer as the one displaying.  Setting this halfs the available VRAM for displaying.&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 6 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 7 || Controls which buffer is displaying.  Changing this bit will swap the currently displaying buffer.  Only works in automatic double buffer mode.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It is recommended to keep track of what buffer is currently displaying, and perhaps this entire configuration register, so that the selected buffer can be switched.  This register is important for controlling how the CPU accesses VRAM, how smooth the display is, and the way the image data is displayed.  Generally, automatic double buffering is recommended if the resolution demands less than half of the available VRAM, as&lt;br /&gt;
double buffering makes the display smoother by removing visual artifacts due to drawing, but doubles VRAM demand. &lt;br /&gt;
&lt;br /&gt;
You can read the [http://www.vintagecomputer.net/fjkraan/comp/qx10/doc/nec7220.pdf programming manual] to get a detailed understanding of how to program the uPD7220, but I will also provide an abridged&lt;br /&gt;
version here.  Once the card's configuration register is set, the uPD7220 must be initialized.  The uPD7220 requires a sequence of commands to be sent to set it up:&lt;br /&gt;
1. A reset command (write 0x00 to address 0x01)&lt;br /&gt;
** This must be followed by parameter bytes, described below&lt;br /&gt;
2. Setting the video mode to master (write 0x6f to address 0x01)&lt;br /&gt;
3. Configuring the pitch&lt;br /&gt;
4. Initializing the PRAM&lt;br /&gt;
** PRAM is some internal memory in the uPD7220, which configures how it accesses memory, and probably some other things&lt;br /&gt;
5. CCHAR command to configure the character characteristics&lt;br /&gt;
6. Disabling zoom (write 0x46 to 0x01, then 0x00 to 0x00)&lt;br /&gt;
7. Starting the display (write 0x6B to 0x01)&lt;br /&gt;
8. Disable blanking (write 0x0D to 0x01)&lt;br /&gt;
&lt;br /&gt;
My driver also seems to issue a FIGS command at the end of startup, but this may not be necessary.&lt;br /&gt;
&lt;br /&gt;
Each command starts with a write to address 0x01, and for each parameter a write to address 0x00 follows.  Each command I describe will proceed this way, with the first byte being the command byte written to&lt;br /&gt;
address 0x01, and all following bytes being parameters written to address 0x00.  At the end I will have some example code in C which provides as both an example and an additional explanation.&lt;br /&gt;
&lt;br /&gt;
The first command is the reset command; here is the format:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ The Reset Command&lt;br /&gt;
|-&lt;br /&gt;
! Address !! Value / Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0x00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Operation mode.  Discussed below.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Horizontal display words minus two.  Must be an even number.  This is equal to ((horizontal_pixels / 4) - 2).  Wide mode does not affect this.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Lower 5 bits are the HSync width minus 1, in words.  Upper 3 bits are the lower 3 bits of the VSync width.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Lower 2 bits are the upper 2 bits of the VSync width.  Upper 6 bits are the horizontal front porch width minus 1.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Horizontal back porch width minus one.  Upper two bits must be zero.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Vertical front porch width minus one.  Upper two bits must be zero.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Number of display lines per frame, lower 8 bits.&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Lower 2 bits are the upper 2 bits of the number of display lines per frame.  Upper 6 bits are the vertical back porch length.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note that the vertical field sizes do not have numbers subtracted from them, but the horizontal field sizes have one or two subtracted from them.  If this description is confusing, the uPD7220 manual provided above does contain a nice illustration you may consider looking at.&lt;br /&gt;
&lt;br /&gt;
The operation mode is a bitmask, which controls interlacing, character or graphic mode, as well as DRAM refresh and if drawing is allowed during an active display cycle.  Here are some of the options that&lt;br /&gt;
one can OR together to control the operation mode:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ Operation control bits&lt;br /&gt;
|-&lt;br /&gt;
! Bitmask !! Selected Option&lt;br /&gt;
|-&lt;br /&gt;
| 0x00 || Leave bits 5 and 1 zero to have mixed character and graphics mode.&lt;br /&gt;
|-&lt;br /&gt;
| 0x02 || Enables just graphics mode&lt;br /&gt;
|-&lt;br /&gt;
| 0x20 || Enables just character mode&lt;br /&gt;
|-&lt;br /&gt;
| 0x00 || Leave bits 3 and 0 zero to disable interlacing&lt;br /&gt;
|-&lt;br /&gt;
| 0x08 || Enables interlacing&lt;br /&gt;
|-&lt;br /&gt;
| 0x09 || Enables &amp;quot;Interlaced repeat field for character displays&amp;quot;.  Read the manual for more information.&lt;br /&gt;
|-&lt;br /&gt;
| 0x04 || Enables DRAM refresh cycles - not used on this uPD7220 card, as static RAM is used.&lt;br /&gt;
|-&lt;br /&gt;
| 0x10 || Disables drawing during active display time.  Enabling this is recommended.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Generally 0x12 is a good setting for this card, unless you intend on using character mode.  0x10 will also work as a good mode.&lt;br /&gt;
&lt;br /&gt;
The second command one must send is 0x6f, again written to address 0x01, and it does not take any parameters.  The third command is the PITCH command:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ The Pitch Command&lt;br /&gt;
|-&lt;br /&gt;
! Address !! Value / Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0x47&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Number of memory words per line.  Usually the same as the number of display words per line.  Generally equal to (horizontal_pixels / 4).  The only reason it would differ, is if the &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The fourth command is a PRAM command, which writes data to the uPD7220 parameter RAM.  Here is a recommended usage of the command (although other usages are possible, again read the manual for more info):&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=UPD7220_Video_Card_(Revision_2)&amp;diff=48</id>
		<title>UPD7220 Video Card (Revision 2)</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=UPD7220_Video_Card_(Revision_2)&amp;diff=48"/>
		<updated>2024-06-14T01:20:27Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Finish video modes section&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
The uPD7220 Video Card revision 2 is a video card with 128KB of VRAM using the uPD7220 GDC or its variants.  It operates at a maximum clock speed of 32Mhz.  The card is based on the original uPD7220 card I (Dylan Brophy) made, which had half the VRAM and generally was far simpler.  This card solves several issues with the older card, at the cost of greater size and complexity.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
* 16-color VGA output&lt;br /&gt;
* 128KB of RAM&lt;br /&gt;
** 4 RAM chips, 32KB each, 25ns access and write time&lt;br /&gt;
** 16 bit bus (8 bits per chip, two chips per byte)&lt;br /&gt;
** 4 color bits per pixel, 4 pixels per word in RAM&lt;br /&gt;
*** Maximum 262144 pixels (hardware double buffering disabled)&lt;br /&gt;
**** Suggested resolutions: 544x480, 640x408&lt;br /&gt;
*** Maximum 131072 pixels (hardware double buffering enabled)&lt;br /&gt;
**** Suggested resolutions: 448x288, 400x300&lt;br /&gt;
* uPD7220 at up to 24Mhz, Z7220 at up to 32Mhz&lt;br /&gt;
* uPD7220 wide mode supported&lt;br /&gt;
** Allows higher pixel clock frequencies, for higher resolutions and lower visible display times&lt;br /&gt;
** Accesses two VRAM words per uPD7220 clock cycle&lt;br /&gt;
* Direct access to VRAM is provided to the host CPU&lt;br /&gt;
&lt;br /&gt;
== Video Modes ==&lt;br /&gt;
&lt;br /&gt;
It is assumed that the reader is familiar with VGA display timings.  If not, then it is recommended to study it, or to use the video timing calculator and to leave the card in wide mode.&lt;br /&gt;
&lt;br /&gt;
The uPD7220 was invented before the VGA standard, and due to this, was not designed to output a VGA signal.  Since VGA was designed to drive CRT monitors, as was the uPD7220, it turns out that&lt;br /&gt;
they are fairly compatible.  VGA timings work perfectly fine, but to compute them one must keep in mind the pixel and uPD7220 clocks.  The uPD7220 clock is always one fourth of the card's clock&lt;br /&gt;
speed.&lt;br /&gt;
&lt;br /&gt;
For common VGA signal timings, I would recommend looking at this very useful site: [http://tinyvga.com/vga-timing TinyVGA signal timings]&lt;br /&gt;
&lt;br /&gt;
One must take the timings and convert them to the correct resolution.  For an example, let's convert the 640x480 VGA standard timings to a 400x400 resolution.  Here are the video timings from [http://tinyvga.com/vga-timing/640x480@60Hz TinyVGA's 640x480 60hz page]:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Horizontal Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Pixels !! Microseconds&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 640 || 25.4220&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 16 || 0.6356&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 96 || 3.8133&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 48 || 1.9067&lt;br /&gt;
|-&lt;br /&gt;
| Whole line || 800 || 31.778&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vertical Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Lines&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 480&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 10&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 2&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 33&lt;br /&gt;
|-&lt;br /&gt;
| Whole frame || 525&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
First, we need to decide on a clock frequency for our card.  24Mhz is slower than the 25.175Mhz recommended, and this will make our pixels wider by about 5%.  However, a 24Mhz clock will run our uPD7220 at 6Mhz,&lt;br /&gt;
right at the maximum frequency, and 25.175Mhz would overclock it.  We will select 24Mhz, as the 5% elongation of pixels is too small to care about, and the timings should be compatible with the uPD7220 IC.  We&lt;br /&gt;
will enable wide mode, so our pixel clock is set at 24Mhz (the same as the card - without wide mode it would be 12Mhz, or half).  This keeps the pixel width close to the intended width.  Importantly, this change in clock speed will change the necessary timings for the video signal.  Since each uPD7220 clock cycle is 166.7ns at 6Mhz, and each word is two cycles, we will be dividing the horizontal porch and sync widths&lt;br /&gt;
by 333.3ns to get the number of words for each of the regions.  We cannot compute this yet though, because the visible region still needs to be adjusted for our new resolution.&lt;br /&gt;
&lt;br /&gt;
For our application, we need less pixels, but still need to meet the timing spec.  To achieve this, we can increase the back porch and reduce the visible region until we have the resolution we want.  We do this&lt;br /&gt;
on both the x and y axes.  This works because, during the back porch, the video signal is sent a black signal.  On older monitors, this corresponds to a dark box around the image, and newer monitors will treat it&lt;br /&gt;
as a blank region.  Fundamentally, the monitor will still generally treat it as a 640x480, standard VGA signal, because we haven't encroached on the porches, and we haven't changed/moved the sync signals.  To&lt;br /&gt;
compute the width of the new visible area, we multiply the number of horizontal pixels by the pixel clock period: 41.67ns * 400 pixels = 16.67us.  From this, we can calculate the new horizontal front porch, and&lt;br /&gt;
then we can recompute the lines per vertical video region:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Updated Horizontal Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Microseconds&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 16.67&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 0.6356&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 3.8133&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 10.6620&lt;br /&gt;
|-&lt;br /&gt;
| Whole line || 31.7776&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The back porch becomes much wider.  All we did was move the time over from the visible area to the back porch, such that the visible area has the correct number of pixels at our clock frequency.  The vertical&lt;br /&gt;
regions are adjusted similarly, but the math is much simpler, because we are only trying to keep constant the number of lines:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vertical Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Lines&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 400&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 60&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 2&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 63&lt;br /&gt;
|-&lt;br /&gt;
| Whole frame || 525&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
You will notice that I added lines to both the front and back vertical porches.  This is because the uPD7220 cannot output more than 63 of each porch, so I needed to add to both to obey this limitation.  For&lt;br /&gt;
lower resolutions, it is usually acceptable to reduce the total number of lines to obey these limitations.  It is important, however, that in such a case the horizontal lines are made longer to keep the refresh rate at 60hz, and that the video settings are tested to work on the monitor.&lt;br /&gt;
&lt;br /&gt;
At this point we have the description of the mode we want, now we can convert it into the numbers used by the card.  The vertical timings are already in the correct format, but the horizontal timing still must&lt;br /&gt;
be converted.  To do this, we need to get the number of memory cycles (not uPD7220 clock cycles) for each horizontal region, which is the time for two uPD7220 clock cycles.  The exception is the visible region,&lt;br /&gt;
which is computed from the amount of memory used to represent the data (4 pixels per word, one word accessed per uPD7220 cycle in wide mode).  For the visible region, we divide the number of pixels by 4, and&lt;br /&gt;
for the other regions, we divide the time by 333.3ns and round to the nearest whole number.  Here is the result:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ uPD7220 timing settings&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Words&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 100&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 1.907 =&amp;gt; 2&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 11.4399 =&amp;gt; 11&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 31.986 =&amp;gt; 32&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Any VGA monitor should accept these signal timings for a 400x400 resolution.  For video memory consumtion, this needs 400 * 400 / 4 = 40000 words of memory.  The card has 65536 words total, but only&lt;br /&gt;
32768 if automatic double buffering is enabled, so we cannot enable automatic double buffering for this resolution.  So, we must enable wide mode and disable double buffering for this video mode.&lt;br /&gt;
&lt;br /&gt;
'''Card Specific Details: Wide Mode'''&lt;br /&gt;
&lt;br /&gt;
This card can reach quite high clock speeds for the uPD7220, and to maintain this high speed for such slow chips, wide mode is used.  This allows twice as many pixels to be output per uPD7220 clock cycle.  This can be used either to increase the maximum resolution, or to reduce the display time.  Reducing the display time gives the uPD7220 and host processor more time to draw to the screen.  On the other hand, due to&lt;br /&gt;
the high speeds used in wide mode, there is also typically more data that must be written to the display per frame, thus putting more pressure on the host processor.  The balance in display characteristics depends on the application.&lt;br /&gt;
&lt;br /&gt;
Due to wide mode support, the card's clock is 4x the uPD7220's clock.  When doing calculations for video, this must be kept in mind.  If wide mode is disabled, then the pixel clock is only half the card's clock.  If the video timing math does not account for this, then the video signal timing will be incorrect.&lt;br /&gt;
&lt;br /&gt;
== Communication with the Host Processor ==&lt;br /&gt;
&lt;br /&gt;
The hardware connection uses a fairly simple 8-bit parallel bus for data exchange.  Once connected, the software can read from and write to several registers and addresses on the card to control it.&lt;br /&gt;
&lt;br /&gt;
=== Hardware Protocol ===&lt;br /&gt;
&lt;br /&gt;
Here is a pinout of the card:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Card Pinout&lt;br /&gt;
|-&lt;br /&gt;
! Pin !! Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || A0 || Address pin 0 (LSB)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || A1 || Address pin 1&lt;br /&gt;
|-&lt;br /&gt;
| 3 || A2 || Address pin 2 (MSB)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || NC || No connection: this is the key pin and should be blocked&lt;br /&gt;
|-&lt;br /&gt;
| 5 || GND || Ground connection&lt;br /&gt;
|-&lt;br /&gt;
| 6 || D0 || Data pin 0 (LSB)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || D1 || Data pin 1&lt;br /&gt;
|-&lt;br /&gt;
| 8 || D2 || Data pin 2&lt;br /&gt;
|-&lt;br /&gt;
| 9 || D3 || Data pin 3&lt;br /&gt;
|-&lt;br /&gt;
| 10 || D4 || Data pin 4&lt;br /&gt;
|-&lt;br /&gt;
| 11 || D5 || Data pin 5&lt;br /&gt;
|-&lt;br /&gt;
| 12 || D6 || Data pin 6&lt;br /&gt;
|-&lt;br /&gt;
| 13 || D7 || Data pin 7 (MSB)&lt;br /&gt;
|-&lt;br /&gt;
| 14 || SEL || Select pin (active low input) - selects the card for communication&lt;br /&gt;
|-&lt;br /&gt;
| 15 || 5v || +5V rail to power the card&lt;br /&gt;
|-&lt;br /&gt;
| 16 || RD || Read enable pin (active low input)&lt;br /&gt;
|-&lt;br /&gt;
| 17 || WR || Write enable pin (active low input)&lt;br /&gt;
|-&lt;br /&gt;
| 18 || WAIT || Wait indication (active low output)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When trying to access VRAM, if the VRAM is being used by the card, then WAIT will be pulled low to tell the CPU not to end the current operation until after the WAIT pin goes high again.  After WAIT goes high, wait at least 31.25ns for the read/write operation to complete before releasing any control lines or changing any data/address lines.  Never read data while WAIT is low, and wait 31.25ns after WAIT goes high before reading the data to the CPU.  WAIT only becomes low when trying to read from or write to VRAM; otherwise WAIT is always high.&lt;br /&gt;
&lt;br /&gt;
Each access cycle should last at least 31.25ns, and SEL should be de-asserted between cycles.  Any conventional processor like Z80, 8086, 6502, etc, should have no issue with WAIT or access cycles below an operating frequency of 32Mhz, regardless of the speed of the card.  There may be exceptions to this, but there are no known exceptions.&lt;br /&gt;
&lt;br /&gt;
=== Software Protocol ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Card Registers&lt;br /&gt;
|-&lt;br /&gt;
! Register !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| 0 || uPD7220 command register (uPD7220 A0 = 0)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || uPD7220 parameter register (uPD7220 A0 = 1)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Card configuration register (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Direct VRAM access (accessing this may cause WAIT to go low)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || VRAM Address bits 0-7 (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Reserved - for now same as register 4&lt;br /&gt;
|-&lt;br /&gt;
| 6 || VRAM Address bits 8-15 (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || Reserved - for now same as register 6&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For this card, a driver should first configure register 2 for the selected video mode and card interface configuration.  This register has various bits to tell the card what to do:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ Configuration Register Bits&lt;br /&gt;
|-&lt;br /&gt;
! Bit !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Enables automatic counting of the VRAM address registers if set.  If reset, then the VRAM address registers do not change when VRAM is written.  More on this later.&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Setting this bit enables the card's wide mode circutry.  When reset, one pixel will be output every other clock cycle.  This should match the setting on the uPD7220 IC.&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Enables automatic double buffering.  When set, draws to VRAM from the host CPU or the uPD7220 will happen on the opposite buffer as the one displaying.  Setting this halfs the available VRAM for displaying.&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 6 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 7 || Controls which buffer is displaying.  Changing this bit will swap the currently displaying buffer.  Only works in automatic double buffer mode.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It is recommended to keep track of what buffer is currently displaying, and perhaps this entire configuration register, so that the selected buffer can be switched.  This register is important for controlling how the CPU accesses VRAM, how smooth the display is, and the way the image data is displayed.&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=UPD7220_Video_Card_(Revision_2)&amp;diff=47</id>
		<title>UPD7220 Video Card (Revision 2)</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=UPD7220_Video_Card_(Revision_2)&amp;diff=47"/>
		<updated>2024-06-14T01:08:53Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Video modes tutorial; checkpoint/saving work&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
The uPD7220 Video Card revision 2 is a video card with 128KB of VRAM using the uPD7220 GDC or its variants.  It operates at a maximum clock speed of 32Mhz.  The card is based on the original uPD7220 card I (Dylan Brophy) made, which had half the VRAM and generally was far simpler.  This card solves several issues with the older card, at the cost of greater size and complexity.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
* 16-color VGA output&lt;br /&gt;
* 128KB of RAM&lt;br /&gt;
** 4 RAM chips, 32KB each, 25ns access and write time&lt;br /&gt;
** 16 bit bus (8 bits per chip, two chips per byte)&lt;br /&gt;
** 4 color bits per pixel, 4 pixels per word in RAM&lt;br /&gt;
*** Maximum 262144 pixels (hardware double buffering disabled)&lt;br /&gt;
**** Suggested resolutions: 544x480, 640x408&lt;br /&gt;
*** Maximum 131072 pixels (hardware double buffering enabled)&lt;br /&gt;
**** Suggested resolutions: 448x288, 400x300&lt;br /&gt;
* uPD7220 at up to 24Mhz, Z7220 at up to 32Mhz&lt;br /&gt;
* uPD7220 wide mode supported&lt;br /&gt;
** Allows higher pixel clock frequencies, for higher resolutions and lower visible display times&lt;br /&gt;
** Accesses two VRAM words per uPD7220 clock cycle&lt;br /&gt;
* Direct access to VRAM is provided to the host CPU&lt;br /&gt;
&lt;br /&gt;
== Video Modes ==&lt;br /&gt;
&lt;br /&gt;
It is assumed that the reader is familiar with VGA display timings.  If not, then it is recommended to study it, or to use the video timing calculator and to leave the card in wide mode.&lt;br /&gt;
&lt;br /&gt;
The uPD7220 was invented before the VGA standard, and due to this, was not designed to output a VGA signal.  Since VGA was designed to drive CRT monitors, as was the uPD7220, it turns out that&lt;br /&gt;
they are fairly compatible.  VGA timings work perfectly fine, but to compute them one must keep in mind the pixel and uPD7220 clocks.  The uPD7220 clock is always one fourth of the card's clock&lt;br /&gt;
speed.&lt;br /&gt;
&lt;br /&gt;
For common VGA signal timings, I would recommend looking at this very useful site: [http://tinyvga.com/vga-timing TinyVGA signal timings]&lt;br /&gt;
&lt;br /&gt;
One must take the timings and convert them to the correct resolution.  For an example, let's convert the 640x480 VGA standard timings to a 400x400 resolution.  Here are the video timings from [http://tinyvga.com/vga-timing/640x480@60Hz TinyVGA's 640x480 60hz page]:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Horizontal Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Pixels !! Microseconds&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 640 || 25.422045680238&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 16 || 0.63555114200596&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 96 || 3.8133068520357&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 48 || 1.9066534260179&lt;br /&gt;
|-&lt;br /&gt;
| Whole line || 800 || 31.777557100298&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vertical Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Lines !! Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
Visible area || 480 || 15.253227408143&lt;br /&gt;
|-&lt;br /&gt;
Front porch || 10 || 0.31777557100298&lt;br /&gt;
|-&lt;br /&gt;
Sync pulse || 2 || 0.063555114200596&lt;br /&gt;
|-&lt;br /&gt;
Back porch || 33 || 1.0486593843098&lt;br /&gt;
|-&lt;br /&gt;
Whole frame || 525 || 16.683217477656&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
First, we need to decide on a clock frequency for our card.  24Mhz is slower than the 25.175Mhz recommended, and this will make our pixels wider by about 5%.  However, a 24Mhz clock will run our uPD7220 at 6Mhz,&lt;br /&gt;
right at the maximum frequency, and 25.175Mhz would overclock it.  We will select 24Mhz, as the 5% elongation of pixels is too small to care about, and the timings should be compatible with the uPD7220 IC.  We&lt;br /&gt;
will enable wide mode, so our pixel clock is set at 24Mhz (the same as the card - without wide mode it would be 12Mhz, or half).  This keeps the pixel width close to the intended width.  Importantly, this change in clock speed will change the necessary timings for the video signal.  Since each uPD7220 clock cycle is 166.7ns at 6Mhz, and each word is two cycles, we will be dividing the horizontal porch and sync widths&lt;br /&gt;
by 333.3ns to get the number of words for each of the regions.  We cannot compute this yet though, because the visible region still needs to be adjusted for our new resolution.&lt;br /&gt;
&lt;br /&gt;
For our application, we need less pixels, but still need to meet the timing spec.  To achieve this, we can increase the back porch and reduce the visible region until we have the resolution we want.  We do this&lt;br /&gt;
on both the x and y axes.  This works because, during the back porch, the video signal is sent a black signal.  On older monitors, this corresponds to a dark box around the image, and newer monitors will treat it&lt;br /&gt;
as a blank region.  Fundamentally, the monitor will still generally treat it as a 640x480, standard VGA signal, because we haven't encroached on the porches, and we haven't changed/moved the sync signals.  To&lt;br /&gt;
compute the width of the new visible area, we multiply the number of horizontal pixels by the pixel clock period: 41.67ns * 400 pixels = 16.67us.  From this, we can calculate the new horizontal front porch, and&lt;br /&gt;
then we can recompute the lines per vertical video region:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Updated Horizontal Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Microseconds&lt;br /&gt;
|-&lt;br /&gt;
| Visible area || 16.67&lt;br /&gt;
|-&lt;br /&gt;
| Front porch || 0.63555114200596&lt;br /&gt;
|-&lt;br /&gt;
| Sync pulse || 3.8133068520357&lt;br /&gt;
|-&lt;br /&gt;
| Back porch || 10.662032439589677&lt;br /&gt;
|-&lt;br /&gt;
| Whole line || 31.777557100298&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The back porch becomes much wider.  All we did was move the time over from the visible area to the back porch, such that the visible area has the correct number of pixels at our clock frequency.  The vertical&lt;br /&gt;
regions are adjusted similarly, but the math is much simpler, because we are only trying to keep constant the number of lines:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vertical Timing&lt;br /&gt;
|-&lt;br /&gt;
! Region !! Lines&lt;br /&gt;
|-&lt;br /&gt;
Visible area || 400&lt;br /&gt;
|-&lt;br /&gt;
Front porch || 60&lt;br /&gt;
|-&lt;br /&gt;
Sync pulse || 2&lt;br /&gt;
|-&lt;br /&gt;
Back porch || 63&lt;br /&gt;
|-&lt;br /&gt;
Whole frame || 525&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
You will notice that I added lines to both the front and back vertical porches.  This is because the uPD7220 cannot output more than 63 of each porch, so I needed to add to both to obey this limitation.  For&lt;br /&gt;
lower resolutions, it is usually acceptable to reduce the total number of lines to obey these limitations.  It is important, however, that in such a case the horizontal lines are made longer to keep the refresh rate at 60hz, and that the video settings are tested to work on the monitor.&lt;br /&gt;
&lt;br /&gt;
At this point we have the description of the mode we want, now we can convert it into the numbers used by the card.  The vertical timings are already in the correct format, but the horizontal timing still must&lt;br /&gt;
be converted.&lt;br /&gt;
&lt;br /&gt;
'''Card Specific Details: Wide Mode'''&lt;br /&gt;
&lt;br /&gt;
This card can reach quite high clock speeds for the uPD7220, and to maintain this high speed for such slow chips, wide mode is used.  This allows twice as many pixels to be output per uPD7220 clock cycle.  This can be used either to increase the maximum resolution, or to reduce the display time.  Reducing the display time gives the uPD7220 and host processor more time to draw to the screen.  On the other hand, due to&lt;br /&gt;
the high speeds used in wide mode, there is also typically more data that must be written to the display per frame, thus putting more pressure on the host processor.  The balance in display characteristics depends on the application.&lt;br /&gt;
&lt;br /&gt;
Due to wide mode support, the card's clock is 4x the uPD7220's clock.  When doing calculations for video, this must be kept in mind.  If wide mode is disabled, then the pixel clock is only half the card's clock.  If the video timing math does not account for this, then the video signal timing will be incorrect.&lt;br /&gt;
&lt;br /&gt;
== Communication with the Host Processor ==&lt;br /&gt;
&lt;br /&gt;
The hardware connection uses a fairly simple 8-bit parallel bus for data exchange.  Once connected, the software can read from and write to several registers and addresses on the card to control it.&lt;br /&gt;
&lt;br /&gt;
=== Hardware Protocol ===&lt;br /&gt;
&lt;br /&gt;
Here is a pinout of the card:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Card Pinout&lt;br /&gt;
|-&lt;br /&gt;
! Pin !! Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || A0 || Address pin 0 (LSB)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || A1 || Address pin 1&lt;br /&gt;
|-&lt;br /&gt;
| 3 || A2 || Address pin 2 (MSB)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || NC || No connection: this is the key pin and should be blocked&lt;br /&gt;
|-&lt;br /&gt;
| 5 || GND || Ground connection&lt;br /&gt;
|-&lt;br /&gt;
| 6 || D0 || Data pin 0 (LSB)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || D1 || Data pin 1&lt;br /&gt;
|-&lt;br /&gt;
| 8 || D2 || Data pin 2&lt;br /&gt;
|-&lt;br /&gt;
| 9 || D3 || Data pin 3&lt;br /&gt;
|-&lt;br /&gt;
| 10 || D4 || Data pin 4&lt;br /&gt;
|-&lt;br /&gt;
| 11 || D5 || Data pin 5&lt;br /&gt;
|-&lt;br /&gt;
| 12 || D6 || Data pin 6&lt;br /&gt;
|-&lt;br /&gt;
| 13 || D7 || Data pin 7 (MSB)&lt;br /&gt;
|-&lt;br /&gt;
| 14 || SEL || Select pin (active low input) - selects the card for communication&lt;br /&gt;
|-&lt;br /&gt;
| 15 || 5v || +5V rail to power the card&lt;br /&gt;
|-&lt;br /&gt;
| 16 || RD || Read enable pin (active low input)&lt;br /&gt;
|-&lt;br /&gt;
| 17 || WR || Write enable pin (active low input)&lt;br /&gt;
|-&lt;br /&gt;
| 18 || WAIT || Wait indication (active low output)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When trying to access VRAM, if the VRAM is being used by the card, then WAIT will be pulled low to tell the CPU not to end the current operation until after the WAIT pin goes high again.  After WAIT goes high, wait at least 31.25ns for the read/write operation to complete before releasing any control lines or changing any data/address lines.  Never read data while WAIT is low, and wait 31.25ns after WAIT goes high before reading the data to the CPU.  WAIT only becomes low when trying to read from or write to VRAM; otherwise WAIT is always high.&lt;br /&gt;
&lt;br /&gt;
Each access cycle should last at least 31.25ns, and SEL should be de-asserted between cycles.  Any conventional processor like Z80, 8086, 6502, etc, should have no issue with WAIT or access cycles below an operating frequency of 32Mhz, regardless of the speed of the card.  There may be exceptions to this, but there are no known exceptions.&lt;br /&gt;
&lt;br /&gt;
=== Software Protocol ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Card Registers&lt;br /&gt;
|-&lt;br /&gt;
! Register !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| 0 || uPD7220 command register (uPD7220 A0 = 0)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || uPD7220 parameter register (uPD7220 A0 = 1)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Card configuration register (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Direct VRAM access (accessing this may cause WAIT to go low)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || VRAM Address bits 0-7 (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Reserved - for now same as register 4&lt;br /&gt;
|-&lt;br /&gt;
| 6 || VRAM Address bits 8-15 (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || Reserved - for now same as register 6&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For this card, a driver should first configure register 2 for the selected video mode and card interface configuration.  This register has various bits to tell the card what to do:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ Configuration Register Bits&lt;br /&gt;
|-&lt;br /&gt;
! Bit !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Enables automatic counting of the VRAM address registers if set.  If reset, then the VRAM address registers do not change when VRAM is written.  More on this later.&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Setting this bit enables the card's wide mode circutry.  When reset, one pixel will be output every other clock cycle.  This should match the setting on the uPD7220 IC.&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Enables automatic double buffering.  When set, draws to VRAM from the host CPU or the uPD7220 will happen on the opposite buffer as the one displaying.  Setting this halfs the available VRAM for displaying.&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 6 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 7 || Controls which buffer is displaying.  Changing this bit will swap the currently displaying buffer.  Only works in automatic double buffer mode.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It is recommended to keep track of what buffer is currently displaying, and perhaps this entire configuration register, so that the selected buffer can be switched.  This register is important for controlling how the CPU accesses VRAM, how smooth the display is, and the way the image data is displayed.&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=UPD7220_Video_Card_(Revision_2)&amp;diff=46</id>
		<title>UPD7220 Video Card (Revision 2)</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=UPD7220_Video_Card_(Revision_2)&amp;diff=46"/>
		<updated>2024-06-14T00:27:23Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Fix table formatting&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
The uPD7220 Video Card revision 2 is a video card with 128KB of VRAM using the uPD7220 GDC or its variants.  It operates at a maximum clock speed of 32Mhz.  The card is based on the original uPD7220 card I (Dylan Brophy) made, which had half the VRAM and generally was far simpler.  This card solves several issues with the older card, at the cost of greater size and complexity.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
* 16-color VGA output&lt;br /&gt;
* 128KB of RAM&lt;br /&gt;
** 4 RAM chips, 32KB each, 25ns access and write time&lt;br /&gt;
** 16 bit bus (8 bits per chip, two chips per byte)&lt;br /&gt;
** 4 color bits per pixel, 4 pixels per word in RAM&lt;br /&gt;
*** Maximum 262144 pixels (hardware double buffering disabled)&lt;br /&gt;
**** Suggested resolutions: 544x480, 640x408&lt;br /&gt;
*** Maximum 131072 pixels (hardware double buffering enabled)&lt;br /&gt;
**** Suggested resolutions: 448x288, 400x300&lt;br /&gt;
* uPD7220 at up to 24Mhz, Z7220 at up to 32Mhz&lt;br /&gt;
* uPD7220 wide mode supported&lt;br /&gt;
** Allows higher pixel clock frequencies, for higher resolutions and lower visible display times&lt;br /&gt;
** Accesses two VRAM words per uPD7220 clock cycle&lt;br /&gt;
* Direct access to VRAM is provided to the host CPU&lt;br /&gt;
&lt;br /&gt;
== Video Modes ==&lt;br /&gt;
&lt;br /&gt;
It is assumed that the reader is familiar with VGA display timings.  If not, then it is recommended to study it, or to use the video timing calculator and to leave the card in wide mode.&lt;br /&gt;
&lt;br /&gt;
The uPD7220 was invented before the VGA standard, and due to this, was not designed to output a VGA signal.  Since VGA was designed to drive CRT monitors, as was the uPD7220, it turns out that&lt;br /&gt;
they are fairly compatible.  &lt;br /&gt;
&lt;br /&gt;
'''Card Specific Details: Wide Mode'''&lt;br /&gt;
&lt;br /&gt;
This card can reach quite high clock speeds for the uPD7220, and to maintain this high speed for such slow chips, wide mode is used.  This allows twice as many pixels to be output per uPD7220 clock cycle.  This can be used either to increase the maximum resolution, or to reduce the display time.  Reducing the display time gives the uPD7220 and host processor more time to draw to the screen.  On the other hand, due to&lt;br /&gt;
the high speeds used in wide mode, there is also typically more data that must be written to the display per frame, thus putting more pressure on the host processor.  The balance in display characteristics depends on the application.&lt;br /&gt;
&lt;br /&gt;
Due to wide mode support, the card's clock is 4x the uPD7220's clock.  When doing calculations for video, this must be kept in mind.  If wide mode is disabled, then the pixel clock is only half the card's clock.  If the video timing math does not account for this, then the video signal timing will be incorrect.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Communication with the Host Processor ==&lt;br /&gt;
&lt;br /&gt;
The hardware connection uses a fairly simple 8-bit parallel bus for data exchange.  Once connected, the software can read from and write to several registers and addresses on the card to control it.&lt;br /&gt;
&lt;br /&gt;
=== Hardware Protocol ===&lt;br /&gt;
&lt;br /&gt;
Here is a pinout of the card:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Card Pinout&lt;br /&gt;
|-&lt;br /&gt;
! Pin !! Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || A0 || Address pin 0 (LSB)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || A1 || Address pin 1&lt;br /&gt;
|-&lt;br /&gt;
| 3 || A2 || Address pin 2 (MSB)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || NC || No connection: this is the key pin and should be blocked&lt;br /&gt;
|-&lt;br /&gt;
| 5 || GND || Ground connection&lt;br /&gt;
|-&lt;br /&gt;
| 6 || D0 || Data pin 0 (LSB)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || D1 || Data pin 1&lt;br /&gt;
|-&lt;br /&gt;
| 8 || D2 || Data pin 2&lt;br /&gt;
|-&lt;br /&gt;
| 9 || D3 || Data pin 3&lt;br /&gt;
|-&lt;br /&gt;
| 10 || D4 || Data pin 4&lt;br /&gt;
|-&lt;br /&gt;
| 11 || D5 || Data pin 5&lt;br /&gt;
|-&lt;br /&gt;
| 12 || D6 || Data pin 6&lt;br /&gt;
|-&lt;br /&gt;
| 13 || D7 || Data pin 7 (MSB)&lt;br /&gt;
|-&lt;br /&gt;
| 14 || SEL || Select pin (active low input) - selects the card for communication&lt;br /&gt;
|-&lt;br /&gt;
| 15 || 5v || +5V rail to power the card&lt;br /&gt;
|-&lt;br /&gt;
| 16 || RD || Read enable pin (active low input)&lt;br /&gt;
|-&lt;br /&gt;
| 17 || WR || Write enable pin (active low input)&lt;br /&gt;
|-&lt;br /&gt;
| 18 || WAIT || Wait indication (active low output)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When trying to access VRAM, if the VRAM is being used by the card, then WAIT will be pulled low to tell the CPU not to end the current operation until after the WAIT pin goes high again.  After WAIT goes high, wait at least 31.25ns for the read/write operation to complete before releasing any control lines or changing any data/address lines.  Never read data while WAIT is low, and wait 31.25ns after WAIT goes high before reading the data to the CPU.  WAIT only becomes low when trying to read from or write to VRAM; otherwise WAIT is always high.&lt;br /&gt;
&lt;br /&gt;
Each access cycle should last at least 31.25ns, and SEL should be de-asserted between cycles.  Any conventional processor like Z80, 8086, 6502, etc, should have no issue with WAIT or access cycles below an operating frequency of 32Mhz, regardless of the speed of the card.  There may be exceptions to this, but there are no known exceptions.&lt;br /&gt;
&lt;br /&gt;
=== Software Protocol ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Card Registers&lt;br /&gt;
|-&lt;br /&gt;
! Register !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| 0 || uPD7220 command register (uPD7220 A0 = 0)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || uPD7220 parameter register (uPD7220 A0 = 1)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Card configuration register (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Direct VRAM access (accessing this may cause WAIT to go low)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || VRAM Address bits 0-7 (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Reserved - for now same as register 4&lt;br /&gt;
|-&lt;br /&gt;
| 6 || VRAM Address bits 8-15 (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || Reserved - for now same as register 6&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For this card, a driver should first configure register 2 for the selected video mode and card interface configuration.  This register has various bits to tell the card what to do:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:50%&amp;quot;&lt;br /&gt;
|+ Configuration Register Bits&lt;br /&gt;
|-&lt;br /&gt;
! Bit !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Enables automatic counting of the VRAM address registers if set.  If reset, then the VRAM address registers do not change when VRAM is written.  More on this later.&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Setting this bit enables the card's wide mode circutry.  When reset, one pixel will be output every other clock cycle.  This should match the setting on the uPD7220 IC.&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Enables automatic double buffering.  When set, draws to VRAM from the host CPU or the uPD7220 will happen on the opposite buffer as the one displaying.  Setting this halfs the available VRAM for displaying.&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 6 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 7 || Controls which buffer is displaying.  Changing this bit will swap the currently displaying buffer.  Only works in automatic double buffer mode.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It is recommended to keep track of what buffer is currently displaying, and perhaps this entire configuration register, so that the selected buffer can be switched.  This register is important for controlling how the CPU accesses VRAM, how smooth the display is, and the way the image data is displayed.&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=UPD7220_Video_Card_(Revision_2)&amp;diff=45</id>
		<title>UPD7220 Video Card (Revision 2)</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=UPD7220_Video_Card_(Revision_2)&amp;diff=45"/>
		<updated>2024-06-14T00:22:35Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Checkpoint for programming guide and video mode information&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
The uPD7220 Video Card revision 2 is a video card with 128KB of VRAM using the uPD7220 GDC or its variants.  It operates at a maximum clock speed of 32Mhz.  The card is based on the original uPD7220 card I (Dylan Brophy) made, which had half the VRAM and generally was far simpler.  This card solves several issues with the older card, at the cost of greater size and complexity.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
* 16-color VGA output&lt;br /&gt;
* 128KB of RAM&lt;br /&gt;
** 4 RAM chips, 32KB each, 25ns access and write time&lt;br /&gt;
** 16 bit bus (8 bits per chip, two chips per byte)&lt;br /&gt;
** 4 color bits per pixel, 4 pixels per word in RAM&lt;br /&gt;
*** Maximum 262144 pixels (hardware double buffering disabled)&lt;br /&gt;
**** Suggested resolutions: 544x480, 640x408&lt;br /&gt;
*** Maximum 131072 pixels (hardware double buffering enabled)&lt;br /&gt;
**** Suggested resolutions: 448x288, 400x300&lt;br /&gt;
* uPD7220 at up to 24Mhz, Z7220 at up to 32Mhz&lt;br /&gt;
* uPD7220 wide mode supported&lt;br /&gt;
** Allows higher pixel clock frequencies, for higher resolutions and lower visible display times&lt;br /&gt;
** Accesses two VRAM words per uPD7220 clock cycle&lt;br /&gt;
* Direct access to VRAM is provided to the host CPU&lt;br /&gt;
&lt;br /&gt;
== Video Modes ==&lt;br /&gt;
&lt;br /&gt;
It is assumed that the reader is familiar with VGA display timings.  If not, then it is recommended to study it, or to use the video timing calculator and to leave the card in wide mode.&lt;br /&gt;
&lt;br /&gt;
The uPD7220 was invented before the VGA standard, and due to this, was not designed to output a VGA signal.  Since VGA was designed to drive CRT monitors, as was the uPD7220, it turns out that&lt;br /&gt;
they are fairly compatible.  &lt;br /&gt;
&lt;br /&gt;
'''Card Specific Details: Wide Mode'''&lt;br /&gt;
&lt;br /&gt;
This card can reach quite high clock speeds for the uPD7220, and to maintain this high speed for such slow chips, wide mode is used.  This allows twice as many pixels to be output per uPD7220 clock cycle.  This can be used either to increase the maximum resolution, or to reduce the display time.  Reducing the display time gives the uPD7220 and host processor more time to draw to the screen.  On the other hand, due to&lt;br /&gt;
the high speeds used in wide mode, there is also typically more data that must be written to the display per frame, thus putting more pressure on the host processor.  The balance in display characteristics depends on the application.&lt;br /&gt;
&lt;br /&gt;
Due to wide mode support, the card's clock is 4x the uPD7220's clock.  When doing calculations for video, this must be kept in mind.  If wide mode is disabled, then the pixel clock is only half the card's clock.  If the video timing math does not account for this, then the video signal timing will be incorrect.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Communication with the Host Processor ==&lt;br /&gt;
&lt;br /&gt;
The hardware connection uses a fairly simple 8-bit parallel bus for data exchange.  Once connected, the software can read from and write to several registers and addresses on the card to control it.&lt;br /&gt;
&lt;br /&gt;
=== Hardware Protocol ===&lt;br /&gt;
&lt;br /&gt;
Here is a pinout of the card:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Card Pinout&lt;br /&gt;
|-&lt;br /&gt;
! Pin !! Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || A0 || Address pin 0 (LSB)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || A1 || Address pin 1&lt;br /&gt;
|-&lt;br /&gt;
| 3 || A2 || Address pin 2 (MSB)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || NC || No connection: this is the key pin and should be blocked&lt;br /&gt;
|-&lt;br /&gt;
| 5 || GND || Ground connection&lt;br /&gt;
|-&lt;br /&gt;
| 6 || D0 || Data pin 0 (LSB)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || D1 || Data pin 1&lt;br /&gt;
|-&lt;br /&gt;
| 8 || D2 || Data pin 2&lt;br /&gt;
|-&lt;br /&gt;
| 9 || D3 || Data pin 3&lt;br /&gt;
|-&lt;br /&gt;
| 10 || D4 || Data pin 4&lt;br /&gt;
|-&lt;br /&gt;
| 11 || D5 || Data pin 5&lt;br /&gt;
|-&lt;br /&gt;
| 12 || D6 || Data pin 6&lt;br /&gt;
|-&lt;br /&gt;
| 13 || D7 || Data pin 7 (MSB)&lt;br /&gt;
|-&lt;br /&gt;
| 14 || SEL || Select pin (active low input) - selects the card for communication&lt;br /&gt;
|-&lt;br /&gt;
| 15 || 5v || +5V rail to power the card&lt;br /&gt;
|-&lt;br /&gt;
| 16 || RD || Read enable pin (active low input)&lt;br /&gt;
|-&lt;br /&gt;
| 17 || WR || Write enable pin (active low input)&lt;br /&gt;
|-&lt;br /&gt;
| 18 || WAIT || Wait indication (active low output)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When trying to access VRAM, if the VRAM is being used by the card, then WAIT will be pulled low to tell the CPU not to end the current operation until after the WAIT pin goes high again.  After WAIT goes high, wait at least 31.25ns for the read/write operation to complete before releasing any control lines or changing any data/address lines.  Never read data while WAIT is low, and wait 31.25ns after WAIT goes high before reading the data to the CPU.  WAIT only becomes low when trying to read from or write to VRAM; otherwise WAIT is always high.&lt;br /&gt;
&lt;br /&gt;
Each access cycle should last at least 31.25ns, and SEL should be de-asserted between cycles.  Any conventional processor like Z80, 8086, 6502, etc, should have no issue with WAIT or access cycles below an operating frequency of 32Mhz, regardless of the speed of the card.  There may be exceptions to this, but there are no known exceptions.&lt;br /&gt;
&lt;br /&gt;
=== Software Protocol ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Card Registers&lt;br /&gt;
|-&lt;br /&gt;
! Register !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| 0 || uPD7220 command register (uPD7220 A0 = 0)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || uPD7220 parameter register (uPD7220 A0 = 1)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Card configuration register (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Direct VRAM access (accessing this may cause WAIT to go low)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || VRAM Address bits 0-7 (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Reserved - for now same as register 4&lt;br /&gt;
|-&lt;br /&gt;
| 6 || VRAM Address bits 8-15 (write only)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || Reserved - for now same as register 6&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For this card, a driver should first configure register 2 for the selected video mode and card interface configuration.  This register has various bits to tell the card what to do:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Configuration Register Bits&lt;br /&gt;
|-&lt;br /&gt;
! Bit !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Enables automatic counting of the VRAM address registers if set.  If reset, then the VRAM address registers do not change when VRAM is written.  More on this later.&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Setting this bit enables the card's wide mode circutry.  When reset, one pixel will be output every other clock cycle.  This should match the setting on the uPD7220 IC.&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Enables automatic double buffering.  When set, draws to VRAM from the host CPU or the uPD7220 will happen on the opposite buffer as the one displaying.  Setting this halfs the available VRAM for displaying.&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 6 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 7 || Controls which buffer is displaying.  Changing this bit will swap the currently displaying buffer.  Only works in automatic double buffer mode.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
It is recommended to keep track of what buffer is currently displaying, and perhaps this entire configuration register, so that the selected buffer can be switched.  This register is important for controlling how the CPU accesses VRAM, how smooth the display is, and the way the image data is displayed.&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=UPD7220_Video_Card_(Revision_2)&amp;diff=44</id>
		<title>UPD7220 Video Card (Revision 2)</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=UPD7220_Video_Card_(Revision_2)&amp;diff=44"/>
		<updated>2024-06-13T22:49:48Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Create UPD7220 Video Card (Revision 2) page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
The uPD7220 Video Card revision 2 is a video card with 128KB of VRAM using the uPD7220 GDC or its variants.  It operates at a maximum clock speed of 32Mhz.  The card is based on the original uPD7220 card I (Dylan Brophy) made, which had half the VRAM and generally was far simpler.  This card solves several issues with the older card, at the cost of greater size and complexity.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
* 16-color VGA output&lt;br /&gt;
* 128KB of RAM&lt;br /&gt;
** 4 RAM chips, 32KB each, 25ns access and write time&lt;br /&gt;
** 16 bit bus (8 bits per chip, two chips per byte)&lt;br /&gt;
** 4 color bits per pixel, 4 pixels per word in RAM&lt;br /&gt;
*** Maximum 262144 pixels (hardware double buffering disabled)&lt;br /&gt;
**** Suggested resolutions: 544x480, 640x408&lt;br /&gt;
*** Maximum 131072 pixels (hardware double buffering enabled)&lt;br /&gt;
**** Suggested resolutions: 448x288, 400x300&lt;br /&gt;
* uPD7220 at up to 24Mhz, Z7220 at up to 32Mhz&lt;br /&gt;
* uPD7220 wide mode supported&lt;br /&gt;
** Allows higher pixel clock frequencies, for higher resolutions and lower visible display times&lt;br /&gt;
** Accesses two VRAM words per uPD7220 clock cycle&lt;br /&gt;
* Direct access to VRAM is provided to the host CPU&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=43</id>
		<title>Teensy 4.1 Computer</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=43"/>
		<updated>2024-06-13T21:51:23Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Add more images to the Expansion Ports section&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Teensy 4.1 Computer is more or less described by its name: Its a carrier board for the Teensy 4.1 which provides four USB ports, an Ethernet port, Hi-Fi audio, and several convenient expansion ports.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
'''USB:'''&lt;br /&gt;
&lt;br /&gt;
* TUSB2046 USB Hub, providing 4 USB A ports&lt;br /&gt;
* Each port has a maximum of 1.5A output - the port will shutdown due to overcurrent above this&lt;br /&gt;
* Board power provides a maximum of 2A to the 5v supply&lt;br /&gt;
** one should be aware of this when considering the power used by USB devices&lt;br /&gt;
&lt;br /&gt;
'''Audio:'''&lt;br /&gt;
&lt;br /&gt;
* I2S control connected to Teensy I2S 2&lt;br /&gt;
* PCM5102 ADC for providing stereo sound&lt;br /&gt;
* OPA1688 for the amplifier&lt;br /&gt;
* 3.5mm audio jack, maximum 75mA per channel&lt;br /&gt;
* Separate ground, connected to main ground through a ferrite bead&lt;br /&gt;
* Separate 3.3v and 5v supplies from linear regulators to reduce noise&lt;br /&gt;
** Note: 5v line will come from main 5v rail if only powered from USB&lt;br /&gt;
&lt;br /&gt;
'''Power:'''&lt;br /&gt;
&lt;br /&gt;
* 2A 5v buck regulator (Exact part is the VR20S05)&lt;br /&gt;
* PMOS switches to prevent regulator backfeeding from USB and to control power flow (U16, U17, and U18 on the PCB)&lt;br /&gt;
* External VIN accepted from 8v to 20v&lt;br /&gt;
* Separate 5v and 3.3v supplies for audio circutry&lt;br /&gt;
* Barrel jack and screw terminal connections for VIN&lt;br /&gt;
&lt;br /&gt;
'''Expansion Ports'''&lt;br /&gt;
&lt;br /&gt;
* I2S, SPI, I2C, and Serial expansion ports&lt;br /&gt;
* 5v-capable 8-bit parallel bus port, with 3-bit address, WR, RD, select, and WAIT lines&lt;br /&gt;
* Two larger expansion ports, each providing a '''3.3v compatible''' 8-bit parallel bus, various protocols like I2C and SPI, power, and GPIO&lt;br /&gt;
&lt;br /&gt;
== The Audio Circuit ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy41_Computer_Audio_circuit.png|thumb|Teensy 4.1 Computer audio circuit|600px]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1 is connected to the audio circuit via the Teensy's I2S 2 port.  This connection goes to the PCM5102 ADC.  As is recommended in the PCM5102's datasheet, filtering is added to the output, however the filter used on this board is a bit stronger, using a 4.7nF capacitor instead of the 2.2nF capacitor recommended in the datasheet.  The filter cutoff is still far above human perception, at 72kHz, and should filter high frequency noise a bit better.  The connection to the OPA1688 is DC, not AC, coupled, but this is according to the recommendations in the OPA1688 datasheet.  The gain of the amplifier is configured to be 1, so the output voltage of the amp should match the ADC's output voltage, and be in the range of 0v to 3.3v.  A voltage divider is used to activate the PCM5102's soft mute function when the 5v line drops, which is designed to prevent any pops in the audio when the power is disconnected.&lt;br /&gt;
&lt;br /&gt;
The circuit was designed with audio quality, but not energy efficiency, in mind.  The circuit may sink as much as 400mA from VIN, and an additional 40mA from the main 5v line.  However, when the audio circuit is idle, it should be less than 50mA all together.  The 3.5mm audio jack can provide a maximum of 75mA per channel.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;Audio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;SD.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// GUItool: begin automatically generated code&lt;br /&gt;
AudioPlaySdWav           playSdWav1;     //xy=719,389&lt;br /&gt;
AudioOutputI2S2          i2s2_1;         //xy=892,388&lt;br /&gt;
AudioConnection          patchCord1(playSdWav1, 0, i2s2_1, 0);&lt;br /&gt;
AudioConnection          patchCord2(playSdWav1, 1, i2s2_1, 1);&lt;br /&gt;
// GUItool: end automatically generated code&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  AudioMemory(10);&lt;br /&gt;
&lt;br /&gt;
  if (!(SD.begin(BUILTIN_SDCARD))) {&lt;br /&gt;
    // stop here, but print a message repetitively&lt;br /&gt;
    while (1) {&lt;br /&gt;
      Serial.println(&amp;quot;Unable to access the SD card&amp;quot;);&lt;br /&gt;
      delay(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Serial.println(&amp;quot;Playing audio.wav...&amp;quot;);&lt;br /&gt;
  playSdWav1.play(&amp;quot;audio.wav&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // Do nothing forever&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== USB Ports ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_USB_schematic.png|thumb|Teensy 4.1 Computer USB circuit|600px]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1's builtin USB port is connected directly to a TUSB2046 IC, which is a USB hub and provides four downstream ports.  Each port has overcurrent protection, with a maximum allowed current of 1.5A.  There are also TVS diodes on all USB data lines on the downstream ports, provided by three TPD3E001DRLR ICs.  Other than that, each downstream USB port has two 15k pulldowns - one on each data line.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&lt;br /&gt;
The best example is found in the Mouse_KeyboardBT example from the USBHost_t36 library, which is included in the TeensyDuino package.  For convenience, I have included a stripped down version of it below:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;quot;USBHost_t36.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
USBHost myusb;&lt;br /&gt;
USBHub hub1(myusb);&lt;br /&gt;
USBHub hub2(myusb);&lt;br /&gt;
KeyboardController keyboard1(myusb);&lt;br /&gt;
KeyboardController keyboard2(myusb);&lt;br /&gt;
USBHIDParser hid1(myusb);&lt;br /&gt;
USBHIDParser hid2(myusb);&lt;br /&gt;
USBHIDParser hid3(myusb);&lt;br /&gt;
USBHIDParser hid4(myusb);&lt;br /&gt;
USBHIDParser hid5(myusb);&lt;br /&gt;
MouseController mouse1(myusb);&lt;br /&gt;
&lt;br /&gt;
BluetoothController bluet(myusb);   // version assumes it already was paired&lt;br /&gt;
&lt;br /&gt;
USBDriver *drivers[] = {&amp;amp;hub1, &amp;amp;hub2, &amp;amp;keyboard1, &amp;amp;keyboard2, &amp;amp;bluet, &amp;amp;hid1, &amp;amp;hid2, &amp;amp;hid3, &amp;amp;hid4, &amp;amp;hid5};&lt;br /&gt;
&lt;br /&gt;
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))&lt;br /&gt;
const char * driver_names[CNT_DEVICES] = {&amp;quot;Hub1&amp;quot;, &amp;quot;Hub2&amp;quot;, &amp;quot;KB1&amp;quot;, &amp;quot;KB2&amp;quot;, &amp;quot;Bluet&amp;quot;, &amp;quot;HID1&amp;quot; , &amp;quot;HID2&amp;quot;, &amp;quot;HID3&amp;quot;, &amp;quot;HID4&amp;quot;, &amp;quot;HID5&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool driver_active[CNT_DEVICES] = {false, false, false, false, false, false, false, false, false, false};&lt;br /&gt;
&lt;br /&gt;
// Lets also look at HID Input devices&lt;br /&gt;
USBHIDInput *hiddrivers[] = {&amp;amp;mouse1};&lt;br /&gt;
&lt;br /&gt;
#define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0]))&lt;br /&gt;
const char * hid_driver_names[CNT_HIDDEVICES] = {&amp;quot;Mouse1&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool hid_driver_active[CNT_HIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
BTHIDInput *bthiddrivers[] = {&amp;amp;keyboard1, &amp;amp;mouse1};&lt;br /&gt;
#define CNT_BTHIDDEVICES (sizeof(bthiddrivers)/sizeof(bthiddrivers[0]))&lt;br /&gt;
const char * bthid_driver_names[CNT_BTHIDDEVICES] = {&amp;quot;Keyboard(BT)&amp;quot;, &amp;quot;Mouse(BT)&amp;quot;};&lt;br /&gt;
bool bthid_driver_active[CNT_BTHIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
bool show_changed_only = false;&lt;br /&gt;
&lt;br /&gt;
uint8_t joystick_left_trigger_value = 0;&lt;br /&gt;
uint8_t joystick_right_trigger_value = 0;&lt;br /&gt;
uint64_t joystick_full_notify_mask = (uint64_t) - 1;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  while (!Serial); // wait for Arduino Serial Monitor&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  myusb.begin();&lt;br /&gt;
  keyboard1.attachPress(OnPress);&lt;br /&gt;
  keyboard2.attachPress(OnPress);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // check to see if the device list has changed:&lt;br /&gt;
  UpdateActiveDeviceInfo();&lt;br /&gt;
&lt;br /&gt;
  myusb.Task();&lt;br /&gt;
&lt;br /&gt;
  if (mouse1.available()) {&lt;br /&gt;
    Serial.print(&amp;quot;Mouse: buttons = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getButtons());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseX = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseX());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseY = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseY());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheel = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheel());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheelH = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheelH());&lt;br /&gt;
    Serial.println();&lt;br /&gt;
    mouse1.mouseDataClear();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void OnPress(int key)&lt;br /&gt;
{&lt;br /&gt;
  Serial.print(&amp;quot;key '&amp;quot;);&lt;br /&gt;
  switch (key) {&lt;br /&gt;
    case KEYD_UP       : Serial.print(&amp;quot;UP&amp;quot;); break;&lt;br /&gt;
    case KEYD_DOWN     : Serial.print(&amp;quot;DN&amp;quot;); break;&lt;br /&gt;
    case KEYD_LEFT     : Serial.print(&amp;quot;LEFT&amp;quot;); break;&lt;br /&gt;
    case KEYD_RIGHT    : Serial.print(&amp;quot;RIGHT&amp;quot;); break;&lt;br /&gt;
    case KEYD_INSERT   : Serial.print(&amp;quot;Ins&amp;quot;); break;&lt;br /&gt;
    case KEYD_DELETE   : Serial.print(&amp;quot;Del&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_UP  : Serial.print(&amp;quot;PUP&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_DOWN: Serial.print(&amp;quot;PDN&amp;quot;); break;&lt;br /&gt;
    case KEYD_HOME     : Serial.print(&amp;quot;HOME&amp;quot;); break;&lt;br /&gt;
    case KEYD_END      : Serial.print(&amp;quot;END&amp;quot;); break;&lt;br /&gt;
    case KEYD_F1       : Serial.print(&amp;quot;F1&amp;quot;); break;&lt;br /&gt;
    case KEYD_F2       : Serial.print(&amp;quot;F2&amp;quot;); break;&lt;br /&gt;
    case KEYD_F3       : Serial.print(&amp;quot;F3&amp;quot;); break;&lt;br /&gt;
    case KEYD_F4       : Serial.print(&amp;quot;F4&amp;quot;); break;&lt;br /&gt;
    case KEYD_F5       : Serial.print(&amp;quot;F5&amp;quot;); break;&lt;br /&gt;
    case KEYD_F6       : Serial.print(&amp;quot;F6&amp;quot;); break;&lt;br /&gt;
    case KEYD_F7       : Serial.print(&amp;quot;F7&amp;quot;); break;&lt;br /&gt;
    case KEYD_F8       : Serial.print(&amp;quot;F8&amp;quot;); break;&lt;br /&gt;
    case KEYD_F9       : Serial.print(&amp;quot;F9&amp;quot;); break;&lt;br /&gt;
    case KEYD_F10      : Serial.print(&amp;quot;F10&amp;quot;); break;&lt;br /&gt;
    case KEYD_F11      : Serial.print(&amp;quot;F11&amp;quot;); break;&lt;br /&gt;
    case KEYD_F12      : Serial.print(&amp;quot;F12&amp;quot;); break;&lt;br /&gt;
    default: Serial.print((char)key); break;&lt;br /&gt;
  }&lt;br /&gt;
  Serial.print(&amp;quot;'  &amp;quot;);&lt;br /&gt;
  Serial.println(key);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//=============================================================================&lt;br /&gt;
// UpdateActiveDeviceInfo&lt;br /&gt;
//=============================================================================&lt;br /&gt;
void UpdateActiveDeviceInfo() {&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_DEVICES; i++) {&lt;br /&gt;
    if (*drivers[i] != driver_active[i]) {&lt;br /&gt;
      if (driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s - disconnected ***\n&amp;quot;, driver_names[i]);&lt;br /&gt;
        driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s %x:%x - connected ***\n&amp;quot;, driver_names[i], drivers[i]-&amp;gt;idVendor(), drivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = drivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
&lt;br /&gt;
        if (drivers[i] == &amp;amp;bluet) {&lt;br /&gt;
          const uint8_t *bdaddr = bluet.myBDAddr();&lt;br /&gt;
          // remember it...&lt;br /&gt;
          Serial.printf(&amp;quot;  BDADDR: %x:%x:%x:%x:%x:%x\n&amp;quot;, bdaddr[0], bdaddr[1], bdaddr[2], bdaddr[3], bdaddr[4], bdaddr[5]);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_HIDDEVICES; i++) {&lt;br /&gt;
    if (*hiddrivers[i] != hid_driver_active[i]) {&lt;br /&gt;
      if (hid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s - disconnected ***\n&amp;quot;, hid_driver_names[i]);&lt;br /&gt;
        hid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s %x:%x - connected ***\n&amp;quot;, hid_driver_names[i], hiddrivers[i]-&amp;gt;idVendor(), hiddrivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        hid_driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = hiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  // Then Bluetooth devices&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_BTHIDDEVICES; i++) {&lt;br /&gt;
    if (*bthiddrivers[i] != bthid_driver_active[i]) {&lt;br /&gt;
      if (bthid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - disconnected ***\n&amp;quot;, bthid_driver_names[i]);&lt;br /&gt;
        bthid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - connected ***\n&amp;quot;, bthid_driver_names[i]); Serial.flush();&lt;br /&gt;
        bthid_driver_active[i] = true;&lt;br /&gt;
        #if 0&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = bthiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
    #endif  &lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Known Issues:'''&lt;br /&gt;
* Version 1.1 of the board leaves TUSB2046's reset line floating.  This can be remedied by connecting the TUSB2046's reset line to some other circuit, although this is error prone.&lt;br /&gt;
&lt;br /&gt;
== Power ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_power_circuit.png|thumb|Teensy 4.1 Computer power circuitry|500px]]&lt;br /&gt;
&lt;br /&gt;
The computer's power supply accepts input from four primary sources:&lt;br /&gt;
* Barrel jack DC input, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Screw terminal block, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Teensy 4.1 micro USB port, 5v input, current limit depends on USB power source&lt;br /&gt;
* Expansion ports&lt;br /&gt;
** Direct to 5v line, only limited by source current.  This allows one to surpass the typical 2A limit on the 5v line.&lt;br /&gt;
** Direct to VIN, accepts 8v to 20v.  5v line limited to 2A.&lt;br /&gt;
** Direct to 3.3v line, only limited by source current.  This can be used to relieve stress from the Teensy 4.1's 3.3v regulator, if needed.&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_audio_power.png|thumb|Teensy 4.1 Computer audio power circuits|800px]]&lt;br /&gt;
There are a few different power planes on the board:&lt;br /&gt;
* VIN, 8v to 20v input&lt;br /&gt;
** Feeds power to system 5v and audio 5v&lt;br /&gt;
** Audio 5v will come from system 5v if VIN is not provided&lt;br /&gt;
* System 5v line&lt;br /&gt;
** Provides power to the system 3.3v line and the audio 3.3v line&lt;br /&gt;
** If coming from VIN, this line is provided by a 2A switching regulator, which may have some noise.&lt;br /&gt;
** Is fed through PMOS power switches to downstream USB devices, functions as VUSB.&lt;br /&gt;
* System 3.3v line&lt;br /&gt;
** Provides power to the Teensy 4.1 and the TUSB&lt;br /&gt;
** Provided by the Teensy 4.1's internal 3.3v regulator from the system 5v line&lt;br /&gt;
* System Ground&lt;br /&gt;
** Connected to all circutry, with the exception of some of the audio circutry, which uses the audio ground instead&lt;br /&gt;
* Audio 5v line&lt;br /&gt;
** Provided by either a 5v linear regulator from VIN, or from the system 5v line if VIN is not provided&lt;br /&gt;
** Powers the OPA1688 audio amplifier&lt;br /&gt;
** Is not broken out to the rest of the board&lt;br /&gt;
* Audio 3.3v line&lt;br /&gt;
** Regulated by a linear regulator, getting input from the system 5v line through a ferrite bead&lt;br /&gt;
** Powers the PCM5102 audio ADC&lt;br /&gt;
** Is not broken out elsewhere on the board&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** Connected to System Ground through a ferrite bead&lt;br /&gt;
** Connected to audio circutry&lt;br /&gt;
** Provides the ground used for the 3.5mm audio jack&lt;br /&gt;
** Not broken out elsewhere on the board&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When providing power to the board, a few things should be kept in mind.  First, for high quality audio, either VIN must be provided, or the system 5v line must be clean.  The latter is generally much harder to do, so it is recommended to provide VIN if possible.  For a clean system 5v line, generally it is to provide the system 5v line though a linear regulator instead.  Adding more ceramic capacitors to the system 5v line can help as well.  If providing power from an exteral board through an expansion connector, providing power to both the 5v and VIN lines can boost the maximum current of the system 5v line above 2A.  If more  than 2A is needed, for example for more current-hungry USB devices, then this may be desirable.  Finally, there is a limit on the safe capacitence to be connected to the system 5v line for the 2A switching regulator to work normally.  It is unadvisable to connect more than 800uF of external capacitence to the system 5v line, as it may interfere with that regulator.  Although, there really usually is no reason to connect to much capacitence to that line.&lt;br /&gt;
&lt;br /&gt;
If providing power to the system 5v line from an external source and the Teensy 4.1's USB port simultaneously, it is a good idea to include an ideal diode or similar circuit to prevent backflow from the USB port into your regulator, or vice versa, depending on your regulators characteristics.  For this purpose LM66100 ICs can be used, as are used to protect the onboard switching regulator from the Teensy 4.1's USB port.&lt;br /&gt;
&lt;br /&gt;
'''Capacitence by Power Plane:'''&lt;br /&gt;
&lt;br /&gt;
* VIN:&lt;br /&gt;
** 22uF electrolytic&lt;br /&gt;
** 4.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 26.7uF total&lt;br /&gt;
* System 5v&lt;br /&gt;
** 2.2uF on Teensy 4.1, ceramic&lt;br /&gt;
** 10uF onboard, ceramic&lt;br /&gt;
** Specific to onboard switching regulator: 22uF electrolytic&lt;br /&gt;
** 12.2uF total when external 5v is provided (Teensy 4.1 micro USB, expansion slots directly to 5v rail)&lt;br /&gt;
** 34.2uF total when onboard regulator is used&lt;br /&gt;
* System 3.3v&lt;br /&gt;
** 7.13uF from Teensy 4.1, ceramic&lt;br /&gt;
** 0.3uF onboard for decoupling, ceramic&lt;br /&gt;
** 7.43uF total&lt;br /&gt;
* System Ground&lt;br /&gt;
** 10.3uF ceramic&lt;br /&gt;
** 14.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 44uF electrolytic&lt;br /&gt;
** Approximately 25.13uF on Teensy 4.1, ceramic&lt;br /&gt;
** 94.13uF total&lt;br /&gt;
* Audio 5v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio 3.3v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** 27.2uF ceramic&lt;br /&gt;
** Connected to system ground through ferrite bead - technically connected to those capacitors as well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Expansion Ports ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_expansion_ports.png|thumb|Expansion port layout, not including the card connector port|800px]]&lt;br /&gt;
[[File:Teensy4.1_computer_external_headers_1.png|thumb|Expansion port schematics, excluding the card connector|800px]]&lt;br /&gt;
&lt;br /&gt;
There are three large expansion ports on the Teensy 4.1 computer board, and 5 smaller ports.  Here is a list, describing what each one breaks out:&lt;br /&gt;
* Largest expansion port on the back of the board (J11)&lt;br /&gt;
** Designed for large expansions to the computer system&lt;br /&gt;
** Exposes all power pins, including VIN, 5v, 3.3v, GND, and even the Teensy's VBAT line&lt;br /&gt;
** Numerous Teensy 4.1 pins: 0, 1, 11, 12, 13, 18, 19, 28, 29, 40, 41&lt;br /&gt;
*** UART1&lt;br /&gt;
*** UART7&lt;br /&gt;
*** SPI0&lt;br /&gt;
*** I2C0&lt;br /&gt;
*** Analog inputs: A4, A5, A16, A17&lt;br /&gt;
*** CRX2 and CTX2&lt;br /&gt;
** '''3.3v-compatible''' 8-bit parallel bus&lt;br /&gt;
*** Particularly useful for LCD displays and LCD display controllers, or any other parallel peripheral, for very high speeds&lt;br /&gt;
* Right-side expansion port (J12 - reference not shown on board revision 1.1)&lt;br /&gt;
** Designed for ATX-compatible expansions to the board&lt;br /&gt;
** Exposes 5v and 3.3v lines&lt;br /&gt;
** '''3.3v-compatible''' 8-bit parallel bus&lt;br /&gt;
** Several Teensy 4.1 pins: 6, 7, 8, 9, 18, 19, 32&lt;br /&gt;
*** UART2&lt;br /&gt;
*** I2C0&lt;br /&gt;
*** Analog inputs: A4, A5&lt;br /&gt;
* Card Expansion Port&lt;br /&gt;
** Designed to provide a card slot to fit in an ATX case.  The card may provide IO to the front.&lt;br /&gt;
** Provides a '''5v-compatible''' 8-bit parallel port.  The pins are shared with the other parallel ports.&lt;br /&gt;
** The direction of all data pins are controlled by the control lines.  '''Cannot use data line as input and output at the same time'''&lt;br /&gt;
*** Controlled by write, read, and select lines.  Select pin is pin 9, pulling it low will enable communication.&lt;br /&gt;
** Provides only a 5v line.&lt;br /&gt;
* I2S Expansion Port (J10)&lt;br /&gt;
** Provides an alternative to the 3.5mm audio output, and allows one to attach a microphone&lt;br /&gt;
** Breaks out I2S port 2&lt;br /&gt;
** Can be used to add a microphone alongside use of the 3.5mm audio jack&lt;br /&gt;
** Provides all power rails: VIN, 5v and 3.3v.&lt;br /&gt;
** Breaks out pins 16 and 17&lt;br /&gt;
*** I2C1&lt;br /&gt;
*** Analog pins A2 and A3&lt;br /&gt;
*** UART4&lt;br /&gt;
* SPI0 Port (J9)&lt;br /&gt;
** Provided for SPI devices.  Designed so that a card can be inserted to provide IO to the front of the board.&lt;br /&gt;
** Breaks out pins 10-13&lt;br /&gt;
** Provides 3.3v, but not 5v&lt;br /&gt;
** Breaks out SPI0&lt;br /&gt;
* UART6 Port&lt;br /&gt;
** This is a small port breaking out pins 24 and 25&lt;br /&gt;
*** UART6&lt;br /&gt;
*** Analog pins A10 and A11&lt;br /&gt;
*** I2C2&lt;br /&gt;
** The port is designed so another board can provide IO to the front.&lt;br /&gt;
** The pinout is designed to be compatible with Sparkfun devices and serial pinouts&lt;br /&gt;
** Can be used also as an additional I2C port, and has the necessary 4.7k resistors connected&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
* I2C1 Port&lt;br /&gt;
** This is a small port breaking out pins 16 and 17&lt;br /&gt;
*** UART4&lt;br /&gt;
*** Analog pins A2 and A3&lt;br /&gt;
*** I2C1&lt;br /&gt;
** Has the necessary 4.7k resistors connected&lt;br /&gt;
** The port is designed for internal devices, and is not designed to provide front-facing IO.&lt;br /&gt;
** Can be used also as an additional UART port&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
* UART3 Port&lt;br /&gt;
** This is a small port breaking out pins 14 and 15&lt;br /&gt;
*** UART3&lt;br /&gt;
*** Analog pins A0 and A1&lt;br /&gt;
** The port is designed for internal devices, and is not designed to provide front-facing IO.&lt;br /&gt;
** The pinout is designed to be compatible with Sparkfun devices and serial pinouts&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
&lt;br /&gt;
=== About the Parallel Port ===&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_card_connector_diagram.png|thumb|Card Connector circuit diagram and pinout|500px]]&lt;br /&gt;
&lt;br /&gt;
All three ports that provide parallel share the parallel pins WR, RD, and WAIT, as well as the busses ADDR and DATA.  Each connected parallel device should use a separate select pin, however, with pin 9 being reserved as the card connector's select pin.  WR, RD, and WAIT are all active low, and it is expected that all select lines also be active low.  The address bus (ADDR, shown on the board as A0, A1, and A2) has three pins, allowing for three addresses per device.  A parallel port is important to have because of the high bandwidth it provides.  The Teensy can write this port extremely fast, thus allowing better framerates and draw times for connected graphics devices.  I imagine there are other uses for this port as well.&lt;br /&gt;
&lt;br /&gt;
As ADDR, WR, RD, and any select pins are all output-only from the Teensy, '''there is no level-shifting logic for them.'''  For the WAIT line, there is a simple resistor voltage divider to take the 5v from the card connector down to 3.3v.  There is only level-shifting logic provided for data lines on the card connector, so that 5v TTL devices and levels can be used with the card connector out of the box.&lt;br /&gt;
&lt;br /&gt;
The card connector was designed to interface with cards for the Z80 computer I previously built, and has a compatible pinout.&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=File:Teensy4.1_computer_card_connector_diagram.png&amp;diff=42</id>
		<title>File:Teensy4.1 computer card connector diagram.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=File:Teensy4.1_computer_card_connector_diagram.png&amp;diff=42"/>
		<updated>2024-06-13T21:50:14Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=File:Teensy4.1_computer_external_headers_1.png&amp;diff=41</id>
		<title>File:Teensy4.1 computer external headers 1.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=File:Teensy4.1_computer_external_headers_1.png&amp;diff=41"/>
		<updated>2024-06-13T21:48:39Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=40</id>
		<title>Teensy 4.1 Computer</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=40"/>
		<updated>2024-06-13T21:47:33Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Added information about the parallel port&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Teensy 4.1 Computer is more or less described by its name: Its a carrier board for the Teensy 4.1 which provides four USB ports, an Ethernet port, Hi-Fi audio, and several convenient expansion ports.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
'''USB:'''&lt;br /&gt;
&lt;br /&gt;
* TUSB2046 USB Hub, providing 4 USB A ports&lt;br /&gt;
* Each port has a maximum of 1.5A output - the port will shutdown due to overcurrent above this&lt;br /&gt;
* Board power provides a maximum of 2A to the 5v supply&lt;br /&gt;
** one should be aware of this when considering the power used by USB devices&lt;br /&gt;
&lt;br /&gt;
'''Audio:'''&lt;br /&gt;
&lt;br /&gt;
* I2S control connected to Teensy I2S 2&lt;br /&gt;
* PCM5102 ADC for providing stereo sound&lt;br /&gt;
* OPA1688 for the amplifier&lt;br /&gt;
* 3.5mm audio jack, maximum 75mA per channel&lt;br /&gt;
* Separate ground, connected to main ground through a ferrite bead&lt;br /&gt;
* Separate 3.3v and 5v supplies from linear regulators to reduce noise&lt;br /&gt;
** Note: 5v line will come from main 5v rail if only powered from USB&lt;br /&gt;
&lt;br /&gt;
'''Power:'''&lt;br /&gt;
&lt;br /&gt;
* 2A 5v buck regulator (Exact part is the VR20S05)&lt;br /&gt;
* PMOS switches to prevent regulator backfeeding from USB and to control power flow (U16, U17, and U18 on the PCB)&lt;br /&gt;
* External VIN accepted from 8v to 20v&lt;br /&gt;
* Separate 5v and 3.3v supplies for audio circutry&lt;br /&gt;
* Barrel jack and screw terminal connections for VIN&lt;br /&gt;
&lt;br /&gt;
'''Expansion Ports'''&lt;br /&gt;
&lt;br /&gt;
* I2S, SPI, I2C, and Serial expansion ports&lt;br /&gt;
* 5v-capable 8-bit parallel bus port, with 3-bit address, WR, RD, select, and WAIT lines&lt;br /&gt;
* Two larger expansion ports, each providing a '''3.3v compatible''' 8-bit parallel bus, various protocols like I2C and SPI, power, and GPIO&lt;br /&gt;
&lt;br /&gt;
== The Audio Circuit ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy41_Computer_Audio_circuit.png|thumb|Teensy 4.1 Computer audio circuit|600px]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1 is connected to the audio circuit via the Teensy's I2S 2 port.  This connection goes to the PCM5102 ADC.  As is recommended in the PCM5102's datasheet, filtering is added to the output, however the filter used on this board is a bit stronger, using a 4.7nF capacitor instead of the 2.2nF capacitor recommended in the datasheet.  The filter cutoff is still far above human perception, at 72kHz, and should filter high frequency noise a bit better.  The connection to the OPA1688 is DC, not AC, coupled, but this is according to the recommendations in the OPA1688 datasheet.  The gain of the amplifier is configured to be 1, so the output voltage of the amp should match the ADC's output voltage, and be in the range of 0v to 3.3v.  A voltage divider is used to activate the PCM5102's soft mute function when the 5v line drops, which is designed to prevent any pops in the audio when the power is disconnected.&lt;br /&gt;
&lt;br /&gt;
The circuit was designed with audio quality, but not energy efficiency, in mind.  The circuit may sink as much as 400mA from VIN, and an additional 40mA from the main 5v line.  However, when the audio circuit is idle, it should be less than 50mA all together.  The 3.5mm audio jack can provide a maximum of 75mA per channel.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;Audio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;SD.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// GUItool: begin automatically generated code&lt;br /&gt;
AudioPlaySdWav           playSdWav1;     //xy=719,389&lt;br /&gt;
AudioOutputI2S2          i2s2_1;         //xy=892,388&lt;br /&gt;
AudioConnection          patchCord1(playSdWav1, 0, i2s2_1, 0);&lt;br /&gt;
AudioConnection          patchCord2(playSdWav1, 1, i2s2_1, 1);&lt;br /&gt;
// GUItool: end automatically generated code&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  AudioMemory(10);&lt;br /&gt;
&lt;br /&gt;
  if (!(SD.begin(BUILTIN_SDCARD))) {&lt;br /&gt;
    // stop here, but print a message repetitively&lt;br /&gt;
    while (1) {&lt;br /&gt;
      Serial.println(&amp;quot;Unable to access the SD card&amp;quot;);&lt;br /&gt;
      delay(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Serial.println(&amp;quot;Playing audio.wav...&amp;quot;);&lt;br /&gt;
  playSdWav1.play(&amp;quot;audio.wav&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // Do nothing forever&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== USB Ports ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_USB_schematic.png|thumb|Teensy 4.1 Computer USB circuit|600px]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1's builtin USB port is connected directly to a TUSB2046 IC, which is a USB hub and provides four downstream ports.  Each port has overcurrent protection, with a maximum allowed current of 1.5A.  There are also TVS diodes on all USB data lines on the downstream ports, provided by three TPD3E001DRLR ICs.  Other than that, each downstream USB port has two 15k pulldowns - one on each data line.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&lt;br /&gt;
The best example is found in the Mouse_KeyboardBT example from the USBHost_t36 library, which is included in the TeensyDuino package.  For convenience, I have included a stripped down version of it below:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;quot;USBHost_t36.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
USBHost myusb;&lt;br /&gt;
USBHub hub1(myusb);&lt;br /&gt;
USBHub hub2(myusb);&lt;br /&gt;
KeyboardController keyboard1(myusb);&lt;br /&gt;
KeyboardController keyboard2(myusb);&lt;br /&gt;
USBHIDParser hid1(myusb);&lt;br /&gt;
USBHIDParser hid2(myusb);&lt;br /&gt;
USBHIDParser hid3(myusb);&lt;br /&gt;
USBHIDParser hid4(myusb);&lt;br /&gt;
USBHIDParser hid5(myusb);&lt;br /&gt;
MouseController mouse1(myusb);&lt;br /&gt;
&lt;br /&gt;
BluetoothController bluet(myusb);   // version assumes it already was paired&lt;br /&gt;
&lt;br /&gt;
USBDriver *drivers[] = {&amp;amp;hub1, &amp;amp;hub2, &amp;amp;keyboard1, &amp;amp;keyboard2, &amp;amp;bluet, &amp;amp;hid1, &amp;amp;hid2, &amp;amp;hid3, &amp;amp;hid4, &amp;amp;hid5};&lt;br /&gt;
&lt;br /&gt;
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))&lt;br /&gt;
const char * driver_names[CNT_DEVICES] = {&amp;quot;Hub1&amp;quot;, &amp;quot;Hub2&amp;quot;, &amp;quot;KB1&amp;quot;, &amp;quot;KB2&amp;quot;, &amp;quot;Bluet&amp;quot;, &amp;quot;HID1&amp;quot; , &amp;quot;HID2&amp;quot;, &amp;quot;HID3&amp;quot;, &amp;quot;HID4&amp;quot;, &amp;quot;HID5&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool driver_active[CNT_DEVICES] = {false, false, false, false, false, false, false, false, false, false};&lt;br /&gt;
&lt;br /&gt;
// Lets also look at HID Input devices&lt;br /&gt;
USBHIDInput *hiddrivers[] = {&amp;amp;mouse1};&lt;br /&gt;
&lt;br /&gt;
#define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0]))&lt;br /&gt;
const char * hid_driver_names[CNT_HIDDEVICES] = {&amp;quot;Mouse1&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool hid_driver_active[CNT_HIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
BTHIDInput *bthiddrivers[] = {&amp;amp;keyboard1, &amp;amp;mouse1};&lt;br /&gt;
#define CNT_BTHIDDEVICES (sizeof(bthiddrivers)/sizeof(bthiddrivers[0]))&lt;br /&gt;
const char * bthid_driver_names[CNT_BTHIDDEVICES] = {&amp;quot;Keyboard(BT)&amp;quot;, &amp;quot;Mouse(BT)&amp;quot;};&lt;br /&gt;
bool bthid_driver_active[CNT_BTHIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
bool show_changed_only = false;&lt;br /&gt;
&lt;br /&gt;
uint8_t joystick_left_trigger_value = 0;&lt;br /&gt;
uint8_t joystick_right_trigger_value = 0;&lt;br /&gt;
uint64_t joystick_full_notify_mask = (uint64_t) - 1;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  while (!Serial); // wait for Arduino Serial Monitor&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  myusb.begin();&lt;br /&gt;
  keyboard1.attachPress(OnPress);&lt;br /&gt;
  keyboard2.attachPress(OnPress);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // check to see if the device list has changed:&lt;br /&gt;
  UpdateActiveDeviceInfo();&lt;br /&gt;
&lt;br /&gt;
  myusb.Task();&lt;br /&gt;
&lt;br /&gt;
  if (mouse1.available()) {&lt;br /&gt;
    Serial.print(&amp;quot;Mouse: buttons = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getButtons());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseX = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseX());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseY = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseY());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheel = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheel());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheelH = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheelH());&lt;br /&gt;
    Serial.println();&lt;br /&gt;
    mouse1.mouseDataClear();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void OnPress(int key)&lt;br /&gt;
{&lt;br /&gt;
  Serial.print(&amp;quot;key '&amp;quot;);&lt;br /&gt;
  switch (key) {&lt;br /&gt;
    case KEYD_UP       : Serial.print(&amp;quot;UP&amp;quot;); break;&lt;br /&gt;
    case KEYD_DOWN     : Serial.print(&amp;quot;DN&amp;quot;); break;&lt;br /&gt;
    case KEYD_LEFT     : Serial.print(&amp;quot;LEFT&amp;quot;); break;&lt;br /&gt;
    case KEYD_RIGHT    : Serial.print(&amp;quot;RIGHT&amp;quot;); break;&lt;br /&gt;
    case KEYD_INSERT   : Serial.print(&amp;quot;Ins&amp;quot;); break;&lt;br /&gt;
    case KEYD_DELETE   : Serial.print(&amp;quot;Del&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_UP  : Serial.print(&amp;quot;PUP&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_DOWN: Serial.print(&amp;quot;PDN&amp;quot;); break;&lt;br /&gt;
    case KEYD_HOME     : Serial.print(&amp;quot;HOME&amp;quot;); break;&lt;br /&gt;
    case KEYD_END      : Serial.print(&amp;quot;END&amp;quot;); break;&lt;br /&gt;
    case KEYD_F1       : Serial.print(&amp;quot;F1&amp;quot;); break;&lt;br /&gt;
    case KEYD_F2       : Serial.print(&amp;quot;F2&amp;quot;); break;&lt;br /&gt;
    case KEYD_F3       : Serial.print(&amp;quot;F3&amp;quot;); break;&lt;br /&gt;
    case KEYD_F4       : Serial.print(&amp;quot;F4&amp;quot;); break;&lt;br /&gt;
    case KEYD_F5       : Serial.print(&amp;quot;F5&amp;quot;); break;&lt;br /&gt;
    case KEYD_F6       : Serial.print(&amp;quot;F6&amp;quot;); break;&lt;br /&gt;
    case KEYD_F7       : Serial.print(&amp;quot;F7&amp;quot;); break;&lt;br /&gt;
    case KEYD_F8       : Serial.print(&amp;quot;F8&amp;quot;); break;&lt;br /&gt;
    case KEYD_F9       : Serial.print(&amp;quot;F9&amp;quot;); break;&lt;br /&gt;
    case KEYD_F10      : Serial.print(&amp;quot;F10&amp;quot;); break;&lt;br /&gt;
    case KEYD_F11      : Serial.print(&amp;quot;F11&amp;quot;); break;&lt;br /&gt;
    case KEYD_F12      : Serial.print(&amp;quot;F12&amp;quot;); break;&lt;br /&gt;
    default: Serial.print((char)key); break;&lt;br /&gt;
  }&lt;br /&gt;
  Serial.print(&amp;quot;'  &amp;quot;);&lt;br /&gt;
  Serial.println(key);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//=============================================================================&lt;br /&gt;
// UpdateActiveDeviceInfo&lt;br /&gt;
//=============================================================================&lt;br /&gt;
void UpdateActiveDeviceInfo() {&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_DEVICES; i++) {&lt;br /&gt;
    if (*drivers[i] != driver_active[i]) {&lt;br /&gt;
      if (driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s - disconnected ***\n&amp;quot;, driver_names[i]);&lt;br /&gt;
        driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s %x:%x - connected ***\n&amp;quot;, driver_names[i], drivers[i]-&amp;gt;idVendor(), drivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = drivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
&lt;br /&gt;
        if (drivers[i] == &amp;amp;bluet) {&lt;br /&gt;
          const uint8_t *bdaddr = bluet.myBDAddr();&lt;br /&gt;
          // remember it...&lt;br /&gt;
          Serial.printf(&amp;quot;  BDADDR: %x:%x:%x:%x:%x:%x\n&amp;quot;, bdaddr[0], bdaddr[1], bdaddr[2], bdaddr[3], bdaddr[4], bdaddr[5]);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_HIDDEVICES; i++) {&lt;br /&gt;
    if (*hiddrivers[i] != hid_driver_active[i]) {&lt;br /&gt;
      if (hid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s - disconnected ***\n&amp;quot;, hid_driver_names[i]);&lt;br /&gt;
        hid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s %x:%x - connected ***\n&amp;quot;, hid_driver_names[i], hiddrivers[i]-&amp;gt;idVendor(), hiddrivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        hid_driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = hiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  // Then Bluetooth devices&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_BTHIDDEVICES; i++) {&lt;br /&gt;
    if (*bthiddrivers[i] != bthid_driver_active[i]) {&lt;br /&gt;
      if (bthid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - disconnected ***\n&amp;quot;, bthid_driver_names[i]);&lt;br /&gt;
        bthid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - connected ***\n&amp;quot;, bthid_driver_names[i]); Serial.flush();&lt;br /&gt;
        bthid_driver_active[i] = true;&lt;br /&gt;
        #if 0&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = bthiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
    #endif  &lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Known Issues:'''&lt;br /&gt;
* Version 1.1 of the board leaves TUSB2046's reset line floating.  This can be remedied by connecting the TUSB2046's reset line to some other circuit, although this is error prone.&lt;br /&gt;
&lt;br /&gt;
== Power ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_power_circuit.png|thumb|Teensy 4.1 Computer power circuitry|500px]]&lt;br /&gt;
&lt;br /&gt;
The computer's power supply accepts input from four primary sources:&lt;br /&gt;
* Barrel jack DC input, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Screw terminal block, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Teensy 4.1 micro USB port, 5v input, current limit depends on USB power source&lt;br /&gt;
* Expansion ports&lt;br /&gt;
** Direct to 5v line, only limited by source current.  This allows one to surpass the typical 2A limit on the 5v line.&lt;br /&gt;
** Direct to VIN, accepts 8v to 20v.  5v line limited to 2A.&lt;br /&gt;
** Direct to 3.3v line, only limited by source current.  This can be used to relieve stress from the Teensy 4.1's 3.3v regulator, if needed.&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_audio_power.png|thumb|Teensy 4.1 Computer audio power circuits|800px]]&lt;br /&gt;
There are a few different power planes on the board:&lt;br /&gt;
* VIN, 8v to 20v input&lt;br /&gt;
** Feeds power to system 5v and audio 5v&lt;br /&gt;
** Audio 5v will come from system 5v if VIN is not provided&lt;br /&gt;
* System 5v line&lt;br /&gt;
** Provides power to the system 3.3v line and the audio 3.3v line&lt;br /&gt;
** If coming from VIN, this line is provided by a 2A switching regulator, which may have some noise.&lt;br /&gt;
** Is fed through PMOS power switches to downstream USB devices, functions as VUSB.&lt;br /&gt;
* System 3.3v line&lt;br /&gt;
** Provides power to the Teensy 4.1 and the TUSB&lt;br /&gt;
** Provided by the Teensy 4.1's internal 3.3v regulator from the system 5v line&lt;br /&gt;
* System Ground&lt;br /&gt;
** Connected to all circutry, with the exception of some of the audio circutry, which uses the audio ground instead&lt;br /&gt;
* Audio 5v line&lt;br /&gt;
** Provided by either a 5v linear regulator from VIN, or from the system 5v line if VIN is not provided&lt;br /&gt;
** Powers the OPA1688 audio amplifier&lt;br /&gt;
** Is not broken out to the rest of the board&lt;br /&gt;
* Audio 3.3v line&lt;br /&gt;
** Regulated by a linear regulator, getting input from the system 5v line through a ferrite bead&lt;br /&gt;
** Powers the PCM5102 audio ADC&lt;br /&gt;
** Is not broken out elsewhere on the board&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** Connected to System Ground through a ferrite bead&lt;br /&gt;
** Connected to audio circutry&lt;br /&gt;
** Provides the ground used for the 3.5mm audio jack&lt;br /&gt;
** Not broken out elsewhere on the board&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When providing power to the board, a few things should be kept in mind.  First, for high quality audio, either VIN must be provided, or the system 5v line must be clean.  The latter is generally much harder to do, so it is recommended to provide VIN if possible.  For a clean system 5v line, generally it is to provide the system 5v line though a linear regulator instead.  Adding more ceramic capacitors to the system 5v line can help as well.  If providing power from an exteral board through an expansion connector, providing power to both the 5v and VIN lines can boost the maximum current of the system 5v line above 2A.  If more  than 2A is needed, for example for more current-hungry USB devices, then this may be desirable.  Finally, there is a limit on the safe capacitence to be connected to the system 5v line for the 2A switching regulator to work normally.  It is unadvisable to connect more than 800uF of external capacitence to the system 5v line, as it may interfere with that regulator.  Although, there really usually is no reason to connect to much capacitence to that line.&lt;br /&gt;
&lt;br /&gt;
If providing power to the system 5v line from an external source and the Teensy 4.1's USB port simultaneously, it is a good idea to include an ideal diode or similar circuit to prevent backflow from the USB port into your regulator, or vice versa, depending on your regulators characteristics.  For this purpose LM66100 ICs can be used, as are used to protect the onboard switching regulator from the Teensy 4.1's USB port.&lt;br /&gt;
&lt;br /&gt;
'''Capacitence by Power Plane:'''&lt;br /&gt;
&lt;br /&gt;
* VIN:&lt;br /&gt;
** 22uF electrolytic&lt;br /&gt;
** 4.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 26.7uF total&lt;br /&gt;
* System 5v&lt;br /&gt;
** 2.2uF on Teensy 4.1, ceramic&lt;br /&gt;
** 10uF onboard, ceramic&lt;br /&gt;
** Specific to onboard switching regulator: 22uF electrolytic&lt;br /&gt;
** 12.2uF total when external 5v is provided (Teensy 4.1 micro USB, expansion slots directly to 5v rail)&lt;br /&gt;
** 34.2uF total when onboard regulator is used&lt;br /&gt;
* System 3.3v&lt;br /&gt;
** 7.13uF from Teensy 4.1, ceramic&lt;br /&gt;
** 0.3uF onboard for decoupling, ceramic&lt;br /&gt;
** 7.43uF total&lt;br /&gt;
* System Ground&lt;br /&gt;
** 10.3uF ceramic&lt;br /&gt;
** 14.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 44uF electrolytic&lt;br /&gt;
** Approximately 25.13uF on Teensy 4.1, ceramic&lt;br /&gt;
** 94.13uF total&lt;br /&gt;
* Audio 5v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio 3.3v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** 27.2uF ceramic&lt;br /&gt;
** Connected to system ground through ferrite bead - technically connected to those capacitors as well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Expansion Ports ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_expansion_ports.png|thumb|Expansion port layout, not including the card connector port|800px]]&lt;br /&gt;
&lt;br /&gt;
There are three large expansion ports on the Teensy 4.1 computer board, and 5 smaller ports.  Here is a list, describing what each one breaks out:&lt;br /&gt;
* Largest expansion port on the back of the board (J11)&lt;br /&gt;
** Designed for large expansions to the computer system&lt;br /&gt;
** Exposes all power pins, including VIN, 5v, 3.3v, GND, and even the Teensy's VBAT line&lt;br /&gt;
** Numerous Teensy 4.1 pins: 0, 1, 11, 12, 13, 18, 19, 28, 29, 40, 41&lt;br /&gt;
*** UART1&lt;br /&gt;
*** UART7&lt;br /&gt;
*** SPI0&lt;br /&gt;
*** I2C0&lt;br /&gt;
*** Analog inputs: A4, A5, A16, A17&lt;br /&gt;
*** CRX2 and CTX2&lt;br /&gt;
** '''3.3v-compatible''' 8-bit parallel bus&lt;br /&gt;
*** Particularly useful for LCD displays and LCD display controllers, or any other parallel peripheral, for very high speeds&lt;br /&gt;
* Right-side expansion port (J12 - reference not shown on board revision 1.1)&lt;br /&gt;
** Designed for ATX-compatible expansions to the board&lt;br /&gt;
** Exposes 5v and 3.3v lines&lt;br /&gt;
** '''3.3v-compatible''' 8-bit parallel bus&lt;br /&gt;
** Several Teensy 4.1 pins: 6, 7, 8, 9, 18, 19, 32&lt;br /&gt;
*** UART2&lt;br /&gt;
*** I2C0&lt;br /&gt;
*** Analog inputs: A4, A5&lt;br /&gt;
* Card Expansion Port&lt;br /&gt;
** Designed to provide a card slot to fit in an ATX case.  The card may provide IO to the front.&lt;br /&gt;
** Provides a '''5v-compatible''' 8-bit parallel port.  The pins are shared with the other parallel ports.&lt;br /&gt;
** The direction of all data pins are controlled by the control lines.  '''Cannot use data line as input and output at the same time'''&lt;br /&gt;
*** Controlled by write, read, and select lines.  Select pin is pin 9, pulling it low will enable communication.&lt;br /&gt;
** Provides only a 5v line.&lt;br /&gt;
* I2S Expansion Port (J10)&lt;br /&gt;
** Provides an alternative to the 3.5mm audio output, and allows one to attach a microphone&lt;br /&gt;
** Breaks out I2S port 2&lt;br /&gt;
** Can be used to add a microphone alongside use of the 3.5mm audio jack&lt;br /&gt;
** Provides all power rails: VIN, 5v and 3.3v.&lt;br /&gt;
** Breaks out pins 16 and 17&lt;br /&gt;
*** I2C1&lt;br /&gt;
*** Analog pins A2 and A3&lt;br /&gt;
*** UART4&lt;br /&gt;
* SPI0 Port (J9)&lt;br /&gt;
** Provided for SPI devices.  Designed so that a card can be inserted to provide IO to the front of the board.&lt;br /&gt;
** Breaks out pins 10-13&lt;br /&gt;
** Provides 3.3v, but not 5v&lt;br /&gt;
** Breaks out SPI0&lt;br /&gt;
* UART6 Port&lt;br /&gt;
** This is a small port breaking out pins 24 and 25&lt;br /&gt;
*** UART6&lt;br /&gt;
*** Analog pins A10 and A11&lt;br /&gt;
*** I2C2&lt;br /&gt;
** The port is designed so another board can provide IO to the front.&lt;br /&gt;
** The pinout is designed to be compatible with Sparkfun devices and serial pinouts&lt;br /&gt;
** Can be used also as an additional I2C port, and has the necessary 4.7k resistors connected&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
* I2C1 Port&lt;br /&gt;
** This is a small port breaking out pins 16 and 17&lt;br /&gt;
*** UART4&lt;br /&gt;
*** Analog pins A2 and A3&lt;br /&gt;
*** I2C1&lt;br /&gt;
** Has the necessary 4.7k resistors connected&lt;br /&gt;
** The port is designed for internal devices, and is not designed to provide front-facing IO.&lt;br /&gt;
** Can be used also as an additional UART port&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
* UART3 Port&lt;br /&gt;
** This is a small port breaking out pins 14 and 15&lt;br /&gt;
*** UART3&lt;br /&gt;
*** Analog pins A0 and A1&lt;br /&gt;
** The port is designed for internal devices, and is not designed to provide front-facing IO.&lt;br /&gt;
** The pinout is designed to be compatible with Sparkfun devices and serial pinouts&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
&lt;br /&gt;
=== About the Parallel Port ===&lt;br /&gt;
&lt;br /&gt;
All three ports that provide parallel share the parallel pins WR, RD, and WAIT, as well as the busses ADDR and DATA.  Each connected parallel device should use a separate select pin, however, with pin 9 being reserved as the card connector's select pin.  WR, RD, and WAIT are all active low, and it is expected that all select lines also be active low.  The address bus (ADDR, shown on the board as A0, A1, and A2) has three pins, allowing for three addresses per device.  A parallel port is important to have because of the high bandwidth it provides.  The Teensy can write this port extremely fast, thus allowing better framerates and draw times for connected graphics devices.  I imagine there are other uses for this port as well.&lt;br /&gt;
&lt;br /&gt;
As ADDR, WR, RD, and any select pins are all output-only from the Teensy, '''there is no level-shifting logic for them.'''  For the WAIT line, there is a simple resistor voltage divider to take the 5v from the card connector down to 3.3v.  There is only level-shifting logic provided for data lines on the card connector, so that 5v TTL devices and levels can be used with the card connector out of the box.&lt;br /&gt;
&lt;br /&gt;
The card connector was designed to interface with cards for the Z80 computer I previously built, and has a compatible pinout.&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=39</id>
		<title>Teensy 4.1 Computer</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=39"/>
		<updated>2024-06-13T21:40:45Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: More expansion port docs&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Teensy 4.1 Computer is more or less described by its name: Its a carrier board for the Teensy 4.1 which provides four USB ports, an Ethernet port, Hi-Fi audio, and several convenient expansion ports.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
'''USB:'''&lt;br /&gt;
&lt;br /&gt;
* TUSB2046 USB Hub, providing 4 USB A ports&lt;br /&gt;
* Each port has a maximum of 1.5A output - the port will shutdown due to overcurrent above this&lt;br /&gt;
* Board power provides a maximum of 2A to the 5v supply&lt;br /&gt;
** one should be aware of this when considering the power used by USB devices&lt;br /&gt;
&lt;br /&gt;
'''Audio:'''&lt;br /&gt;
&lt;br /&gt;
* I2S control connected to Teensy I2S 2&lt;br /&gt;
* PCM5102 ADC for providing stereo sound&lt;br /&gt;
* OPA1688 for the amplifier&lt;br /&gt;
* 3.5mm audio jack, maximum 75mA per channel&lt;br /&gt;
* Separate ground, connected to main ground through a ferrite bead&lt;br /&gt;
* Separate 3.3v and 5v supplies from linear regulators to reduce noise&lt;br /&gt;
** Note: 5v line will come from main 5v rail if only powered from USB&lt;br /&gt;
&lt;br /&gt;
'''Power:'''&lt;br /&gt;
&lt;br /&gt;
* 2A 5v buck regulator (Exact part is the VR20S05)&lt;br /&gt;
* PMOS switches to prevent regulator backfeeding from USB and to control power flow (U16, U17, and U18 on the PCB)&lt;br /&gt;
* External VIN accepted from 8v to 20v&lt;br /&gt;
* Separate 5v and 3.3v supplies for audio circutry&lt;br /&gt;
* Barrel jack and screw terminal connections for VIN&lt;br /&gt;
&lt;br /&gt;
'''Expansion Ports'''&lt;br /&gt;
&lt;br /&gt;
* I2S, SPI, I2C, and Serial expansion ports&lt;br /&gt;
* 5v-capable 8-bit parallel bus port, with 3-bit address, WR, RD, select, and WAIT lines&lt;br /&gt;
* Two larger expansion ports, each providing a '''3.3v compatible''' 8-bit parallel bus, various protocols like I2C and SPI, power, and GPIO&lt;br /&gt;
&lt;br /&gt;
== The Audio Circuit ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy41_Computer_Audio_circuit.png|thumb|Teensy 4.1 Computer audio circuit|600px]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1 is connected to the audio circuit via the Teensy's I2S 2 port.  This connection goes to the PCM5102 ADC.  As is recommended in the PCM5102's datasheet, filtering is added to the output, however the filter used on this board is a bit stronger, using a 4.7nF capacitor instead of the 2.2nF capacitor recommended in the datasheet.  The filter cutoff is still far above human perception, at 72kHz, and should filter high frequency noise a bit better.  The connection to the OPA1688 is DC, not AC, coupled, but this is according to the recommendations in the OPA1688 datasheet.  The gain of the amplifier is configured to be 1, so the output voltage of the amp should match the ADC's output voltage, and be in the range of 0v to 3.3v.  A voltage divider is used to activate the PCM5102's soft mute function when the 5v line drops, which is designed to prevent any pops in the audio when the power is disconnected.&lt;br /&gt;
&lt;br /&gt;
The circuit was designed with audio quality, but not energy efficiency, in mind.  The circuit may sink as much as 400mA from VIN, and an additional 40mA from the main 5v line.  However, when the audio circuit is idle, it should be less than 50mA all together.  The 3.5mm audio jack can provide a maximum of 75mA per channel.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;Audio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;SD.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// GUItool: begin automatically generated code&lt;br /&gt;
AudioPlaySdWav           playSdWav1;     //xy=719,389&lt;br /&gt;
AudioOutputI2S2          i2s2_1;         //xy=892,388&lt;br /&gt;
AudioConnection          patchCord1(playSdWav1, 0, i2s2_1, 0);&lt;br /&gt;
AudioConnection          patchCord2(playSdWav1, 1, i2s2_1, 1);&lt;br /&gt;
// GUItool: end automatically generated code&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  AudioMemory(10);&lt;br /&gt;
&lt;br /&gt;
  if (!(SD.begin(BUILTIN_SDCARD))) {&lt;br /&gt;
    // stop here, but print a message repetitively&lt;br /&gt;
    while (1) {&lt;br /&gt;
      Serial.println(&amp;quot;Unable to access the SD card&amp;quot;);&lt;br /&gt;
      delay(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Serial.println(&amp;quot;Playing audio.wav...&amp;quot;);&lt;br /&gt;
  playSdWav1.play(&amp;quot;audio.wav&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // Do nothing forever&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== USB Ports ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_USB_schematic.png|thumb|Teensy 4.1 Computer USB circuit|600px]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1's builtin USB port is connected directly to a TUSB2046 IC, which is a USB hub and provides four downstream ports.  Each port has overcurrent protection, with a maximum allowed current of 1.5A.  There are also TVS diodes on all USB data lines on the downstream ports, provided by three TPD3E001DRLR ICs.  Other than that, each downstream USB port has two 15k pulldowns - one on each data line.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&lt;br /&gt;
The best example is found in the Mouse_KeyboardBT example from the USBHost_t36 library, which is included in the TeensyDuino package.  For convenience, I have included a stripped down version of it below:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;quot;USBHost_t36.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
USBHost myusb;&lt;br /&gt;
USBHub hub1(myusb);&lt;br /&gt;
USBHub hub2(myusb);&lt;br /&gt;
KeyboardController keyboard1(myusb);&lt;br /&gt;
KeyboardController keyboard2(myusb);&lt;br /&gt;
USBHIDParser hid1(myusb);&lt;br /&gt;
USBHIDParser hid2(myusb);&lt;br /&gt;
USBHIDParser hid3(myusb);&lt;br /&gt;
USBHIDParser hid4(myusb);&lt;br /&gt;
USBHIDParser hid5(myusb);&lt;br /&gt;
MouseController mouse1(myusb);&lt;br /&gt;
&lt;br /&gt;
BluetoothController bluet(myusb);   // version assumes it already was paired&lt;br /&gt;
&lt;br /&gt;
USBDriver *drivers[] = {&amp;amp;hub1, &amp;amp;hub2, &amp;amp;keyboard1, &amp;amp;keyboard2, &amp;amp;bluet, &amp;amp;hid1, &amp;amp;hid2, &amp;amp;hid3, &amp;amp;hid4, &amp;amp;hid5};&lt;br /&gt;
&lt;br /&gt;
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))&lt;br /&gt;
const char * driver_names[CNT_DEVICES] = {&amp;quot;Hub1&amp;quot;, &amp;quot;Hub2&amp;quot;, &amp;quot;KB1&amp;quot;, &amp;quot;KB2&amp;quot;, &amp;quot;Bluet&amp;quot;, &amp;quot;HID1&amp;quot; , &amp;quot;HID2&amp;quot;, &amp;quot;HID3&amp;quot;, &amp;quot;HID4&amp;quot;, &amp;quot;HID5&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool driver_active[CNT_DEVICES] = {false, false, false, false, false, false, false, false, false, false};&lt;br /&gt;
&lt;br /&gt;
// Lets also look at HID Input devices&lt;br /&gt;
USBHIDInput *hiddrivers[] = {&amp;amp;mouse1};&lt;br /&gt;
&lt;br /&gt;
#define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0]))&lt;br /&gt;
const char * hid_driver_names[CNT_HIDDEVICES] = {&amp;quot;Mouse1&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool hid_driver_active[CNT_HIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
BTHIDInput *bthiddrivers[] = {&amp;amp;keyboard1, &amp;amp;mouse1};&lt;br /&gt;
#define CNT_BTHIDDEVICES (sizeof(bthiddrivers)/sizeof(bthiddrivers[0]))&lt;br /&gt;
const char * bthid_driver_names[CNT_BTHIDDEVICES] = {&amp;quot;Keyboard(BT)&amp;quot;, &amp;quot;Mouse(BT)&amp;quot;};&lt;br /&gt;
bool bthid_driver_active[CNT_BTHIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
bool show_changed_only = false;&lt;br /&gt;
&lt;br /&gt;
uint8_t joystick_left_trigger_value = 0;&lt;br /&gt;
uint8_t joystick_right_trigger_value = 0;&lt;br /&gt;
uint64_t joystick_full_notify_mask = (uint64_t) - 1;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  while (!Serial); // wait for Arduino Serial Monitor&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  myusb.begin();&lt;br /&gt;
  keyboard1.attachPress(OnPress);&lt;br /&gt;
  keyboard2.attachPress(OnPress);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // check to see if the device list has changed:&lt;br /&gt;
  UpdateActiveDeviceInfo();&lt;br /&gt;
&lt;br /&gt;
  myusb.Task();&lt;br /&gt;
&lt;br /&gt;
  if (mouse1.available()) {&lt;br /&gt;
    Serial.print(&amp;quot;Mouse: buttons = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getButtons());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseX = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseX());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseY = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseY());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheel = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheel());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheelH = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheelH());&lt;br /&gt;
    Serial.println();&lt;br /&gt;
    mouse1.mouseDataClear();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void OnPress(int key)&lt;br /&gt;
{&lt;br /&gt;
  Serial.print(&amp;quot;key '&amp;quot;);&lt;br /&gt;
  switch (key) {&lt;br /&gt;
    case KEYD_UP       : Serial.print(&amp;quot;UP&amp;quot;); break;&lt;br /&gt;
    case KEYD_DOWN     : Serial.print(&amp;quot;DN&amp;quot;); break;&lt;br /&gt;
    case KEYD_LEFT     : Serial.print(&amp;quot;LEFT&amp;quot;); break;&lt;br /&gt;
    case KEYD_RIGHT    : Serial.print(&amp;quot;RIGHT&amp;quot;); break;&lt;br /&gt;
    case KEYD_INSERT   : Serial.print(&amp;quot;Ins&amp;quot;); break;&lt;br /&gt;
    case KEYD_DELETE   : Serial.print(&amp;quot;Del&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_UP  : Serial.print(&amp;quot;PUP&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_DOWN: Serial.print(&amp;quot;PDN&amp;quot;); break;&lt;br /&gt;
    case KEYD_HOME     : Serial.print(&amp;quot;HOME&amp;quot;); break;&lt;br /&gt;
    case KEYD_END      : Serial.print(&amp;quot;END&amp;quot;); break;&lt;br /&gt;
    case KEYD_F1       : Serial.print(&amp;quot;F1&amp;quot;); break;&lt;br /&gt;
    case KEYD_F2       : Serial.print(&amp;quot;F2&amp;quot;); break;&lt;br /&gt;
    case KEYD_F3       : Serial.print(&amp;quot;F3&amp;quot;); break;&lt;br /&gt;
    case KEYD_F4       : Serial.print(&amp;quot;F4&amp;quot;); break;&lt;br /&gt;
    case KEYD_F5       : Serial.print(&amp;quot;F5&amp;quot;); break;&lt;br /&gt;
    case KEYD_F6       : Serial.print(&amp;quot;F6&amp;quot;); break;&lt;br /&gt;
    case KEYD_F7       : Serial.print(&amp;quot;F7&amp;quot;); break;&lt;br /&gt;
    case KEYD_F8       : Serial.print(&amp;quot;F8&amp;quot;); break;&lt;br /&gt;
    case KEYD_F9       : Serial.print(&amp;quot;F9&amp;quot;); break;&lt;br /&gt;
    case KEYD_F10      : Serial.print(&amp;quot;F10&amp;quot;); break;&lt;br /&gt;
    case KEYD_F11      : Serial.print(&amp;quot;F11&amp;quot;); break;&lt;br /&gt;
    case KEYD_F12      : Serial.print(&amp;quot;F12&amp;quot;); break;&lt;br /&gt;
    default: Serial.print((char)key); break;&lt;br /&gt;
  }&lt;br /&gt;
  Serial.print(&amp;quot;'  &amp;quot;);&lt;br /&gt;
  Serial.println(key);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//=============================================================================&lt;br /&gt;
// UpdateActiveDeviceInfo&lt;br /&gt;
//=============================================================================&lt;br /&gt;
void UpdateActiveDeviceInfo() {&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_DEVICES; i++) {&lt;br /&gt;
    if (*drivers[i] != driver_active[i]) {&lt;br /&gt;
      if (driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s - disconnected ***\n&amp;quot;, driver_names[i]);&lt;br /&gt;
        driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s %x:%x - connected ***\n&amp;quot;, driver_names[i], drivers[i]-&amp;gt;idVendor(), drivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = drivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
&lt;br /&gt;
        if (drivers[i] == &amp;amp;bluet) {&lt;br /&gt;
          const uint8_t *bdaddr = bluet.myBDAddr();&lt;br /&gt;
          // remember it...&lt;br /&gt;
          Serial.printf(&amp;quot;  BDADDR: %x:%x:%x:%x:%x:%x\n&amp;quot;, bdaddr[0], bdaddr[1], bdaddr[2], bdaddr[3], bdaddr[4], bdaddr[5]);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_HIDDEVICES; i++) {&lt;br /&gt;
    if (*hiddrivers[i] != hid_driver_active[i]) {&lt;br /&gt;
      if (hid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s - disconnected ***\n&amp;quot;, hid_driver_names[i]);&lt;br /&gt;
        hid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s %x:%x - connected ***\n&amp;quot;, hid_driver_names[i], hiddrivers[i]-&amp;gt;idVendor(), hiddrivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        hid_driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = hiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  // Then Bluetooth devices&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_BTHIDDEVICES; i++) {&lt;br /&gt;
    if (*bthiddrivers[i] != bthid_driver_active[i]) {&lt;br /&gt;
      if (bthid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - disconnected ***\n&amp;quot;, bthid_driver_names[i]);&lt;br /&gt;
        bthid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - connected ***\n&amp;quot;, bthid_driver_names[i]); Serial.flush();&lt;br /&gt;
        bthid_driver_active[i] = true;&lt;br /&gt;
        #if 0&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = bthiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
    #endif  &lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Known Issues:'''&lt;br /&gt;
* Version 1.1 of the board leaves TUSB2046's reset line floating.  This can be remedied by connecting the TUSB2046's reset line to some other circuit, although this is error prone.&lt;br /&gt;
&lt;br /&gt;
== Power ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_power_circuit.png|thumb|Teensy 4.1 Computer power circuitry|500px]]&lt;br /&gt;
&lt;br /&gt;
The computer's power supply accepts input from four primary sources:&lt;br /&gt;
* Barrel jack DC input, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Screw terminal block, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Teensy 4.1 micro USB port, 5v input, current limit depends on USB power source&lt;br /&gt;
* Expansion ports&lt;br /&gt;
** Direct to 5v line, only limited by source current.  This allows one to surpass the typical 2A limit on the 5v line.&lt;br /&gt;
** Direct to VIN, accepts 8v to 20v.  5v line limited to 2A.&lt;br /&gt;
** Direct to 3.3v line, only limited by source current.  This can be used to relieve stress from the Teensy 4.1's 3.3v regulator, if needed.&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_audio_power.png|thumb|Teensy 4.1 Computer audio power circuits|800px]]&lt;br /&gt;
There are a few different power planes on the board:&lt;br /&gt;
* VIN, 8v to 20v input&lt;br /&gt;
** Feeds power to system 5v and audio 5v&lt;br /&gt;
** Audio 5v will come from system 5v if VIN is not provided&lt;br /&gt;
* System 5v line&lt;br /&gt;
** Provides power to the system 3.3v line and the audio 3.3v line&lt;br /&gt;
** If coming from VIN, this line is provided by a 2A switching regulator, which may have some noise.&lt;br /&gt;
** Is fed through PMOS power switches to downstream USB devices, functions as VUSB.&lt;br /&gt;
* System 3.3v line&lt;br /&gt;
** Provides power to the Teensy 4.1 and the TUSB&lt;br /&gt;
** Provided by the Teensy 4.1's internal 3.3v regulator from the system 5v line&lt;br /&gt;
* System Ground&lt;br /&gt;
** Connected to all circutry, with the exception of some of the audio circutry, which uses the audio ground instead&lt;br /&gt;
* Audio 5v line&lt;br /&gt;
** Provided by either a 5v linear regulator from VIN, or from the system 5v line if VIN is not provided&lt;br /&gt;
** Powers the OPA1688 audio amplifier&lt;br /&gt;
** Is not broken out to the rest of the board&lt;br /&gt;
* Audio 3.3v line&lt;br /&gt;
** Regulated by a linear regulator, getting input from the system 5v line through a ferrite bead&lt;br /&gt;
** Powers the PCM5102 audio ADC&lt;br /&gt;
** Is not broken out elsewhere on the board&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** Connected to System Ground through a ferrite bead&lt;br /&gt;
** Connected to audio circutry&lt;br /&gt;
** Provides the ground used for the 3.5mm audio jack&lt;br /&gt;
** Not broken out elsewhere on the board&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When providing power to the board, a few things should be kept in mind.  First, for high quality audio, either VIN must be provided, or the system 5v line must be clean.  The latter is generally much harder to do, so it is recommended to provide VIN if possible.  For a clean system 5v line, generally it is to provide the system 5v line though a linear regulator instead.  Adding more ceramic capacitors to the system 5v line can help as well.  If providing power from an exteral board through an expansion connector, providing power to both the 5v and VIN lines can boost the maximum current of the system 5v line above 2A.  If more  than 2A is needed, for example for more current-hungry USB devices, then this may be desirable.  Finally, there is a limit on the safe capacitence to be connected to the system 5v line for the 2A switching regulator to work normally.  It is unadvisable to connect more than 800uF of external capacitence to the system 5v line, as it may interfere with that regulator.  Although, there really usually is no reason to connect to much capacitence to that line.&lt;br /&gt;
&lt;br /&gt;
If providing power to the system 5v line from an external source and the Teensy 4.1's USB port simultaneously, it is a good idea to include an ideal diode or similar circuit to prevent backflow from the USB port into your regulator, or vice versa, depending on your regulators characteristics.  For this purpose LM66100 ICs can be used, as are used to protect the onboard switching regulator from the Teensy 4.1's USB port.&lt;br /&gt;
&lt;br /&gt;
'''Capacitence by Power Plane:'''&lt;br /&gt;
&lt;br /&gt;
* VIN:&lt;br /&gt;
** 22uF electrolytic&lt;br /&gt;
** 4.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 26.7uF total&lt;br /&gt;
* System 5v&lt;br /&gt;
** 2.2uF on Teensy 4.1, ceramic&lt;br /&gt;
** 10uF onboard, ceramic&lt;br /&gt;
** Specific to onboard switching regulator: 22uF electrolytic&lt;br /&gt;
** 12.2uF total when external 5v is provided (Teensy 4.1 micro USB, expansion slots directly to 5v rail)&lt;br /&gt;
** 34.2uF total when onboard regulator is used&lt;br /&gt;
* System 3.3v&lt;br /&gt;
** 7.13uF from Teensy 4.1, ceramic&lt;br /&gt;
** 0.3uF onboard for decoupling, ceramic&lt;br /&gt;
** 7.43uF total&lt;br /&gt;
* System Ground&lt;br /&gt;
** 10.3uF ceramic&lt;br /&gt;
** 14.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 44uF electrolytic&lt;br /&gt;
** Approximately 25.13uF on Teensy 4.1, ceramic&lt;br /&gt;
** 94.13uF total&lt;br /&gt;
* Audio 5v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio 3.3v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** 27.2uF ceramic&lt;br /&gt;
** Connected to system ground through ferrite bead - technically connected to those capacitors as well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Expansion Ports ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_expansion_ports.png|thumb|Expansion port layout, not including the card connector port|800px]]&lt;br /&gt;
&lt;br /&gt;
There are three large expansion ports on the Teensy 4.1 computer board, and 5 smaller ports.  Here is a list, describing what each one breaks out:&lt;br /&gt;
* Largest expansion port on the back of the board (J11)&lt;br /&gt;
** Designed for large expansions to the computer system&lt;br /&gt;
** Exposes all power pins, including VIN, 5v, 3.3v, GND, and even the Teensy's VBAT line&lt;br /&gt;
** Numerous Teensy 4.1 pins: 0, 1, 11, 12, 13, 18, 19, 28, 29, 40, 41&lt;br /&gt;
*** UART1&lt;br /&gt;
*** UART7&lt;br /&gt;
*** SPI0&lt;br /&gt;
*** I2C0&lt;br /&gt;
*** Analog inputs: A4, A5, A16, A17&lt;br /&gt;
*** CRX2 and CTX2&lt;br /&gt;
** '''3.3v-compatible''' 8-bit parallel bus&lt;br /&gt;
*** Particularly useful for LCD displays and LCD display controllers, or any other parallel peripheral, for very high speeds&lt;br /&gt;
* Right-side expansion port (J12 - reference not shown on board revision 1.1)&lt;br /&gt;
** Designed for ATX-compatible expansions to the board&lt;br /&gt;
** Exposes 5v and 3.3v lines&lt;br /&gt;
** '''3.3v-compatible''' 8-bit parallel bus&lt;br /&gt;
** Several Teensy 4.1 pins: 6, 7, 8, 9, 18, 19, 32&lt;br /&gt;
*** UART2&lt;br /&gt;
*** I2C0&lt;br /&gt;
*** Analog inputs: A4, A5&lt;br /&gt;
* Card Expansion Port&lt;br /&gt;
** Designed to provide a card slot to fit in an ATX case.  The card may provide IO to the front.&lt;br /&gt;
** Provides a '''5v-compatible''' 8-bit parallel port.  The pins are shared with the other parallel ports.&lt;br /&gt;
** The direction of all data pins are controlled by the control lines.  '''Cannot use data line as input and output at the same time'''&lt;br /&gt;
*** Controlled by write, read, and select lines.  Select pin is pin 9, pulling it low will enable communication.&lt;br /&gt;
** Provides only a 5v line.&lt;br /&gt;
* I2S Expansion Port (J10)&lt;br /&gt;
** Provides an alternative to the 3.5mm audio output, and allows one to attach a microphone&lt;br /&gt;
** Breaks out I2S port 2&lt;br /&gt;
** Can be used to add a microphone alongside use of the 3.5mm audio jack&lt;br /&gt;
** Provides all power rails: VIN, 5v and 3.3v.&lt;br /&gt;
** Breaks out pins 16 and 17&lt;br /&gt;
*** I2C1&lt;br /&gt;
*** Analog pins A2 and A3&lt;br /&gt;
*** UART4&lt;br /&gt;
* SPI0 Port (J9)&lt;br /&gt;
** Provided for SPI devices.  Designed so that a card can be inserted to provide IO to the front of the board.&lt;br /&gt;
** Breaks out pins 10-13&lt;br /&gt;
** Provides 3.3v, but not 5v&lt;br /&gt;
** Breaks out SPI0&lt;br /&gt;
* UART6 Port&lt;br /&gt;
** This is a small port breaking out pins 24 and 25&lt;br /&gt;
*** UART6&lt;br /&gt;
*** Analog pins A10 and A11&lt;br /&gt;
*** I2C2&lt;br /&gt;
** The port is designed so another board can provide IO to the front.&lt;br /&gt;
** The pinout is designed to be compatible with Sparkfun devices and serial pinouts&lt;br /&gt;
** Can be used also as an additional I2C port, and has the necessary 4.7k resistors connected&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
* I2C1 Port&lt;br /&gt;
** This is a small port breaking out pins 16 and 17&lt;br /&gt;
*** UART4&lt;br /&gt;
*** Analog pins A2 and A3&lt;br /&gt;
*** I2C1&lt;br /&gt;
** Has the necessary 4.7k resistors connected&lt;br /&gt;
** The port is designed for internal devices, and is not designed to provide front-facing IO.&lt;br /&gt;
** Can be used also as an additional UART port&lt;br /&gt;
** Provides 3.3v and ground&lt;br /&gt;
* UART3 Port&lt;br /&gt;
** This is a small port breaking out pins 14 and 15&lt;br /&gt;
*** UART3&lt;br /&gt;
*** Analog pins A0 and A1&lt;br /&gt;
** The port is designed for internal devices, and is not designed to provide front-facing IO.&lt;br /&gt;
** The pinout is designed to be compatible with Sparkfun devices and serial pinouts&lt;br /&gt;
** Provides 3.3v and ground&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=38</id>
		<title>Teensy 4.1 Computer</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=38"/>
		<updated>2024-06-13T21:29:10Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Saving work on the expansion port documentation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Teensy 4.1 Computer is more or less described by its name: Its a carrier board for the Teensy 4.1 which provides four USB ports, an Ethernet port, Hi-Fi audio, and several convenient expansion ports.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
'''USB:'''&lt;br /&gt;
&lt;br /&gt;
* TUSB2046 USB Hub, providing 4 USB A ports&lt;br /&gt;
* Each port has a maximum of 1.5A output - the port will shutdown due to overcurrent above this&lt;br /&gt;
* Board power provides a maximum of 2A to the 5v supply&lt;br /&gt;
** one should be aware of this when considering the power used by USB devices&lt;br /&gt;
&lt;br /&gt;
'''Audio:'''&lt;br /&gt;
&lt;br /&gt;
* I2S control connected to Teensy I2S 2&lt;br /&gt;
* PCM5102 ADC for providing stereo sound&lt;br /&gt;
* OPA1688 for the amplifier&lt;br /&gt;
* 3.5mm audio jack, maximum 75mA per channel&lt;br /&gt;
* Separate ground, connected to main ground through a ferrite bead&lt;br /&gt;
* Separate 3.3v and 5v supplies from linear regulators to reduce noise&lt;br /&gt;
** Note: 5v line will come from main 5v rail if only powered from USB&lt;br /&gt;
&lt;br /&gt;
'''Power:'''&lt;br /&gt;
&lt;br /&gt;
* 2A 5v buck regulator (Exact part is the VR20S05)&lt;br /&gt;
* PMOS switches to prevent regulator backfeeding from USB and to control power flow (U16, U17, and U18 on the PCB)&lt;br /&gt;
* External VIN accepted from 8v to 20v&lt;br /&gt;
* Separate 5v and 3.3v supplies for audio circutry&lt;br /&gt;
* Barrel jack and screw terminal connections for VIN&lt;br /&gt;
&lt;br /&gt;
'''Expansion Ports'''&lt;br /&gt;
&lt;br /&gt;
* I2S, SPI, I2C, and Serial expansion ports&lt;br /&gt;
* 5v-capable 8-bit parallel bus port, with 3-bit address, WR, RD, select, and WAIT lines&lt;br /&gt;
* Two larger expansion ports, each providing a '''3.3v compatible''' 8-bit parallel bus, various protocols like I2C and SPI, power, and GPIO&lt;br /&gt;
&lt;br /&gt;
== The Audio Circuit ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy41_Computer_Audio_circuit.png|thumb|Teensy 4.1 Computer audio circuit|600px]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1 is connected to the audio circuit via the Teensy's I2S 2 port.  This connection goes to the PCM5102 ADC.  As is recommended in the PCM5102's datasheet, filtering is added to the output, however the filter used on this board is a bit stronger, using a 4.7nF capacitor instead of the 2.2nF capacitor recommended in the datasheet.  The filter cutoff is still far above human perception, at 72kHz, and should filter high frequency noise a bit better.  The connection to the OPA1688 is DC, not AC, coupled, but this is according to the recommendations in the OPA1688 datasheet.  The gain of the amplifier is configured to be 1, so the output voltage of the amp should match the ADC's output voltage, and be in the range of 0v to 3.3v.  A voltage divider is used to activate the PCM5102's soft mute function when the 5v line drops, which is designed to prevent any pops in the audio when the power is disconnected.&lt;br /&gt;
&lt;br /&gt;
The circuit was designed with audio quality, but not energy efficiency, in mind.  The circuit may sink as much as 400mA from VIN, and an additional 40mA from the main 5v line.  However, when the audio circuit is idle, it should be less than 50mA all together.  The 3.5mm audio jack can provide a maximum of 75mA per channel.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;Audio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;SD.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// GUItool: begin automatically generated code&lt;br /&gt;
AudioPlaySdWav           playSdWav1;     //xy=719,389&lt;br /&gt;
AudioOutputI2S2          i2s2_1;         //xy=892,388&lt;br /&gt;
AudioConnection          patchCord1(playSdWav1, 0, i2s2_1, 0);&lt;br /&gt;
AudioConnection          patchCord2(playSdWav1, 1, i2s2_1, 1);&lt;br /&gt;
// GUItool: end automatically generated code&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  AudioMemory(10);&lt;br /&gt;
&lt;br /&gt;
  if (!(SD.begin(BUILTIN_SDCARD))) {&lt;br /&gt;
    // stop here, but print a message repetitively&lt;br /&gt;
    while (1) {&lt;br /&gt;
      Serial.println(&amp;quot;Unable to access the SD card&amp;quot;);&lt;br /&gt;
      delay(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Serial.println(&amp;quot;Playing audio.wav...&amp;quot;);&lt;br /&gt;
  playSdWav1.play(&amp;quot;audio.wav&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // Do nothing forever&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== USB Ports ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_USB_schematic.png|thumb|Teensy 4.1 Computer USB circuit|600px]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1's builtin USB port is connected directly to a TUSB2046 IC, which is a USB hub and provides four downstream ports.  Each port has overcurrent protection, with a maximum allowed current of 1.5A.  There are also TVS diodes on all USB data lines on the downstream ports, provided by three TPD3E001DRLR ICs.  Other than that, each downstream USB port has two 15k pulldowns - one on each data line.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&lt;br /&gt;
The best example is found in the Mouse_KeyboardBT example from the USBHost_t36 library, which is included in the TeensyDuino package.  For convenience, I have included a stripped down version of it below:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;quot;USBHost_t36.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
USBHost myusb;&lt;br /&gt;
USBHub hub1(myusb);&lt;br /&gt;
USBHub hub2(myusb);&lt;br /&gt;
KeyboardController keyboard1(myusb);&lt;br /&gt;
KeyboardController keyboard2(myusb);&lt;br /&gt;
USBHIDParser hid1(myusb);&lt;br /&gt;
USBHIDParser hid2(myusb);&lt;br /&gt;
USBHIDParser hid3(myusb);&lt;br /&gt;
USBHIDParser hid4(myusb);&lt;br /&gt;
USBHIDParser hid5(myusb);&lt;br /&gt;
MouseController mouse1(myusb);&lt;br /&gt;
&lt;br /&gt;
BluetoothController bluet(myusb);   // version assumes it already was paired&lt;br /&gt;
&lt;br /&gt;
USBDriver *drivers[] = {&amp;amp;hub1, &amp;amp;hub2, &amp;amp;keyboard1, &amp;amp;keyboard2, &amp;amp;bluet, &amp;amp;hid1, &amp;amp;hid2, &amp;amp;hid3, &amp;amp;hid4, &amp;amp;hid5};&lt;br /&gt;
&lt;br /&gt;
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))&lt;br /&gt;
const char * driver_names[CNT_DEVICES] = {&amp;quot;Hub1&amp;quot;, &amp;quot;Hub2&amp;quot;, &amp;quot;KB1&amp;quot;, &amp;quot;KB2&amp;quot;, &amp;quot;Bluet&amp;quot;, &amp;quot;HID1&amp;quot; , &amp;quot;HID2&amp;quot;, &amp;quot;HID3&amp;quot;, &amp;quot;HID4&amp;quot;, &amp;quot;HID5&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool driver_active[CNT_DEVICES] = {false, false, false, false, false, false, false, false, false, false};&lt;br /&gt;
&lt;br /&gt;
// Lets also look at HID Input devices&lt;br /&gt;
USBHIDInput *hiddrivers[] = {&amp;amp;mouse1};&lt;br /&gt;
&lt;br /&gt;
#define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0]))&lt;br /&gt;
const char * hid_driver_names[CNT_HIDDEVICES] = {&amp;quot;Mouse1&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool hid_driver_active[CNT_HIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
BTHIDInput *bthiddrivers[] = {&amp;amp;keyboard1, &amp;amp;mouse1};&lt;br /&gt;
#define CNT_BTHIDDEVICES (sizeof(bthiddrivers)/sizeof(bthiddrivers[0]))&lt;br /&gt;
const char * bthid_driver_names[CNT_BTHIDDEVICES] = {&amp;quot;Keyboard(BT)&amp;quot;, &amp;quot;Mouse(BT)&amp;quot;};&lt;br /&gt;
bool bthid_driver_active[CNT_BTHIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
bool show_changed_only = false;&lt;br /&gt;
&lt;br /&gt;
uint8_t joystick_left_trigger_value = 0;&lt;br /&gt;
uint8_t joystick_right_trigger_value = 0;&lt;br /&gt;
uint64_t joystick_full_notify_mask = (uint64_t) - 1;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  while (!Serial); // wait for Arduino Serial Monitor&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  myusb.begin();&lt;br /&gt;
  keyboard1.attachPress(OnPress);&lt;br /&gt;
  keyboard2.attachPress(OnPress);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // check to see if the device list has changed:&lt;br /&gt;
  UpdateActiveDeviceInfo();&lt;br /&gt;
&lt;br /&gt;
  myusb.Task();&lt;br /&gt;
&lt;br /&gt;
  if (mouse1.available()) {&lt;br /&gt;
    Serial.print(&amp;quot;Mouse: buttons = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getButtons());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseX = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseX());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseY = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseY());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheel = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheel());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheelH = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheelH());&lt;br /&gt;
    Serial.println();&lt;br /&gt;
    mouse1.mouseDataClear();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void OnPress(int key)&lt;br /&gt;
{&lt;br /&gt;
  Serial.print(&amp;quot;key '&amp;quot;);&lt;br /&gt;
  switch (key) {&lt;br /&gt;
    case KEYD_UP       : Serial.print(&amp;quot;UP&amp;quot;); break;&lt;br /&gt;
    case KEYD_DOWN     : Serial.print(&amp;quot;DN&amp;quot;); break;&lt;br /&gt;
    case KEYD_LEFT     : Serial.print(&amp;quot;LEFT&amp;quot;); break;&lt;br /&gt;
    case KEYD_RIGHT    : Serial.print(&amp;quot;RIGHT&amp;quot;); break;&lt;br /&gt;
    case KEYD_INSERT   : Serial.print(&amp;quot;Ins&amp;quot;); break;&lt;br /&gt;
    case KEYD_DELETE   : Serial.print(&amp;quot;Del&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_UP  : Serial.print(&amp;quot;PUP&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_DOWN: Serial.print(&amp;quot;PDN&amp;quot;); break;&lt;br /&gt;
    case KEYD_HOME     : Serial.print(&amp;quot;HOME&amp;quot;); break;&lt;br /&gt;
    case KEYD_END      : Serial.print(&amp;quot;END&amp;quot;); break;&lt;br /&gt;
    case KEYD_F1       : Serial.print(&amp;quot;F1&amp;quot;); break;&lt;br /&gt;
    case KEYD_F2       : Serial.print(&amp;quot;F2&amp;quot;); break;&lt;br /&gt;
    case KEYD_F3       : Serial.print(&amp;quot;F3&amp;quot;); break;&lt;br /&gt;
    case KEYD_F4       : Serial.print(&amp;quot;F4&amp;quot;); break;&lt;br /&gt;
    case KEYD_F5       : Serial.print(&amp;quot;F5&amp;quot;); break;&lt;br /&gt;
    case KEYD_F6       : Serial.print(&amp;quot;F6&amp;quot;); break;&lt;br /&gt;
    case KEYD_F7       : Serial.print(&amp;quot;F7&amp;quot;); break;&lt;br /&gt;
    case KEYD_F8       : Serial.print(&amp;quot;F8&amp;quot;); break;&lt;br /&gt;
    case KEYD_F9       : Serial.print(&amp;quot;F9&amp;quot;); break;&lt;br /&gt;
    case KEYD_F10      : Serial.print(&amp;quot;F10&amp;quot;); break;&lt;br /&gt;
    case KEYD_F11      : Serial.print(&amp;quot;F11&amp;quot;); break;&lt;br /&gt;
    case KEYD_F12      : Serial.print(&amp;quot;F12&amp;quot;); break;&lt;br /&gt;
    default: Serial.print((char)key); break;&lt;br /&gt;
  }&lt;br /&gt;
  Serial.print(&amp;quot;'  &amp;quot;);&lt;br /&gt;
  Serial.println(key);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//=============================================================================&lt;br /&gt;
// UpdateActiveDeviceInfo&lt;br /&gt;
//=============================================================================&lt;br /&gt;
void UpdateActiveDeviceInfo() {&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_DEVICES; i++) {&lt;br /&gt;
    if (*drivers[i] != driver_active[i]) {&lt;br /&gt;
      if (driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s - disconnected ***\n&amp;quot;, driver_names[i]);&lt;br /&gt;
        driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s %x:%x - connected ***\n&amp;quot;, driver_names[i], drivers[i]-&amp;gt;idVendor(), drivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = drivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
&lt;br /&gt;
        if (drivers[i] == &amp;amp;bluet) {&lt;br /&gt;
          const uint8_t *bdaddr = bluet.myBDAddr();&lt;br /&gt;
          // remember it...&lt;br /&gt;
          Serial.printf(&amp;quot;  BDADDR: %x:%x:%x:%x:%x:%x\n&amp;quot;, bdaddr[0], bdaddr[1], bdaddr[2], bdaddr[3], bdaddr[4], bdaddr[5]);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_HIDDEVICES; i++) {&lt;br /&gt;
    if (*hiddrivers[i] != hid_driver_active[i]) {&lt;br /&gt;
      if (hid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s - disconnected ***\n&amp;quot;, hid_driver_names[i]);&lt;br /&gt;
        hid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s %x:%x - connected ***\n&amp;quot;, hid_driver_names[i], hiddrivers[i]-&amp;gt;idVendor(), hiddrivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        hid_driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = hiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  // Then Bluetooth devices&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_BTHIDDEVICES; i++) {&lt;br /&gt;
    if (*bthiddrivers[i] != bthid_driver_active[i]) {&lt;br /&gt;
      if (bthid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - disconnected ***\n&amp;quot;, bthid_driver_names[i]);&lt;br /&gt;
        bthid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - connected ***\n&amp;quot;, bthid_driver_names[i]); Serial.flush();&lt;br /&gt;
        bthid_driver_active[i] = true;&lt;br /&gt;
        #if 0&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = bthiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
    #endif  &lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Known Issues:'''&lt;br /&gt;
* Version 1.1 of the board leaves TUSB2046's reset line floating.  This can be remedied by connecting the TUSB2046's reset line to some other circuit, although this is error prone.&lt;br /&gt;
&lt;br /&gt;
== Power ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_power_circuit.png|thumb|Teensy 4.1 Computer power circuitry|500px]]&lt;br /&gt;
&lt;br /&gt;
The computer's power supply accepts input from four primary sources:&lt;br /&gt;
* Barrel jack DC input, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Screw terminal block, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Teensy 4.1 micro USB port, 5v input, current limit depends on USB power source&lt;br /&gt;
* Expansion ports&lt;br /&gt;
** Direct to 5v line, only limited by source current.  This allows one to surpass the typical 2A limit on the 5v line.&lt;br /&gt;
** Direct to VIN, accepts 8v to 20v.  5v line limited to 2A.&lt;br /&gt;
** Direct to 3.3v line, only limited by source current.  This can be used to relieve stress from the Teensy 4.1's 3.3v regulator, if needed.&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_audio_power.png|thumb|Teensy 4.1 Computer audio power circuits|800px]]&lt;br /&gt;
There are a few different power planes on the board:&lt;br /&gt;
* VIN, 8v to 20v input&lt;br /&gt;
** Feeds power to system 5v and audio 5v&lt;br /&gt;
** Audio 5v will come from system 5v if VIN is not provided&lt;br /&gt;
* System 5v line&lt;br /&gt;
** Provides power to the system 3.3v line and the audio 3.3v line&lt;br /&gt;
** If coming from VIN, this line is provided by a 2A switching regulator, which may have some noise.&lt;br /&gt;
** Is fed through PMOS power switches to downstream USB devices, functions as VUSB.&lt;br /&gt;
* System 3.3v line&lt;br /&gt;
** Provides power to the Teensy 4.1 and the TUSB&lt;br /&gt;
** Provided by the Teensy 4.1's internal 3.3v regulator from the system 5v line&lt;br /&gt;
* System Ground&lt;br /&gt;
** Connected to all circutry, with the exception of some of the audio circutry, which uses the audio ground instead&lt;br /&gt;
* Audio 5v line&lt;br /&gt;
** Provided by either a 5v linear regulator from VIN, or from the system 5v line if VIN is not provided&lt;br /&gt;
** Powers the OPA1688 audio amplifier&lt;br /&gt;
** Is not broken out to the rest of the board&lt;br /&gt;
* Audio 3.3v line&lt;br /&gt;
** Regulated by a linear regulator, getting input from the system 5v line through a ferrite bead&lt;br /&gt;
** Powers the PCM5102 audio ADC&lt;br /&gt;
** Is not broken out elsewhere on the board&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** Connected to System Ground through a ferrite bead&lt;br /&gt;
** Connected to audio circutry&lt;br /&gt;
** Provides the ground used for the 3.5mm audio jack&lt;br /&gt;
** Not broken out elsewhere on the board&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When providing power to the board, a few things should be kept in mind.  First, for high quality audio, either VIN must be provided, or the system 5v line must be clean.  The latter is generally much harder to do, so it is recommended to provide VIN if possible.  For a clean system 5v line, generally it is to provide the system 5v line though a linear regulator instead.  Adding more ceramic capacitors to the system 5v line can help as well.  If providing power from an exteral board through an expansion connector, providing power to both the 5v and VIN lines can boost the maximum current of the system 5v line above 2A.  If more  than 2A is needed, for example for more current-hungry USB devices, then this may be desirable.  Finally, there is a limit on the safe capacitence to be connected to the system 5v line for the 2A switching regulator to work normally.  It is unadvisable to connect more than 800uF of external capacitence to the system 5v line, as it may interfere with that regulator.  Although, there really usually is no reason to connect to much capacitence to that line.&lt;br /&gt;
&lt;br /&gt;
If providing power to the system 5v line from an external source and the Teensy 4.1's USB port simultaneously, it is a good idea to include an ideal diode or similar circuit to prevent backflow from the USB port into your regulator, or vice versa, depending on your regulators characteristics.  For this purpose LM66100 ICs can be used, as are used to protect the onboard switching regulator from the Teensy 4.1's USB port.&lt;br /&gt;
&lt;br /&gt;
'''Capacitence by Power Plane:'''&lt;br /&gt;
&lt;br /&gt;
* VIN:&lt;br /&gt;
** 22uF electrolytic&lt;br /&gt;
** 4.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 26.7uF total&lt;br /&gt;
* System 5v&lt;br /&gt;
** 2.2uF on Teensy 4.1, ceramic&lt;br /&gt;
** 10uF onboard, ceramic&lt;br /&gt;
** Specific to onboard switching regulator: 22uF electrolytic&lt;br /&gt;
** 12.2uF total when external 5v is provided (Teensy 4.1 micro USB, expansion slots directly to 5v rail)&lt;br /&gt;
** 34.2uF total when onboard regulator is used&lt;br /&gt;
* System 3.3v&lt;br /&gt;
** 7.13uF from Teensy 4.1, ceramic&lt;br /&gt;
** 0.3uF onboard for decoupling, ceramic&lt;br /&gt;
** 7.43uF total&lt;br /&gt;
* System Ground&lt;br /&gt;
** 10.3uF ceramic&lt;br /&gt;
** 14.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 44uF electrolytic&lt;br /&gt;
** Approximately 25.13uF on Teensy 4.1, ceramic&lt;br /&gt;
** 94.13uF total&lt;br /&gt;
* Audio 5v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio 3.3v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** 27.2uF ceramic&lt;br /&gt;
** Connected to system ground through ferrite bead - technically connected to those capacitors as well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Expansion Ports ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_expansion_ports.png|caption|Expansion port layout, not including the card connector port|800px]]&lt;br /&gt;
&lt;br /&gt;
There are three large expansion ports on the Teensy 4.1 computer board, and 5 smaller ports.  Here is a list, describing what each one breaks out:&lt;br /&gt;
* Largest expansion port on the back of the board (J11)&lt;br /&gt;
** Designed for large expansions to the computer system&lt;br /&gt;
** Exposes all power pins, including VIN, 5v, 3.3v, GND, and even the Teensy's VBAT line&lt;br /&gt;
** Numerous Teensy 4.1 pins: 0, 1, 11, 12, 13, 18, 19, 28, 29, 40, 41&lt;br /&gt;
*** UART1&lt;br /&gt;
*** UART7&lt;br /&gt;
*** SPI0&lt;br /&gt;
*** I2C0&lt;br /&gt;
*** Analog inputs: A4, A5, A16, A17&lt;br /&gt;
*** CRX2 and CTX2&lt;br /&gt;
** '''3.3v-compatible''' 8-bit parallel bus&lt;br /&gt;
*** Particularly useful for LCD displays and LCD display controllers, or any other parallel peripheral, for very high speeds&lt;br /&gt;
* Right-side expansion port (J10 - reference not shown on board revision 1.1)&lt;br /&gt;
** Designed for ATX-compatible expansions to the board&lt;br /&gt;
** Exposes 5v and 3.3v lines&lt;br /&gt;
** '''3.3v-compatible''' 8-bit parallel bus&lt;br /&gt;
** Several Teensy 4.1 pins: 6, 7, 8, 9, 18, 19, 32&lt;br /&gt;
*** UART2&lt;br /&gt;
*** I2C0&lt;br /&gt;
*** Analog inputs: A4, A5&lt;br /&gt;
* Card Expansion Port&lt;br /&gt;
** Provides a '''5v-compatible''' 8-bit parallel port.  The pins are shared with the other parallel ports.  The port is '''not bidirectional'''&lt;br /&gt;
*** Controlled by write, read, and select lines.  Select pin is pin 9, pulling it low will enable communication.&lt;br /&gt;
** Provides only a 5v line.&lt;br /&gt;
* I2S Expansion Port&lt;br /&gt;
** Provides an alternative to the 3.5mm audio output, and allows one to attach a microphone&lt;br /&gt;
** Breaks out I2S port 2&lt;br /&gt;
** Can be used to add a microphone alongside use of the 3.5mm audio jack&lt;br /&gt;
** Provides all power rails: VIN, 5v and 3.3v.&lt;br /&gt;
** Breaks out pins 16 and 17&lt;br /&gt;
*** I2C1&lt;br /&gt;
*** Analog pins A2 and A3&lt;br /&gt;
*** UART4&lt;br /&gt;
*&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=File:Teensy4.1_computer_expansion_ports.png&amp;diff=37</id>
		<title>File:Teensy4.1 computer expansion ports.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=File:Teensy4.1_computer_expansion_ports.png&amp;diff=37"/>
		<updated>2024-06-13T21:10:10Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Describes all the expansion ports, except the card connector.  Most descriptions are present on the board silkscreen, the rest were edited onto the image.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Describes all the expansion ports, except the card connector.  Most descriptions are present on the board silkscreen, the rest were edited onto the image.&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=36</id>
		<title>Teensy 4.1 Computer</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=36"/>
		<updated>2024-06-13T20:51:52Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Teensy 4.1 Computer is more or less described by its name: Its a carrier board for the Teensy 4.1 which provides four USB ports, an Ethernet port, Hi-Fi audio, and several convenient expansion ports.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
'''USB:'''&lt;br /&gt;
&lt;br /&gt;
* TUSB2046 USB Hub, providing 4 USB A ports&lt;br /&gt;
* Each port has a maximum of 1.5A output - the port will shutdown due to overcurrent above this&lt;br /&gt;
* Board power provides a maximum of 2A to the 5v supply&lt;br /&gt;
** one should be aware of this when considering the power used by USB devices&lt;br /&gt;
&lt;br /&gt;
'''Audio:'''&lt;br /&gt;
&lt;br /&gt;
* I2S control connected to Teensy I2S 2&lt;br /&gt;
* PCM5102 ADC for providing stereo sound&lt;br /&gt;
* OPA1688 for the amplifier&lt;br /&gt;
* 3.5mm audio jack, maximum 75mA per channel&lt;br /&gt;
* Separate ground, connected to main ground through a ferrite bead&lt;br /&gt;
* Separate 3.3v and 5v supplies from linear regulators to reduce noise&lt;br /&gt;
** Note: 5v line will come from main 5v rail if only powered from USB&lt;br /&gt;
&lt;br /&gt;
'''Power:'''&lt;br /&gt;
&lt;br /&gt;
* 2A 5v buck regulator (Exact part is the VR20S05)&lt;br /&gt;
* PMOS switches to prevent regulator backfeeding from USB and to control power flow (U16, U17, and U18 on the PCB)&lt;br /&gt;
* External VIN accepted from 8v to 20v&lt;br /&gt;
* Separate 5v and 3.3v supplies for audio circutry&lt;br /&gt;
* Barrel jack and screw terminal connections for VIN&lt;br /&gt;
&lt;br /&gt;
'''Expansion Ports'''&lt;br /&gt;
&lt;br /&gt;
* I2S, SPI, I2C, and Serial expansion ports&lt;br /&gt;
* 5v-capable 8-bit parallel bus port, with 3-bit address, WR, RD, select, and WAIT lines&lt;br /&gt;
* Two larger expansion ports, each providing a '''3.3v compatible''' 8-bit parallel bus, various protocols like I2C and SPI, power, and GPIO&lt;br /&gt;
&lt;br /&gt;
== The Audio Circuit ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy41_Computer_Audio_circuit.png|thumb|Teensy 4.1 Computer audio circuit|600px]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1 is connected to the audio circuit via the Teensy's I2S 2 port.  This connection goes to the PCM5102 ADC.  As is recommended in the PCM5102's datasheet, filtering is added to the output, however the filter used on this board is a bit stronger, using a 4.7nF capacitor instead of the 2.2nF capacitor recommended in the datasheet.  The filter cutoff is still far above human perception, at 72kHz, and should filter high frequency noise a bit better.  The connection to the OPA1688 is DC, not AC, coupled, but this is according to the recommendations in the OPA1688 datasheet.  The gain of the amplifier is configured to be 1, so the output voltage of the amp should match the ADC's output voltage, and be in the range of 0v to 3.3v.  A voltage divider is used to activate the PCM5102's soft mute function when the 5v line drops, which is designed to prevent any pops in the audio when the power is disconnected.&lt;br /&gt;
&lt;br /&gt;
The circuit was designed with audio quality, but not energy efficiency, in mind.  The circuit may sink as much as 400mA from VIN, and an additional 40mA from the main 5v line.  However, when the audio circuit is idle, it should be less than 50mA all together.  The 3.5mm audio jack can provide a maximum of 75mA per channel.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;Audio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;SD.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// GUItool: begin automatically generated code&lt;br /&gt;
AudioPlaySdWav           playSdWav1;     //xy=719,389&lt;br /&gt;
AudioOutputI2S2          i2s2_1;         //xy=892,388&lt;br /&gt;
AudioConnection          patchCord1(playSdWav1, 0, i2s2_1, 0);&lt;br /&gt;
AudioConnection          patchCord2(playSdWav1, 1, i2s2_1, 1);&lt;br /&gt;
// GUItool: end automatically generated code&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  AudioMemory(10);&lt;br /&gt;
&lt;br /&gt;
  if (!(SD.begin(BUILTIN_SDCARD))) {&lt;br /&gt;
    // stop here, but print a message repetitively&lt;br /&gt;
    while (1) {&lt;br /&gt;
      Serial.println(&amp;quot;Unable to access the SD card&amp;quot;);&lt;br /&gt;
      delay(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Serial.println(&amp;quot;Playing audio.wav...&amp;quot;);&lt;br /&gt;
  playSdWav1.play(&amp;quot;audio.wav&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // Do nothing forever&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== USB Ports ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_USB_schematic.png|thumb|Teensy 4.1 Computer USB circuit|600px]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1's builtin USB port is connected directly to a TUSB2046 IC, which is a USB hub and provides four downstream ports.  Each port has overcurrent protection, with a maximum allowed current of 1.5A.  There are also TVS diodes on all USB data lines on the downstream ports, provided by three TPD3E001DRLR ICs.  Other than that, each downstream USB port has two 15k pulldowns - one on each data line.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&lt;br /&gt;
The best example is found in the Mouse_KeyboardBT example from the USBHost_t36 library, which is included in the TeensyDuino package.  For convenience, I have included a stripped down version of it below:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;quot;USBHost_t36.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
USBHost myusb;&lt;br /&gt;
USBHub hub1(myusb);&lt;br /&gt;
USBHub hub2(myusb);&lt;br /&gt;
KeyboardController keyboard1(myusb);&lt;br /&gt;
KeyboardController keyboard2(myusb);&lt;br /&gt;
USBHIDParser hid1(myusb);&lt;br /&gt;
USBHIDParser hid2(myusb);&lt;br /&gt;
USBHIDParser hid3(myusb);&lt;br /&gt;
USBHIDParser hid4(myusb);&lt;br /&gt;
USBHIDParser hid5(myusb);&lt;br /&gt;
MouseController mouse1(myusb);&lt;br /&gt;
&lt;br /&gt;
BluetoothController bluet(myusb);   // version assumes it already was paired&lt;br /&gt;
&lt;br /&gt;
USBDriver *drivers[] = {&amp;amp;hub1, &amp;amp;hub2, &amp;amp;keyboard1, &amp;amp;keyboard2, &amp;amp;bluet, &amp;amp;hid1, &amp;amp;hid2, &amp;amp;hid3, &amp;amp;hid4, &amp;amp;hid5};&lt;br /&gt;
&lt;br /&gt;
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))&lt;br /&gt;
const char * driver_names[CNT_DEVICES] = {&amp;quot;Hub1&amp;quot;, &amp;quot;Hub2&amp;quot;, &amp;quot;KB1&amp;quot;, &amp;quot;KB2&amp;quot;, &amp;quot;Bluet&amp;quot;, &amp;quot;HID1&amp;quot; , &amp;quot;HID2&amp;quot;, &amp;quot;HID3&amp;quot;, &amp;quot;HID4&amp;quot;, &amp;quot;HID5&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool driver_active[CNT_DEVICES] = {false, false, false, false, false, false, false, false, false, false};&lt;br /&gt;
&lt;br /&gt;
// Lets also look at HID Input devices&lt;br /&gt;
USBHIDInput *hiddrivers[] = {&amp;amp;mouse1};&lt;br /&gt;
&lt;br /&gt;
#define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0]))&lt;br /&gt;
const char * hid_driver_names[CNT_HIDDEVICES] = {&amp;quot;Mouse1&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool hid_driver_active[CNT_HIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
BTHIDInput *bthiddrivers[] = {&amp;amp;keyboard1, &amp;amp;mouse1};&lt;br /&gt;
#define CNT_BTHIDDEVICES (sizeof(bthiddrivers)/sizeof(bthiddrivers[0]))&lt;br /&gt;
const char * bthid_driver_names[CNT_BTHIDDEVICES] = {&amp;quot;Keyboard(BT)&amp;quot;, &amp;quot;Mouse(BT)&amp;quot;};&lt;br /&gt;
bool bthid_driver_active[CNT_BTHIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
bool show_changed_only = false;&lt;br /&gt;
&lt;br /&gt;
uint8_t joystick_left_trigger_value = 0;&lt;br /&gt;
uint8_t joystick_right_trigger_value = 0;&lt;br /&gt;
uint64_t joystick_full_notify_mask = (uint64_t) - 1;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  while (!Serial); // wait for Arduino Serial Monitor&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  myusb.begin();&lt;br /&gt;
  keyboard1.attachPress(OnPress);&lt;br /&gt;
  keyboard2.attachPress(OnPress);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // check to see if the device list has changed:&lt;br /&gt;
  UpdateActiveDeviceInfo();&lt;br /&gt;
&lt;br /&gt;
  myusb.Task();&lt;br /&gt;
&lt;br /&gt;
  if (mouse1.available()) {&lt;br /&gt;
    Serial.print(&amp;quot;Mouse: buttons = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getButtons());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseX = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseX());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseY = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseY());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheel = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheel());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheelH = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheelH());&lt;br /&gt;
    Serial.println();&lt;br /&gt;
    mouse1.mouseDataClear();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void OnPress(int key)&lt;br /&gt;
{&lt;br /&gt;
  Serial.print(&amp;quot;key '&amp;quot;);&lt;br /&gt;
  switch (key) {&lt;br /&gt;
    case KEYD_UP       : Serial.print(&amp;quot;UP&amp;quot;); break;&lt;br /&gt;
    case KEYD_DOWN     : Serial.print(&amp;quot;DN&amp;quot;); break;&lt;br /&gt;
    case KEYD_LEFT     : Serial.print(&amp;quot;LEFT&amp;quot;); break;&lt;br /&gt;
    case KEYD_RIGHT    : Serial.print(&amp;quot;RIGHT&amp;quot;); break;&lt;br /&gt;
    case KEYD_INSERT   : Serial.print(&amp;quot;Ins&amp;quot;); break;&lt;br /&gt;
    case KEYD_DELETE   : Serial.print(&amp;quot;Del&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_UP  : Serial.print(&amp;quot;PUP&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_DOWN: Serial.print(&amp;quot;PDN&amp;quot;); break;&lt;br /&gt;
    case KEYD_HOME     : Serial.print(&amp;quot;HOME&amp;quot;); break;&lt;br /&gt;
    case KEYD_END      : Serial.print(&amp;quot;END&amp;quot;); break;&lt;br /&gt;
    case KEYD_F1       : Serial.print(&amp;quot;F1&amp;quot;); break;&lt;br /&gt;
    case KEYD_F2       : Serial.print(&amp;quot;F2&amp;quot;); break;&lt;br /&gt;
    case KEYD_F3       : Serial.print(&amp;quot;F3&amp;quot;); break;&lt;br /&gt;
    case KEYD_F4       : Serial.print(&amp;quot;F4&amp;quot;); break;&lt;br /&gt;
    case KEYD_F5       : Serial.print(&amp;quot;F5&amp;quot;); break;&lt;br /&gt;
    case KEYD_F6       : Serial.print(&amp;quot;F6&amp;quot;); break;&lt;br /&gt;
    case KEYD_F7       : Serial.print(&amp;quot;F7&amp;quot;); break;&lt;br /&gt;
    case KEYD_F8       : Serial.print(&amp;quot;F8&amp;quot;); break;&lt;br /&gt;
    case KEYD_F9       : Serial.print(&amp;quot;F9&amp;quot;); break;&lt;br /&gt;
    case KEYD_F10      : Serial.print(&amp;quot;F10&amp;quot;); break;&lt;br /&gt;
    case KEYD_F11      : Serial.print(&amp;quot;F11&amp;quot;); break;&lt;br /&gt;
    case KEYD_F12      : Serial.print(&amp;quot;F12&amp;quot;); break;&lt;br /&gt;
    default: Serial.print((char)key); break;&lt;br /&gt;
  }&lt;br /&gt;
  Serial.print(&amp;quot;'  &amp;quot;);&lt;br /&gt;
  Serial.println(key);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//=============================================================================&lt;br /&gt;
// UpdateActiveDeviceInfo&lt;br /&gt;
//=============================================================================&lt;br /&gt;
void UpdateActiveDeviceInfo() {&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_DEVICES; i++) {&lt;br /&gt;
    if (*drivers[i] != driver_active[i]) {&lt;br /&gt;
      if (driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s - disconnected ***\n&amp;quot;, driver_names[i]);&lt;br /&gt;
        driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s %x:%x - connected ***\n&amp;quot;, driver_names[i], drivers[i]-&amp;gt;idVendor(), drivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = drivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
&lt;br /&gt;
        if (drivers[i] == &amp;amp;bluet) {&lt;br /&gt;
          const uint8_t *bdaddr = bluet.myBDAddr();&lt;br /&gt;
          // remember it...&lt;br /&gt;
          Serial.printf(&amp;quot;  BDADDR: %x:%x:%x:%x:%x:%x\n&amp;quot;, bdaddr[0], bdaddr[1], bdaddr[2], bdaddr[3], bdaddr[4], bdaddr[5]);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_HIDDEVICES; i++) {&lt;br /&gt;
    if (*hiddrivers[i] != hid_driver_active[i]) {&lt;br /&gt;
      if (hid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s - disconnected ***\n&amp;quot;, hid_driver_names[i]);&lt;br /&gt;
        hid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s %x:%x - connected ***\n&amp;quot;, hid_driver_names[i], hiddrivers[i]-&amp;gt;idVendor(), hiddrivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        hid_driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = hiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  // Then Bluetooth devices&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_BTHIDDEVICES; i++) {&lt;br /&gt;
    if (*bthiddrivers[i] != bthid_driver_active[i]) {&lt;br /&gt;
      if (bthid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - disconnected ***\n&amp;quot;, bthid_driver_names[i]);&lt;br /&gt;
        bthid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - connected ***\n&amp;quot;, bthid_driver_names[i]); Serial.flush();&lt;br /&gt;
        bthid_driver_active[i] = true;&lt;br /&gt;
        #if 0&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = bthiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
    #endif  &lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Known Issues:'''&lt;br /&gt;
* Version 1.1 of the board leaves TUSB2046's reset line floating.  This can be remedied by connecting the TUSB2046's reset line to some other circuit, although this is error prone.&lt;br /&gt;
&lt;br /&gt;
== Power ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_power_circuit.png|thumb|Teensy 4.1 Computer power circuitry|500px]]&lt;br /&gt;
&lt;br /&gt;
The computer's power supply accepts input from four primary sources:&lt;br /&gt;
* Barrel jack DC input, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Screw terminal block, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Teensy 4.1 micro USB port, 5v input, current limit depends on USB power source&lt;br /&gt;
* Expansion ports&lt;br /&gt;
** Direct to 5v line, only limited by source current.  This allows one to surpass the typical 2A limit on the 5v line.&lt;br /&gt;
** Direct to VIN, accepts 8v to 20v.  5v line limited to 2A.&lt;br /&gt;
** Direct to 3.3v line, only limited by source current.  This can be used to relieve stress from the Teensy 4.1's 3.3v regulator, if needed.&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_audio_power.png|thumb|Teensy 4.1 Computer audio power circuits|800px]]&lt;br /&gt;
There are a few different power planes on the board:&lt;br /&gt;
* VIN, 8v to 20v input&lt;br /&gt;
** Feeds power to system 5v and audio 5v&lt;br /&gt;
** Audio 5v will come from system 5v if VIN is not provided&lt;br /&gt;
* System 5v line&lt;br /&gt;
** Provides power to the system 3.3v line and the audio 3.3v line&lt;br /&gt;
** If coming from VIN, this line is provided by a 2A switching regulator, which may have some noise.&lt;br /&gt;
** Is fed through PMOS power switches to downstream USB devices, functions as VUSB.&lt;br /&gt;
* System 3.3v line&lt;br /&gt;
** Provides power to the Teensy 4.1 and the TUSB&lt;br /&gt;
** Provided by the Teensy 4.1's internal 3.3v regulator from the system 5v line&lt;br /&gt;
* System Ground&lt;br /&gt;
** Connected to all circutry, with the exception of some of the audio circutry, which uses the audio ground instead&lt;br /&gt;
* Audio 5v line&lt;br /&gt;
** Provided by either a 5v linear regulator from VIN, or from the system 5v line if VIN is not provided&lt;br /&gt;
** Powers the OPA1688 audio amplifier&lt;br /&gt;
** Is not broken out to the rest of the board&lt;br /&gt;
* Audio 3.3v line&lt;br /&gt;
** Regulated by a linear regulator, getting input from the system 5v line through a ferrite bead&lt;br /&gt;
** Powers the PCM5102 audio ADC&lt;br /&gt;
** Is not broken out elsewhere on the board&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** Connected to System Ground through a ferrite bead&lt;br /&gt;
** Connected to audio circutry&lt;br /&gt;
** Provides the ground used for the 3.5mm audio jack&lt;br /&gt;
** Not broken out elsewhere on the board&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When providing power to the board, a few things should be kept in mind.  First, for high quality audio, either VIN must be provided, or the system 5v line must be clean.  The latter is generally much harder to do, so it is recommended to provide VIN if possible.  For a clean system 5v line, generally it is to provide the system 5v line though a linear regulator instead.  Adding more ceramic capacitors to the system 5v line can help as well.  If providing power from an exteral board through an expansion connector, providing power to both the 5v and VIN lines can boost the maximum current of the system 5v line above 2A.  If more  than 2A is needed, for example for more current-hungry USB devices, then this may be desirable.  Finally, there is a limit on the safe capacitence to be connected to the system 5v line for the 2A switching regulator to work normally.  It is unadvisable to connect more than 800uF of external capacitence to the system 5v line, as it may interfere with that regulator.  Although, there really usually is no reason to connect to much capacitence to that line.&lt;br /&gt;
&lt;br /&gt;
If providing power to the system 5v line from an external source and the Teensy 4.1's USB port simultaneously, it is a good idea to include an ideal diode or similar circuit to prevent backflow from the USB port into your regulator, or vice versa, depending on your regulators characteristics.  For this purpose LM66100 ICs can be used, as are used to protect the onboard switching regulator from the Teensy 4.1's USB port.&lt;br /&gt;
&lt;br /&gt;
'''Capacitence by Power Plane:'''&lt;br /&gt;
&lt;br /&gt;
* VIN:&lt;br /&gt;
** 22uF electrolytic&lt;br /&gt;
** 4.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 26.7uF total&lt;br /&gt;
* System 5v&lt;br /&gt;
** 2.2uF on Teensy 4.1, ceramic&lt;br /&gt;
** 10uF onboard, ceramic&lt;br /&gt;
** Specific to onboard switching regulator: 22uF electrolytic&lt;br /&gt;
** 12.2uF total when external 5v is provided (Teensy 4.1 micro USB, expansion slots directly to 5v rail)&lt;br /&gt;
** 34.2uF total when onboard regulator is used&lt;br /&gt;
* System 3.3v&lt;br /&gt;
** 7.13uF from Teensy 4.1, ceramic&lt;br /&gt;
** 0.3uF onboard for decoupling, ceramic&lt;br /&gt;
** 7.43uF total&lt;br /&gt;
* System Ground&lt;br /&gt;
** 10.3uF ceramic&lt;br /&gt;
** 14.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 44uF electrolytic&lt;br /&gt;
** Approximately 25.13uF on Teensy 4.1, ceramic&lt;br /&gt;
** 94.13uF total&lt;br /&gt;
* Audio 5v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio 3.3v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** 27.2uF ceramic&lt;br /&gt;
** Connected to system ground through ferrite bead - technically connected to those capacitors as well.&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=35</id>
		<title>Teensy 4.1 Computer</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=35"/>
		<updated>2024-06-13T20:50:36Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Add image for the audio power circuit, change image sizes&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Teensy 4.1 Computer is more or less described by its name: Its a carrier board for the Teensy 4.1 which provides four USB ports, an Ethernet port, Hi-Fi audio, and several convenient expansion ports.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
'''USB:'''&lt;br /&gt;
&lt;br /&gt;
* TUSB2046 USB Hub, providing 4 USB A ports&lt;br /&gt;
* Each port has a maximum of 1.5A output - the port will shutdown due to overcurrent above this&lt;br /&gt;
* Board power provides a maximum of 2A to the 5v supply&lt;br /&gt;
** one should be aware of this when considering the power used by USB devices&lt;br /&gt;
&lt;br /&gt;
'''Audio:'''&lt;br /&gt;
&lt;br /&gt;
* I2S control connected to Teensy I2S 2&lt;br /&gt;
* PCM5102 ADC for providing stereo sound&lt;br /&gt;
* OPA1688 for the amplifier&lt;br /&gt;
* 3.5mm audio jack, maximum 75mA per channel&lt;br /&gt;
* Separate ground, connected to main ground through a ferrite bead&lt;br /&gt;
* Separate 3.3v and 5v supplies from linear regulators to reduce noise&lt;br /&gt;
** Note: 5v line will come from main 5v rail if only powered from USB&lt;br /&gt;
&lt;br /&gt;
'''Power:'''&lt;br /&gt;
&lt;br /&gt;
* 2A 5v buck regulator (Exact part is the VR20S05)&lt;br /&gt;
* PMOS switches to prevent regulator backfeeding from USB and to control power flow (U16, U17, and U18 on the PCB)&lt;br /&gt;
* External VIN accepted from 8v to 20v&lt;br /&gt;
* Separate 5v and 3.3v supplies for audio circutry&lt;br /&gt;
* Barrel jack and screw terminal connections for VIN&lt;br /&gt;
&lt;br /&gt;
'''Expansion Ports'''&lt;br /&gt;
&lt;br /&gt;
* I2S, SPI, I2C, and Serial expansion ports&lt;br /&gt;
* 5v-capable 8-bit parallel bus port, with 3-bit address, WR, RD, select, and WAIT lines&lt;br /&gt;
* Two larger expansion ports, each providing a '''3.3v compatible''' 8-bit parallel bus, various protocols like I2C and SPI, power, and GPIO&lt;br /&gt;
&lt;br /&gt;
== The Audio Circuit ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy41_Computer_Audio_circuit.png|thumb|Teensy 4.1 Computer audio circuit]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1 is connected to the audio circuit via the Teensy's I2S 2 port.  This connection goes to the PCM5102 ADC.  As is recommended in the PCM5102's datasheet, filtering is added to the output, however the filter used on this board is a bit stronger, using a 4.7nF capacitor instead of the 2.2nF capacitor recommended in the datasheet.  The filter cutoff is still far above human perception, at 72kHz, and should filter high frequency noise a bit better.  The connection to the OPA1688 is DC, not AC, coupled, but this is according to the recommendations in the OPA1688 datasheet.  The gain of the amplifier is configured to be 1, so the output voltage of the amp should match the ADC's output voltage, and be in the range of 0v to 3.3v.  A voltage divider is used to activate the PCM5102's soft mute function when the 5v line drops, which is designed to prevent any pops in the audio when the power is disconnected.&lt;br /&gt;
&lt;br /&gt;
The circuit was designed with audio quality, but not energy efficiency, in mind.  The circuit may sink as much as 400mA from VIN, and an additional 40mA from the main 5v line.  However, when the audio circuit is idle, it should be less than 50mA all together.  The 3.5mm audio jack can provide a maximum of 75mA per channel.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;Audio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;SD.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// GUItool: begin automatically generated code&lt;br /&gt;
AudioPlaySdWav           playSdWav1;     //xy=719,389&lt;br /&gt;
AudioOutputI2S2          i2s2_1;         //xy=892,388&lt;br /&gt;
AudioConnection          patchCord1(playSdWav1, 0, i2s2_1, 0);&lt;br /&gt;
AudioConnection          patchCord2(playSdWav1, 1, i2s2_1, 1);&lt;br /&gt;
// GUItool: end automatically generated code&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  AudioMemory(10);&lt;br /&gt;
&lt;br /&gt;
  if (!(SD.begin(BUILTIN_SDCARD))) {&lt;br /&gt;
    // stop here, but print a message repetitively&lt;br /&gt;
    while (1) {&lt;br /&gt;
      Serial.println(&amp;quot;Unable to access the SD card&amp;quot;);&lt;br /&gt;
      delay(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Serial.println(&amp;quot;Playing audio.wav...&amp;quot;);&lt;br /&gt;
  playSdWav1.play(&amp;quot;audio.wav&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // Do nothing forever&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== USB Ports ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_USB_schematic.png|thumb|Teensy 4.1 Computer USB circuit]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1's builtin USB port is connected directly to a TUSB2046 IC, which is a USB hub and provides four downstream ports.  Each port has overcurrent protection, with a maximum allowed current of 1.5A.  There are also TVS diodes on all USB data lines on the downstream ports, provided by three TPD3E001DRLR ICs.  Other than that, each downstream USB port has two 15k pulldowns - one on each data line.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&lt;br /&gt;
The best example is found in the Mouse_KeyboardBT example from the USBHost_t36 library, which is included in the TeensyDuino package.  For convenience, I have included a stripped down version of it below:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;quot;USBHost_t36.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
USBHost myusb;&lt;br /&gt;
USBHub hub1(myusb);&lt;br /&gt;
USBHub hub2(myusb);&lt;br /&gt;
KeyboardController keyboard1(myusb);&lt;br /&gt;
KeyboardController keyboard2(myusb);&lt;br /&gt;
USBHIDParser hid1(myusb);&lt;br /&gt;
USBHIDParser hid2(myusb);&lt;br /&gt;
USBHIDParser hid3(myusb);&lt;br /&gt;
USBHIDParser hid4(myusb);&lt;br /&gt;
USBHIDParser hid5(myusb);&lt;br /&gt;
MouseController mouse1(myusb);&lt;br /&gt;
&lt;br /&gt;
BluetoothController bluet(myusb);   // version assumes it already was paired&lt;br /&gt;
&lt;br /&gt;
USBDriver *drivers[] = {&amp;amp;hub1, &amp;amp;hub2, &amp;amp;keyboard1, &amp;amp;keyboard2, &amp;amp;bluet, &amp;amp;hid1, &amp;amp;hid2, &amp;amp;hid3, &amp;amp;hid4, &amp;amp;hid5};&lt;br /&gt;
&lt;br /&gt;
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))&lt;br /&gt;
const char * driver_names[CNT_DEVICES] = {&amp;quot;Hub1&amp;quot;, &amp;quot;Hub2&amp;quot;, &amp;quot;KB1&amp;quot;, &amp;quot;KB2&amp;quot;, &amp;quot;Bluet&amp;quot;, &amp;quot;HID1&amp;quot; , &amp;quot;HID2&amp;quot;, &amp;quot;HID3&amp;quot;, &amp;quot;HID4&amp;quot;, &amp;quot;HID5&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool driver_active[CNT_DEVICES] = {false, false, false, false, false, false, false, false, false, false};&lt;br /&gt;
&lt;br /&gt;
// Lets also look at HID Input devices&lt;br /&gt;
USBHIDInput *hiddrivers[] = {&amp;amp;mouse1};&lt;br /&gt;
&lt;br /&gt;
#define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0]))&lt;br /&gt;
const char * hid_driver_names[CNT_HIDDEVICES] = {&amp;quot;Mouse1&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool hid_driver_active[CNT_HIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
BTHIDInput *bthiddrivers[] = {&amp;amp;keyboard1, &amp;amp;mouse1};&lt;br /&gt;
#define CNT_BTHIDDEVICES (sizeof(bthiddrivers)/sizeof(bthiddrivers[0]))&lt;br /&gt;
const char * bthid_driver_names[CNT_BTHIDDEVICES] = {&amp;quot;Keyboard(BT)&amp;quot;, &amp;quot;Mouse(BT)&amp;quot;};&lt;br /&gt;
bool bthid_driver_active[CNT_BTHIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
bool show_changed_only = false;&lt;br /&gt;
&lt;br /&gt;
uint8_t joystick_left_trigger_value = 0;&lt;br /&gt;
uint8_t joystick_right_trigger_value = 0;&lt;br /&gt;
uint64_t joystick_full_notify_mask = (uint64_t) - 1;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  while (!Serial); // wait for Arduino Serial Monitor&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  myusb.begin();&lt;br /&gt;
  keyboard1.attachPress(OnPress);&lt;br /&gt;
  keyboard2.attachPress(OnPress);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // check to see if the device list has changed:&lt;br /&gt;
  UpdateActiveDeviceInfo();&lt;br /&gt;
&lt;br /&gt;
  myusb.Task();&lt;br /&gt;
&lt;br /&gt;
  if (mouse1.available()) {&lt;br /&gt;
    Serial.print(&amp;quot;Mouse: buttons = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getButtons());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseX = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseX());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseY = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseY());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheel = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheel());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheelH = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheelH());&lt;br /&gt;
    Serial.println();&lt;br /&gt;
    mouse1.mouseDataClear();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void OnPress(int key)&lt;br /&gt;
{&lt;br /&gt;
  Serial.print(&amp;quot;key '&amp;quot;);&lt;br /&gt;
  switch (key) {&lt;br /&gt;
    case KEYD_UP       : Serial.print(&amp;quot;UP&amp;quot;); break;&lt;br /&gt;
    case KEYD_DOWN     : Serial.print(&amp;quot;DN&amp;quot;); break;&lt;br /&gt;
    case KEYD_LEFT     : Serial.print(&amp;quot;LEFT&amp;quot;); break;&lt;br /&gt;
    case KEYD_RIGHT    : Serial.print(&amp;quot;RIGHT&amp;quot;); break;&lt;br /&gt;
    case KEYD_INSERT   : Serial.print(&amp;quot;Ins&amp;quot;); break;&lt;br /&gt;
    case KEYD_DELETE   : Serial.print(&amp;quot;Del&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_UP  : Serial.print(&amp;quot;PUP&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_DOWN: Serial.print(&amp;quot;PDN&amp;quot;); break;&lt;br /&gt;
    case KEYD_HOME     : Serial.print(&amp;quot;HOME&amp;quot;); break;&lt;br /&gt;
    case KEYD_END      : Serial.print(&amp;quot;END&amp;quot;); break;&lt;br /&gt;
    case KEYD_F1       : Serial.print(&amp;quot;F1&amp;quot;); break;&lt;br /&gt;
    case KEYD_F2       : Serial.print(&amp;quot;F2&amp;quot;); break;&lt;br /&gt;
    case KEYD_F3       : Serial.print(&amp;quot;F3&amp;quot;); break;&lt;br /&gt;
    case KEYD_F4       : Serial.print(&amp;quot;F4&amp;quot;); break;&lt;br /&gt;
    case KEYD_F5       : Serial.print(&amp;quot;F5&amp;quot;); break;&lt;br /&gt;
    case KEYD_F6       : Serial.print(&amp;quot;F6&amp;quot;); break;&lt;br /&gt;
    case KEYD_F7       : Serial.print(&amp;quot;F7&amp;quot;); break;&lt;br /&gt;
    case KEYD_F8       : Serial.print(&amp;quot;F8&amp;quot;); break;&lt;br /&gt;
    case KEYD_F9       : Serial.print(&amp;quot;F9&amp;quot;); break;&lt;br /&gt;
    case KEYD_F10      : Serial.print(&amp;quot;F10&amp;quot;); break;&lt;br /&gt;
    case KEYD_F11      : Serial.print(&amp;quot;F11&amp;quot;); break;&lt;br /&gt;
    case KEYD_F12      : Serial.print(&amp;quot;F12&amp;quot;); break;&lt;br /&gt;
    default: Serial.print((char)key); break;&lt;br /&gt;
  }&lt;br /&gt;
  Serial.print(&amp;quot;'  &amp;quot;);&lt;br /&gt;
  Serial.println(key);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//=============================================================================&lt;br /&gt;
// UpdateActiveDeviceInfo&lt;br /&gt;
//=============================================================================&lt;br /&gt;
void UpdateActiveDeviceInfo() {&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_DEVICES; i++) {&lt;br /&gt;
    if (*drivers[i] != driver_active[i]) {&lt;br /&gt;
      if (driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s - disconnected ***\n&amp;quot;, driver_names[i]);&lt;br /&gt;
        driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s %x:%x - connected ***\n&amp;quot;, driver_names[i], drivers[i]-&amp;gt;idVendor(), drivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = drivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
&lt;br /&gt;
        if (drivers[i] == &amp;amp;bluet) {&lt;br /&gt;
          const uint8_t *bdaddr = bluet.myBDAddr();&lt;br /&gt;
          // remember it...&lt;br /&gt;
          Serial.printf(&amp;quot;  BDADDR: %x:%x:%x:%x:%x:%x\n&amp;quot;, bdaddr[0], bdaddr[1], bdaddr[2], bdaddr[3], bdaddr[4], bdaddr[5]);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_HIDDEVICES; i++) {&lt;br /&gt;
    if (*hiddrivers[i] != hid_driver_active[i]) {&lt;br /&gt;
      if (hid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s - disconnected ***\n&amp;quot;, hid_driver_names[i]);&lt;br /&gt;
        hid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s %x:%x - connected ***\n&amp;quot;, hid_driver_names[i], hiddrivers[i]-&amp;gt;idVendor(), hiddrivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        hid_driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = hiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  // Then Bluetooth devices&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_BTHIDDEVICES; i++) {&lt;br /&gt;
    if (*bthiddrivers[i] != bthid_driver_active[i]) {&lt;br /&gt;
      if (bthid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - disconnected ***\n&amp;quot;, bthid_driver_names[i]);&lt;br /&gt;
        bthid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - connected ***\n&amp;quot;, bthid_driver_names[i]); Serial.flush();&lt;br /&gt;
        bthid_driver_active[i] = true;&lt;br /&gt;
        #if 0&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = bthiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
    #endif  &lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Known Issues:'''&lt;br /&gt;
* Version 1.1 of the board leaves TUSB2046's reset line floating.  This can be remedied by connecting the TUSB2046's reset line to some other circuit, although this is error prone.&lt;br /&gt;
&lt;br /&gt;
== Power ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_power_circuit.png|thumb|Teensy 4.1 Computer power circuitry|500px]]&lt;br /&gt;
&lt;br /&gt;
The computer's power supply accepts input from four primary sources:&lt;br /&gt;
* Barrel jack DC input, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Screw terminal block, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Teensy 4.1 micro USB port, 5v input, current limit depends on USB power source&lt;br /&gt;
* Expansion ports&lt;br /&gt;
** Direct to 5v line, only limited by source current.  This allows one to surpass the typical 2A limit on the 5v line.&lt;br /&gt;
** Direct to VIN, accepts 8v to 20v.  5v line limited to 2A.&lt;br /&gt;
** Direct to 3.3v line, only limited by source current.  This can be used to relieve stress from the Teensy 4.1's 3.3v regulator, if needed.&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_audio_power.png|thumb|Teensy 4.1 Computer audio power circuits|800px]]&lt;br /&gt;
There are a few different power planes on the board:&lt;br /&gt;
* VIN, 8v to 20v input&lt;br /&gt;
** Feeds power to system 5v and audio 5v&lt;br /&gt;
** Audio 5v will come from system 5v if VIN is not provided&lt;br /&gt;
* System 5v line&lt;br /&gt;
** Provides power to the system 3.3v line and the audio 3.3v line&lt;br /&gt;
** If coming from VIN, this line is provided by a 2A switching regulator, which may have some noise.&lt;br /&gt;
** Is fed through PMOS power switches to downstream USB devices, functions as VUSB.&lt;br /&gt;
* System 3.3v line&lt;br /&gt;
** Provides power to the Teensy 4.1 and the TUSB&lt;br /&gt;
** Provided by the Teensy 4.1's internal 3.3v regulator from the system 5v line&lt;br /&gt;
* System Ground&lt;br /&gt;
** Connected to all circutry, with the exception of some of the audio circutry, which uses the audio ground instead&lt;br /&gt;
* Audio 5v line&lt;br /&gt;
** Provided by either a 5v linear regulator from VIN, or from the system 5v line if VIN is not provided&lt;br /&gt;
** Powers the OPA1688 audio amplifier&lt;br /&gt;
** Is not broken out to the rest of the board&lt;br /&gt;
* Audio 3.3v line&lt;br /&gt;
** Regulated by a linear regulator, getting input from the system 5v line through a ferrite bead&lt;br /&gt;
** Powers the PCM5102 audio ADC&lt;br /&gt;
** Is not broken out elsewhere on the board&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** Connected to System Ground through a ferrite bead&lt;br /&gt;
** Connected to audio circutry&lt;br /&gt;
** Provides the ground used for the 3.5mm audio jack&lt;br /&gt;
** Not broken out elsewhere on the board&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When providing power to the board, a few things should be kept in mind.  First, for high quality audio, either VIN must be provided, or the system 5v line must be clean.  The latter is generally much harder to do, so it is recommended to provide VIN if possible.  For a clean system 5v line, generally it is to provide the system 5v line though a linear regulator instead.  Adding more ceramic capacitors to the system 5v line can help as well.  If providing power from an exteral board through an expansion connector, providing power to both the 5v and VIN lines can boost the maximum current of the system 5v line above 2A.  If more  than 2A is needed, for example for more current-hungry USB devices, then this may be desirable.  Finally, there is a limit on the safe capacitence to be connected to the system 5v line for the 2A switching regulator to work normally.  It is unadvisable to connect more than 800uF of external capacitence to the system 5v line, as it may interfere with that regulator.  Although, there really usually is no reason to connect to much capacitence to that line.&lt;br /&gt;
&lt;br /&gt;
If providing power to the system 5v line from an external source and the Teensy 4.1's USB port simultaneously, it is a good idea to include an ideal diode or similar circuit to prevent backflow from the USB port into your regulator, or vice versa, depending on your regulators characteristics.  For this purpose LM66100 ICs can be used, as are used to protect the onboard switching regulator from the Teensy 4.1's USB port.&lt;br /&gt;
&lt;br /&gt;
'''Capacitence by Power Plane:'''&lt;br /&gt;
&lt;br /&gt;
* VIN:&lt;br /&gt;
** 22uF electrolytic&lt;br /&gt;
** 4.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 26.7uF total&lt;br /&gt;
* System 5v&lt;br /&gt;
** 2.2uF on Teensy 4.1, ceramic&lt;br /&gt;
** 10uF onboard, ceramic&lt;br /&gt;
** Specific to onboard switching regulator: 22uF electrolytic&lt;br /&gt;
** 12.2uF total when external 5v is provided (Teensy 4.1 micro USB, expansion slots directly to 5v rail)&lt;br /&gt;
** 34.2uF total when onboard regulator is used&lt;br /&gt;
* System 3.3v&lt;br /&gt;
** 7.13uF from Teensy 4.1, ceramic&lt;br /&gt;
** 0.3uF onboard for decoupling, ceramic&lt;br /&gt;
** 7.43uF total&lt;br /&gt;
* System Ground&lt;br /&gt;
** 10.3uF ceramic&lt;br /&gt;
** 14.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 44uF electrolytic&lt;br /&gt;
** Approximately 25.13uF on Teensy 4.1, ceramic&lt;br /&gt;
** 94.13uF total&lt;br /&gt;
* Audio 5v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio 3.3v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** 27.2uF ceramic&lt;br /&gt;
** Connected to system ground through ferrite bead - technically connected to those capacitors as well.&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=File:Teensy4.1_computer_audio_power.png&amp;diff=34</id>
		<title>File:Teensy4.1 computer audio power.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=File:Teensy4.1_computer_audio_power.png&amp;diff=34"/>
		<updated>2024-06-13T20:46:25Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Image of the Teensy 4.1's audio power circuits&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Image of the Teensy 4.1's audio power circuits&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=33</id>
		<title>Teensy 4.1 Computer</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=33"/>
		<updated>2024-06-13T20:44:24Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Finish details about the capacitence on various power planes&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Teensy 4.1 Computer is more or less described by its name: Its a carrier board for the Teensy 4.1 which provides four USB ports, an Ethernet port, Hi-Fi audio, and several convenient expansion ports.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
'''USB:'''&lt;br /&gt;
&lt;br /&gt;
* TUSB2046 USB Hub, providing 4 USB A ports&lt;br /&gt;
* Each port has a maximum of 1.5A output - the port will shutdown due to overcurrent above this&lt;br /&gt;
* Board power provides a maximum of 2A to the 5v supply&lt;br /&gt;
** one should be aware of this when considering the power used by USB devices&lt;br /&gt;
&lt;br /&gt;
'''Audio:'''&lt;br /&gt;
&lt;br /&gt;
* I2S control connected to Teensy I2S 2&lt;br /&gt;
* PCM5102 ADC for providing stereo sound&lt;br /&gt;
* OPA1688 for the amplifier&lt;br /&gt;
* 3.5mm audio jack, maximum 75mA per channel&lt;br /&gt;
* Separate ground, connected to main ground through a ferrite bead&lt;br /&gt;
* Separate 3.3v and 5v supplies from linear regulators to reduce noise&lt;br /&gt;
** Note: 5v line will come from main 5v rail if only powered from USB&lt;br /&gt;
&lt;br /&gt;
'''Power:'''&lt;br /&gt;
&lt;br /&gt;
* 2A 5v buck regulator (Exact part is the VR20S05)&lt;br /&gt;
* PMOS switches to prevent regulator backfeeding from USB and to control power flow (U16, U17, and U18 on the PCB)&lt;br /&gt;
* External VIN accepted from 8v to 20v&lt;br /&gt;
* Separate 5v and 3.3v supplies for audio circutry&lt;br /&gt;
* Barrel jack and screw terminal connections for VIN&lt;br /&gt;
&lt;br /&gt;
'''Expansion Ports'''&lt;br /&gt;
&lt;br /&gt;
* I2S, SPI, I2C, and Serial expansion ports&lt;br /&gt;
* 5v-capable 8-bit parallel bus port, with 3-bit address, WR, RD, select, and WAIT lines&lt;br /&gt;
* Two larger expansion ports, each providing a '''3.3v compatible''' 8-bit parallel bus, various protocols like I2C and SPI, power, and GPIO&lt;br /&gt;
&lt;br /&gt;
== The Audio Circuit ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy41_Computer_Audio_circuit.png|thumb|Teensy 4.1 Computer audio circuit]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1 is connected to the audio circuit via the Teensy's I2S 2 port.  This connection goes to the PCM5102 ADC.  As is recommended in the PCM5102's datasheet, filtering is added to the output, however the filter used on this board is a bit stronger, using a 4.7nF capacitor instead of the 2.2nF capacitor recommended in the datasheet.  The filter cutoff is still far above human perception, at 72kHz, and should filter high frequency noise a bit better.  The connection to the OPA1688 is DC, not AC, coupled, but this is according to the recommendations in the OPA1688 datasheet.  The gain of the amplifier is configured to be 1, so the output voltage of the amp should match the ADC's output voltage, and be in the range of 0v to 3.3v.  A voltage divider is used to activate the PCM5102's soft mute function when the 5v line drops, which is designed to prevent any pops in the audio when the power is disconnected.&lt;br /&gt;
&lt;br /&gt;
The circuit was designed with audio quality, but not energy efficiency, in mind.  The circuit may sink as much as 400mA from VIN, and an additional 40mA from the main 5v line.  However, when the audio circuit is idle, it should be less than 50mA all together.  The 3.5mm audio jack can provide a maximum of 75mA per channel.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;Audio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;SD.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// GUItool: begin automatically generated code&lt;br /&gt;
AudioPlaySdWav           playSdWav1;     //xy=719,389&lt;br /&gt;
AudioOutputI2S2          i2s2_1;         //xy=892,388&lt;br /&gt;
AudioConnection          patchCord1(playSdWav1, 0, i2s2_1, 0);&lt;br /&gt;
AudioConnection          patchCord2(playSdWav1, 1, i2s2_1, 1);&lt;br /&gt;
// GUItool: end automatically generated code&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  AudioMemory(10);&lt;br /&gt;
&lt;br /&gt;
  if (!(SD.begin(BUILTIN_SDCARD))) {&lt;br /&gt;
    // stop here, but print a message repetitively&lt;br /&gt;
    while (1) {&lt;br /&gt;
      Serial.println(&amp;quot;Unable to access the SD card&amp;quot;);&lt;br /&gt;
      delay(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Serial.println(&amp;quot;Playing audio.wav...&amp;quot;);&lt;br /&gt;
  playSdWav1.play(&amp;quot;audio.wav&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // Do nothing forever&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== USB Ports ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_USB_schematic.png|thumb|Teensy 4.1 Computer USB circuit]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1's builtin USB port is connected directly to a TUSB2046 IC, which is a USB hub and provides four downstream ports.  Each port has overcurrent protection, with a maximum allowed current of 1.5A.  There are also TVS diodes on all USB data lines on the downstream ports, provided by three TPD3E001DRLR ICs.  Other than that, each downstream USB port has two 15k pulldowns - one on each data line.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&lt;br /&gt;
The best example is found in the Mouse_KeyboardBT example from the USBHost_t36 library, which is included in the TeensyDuino package.  For convenience, I have included a stripped down version of it below:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;quot;USBHost_t36.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
USBHost myusb;&lt;br /&gt;
USBHub hub1(myusb);&lt;br /&gt;
USBHub hub2(myusb);&lt;br /&gt;
KeyboardController keyboard1(myusb);&lt;br /&gt;
KeyboardController keyboard2(myusb);&lt;br /&gt;
USBHIDParser hid1(myusb);&lt;br /&gt;
USBHIDParser hid2(myusb);&lt;br /&gt;
USBHIDParser hid3(myusb);&lt;br /&gt;
USBHIDParser hid4(myusb);&lt;br /&gt;
USBHIDParser hid5(myusb);&lt;br /&gt;
MouseController mouse1(myusb);&lt;br /&gt;
&lt;br /&gt;
BluetoothController bluet(myusb);   // version assumes it already was paired&lt;br /&gt;
&lt;br /&gt;
USBDriver *drivers[] = {&amp;amp;hub1, &amp;amp;hub2, &amp;amp;keyboard1, &amp;amp;keyboard2, &amp;amp;bluet, &amp;amp;hid1, &amp;amp;hid2, &amp;amp;hid3, &amp;amp;hid4, &amp;amp;hid5};&lt;br /&gt;
&lt;br /&gt;
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))&lt;br /&gt;
const char * driver_names[CNT_DEVICES] = {&amp;quot;Hub1&amp;quot;, &amp;quot;Hub2&amp;quot;, &amp;quot;KB1&amp;quot;, &amp;quot;KB2&amp;quot;, &amp;quot;Bluet&amp;quot;, &amp;quot;HID1&amp;quot; , &amp;quot;HID2&amp;quot;, &amp;quot;HID3&amp;quot;, &amp;quot;HID4&amp;quot;, &amp;quot;HID5&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool driver_active[CNT_DEVICES] = {false, false, false, false, false, false, false, false, false, false};&lt;br /&gt;
&lt;br /&gt;
// Lets also look at HID Input devices&lt;br /&gt;
USBHIDInput *hiddrivers[] = {&amp;amp;mouse1};&lt;br /&gt;
&lt;br /&gt;
#define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0]))&lt;br /&gt;
const char * hid_driver_names[CNT_HIDDEVICES] = {&amp;quot;Mouse1&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool hid_driver_active[CNT_HIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
BTHIDInput *bthiddrivers[] = {&amp;amp;keyboard1, &amp;amp;mouse1};&lt;br /&gt;
#define CNT_BTHIDDEVICES (sizeof(bthiddrivers)/sizeof(bthiddrivers[0]))&lt;br /&gt;
const char * bthid_driver_names[CNT_BTHIDDEVICES] = {&amp;quot;Keyboard(BT)&amp;quot;, &amp;quot;Mouse(BT)&amp;quot;};&lt;br /&gt;
bool bthid_driver_active[CNT_BTHIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
bool show_changed_only = false;&lt;br /&gt;
&lt;br /&gt;
uint8_t joystick_left_trigger_value = 0;&lt;br /&gt;
uint8_t joystick_right_trigger_value = 0;&lt;br /&gt;
uint64_t joystick_full_notify_mask = (uint64_t) - 1;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  while (!Serial); // wait for Arduino Serial Monitor&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  myusb.begin();&lt;br /&gt;
  keyboard1.attachPress(OnPress);&lt;br /&gt;
  keyboard2.attachPress(OnPress);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // check to see if the device list has changed:&lt;br /&gt;
  UpdateActiveDeviceInfo();&lt;br /&gt;
&lt;br /&gt;
  myusb.Task();&lt;br /&gt;
&lt;br /&gt;
  if (mouse1.available()) {&lt;br /&gt;
    Serial.print(&amp;quot;Mouse: buttons = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getButtons());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseX = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseX());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseY = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseY());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheel = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheel());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheelH = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheelH());&lt;br /&gt;
    Serial.println();&lt;br /&gt;
    mouse1.mouseDataClear();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void OnPress(int key)&lt;br /&gt;
{&lt;br /&gt;
  Serial.print(&amp;quot;key '&amp;quot;);&lt;br /&gt;
  switch (key) {&lt;br /&gt;
    case KEYD_UP       : Serial.print(&amp;quot;UP&amp;quot;); break;&lt;br /&gt;
    case KEYD_DOWN     : Serial.print(&amp;quot;DN&amp;quot;); break;&lt;br /&gt;
    case KEYD_LEFT     : Serial.print(&amp;quot;LEFT&amp;quot;); break;&lt;br /&gt;
    case KEYD_RIGHT    : Serial.print(&amp;quot;RIGHT&amp;quot;); break;&lt;br /&gt;
    case KEYD_INSERT   : Serial.print(&amp;quot;Ins&amp;quot;); break;&lt;br /&gt;
    case KEYD_DELETE   : Serial.print(&amp;quot;Del&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_UP  : Serial.print(&amp;quot;PUP&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_DOWN: Serial.print(&amp;quot;PDN&amp;quot;); break;&lt;br /&gt;
    case KEYD_HOME     : Serial.print(&amp;quot;HOME&amp;quot;); break;&lt;br /&gt;
    case KEYD_END      : Serial.print(&amp;quot;END&amp;quot;); break;&lt;br /&gt;
    case KEYD_F1       : Serial.print(&amp;quot;F1&amp;quot;); break;&lt;br /&gt;
    case KEYD_F2       : Serial.print(&amp;quot;F2&amp;quot;); break;&lt;br /&gt;
    case KEYD_F3       : Serial.print(&amp;quot;F3&amp;quot;); break;&lt;br /&gt;
    case KEYD_F4       : Serial.print(&amp;quot;F4&amp;quot;); break;&lt;br /&gt;
    case KEYD_F5       : Serial.print(&amp;quot;F5&amp;quot;); break;&lt;br /&gt;
    case KEYD_F6       : Serial.print(&amp;quot;F6&amp;quot;); break;&lt;br /&gt;
    case KEYD_F7       : Serial.print(&amp;quot;F7&amp;quot;); break;&lt;br /&gt;
    case KEYD_F8       : Serial.print(&amp;quot;F8&amp;quot;); break;&lt;br /&gt;
    case KEYD_F9       : Serial.print(&amp;quot;F9&amp;quot;); break;&lt;br /&gt;
    case KEYD_F10      : Serial.print(&amp;quot;F10&amp;quot;); break;&lt;br /&gt;
    case KEYD_F11      : Serial.print(&amp;quot;F11&amp;quot;); break;&lt;br /&gt;
    case KEYD_F12      : Serial.print(&amp;quot;F12&amp;quot;); break;&lt;br /&gt;
    default: Serial.print((char)key); break;&lt;br /&gt;
  }&lt;br /&gt;
  Serial.print(&amp;quot;'  &amp;quot;);&lt;br /&gt;
  Serial.println(key);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//=============================================================================&lt;br /&gt;
// UpdateActiveDeviceInfo&lt;br /&gt;
//=============================================================================&lt;br /&gt;
void UpdateActiveDeviceInfo() {&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_DEVICES; i++) {&lt;br /&gt;
    if (*drivers[i] != driver_active[i]) {&lt;br /&gt;
      if (driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s - disconnected ***\n&amp;quot;, driver_names[i]);&lt;br /&gt;
        driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s %x:%x - connected ***\n&amp;quot;, driver_names[i], drivers[i]-&amp;gt;idVendor(), drivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = drivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
&lt;br /&gt;
        if (drivers[i] == &amp;amp;bluet) {&lt;br /&gt;
          const uint8_t *bdaddr = bluet.myBDAddr();&lt;br /&gt;
          // remember it...&lt;br /&gt;
          Serial.printf(&amp;quot;  BDADDR: %x:%x:%x:%x:%x:%x\n&amp;quot;, bdaddr[0], bdaddr[1], bdaddr[2], bdaddr[3], bdaddr[4], bdaddr[5]);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_HIDDEVICES; i++) {&lt;br /&gt;
    if (*hiddrivers[i] != hid_driver_active[i]) {&lt;br /&gt;
      if (hid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s - disconnected ***\n&amp;quot;, hid_driver_names[i]);&lt;br /&gt;
        hid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s %x:%x - connected ***\n&amp;quot;, hid_driver_names[i], hiddrivers[i]-&amp;gt;idVendor(), hiddrivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        hid_driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = hiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  // Then Bluetooth devices&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_BTHIDDEVICES; i++) {&lt;br /&gt;
    if (*bthiddrivers[i] != bthid_driver_active[i]) {&lt;br /&gt;
      if (bthid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - disconnected ***\n&amp;quot;, bthid_driver_names[i]);&lt;br /&gt;
        bthid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - connected ***\n&amp;quot;, bthid_driver_names[i]); Serial.flush();&lt;br /&gt;
        bthid_driver_active[i] = true;&lt;br /&gt;
        #if 0&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = bthiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
    #endif  &lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Known Issues:'''&lt;br /&gt;
* Version 1.1 of the board leaves TUSB2046's reset line floating.  This can be remedied by connecting the TUSB2046's reset line to some other circuit, although this is error prone.&lt;br /&gt;
&lt;br /&gt;
== Power ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_power_circuit.png|thumb|Teensy 4.1 Computer power circuitry]]&lt;br /&gt;
&lt;br /&gt;
The computer's power supply accepts input from four primary sources:&lt;br /&gt;
* Barrel jack DC input, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Screw terminal block, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Teensy 4.1 micro USB port, 5v input, current limit depends on USB power source&lt;br /&gt;
* Expansion ports&lt;br /&gt;
** Direct to 5v line, only limited by source current.  This allows one to surpass the typical 2A limit on the 5v line.&lt;br /&gt;
** Direct to VIN, accepts 8v to 20v.  5v line limited to 2A.&lt;br /&gt;
** Direct to 3.3v line, only limited by source current.  This can be used to relieve stress from the Teensy 4.1's 3.3v regulator, if needed.&lt;br /&gt;
&lt;br /&gt;
There are a few different power planes on the board:&lt;br /&gt;
* VIN, 8v to 20v input&lt;br /&gt;
** Feeds power to system 5v and audio 5v&lt;br /&gt;
** Audio 5v will come from system 5v if VIN is not provided&lt;br /&gt;
* System 5v line&lt;br /&gt;
** Provides power to the system 3.3v line and the audio 3.3v line&lt;br /&gt;
** If coming from VIN, this line is provided by a 2A switching regulator, which may have some noise.&lt;br /&gt;
** Is fed through PMOS power switches to downstream USB devices, functions as VUSB.&lt;br /&gt;
* System 3.3v line&lt;br /&gt;
** Provides power to the Teensy 4.1 and the TUSB&lt;br /&gt;
** Provided by the Teensy 4.1's internal 3.3v regulator from the system 5v line&lt;br /&gt;
* System Ground&lt;br /&gt;
** Connected to all circutry, with the exception of some of the audio circutry, which uses the audio ground instead&lt;br /&gt;
* Audio 5v line&lt;br /&gt;
** Provided by either a 5v linear regulator from VIN, or from the system 5v line if VIN is not provided&lt;br /&gt;
** Powers the OPA1688 audio amplifier&lt;br /&gt;
** Is not broken out to the rest of the board&lt;br /&gt;
* Audio 3.3v line&lt;br /&gt;
** Regulated by a linear regulator, getting input from the system 5v line through a ferrite bead&lt;br /&gt;
** Powers the PCM5102 audio ADC&lt;br /&gt;
** Is not broken out elsewhere on the board&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** Connected to System Ground through a ferrite bead&lt;br /&gt;
** Connected to audio circutry&lt;br /&gt;
** Provides the ground used for the 3.5mm audio jack&lt;br /&gt;
** Not broken out elsewhere on the board&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When providing power to the board, a few things should be kept in mind.  First, for high quality audio, either VIN must be provided, or the system 5v line must be clean.  The latter is generally much harder to do, so it is recommended to provide VIN if possible.  For a clean system 5v line, generally it is to provide the system 5v line though a linear regulator instead.  Adding more ceramic capacitors to the system 5v line can help as well.  If providing power from an exteral board through an expansion connector, providing power to both the 5v and VIN lines can boost the maximum current of the system 5v line above 2A.  If more  than 2A is needed, for example for more current-hungry USB devices, then this may be desirable.  Finally, there is a limit on the safe capacitence to be connected to the system 5v line for the 2A switching regulator to work normally.  It is unadvisable to connect more than 800uF of external capacitence to the system 5v line, as it may interfere with that regulator.  Although, there really usually is no reason to connect to much capacitence to that line.&lt;br /&gt;
&lt;br /&gt;
If providing power to the system 5v line from an external source and the Teensy 4.1's USB port simultaneously, it is a good idea to include an ideal diode or similar circuit to prevent backflow from the USB port into your regulator, or vice versa, depending on your regulators characteristics.  For this purpose LM66100 ICs can be used, as are used to protect the onboard switching regulator from the Teensy 4.1's USB port.&lt;br /&gt;
&lt;br /&gt;
'''Capacitence by Power Plane:'''&lt;br /&gt;
&lt;br /&gt;
* VIN:&lt;br /&gt;
** 22uF electrolytic&lt;br /&gt;
** 4.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 26.7uF total&lt;br /&gt;
* System 5v&lt;br /&gt;
** 2.2uF on Teensy 4.1, ceramic&lt;br /&gt;
** 10uF onboard, ceramic&lt;br /&gt;
** Specific to onboard switching regulator: 22uF electrolytic&lt;br /&gt;
** 12.2uF total when external 5v is provided (Teensy 4.1 micro USB, expansion slots directly to 5v rail)&lt;br /&gt;
** 34.2uF total when onboard regulator is used&lt;br /&gt;
* System 3.3v&lt;br /&gt;
** 7.13uF from Teensy 4.1, ceramic&lt;br /&gt;
** 0.3uF onboard for decoupling, ceramic&lt;br /&gt;
** 7.43uF total&lt;br /&gt;
* System Ground&lt;br /&gt;
** 10.3uF ceramic&lt;br /&gt;
** 14.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 44uF electrolytic&lt;br /&gt;
** Approximately 25.13uF on Teensy 4.1, ceramic&lt;br /&gt;
** 94.13uF total&lt;br /&gt;
* Audio 5v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio 3.3v&lt;br /&gt;
** 10.1uF ceramic&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** 27.2uF ceramic&lt;br /&gt;
** Connected to system ground through ferrite bead - technically connected to those capacitors as well.&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=32</id>
		<title>Teensy 4.1 Computer</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=Teensy_4.1_Computer&amp;diff=32"/>
		<updated>2024-06-13T20:33:46Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Add power section, saving work even though not yet finished&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Teensy 4.1 Computer is more or less described by its name: Its a carrier board for the Teensy 4.1 which provides four USB ports, an Ethernet port, Hi-Fi audio, and several convenient expansion ports.&lt;br /&gt;
&lt;br /&gt;
== Detailed Specifications ==&lt;br /&gt;
&lt;br /&gt;
'''USB:'''&lt;br /&gt;
&lt;br /&gt;
* TUSB2046 USB Hub, providing 4 USB A ports&lt;br /&gt;
* Each port has a maximum of 1.5A output - the port will shutdown due to overcurrent above this&lt;br /&gt;
* Board power provides a maximum of 2A to the 5v supply&lt;br /&gt;
** one should be aware of this when considering the power used by USB devices&lt;br /&gt;
&lt;br /&gt;
'''Audio:'''&lt;br /&gt;
&lt;br /&gt;
* I2S control connected to Teensy I2S 2&lt;br /&gt;
* PCM5102 ADC for providing stereo sound&lt;br /&gt;
* OPA1688 for the amplifier&lt;br /&gt;
* 3.5mm audio jack, maximum 75mA per channel&lt;br /&gt;
* Separate ground, connected to main ground through a ferrite bead&lt;br /&gt;
* Separate 3.3v and 5v supplies from linear regulators to reduce noise&lt;br /&gt;
** Note: 5v line will come from main 5v rail if only powered from USB&lt;br /&gt;
&lt;br /&gt;
'''Power:'''&lt;br /&gt;
&lt;br /&gt;
* 2A 5v buck regulator (Exact part is the VR20S05)&lt;br /&gt;
* PMOS switches to prevent regulator backfeeding from USB and to control power flow (U16, U17, and U18 on the PCB)&lt;br /&gt;
* External VIN accepted from 8v to 20v&lt;br /&gt;
* Separate 5v and 3.3v supplies for audio circutry&lt;br /&gt;
* Barrel jack and screw terminal connections for VIN&lt;br /&gt;
&lt;br /&gt;
'''Expansion Ports'''&lt;br /&gt;
&lt;br /&gt;
* I2S, SPI, I2C, and Serial expansion ports&lt;br /&gt;
* 5v-capable 8-bit parallel bus port, with 3-bit address, WR, RD, select, and WAIT lines&lt;br /&gt;
* Two larger expansion ports, each providing a '''3.3v compatible''' 8-bit parallel bus, various protocols like I2C and SPI, power, and GPIO&lt;br /&gt;
&lt;br /&gt;
== The Audio Circuit ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy41_Computer_Audio_circuit.png|thumb|Teensy 4.1 Computer audio circuit]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1 is connected to the audio circuit via the Teensy's I2S 2 port.  This connection goes to the PCM5102 ADC.  As is recommended in the PCM5102's datasheet, filtering is added to the output, however the filter used on this board is a bit stronger, using a 4.7nF capacitor instead of the 2.2nF capacitor recommended in the datasheet.  The filter cutoff is still far above human perception, at 72kHz, and should filter high frequency noise a bit better.  The connection to the OPA1688 is DC, not AC, coupled, but this is according to the recommendations in the OPA1688 datasheet.  The gain of the amplifier is configured to be 1, so the output voltage of the amp should match the ADC's output voltage, and be in the range of 0v to 3.3v.  A voltage divider is used to activate the PCM5102's soft mute function when the 5v line drops, which is designed to prevent any pops in the audio when the power is disconnected.&lt;br /&gt;
&lt;br /&gt;
The circuit was designed with audio quality, but not energy efficiency, in mind.  The circuit may sink as much as 400mA from VIN, and an additional 40mA from the main 5v line.  However, when the audio circuit is idle, it should be less than 50mA all together.  The 3.5mm audio jack can provide a maximum of 75mA per channel.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;Audio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;SD.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// GUItool: begin automatically generated code&lt;br /&gt;
AudioPlaySdWav           playSdWav1;     //xy=719,389&lt;br /&gt;
AudioOutputI2S2          i2s2_1;         //xy=892,388&lt;br /&gt;
AudioConnection          patchCord1(playSdWav1, 0, i2s2_1, 0);&lt;br /&gt;
AudioConnection          patchCord2(playSdWav1, 1, i2s2_1, 1);&lt;br /&gt;
// GUItool: end automatically generated code&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  AudioMemory(10);&lt;br /&gt;
&lt;br /&gt;
  if (!(SD.begin(BUILTIN_SDCARD))) {&lt;br /&gt;
    // stop here, but print a message repetitively&lt;br /&gt;
    while (1) {&lt;br /&gt;
      Serial.println(&amp;quot;Unable to access the SD card&amp;quot;);&lt;br /&gt;
      delay(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Serial.println(&amp;quot;Playing audio.wav...&amp;quot;);&lt;br /&gt;
  playSdWav1.play(&amp;quot;audio.wav&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // Do nothing forever&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== USB Ports ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_USB_schematic.png|thumb|Teensy 4.1 Computer USB circuit]]&lt;br /&gt;
&lt;br /&gt;
The Teensy 4.1's builtin USB port is connected directly to a TUSB2046 IC, which is a USB hub and provides four downstream ports.  Each port has overcurrent protection, with a maximum allowed current of 1.5A.  There are also TVS diodes on all USB data lines on the downstream ports, provided by three TPD3E001DRLR ICs.  Other than that, each downstream USB port has two 15k pulldowns - one on each data line.&lt;br /&gt;
&lt;br /&gt;
'''Example Code:'''&lt;br /&gt;
&lt;br /&gt;
The best example is found in the Mouse_KeyboardBT example from the USBHost_t36 library, which is included in the TeensyDuino package.  For convenience, I have included a stripped down version of it below:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;quot;USBHost_t36.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
USBHost myusb;&lt;br /&gt;
USBHub hub1(myusb);&lt;br /&gt;
USBHub hub2(myusb);&lt;br /&gt;
KeyboardController keyboard1(myusb);&lt;br /&gt;
KeyboardController keyboard2(myusb);&lt;br /&gt;
USBHIDParser hid1(myusb);&lt;br /&gt;
USBHIDParser hid2(myusb);&lt;br /&gt;
USBHIDParser hid3(myusb);&lt;br /&gt;
USBHIDParser hid4(myusb);&lt;br /&gt;
USBHIDParser hid5(myusb);&lt;br /&gt;
MouseController mouse1(myusb);&lt;br /&gt;
&lt;br /&gt;
BluetoothController bluet(myusb);   // version assumes it already was paired&lt;br /&gt;
&lt;br /&gt;
USBDriver *drivers[] = {&amp;amp;hub1, &amp;amp;hub2, &amp;amp;keyboard1, &amp;amp;keyboard2, &amp;amp;bluet, &amp;amp;hid1, &amp;amp;hid2, &amp;amp;hid3, &amp;amp;hid4, &amp;amp;hid5};&lt;br /&gt;
&lt;br /&gt;
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))&lt;br /&gt;
const char * driver_names[CNT_DEVICES] = {&amp;quot;Hub1&amp;quot;, &amp;quot;Hub2&amp;quot;, &amp;quot;KB1&amp;quot;, &amp;quot;KB2&amp;quot;, &amp;quot;Bluet&amp;quot;, &amp;quot;HID1&amp;quot; , &amp;quot;HID2&amp;quot;, &amp;quot;HID3&amp;quot;, &amp;quot;HID4&amp;quot;, &amp;quot;HID5&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool driver_active[CNT_DEVICES] = {false, false, false, false, false, false, false, false, false, false};&lt;br /&gt;
&lt;br /&gt;
// Lets also look at HID Input devices&lt;br /&gt;
USBHIDInput *hiddrivers[] = {&amp;amp;mouse1};&lt;br /&gt;
&lt;br /&gt;
#define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0]))&lt;br /&gt;
const char * hid_driver_names[CNT_HIDDEVICES] = {&amp;quot;Mouse1&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
bool hid_driver_active[CNT_HIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
BTHIDInput *bthiddrivers[] = {&amp;amp;keyboard1, &amp;amp;mouse1};&lt;br /&gt;
#define CNT_BTHIDDEVICES (sizeof(bthiddrivers)/sizeof(bthiddrivers[0]))&lt;br /&gt;
const char * bthid_driver_names[CNT_BTHIDDEVICES] = {&amp;quot;Keyboard(BT)&amp;quot;, &amp;quot;Mouse(BT)&amp;quot;};&lt;br /&gt;
bool bthid_driver_active[CNT_BTHIDDEVICES] = {false};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
bool show_changed_only = false;&lt;br /&gt;
&lt;br /&gt;
uint8_t joystick_left_trigger_value = 0;&lt;br /&gt;
uint8_t joystick_right_trigger_value = 0;&lt;br /&gt;
uint64_t joystick_full_notify_mask = (uint64_t) - 1;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  while (!Serial); // wait for Arduino Serial Monitor&lt;br /&gt;
  Serial.begin(115200);&lt;br /&gt;
  myusb.begin();&lt;br /&gt;
  keyboard1.attachPress(OnPress);&lt;br /&gt;
  keyboard2.attachPress(OnPress);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // check to see if the device list has changed:&lt;br /&gt;
  UpdateActiveDeviceInfo();&lt;br /&gt;
&lt;br /&gt;
  myusb.Task();&lt;br /&gt;
&lt;br /&gt;
  if (mouse1.available()) {&lt;br /&gt;
    Serial.print(&amp;quot;Mouse: buttons = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getButtons());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseX = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseX());&lt;br /&gt;
    Serial.print(&amp;quot;,  mouseY = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getMouseY());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheel = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheel());&lt;br /&gt;
    Serial.print(&amp;quot;,  wheelH = &amp;quot;);&lt;br /&gt;
    Serial.print(mouse1.getWheelH());&lt;br /&gt;
    Serial.println();&lt;br /&gt;
    mouse1.mouseDataClear();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void OnPress(int key)&lt;br /&gt;
{&lt;br /&gt;
  Serial.print(&amp;quot;key '&amp;quot;);&lt;br /&gt;
  switch (key) {&lt;br /&gt;
    case KEYD_UP       : Serial.print(&amp;quot;UP&amp;quot;); break;&lt;br /&gt;
    case KEYD_DOWN     : Serial.print(&amp;quot;DN&amp;quot;); break;&lt;br /&gt;
    case KEYD_LEFT     : Serial.print(&amp;quot;LEFT&amp;quot;); break;&lt;br /&gt;
    case KEYD_RIGHT    : Serial.print(&amp;quot;RIGHT&amp;quot;); break;&lt;br /&gt;
    case KEYD_INSERT   : Serial.print(&amp;quot;Ins&amp;quot;); break;&lt;br /&gt;
    case KEYD_DELETE   : Serial.print(&amp;quot;Del&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_UP  : Serial.print(&amp;quot;PUP&amp;quot;); break;&lt;br /&gt;
    case KEYD_PAGE_DOWN: Serial.print(&amp;quot;PDN&amp;quot;); break;&lt;br /&gt;
    case KEYD_HOME     : Serial.print(&amp;quot;HOME&amp;quot;); break;&lt;br /&gt;
    case KEYD_END      : Serial.print(&amp;quot;END&amp;quot;); break;&lt;br /&gt;
    case KEYD_F1       : Serial.print(&amp;quot;F1&amp;quot;); break;&lt;br /&gt;
    case KEYD_F2       : Serial.print(&amp;quot;F2&amp;quot;); break;&lt;br /&gt;
    case KEYD_F3       : Serial.print(&amp;quot;F3&amp;quot;); break;&lt;br /&gt;
    case KEYD_F4       : Serial.print(&amp;quot;F4&amp;quot;); break;&lt;br /&gt;
    case KEYD_F5       : Serial.print(&amp;quot;F5&amp;quot;); break;&lt;br /&gt;
    case KEYD_F6       : Serial.print(&amp;quot;F6&amp;quot;); break;&lt;br /&gt;
    case KEYD_F7       : Serial.print(&amp;quot;F7&amp;quot;); break;&lt;br /&gt;
    case KEYD_F8       : Serial.print(&amp;quot;F8&amp;quot;); break;&lt;br /&gt;
    case KEYD_F9       : Serial.print(&amp;quot;F9&amp;quot;); break;&lt;br /&gt;
    case KEYD_F10      : Serial.print(&amp;quot;F10&amp;quot;); break;&lt;br /&gt;
    case KEYD_F11      : Serial.print(&amp;quot;F11&amp;quot;); break;&lt;br /&gt;
    case KEYD_F12      : Serial.print(&amp;quot;F12&amp;quot;); break;&lt;br /&gt;
    default: Serial.print((char)key); break;&lt;br /&gt;
  }&lt;br /&gt;
  Serial.print(&amp;quot;'  &amp;quot;);&lt;br /&gt;
  Serial.println(key);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//=============================================================================&lt;br /&gt;
// UpdateActiveDeviceInfo&lt;br /&gt;
//=============================================================================&lt;br /&gt;
void UpdateActiveDeviceInfo() {&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_DEVICES; i++) {&lt;br /&gt;
    if (*drivers[i] != driver_active[i]) {&lt;br /&gt;
      if (driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s - disconnected ***\n&amp;quot;, driver_names[i]);&lt;br /&gt;
        driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** Device %s %x:%x - connected ***\n&amp;quot;, driver_names[i], drivers[i]-&amp;gt;idVendor(), drivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = drivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = drivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
&lt;br /&gt;
        if (drivers[i] == &amp;amp;bluet) {&lt;br /&gt;
          const uint8_t *bdaddr = bluet.myBDAddr();&lt;br /&gt;
          // remember it...&lt;br /&gt;
          Serial.printf(&amp;quot;  BDADDR: %x:%x:%x:%x:%x:%x\n&amp;quot;, bdaddr[0], bdaddr[1], bdaddr[2], bdaddr[3], bdaddr[4], bdaddr[5]);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_HIDDEVICES; i++) {&lt;br /&gt;
    if (*hiddrivers[i] != hid_driver_active[i]) {&lt;br /&gt;
      if (hid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s - disconnected ***\n&amp;quot;, hid_driver_names[i]);&lt;br /&gt;
        hid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** HID Device %s %x:%x - connected ***\n&amp;quot;, hid_driver_names[i], hiddrivers[i]-&amp;gt;idVendor(), hiddrivers[i]-&amp;gt;idProduct());&lt;br /&gt;
        hid_driver_active[i] = true;&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = hiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = hiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  // Then Bluetooth devices&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; CNT_BTHIDDEVICES; i++) {&lt;br /&gt;
    if (*bthiddrivers[i] != bthid_driver_active[i]) {&lt;br /&gt;
      if (bthid_driver_active[i]) {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - disconnected ***\n&amp;quot;, bthid_driver_names[i]);&lt;br /&gt;
        bthid_driver_active[i] = false;&lt;br /&gt;
      } else {&lt;br /&gt;
        Serial.printf(&amp;quot;*** BTHID Device %s - connected ***\n&amp;quot;, bthid_driver_names[i]); Serial.flush();&lt;br /&gt;
        bthid_driver_active[i] = true;&lt;br /&gt;
        #if 0&lt;br /&gt;
&lt;br /&gt;
        const uint8_t *psz = bthiddrivers[i]-&amp;gt;manufacturer();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  manufacturer: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;product();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  product: %s\n&amp;quot;, psz);&lt;br /&gt;
        psz = bthiddrivers[i]-&amp;gt;serialNumber();&lt;br /&gt;
        if (psz &amp;amp;&amp;amp; *psz) Serial.printf(&amp;quot;  Serial: %s\n&amp;quot;, psz);&lt;br /&gt;
    #endif  &lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Known Issues:'''&lt;br /&gt;
* Version 1.1 of the board leaves TUSB2046's reset line floating.  This can be remedied by connecting the TUSB2046's reset line to some other circuit, although this is error prone.&lt;br /&gt;
&lt;br /&gt;
== Power ==&lt;br /&gt;
&lt;br /&gt;
[[File:Teensy4.1_computer_power_circuit.png|thumb|Teensy 4.1 Computer power circuitry]]&lt;br /&gt;
&lt;br /&gt;
The computer's power supply accepts input from four primary sources:&lt;br /&gt;
* Barrel jack DC input, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Screw terminal block, 8v to 20v, provides 2A to 5v line&lt;br /&gt;
* Teensy 4.1 micro USB port, 5v input, current limit depends on USB power source&lt;br /&gt;
* Expansion ports&lt;br /&gt;
** Direct to 5v line, only limited by source current.  This allows one to surpass the typical 2A limit on the 5v line.&lt;br /&gt;
** Direct to VIN, accepts 8v to 20v.  5v line limited to 2A.&lt;br /&gt;
** Direct to 3.3v line, only limited by source current.  This can be used to relieve stress from the Teensy 4.1's 3.3v regulator, if needed.&lt;br /&gt;
&lt;br /&gt;
There are a few different power planes on the board:&lt;br /&gt;
* VIN, 8v to 20v input&lt;br /&gt;
** Feeds power to system 5v and audio 5v&lt;br /&gt;
** Audio 5v will come from system 5v if VIN is not provided&lt;br /&gt;
* System 5v line&lt;br /&gt;
** Provides power to the system 3.3v line and the audio 3.3v line&lt;br /&gt;
** If coming from VIN, this line is provided by a 2A switching regulator, which may have some noise.&lt;br /&gt;
** Is fed through PMOS power switches to downstream USB devices, functions as VUSB.&lt;br /&gt;
* System 3.3v line&lt;br /&gt;
** Provides power to the Teensy 4.1 and the TUSB&lt;br /&gt;
** Provided by the Teensy 4.1's internal 3.3v regulator from the system 5v line&lt;br /&gt;
* System Ground&lt;br /&gt;
** Connected to all circutry, with the exception of some of the audio circutry, which uses the audio ground instead&lt;br /&gt;
* Audio 5v line&lt;br /&gt;
** Provided by either a 5v linear regulator from VIN, or from the system 5v line if VIN is not provided&lt;br /&gt;
** Powers the OPA1688 audio amplifier&lt;br /&gt;
** Is not broken out to the rest of the board&lt;br /&gt;
* Audio 3.3v line&lt;br /&gt;
** Regulated by a linear regulator, getting input from the system 5v line through a ferrite bead&lt;br /&gt;
** Powers the PCM5102 audio ADC&lt;br /&gt;
** Is not broken out elsewhere on the board&lt;br /&gt;
* Audio Ground&lt;br /&gt;
** Connected to System Ground through a ferrite bead&lt;br /&gt;
** Connected to audio circutry&lt;br /&gt;
** Provides the ground used for the 3.5mm audio jack&lt;br /&gt;
** Not broken out elsewhere on the board&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When providing power to the board, a few things should be kept in mind.  First, for high quality audio, either VIN must be provided, or the system 5v line must be clean.  The latter is generally much harder to do, so it is recommended to provide VIN if possible.  For a clean system 5v line, generally it is to provide the system 5v line though a linear regulator instead.  Adding more ceramic capacitors to the system 5v line can help as well.  If providing power from an exteral board through an expansion connector, providing power to both the 5v and VIN lines can boost the maximum current of the system 5v line above 2A.  If more  than 2A is needed, for example for more current-hungry USB devices, then this may be desirable.  Finally, there is a limit on the safe capacitence to be connected to the system 5v line for the 2A switching regulator to work normally.  It is unadvisable to connect more than 800uF of external capacitence to the system 5v line, as it may interfere with that regulator.  Although, there really usually is no reason to connect to much capacitence to that line.&lt;br /&gt;
&lt;br /&gt;
If providing power to the system 5v line from an external source and the Teensy 4.1's USB port simultaneously, it is a good idea to include an ideal diode or similar circuit to prevent backflow from the USB port into your regulator, or vice versa, depending on your regulators characteristics.  For this purpose LM66100 ICs can be used, as are used to protect the onboard switching regulator from the Teensy 4.1's USB port.&lt;br /&gt;
&lt;br /&gt;
'''Capacitence by Power Plane:'''&lt;br /&gt;
&lt;br /&gt;
* VIN:&lt;br /&gt;
** 22uF electrolytic&lt;br /&gt;
** 4.7uF ceramic (through ferrite bead)&lt;br /&gt;
** 26.7uF total&lt;br /&gt;
* System 5v&lt;br /&gt;
** 10uF ceramic&lt;br /&gt;
** Specific to onboard switching regulator: 22uF electrolytic&lt;br /&gt;
** &lt;br /&gt;
** 32uF total when onboard regulator is used&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
	<entry>
		<id>http://wiki.nuclearman.technology/mediawiki//index.php?title=File:Teensy4.1_computer_power_circuit.png&amp;diff=31</id>
		<title>File:Teensy4.1 computer power circuit.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.nuclearman.technology/mediawiki//index.php?title=File:Teensy4.1_computer_power_circuit.png&amp;diff=31"/>
		<updated>2024-06-13T20:03:05Z</updated>

		<summary type="html">&lt;p&gt;Nuclear: Power circuitry for the Teensy 4.1&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Power circuitry for the Teensy 4.1&lt;/div&gt;</summary>
		<author><name>Nuclear</name></author>
	</entry>
</feed>