您现在的位置是:网站首页> 硬件

常用经验及模块接口相关编程收集

  • 硬件
  • 2024-08-23
  • 227人已阅读
摘要

常用经验及模块接口相关编程收集

*特别注意在loop中不要一直消耗时间可以在loop中加delay防止影响中断执行*


Arduino IO口采集高低电平上拉下拉电阻

Arduino--SPI协议官方库官方文档

两个Ardunio通过I2C通讯



Arduino IO口采集高低电平上拉下拉电阻

   当输入端口当属插入采集高低电平时候记得设置为pinMode(36,INPUT_PULLUP);

  1. 单片机输出,为避免短路可接电阻,如驱动能力不够可接三级管,MOS管或继电器,(驱动能力满足,只是电压不匹配可以接电瓶转换模块)

  2. 上拉电阻是将电位平时强行拉到高电位,当接通后电位变低,也就是电阻一段端接电源电平

    接线图

     1.png        

  3. 下拉电阻,将电位平时强拉到底电位,当接通后电位变高电位,也就是电阻一段接低

    接线

    1.png
           


           


           


           

    内置上拉电阻

    Arduino自带内置上拉电阻,不用像上面那样自己搭建,直接在代码中开启即可

    pinMode(pin,INPUT_PULLUP);        

使用例子

void setup() {

  // put your setup code here, to run once:

  //pinMode(4,INPUT);

  Serial.begin(9600);

  pinMode(4,INPUT_PULLUP); //将按钮4号引脚拉高电平

  pinMode(2,OUTPUT);//设置2号口输出

}


void loop() {

  // put your main code here, to run repeatedly:

  if(digitalRead(4)==HIGH)//读取4号口的电平高低电平

  {

  digitalWrite(2, HIGH);

  Serial.println("HIGH");

  }

  else

  {

    digitalWrite(2, LOW);

    Serial.println("LOW");

  }

  

}

//按钮按下时为低电平

1.png

由于IO口未内置下拉电阻,所以对于采集外结传感器搞电平时候要接下拉电阻

总结:

 下拉电阻就是电阻一端接地,另一端接IO口和传感器,采集传感器高电平

 上拉电阻就是电阻一端接电源正极,另一端接IO口和传感器,采集传感器低电平

采集脉冲数量可以使用中断

digitalPinToInterrupt(pin);//IO口获得终断号




Arduino--SPI协议官方库官方文档

串行外围设备接口(SPI)是微控制器使用的同步串行数据协议,用于在短距离内快速与一个或多个外围设备通信。它也可以用于两个微控制器之间的通信。通过SPI连接,总是有一个主设备(通常是微控制器)来控制外围设备。通常,所有设备都有三根线:

1.jpg

MISO(Master In Slave Out)-用于将数据发送到主机的从机线路,

MOSI(Master Out Slave In)-主机线路,用于向外围设备发送数据,

SCK(串行时钟)-同步主设备生成的数据传输的时钟脉冲

每条设备专用一条线:


SS(从设备选择)-主设备可用来启用和禁用特定设备的每个设备上的引脚。

当设备的从选择引脚为低电平时,它与主机通信。高时,它将忽略主节点。这使您可以让多个SPI器件共享相同的MISO,MOSI和CLK线。


要为新的SPI器件编写代码,需要注意以下几点:


您的设备可以使用的最大SPI速度是多少?这由SPISettings中的第一个参数控制。如果使用的芯片的额定频率为15 MHz,则使用15000000。Arduino将自动使用等于或小于SPISettings的数字的最佳速度。

数据是先移位最高有效位(MSB)还是最低有效位(LSB)?这由第二个SPISettings参数MSBFIRST或LSBFIRST控制。大多数SPI芯片使用MSB优先数据顺序。

数据时钟为高电平还是低电平时是空闲的吗?采样是在时钟脉冲的上升沿还是下降沿上?这些模式由SPISettings中的第三个参数控制。

SPI标准比较宽松,每个设备在实现上都略有不同。这意味着在编写代码时必须特别注意设备的数据表。


一般来说,有四种传输方式。这些模式控制数据是否在数据时钟信号的上升沿或下降沿移入和移出(称为时钟相位),以及在高电平或低电平时时钟空闲(称为时钟极性)。四种模式根据此表将极性和相位组合在一起:


模式时钟极性(CPOL)时钟相位(CPHA)输出边数据抓取

