【问题标题】:attiny85 bit bang uart with arduinoattiny85 bit bang uart 与 arduino
【发布时间】:2020-07-20 06:35:32
【问题描述】:

我尝试为 ATTiny85 实现 TX only uart 并使用 arduino micro 接收位。 (similiar question 没有帮助,因为它与我的情况完全不同)

我的意图是能够通过 attiny85 -> arduino -> 控制台进行打印,这样我就可以调试 attiny85,因为我没有可用的示波器。

attiny85 保险丝是:"efuse:w:0xff:m -U hfuse:w:0xdf:m -U lfuse:w:0xf1:m" aka。 16MHz F_CPU arduino 似乎也有 16MHz F_CPU

类似于提到的问题attiny85通过timer0 ISR发送比特,一次一位:

#ifndef F_CPU
#define F_CPU 16000000UL // 16 MHz
#endif
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define TX_PIN  PB0

volatile uint16_t tx_shift_reg = 0;

ISR(TIMER0_COMPA_vect) {
   uint16_t local_tx_shift_reg = tx_shift_reg;
   if( local_tx_shift_reg & 0x01 ) {
      PORTB |= _BV(TX_PIN);
   } else {
      PORTB &= ~_BV(TX_PIN);
   }
   local_tx_shift_reg >>= 1;
   if(!local_tx_shift_reg) {
      // Stop timer0.
      GTCCR |= (1<<TSM) | (1<<PSR0);
   }
   tx_shift_reg = local_tx_shift_reg;
}

void UART_tx(char byte) {
   uint16_t local_tx_shift_reg = tx_shift_reg;
   local_tx_shift_reg = (0b1<<9) | ((uint16_t)byte<<1);
   tx_shift_reg = local_tx_shift_reg;
   TCNT0 = 0;
   TCCR0B |= (1<<CS02)|(1<<CS00); // 1024 prescaler
   GTCCR &= ~(1<<TSM);
}

void UART_tx_char(char c) {
   UART_tx( c );
   // wait until transmission is finished
   while(tx_shift_reg);
}

void UART_init() {
   cli()
   // set TX pins as output
   DDRB |= (1<<TX_PIN);
   PORTB |= (1<<TX_PIN);
   // set timer0 to CTC mode, keep it halted.
   TCCR0A |= (1<<WGM01);
   TCCR0B = 0;
   // enable interrupt
   TIMSK |= (1<<OCIE0A);
   OCR0A = 128;
   OCR0B = 128;
   TCNT0 = 0;
   GTCCR |= (1<<TSM);
   sei();
}

void main(void)
{
   UART_init();
   while(1) {
      for(char c = 1; c < 128; ++c) {
         UART_tx_char(c);
         _delay_ms(100);
      }
   }
}

然后arduino接收比特:

/*
 * ATtiny85 bit-bang uart monitor for ATmega32u4
 */
#define LED_PIN 13
#define RX_PIN 7
// rx_state == 0 // timer1 not running
// rx_state == 1 // receive in progress
// rx_state == 2 // data ready in rx_data_reg
volatile int rx_state = 0;
volatile int rx_bit_nro = 0;
volatile uint16_t rx_shift_reg = 0;
volatile uint16_t rx_data_reg = 0;

void rx_start_interupt() {
  if(rx_state == 0) {
    digitalWrite(LED_PIN, HIGH);
    while(digitalRead(RX_PIN));
    // Start timer1
    rx_state++;
    TCNT1  = 0;
    TCCR1B = (1 << WGM12) | (1 <<CS12) | (1 << CS10);
  }
}

ISR(TIMER1_COMPA_vect) {
  uint16_t bit = digitalRead(RX_PIN) > 0;
  rx_shift_reg >>= 1;
  rx_shift_reg |= (bit << 7);
  ++rx_bit_nro;
  if(rx_bit_nro == 9) {
    // Stop timer.
    TCCR1B = 0;
    TCNT1  = 0;
    rx_data_reg = rx_shift_reg;
    rx_shift_reg = 0;
    rx_bit_nro = 0;
    rx_state++;
    digitalWrite(LED_PIN, LOW);
  }
}

