乐趣区

关于stm:基于51单片机的通信方式

什么是串行通信

  • 通信分串行和并行:串行是指用一根线通信,并行是指用多根线同时通信。
  • 这种通信也能够称为:串行通信、串口通信、UART、USART。
  • 特点:异步、串行、全双工。
  • 工作形式:有三根线(GND/RXD 接管线 /TXD 发送线);数据在发送方和接管方的 CPU 中都是以字节为单位进行解决的;数据在通信线上以位为单位一一 bit 传输。
  • 串行通信次要概念:起始位、数据位、奇偶效验位、进行位、波特率(一秒钟发送多少 bit 位,发送方和接管方波特率必须一样)
  • 串行通信性能是 SOC 的一个外部外设提供的,与 CPU 自身无关。

STC51 串行通信相干寄存器

管制寄存器 PCON 和 SCON

串行管制寄存器 SCON:串行管制寄存器 SCON 用于抉择串行通信的工作形式和某些管制性能。

  • SM0/FE:当 PCON 寄存器中的 SMODO 位为 1 时,该位用于帧谬误检测。当检测到一个有效进行位时,通过 UART 接收器设置该位。它必须由软件清零。当 PCON 寄存器中的 SMODO 位为 0 时,该位和 SM1 一起指定串行通信的工作形式(SM0、SM1)。

    • 0 0:工作形式 0;同步移位串行形式;波特率是 SYSclk/12.
    • 0 1:工作形式 1;8 位 UART,波特率可变;
    • 1 0:工作形式 2;9 位 UART;
    • 1 1:工作形式 3;9 位 UART;波特率可变;
  • REN:容许 / 禁止串行接管管制位。(由软件置位和复位)
  • TI:发送数据时的中断请求标记位。当发送完一帧数据后硬件主动置位,即 TI=1。
  • RI:接收数据时的中断请求标记位。当接管完一帧数据后硬件主动置位,即 RI=1。

电源管制寄存器 PCON:用于抉择波特率是否加倍

  • SMOD:波特率抉择位。SMOD=1,加倍;SMOD=0,不加倍。
  • SMOD0:帧谬误检测无效管制位。

中断容许寄存器 IE

  • EA : CPU 的总中断容许管制位。
  • ES: 串行口中断容许位。

定时器 / 计数器管制寄存器 TCON

  • TR1:定时器 T1 的运行管制位。该位由软件置位和清零。
  • TR1= 1 时开始计数,TR1= 0 时禁止计数。

串行口工作模式

次要利用串行口工作模式 1:8 位 UART,波特率可变。

  • 当软件设置 SCON 的 SMO、SM1 为“01”时,串行通信则以模式 1 工作。
  • 一帧信息为 10 位:1 位起始位,8 位数据位 (低位在先) 和 1 位进行位。
  • TXD 为发送信息,RXD 为接管信息。
  • 一帧信息发送或接管实现后,就会发动中断请求,置位 RI 或 TI。

波特率计算公式

  • SMOD 为 0 或 1。
  • SYSclk 为零碎时钟频率。(我的是 12MHz)
  • TH1 为主动重装载值。TH1 取整数。
  • 留神;计算波特率时,256-TH1 的值必须要是整数左近零点零几的地位才行,差太多可能会造成乱码。(差了零点 2 都可能会乱码)

    代码示例

串口初始化

// 串口初始化函数
// 预设一个串口条件:8 数据位、1 进行位、0 校验位、波特率 9600
// 初始化的次要工作就是去设置相干的寄存器
void uart_init(void)
{

    SCON = 0x50;       // 01010000 串口工作在模式 1(8 位串口)、容许接管
    PCON = 0x00;        // 波特率不加倍

    // 通信波特率相干的设置
    TMOD = 0x20;        // 用作定时器,设置 T1 为模式 2(8 位主动重装载)TH1 = 243;            //2×12000000÷32÷12÷4800=13.0208,256-TH1=13,TH=243。TL1 = 243;           // 8 位主动重装,当溢出时主动将 TH1 寄存的值装入 TL1 中。TH1 和 TL1 必须雷同
    
    TR1 = 1;                // 开启 T1 定时 / 计数器
    ES = 1;                    // 串行口中断容许位
    EA = 1;                    // 总中断容许位

}

发送数据

// 单片机通过串口发送 1 个字节到电脑端。void uart_send_byte(unsigned char c)
{
   // 第 1 步,发送一个字节
   SBUF = c;

   // 第 2 步,先确认串口发送局部没有在忙
   while (!TI);

   // 第 3 步,软件复位 TI 标记位
   TI = 0;
}

// 单片机发送字符到电脑端
void uart_send_string(unsigned char *str)
{while (*str != '\0')
    {uart_send_byte(*str);        // 发送 1 个字符
        str++;                        // 指针指向下一个字符
    }
}


