Arduino 代码武器库(持续更新中)

各种板子引脚定义

在这里插入图片描述
在这里插入图片描述在这里插入图片描述https://arduino.nxez.com/wp-content/uploads/2016/12/145202dn1isx9aiz9ajaox.jpg在这里插入图片描述其他:
https://arduino.nxez.com/pinout-diagram-of-arduino-versions

3283和328P芯片引脚定义

在这里插入图片描述在这里插入图片描述

快速写一个基于Json解析的灵活串口Interface

安装的时候:一定选择ArduinoJson 5版本
任意arduino都可
在这里插入图片描述
5版本的使用教程:

深入学习ArduinoJson库 V5版本:
https://blog.csdn.net/dpjcn1990/article/details/92831648

#include <ArduinoJson.h>
unsigned int timecnt;
String ReceivedCache="";
int json_finish_flag = 0;
char ch[200];
StaticJsonBuffer<200> jsonBuffer;
float ki = 10;
float kp = 20;
float kd = 30;
int mode =0;

void setup() {
  Serial.begin(115200);
  while (!Serial) {
    // wait serial port initialization
  }
}

void handle_g_bty(){
  Serial.println("handle_it!");
}
void loop() {
   char readChar=' ';
   while(Serial.available()>0){
      readChar=(char)Serial.read();
      ReceivedCache =ReceivedCache+ (String)readChar;
      if(readChar == '}'){
        json_finish_flag = 1;
      }  
   }
   timecnt = micros();//us
   if(!ReceivedCache.startsWith("{")){
      ReceivedCache.remove(0);
   }
   if(json_finish_flag == 1){
      json_finish_flag=0;
      ReceivedCache.toCharArray(ch, ReceivedCache.length()+1);//TODO
      JsonObject& root = jsonBuffer.parseObject(ch);
      if (root.success()) {
        // In other case, you can do root["time"].as<long>();
        const char* sensor = root["sensor"];
        long time = root["time"];
        double latitude = root["data"][0];
        double longitude = root["data"][1];
        if(root.containsKey("s_kp")){
          kp = root["s_kp"].as<float>();
        }
        if(root.containsKey("s_ki")){
          ki = root["s_ki"].as<float>();
        }
        if(root.containsKey("s_kd")){
          kd = root["s_kd"].as<float>();
        }
        if(root.containsKey("s_mode")){
          mode = root["s_mode"].as<int>();
        }
        if(root.containsKey("g_bty")){
          handle_g_bty();
        }
        timecnt = micros()-timecnt;
        Serial.println(timecnt);
     }
     ReceivedCache ="";
     jsonBuffer.clear();
    
   }
   
   
}

解析时间很短:大约几百us,可以跑满115200串口速率。

Arduino 保存int,float,byte到芯片内的FLASH中,再读回来

任意arduino都可

#include<EEPROM.h>
float kp = 88;
float ki = 22;
float kd = 33;
unsigned char mode = 0;
union floatintchar{
  uint8_t char_data[4];
  float float_data;
  int int_data;
};
floatintchar floatintchar_u;




void setup() {
  Serial.begin(19200);
  loadConfigFromEEPROM();
}

void loop() {
  while(1);
}




void saveFloatToEEPROM(int startAddr,float Val){
  floatintchar_u.float_data = Val;
  for(int i=0;i<4;i++){
     EEPROM.write(startAddr+i,floatintchar_u.char_data[i]);
  }
}

float getFloatFromEEPROM(int startAddr){
  for(int i=0;i<4;i++){
     floatintchar_u.char_data[i] = EEPROM.read(startAddr+i);
  }
  return floatintchar_u.float_data;
}

int saveIntToEEPROM(int startAddr,int Val){
  floatintchar_u.int_data = Val;
  for(int i=0;i<4;i++){
     EEPROM.write(startAddr+i,floatintchar_u.char_data[i]);
  }
}

int getIntFromEEPROM(int startAddr){
  for(int i=0;i<4;i++){
     floatintchar_u.char_data[i] = EEPROM.read(startAddr+i);
  }
  return floatintchar_u.int_data;
}


void saveConfigToEEPROM(){
  saveFloatToEEPROM(0,kp);
  saveFloatToEEPROM(4,ki);
  saveFloatToEEPROM(8,kd);
}
void loadConfigFromEEPROM(){
  kp = getFloatFromEEPROM(0);
  ki = getFloatFromEEPROM(4);
  kd = getFloatFromEEPROM(8);
}

好用又清晰的双电机增量式PID速度控制器+AB霍尔编码器测速+滤波器(注释全)

使用在arduino mega上

/左电机端口定义
#define MotorLpinEN   35 //控制位3
#define MotorLpinDIR   37 //控制位4
#define MotorLpwm    3  //使能调速 ENB
#define MotorLcountA 18 //编码器A 中断号:5
#define MotorLcountB 19 //编码器B 中断号:4
 
//右电机端口定义
#define MotorRpinEN   31 //控制位1
#define MotorRpinDIR   33 //控制位2
#define MotorRpwm    2  //使能调速 ENA
#define MotorRcountA 20 //编码器A 中断号:3
#define MotorRcountB 21 //编码器B 中断号:2
 
volatile float motorL=0;//中断变量,左轮子脉冲计数
volatile float motorR=0;//中断变量,右轮子脉冲计数
float V_L=0; //左轮速度 单位cm/s
float V_R=0; //右边轮速 单位cm/s
float v1=0;  //单位cm/s
float v2=0;  //单位cm/s
float Target_V_L=30,Target_V_R=30;   //单位cm/s
int Pwm_L=0,Pwm_R=0;  //左右轮PWM
 
 
//PID变量
float kp=2,ki=0.5,kd=0;  //PID参数
 
 
/**************************************
 * Arduino初始化函数
 * 
 *************************************/
void setup() {
   Motor_Init();//电机端口初始化
   Serial.begin(19200);//开启串口
}
 
 
 
/*********************************************************
 * 函数功能:增量式PI控制器(左轮)
 * 入口参数:当前速度(编码器测量值),目标速度
 * 返回 值:电机PWM 
 * 参考资料: 
 *    增量式离散PID公式:
 *                Pwm-=Kp*[e(k)-e(k-1)]+Ki*e(k)+Kd*[e(k)-2e(k-1)+e(k-2)]
 *                e(k):本次偏差
 *                e(k-1):上一次偏差
 *                e(k-2):上上次偏差
 *                Pwm:代表增量输出
 *    在速度闭环控制系统里面我们只使用PI控制,因此对PID公式可简化为:
 *                Pwm-=Kp*[e(k)-e(k-1)]+Ki*e(k)
 *                e(k):本次偏差
 *                e(k-1):上一次偏差
 *                Pwm:代表增量输出
 *                
 *    注意增量式PID先调I,再调P,最后再调D
 *********************************************************/
 int Incremental_Pi_L(int current_speed,int target_speed){
  static float pwm_L,bias,last_bias,prev_bias;  //静态变量存在程序全周期:pwm_L:增量输出,bias:本次偏差,last_bias:上次偏差,prev_bais_:上上次偏差
  bias=current_speed-target_speed;    //计算本次偏差e(k)
  pwm_L-=(kp*(bias-last_bias)+ki*bias+kd*(bias-2*last_bias+prev_bias));   //增量式PID控制器
  prev_bias=last_bias;  //保存上上次偏差
  last_bias=bias;     //保存上一次偏差
 
 
 
  //pwm_L 限幅度  Arduino的PWM 最高为255  限制在250
  if(pwm_L<-250){
    pwm_L=0;     
  }
  if(pwm_L>250){
    pwm_L=250;  
  }
  //Serial.println(pwm_L);
  return pwm_L;         //增量输出
 }
 
 
//右轮速度增量式PID控制器
int Incremental_Pi_R(float current_speed,float target_speed){
  static float pwm_R,bias,last_bias,prev_bias;  //静态变量存在程序全周期:pwm_R:增量输出,bias:本次偏差,last_bias:上次偏差,prev_bais_:上上次偏差
  bias=current_speed-target_speed;    //计算本次偏差e(k)
  pwm_R=pwm_R-(kp*(bias-last_bias)+ki*bias+kd*(bias-2*last_bias+prev_bias));   //增量式PID控制器
  prev_bias=last_bias;  //保存上上次偏差
  last_bias=bias;     //保存上一次偏差
 
  //pwm_R 限幅度  Arduino的pwm_R 最高为255限制在250
  if(pwm_R<0){
    pwm_R=0;     
  }
  if(pwm_R>250){
    pwm_R=250;  
  }
  //Serial.println(pwm_R);
  return pwm_R;         //增量输出
 }
 
