cne53102 发表于 2020-9-6 17:45:15

发2个利用AVR的RST复位引脚用作输入接口的做法,无需设置熔丝禁用复位功能

这两天给朋友做一个小东西,打算采用手头一小袋拆机的attiny13,但IO恰好少一个不够用。

众所周知,虽然AVR的RST脚可以通过熔丝设置为IO来使用,但一旦设置成这种模式,就无法再次烧写程序了,因为烧写的时候需要用到RST引脚,
这样相当于是锁了芯片,除非利用高压编程器恢复熔丝,否则无法再次烧写程序,非常麻烦和有风险,虽然不贵,但初学者通常是不敢搞这个操作的。

其实AVR的RST复位引脚在不通过熔丝禁用其复位功能的情况下,也是有办法实现输入功能的。
我简单在论坛搜索了一下,没有看到同样的内容,虽然现在可能用AVR的不多了,但还是决定写下来发个帖子,就当作是记录一下。

本次在不牺牲AVR的RST复位脚的情况下,介绍2种办法通过具备正常复位功能的RST复位引脚,实现信号输入,程序就不贴了,程序上都是常规操作。
注意:这2种方法都可能降低RST复位引脚的稳定性,不应用于恶劣环境和涉及安全的设备。

方法1:
读取AVR的MCUSR寄存器,查看复位原因。
如果是正常上电启动,应该是PORF位Power-on Reset Flag。
如果是RST复位引脚外部复位,则会是EXTRF位External Reset Flag。(注意不同型号这个名称可能不一样,此处以attiny13为例)
通过判断MCUSR寄存器的EXTRF位可以得知RST复位引脚是否被按键一类的东西拉低过。
MCUSR寄存器是可以读写的,读后可以清掉它以便进行下一次判断。
这个方法有一定的缺点,主要是会导致单片机真的复位,可能打乱现有工作,使用这种方法可能导致程序功能的实现有较大变化。
其次,必须注意AVR启动延时完成之前RST复位引脚必须是高电平,否则会出错。
这个方法的好处是很多时候,尤其是开发板,无需任何硬件变更,因为开发板几乎都带有复位按钮。


方法2:
其实在没有禁用RST复位引脚的复位功能时,这个引脚上的ADC功能也是可用的,是能够读到正确的电压的。

对attiny13来说,RST复位引脚上的ADC通道是ADC0,设置ADC以VCC为参考电压,直接读取ADC0通道,即可得到RST复位引脚上的电压信息。
由于RST复位引脚的逻辑电平具备一定的“容限”,小幅度的改变此引脚上的电压不会导致单片机复位,所以在有限范围内作为ADC输入是可用的。
按手册内容来看,绝对安全是范围是0.9*VCC以上,可以有1/10的范围是可用的,ADC值约100,也可以,但电压范围稍小。

考虑到这个引脚有一定的迟滞区间,如果能保证上电时该输入是高,则可以在一定程度上玩火,扩大使用范围。
下图电路在按键按下时会把电压拉到3.3V左右,这个电路在室内经过一周的测试是可以稳定可靠使用的。
在软件上通过ADC值判断按键的状态,我设置了ADC值低于750为低,高于950为高,持续约20ms认为有效,更新状态,运行良好。
理论上,可以用这个方法在RST复位引脚上通过多个电阻实现AD按键,实现多个按键的识别。模拟量输入如果可以限制好范围的话,应该也可以。

skyxjh 发表于 2020-9-6 23:04:11

用ADC的方法不错

t3486784401 发表于 2020-9-7 00:19:04

这个折腾可以有

iamseer 发表于 2020-9-7 00:34:47

这方法还真不错。
另外attiny自己搭一个高压恢复器也不难。之前调试Attiny85就现搭了一个。


图上电池反了,直接用12V插Arduino的DC口,然后用VIN脚就行。

// AVR High-voltage Serial Fuse Reprogrammer
// Adapted from code and design by Paul Willoughby 03/20/2010
//   http://www.rickety.us/2010/03/arduino-avr-high-voltage-serial-programmer/
//
// Fuse Calc:
//   http://www.engbedded.com/fusecalc/

#defineRST   13    // Output to level shifter for !RESET from transistor
#defineSCI   12    // Target Clock Input
#defineSDO   11    // Target Data Output
#defineSII   10    // Target Instruction Input
#defineSDI      9    // Target Data Input
#defineVCC      8    // Target VCC

#defineHFUSE0x747C
#defineLFUSE0x646C
#defineEFUSE0x666E