SPI_MODE000坠落升起

SPI_MODE101个升起坠落

SPI_MODE21个0升起坠落

SPI_MODE31个1个坠落升起


Mode 0 CPOL=0, CPHA=0 

Mode 1 CPOL=0, CPHA=1

Mode 2 CPOL=1, CPHA=0 

Mode 3 CPOL=1, CPHA=1

从设备也是一个SPI控制器,4种模式都支持,此时只要自己设置为某种模式即可。

然后知道了从设备的模式后,再去将SPI主设备的模式,设置为和从设备模式一样,即可。

对于如何配置SPI的CPOL和CPHA的话,不多细说,多数都是直接去写对应的SPI控制器中对应寄存器中的CPOL和CPHA那两位,写0或写1即可。

有了SPI参数后,请使用SPI.beginTransaction()开始使用SPI端口。SPI端口将使用所有设置进行配置。使用SPISettings的最简单,最有效的方法是直接在SPI.beginTransaction()内部。例如:


SPI.beginTransaction(SPISettings(14000000, MSBFIRST, SPI_MODE0));


如果其他库通过中断使用SPI,则在您调用之前,它们将无法访问SPI SPI.endTransaction()。SPI设置在事务开始时应用,并且SPI.endTransaction() 不会更改 SPI设置。除非您或某些库再次调用 beginTransaction ,否则将保持设置。SPI.endTransaction()如果您的程序与其他使用SPI的库一起使用,则应尝试最大程度地缩短调用之间的时间,以实现最佳兼容性。


对于大多数SPI器件,之后SPI.beginTransaction(),您需要将从选择引脚写为LOW,调用SPI.transfer()任意次以传输数据,然后将SS引脚写为HIGH,最后调用SPI.endTransaction()。


有关SPI的更多信息,请参见Wikipedia的SPI页面。


连接引脚

下表显示了不同Arduino板上的SPI线的针脚:


Arduino / Genuino BoardMOSIMISOSCKSS (slave)SS (master)Level

Uno or Duemilanove11 or ICSP-412 or ICSP-113 or ICSP-310-5V

Mega1280 or Mega256051 or ICSP-450 or ICSP-152 or ICSP-353-5V

LeonardoICSP-4ICSP-1ICSP-3--5V

DueICSP-4ICSP-1ICSP-3-4, 10, 52 3,3V

ZeroICSP-4ICSP-1ICSP-3--3,3V

10111 or ICSP-412 or ICSP-113 or ICSP-310103,3V

MKR10008109--3,3V

关于基于AVR的板上的从选择(SS)引脚的说明

所有基于AVR的板都有一个SS引脚,当它们充当由外部主机控制的从机时,该引脚很有用。由于该库仅支持主模式,因此该引脚应始终设置为OUTPUT,否则硬件会自动将SPI接口置于从模式,从而使库无法工作。


但是,可以将任何引脚用作设备的从选择(SS)。例如,Arduino以太网屏蔽使用引脚4控制到板载SD卡的SPI连接,并使用引脚10控制到以太网控制器的连接。


函数

SPISettings

begin()

end()

beginTransaction()

endTransaction()

setBitOrder()

setClockDivider()

setDataMode()

transfer()

usingInterrupt()

Mega2560和外部设备SPI通信

SPI设置

在一个SPI设备中,通常会有4个引脚。SPI总线有主从机之分,主机负责输出时钟信号及选择通信的从设备。时钟信号会通过主机的SCK引脚输出,提供给通信从机使用。而从机的选择由从机CS引脚来决定,CS引脚为低电平时,该从机被选中,CS引脚拉高,该从机被断开。数据的收发则通过MISO和MOSI引脚进行。Arduino Mega2560开发板上引脚位置分别为:MOSI–51脚,MISO–50脚,SCK–52脚,SS–53脚。或者利用6针的ICSP引脚来使用SPI总线。

MISO(Master In Slave Out):主机数据输入,从机数据输出

MOSI(Master Out Slave In):主机数据输出,从机数据输入

SCK(Serial Clock):用于通信同步的时钟信号,由主机产生

SS(Slave Select)或CS(Chip Select):从机使能信号,由主机控制

SPI类库成员函数

1. SPI.begin()

初始化SPI通信,调用该函数后,SCK/MOSI/SS引脚将被设置为输出模式,且SCK/MOSI引脚拉低,SS引脚拉高。