/**************************************************************************(测试完成)
  函数功能:设置双轮工作模式和运动速度
  入口参数:工作模式,左右轮pwm
  返回  值:无
**************************************************************************/
void Set_Pwm(int mode,int speed_L,int speed_R){
 
  if(mode==1){
  //前进模式
  //左电机
  digitalWrite(MotorLpinEN,HIGH);
  digitalWrite(MotorLpinDIR,HIGH);
  analogWrite(MotorLpwm,speed_L);
  
  //右电机
  digitalWrite(MotorRpinEN,HIGH);
  digitalWrite(MotorRpinDIR,HIGH);
  analogWrite(MotorRpwm,speed_R);
  
  }else if(mode==2){
  //后退模式
  //左电机
  digitalWrite(MotorLpinEN,HIGH);
  digitalWrite(MotorLpinDIR,LOW);
  analogWrite(MotorLpwm,speed_L);
  
  //右电机
  digitalWrite(MotorRpinEN,HIGH);
  digitalWrite(MotorRpinDIR,LOW);
  analogWrite(MotorRpwm,speed_R);
  }else if(mode==3){
  //左转模式
  //左电机
  digitalWrite(MotorLpinEN,HIGH);
  digitalWrite(MotorLpinDIR,LOW);
  analogWrite(MotorLpwm,speed_L);
  
  //右电机
  digitalWrite(MotorRpinEN,HIGH);
  digitalWrite(MotorRpinDIR,LOW);
  analogWrite(MotorRpwm,speed_R);
  
  }else if(mode==4){
  //右转模式
  //左电机
  digitalWrite(MotorLpinEN,LOW);
  digitalWrite(MotorLpinDIR,HIGH);
  analogWrite(MotorLpwm,speed_L);
  
  //右电机
  digitalWrite(MotorRpinEN,LOW);
  digitalWrite(MotorRpinDIR,HIGH);
  analogWrite(MotorRpwm,speed_R);
  
  }
}
 
 
 /**************************************************************************(测试完成)
  函数功能:电机端口初始化,控制芯片引脚拉低
  入口参数:无
  返回  值:无
**************************************************************************/
void Motor_Init(){
  //左电机
  pinMode(MotorLpinEN,OUTPUT);  //驱动芯片控制引脚
  pinMode(MotorLpinDIR,OUTPUT);  //驱动芯片控制引脚
  pinMode(MotorLpwm,OUTPUT);   //驱动芯片控制引脚,PWM调速
  pinMode(MotorLcountA,INPUT); //左轮编码器A引脚
  pinMode(MotorLcountB,INPUT); //左轮编码器B引脚
  
  //右电机
  pinMode(MotorRpinEN,OUTPUT);  //驱动芯片控制引脚
  pinMode(MotorRpinDIR,OUTPUT);  //驱动芯片控制引脚
  pinMode(MotorRpwm,OUTPUT);   //驱动芯片控制引脚,PWM调速
  pinMode(MotorRcountA,INPUT); //右轮编码器A引脚
  pinMode(MotorRcountB,INPUT); //右轮编码器B引脚
 
  //驱动芯片控制引脚全部拉低
  digitalWrite(MotorLpinEN,LOW); //左电机
  digitalWrite(MotorLpinDIR,LOW);
  digitalWrite(MotorLpwm,LOW);
  digitalWrite(MotorRpinEN,LOW); //右电机
  digitalWrite(MotorRpinDIR,LOW);
  digitalWrite(MotorRpwm,LOW);
}
 
 
 
/***********************************
 * 电机实际速度计算:
 * 公式:
 * 已知参数:
 *     车轮直径65mm,
 *     左边轮子一圈:390脉冲(RISING),
 *     右边轮子一圈:390脉冲(RISING),
 * 单位时间读两个轮子脉冲读取两个轮子脉冲
 ***********************************/
 void Read_Moto_V(){
  unsigned long nowtime=0;
  motorL=0;
  motorR=0;
  nowtime=millis()+50;//读50毫秒
  attachInterrupt(digitalPinToInterrupt(MotorLcountA),Read_Moto_L,RISING);//左轮脉冲开中断计数
  attachInterrupt(digitalPinToInterrupt(MotorRcountA),Read_Moto_R,RISING);//右轮脉冲开中断计数
  while(millis()<nowtime); //达到50毫秒关闭中断
  detachInterrupt(digitalPinToInterrupt(MotorLcountA));//左轮脉冲关中断计数
  detachInterrupt(digitalPinToInterrupt(MotorRcountA));//右轮脉冲关中断计数
  V_L=((motorL/390)*6.5*PI)/0.05;   //单位cm/s
  V_R=((motorR/390)*6.5*PI)/0.05;   //单位cm/s
  v1=V_L;
  v2=V_R;
}
 
 
 
/***************************
 * 中断函数:读左轮脉冲
 *
 **************************/
void Read_Moto_L(){
  motorL++;
}
 
 
 
/**************************
 * 中断函数:读右轮脉冲
 * 
 *************************/
void Read_Moto_R(){
  motorR++;
}


 // 递推平均滤波法(又称滑动平均滤波法)
#define FILTER_N 6
float filter_buf_L[FILTER_N + 1];
float Filter_L(float Val) {
  int i;
  float filter_sum = 0;
  filter_buf_L[FILTER_N] = Val;
  for(i = 0; i < FILTER_N; i++) {
    filter_buf_L[i] = filter_buf_L[i + 1]; // 所有数据左移,低位仍掉
    filter_sum += filter_buf_L[i];
  }
  return (float)(filter_sum / FILTER_N);
}

float filter_buf_R[FILTER_N + 1];
float Filter_R(float Val) {
  int i;
  float filter_sum = 0;
  filter_buf_R[FILTER_N] = Val;
  for(i = 0; i < FILTER_N; i++) {
    filter_buf_R[i] = filter_buf_R[i + 1]; // 所有数据左移,低位仍掉
    filter_sum += filter_buf_R[i];
  }
  return (float)(filter_sum / FILTER_N);
}
 
/***************************************
 * Arduino主循环
 * 
 ***************************************/
void loop() {
  Read_Moto_V();//读取脉冲计算速度
  Serial.print(V_L);
  Serial.print(" ");
  Serial.println(V_R);
  V_L = Filter_L(V_L);
  V_R = Filter_R(V_R);
  Pwm_L=255-Incremental_Pi_L(V_L,Target_V_L);//左轮PI运算
  Pwm_R=255-Incremental_Pi_R(V_R,Target_V_R);//右轮PI运算
  
  //Serial.print("Pwm_L:");
  //Serial.println(Pwm_L);
  Set_Pwm(1,Pwm_L,Pwm_R);  //设置左右轮速度
}
 

在Arduino上使用printf格式化输出到串口(能够使用%f%d这些)

不少朋友尝试在Arduino上使用printf格式化输出,但没有效果。在PC上printf默认输出到屏幕,但arduino不是PC,也没有屏幕,所以不能直接使用。
这里提供一个使用printf输出到串口的方法:

首先在程序中加入这两个函数:

int serial_putc( char c, struct __file * )
{
  Serial.write( c );
  return c;
}
void printf_begin(void)
{
  fdevopen( &serial_putc, 0 );
}
void setup() {
  Serial.begin(115200);
  printf_begin();
 
  //其他代码
}

现在你可以尝试下使用printf输出了:

int cs=12345;
float cf=12.345;
char c[15]="Hello,world!";
printf("***arduino.cn***\n\r");
printf("111:%d\n",cs);
printf("222:%lf\n",cf);

注意!无法格式化%f!这里的为精简的printf
在这里插入图片描述

上面几个的综合:底盘控制器(半成品)

#include<EEPROM.h>
#include <ArduinoJson.h>
/*--------------------------Pin Define&Function--------------------------*/
//左电机端口定义
#define MotorLpinEN   35 //控制位3
#define MotorLpinDIR   37 //控制位4
#define MotorLpwm    3  //使能调速 ENB
#define MotorLcountA 18 //编码器A 中断号:5
#define MotorLcountB 19 //编码器B 中断号:4
 
//右电机端口定义
#define MotorRpinEN   31 //控制位1
#define MotorRpinDIR   33 //控制位2
#define MotorRpwm    2  //使能调速 ENA
#define MotorRcountA 20 //编码器A 中断号:3
#define MotorRcountB 21 //编码器B 中断号:2
 
volatile float motorL=0;//中断变量,左轮子脉冲计数
volatile float motorR=0;//中断变量,右轮子脉冲计数
float V_L=0; //左轮速度 单位cm/s
float V_R=0; //右边轮速 单位cm/s
float v1=0;  //单位cm/s
float v2=0;  //单位cm/s
float Target_V_L=30,Target_V_R=30;   //单位cm/s
int Pwm_L=0,Pwm_R=0;  //左右轮PWM
 
 
//PID变量
float kp=2,ki=0.5,kd=0;  //PID参数

