ljcucc blog

Hi! Welcome to my new blog (https://blog.ljc.cc)!! (。・ω・。)ノ And I'll post here about any new news, research, ideas and more... Welcome and have a stay here! Come and join the offical discuss chat in matrix #ljcucc-blog-discuss:matrix.org #ljcucc-blog-discuss:matrix.org

Hi! Welcome to my new blog (https://blog.ljc.cc)!! (。・ω・。)ノ And I'll post here about any new news, research, ideas and more... Welcome and have a stay here! Come and join the offical discuss chat in matrix #ljcucc-blog-discuss:matrix.org #ljcucc-blog-discuss:matrix.org

CSSC - EP.2: Homebrew a Simple Signal Analyzer

Estimated reading time: 6 minutes

Available in 繁體中文  

In the previous article, I mentioned that I had completed the design and implementation of Register A, B, and the ALU. However, during the actual implementation process, I found that when operating and debugging signals, relying solely on LEDs for visual inspection made it difficult to directly observe specific signal changes. Therefore, I decided to implement a logic analysis tool.

A logic analyzer is a tool used for analyzing electronic products. It can detect and record signals from electronic products, converting them into digital or graphical forms for observation and analysis. Using a logic analyzer can improve the efficiency of electronic product design and development, and make the debugging process more efficient. However, as a student, while I wanted to use a logic analyzer, connecting every single pin of the computer for debugging would not only increase the cost of the logic analyzer itself, but I also had no idea where to even begin buying one.

Thus, pressed by a one-week deadline, I used Arduino and Processing to implement a simple logic analysis tool. Processing is an open-source programming language and environment that can be used to create interactive graphics and animations.

*Why Processing?

During the design process of the logic analysis tool, I chose Processing for rapid prototyping/sketching because, in addition to its Serial Library support, Processing often plays the role of a rapid prototyping tool in Creative Coding. Its concise syntax and API design make quick prototyping easier, which was an added bonus for me, given only one week to get things done.

*Why use Arduino instead of buying a logic analyzer?

Besides time constraints, I also wanted to try to build as many things as possible myself for this project, if feasible. Furthermore, during this period, I also discovered that I/O expansion can be achieved through I/O Expanders, which greatly boosted my motivation to build this with Arduino.

Floating Pin Issues and Pull-up Resistors

After initially setting it up, I noticed that most pins were in an erratic state, even when not connected to anything (floating). This was likely because most of the ICs are TTL-based, as indicated by their part numbers: 74[LS]??. Or, one could infer the default floating state from V_min or V_max…

Note: In TTL circuits, floating inputs can lead to unstable circuit operation because current cannot be effectively controlled. To prevent floating inputs in TTL circuits, a pull-up resistor is typically used. This is a resistor connected between the input terminal’s high potential and ground. This ensures the circuit has a high potential reference point, preventing unstable operation. In Arduino, the built-in digitalRead() function can be used to read the input pin’s voltage level, but if the input is not connected, the circuit will behave unstably. Therefore, when using input pins, the Arduino’s built-in INPUT_PULLUP circuit can be used to set up a pull-up resistor.

attachInterrupt

Next, I encountered a frequency issue. Since the amount of data transmitted each time is limited, continuously and frequently transmitting the state of every input could lead to buffer overflow or missed readings. Based on this, my solution was to use Arduino’s attachInterrupt function.

Note: In Arduino, attachInterrupt is a function that allows you to set up an Interrupt Service Routine (ISR), enabling your program to execute specific code when a particular event occurs. Typically, the main Arduino program runs continuously, while the ISR executes only when a specific event happens. For instance, when a button is pressed, the ISR would be executed. This design allows the program to respond to certain events without affecting the main program’s execution. By using the attachInterrupt function, you can associate an ISR with specific I/O pins. For example, you can associate an ISR with a button pin so that the ISR executes when the button is pressed.

However, here I used D2 as the attachInterrupt pin; any change in this input will trigger an interrupt. Not many pins on Arduino support this interrupt mode, but pin 2 is one of them (for the Arduino Mega I’m using).

Conclusion

Due to time constraints, the depth of research I could do and the completeness of what I could achieve were limited. However, after the semester-end “disaster” is over, I should have more time to freely develop things… Looking forward to discovering more things while building the CPU in the next episode~

(´▽`)

Next Post

Appendix: Source Code and Final Result

The source code is published directly below; since it’s just a simple tool, it’s only a few lines:

processing:

import processing.serial.*;

final int SIZE = 11;

Serial device;
byte[] input;
int hi = 100000000;
PGraphics view;
float scaling = 1;
int viewPosX = 0, viewPosY = 0;

void setup() {
  size(800, 600);
  pixelDensity(2);

  view = createGraphics(800, 600);

  print(Serial.list()[1]);

  device = new Serial(this, Serial.list()[1], 115200);
  delay(10);
  device.write('b');

  // avoid nullPointException
  view.beginDraw();
  view.endDraw();
}

void draw() {
  background(255);


  if (hi > width) {
    hi = SIZE;
    device.write('s');
    view.clear();
    view.background(255);
  }

  while (device.available() > 0) {
    view.beginDraw();
    String input = device.readStringUntil('\n');

    if (input == null) continue;
    if (input.length() != 56) continue;

    for (int i = 2; i < 54; i++) {
      if (input.charAt(i) != '0' && input.charAt(i) != '1') continue;

      view.fill(0, input.charAt(i) == '1'? 255: 100, 100);
      view.rect(hi, SIZE*i, SIZE, SIZE);
    }

    //view.line(hi, 0, hi, height);

    hi+=SIZE;

    device.clear();

    view.fill(0);
    view.textSize(SIZE);

    for (int i = 2; i < 54; i++)
      view.text(String.valueOf(i), 0, (i+1)*SIZE);

    view.endDraw();
  }

  image(view, 0 + viewPosX, 0 + viewPosY, 800 * scaling, 600 * scaling);
}

String numBuf = "0";

int toNonZero(int num){
  return num == 0? 1: num;
}

void keyPressed() {
  switch(key) {
  case '+':
    for (int i = 0; i < toNonZero(int(numBuf)); i++)
      scaling += 0.1;
    numBuf = "0";
    break;
  case '-':
    for (int i = 0; i < toNonZero(int(numBuf)); i++)
      scaling -= 0.1;
    numBuf = "0";
    break;
  case 'h':
    for (int i = 0; i < toNonZero(int(numBuf)); i++)
      viewPosX += 10;
    numBuf = "0";
    break;
  case 'l':
    for (int i = 0; i < toNonZero(int(numBuf)); i++)
      viewPosX -= 10;
    numBuf = "0";
    break;
  case 'j':
    for (int i = 0; i < toNonZero(int(numBuf)); i++)
      viewPosY -= 10;
    numBuf = "0";
    break;
  case 'k':
    for (int i = 0; i < toNonZero(int(numBuf)); i++)
      viewPosY += 10;
    numBuf = "0";
    break;
  default:
    if (key <= '9' && key >= '0') {
      numBuf += key;
    }
    break;
  }
}

Arduino:

char input[54];
char readBuf[54];

void setMode(char* modes) {
  for (int i = 2; i < 54; i++)
    pinMode(i, modes[i] == '1' ? OUTPUT : INPUT);
}

void writePin(char* data) {
  for (int i = 2; i < 54; i++)
    digitalWrite(i, data[i] == '1' ? HIGH : LOW);
}

void readPin() {
  readBuf[0] = 1;
  readBuf[1] = 1;
  for (int i = 2; i < 54; i++)
    readBuf[i] = digitalRead(i) == HIGH ? '1' : '0';
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println("start");

  for (int i = 2; i < 54; i++) {
    pinMode(i, INPUT_PULLUP);
  }

  attachInterrupt(digitalPinToInterrupt(2), updateInfo, CHANGE);
}

void updateInfo() {
  readPin();
  readBuf[54] = 0;
  Serial.println(readBuf);
}

void loop() {
  // put your main code here, to run repeatedly:
}

The code might not be perfectly complete, but its structure is very simple. Feel free to take a look at what’s inside; I won’t elaborate further here.

The image below shows the final result from Processing

The image below shows the Arduino Mega used for this project

Unless specified, all blog posts are licensed under CC BY-NC-ND 4.0 . Please credit "ljcucc" and this site's URL when reposting.