Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates for Release 2.0.0 #4

Merged
merged 6 commits into from
Dec 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 23 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ I created an Arduino-AY38910 chiptunes player board; see the [README][9] in the

Note that as of version 1.1.0 of the library, the `Notes[]` array in AY3891x_sounds.h is defined as `PROGMEM`. This will require minor changes to any sketches which include this file and use the `Notes[]` array. See example programs 3 and 5.

3. Instantiate the object. There are two forms of the constructor. The first one supports all of the chip's interface pins. The second one allows for a slightly simplified format when using only the minimal pins necessary to interface with the chip.
3. Instantiate the object. There are two forms of the constructor.

This first form supports all of the chip's interface pins. Use the enumeration `AY3891x::NO_PIN` for any of the optional signals `A9`, `A8`, `RESET` or `CLOCK` required by the constructor that are not connected.

```C++
AY3891x(byte DA7, byte DA6, byte DA5, byte DA4,
Expand All @@ -48,21 +50,16 @@ I created an Arduino-AY38910 chiptunes player board; see the [README][9] in the
byte reset, byte clock);
```

The second form allows for a slightly simplified format when using only the minimal pins necessary to interface with the chip.

```C++
AY3891x(byte DA7, byte DA6, byte DA5, byte DA4,
byte DA3, byte DA2, byte DA1, byte DA0,
byte BDIR, byte BC2, byte BC1);
```

```C++
// Example constructor using the simpler form
AY3891x psg(4, 5, 6, 7, 8, 10, 11, 12, 2, A5, 3);
```

Use the enumeration `AY3891x::NO_PIN` if any of the pins required by the constructor are not connected.

> [!IMPORTANT]
> All three of the bus control signals (BDIR, BC1, BC2) need to be connected to the processor. Do not use `AY3891x::NO_PIN` in the constructor for these signals.
> All three of the bus control signals (BDIR, BC1, BC2) need to be connected to the processor. Do not use `AY3891x::NO_PIN` in either constructor for these signals.

4. Initialize the object.

Expand All @@ -74,7 +71,7 @@ I created an Arduino-AY38910 chiptunes player board; see the [README][9] in the

```C++
psg.write(AY3891x::Enable_Reg, 0x3F);
psg.read(AY3891x::IO_Port_A_Reg;
psg.read(AY3891x::IO_Port_A_Reg);
```

6. See the library source code and example sketches for other available methods and usage.
Expand Down Expand Up @@ -118,6 +115,10 @@ This example sketch plays YM files which are stored on an SD card. The sketch se

See this [README][6] for details on finding and converting YM files for use with this sketch.

## Interrupts

The library does not use any interrupts, but briefly disables interrupts during the write pulse to the chip. This is to ensure that the write signal time (`tDW`) is within the 10 us maximum spec. On a 16 MHz ATmega 328, interrupts are disabled for about 4.5 us during each register write.

## YouTube Videos

My library was featured in two YouTube videos by [Gadget Reboot][11]:
Expand All @@ -127,7 +128,13 @@ My library was featured in two YouTube videos by [Gadget Reboot][11]:

## References