//MODE变量
int mode =0;

//bty_soc变量
int bty_soc = 0;

int debug_mode =0;

/*--------------------------EEPROM Function--------------------------*/
union floatintchar{
  uint8_t char_data[4];
  float float_data;
  int int_data;
};
floatintchar floatintchar_u;

void saveFloatToEEPROM(int startAddr,float Val){
  floatintchar_u.float_data = Val;
  for(int i=0;i<4;i++){
     EEPROM.write(startAddr+i,floatintchar_u.char_data[i]);
  }
}

float getFloatFromEEPROM(int startAddr){
  for(int i=0;i<4;i++){
     floatintchar_u.char_data[i] = EEPROM.read(startAddr+i);
  }
  return floatintchar_u.float_data;
}

int saveIntToEEPROM(int startAddr,int Val){
  floatintchar_u.int_data = Val;
  for(int i=0;i<2;i++){
     EEPROM.write(startAddr+i,floatintchar_u.char_data[i]);
  }
}

int getIntFromEEPROM(int startAddr){
  for(int i=0;i<2;i++){
     floatintchar_u.char_data[i] = EEPROM.read(startAddr+i);
  }
  return floatintchar_u.int_data;
}


void saveConfigToEEPROM(){
  saveFloatToEEPROM(0,kp);
  saveFloatToEEPROM(4,ki);
  saveFloatToEEPROM(8,kd);
  saveIntToEEPROM(12,mode);
}
void loadConfigFromEEPROM(){
  kp = getFloatFromEEPROM(0);
  ki = getFloatFromEEPROM(4);
  kd = getFloatFromEEPROM(8);
  mode = getIntFromEEPROM(12);
}
/*--------------------------printf Function--------------------------*/
int serial_putc( char c, struct __file * )
{
  Serial.write( c );
  return c;
}
void printf_begin(void)
{
  fdevopen( &serial_putc, 0 );
}
/*--------------------------ArduinoJson Function--------------------------*/
String ReceivedCache="";
int json_finish_flag = 0;
char ch[200];
StaticJsonBuffer<200> jsonBuffer;

void handle_g_bty(){
  printf("{\"bty\":%d}",bty_soc);
}
void handle_g_pid_i(){
  Serial.print("{");
  Serial.print("\"kp\":");
  Serial.print(kp);
  Serial.print(",");
  Serial.print("\"ki\":");
  Serial.print(ki);
  Serial.print(",");
  Serial.print("\"kd\":");
  Serial.print(kd);
  Serial.print(",");
  Serial.print("\"mode\":");
  Serial.print(mode);
  Serial.print("}");
  
}
void handle_g_s(){
;  
}
void json_loop() {
   char readChar=' ';
   while(Serial.available()>0){
      readChar=(char)Serial.read();
      ReceivedCache =ReceivedCache+ (String)readChar;
      if(readChar == '}'){
        json_finish_flag = 1;
      }  
   }
   //timecnt = micros();//us
   if(!ReceivedCache.startsWith("{")){
      ReceivedCache.remove(0);
   }
   if(json_finish_flag == 1){
      json_finish_flag=0;
      ReceivedCache.toCharArray(ch, ReceivedCache.length()+1);//TODO
      JsonObject& root = jsonBuffer.parseObject(ch);
      if (root.success()) {
          // In other case, you can do root["time"].as<long>();
          if(root.containsKey("set")){//任何set 都要写一个Set的键,值任意以触发
             if(root.containsKey("s_debug")){
            debug_mode = root["s_debug"].as<int>();
            if(debug_mode ==1){
              Serial.print("\r\nset OK!debug_mode_Val=");
              Serial.println(debug_mode);
            }
            }
            if(root.containsKey("s_kp")){
            kp = root["s_kp"].as<float>();
            if(debug_mode ==1){
              Serial.print("\r\nset OK!kp_Val=");
              Serial.println(kp);
            }
          }
          if(root.containsKey("s_ki")){
            ki = root["s_ki"].as<float>();
            if(debug_mode ==1){
              Serial.print("\r\nset OK!ki_Val=");
              Serial.println(ki);
            }
          }
          if(root.containsKey("s_kd")){
            kd = root["s_kd"].as<float>();
            if(debug_mode ==1){
              Serial.print("\r\nset OK!kd_Val=");
              Serial.println(kd);
            }
          }
          if(root.containsKey("s_mode")){
            mode = root["s_mode"].as<int>();
            if(debug_mode ==1){
              Serial.print("\r\nset OK!mode_Val=");
              Serial.println(mode);
            }
          }
          if(root.containsKey("s_csave")){
            if(debug_mode ==1){
              saveConfigToEEPROM();
              Serial.println("\r\nset OK!config is saved!");
            }
          }
        }
        if(root.containsKey("g_s")){
          handle_g_s();
        }
        if(root.containsKey("g_bty")){
          handle_g_bty();
        }
        if(root.containsKey("g_pid_i")){
          handle_g_pid_i();
        }
       
        //timecnt = micros()-timecnt;
        //Serial.println(timecnt);
     }
     ReceivedCache ="";
     jsonBuffer.clear();
    
   }
   
   
}


/*--------------------------PID Control Function--------------------------*/
 
 
/*********************************************************
 * 函数功能:增量式PI控制器(左轮)
 * 入口参数:当前速度(编码器测量值),目标速度
 * 返回 值:电机PWM 
 * 参考资料: 
 *    增量式离散PID公式:
 *                Pwm-=Kp*[e(k)-e(k-1)]+Ki*e(k)+Kd*[e(k)-2e(k-1)+e(k-2)]
 *                e(k):本次偏差
 *                e(k-1):上一次偏差
 *                e(k-2):上上次偏差
 *                Pwm:代表增量输出
 *    在速度闭环控制系统里面我们只使用PI控制,因此对PID公式可简化为:
 *                Pwm-=Kp*[e(k)-e(k-1)]+Ki*e(k)
 *                e(k):本次偏差
 *                e(k-1):上一次偏差
 *                Pwm:代表增量输出
 *                
 *    注意增量式PID先调I,再调P,最后再调D
 *********************************************************/
 int Incremental_Pi_L(int current_speed,int target_speed){
  static float pwm_L,bias,last_bias,prev_bias;  //静态变量存在程序全周期:pwm_L:增量输出,bias:本次偏差,last_bias:上次偏差,prev_bais_:上上次偏差
  bias=current_speed-target_speed;    //计算本次偏差e(k)
  pwm_L-=(kp*(bias-last_bias)+ki*bias+kd*(bias-2*last_bias+prev_bias));   //增量式PID控制器
  prev_bias=last_bias;  //保存上上次偏差
  last_bias=bias;     //保存上一次偏差
 
 
 
  //pwm_L 限幅度  Arduino的PWM 最高为255  限制在250
  if(pwm_L<-250){
    pwm_L=0;     
  }
  if(pwm_L>250){
    pwm_L=250;  
  }
  //Serial.println(pwm_L);
  return pwm_L;         //增量输出
 }
 
 
//右轮速度增量式PID控制器
int Incremental_Pi_R(float current_speed,float target_speed){
  static float pwm_R,bias,last_bias,prev_bias;  //静态变量存在程序全周期:pwm_R:增量输出,bias:本次偏差,last_bias:上次偏差,prev_bais_:上上次偏差
  bias=current_speed-target_speed;    //计算本次偏差e(k)
  pwm_R=pwm_R-(kp*(bias-last_bias)+ki*bias+kd*(bias-2*last_bias+prev_bias));   //增量式PID控制器
  prev_bias=last_bias;  //保存上上次偏差
  last_bias=bias;     //保存上一次偏差
 
  //pwm_R 限幅度  Arduino的pwm_R 最高为255限制在250
  if(pwm_R<0){
    pwm_R=0;     
  }
  if(pwm_R>250){
    pwm_R=250;  
  }
  //Serial.println(pwm_R);
  return pwm_R;         //增量输出
 }
 