2. SPI.end()

关闭SPI总线通信


3. SPI.setBitOrder(order)

设置传输顺序。order:传输顺序,LSBFIRST,低位在前;MSBFIRST,高位在前


4. SPI.setClockDivider(divider)

设置通信时钟,由主机产生,从机不用配置。divider:SPI通信的系统时钟分频得到,可选配置有SPI_CLOCK_DIV2、SPI_CLOCK_DIV4(默认配置)等,最大可达128分频


5. SPI.setDataMode(mode)

设置数据模式。mode:可配置的模式,可选项有SPI_MODE0、SPI_MODE1、SPI_MODE2、SPI_MODE3


6. SPI.transfer(val)

传输1Byte的数据,SPI是全双工通信,所以发送1B的数据,也会接收到1B的数据。val:要发送的字节数据。


SPI通信示例

下面这个例子是我用Arduino通过SPI通信控制下变频模块产生两级变频。这里有两个SPI从设备PLL1和PLL2,所以这里我需要重新定义SPI从设备的使能引脚,如下图代码中所示,PLL1定义为47脚,PLL2定义为48脚;SCK脚则使用Mega2560默认的52引脚;因为我这里只需要从Arduino发送数据到从设备,并不需要从设备里读取数据,所以我只使用到了MOSI 51引脚,MISO引脚就不需要使用。剩下的就是将各个引脚和从设备上对应的引脚相连就行了。如果只有一个从设备的话,可以直接使用Mega2560上默认的SPI引脚。

#include <SPI.h>                 //引入SPI类库

const int PLL1 = 47;             //定义SPI从设备的使能引脚

const int PLL2 = 48;


//因为篇幅原因,这里PLL寄存器的数据接收做了简化处理,两个从设备都只发送了一组数据,

//实际上这里需要发送多组寄存器数据才能使从设备工作,这也是根据不同的外部设备的规格定义去修改的。

//这里每组4个字节数据

unsigned char PLL1_Reg_buf0[4] = {0x00,0x30,0x1B,0x30};

unsigned char PLL2_Reg_buf0[4] = {0x00,0xBE,0x00,0x00};


void setup() 

Serial.begin(9600);

pinMode(PLL1,OUTPUT);

pinMode(PLL2,OUTPUT);          //管脚设置为输出模式

digitalWrite(PLL1,HIGH);       //拉高SPI从设备引脚

digitalWrite(PLL2,HIGH);

delay(50);

SPI.setBitOrder(MSBFIRST);     // 最高有效位

SPI.setDataMode(SPI_MODE0);    // SCK上升沿输入,下降沿输出;SCK低电平空闲

SPI.begin();                   // SCK MOSI SS设为输出模式,SCK MOSI拉低,SS拉高

set_frq(); 

delay(1000);

}


void loop() 

}


void WriteToADF4350(unsigned char PLL,unsigned char count, unsigned char *buf)

{

if(PLL == 1)  digitalWrite(PLL1,LOW);    //拉低电平,选择对应SPI从设备

if(PLL == 2)  digitalWrite(PLL2,LOW);

delay(50);

unsigned char ValueToWrite = 0;

unsigned char i = 0;

for(i=0;i<count;i++)

{

    ValueToWrite = *(buf + i);

    SPI.transfer(ValueToWrite);           // 发送数据到SPI从设备

    }

 if(PLL == 1)  digitalWrite(PLL1,HIGH);    //拉高电平,释放对应SPI从设备

 if(PLL == 2)  digitalWrite(PLL2,HIGH);

  delay(50);

}


void set_frq(void)

{

WriteToADF4350(1,4,PLL1_Reg_buf0);

delay(500);

WriteToADF4350(2,4,PLL2_Reg_buf0);

delay(500); 

}



两个Ardunio通过I2C通讯

I2C总线Arduino中的引脚

SDAA4

SCLA5

在开始使用两个Arduino编程I2C之前,我们需要了解Arduino IDE中使用的Wire库。

库《Wire.h》包含在程序中,用于使用以下I2C通信函数。

1. Wire.begin(address):

用途:该库用于与I2C设备进行通信。初始化Wire库,并作为从机或主机加入I2C总线。

address:7位从机地址是可选的,如果未指定地址,类似[Wire.begin()],将作为主机加入总线。