void setup() {
  noInterrupts();
  // Program timer1 for UART bit bang receive.
  TCCR1A = 0; // set entire TCCR1A register to 0 (stops it)
  TCCR1B = 0; // same for TCCR1B
  TCNT1  = 0; // initialize counter value to 0
  OCR1A = 128;
  TIMSK1 |= (1 << OCIE1A);
  interrupts();
  
  pinMode(LED_PIN, OUTPUT);
  pinMode(RX_PIN, INPUT);

  // Attach RX start interupt.
  pinMode(digitalPinToInterrupt(RX_PIN), INPUT);
  attachInterrupt(digitalPinToInterrupt(RX_PIN), rx_start_interupt, FALLING);
  
  Serial.begin(9600);
  while(!Serial);
  Serial.print("F_CPU:");
  Serial.println(F_CPU,DEC);
  Serial.println("Waiting for data from attiny85...");
}

void loop() {
  if(rx_state == 2) {
    uint16_t local_rx_data = rx_data_reg;
    rx_state = 0;
    Serial.println(local_rx_data,HEX);
  }
}

我已经尝试了几乎所有的方法来让它工作,但是收到的字节回来是垃圾。 我错过了什么?

注意:我在 attiny85 上使用 timer0,在 arduino 上使用 timer1。 已解决: 将 ISR 切换为 TIMER1_COMPAB_vect 和 OCR1B = 64 确实有效!耶!

【问题讨论】:

    标签: timer arduino uart attiny


    【解决方案1】:

    我最近遇到了这个问题,串行通讯在另一端显示为垃圾。就我而言,ATtiny85 使用 USB 到 TTL UART 转换器向我的笔记本电脑发送比特,该转换器在其他情况下工作得很好,但是,我只是在 Arduino IDE 串行监视器中得到垃圾。

    我找到了forum post,其中提到了校准OSCCAL 的可能性。

    我对我的相关@​​987654322@ 有点幻想,但我测试了我应该使用此代码校准OSCCAL 的理论:

    #include <SoftwareSerial.h>
    
    SoftwareSerial comm(-1, 0);
    
    static const int anchor = 128;
    
    void
    print_osccal(int v) {
      comm.println(F("********************************"));
      comm.print(F("OSCCAL = "));
      comm.println(v);
      comm.println(F("********************************"));
    }
    
    void
    setup() {
      delay(5000);
      comm.begin(300);
      OSCCAL = anchor;
      print_osccal(anchor);
      delay(5000);
    }
    
    void
    loop() {
      int x;
      for (int i = 1; i < 128; ++i) {
        x = anchor + i;
        OSCCAL = x;
        print_osccal(x);
        delay(1000);
        x = anchor - i;
        OSCCAL = x;
        print_osccal(x);
        delay(1000);
      }
    }
    

    在串行监视器中看到垃圾突然转变为格式良好的消息,这是一个启示(当然,由于搜索是一个无限循环,所以又回到垃圾)。

    现在,ATtiny85 的内部振荡器只能支持 1 MHz 和 8 MHz。据我了解,OSCCAL 的存在是因为这个内部振荡器 1)不是很准确,2)对温度非常敏感。

    如果 ATtiny85 设置为以 16MHz 运行,则它需要一个可靠的外部振荡器,再多摆弄OSCCAL 也无济于事。但是,在我的情况下,它确实让我发现了使SoftwareSerial 在 8 MHz 上滴答作响的值以及从300 到38400 的一系列波特率。

    这让我可以得到正确的值,让位敲击 UART 与 SoftwareSerial 不支持的 1MHz 时钟一起工作。

    【讨论】:

    • 非常有趣的是,时钟频率为 16MHz 的 ATtiny85 可能不可靠。这可能解释了为什么在更高的串行速度下时钟会很快失步。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2020-07-14
    • 2021-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多