/**************************************************************************(测试完成)
  函数功能:设置双轮工作模式和运动速度
  入口参数:工作模式,左右轮pwm
  返回  值:无
**************************************************************************/
void Set_Pwm(int mode,int speed_L,int speed_R){
 
  if(mode==1){
  //前进模式
  //左电机
  digitalWrite(MotorLpinEN,HIGH);
  digitalWrite(MotorLpinDIR,HIGH);
  analogWrite(MotorLpwm,speed_L);
  
  //右电机
  digitalWrite(MotorRpinEN,HIGH);
  digitalWrite(MotorRpinDIR,HIGH);
  analogWrite(MotorRpwm,speed_R);
  
  }else if(mode==2){
  //后退模式
  //左电机
  digitalWrite(MotorLpinEN,HIGH);
  digitalWrite(MotorLpinDIR,LOW);
  analogWrite(MotorLpwm,speed_L);
  
  //右电机
  digitalWrite(MotorRpinEN,HIGH);
  digitalWrite(MotorRpinDIR,LOW);
  analogWrite(MotorRpwm,speed_R);
  }else if(mode==3){
  //左转模式
  //左电机
  digitalWrite(MotorLpinEN,HIGH);
  digitalWrite(MotorLpinDIR,LOW);
  analogWrite(MotorLpwm,speed_L);
  
  //右电机
  digitalWrite(MotorRpinEN,HIGH);
  digitalWrite(MotorRpinDIR,LOW);
  analogWrite(MotorRpwm,speed_R);
  
  }else if(mode==4){
  //右转模式
  //左电机
  digitalWrite(MotorLpinEN,LOW);
  digitalWrite(MotorLpinDIR,HIGH);
  analogWrite(MotorLpwm,speed_L);
  
  //右电机
  digitalWrite(MotorRpinEN,LOW);
  digitalWrite(MotorRpinDIR,HIGH);
  analogWrite(MotorRpwm,speed_R);
  
  }
}
 
 
 /**************************************************************************(测试完成)
  函数功能:电机端口初始化,控制芯片引脚拉低
  入口参数:无
  返回  值:无
**************************************************************************/
void Motor_Init(){
  //左电机
  pinMode(MotorLpinEN,OUTPUT);  //驱动芯片控制引脚
  pinMode(MotorLpinDIR,OUTPUT);  //驱动芯片控制引脚
  pinMode(MotorLpwm,OUTPUT);   //驱动芯片控制引脚,PWM调速
  pinMode(MotorLcountA,INPUT); //左轮编码器A引脚
  pinMode(MotorLcountB,INPUT); //左轮编码器B引脚
  
  //右电机
  pinMode(MotorRpinEN,OUTPUT);  //驱动芯片控制引脚
  pinMode(MotorRpinDIR,OUTPUT);  //驱动芯片控制引脚
  pinMode(MotorRpwm,OUTPUT);   //驱动芯片控制引脚,PWM调速
  pinMode(MotorRcountA,INPUT); //右轮编码器A引脚
  pinMode(MotorRcountB,INPUT); //右轮编码器B引脚
 
  //驱动芯片控制引脚全部拉低
  digitalWrite(MotorLpinEN,LOW); //左电机
  digitalWrite(MotorLpinDIR,LOW);
  digitalWrite(MotorLpwm,LOW);
  digitalWrite(MotorRpinEN,LOW); //右电机
  digitalWrite(MotorRpinDIR,LOW);
  digitalWrite(MotorRpwm,LOW);
}
 
 
 
/***********************************
 * 电机实际速度计算:
 * 公式:
 * 已知参数:
 *     车轮直径65mm,
 *     左边轮子一圈:390脉冲(RISING),
 *     右边轮子一圈:390脉冲(RISING),
 * 单位时间读两个轮子脉冲读取两个轮子脉冲
 ***********************************/
 void Read_Moto_V(){
  unsigned long nowtime=0;
  motorL=0;
  motorR=0;
  nowtime=millis()+50;//读50毫秒
  attachInterrupt(digitalPinToInterrupt(MotorLcountA),Read_Moto_L,RISING);//左轮脉冲开中断计数
  attachInterrupt(digitalPinToInterrupt(MotorRcountA),Read_Moto_R,RISING);//右轮脉冲开中断计数
  while(millis()<nowtime); //达到50毫秒关闭中断
  detachInterrupt(digitalPinToInterrupt(MotorLcountA));//左轮脉冲关中断计数
  detachInterrupt(digitalPinToInterrupt(MotorRcountA));//右轮脉冲关中断计数
  V_L=((motorL/390)*6.5*PI)/0.05;   //单位cm/s
  V_R=((motorR/390)*6.5*PI)/0.05;   //单位cm/s
  v1=V_L;
  v2=V_R;
}
 
 
 
/***************************
 * 中断函数:读左轮脉冲
 *
 **************************/
void Read_Moto_L(){
  motorL++;
}
 
 
 
/**************************
 * 中断函数:读右轮脉冲
 * 
 *************************/
void Read_Moto_R(){
  motorR++;
}

/*--------------------------Filter Function--------------------------*/

 // 递推平均滤波法(又称滑动平均滤波法)
#define FILTER_N 6
float filter_buf_L[FILTER_N + 1];
float Filter_L(float Val) {
  int i;
  float filter_sum = 0;
  filter_buf_L[FILTER_N] = Val;
  for(i = 0; i < FILTER_N; i++) {
    filter_buf_L[i] = filter_buf_L[i + 1]; // 所有数据左移,低位仍掉
    filter_sum += filter_buf_L[i];
  }
  return (float)(filter_sum / FILTER_N);
}

float filter_buf_R[FILTER_N + 1];
float Filter_R(float Val) {
  int i;
  float filter_sum = 0;
  filter_buf_R[FILTER_N] = Val;
  for(i = 0; i < FILTER_N; i++) {
    filter_buf_R[i] = filter_buf_R[i + 1]; // 所有数据左移,低位仍掉
    filter_sum += filter_buf_R[i];
  }
  return (float)(filter_sum / FILTER_N);
}
/*--------------------------Main Function--------------------------*/
void setup() {
  // put your setup code here, to run once:
  loadConfigFromEEPROM();
  Serial.begin(115200);
  printf_begin();
  Motor_Init();//电机端口初始化
}

void loop() {
  // put your main code here, to run repeatedly:
  Read_Moto_V();//读取脉冲计算速度
  V_L = Filter_L(V_L);
  V_R = Filter_R(V_R);
  Pwm_L=255-Incremental_Pi_L(V_L,Target_V_L);//左轮PI运算
  Pwm_R=255-Incremental_Pi_R(V_R,Target_V_R);//右轮PI运算
  //Serial.print("Pwm_L:");
  //Serial.println(Pwm_L);
  Set_Pwm(1,Pwm_L,Pwm_R);  //设置左右轮速度
  json_loop();
}

新版ABZ编码器读取

#include <FlexiTimer2.h>  //定时器库头文件
#define MotorLcountA 18 //编码器A 中断号:5
#define MotorLcountB 19 //编码器B 中断号:4

#define MotorRcountA 20 //编码器A 中断号:3
#define MotorRcountB 21 //编码器B 中断号:2

const double sampTime = 50; 

float V_L=0; //左轮速度 单位cm/s
float V_R=0; //右边速度 单位cm/s

volatile long timeL;
volatile long timeR;
volatile double countL = 0;   // 脉冲数计数器,由A相计数
volatile double countR = 0;   // 脉冲数计数器,由A相计数

float V_LFactor = 0;
float V_RFactor = 0;


volatile double distance = 0;
const float D = 0.30; //轮子的直径,单位m
const float pi = 3.141592654;//圆周率

void setup() {
  pinMode(MotorLcountA, INPUT_PULLUP);//因为编码器信号为欧姆龙E6B2-CWZ6C,为开漏输出,因此需要上拉电阻,此处采用arduino的内部上拉输入模式,置高
  pinMode(MotorLcountB, INPUT_PULLUP);//同上
  attachInterrupt(digitalPinToInterrupt(MotorLcountA),EncodeL,FALLING);//左轮脉冲开中断计数
  attachInterrupt(digitalPinToInterrupt(MotorLcountA),EncodeL,RISING);//左轮脉冲开中断计数
  attachInterrupt(digitalPinToInterrupt(MotorRcountA),EncodeR,RISING);//右轮脉冲开
  FlexiTimer2::set(sampTime,calcSpeed);  //设置计时器参数以及终端子程序
  FlexiTimer2::start();
}
// 编码器L脉冲计数中断子程序
void EncodeL()
{
    //为了不计入噪音干扰脉冲,
    //当2次中断之间的时间大于0.1ms时,计一次有效计数
    
    if ((millis() - timeL) > 0.1)
 
    {
        //当编码器码盘的OUTA脉冲信号下跳沿每中断一次,
        if ((digitalRead(MotorLcountA) == HIGH) && (digitalRead(MotorLcountB) == HIGH))
        {
            countL++;
        }
        else
        {
           countL--;
        }
    }
    timeL == millis();
}