// Define ATTiny series signatures
#defineATTINY13   0x9007// L: 0x6A, H: 0xFF             8 pin
#defineATTINY24   0x910B// L: 0x62, H: 0xDF, E: 0xFF   14 pin
#defineATTINY25   0x9108// L: 0x62, H: 0xDF, E: 0xFF    8 pin
#defineATTINY44   0x9207// L: 0x62, H: 0xDF, E: 0xFFF14 pin
#defineATTINY45   0x9206// L: 0x62, H: 0xDF, E: 0xFF    8 pin
#defineATTINY84   0x930C// L: 0x62, H: 0xDF, E: 0xFFF14 pin
#defineATTINY85   0x930B// L: 0x62, H: 0xDF, E: 0xFF    8 pin

void setup() {
pinMode(VCC, OUTPUT);
pinMode(RST, OUTPUT);
pinMode(SDI, OUTPUT);
pinMode(SII, OUTPUT);
pinMode(SCI, OUTPUT);
pinMode(SDO, OUTPUT);   // Configured as input when in programming mode
digitalWrite(RST, HIGH);// Level shifter is inverting, this shuts off 12V
Serial.begin(19200);
}

void loop() {
   if (Serial.available() > 0) {
    Serial.read();
    pinMode(SDO, OUTPUT);   // Set SDO to output
    digitalWrite(SDI, LOW);
    digitalWrite(SII, LOW);
    digitalWrite(SDO, LOW);
    digitalWrite(RST, HIGH);// 12v Off
    digitalWrite(VCC, HIGH);// Vcc On
    delayMicroseconds(20);
    digitalWrite(RST, LOW);   // 12v On
    delayMicroseconds(10);
    pinMode(SDO, INPUT);      // Set SDO to input
    delayMicroseconds(300);
    unsigned int sig = readSignature();
    Serial.print("Signature is: ");
    Serial.println(sig, HEX);
    readFuses();
    if (sig == ATTINY13) {
      writeFuse(LFUSE, 0x6A);
      writeFuse(HFUSE, 0xFF);
    } else if (sig == ATTINY24 || sig == ATTINY44 || sig == ATTINY84 ||
               sig == ATTINY25 || sig == ATTINY45 || sig == ATTINY85) {
      writeFuse(LFUSE, 0x62);
      writeFuse(HFUSE, 0xDF);
      writeFuse(EFUSE, 0xFF);
    }
    readFuses();
    digitalWrite(SCI, LOW);
    digitalWrite(VCC, LOW);    // Vcc Off
    digitalWrite(RST, HIGH);   // 12v Off
}
}

byte shiftOut (byte val1, byte val2) {
int inBits = 0;
//Wait until SDO goes high
while (!digitalRead(SDO))
    ;
unsigned int dout = (unsigned int) val1 << 2;
unsigned int iout = (unsigned int) val2 << 2;
for (int ii = 10; ii >= 0; ii--){
    digitalWrite(SDI, !!(dout & (1 << ii)));
    digitalWrite(SII, !!(iout & (1 << ii)));
    inBits <<= 1;
    inBits |= digitalRead(SDO);
    digitalWrite(SCI, HIGH);
    digitalWrite(SCI, LOW);
}
return inBits >> 2;
}

void writeFuse (unsigned int fuse, byte val) {
shiftOut(0x40, 0x4C);
shiftOut( val, 0x2C);
shiftOut(0x00, (byte) (fuse >> 8));
shiftOut(0x00, (byte) fuse);
}

void readFuses () {
byte val;
      shiftOut(0x04, 0x4C);// LFuse
      shiftOut(0x00, 0x68);
val = shiftOut(0x00, 0x6C);
Serial.print("LFuse: ");
Serial.print(val, HEX);
      shiftOut(0x04, 0x4C);// HFuse
      shiftOut(0x00, 0x7A);
val = shiftOut(0x00, 0x7E);
Serial.print(", HFuse: ");
Serial.print(val, HEX);
      shiftOut(0x04, 0x4C);// EFuse
      shiftOut(0x00, 0x6A);
val = shiftOut(0x00, 0x6E);
Serial.print(", EFuse: ");
Serial.println(val, HEX);
}

unsigned int readSignature () {
unsigned int sig = 0;
byte val;
for (int ii = 1; ii < 3; ii++) {
          shiftOut(0x08, 0x4C);
          shiftOut(ii, 0x0C);
          shiftOut(0x00, 0x68);
    val = shiftOut(0x00, 0x6C);
    sig = (sig << 8) + val;
}
return sig;
}

cne53102 发表于 2020-9-7 00:40:55

iamseer 发表于 2020-9-7 00:34
这方法还真不错。
另外attiny自己搭一个高压恢复器也不难。之前调试Attiny85就现搭了一个。



哈哈,不错,这下搜索而来的人信息就全套了
页: [1]
查看完整版本: 发2个利用AVR的RST复位引脚用作输入接口的做法,无需设置熔丝禁用复位功能