【教程】Digispark实现串口通信

转载请注明出处:小锋学长生活大爆炸[xfxuezhagn.cn]

如果本文帮助到了你,欢迎[点赞、收藏、关注]哦~


没想到这么老,很多代码都不能用,修了好久。。。

TinySoftwareSerial.cpp

cpp 复制代码
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>

#include "Arduino.h"
#include "wiring_private.h"

#if USE_SOFTWARE_SERIAL
#include "TinySoftwareSerial.h"

extern "C"{
uint8_t getch() {
  uint8_t ch = 0;
    __asm__ __volatile__ (
    "   rcall uartDelay\n"          // Get to 0.25 of start bit (our baud is too fast, so give room to correct)
    "1: rcall uartDelay\n"              // Wait 0.25 bit period
    "   rcall uartDelay\n"              // Wait 0.25 bit period
    "   rcall uartDelay\n"              // Wait 0.25 bit period
    "   rcall uartDelay\n"              // Wait 0.25 bit period
    "   clc\n"
    "   in r23,%[pin]\n"
    "   and r23, %[mask]\n"
    "   breq 2f\n"
    "   sec\n"
    "2: ror   %0\n"
    "   dec   %[count]\n"
    "   breq  3f\n"
    "   rjmp  1b\n"
    "3: rcall uartDelay\n"              // Wait 0.25 bit period
    "   rcall uartDelay\n"              // Wait 0.25 bit period
    :
      "=r" (ch)
    :
      "0" ((uint8_t)0),
      [count] "r" ((uint8_t)8),
      [pin] "I" (_SFR_IO_ADDR(ANALOG_COMP_PIN)),
      [mask] "r" (Serial._rxmask)
    :
      "r23",
      "r24",
      "r25"
    );
  return ch;
}

void uartDelay() {
  __asm__ __volatile__ (
    "mov r25,%[count]\n"
    "1:dec r25\n"
      "brne 1b\n"
      "ret\n"
    ::[count] "r" ((uint8_t)Serial._delayCount)
  );
}

#if !defined (ANALOG_COMP_vect) && defined(ANA_COMP_vect)
//rename the vector so we can use it.
  #define ANALOG_COMP_vect ANA_COMP_vect
#elif !defined (ANALOG_COMP_vect)
  #error Tiny Software Serial cannot find the Analog comparator interrupt vector!
#endif
ISR(ANALOG_COMP_vect){
  char ch = getch(); //read in the character softwarily - I know its not a word, but it sounded cool, so you know what: #define softwarily 1
  store_char(ch, Serial._rx_buffer);
  sbi(ACSR,ACI); //clear the flag.
}

}
soft_ring_buffer rx_buffer  =  { { 0 }, 0, 0 };

// Constructor 

TinySoftwareSerial::TinySoftwareSerial(soft_ring_buffer *rx_buffer, uint8_t txBit, uint8_t rxBit)
{
  _rx_buffer = rx_buffer;

  _rxmask = _BV(rxBit);
  _txmask = _BV(txBit);
  _txunmask = ~_txmask;

  _delayCount = 0;
}

// Public Methods //
void TinySoftwareSerial::setTxBit(uint8_t txbit)
{
  _txmask=_BV(txbit);
  _txunmask=~txbit;
}

void TinySoftwareSerial::begin(long baud)
{
  long tempDelay = (((F_CPU/baud)-39)/12);
  if ((tempDelay > 255) || (tempDelay <= 0)){
  end(); //Cannot start as it would screw up uartDelay().
  }
  _delayCount = (uint8_t)tempDelay;
  cbi(ACSR,ACIE);  //turn off the comparator interrupt to allow change of ACD
#ifdef ACBG
  sbi(ACSR,ACBG); //enable the internal bandgap reference - used instead of AIN0 to allow it to be used for TX.
#endif
  cbi(ACSR,ACD);  //turn on the comparator for RX
#ifdef ACIC
  cbi(ACSR,ACIC);  //prevent the comparator from affecting timer1 - just to be safe.
#endif
  sbi(ACSR,ACIS1);  //interrupt on rising edge (this means RX has gone from Mark state to Start bit state).
  sbi(ACSR,ACIS0);
  //Setup the pins in case someone messed with them.
  ANALOG_COMP_DDR &= ~_rxmask; //set RX to an input
  ANALOG_COMP_PORT |= _rxmask; //enable pullup on RX pin - to prevent accidental interrupt triggers.
  ANALOG_COMP_DDR |= _txmask; //set TX to an output.
  ANALOG_COMP_PORT |= _txmask; //set TX pin high
  sbi(ACSR,ACI); //clear the flag.
  sbi(ACSR,ACIE);  //turn on the comparator interrupt to allow us to use it for RX
#ifdef ACSRB
  ACSRB = 0; //Use AIN0 as +, AIN1 as -, no hysteresis - just like ones without this register.
#endif
}