// 编码器R脉冲计数中断子程序
void EncodeR()
{
    //为了不计入噪音干扰脉冲,
    //当2次中断之间的时间大于0.1ms时,计一次有效计数
    
    if ((millis() - timeR) > 0.1)
 
    {
        //当编码器码盘的OUTA脉冲信号下跳沿每中断一次,
        if ((digitalRead(MotorRcountA) == HIGH) && (digitalRead(MotorRcountB) == HIGH))
        {
            countR--;
        }
        else
        {
           countR++;
        }
    }
    timeR == millis();
}
void calcSpeed(){
  V_L=countL*V_LFactor;   //单位cm/s
  V_R=countR*V_RFactor;   //单位cm/s
  //V_L_Filter = Filter_L(V_L);
  //V_R_Filter = Filter_R(V_R);

  distance += sampTime*0.001*abs((V_L+V_R))*0.5;
  countL = 0;
  countR = 0;
  
  //Serial.println(distance);
  //Serial.println(V_LFactor);
  //Serial.print("The wheel has run : ");Serial.print(distance); Serial.println("m.");
  //Serial.print("The w_speed is:  ");
  //Serial.print(V_L); //Serial.println("r/min.");
  //Serial.print(" "); //Serial.println("r/min.");
  //Serial.print(V_R); //Serial.println("r/min.");
  //Serial.println(" "); //Serial.println("r/min.");
}
 

新版底盘控制器代码

#define SERIAL_RX_BUFFER_SIZE 2048 //修改串口发送缓冲区大小为2048
#define SERIAL_TX_BUFFER_SIZE 2048 //修改串口接收缓冲区大小为2048
#include <EEPROM.h>
#include <ArduinoJson.h>
#include <FlexiTimer2.h>  //定时器库头文件
#include <PID_v1.h>
/*--------------------------Pin Define&Function--------------------------*/
//左电机端口定义
#define MotorLpinEN   31 //控制位3
#define MotorLpinDIR  33 //控制位4
#define MotorLpwm    3  //使能调速 ENB
#define MotorLcountA 18 //编码器A 中断号:5
#define MotorLcountB 19 //编码器B 中断号:4
 
//右电机端口定义
#define MotorRpinEN   35 //控制位1
#define MotorRpinDIR  37 //控制位2
#define MotorRpwm    2  //使能调速 ENA
#define MotorRcountA 20 //编码器A 中断号:3
#define MotorRcountB 21 //编码器B 中断号:2

const double sampTime = 50; 

float V_L=0; //左轮速度 单位cm/s
float V_R=0; //右边速度 单位cm/s

volatile long timeL;
volatile long timeR;
volatile double countL = 0;   // 脉冲数计数器,由A相计数
volatile double countR = 0;   // 脉冲数计数器,由A相计数

float V_LFactor = 0;
float V_RFactor = 0;

float V_L_Filter =0;
float V_R_Filter =0;

volatile double distance = 0;
const float D = 0.30; //轮子的直径,单位m
const float pi = 3.141592654;//圆周率

float t_targetL = 0, t_targetR = 0;
float v_targetL=0, v_targetR=0;   //单位cm/s

int Pwm_L=0,Pwm_R=0;  //左右轮PWM

//PID变量
float kp=0,ki=0,kd=0;  //PID参数
double SetpointL, InputL, OutputL;
double SetpointR, InputR, OutputR;
PID myPIDL(&InputL, &OutputL, &SetpointL, kp, ki, kd, DIRECT);
PID myPIDR(&InputR, &OutputR, &SetpointR, kp, ki, kd, DIRECT);
//ctl_mode变量
int ctl_mode =0;

//bty_soc变量
int bty_soc = 0;

int debug_mode =0;

int getBtySoc(){
  float volt,val;
  val = analogRead(A0);
  volt = val*5.0/1024.0;
  volt = volt/3.3*12.6;
  volt = 11.7;
  return int(27.746*volt*volt*volt -972.09*volt*volt+11370*volt-44336);
}

/*--------------------------EEPROM Function--------------------------*/
union floatintchar{
  uint8_t char_data[4];
  float float_data;
  int int_data;
};
floatintchar floatintchar_u;

void saveFloatToEEPROM(int startAddr,float Val){
  floatintchar_u.float_data = Val;
  for(int i=0;i<4;i++){
     EEPROM.write(startAddr+i,floatintchar_u.char_data[i]);
  }
}

float getFloatFromEEPROM(int startAddr){
  for(int i=0;i<4;i++){
     floatintchar_u.char_data[i] = EEPROM.read(startAddr+i);
  }
  return floatintchar_u.float_data;
}

int saveIntToEEPROM(int startAddr,int Val){
  floatintchar_u.int_data = Val;
  for(int i=0;i<2;i++){
     EEPROM.write(startAddr+i,floatintchar_u.char_data[i]);
  }
}

int getIntFromEEPROM(int startAddr){
  for(int i=0;i<2;i++){
     floatintchar_u.char_data[i] = EEPROM.read(startAddr+i);
  }
  return floatintchar_u.int_data;
}


void saveConfigToEEPROM(){
  saveFloatToEEPROM(0,kp);
  saveFloatToEEPROM(4,ki);
  saveFloatToEEPROM(8,kd);
  saveIntToEEPROM(12,ctl_mode);
  saveFloatToEEPROM(14,V_LFactor);
  saveFloatToEEPROM(18,V_RFactor);
  
}
void loadConfigFromEEPROM(){
  kp = getFloatFromEEPROM(0);
  ki = getFloatFromEEPROM(4);
  kd = getFloatFromEEPROM(8);
  ctl_mode = getIntFromEEPROM(12);
  V_LFactor = getFloatFromEEPROM(14);
  V_RFactor = getFloatFromEEPROM(18);
}
/*--------------------------printf Function--------------------------*/
int serial_putc( char c, struct __file * )
{
  Serial.write( c );
  return c;
}
void printf_begin(void)
{
  fdevopen( &serial_putc, 0 );
}
/*--------------------------ArduinoJson Function--------------------------*/
String ReceivedCache="";
int json_finish_flag = 0;
char ch[200];
StaticJsonBuffer<600> jsonBuffer;

void handle_g_bty(){
  Serial.print("{");
  Serial.print("\"bty\":");
  Serial.print(bty_soc);
  Serial.println("}");
}
void handle_g_pid_i(){
  Serial.print("{");
  Serial.print("\"kp\":");
  Serial.print(kp);
  Serial.print(",");
  Serial.print("\"ki\":");
  Serial.print(ki);
  Serial.print(",");
  Serial.print("\"kd\":");
  Serial.print(kd);
  Serial.print(",");
  Serial.print("\"ctl_mode\":");
  Serial.print(ctl_mode);
  Serial.println("}");
  
}
void handle_g_sd_i(){
  Serial.print("{");
  Serial.print("\"sl\":");
  Serial.print(V_L);
  Serial.print(",");
  Serial.print("\"sr\":");
  Serial.print(V_R);
  Serial.print(",");
  Serial.print("\"d\":");
  Serial.print(distance);
  Serial.println("}");
}
void handle_g_mode(){
  ;
  }