// 电脑端发送数据到单片机,单片机接收数据,触发中断,RI=1。void uart_isr(void) interrupt 4 using 1
{
    unsigned char tmp;

    if (RI)
    {
        tmp = SBUF;        // 读取 SBUF,其实就是读出了串口接管到的 1 字节
        RI = 0;
    }
    uart_send_byte(tmp); // 将接管到的数据再发送给电脑端,测试是否接管胜利。}

基于 51 单片机的 I2C 通信之 EEPROM

基本知识

  • ROM(只读存储器)、RAM(随机拜访存储器)、PROM(可编程 ROM)
  • EPROM(可擦除 ROM)、EEPROM(电可擦除 ROM)
  • EEPROM 在零碎中的存在模式:内置在单片机外部或内部扩大。
  • EEPROM 原理图:

I2C 总线协定规定

  • 任何将数据传输到总线的都作为发送器,任何从总线接收数据的器件都作为接收器。
  • 数据传输由主器件管制,总线的串行时钟、起始进行条件均由主器件产生。
  • 只有在总线非忙时才容许进行数据传输。
  • 在数据传送时,过后钟线为高电平,数据线必须为固定状态,不容许有跳变。时钟线为高电平时,数据线的任何电平变动将被当作总线的启动或进行条件。
  • 启动条件:启动条件必须在所以操作命令之前发送。时钟线放弃高电平期间,数据线电平从高到低的跳变作为 I2C 总线的启动信号。
  • 进行条件:时钟线放弃高电平期间,数据线从低到高的跳变作为 I2C 总线的进行信号。操作完结时必须发送进行条件。
  • I2C 数据传输方式:

I2C 总结

  • 主 CPU 和其从属芯片最罕用的接口,尤其是各种传感器。
  • 三根控制线:GND、SCL、SDA。串行、电平式。(STM32 单片机中也是如此)
  • 发送和接管字节时都是从高位开始。
  • 总线式构造,能够一对多,总线上能够挂载上百个器件,用从地址来辨别。
  • 主从式,由主设施发动通信及总线仲裁,从设施被动响应。
  • 通信速率较低,不适宜语音、视频等。

I2C 代码示例

/*******************************************************************************
* 函 数 名         : I2C_Start()
* 函数性能           : 起始信号:在 I2C_SCL 时钟信号在高电平期间 I2C_SDA 信号产生一个降落沿
* 输    入         : 无
* 输    出         : 无
* 备    注         : 起始之后 I2C_SDA 和 I2C_SCL 都为 0
*******************************************************************************/

void I2C_Start()
{
    I2C_SDA = 1;
    I2C_Delay10us();
    I2C_SCL = 1;
    I2C_Delay10us();// 建设工夫是 I2C_SDA 放弃工夫 >4.7us
    I2C_SDA = 0;
    I2C_Delay10us();// 放弃工夫是 >4us
    I2C_SCL = 0;            
    I2C_Delay10us();}
/*******************************************************************************
* 函 数 名           : I2C_Stop()
* 函数性能             : 终止信号:在 I2C_SCL 时钟信号高电平期间 I2C_SDA 信号产生一个回升沿
* 输    入           : 无
* 输    出              : 无
* 备    注           : 完结之后放弃 I2C_SDA 和 I2C_SCL 都为 1;示意总线闲暇
*******************************************************************************/

void I2C_Stop()
{
    I2C_SDA = 0;
    I2C_Delay10us();
    I2C_SCL = 1;
    I2C_Delay10us();// 建设工夫大于 4.7us
    I2C_SDA = 1;
    I2C_Delay10us();}
/*******************************************************************************
* 函 数 名           : I2cSendByte(uchar num)
* 函数性能              : 通过 I2C 发送一个字节。在 I2C_SCL 时钟信号高电平期间,*                    * 放弃发送信号 I2C_SDA 保持稳定
* 输    入           : num ,ack
* 输    出              : 0 或 1。发送胜利返回 1,发送失败返回 0
* 备    注           : 发送完一个字节 I2C_SCL=0, 须要应答则应答设置为 1,否则为 0
*******************************************************************************/

uchar I2C_SendByte(uchar dat, uchar ack)
{
    uchar a = 0,b = 0;// 最大 255,一个机器周期为 1us,最大延时 255us。// 为了保障时序正确,这里应该加一句 SCL = 0;        
    for(a=0; a<8; a++)// 要发送 8 位,从最高位开始
    {
        I2C_SDA = dat >> 7;     // 起始信号之后 I2C_SCL=0,所以能够间接扭转 I2C_SDA 信号
        dat = dat << 1;
        I2C_Delay10us();
        I2C_SCL = 1;
        I2C_Delay10us();// 建设工夫 >4.7us
        I2C_SCL = 0;
        I2C_Delay10us();// 工夫大于 4us}

    I2C_SDA = 1;            // 主设施开释 SDA 线给从设施去操作
    I2C_Delay10us();
    I2C_SCL = 1;            // 主设施开始了第 9 个周期
    while(I2C_SDA && (ack == 1))// 期待应答,也就是期待从设施把 I2C_SDA 拉低
    {
        b++;
        if(b > 200)     // 如果超过 200us 没有应答发送失败,或者为非应答,示意接管完结
        {
            I2C_SCL = 0;
            I2C_Delay10us();
            return 0;
        }
    }

    I2C_SCL = 0;
    I2C_Delay10us();
     return 1;        
}
/*******************************************************************************
* 函 数 名           : I2cReadByte()
* 函数性能             : 应用 I2c 读取一个字节
* 输    入           : 无
* 输    出              : dat
* 备    注           : 接管完一个字节 I2C_SCL=0
*******************************************************************************/

uchar I2C_ReadByte()
{
    uchar a = 0,dat = 0;
    I2C_SDA = 1;            //CPU 开释总线,让从设施接管。(51 单片机中是这样解决,32 中可能有点不同)I2C_Delay10us();
    // 按情理这里应该有一个 SCL = 0 的
    for(a=0; a<8; a++)// 接管 8 个位
    {
        I2C_SCL = 1;        // 告诉从设施我要开始读了,能够放 1bit 数据到 SDA 了
        I2C_Delay10us();
        dat <<= 1;            // 读取的时候是高位在前的
        dat |= I2C_SDA;
        I2C_Delay10us();
        I2C_SCL = 0;        // 拉低,为下一个 bit 的周期做筹备
        I2C_Delay10us();}    
    return dat;        
}

EEPROM(AT24C02 芯片介绍(最多保留 255 个字节))

器件寻址

  • 主器件通过发送一个起始信号启动发送过程,而后发送从器件地址。(从地址是芯片自身定义的)
  • 8 位从器件地址的高 4 位固定为 1010,接下来的 3 位为从器件的地址位,最低位为从器件的读写位(1 示意对从器件进行读操作,0 示意对从器件进行写操作)
  • 在主器件发送起始信号和从器件地址后,AT24C02 会监督总线,当某一个器件的地址与发送的从地址雷同时,响应一个应答信号(通过 SDA 线)。

应答信号

  • I2C 总线数据传输时,每胜利传输一个字节数据后,接收器都必须产生一个应答信号,接收器在第九个时钟周期将 SDA 拉低,示意曾经收到一个字节数据。
  • 当 AT24C02 工作于被读状态时,在发送一个字节之后会开释总线,并监督总线,如果接管到应答信号,则持续发送数据;若没有接管到应答信号,则进行发送数据。
  • 写操作时序图)(ACK 就是应答信号)

写操作

  • 单写一个字节:

    • 首先主器件发送起始命令和从器件地址信息。期待应答。
    • 而后发送要写入数据的内存地址(即字节地址)。期待应答。
    • 而后开始发送数据。期待应答。
    • 最初发送进行信号。

  • 代码示例
/*******************************************************************************
* 函 数 名         : void At24c02Write(unsigned char addr,unsigned char dat)
* 函数性能           : 往 24c02 的一个地址写入一个数据
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/

void At24c02Write(unsigned char addr,unsigned char dat)
{I2C_Start();
    I2C_SendByte(0xa0, 1);// 发送从器件地址
    I2C_SendByte(addr, 1);// 发送要写入内存地址
    I2C_SendByte(dat, 0);    // 发送数据
    I2C_Stop();}
  • 间断写入多个字节(AT24C01 可一次写入 8 个字节,AT24C02/04/08/16 可一次写入 16 个字节)

    • 每发送一个字节数据后,AT24C 芯片会产生一个应答并将字节地址低位加一,高位不变。
    • 如果在发送进行信号之前,主器件发送的字节超出规定字节,则超出的字节将会笼罩先前写入的字节。

读操作

  • 立即地址读:

    • 如果上一次读 / 写操作的地址为 N,则立刻读的地址从 N + 1 开始。如果读数据时超过地址极限(AT04C02 最多只能保留 255 字节),则会翻转到 0 地址持续读取。

  • 选择性读:

    • 选择性读操作容许主器件对寄存器的任意地址进行读操作。
    • 读取过程:主器件首先通过发送起始信号和从器件地址以及想读取的字节数据地址执行一个伪写操作;在应答之后主器件从新发送起始信号和从器件地址,此时地址最初一地位一(进行读操作);AT24C02 响应并发送应答信号,而后发送所须要的数据,主器件不发送应答信号但发送进行信号。

  • 代码示例
/*******************************************************************************
* 函 数 名         : unsigned char At24c02Read(unsigned char addr)
* 函数性能           : 读取 24c02 的一个地址的一个数据
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/

unsigned char At24c02Read(unsigned char addr)
{
    unsigned char num;
    I2C_Start();
    I2C_SendByte(0xa0, 1); // 发送从器件地址
    I2C_SendByte(addr, 1); // 发送要读取的地址
    I2C_Start();
    I2C_SendByte(0xa1, 1); // 发送读器件地址
    num=I2C_ReadByte(); // 读取数据
    I2C_Stop();
    return num;    
}
退出移动版