- AY-3-891x [datasheet][1]
- AY-3-891x Technical Information
- Individual Genral Instrument [datasheet][1], unknown publish date
- General Instrument [Microelectronics Data Catalog 1980][16], starting at page 7-88 (pdf page 419)
- General Instrument [Microelectronics Data Catalog 1982][17], starting at page 5-14 (pdf page 299)
- [Data Manual][18]
- Microchip individual [datasheet][19], 1992 copyright
- YM2149 [datasheet][20], 1989 copyright
- Chiptunes player board [hardware design][9]
- Details on [finding and converting][6] YM files for use with the example sketches
- Info from the Synth DIY [wiki][4]
Expand All @@ -152,6 +159,11 @@ The software and other files in this repository are released under what is commo
[13]: https://www.youtube.com/watch?v=b8uAda926so
[14]: https://www.youtube.com/watch?v=x9Ac49FLJ0c
[15]: https://github.com/GadgetReboot/AY-3-8910
[16]: https://www.rsp-italy.it/Electronics/Databooks/General%20Instrument/_contents/General%20Instrument%20Microelectronics%20Data%20Catalog%201980.pdf
[17]: https://www.rsp-italy.it/Electronics/Databooks/General%20Instrument/_contents/General%20Instrument%20Microelectronics%20Data%20Catalog%201982.pdf
[18]: https://f.rdw.se/AY-3-8910-datasheet.pdf
[19]: https://datasheet.datasheetarchive.com/originals/scans/Scans-061/DSA2IH0094116.pdf
[20]: https://www.ym2149.com/ym2149.pdf
[100]: https://choosealicense.com/licenses/mit/
[101]: ./LICENSE.txt
[//]: # ([200]: https://github.com/Andy4495/AY3891x)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ void setup()
// psg.setAddress(TheChipsAddress); // Only need this for special-ordered chips with non-default address.
// Set up a tone on channel A and B to make testing easier.
// By default, the tone and noise generators are enabled when the chip comes out of reset, I/O pins are set to INPUT
psg.write(AY3891x::Enable_Reg, MIXER_NOISES_DISABLE); // Disable the noise
psg.write(AY3891x::Enable_Reg, MIXER_NOISES_DISABLE | MIXER_TONE_C_DISABLE); // Disable the noise and channel C
psg.write(AY3891x::ChA_Amplitude, 0x04); // Lower amplitude
psg.write(AY3891x::ChB_Amplitude, 0x08); // Mid amplitude
Serial.print(F("Configuring note: C4 (middle C)"));
Expand Down
2 changes: 1 addition & 1 deletion extras/tools/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ This library does not include any YM files because I could not find any clear li
[6]: https://github.com/nguillaumin/ym-jukebox/tree/master/data
[7]: https://github.com/Andy4495/AY3891x
[8]: https://en.wikipedia.org/wiki/Chiptune
[9]: http://www.vgmpf.com/Wiki/index.php?title=YM
[9]: http://leonard.oxg.free.fr/ymformat.html
[10]: https://en.wikipedia.org/wiki/Programmable_sound_generator
[11]: ../../examples/AY3891x_EX6_Chiptunes_Flash
[12]: ../../examples/AY3891x_EX7_Chiptunes_SD
Expand Down
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=AY3891x
version=1.1.3
version=2.0.0
author=Andreas Taylor <Andy4495@outlook.com>
maintainer=Andreas Taylor <Andy4495@outlook.com>
sentence=Library for General Instrument AY-3-8910, AY-3-8912 Programmable Sound Generator chip.
Expand Down
119 changes: 70 additions & 49 deletions src/AY3891x.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
https://github.com/Andy4495/AY3891x

12/21/20 - A.T. - Original
29-Dec-2023 - Andy4495 - Release 2.0.0

The AY3891x chip uses an unconventional microcontroller interfacing
method. Instead of using a chip select signal, the chip requires
Expand Down Expand Up @@ -45,9 +46,11 @@
#include "AY3891x.h"

// Constructor with all possible pins
// ****** IMPORTANT! *****
// All three of the bus control signals (BDIR, BC1, BC2) need to be connected
// to the processor.
// Do not use AY3891x::NO_PIN in the constructor for these signals.
// ****** IMPORTANT! *****
AY3891x::AY3891x(byte DA7, byte DA6, byte DA5, byte DA4, byte DA3, byte DA2, byte DA1, byte DA0,
byte BDIR, byte BC2, byte BC1,
byte A9, byte A8,
Expand Down Expand Up @@ -77,9 +80,11 @@ AY3891x::AY3891x(byte DA7, byte DA6, byte DA5, byte DA4, byte DA3, byte DA2, b
// - A9 tied LOW or left unconnected
// - RESET tied high or to an external reset circuit
// - CLOCK connected to an independent clock source
// ****** IMPORTANT! *****
// All three of the bus control signals (BDIR, BC1, BC2) need to be connected
// to the processor.
// Do not use AY3891x::NO_PIN in the constructor for these signals.
// ****** IMPORTANT! *****
AY3891x::AY3891x(byte DA7, byte DA6, byte DA5, byte DA4, byte DA3, byte DA2, byte DA1, byte DA0,
byte BDIR, byte BC2, byte BC1) {

Expand All @@ -101,11 +106,14 @@ AY3891x::AY3891x(byte DA7, byte DA6, byte DA5, byte DA4, byte DA3, byte DA2, b
_chipAddress = 0; // Assume address 0 unless manually changed
}

// ****** IMPORTANT! *****
// For other combinations of pin connections, use the "All pins" constructor
// and use "AY3891::NO_PIN" for pins that are not tied to the microcontroller.
// All three of the bus control signals (BDIR, BC1, BC2) need to be connected
// to the processor.
// Do not use AY3891x::NO_PIN in the constructor for these signals.
// ****** IMPORTANT! *****


void AY3891x::begin() {

Expand All @@ -119,7 +127,11 @@ void AY3891x::begin() {
if (_BDIR_pin != NO_PIN) pinMode(_BDIR_pin, OUTPUT);
if (_BC2_pin != NO_PIN) pinMode(_BC2_pin, OUTPUT);
if (_BC1_pin != NO_PIN) pinMode(_BC1_pin, OUTPUT);
setMode(INACTIVE_000);
//setMode(INACTIVE_010);
// Need to do this one manually since we are in "unknown" state at this point
digitalWrite(_BDIR_pin, LOW);
digitalWrite(_BC2_pin, HIGH);
digitalWrite(_BC1_pin, LOW);

// Set up A8 and A9 to proper levels.
// Pins have internal pullup (A8), or pulldown (A9) so need
Expand All @@ -142,11 +154,17 @@ void AY3891x::latchAddressMode(byte regAddr) {
// 4. Set bus mode to LATCH_ADDR
// 5. Set bus mode to INACTIVE
// Leave DA pins in OUTPUT mode
setMode(INACTIVE_000);

///setMode(INACTIVE_000);
mode010to000();
daPinsOutput(_chipAddress | regAddr); // Register address is 4 lsb
setMode(LATCH_ADDR);
// setMode(LATCH_ADDR);
mode000to001();
// delay is not needed here because code is slow enough (300 ns min per datasheet)
setMode(INACTIVE_000);
// setMode(INACTIVE_000);
mode001to000();
// setMode(INACTIVE_010); // default state is 010
mode000to010();
}

void AY3891x::write(byte regAddr, byte data) {
Expand All @@ -158,15 +176,15 @@ void AY3891x::write(byte regAddr, byte data) {
// 5. Set BC to INACTIVE
// 6. Set BA pins to pinMode INPUT (high impedance)
latchAddressMode(regAddr);
setMode(INACTIVE_010);
daPinsOutput(data);
// The Write Data Pulse Width tDW has a max time of 10 us per the datasheet
// The Write Data Pulse Width tDW has a max time of 10 us per some datasheets
// tDW = time that WRITE_DATA mode is enabled before going back to INACTIVE
if (_BDIR_pin != NO_PIN) {
digitalWrite(_BDIR_pin, HIGH);
digitalWrite(_BDIR_pin, LOW);
}
// setMode(INACTIVE_000); // This is not needed since latchAddressMode() changes to 000
//setMode(WRITE_DATA);
noInterrupts();
mode010to110();
//setMode(INACTIVE_010);
mode110to010();
interrupts();
daPinsInput();
}

Expand All @@ -181,13 +199,13 @@ byte AY3891x::read(byte regAddr) {

latchAddressMode(regAddr);
daPinsInput();
setMode(INACTIVE_010);
setMode(READ_DATA);
///setMode(READ_DATA);
mode010to011();
for (byte i = 0; i < NUM_DA_LINES; i++) {
returnData = returnData | (digitalRead(_DA_pin[i]) << i);
}
setMode(INACTIVE_010);
// setMode(INACTIVE_000); // This is not needed since latchAddressMode() changes to 000
///setMode(INACTIVE_010);
mode011to010();
return returnData;
}

Expand Down Expand Up @@ -264,43 +282,46 @@ byte AY3891x::getChipAddress() {
return _chipAddress;
}

void AY3891x::setMode(byte mode) {
switch (mode) {
case INACTIVE_000: // 0b000
if (_BDIR_pin != NO_PIN) digitalWrite(_BDIR_pin, LOW);
if (_BC2_pin != NO_PIN) digitalWrite(_BC2_pin, LOW);
if (_BC1_pin != NO_PIN) digitalWrite(_BC1_pin, LOW);
break;
// For the following mode change calls, the three
// binary digits represent the following signals:
// BDIR BC2 BC1

case INACTIVE_010: // 0b010
if (_BDIR_pin != NO_PIN) digitalWrite(_BDIR_pin, LOW);
if (_BC2_pin != NO_PIN) digitalWrite(_BC2_pin, HIGH);
if (_BC1_pin != NO_PIN) digitalWrite(_BC1_pin, LOW);
break;
// INACTIVE to LATCH_ADDR
void AY3891x::mode000to001() {
digitalWrite(_BC1_pin, HIGH);
}

case LATCH_ADDR: // 0b001
if (_BDIR_pin != NO_PIN) digitalWrite(_BDIR_pin, LOW);
if (_BC2_pin != NO_PIN) digitalWrite(_BC2_pin, LOW);
if (_BC1_pin != NO_PIN) digitalWrite(_BC1_pin, HIGH);
break;
// LATCH_ADDR to INACTIVE
void AY3891x::mode001to000() {
digitalWrite(_BC1_pin, LOW);
}

case READ_DATA: // 0b011
if (_BDIR_pin != NO_PIN) digitalWrite(_BDIR_pin, LOW);
if (_BC2_pin != NO_PIN) digitalWrite(_BC2_pin, HIGH);
if (_BC1_pin != NO_PIN) digitalWrite(_BC1_pin, HIGH);
break;
// INACTIVE to INACTIVE
void AY3891x::mode000to010() {
digitalWrite(_BC2_pin, HIGH);
}

case WRITE_DATA: // 0b110
if (_BDIR_pin != NO_PIN) digitalWrite(_BDIR_pin, HIGH);
if (_BC2_pin != NO_PIN) digitalWrite(_BC2_pin, HIGH);
if (_BC1_pin != NO_PIN) digitalWrite(_BC1_pin, LOW);
break;
// INACTIVE to INACTIVE
void AY3891x::mode010to000() {
digitalWrite(_BC2_pin, LOW);
}

default:
// Set pins the same as INACTIVE mode 0b000
if (_BDIR_pin != NO_PIN) digitalWrite(_BDIR_pin, LOW);
if (_BC2_pin != NO_PIN) digitalWrite(_BC2_pin, LOW);
if (_BC1_pin != NO_PIN) digitalWrite(_BC1_pin, LOW);
break;
}
// INACTIVE to WRITE_DATA
void AY3891x::mode010to110() {
digitalWrite(_BDIR_pin, HIGH);
}

// WRITE_DATA to INACTIVE
void AY3891x::mode110to010() {
digitalWrite(_BDIR_pin, LOW);
}

// INACTIVE to READ_DATA
void AY3891x::mode010to011() {
digitalWrite(_BC1_pin, HIGH);
}

// READ_DATA to INACTIVE
void AY3891x::mode011to010() {
digitalWrite(_BC1_pin, LOW);
}
14 changes: 13 additions & 1 deletion src/AY3891x.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
https://github.com/Andy4495/AY3891x

12/21/20 - A.T. - Original
29-Dec-2023 - Andy4495 - Release 2.0.0

*/

Expand Down Expand Up @@ -39,9 +40,11 @@ class AY3891x {

// Constructor for all pins
// Use "AY3891::NO_PIN" for pins that are not tied to the microcontroller.
// ****** IMPORTANT! *****
// All three of the bus control signals (BDIR, BC1, BC2) need to be connected
// to the processor.
// Do not use AY3891x::NO_PIN in the constructor for these signals.
// ****** IMPORTANT! *****
AY3891x(byte DA7, byte DA6, byte DA5, byte DA4, byte DA3, byte DA2, byte DA1, byte DA0,
byte BDIR, byte BC2, byte BC1,
byte A9, byte A8,
Expand All @@ -54,9 +57,11 @@ AY3891x(byte DA7, byte DA6, byte DA5, byte DA4, byte DA3, byte DA2, byte DA1,
// - A9 tied LOW or left unconnected
// - RESET tied high or to an external reset circuit
// - CLOCK connected to an independent clock source
// ****** IMPORTANT! *****
// All three of the bus control signals (BDIR, BC1, BC2) need to be connected
// to the processor.
// Do not use AY3891x::NO_PIN in the constructor for these signals.
// ****** IMPORTANT! *****
AY3891x(byte DA7, byte DA6, byte DA5, byte DA4, byte DA3, byte DA2, byte DA1, byte DA0,
byte BDIR, byte BC2, byte BC1);

Expand All @@ -80,7 +85,14 @@ AY3891x(byte DA7, byte DA6, byte DA5, byte DA4, byte DA3, byte DA2, byte DA1,
void daPinsInput();
void daPinsOutput(byte data);
void latchAddressMode(byte regAddr);
void setMode(byte mode);
inline void mode000to001();
inline void mode001to000();
inline void mode000to010();
inline void mode010to000();
inline void mode010to110();
inline void mode110to010();
inline void mode010to011();
inline void mode011to010();
};

#endif
Loading