void json_loop() {
   char readChar=' ';
   while(Serial.available()>0){
      readChar=(char)Serial.read();
      ReceivedCache =ReceivedCache+ (String)readChar;
      //Serial.println(ReceivedCache);
      if(readChar == '}'){
        json_finish_flag = 1;
        break;
      }  
      if(!ReceivedCache.startsWith("{")){
        ReceivedCache.remove(0);
      }
      if(ReceivedCache.length()>50){
        //Serial.println("qing");
        ReceivedCache="";
      }
   }
   if(!ReceivedCache.startsWith("{")){
        ReceivedCache.remove(0);
      }
   //timecnt = micros();//us
   
   if(json_finish_flag == 1){
      json_finish_flag=0;
      ReceivedCache.toCharArray(ch, ReceivedCache.length()+1);//TODO
      JsonObject& root = jsonBuffer.parseObject(ch);
      if (root.success()) {
          // In other case, you can do root["time"].as<long>();
          if(root.containsKey("unlock")){//任何set 都要写一个Set的键,值任意以触发
            if(root.containsKey("s_en")){
            int en = root["s_en"].as<int>();
            setEnable(en);
            if(debug_mode ==1){
              Serial.print("\r\nset OK!enable!");
              Serial.println(en);
            }
            }
            if(root.containsKey("s_debug")){
              debug_mode = root["s_debug"].as<int>();
              if(debug_mode ==1){
                Serial.print("\r\nset OK!debug_mode_Val=");
                Serial.println(debug_mode);
              }
            }
            if(root.containsKey("s_kp")){
              kp = root["s_kp"].as<float>();
              if(debug_mode ==1){
                Serial.print("\r\nset OK!kp_Val=");
                Serial.println(kp);
              }
          }
          if(root.containsKey("s_ki")){
              ki = root["s_ki"].as<float>();
              if(debug_mode ==1){
                Serial.print("\r\nset OK!ki_Val=");
                Serial.println(ki);
              }
          }
          if(root.containsKey("s_kd")){
              kd = root["s_kd"].as<float>();
              if(debug_mode ==1){
                Serial.print("\r\nset OK!kd_Val=");
                Serial.println(kd);
              }
          }
          if(root.containsKey("s_vlf")){
              V_LFactor = root["s_vlf"].as<float>();
              if(debug_mode ==1){
                Serial.print("\r\nset OK!vlf_Val=");
                Serial.println(V_LFactor);
              }
          }
          if(root.containsKey("s_vrf")){
              V_RFactor = root["s_vrf"].as<float>();
              if(debug_mode ==1){
                Serial.print("\r\nset OK!vrf_Val=");
                Serial.println(V_RFactor);
              }
          }
          
        }
        
        if(ctl_mode == 0&&root.containsKey("s_tl")){
            t_targetL = root["s_tl"].as<float>();
            if(debug_mode ==1){
              Serial.print("\r\nset OK!tl_Val=");
              Serial.println(t_targetL);
            }
        }
        if(ctl_mode == 0&&root.containsKey("s_tr")){
            t_targetR = root["s_tr"].as<float>();
            if(debug_mode ==1){
              Serial.print("\r\nset OK!tr_Val=");
              Serial.println(t_targetR);
            }
        }
        if(ctl_mode == 1&&root.containsKey("s_sl")){
            v_targetL = root["s_sl"].as<float>();
            if(debug_mode ==1){
              Serial.print("\r\nset OK!sl_Val=");
              Serial.println(v_targetL);
            }
        }
        if(ctl_mode == 1&&root.containsKey("s_sr")){
            v_targetR = root["s_sr"].as<float>();
            if(debug_mode ==1){
              Serial.print("\r\nset OK!sr_Val=");
              Serial.println(v_targetR);
            }
        }
        if(root.containsKey("s_mode")){
            ctl_mode = root["s_mode"].as<int>();
            if(debug_mode ==1){
              Serial.print("\r\nset OK!mode_Val=");
              Serial.println(ctl_mode);
            }
         }
        if(root.containsKey("g_sd_i")){
          handle_g_sd_i();
        }
        if(root.containsKey("g_mode")){
          handle_g_mode();
        }
        if(root.containsKey("g_bty")){
          handle_g_bty();
        }
        if(root.containsKey("g_pid_i")){
          handle_g_pid_i();
        }
        if(root.containsKey("s_csave")){
            if(debug_mode ==1){
              saveConfigToEEPROM();
              Serial.println("\r\nset OK!config is saved!");
            }
        }
       
        //timecnt = micros()-timecnt;
        //Serial.println(timecnt);
     }
     ReceivedCache ="";
     jsonBuffer.clear();
    
   }
   
   
}


/**************************************************************************(测试完成)
  函数功能:设置双轮工作模式和运动速度
  入口参数:工作模式,左右轮pwm
  返回  值:无
**************************************************************************/
void Set_Pwm(int pwm_L,int pwm_R){

  if(pwm_L<0){
    digitalWrite(MotorLpinDIR,HIGH);
  }else{
    digitalWrite(MotorLpinDIR,LOW);
  }
  if(pwm_R<0){
    digitalWrite(MotorRpinDIR,HIGH);
  }else{
    digitalWrite(MotorRpinDIR,LOW);
  }
  if(abs(pwm_L)<3){
    digitalWrite(MotorLpinEN,LOW);
  }else{
    digitalWrite(MotorLpinEN,HIGH);
  }
  if(abs(pwm_R)<3){
    digitalWrite(MotorRpinEN,LOW);
  }else{
    digitalWrite(MotorRpinEN,HIGH);
  }
  analogWrite(MotorLpwm,255 - abs(pwm_L));
  analogWrite(MotorRpwm,255 - abs(pwm_R));
}
 
 
 /**************************************************************************(测试完成)
  函数功能:电机端口初始化,控制芯片引脚拉低
  入口参数:无
  返回  值:无
**************************************************************************/
void Motor_Init(){
  //左电机
  pinMode(MotorLpinEN,OUTPUT);  //驱动芯片控制引脚
  pinMode(MotorLpinDIR,OUTPUT);  //驱动芯片控制引脚
  pinMode(MotorLpwm,OUTPUT);   //驱动芯片控制引脚,PWM调速
  pinMode(MotorLcountA,INPUT); //左轮编码器A引脚
  pinMode(MotorLcountB,INPUT); //左轮编码器B引脚
  
  //右电机
  pinMode(MotorRpinEN,OUTPUT);  //驱动芯片控制引脚
  pinMode(MotorRpinDIR,OUTPUT);  //驱动芯片控制引脚
  pinMode(MotorRpwm,OUTPUT);   //驱动芯片控制引脚,PWM调速
  pinMode(MotorRcountA,INPUT); //右轮编码器A引脚
  pinMode(MotorRcountB,INPUT); //右轮编码器B引脚
 
  //驱动芯片控制引脚全部拉低
  digitalWrite(MotorLpinEN,LOW); //左电机
  digitalWrite(MotorLpinDIR,LOW);
  digitalWrite(MotorLpwm,HIGH);
  digitalWrite(MotorRpinEN,LOW); //右电机
  digitalWrite(MotorRpinDIR,LOW);
  digitalWrite(MotorRpwm,HIGH);
}

void setEnable(int en){
  if(en>=1){
  digitalWrite(MotorLpinEN,HIGH); //左电机
  digitalWrite(MotorRpinEN,HIGH); //右电机
  }else{
  digitalWrite(MotorLpinEN,LOW); //左电机
  digitalWrite(MotorRpinEN,LOW); //右电机
  }
  
}

void calcSpeed(){
  V_L=countL*V_LFactor;   //单位cm/s
  V_R=countR*V_RFactor;   //单位cm/s
  //V_L_Filter = Filter_L(V_L);
  //V_R_Filter = Filter_R(V_R);

  distance += sampTime*0.001*abs((V_L+V_R))*0.5;
  countL = 0;
  countR = 0;

  InputL = V_L;
  InputR = V_R;

  myPIDL.Compute();
  myPIDR.Compute();

  
  Set_Pwm(OutputL,OutputR);
  bty_soc = (int)Filter_BTY(getBtySoc());
  //Serial.println(distance);
  //Serial.println(V_LFactor);
  //Serial.print("The wheel has run : ");Serial.print(distance); Serial.println("m.");
  //Serial.print("The w_speed is:  ");
  //Serial.print(V_L); //Serial.println("r/min.");
  //Serial.print(" "); //Serial.println("r/min.");
  //Serial.print(V_R); //Serial.println("r/min.");
  //Serial.println(" "); //Serial.println("r/min.");
  //Serial.print(OutputL); //Serial.println("r/min.");
  //Serial.println(" "); //Serial.println("r/min.");
  //Serial.print(OutputR); //Serial.println("r/min.");
  //Serial.println(" "); //Serial.println("r/min.");
  //Serial.println("");
}
 
/***********************************
 * 电机实际速度计算:
 * 公式:
 * 已知参数:
 *     车轮直径65mm,
 *     左边轮子一圈:390脉冲(RISING),
 *     右边轮子一圈:390脉冲(RISING),
 * 单位时间读两个轮子脉冲读取两个轮子脉冲
 ***********************************/ 

// 编码器L脉冲计数中断子程序
void EncodeL()
{
    //为了不计入噪音干扰脉冲,
    //当2次中断之间的时间大于0.1ms时,计一次有效计数
    
    if ((millis() - timeL) > 0.1)
 
    {
        //当编码器码盘的OUTA脉冲信号下跳沿每中断一次,
        if ((digitalRead(MotorLcountA) == HIGH) && (digitalRead(MotorLcountB) == HIGH))
        {
            countL++;
        }
        else
        {
           countL--;
        }
    }
    timeL == millis();
}


// 编码器R脉冲计数中断子程序
void EncodeR()
{
    //为了不计入噪音干扰脉冲,
    //当2次中断之间的时间大于0.1ms时,计一次有效计数
    
    if ((millis() - timeR) > 0.1)
 
    {
        //当编码器码盘的OUTA脉冲信号下跳沿每中断一次,
        if ((digitalRead(MotorRcountA) == HIGH) && (digitalRead(MotorRcountB) == HIGH))
        {
            countR--;
        }
        else
        {
           countR++;
        }
    }
    timeR == millis();
}

/*--------------------------Filter Function--------------------------*/

 // 递推平均滤波法(又称滑动平均滤波法)
