奇技淫巧!用STM32伪装成ADC芯片

发布时间 2023-09-21 22:38:52作者: 不才狸子

事件背景

课程设计,同学抽到的题目是用单片机做万用表。但他没学好,是直接几百块买了淘宝上设计好的套件,用的是STC和ADC0832……然后他在检查日的前两天把玩的时候把ADC芯片烧了(到这里我才知道为什么要额外使用ADC芯片,使用麻烦精度又低),淘宝买来不及了。
学校教的是STM32,51单片机编程我也不会。突发奇想用STM32读取电压,伪装成ADC芯片把读数发给51单片机。正好老师允许拍成视频发给他检查,我的那一套STM32摆在画框外面也不至于逆向工程喧宾夺主了。

道路曲折

逻辑分析

找来了ADC0832的手册,看着时序图用Arduino两下把程序写好了。烧录,连线,不出意外的第一次是不会正常工作的。
找来逻辑分析仪打了一下看,反正折腾了一晚上没搞懂。第二天再研究,才想起要看看51的代码是怎么写的,又问同学要了店家给的代码。
“你是不是改过代码?”
“没有啊”
“卖家应该会给代码吧?就是把他原版的给我”
“这个就是啊!”
…………

水落石出

这个代码根本不符合ADC0832的时序,最后我根据这个错误的代码和逻辑分析仪的结果,写了Arduino。跑通了,而我也确信了这个店家在做这个项目的时候也是一知半解,不知道为什么没跑通,又为什么跑通了,既然跑通就能卖给大学生了。
最后成功让万用表能测试电压电流了。

源码

因为这个代码是给有BUG的51代码设计的,而且没有正确调用ADC0832的信号作为测试,又懒得再调试完整实现ADC0832的功能,以下代码仅供参考,提供思路。

#include <Arduino.h>

const int adc0 = PA0, fake_clk = PA15, fake_io = PA11, fake_cs = PA9;
const int debug_pin = PC13;
const float Vref = 3.3, adc0_Vmax = 2.857, correct_ratio = 0.969;
const float diff_rate = adc0_Vmax / Vref * correct_ratio;

typedef enum
{
  CH0,
  CH1,
  none
} CHx;

uint8_t CS = HIGH;
uint8_t ch_selected = CH0;
int adc_val = 0;
float adc_ratio;
uint8_t fake_reg_ch0 = 0, fake_reg_ch1 = 0, fake_reg_ch0_reverse = 0;

void cs_callback();
bool wait_clkrising();
bool wait_clkfalling();
uint8_t bit_reverse(uint8_t x);

void setup()
{
  // noInterrupts();
  attachInterrupt(fake_cs, cs_callback, FALLING);
  pinMode(fake_cs, INPUT_PULLUP);
  // interrupts();
  pinMode(fake_clk, INPUT_PULLDOWN);
  pinMode(fake_io, INPUT);

  pinMode(debug_pin, OUTPUT);
  digitalWrite(debug_pin, HIGH);

  Serial2.begin(115200);
}

void loop()
{
  adc_val = analogRead(adc0);
  adc_ratio = adc_val / (1023.0 * diff_rate);
  // Serial2.println(adc_val);
  // Serial2.println(adc_ratio);
  if (adc_ratio < 0.1)
  {
    fake_reg_ch0_reverse = 0U;
  }
  else
  {
    fake_reg_ch0_reverse = bit_reverse(128U);
  }
  
  // if (adc_ratio > 1)
  // {
  //   fake_reg_ch0 = 255;
  // }
  // else
  // {
  //   fake_reg_ch0 = (adc_ratio * 255.0) + 0.5;
  //   fake_reg_ch0_reverse = bit_reverse(fake_reg_ch0);
  // }
  // Serial2.println(fake_reg_ch0);
  Serial2.println((fake_reg_ch0 / 255.0) * 20.0);
  Serial2.println(fake_reg_ch0);
  delay(200);
}

void cs_callback()
{
  do
  {
    if (digitalRead(fake_cs != LOW))
      break;
    wait_clkfalling(); // 1

    wait_clkfalling(); // 3
    // if (digitalRead(fake_io) != LOW) // 选择通道
    //   break;
    // wait_clkfalling();                // 3
    // if (digitalRead(fake_io) != HIGH) // 我也不知道干嘛,51的程序有误
    //   break;
    // ch_selected = digitalRead(fake_io);
    digitalToggle(debug_pin);
    pinMode(fake_io, OUTPUT);
    // digitalWrite(fake_io, LOW);

    // for (uint8_t i = 7; i > 0; i--)
    // {
    //   wait_clkfalling();
    //   digitalWrite(fake_io, bitRead(fake_reg_ch0, i));
    //   // digitalToggle(debug_pin); // debug
    // }
    // digitalToggle(debug_pin); // debug
    for (uint8_t i = 0; i < 8; i++)
    {
      digitalWrite(fake_io, bitRead(fake_reg_ch0_reverse, i));
      wait_clkfalling();
    } 
    // wait_clkfalling();
    // digitalToggle(debug_pin); // debug
  } while (0);
  // digitalToggle(debug_pin); // debug
  pinMode(fake_io, INPUT);
}

bool wait_clkrising()
{
  // while(digitalRead(fake_clk) == HIGH);
  // while(digitalRead(fake_clk) == LOW);
  uint32_t time_start = HAL_GetTick();
  while (LL_GPIO_IsInputPinSet(GPIOA, LL_GPIO_PIN_15) == HIGH)
  {
    if ((HAL_GetTick() - time_start) > 1)
      return false;
  }
  while (LL_GPIO_IsInputPinSet(GPIOA, LL_GPIO_PIN_15) == LOW)
  {
    if ((HAL_GetTick() - time_start) > 1)
      return false;
  }
  return true;
}
bool wait_clkfalling()
{
  // while (digitalRead(fake_clk) == LOW);
  // while (digitalRead(fake_clk) == HIGH);
  uint32_t time_start = HAL_GetTick();
  while (LL_GPIO_IsInputPinSet(GPIOA, LL_GPIO_PIN_15) == LOW)
  {
    if ((HAL_GetTick() - time_start) > 1)
      return false;
  }
  while (LL_GPIO_IsInputPinSet(GPIOA, LL_GPIO_PIN_15) == HIGH)
  {
    if ((HAL_GetTick() - time_start) > 1)
      return false;
  }
  return true;
}

uint8_t bit_reverse(uint8_t x)
{
  x = (((x & 0xaa) >> 1) | ((x & 0x55) << 1));
  x = (((x & 0xcc) >> 2) | ((x & 0x33) << 2));

  return ((x >> 4) | (x << 4));
}