2. Wire.read():

用途:该函数用于读取从主机或从机接收的字节,该字节是在调用requestFrom()后从一个从机发送到主设备的字节,或从主设备发送到从机的字节。

3. Wire.write():

用途:该函数用于将数据写入从机或主机。

从机到主机:当主站中使用Wire.RequestFrom()时,从机将数据写入主机。

主机到从机:从主机到从机的传输,Wire.write()用在调用Wire.beginTransmission()和Wire.endTransmission()之间。

Wire.write()可以写成:

? Wire.write(value)

value:要作为单个字节发送的值。

? Wire.write(string):

string:要作为一系列字节发送的字符串。

? Wire.write(data,length):

data:要作为字节发送的数据数组

length:要传输的字节数。

4. Wire.beginTransmission(address):

用途:该函数用于开始使用给定的从地址传输到I2C设备。随后,使用write()函数构建用于传输的字节队列,然后通过调用endTransmission()函数传输它们。

address:发送设备的7位地址。

5. Wire.endTransmission();

用途:此函数用于结束由beginTransmission()发起的从机的传输,并传输由Wire.write()排队的字节。

6. Wire.onRequest();

用途:当主设备使用Wire.requestFrom()请求来自从设备的数据时,将调用此函数。这里我们可以包含Wire.write()函数来向主机发送数据。

7. Wire.onReceive();

用途:当从设备从主设备接收数据时,将调用此函数。这里我们可以包含Wire.read();用于读取从主站发送的数据的函数。

8. Wire.requestFrom(addres,quantity);

用途:该函数在主设备中用于从从设备请求字节。函数Wire.read()用于读取从设备发送的数据。

address:要从中请求字节的设备的7位地址

quantity:要请求的字节数






/** I2C_Master_Transmitter.ino

 * 

 * | Arduino | A4 | ---- SDA

 *           | A5 | ---- SCL

 */


#include <Wire.h>


void setup() {

  Serial.begin(115200);

  Wire.begin();       // join I2C bus as master (no address provided).

}


byte x = 0;


void loop() {

  uint8_t slave_addr = 0x02;

  Serial.println("Finish transmission");

  Wire.beginTransmission(slave_addr); // transmit to device

  Wire.write("x is ");        // sends five bytes

  Wire.write(x);              // sends one byte  

  Wire.endTransmission();    // stop transmitting


  x += 1;

  delay(100);

}



/** I2C_Master_Receiver.ino

 * 

 * | Arduino | A4 | ---- SDA

 *           | A5 | ---- SCL

 */


#include <Wire.h>


void setup() {

  Serial.begin(115200);

  Wire.begin();       // join I2C bus as master (no address provided).

}


void loop() {

  uint8_t slave_addr = 0x2;

  uint8_t n_bytes = 1;

  Wire.requestFrom(slave_addr, n_bytes);    // request data from slave device


  while (Wire.available() > 0) {  // slave may send less than requested

    char c = Wire.read();         // receive a byte as character

    Serial.print(c);

  }


  delay(100);

}



/** I2C_Slave_Transmitter.ino

 * 

 * | Arduino | A4 | ---- SDA

 *           | A5 | ---- SCL

 */


#include <Wire.h>


void setup() {

  uint8_t device_addr = 0x02;

  Serial.begin(115200);

  Wire.begin(device_addr);            // join I2C bus as slave (address provided)

  Wire.onRequest(requestEvent);       // register event

}


void loop() {

  delay(100);

}


// function that executes whenever data is requested by master

// this function is registered as an event, see setup()

void requestEvent() {

  Serial.println("replied");

  

  Wire.write("hello "); // respond with message of 6 bytes

  // as expected by master

}

/** I2C_Slave_Receiver.ino

 * 

 * | Arduino | A4 | ---- SDA

 *           | A5 | ---- SCL

 */


#include <Wire.h>


void setup() {

  uint8_t device_addr = 0x02;

  Serial.begin(115200);

  Wire.begin(device_addr);            // join I2C bus as slave (address provided)

  Wire.onReceive(receiveEvent);       // register event

}


void loop() {

  delay(100);

}


void receiveEvent(int howMany) {

  while(1 < Wire.available()) {

    char c = Wire.read(); // receive byte as a character

    Serial.print(c);

  }

  int x = Wire.read();    // receive byte as an integer

  Serial.println(x);

}


















Top