#define FILTER_N 2
float filter_buf_L[FILTER_N + 1];
float Filter_L(float Val) {
  int i;
  float filter_sum = 0;
  filter_buf_L[FILTER_N] = Val;
  for(i = 0; i < FILTER_N; i++) {
    filter_buf_L[i] = filter_buf_L[i + 1]; // 所有数据左移,低位仍掉
    filter_sum += filter_buf_L[i];
  }
  return (float)(filter_sum / FILTER_N);
}

float filter_buf_R[FILTER_N + 1];
float Filter_R(float Val) {
  int i;
  float filter_sum = 0;
  filter_buf_R[FILTER_N] = Val;
  for(i = 0; i < FILTER_N; i++) {
    filter_buf_R[i] = filter_buf_R[i + 1]; // 所有数据左移,低位仍掉
    filter_sum += filter_buf_R[i];
  }
  return (float)(filter_sum / FILTER_N);
}
#define FILTER_BTY_N 10
float filter_buf_bty[FILTER_BTY_N + 1];
float Filter_BTY(float Val) {
  int i;
  float filter_sum = 0;
  filter_buf_bty[FILTER_BTY_N] = Val;
  for(i = 0; i < FILTER_BTY_N; i++) {
    filter_buf_bty[i] = filter_buf_bty[i + 1]; // 所有数据左移,低位仍掉
    filter_sum += filter_buf_bty[i];
  }
  return (float)(filter_sum / FILTER_BTY_N);
}
/*--------------------------Main Function--------------------------*/
void setup() {
  //saveFloatToEEPROM(0,5);//kp
  //saveFloatToEEPROM(4,0);//ki
  //saveFloatToEEPROM(8,0);//kd
  // put your setup code here, to run once:
  loadConfigFromEEPROM();
  myPIDL.SetTunings(17*0.618*0.4,40*0.70*0.4,0.01*0.4,1);
  myPIDR.SetTunings(17*0.618*0.4,40*0.70*0.4,0.01*0.4,1); 
  myPIDL.SetOutputLimits(-250,250);
  myPIDR.SetOutputLimits(-250,250);
  myPIDL.SetSampleTime(50);
  myPIDR.SetSampleTime(50);
  Serial.begin(115200);
  printf_begin();
  Motor_Init();//电机端口初始化
  //Serial.println(V_LFactor);
  //Serial.println(V_RFactor);
  attachInterrupt(digitalPinToInterrupt(MotorLcountA),EncodeL,RISING);//左轮脉冲开中断计数
  attachInterrupt(digitalPinToInterrupt(MotorRcountA),EncodeR,RISING);//右轮脉冲开中断计数
  FlexiTimer2::set(sampTime,calcSpeed);  //设置计时器参数以及终端子程序
  FlexiTimer2::start();
}
void loop() {

  if(ctl_mode == 0){//转矩控制模式
    myPIDL.SetMode(MANUAL);
    myPIDR.SetMode(MANUAL);
    Set_Pwm((int)(t_targetL*2.5),(int)(t_targetR*2.5));
  }else if(ctl_mode == 1){//速度控制模式1
    myPIDL.SetMode(AUTOMATIC);
    myPIDR.SetMode(AUTOMATIC);
  }
  json_loop();
  SetpointL = v_targetL;
  SetpointR = v_targetR;
  //Set_Pwm(70,70);
  //delay(4000);
  //Serial.println("uhgvghiujhoijhgbhjiojhbvbjijhb");
  //Set_Pwm(-70,-70);
  //delay(4000);
  //Pwm_L = 255-Incremental_Pi_L(V_L_Filter,-10);//左轮PI运算
  //Pwm_R = 255-Incremental_Pi_R(V_R_Filter,-10);//右轮PI运算
  //Serial.println(Pwm_R);
  //Set_Pwm(1,Pwm_L,Pwm_R);  //设置左右轮速度

}

网上另一个ABZ编码器读取,带Z的处理的

来源:https://blog.csdn.net/chenjiayu938/article/details/81349866

#define PinA 2 //外部中断0
#define PinZ 3 //外部中断1
#define PinB 8 //编码器的OUT_B信号连接到数字端口8
 
//变量初始化
unsigned long time1 = 0; // 时间标记
float time_cw;
float time_ccw;
long count = 0;
const float d = 75.7/1000; //轮子的直径
const float pi = 3.141592654;//圆周率
int num = 0;//圈数
double t;//一圈的运动时间
float velocity;
double time3;//外部中断1产生时的时间,即捕捉到Z相的置零信号时,用于在loop循环内进行一圈时间长短的计算
 
void setup()
 
{
 
    pinMode(PinA, INPUT_PULLUP);//因为编码器信号为欧姆龙E6B2-CWZ6C,为开漏输出,因此需要上拉电阻,此处采用arduino的内部上拉输入模式,置高
    pinMode(PinB, INPUT_PULLUP);//同上
    pinMode(PinZ, INPUT_PULLUP);//同上
    attachInterrupt(0, Encode, FALLING);//脉冲中断函数:捕捉A相信号,并判断A、B相先后顺序
    attachInterrupt(1, Set_state , FALLING);//用于在捕捉到Z的零信号时,进行状态置零
 
 
    Serial.begin (9600);
 
}
 
void loop()
 
{
    double distance;
    //正转
    if (count == 2500)
    {
//      Serial.println("ok");//调试用
      num = num + 1;
      time_cw = millis();
      t = time_cw - time3;
      t = t / 1000;
      distance = num * d * pi; 
      velocity = d * pi / t;
      Serial.print("The wheel has run ");Serial.print(distance); Serial.println("m.");
      Serial.print("The cw_speed is ");Serial.print(velocity); Serial.println("m/s.");
      Serial.print("The time is ");Serial.print(t); Serial.println("s.");
    }
    //反转
    if (count == -2500)
    {
//      Serial.println("ok");//调试用
      num = num + 1;
      time_ccw = millis();
      t = time_ccw - time3;
      t = t / 1000;
      distance = num * d * pi; 
      velocity = d * pi / t;
      Serial.print("The wheel has run ");Serial.print(distance); Serial.println("m.");
      Serial.print("The ccw_speed is ");Serial.print(velocity); Serial.println("m/s.");
      Serial.print("The time is ");Serial.print(t); Serial.println("s.");
    }
}
 
// 编码器计数中断子程序
 
void Encode()
 
{
 
    //为了不计入噪音干扰脉冲,
    
    //当2次中断之间的时间大于5ms时,计一次有效计数
    
    if ((millis() - time1) > 5)
 
    {
 
        //当编码器码盘的OUTA脉冲信号下跳沿每中断一次,
        
        if ((digitalRead(PinA) == LOW) && (digitalRead(PinB) == HIGH))
        
        {
        
            count--;
        
        }
        
             else
        
        {
        
              count++;
        
        }
    
    }
    
    time1 == millis();
    
}
void Set_state(){
    count = 0;
    time3 = millis();//发生中断时的时间
}

arduino 串口自收自发(loop)

String inputString = "";         // 缓存字符串
boolean stringComplete = false;  // 是否string已经完成缓存

void setup() {
  // 初始化串口:
  Serial.begin(9600);
}

void loop() {
  // 如果缓存string接收完成:
  if (stringComplete) {
    Serial.println(inputString);
    // 清空String:
    inputString = "";
    stringComplete = false;
  }
}

void serialEvent() {
  while (Serial.available()) {
    // 获取新的字符:
    char inChar = (char)Serial.read();
    // 将它加到inputString中:
    inputString += inChar;
    // 如果收到了换行符,就将一个“旗标”变量设置为true,这样loop函数就知道inputString已经缓存完成了:
    if (inChar == '\n') {
      stringComplete = true;
    }
  }
}

0.96 128*64 oled 显示测试

重要参考资料:
Adafruit_SSD1306库学习:
https://blog.csdn.net/qq_41477556/article/details/112311181

在这里插入图片描述

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
 

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(128, 64, &Wire, OLED_RESET);

static const unsigned char PROGMEM cnWord_shi[] = 
{
  0x02,0x20,0x12,0x20,0x12,0x20,0x12,0x20,0x12,0x20,0xFF,0xFE,0x12,0x20,0x12,0x20,
  0x12,0x20,0x12,0x20,0x13,0xE0,0x10,0x00,0x10,0x00,0x10,0x00,0x1F,0xFC,0x00,0x00,//世
};

static const unsigned char PROGMEM cnWord_jie[] = 
{  
  0x00,0x00,0x1F,0xF0,0x11,0x10,0x11,0x10,0x1F,0xF0,0x11,0x10,0x11,0x10,0x1F,0xF0,
  0x02,0x80,0x0C,0x60,0x34,0x58,0xC4,0x46,0x04,0x40,0x08,0x40,0x08,0x40,0x10,0x40,//界
};