void TinySoftwareSerial::end()
{
  sbi(ACSR,ACI); //clear the flag.
  cbi(ACSR,ACIE);  //turn off the comparator interrupt to allow change of ACD, and because it needs to be turned off now too!
#ifdef ACBG
  cbi(ACSR,ACBG); //disable the bandgap reference
#endif
  sbi(ACSR,ACD);  //turn off the comparator to save power
  _delayCount = 0;
  _rx_buffer->head = _rx_buffer->tail;
}

int TinySoftwareSerial::available(void)
{
  return (unsigned int)(SERIAL_BUFFER_SIZE + _rx_buffer->head - _rx_buffer->tail) % SERIAL_BUFFER_SIZE;
}

void store_char(unsigned char c, soft_ring_buffer *buffer)
{
  int i = (unsigned int)(buffer->head + 1) % SERIAL_BUFFER_SIZE;

  // if we should be storing the received character into the location
  // just before the tail (meaning that the head would advance to the
  // current location of the tail), we're about to overflow the buffer
  // and so we don't write the character or advance the head.
  if (i != buffer->tail) {
    buffer->buffer[buffer->head] = c;
    buffer->head = i;
  }
}

int TinySoftwareSerial::peek(void)
{
  if (_rx_buffer->head == _rx_buffer->tail) {
    return -1;
  } else {
    return _rx_buffer->buffer[_rx_buffer->tail];
  }
}

int TinySoftwareSerial::read(void)
{
  // if the head isn't ahead of the tail, we don't have any characters
  if (_rx_buffer->head == _rx_buffer->tail) {
    return -1;
  } else {
    unsigned char c = _rx_buffer->buffer[_rx_buffer->tail];
    _rx_buffer->tail = (unsigned int)(_rx_buffer->tail + 1) % SERIAL_BUFFER_SIZE;
    return c;
  }
}

size_t TinySoftwareSerial::write(uint8_t ch)
{
  uint8_t oldSREG = SREG;
  cli(); //Prevent interrupts from breaking the transmission. Note: TinySoftwareSerial is half duplex.
  //it can either receive or send, not both (because receiving requires an interrupt and would stall transmission
  __asm__ __volatile__ (
    "   com %[ch]             \n" // ones complement, carry set
    "   sec                   \n"
    "1: brcc 2f               \n"
    "   in r23,%[uartPort]    \n"
    "   and r23,%[uartUnmask] \n"
    "   out %[uartPort],r23   \n"
    "   rjmp 3f               \n"
    "2: in r23,%[uartPort]    \n"
    "   or r23,%[uartMask]    \n"
    "   out %[uartPort],r23   \n"
    "   nop                   \n"
    "3: rcall uartDelay       \n"
    "   rcall uartDelay       \n"
    "   rcall uartDelay       \n"
    "   rcall uartDelay       \n"
    "   lsr %[ch]             \n"
    "   dec %[count]          \n"
    "   brne 1b               \n"
    :
    :
      [ch] "r" (ch),
      [count] "r" ((uint8_t)10),
      [uartPort] "I" (_SFR_IO_ADDR(ANALOG_COMP_PORT)),
      [uartMask] "r" (_txmask),
      [uartUnmask] "r" (_txunmask)
    : "r23",
      "r24",
      "r25"
  );
  SREG = oldSREG;
  return 1;
}

void TinySoftwareSerial::flush()
{

}

TinySoftwareSerial::operator bool() {
  return true;
}

// Preinstantiate Objects //
#ifndef ANALOG_COMP_DDR
#error Please define ANALOG_COMP_DDR in the pins_arduino.h file!
#endif

#ifndef ANALOG_COMP_PORT
#error Please define ANALOG_COMP_PORT in the pins_arduino.h file!
#endif

#ifndef ANALOG_COMP_PIN
#error Please define ANALOG_COMP_PIN in the pins_arduino.h file!
#endif

#ifndef ANALOG_COMP_AIN0_BIT
#error Please define ANALOG_COMP_AIN0_BIT in the pins_arduino.h file!
#endif

#ifndef ANALOG_COMP_AIN1_BIT
#error Please define ANALOG_COMP_AIN1_BIT in the pins_arduino.h file!
#endif