static const unsigned char PROGMEM cnWord_jia[] = 
{
  0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x7C,0xFE,0x44,0x12,0x44,0x12,0x44,0x12,0x44,
  0x12,0x44,0x12,0x44,0x12,0x44,0x12,0x44,0x22,0x44,0x22,0x7C,0x4A,0x44,0x84,0x00,//加
};

static const unsigned char PROGMEM bty_0_bitmap[] = 
{
0x00,0x00,0x00,0x00,0x07,0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,0x07,0x07,0xE0,0x07,
0x7F,0x07,0xE0,0x07,0x7F,0x03,0xF0,0x07,0x7F,0x03,0xF8,0x07,0x7F,0x01,0xF8,0x07,
0x7F,0x00,0xFC,0x07,0x7F,0x00,0xFE,0x07,0x7F,0x00,0x7E,0x07,0x7F,0x00,0x3F,0x07,
0x07,0x00,0x3F,0x07,0x07,0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
};

static const unsigned char PROGMEM bty_1_bitmap[] = 
{
0x00,0x00,0x00,0x00,0x07,0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,0x07,0x00,0x00,0x07,
0x7F,0x00,0x01,0xF7,0x7F,0x00,0x01,0xF7,0x7F,0x00,0x01,0xF7,0x7F,0x00,0x01,0xF7,
0x7F,0x00,0x01,0xF7,0x7F,0x00,0x01,0xF7,0x7F,0x00,0x01,0xF7,0x7F,0x00,0x01,0xF7,
0x07,0x00,0x00,0x07,0x07,0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
};

static const unsigned char PROGMEM bty_2_bitmap[] = 
{
0x00,0x00,0x00,0x00,0x07,0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,0x07,0x00,0x00,0x07,
0x7F,0x00,0xF9,0xF7,0x7F,0x00,0xF9,0xF7,0x7F,0x00,0xF9,0xF7,0x7F,0x00,0xF9,0xF7,
0x7F,0x00,0xF9,0xF7,0x7F,0x00,0xF9,0xF7,0x7F,0x00,0xF9,0xF7,0x7F,0x00,0xF9,0xF7,
0x07,0x00,0x00,0x07,0x07,0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
};

static const unsigned char PROGMEM bty_3_bitmap[] = 
{
0x00,0x00,0x00,0x00,0x07,0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,0x07,0x00,0x00,0x07,
0x7F,0x7C,0xF9,0xF7,0x7F,0x7C,0xF9,0xF7,0x7F,0x7C,0xF9,0xF7,0x7F,0x7C,0xF9,0xF7,
0x7F,0x7C,0xF9,0xF7,0x7F,0x7C,0xF9,0xF7,0x7F,0x7C,0xF9,0xF7,0x7F,0x7C,0xF9,0xF7,
0x07,0x00,0x00,0x07,0x07,0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
};




static const unsigned char PROGMEM welcome_bitmap[] = 
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x20,0x61,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x30,0x47,0x00,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x10,0xC9,0x01,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x10,0x81,0x01,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x18,0x81,0x01,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x09,0x81,0x01,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x09,0x01,0x01,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0D,0x01,0x01,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x06,0x01,0x01,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x06,0x01,0x08,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x3F,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x7F,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x70,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x60,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x60,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x70,0xE0,0x01,0xF8,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x7F,0xC0,0x03,0xF8,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x3F,0xC0,0x03,0xF8,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0x80,0x03,0xE1,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0x80,0x03,0xE1,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0x80,0x03,0xE3,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0x80,0x07,0xC3,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0x80,0x07,0xC7,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0x80,0x0F,0x87,0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0x18,0x00,0x00,
0x0F,0x80,0x0F,0x87,0x80,0x03,0x03,0x06,0xC0,0x0F,0x00,0x00,0x00,0x19,0x80,0x00,
0x0F,0x80,0x0F,0x87,0x80,0x03,0x03,0x06,0xC0,0x18,0x86,0x00,0x00,0x19,0x80,0x00,
0x0F,0x80,0x0F,0x9F,0x00,0x01,0x87,0x0C,0xC0,0x30,0x06,0x00,0x00,0x18,0x00,0x00,
0x0F,0x9F,0x1F,0x1F,0x00,0x01,0x87,0x8C,0xC0,0x30,0x06,0x00,0x00,0x18,0x00,0x00,
0x0F,0x9F,0x1F,0x3F,0xFF,0xE1,0x85,0x8C,0xDE,0x38,0x1F,0xB0,0xC3,0xD9,0x81,0xF0,
0x0F,0x9F,0x3F,0x3F,0xFF,0xE0,0xCC,0x98,0xE7,0x1C,0x06,0x30,0xC6,0x39,0x86,0x18,
0x0F,0x9F,0x3E,0x7F,0xFF,0xC0,0xCC,0x98,0xC3,0x0F,0x06,0x30,0xCC,0x19,0x8C,0x0C,
0x0F,0xFF,0xFE,0x7F,0xFF,0xC0,0xCC,0x98,0xC3,0x03,0x86,0x30,0xCC,0x19,0x8C,0x0C,
0x0F,0xFF,0xFC,0x7F,0xFF,0xC0,0x48,0xD0,0xC3,0x01,0xC6,0x30,0xCC,0x19,0x8C,0x0C,
0x0F,0xFF,0xFC,0x7C,0x0F,0x80,0x78,0xF0,0xC3,0x00,0xC6,0x30,0xCC,0x19,0x8C,0x0C,
0x0F,0xFF,0xFC,0x7C,0x1F,0x00,0x78,0x70,0xC3,0x00,0xC6,0x30,0xCC,0x19,0x8C,0x0C,
0x0F,0xFF,0xFC,0xF8,0x1F,0x00,0x30,0x60,0xC3,0x21,0x86,0x39,0xC6,0x39,0x86,0x18,
0x0F,0xFF,0xF8,0xF8,0x3F,0x00,0x30,0x60,0xC3,0x1F,0x03,0x9E,0xC3,0xD9,0x83,0xE0,
0x0F,0xFF,0xF9,0xF0,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0xFF,0xF1,0xF0,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0xFF,0xF3,0xE0,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0xFF,0xF3,0xE0,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0xE7,0xF3,0xC0,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0xE7,0xE3,0xC0,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0xE7,0xE7,0xC0,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0xC7,0xE7,0x80,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

};

void setup() {
  Serial.begin(9600);

  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  } 

  // Clear the buffer 这里如果不清理buffer,默认的显示内容为Adafruit类库的LOGO
  display.clearDisplay();
 
  ShowCnText();
}

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

}


void ShowCnText()
{
//  display.clearDisplay(); // clears the screen and buffer
//  display.drawBitmap(10,10,cnWord_shi,16,16, 1);
//  display.display();

  display.clearDisplay();
  //display.drawBitmap(10,10, cnWord_shi, 16,16, 1);//16*16汉字
  display.drawBitmap(0,0, welcome_bitmap, 128,64, 1);//welcome logo
  display.drawBitmap(90,0, bty_1_bitmap, 32,16, 1);//电池标志
  display.display();
  delay(1000);
  display.drawBitmap(90,0, bty_2_bitmap, 32,16, 1);//电池标志
  display.display();
  delay(1000);
  display.fillRect(90,0,128,16,SSD1306_BLACK);
  display.drawBitmap(90,0, bty_1_bitmap, 32,16, 1);//电池标志
  display.display();
  delay(1000);
  display.fillRect(90,0,128,16,SSD1306_BLACK);
  display.drawBitmap(90,0, bty_0_bitmap, 32,16, 1);//电池标志
  display.display();
  delay(1000);
}

版权声明:本文为CSDN博主「whstudio123」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/whstudio123/article/details/119065370

生成海报
点赞 0

whstudio123

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

Arduino 让小车走直线的秘密 增量式PID 直流减速编码电机

直流减速电机增量式PID 通俗易懂版 对于我一个双非学校没上过自控的电子专业本科生而言,这个东西真的是太难了,之前研究了一个A4950驱动,价格便宜,驱动能力高,安全性高,便捷性高,一句话就是比L298N好用,我写完之后发了出去,我的一位老师

七、Arduino 串口的使用

Arduino与计算机通信最常用的方式就是串口通信 我们使用USB线连接Arduino Uno与计算机时,Arduino Uno会在计算机上虚拟出一个串口设备,此时两者之间便建立了串口连接。通过此连接,

Arduino与Proteus仿真实例-DHT11温度湿度传感器驱动仿真

DHT11温度湿度传感器驱动仿真 DHT11 是一款基本的超低成本数字温度和湿度传感器。 它使用电容式湿度传感器和热敏电阻来测量周围的空气,并在数据引脚上吐出数字信号(不需要模拟输入引脚)。 它使用起来相当简单,但需要仔细定时来抓取数据。