TinySoftwareSerial Serial(&rx_buffer, ANALOG_COMP_AIN0_BIT, ANALOG_COMP_AIN1_BIT);

#endif // whole file

TinySoftwareSerial.h

cpp 复制代码
#if USE_SOFTWARE_SERIAL
#ifndef TinySoftwareSerial_h
#define TinySoftwareSerial_h
#include <inttypes.h>
#include "Stream.h"

#if !defined(ACSR) && defined(ACSRA)
#define ACSR ACSRA
#endif

#if (RAMEND < 250)
  #define SERIAL_BUFFER_SIZE 8
#elif (RAMEND < 500)
  #define SERIAL_BUFFER_SIZE 16
#elif (RAMEND < 1000)
  #define SERIAL_BUFFER_SIZE 32
#else
  #define SERIAL_BUFFER_SIZE 128
#endif
struct soft_ring_buffer
{
  volatile unsigned char buffer[SERIAL_BUFFER_SIZE];
  volatile int head;
  volatile int tail;
};

extern "C"{
  void uartDelay() __attribute__ ((naked,used)); //used attribute needed to prevent LTO from throwing it out.
  uint8_t getch();
  void store_char(unsigned char c, soft_ring_buffer *buffer);
}

class TinySoftwareSerial : public Stream
{
  public: //should be private but needed by extern "C" {} functions.
  uint8_t _rxmask;
  uint8_t _txmask;
  uint8_t _txunmask;
  soft_ring_buffer *_rx_buffer;
  uint8_t _delayCount;
  public:
    TinySoftwareSerial(soft_ring_buffer *rx_buffer, uint8_t txBit, uint8_t rxBit);
    void begin(long);
    void setTxBit(uint8_t);
    void end();
    virtual int available(void);
    virtual int peek(void);
    virtual int read(void);
    virtual void flush(void);
    virtual size_t write(uint8_t);
    using Print::write; // pull in write(str) and write(buf, size) from Print
    operator bool();
};

#if (!defined(UBRRH) && !defined(UBRR0H)) || USE_SOFTWARE_SERIAL
  extern TinySoftwareSerial Serial;
#endif

//extern void putch(uint8_t);
#endif
#endif

pins_arduino.h

cpp 复制代码
#ifndef Pins_Arduino_h
#define Pins_Arduino_h

#include <avr/pgmspace.h>

#include "core_build_options.h"

//WARNING, if using software, TX is on AIN0, RX is on AIN1. Comparator is favoured to use its interrupt for the RX pin.
#define USE_SOFTWARE_SERIAL           1
//Please define the port on which the analog comparator is found.
#define ANALOG_COMP_DDR               DDRB
#define ANALOG_COMP_PORT              PORTB
#define ANALOG_COMP_PIN               PINB
#define ANALOG_COMP_AIN0_BIT          0
#define ANALOG_COMP_AIN1_BIT          1



#if defined( __AVR_ATtinyX313__ )
#define PORT_A_ID 1
#define PORT_B_ID 2
#define PORT_D_ID 4
#endif

#if defined( __AVR_ATtinyX4__ )
#define PORT_A_ID 1
#define PORT_B_ID 2
#endif

#if defined( __AVR_ATtinyX5__ )
#define PORT_B_ID 1
#endif

#define NOT_A_PIN 0
#define NOT_A_PORT 0

#define NOT_ON_TIMER 0
#define TIMER0A 1
#define TIMER0B 2
#define TIMER1A 3
#define TIMER1B 4

//changed it to uint16_t to uint8_t
extern const uint8_t PROGMEM port_to_mode_PGM[];
extern const uint8_t PROGMEM port_to_input_PGM[];
extern const uint8_t PROGMEM port_to_output_PGM[];
extern const uint8_t PROGMEM port_to_pcmask_PGM[];

extern const uint8_t PROGMEM digital_pin_to_port_PGM[];
// extern const uint8_t PROGMEM digital_pin_to_bit_PGM[];
extern const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[];
extern const uint8_t PROGMEM digital_pin_to_timer_PGM[];

// Get the bit location within the hardware port of the given virtual pin.
// This comes from the pins_*.c file for the active board configuration.
// 
// These perform slightly better as macros compared to inline functions
//
#define digitalPinToPort(P) ( pgm_read_byte( digital_pin_to_port_PGM + (P) ) )
#define digitalPinToBitMask(P) ( pgm_read_byte( digital_pin_to_bit_mask_PGM + (P) ) )
#define digitalPinToTimer(P) ( pgm_read_byte( digital_pin_to_timer_PGM + (P) ) )
#define analogInPinToBit(P) (P)
// in the following lines modified pgm_read_word in pgm_read_byte, word doesn't work on attiny45
#define portOutputRegister(P) ( (volatile uint8_t *)( pgm_read_byte( port_to_output_PGM + (P))) )
#define portInputRegister(P) ( (volatile uint8_t *)( pgm_read_byte( port_to_input_PGM + (P))) )
#define portModeRegister(P) ( (volatile uint8_t *)( pgm_read_byte( port_to_mode_PGM + (P))) )
#define portPcMaskRegister(P) ( (volatile uint8_t *)( pgm_read_byte( port_to_pcmask_PGM + (P))) )

#if defined(__AVR_ATtinyX5__)
#define digitalPinToPCICR(p)    (((p) >= 0 && (p) <= 5) ? (&GIMSK) : ((uint8_t *)NULL))
#define digitalPinToPCICRbit(p) (PCIE)
#define digitalPinToPCMSK(p)    (((p) >= 0 && (p) <= 5) ? (&PCMSK) : ((uint8_t *)NULL))
#define digitalPinToPCMSKbit(p) (p)
#endif

#if defined(__AVR_ATtinyX4__)
#define digitalPinToPCICR(p)    (((p) >= 0 && (p) <= 10) ? (&GIMSK) : ((uint8_t *)NULL))
#define digitalPinToPCICRbit(p) (((p) <= 2) ? PCIE1 : PCIE0)
#define digitalPinToPCMSK(p)    (((p) <= 2) ? (&PCMSK1) : (((p) <= 10) ? (&PCMSK0) : ((uint8_t *)NULL)))
#define digitalPinToPCMSKbit(p) (((p) <= 2) ? (p) : (10 - (p)))
#endif

#if defined(__AVR_ATtiny4313__)
#define digitalPinToPCX(p,s1,s2,s3,s4,s5) \
    (((p) >= 0) \
        ? (((p) <=  1) ? (s1)  /*  0 -  1  ==>  D0 - D1 */  \
        : (((p) <=  3) ? (s2)  /*  2 -  3  ==>  A1 - A0 */  \
        : (((p) <=  8) ? (s3)  /*  4 -  8  ==>  D2 - D6 */  \
        : (((p) <= 16) ? (s4)  /*  9 - 16  ==>  B0 - B7 */  \
        : (s5))))) \
        : (s5))
//                                                   s1 D     s2 A     s3 D     s4 B
#define digitalPinToPCICR(p)    digitalPinToPCX( p, &GIMSK,  &GIMSK,  &GIMSK,  &GIMSK,  NULL )
#define digitalPinToPCICRbit(p) digitalPinToPCX( p, PCIE2,   PCIE1,   PCIE2,   PCIE0,   0    )
#define digitalPinToPCMSK(p)    digitalPinToPCX( p, &PCMSK2, &PCMSK1, &PCMSK2, &PCMSK0, NULL )
#define digitalPinToPCMSKbit(p) digitalPinToPCX( p, p,       3-p,     p-2,     p-9,     0    )
#endif

#endif

实测效果

相关推荐
yangchanghua1111 小时前
pgsql 如何查询今天范围内的数据(当天0点0分0秒 - 当天23点59分59秒....)
数据库·pgsql
larance1 小时前
SQLAlchemy 的异步操作来批量保存对象列表
数据库·python
python_chai1 小时前
从数据汇总到高级分析,SQL 查询进阶实战(下篇)—— 分组、子查询与窗口函数全攻略
数据库·sql·mysql
在努力的前端小白2 小时前
Spring Boot 敏感词过滤组件实现:基于DFA算法的高效敏感词检测与替换
java·数据库·spring boot·文本处理·敏感词过滤·dfa算法·组件开发
未来之窗软件服务2 小时前
自建知识库,向量数据库 (九)之 量化前奏分词服务——仙盟创梦IDE
数据库·仙盟创梦ide·东方仙盟·自建ai·ai分词
冒泡的肥皂5 小时前
MVCC初学demo(一
数据库·后端·mysql
.Shu.6 小时前
Redis Reactor 模型详解【基本架构、事件循环机制、结合源码详细追踪读写请求从客户端连接到命令执行的完整流程】
数据库·redis·架构
薛晓刚9 小时前
当MySQL的int不够用了
数据库
SelectDB技术团队9 小时前
Apache Doris 在菜鸟的大规模湖仓业务场景落地实践
数据库·数据仓库·数据分析·apache doris·菜鸟技术
星空下的曙光9 小时前
mysql 命令语法操作篇 数据库约束有哪些 怎么使用
数据库·mysql