IIC 通用文件,文件是在NRF51xx 芯片基础,keil 平台开发测试通过,后期修改为STM32F2xx系列的配置。

文件百度云盘链接 : https://pan.baidu.com/s/1AFxanwzrAViaubtERZMRsA

注意:在用于STM32Fx 系列时注意引脚读取函数的选择!

下面是示波器现实,IIC通讯读的操作。

理论基础:

  • SCL为高,SDA发生变化,即为发生了特殊状态(ACK, NACK, START, STOP)
  • SCL为低,SDA发生变化,即为数据端发生跳变,这个是无影响的。
  • IIC总线在数据传输时,时钟信号为高电平,数据线上的数据必须保持稳定,只有在SCL上的信号为低电平时,SDA线才允许变
  • IIC_confing.h 主要是SIMU_IIC.c 文件的一些结构体和端口设置, I2c_Str 结构体可以再添加数据属性,包括后期的数据,主要用于 SIMU_IIC.c 文件内部使用。
  • /**************************************************************************//**
    * @file IIC_confing.h
    * @brief For IIC communication
    * @author ning lv
    * @version 0.01
    ******************************************************************************
    * @section License
    ******************************************************************************
    *
    *****************************************************************************/ #ifndef _I2C_CONFING_H_
    #define _I2C_CONFING_H_ #ifdef __cplusplus
    extern "C" {
    #endif #include "SIMU_IIC.h"
    /*
    *** You can choose yours projects ...*/
    //#define NRF51822 //试用在蓝牙芯片上
    #define STM32F205 // /*
    *** Redefines the exact width signed integer type ...*/
    //typedef unsigned char uint8;
    //typedef unsigned int uint32;
    //typedef signed char uint8_t; /*
    *** Define variables and structures ... */
    typedef struct {
    uint32 SDA;
    uint32 SCL;
    uint32 GPIO;
    }I2c_Str; /*
    ***The global variable ... */
    I2c_Str type_Struct; //all
    //sda \ scl
    typedef void(*pI2cLineF)(uint32 sda,uint32 scl);
    pI2cLineF _I2cLine = _NULL;
    // gpio
    typedef void(*pI2cGpioF)(uint32 gpo);
    pI2cGpioF _I2cGpio = _NULL;
    /*
    ***Chip selection ... */
    #if defined NRF51822
    #define CHOOSE_IN _TRUE
    #define CHOOSE_OUT _NULL
    #elif defined STM32F205
    #define CHOOSE_IN (GPIO_Mode_IN )
    #define CHOOSE_OUT (GPIO_Mode_OUT)
    #define GPIO_PORT ((GPIO_TypeDef*)type_Struct.GPIO)
    #endif /*
    ***Output 0/1 ... */
    #define SDA_OUT_H ( Gpio_Pin_Wr(type_Struct.SDA,_TRUE) )
    #define SDA_OUT_L ( Gpio_Pin_Wr(type_Struct.SDA,_NULL) )
    #define SCL_OUT_H ( Gpio_Pin_Wr(type_Struct.SCL,_TRUE) )
    #define SCL_OUT_L ( Gpio_Pin_Wr(type_Struct.SCL,_NULL) ) /*
    ***Choose output/input ... */
    #define SDA_SET_IN ( I2c_Pin_Dir (type_Struct.SDA,CHOOSE_IN) )
    #define SDA_SET_OUT ( I2c_Pin_Dir (type_Struct.SDA,CHOOSE_OUT) )
    #define SCL_SET_IN_ ( I2c_Pin_Dir (type_Struct.SCL,CHOOSE_IN) )
    #define SCL_SET_OUT ( I2c_Pin_Dir (type_Struct.SCL,CHOOSE_OUT) )
    /*
    *** Reads lines ... */
    #define RD_SDA_IN ( Gpio_Pin_Red(type_Struct.SDA) )
    #define RD_SCL_IN ( Gpio_Pin_Red(type_Struct.SCL) ) /*
    *** times ... */
    #define NOP() ( __nop() )
    #define Iic_Delay(x) ( delay_us(x) ) #ifdef __cplusplus
    }
    #endif
    #endif /*_I2C_CONFING_H_*/
    SIMU_IIC.c 文件主要的运行文件,开头是所需的芯片头文件,特别注意的是delay_ms();和delay_us();两个函数要根据芯片的不同,配置设备的不同定向修改,修改的过程中可以用示波器具体调试时间。
  • //Header files ...
    #include "IIC_confing.h" //master and the explanation is here!!
    #include "usart.h" //Chip selection ...
    #if defined NRF51822
    #include "nrf_gpio.h"
    #elif defined STM32F205
    #include <stm32f2xx_gpio.h>
    #endif /*
    ***Function declaration ...*/
    void Delay5us(void);
    void Delay5us(void);
    void Delay10us(void);
    void Gpio_Pin_Wr(uint32 pin ,uint8 wr);
    void I2c_Pin_Dir(uint32 pin, uint8 dir);
    void Choose_I2c_Pin(uint32 sda,uint32 scl);
    void Gpio_Confing(uint32 gpo); //下面的可以发布在IIC.h 函数中,对外的接口设置
    void I2c_Stop(void);
    void I2c_NoAck(void);
    void I2c_RecAck(void);
    void I2c_Reset (void);
    uint8 I2c_Start(void);
    uint8 I2c_WaitACK(void);
    uint8 I2c_RcvByte(void);
    void I2c_SendByte(uint8 c);
    uint8 Gpio_Pin_Red(uint32 pin);
    void IIC_confing_Pin(uint32 sda,uint32 scl, uint32 gpo);
    void delay_ms(uint32 volatile number_of_ms);
    void delay_us(uint32 volatile number_of_us); /**@brief Delay 1us
    */
    void Delay1us(void)
    {
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    #if defined STM32F205
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    NOP();NOP();NOP();NOP();
    #endif
    }
    /**@brief Delay 5us
    */
    void Delay5us(void)
    {
    Delay1us();
    Delay1us();
    Delay1us();
    Delay1us();
    } /**@brief Delay 10us
    */
    void Delay10us(void)
    {
    Delay5us();
    Delay5us();
    } /**@brief Delay(ms)
    *
    * @param[in] number_of_ms
    */
    void delay_ms(uint32 volatile number_of_ms)
    {
    number_of_ms = number_of_ms * ;
    while(number_of_ms != )
    {
    number_of_ms--;
    Delay1us();
    }
    } /**@brief Delay(us)
    *
    * @param[in] number_of_us
    */
    void delay_us(uint32 volatile number_of_us)
    {
    while(number_of_us != )
    {
    number_of_us--;
    Delay1us(); }
    } /**@brief Select the I2C sda and scl pins.
    */
    void Choose_I2c_Pin( uint32 sda,uint32 scl )
    {
    type_Struct.SDA = sda;
    type_Struct.SCL = scl;
    } /**@brief Function Pointers set GPIO
    */
    void Gpio_Confing( uint32 gpo )
    {
    type_Struct.GPIO = gpo;
    } /**@brief Function Pointers set the sda and SCL pins
    */
    void IIC_confing_Pin( uint32 sda,uint32 scl, uint32 gpo)
    {
    #if defined STM32F205
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    #endif
    _I2cGpio = (pI2cGpioF)Gpio_Confing;
    _I2cGpio(gpo); _I2cLine = (pI2cLineF)Choose_I2c_Pin ;
    _I2cLine(sda,scl);
    } /**@brief Iic_Pin_Dir 支持文件 STM32F2xx or NRF51822
    *
    * @param[in] dir
    */
    #if defined ( STM32F205 )
    void STM32F205_Pin_Dir (uint32 pin, uint8 dir)
    {
    GPIO_InitTypeDef GPIO_InitStructure;
    if(GPIO_Mode_OUT == dir){
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    }else if(GPIO_Mode_IN == dir){
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    }
    GPIO_InitStructure.GPIO_Pin = pin;
    GPIO_Init(GPIO_PORT, &GPIO_InitStructure);
    }
    #elif defined ( NRF51822 )
    void nRF51822_Pin_Dir (uint32 pin, uint8 dir)
    {
    if ( dir == ) {
    nrf_gpio_cfg_output(pin);
    }else {
    nrf_gpio_cfg_input(pin,NRF_GPIO_PIN_PULLUP);
    }
    }
    #endif /**@brief The pin line outputs 0/1.
    *
    * @param[in] dir
    */
    void I2c_Pin_Dir (uint32 pin, uint8 dir)
    {
    #if defined ( NRF51822 )
    nRF51822_Pin_Dir (pin, dir);
    #elif defined ( STM32F205 )
    STM32F205_Pin_Dir (pin, dir);
    #endif
    } /**@brief SDA/SCL line write 0/1.
    *
    * @param[in] pin ,wr
    */ void Gpio_Pin_Wr(uint32 pin ,uint8 wr)
    {
    if ( wr == ) {
    #if defined ( NRF51822 )
    nrf_gpio_pin_write(pin,);
    #elif defined ( STM32F205 )
    GPIO_WriteBit(GPIO_PORT , pin , );
    #endif
    }
    else {
    #if defined ( NRF51822 )
    nrf_gpio_pin_write(pin,);
    #elif defined ( STM32F205 )
    GPIO_WriteBit(GPIO_PORT , pin , );
    #endif
    }
    } /**@brief SDA/SCL line read 0/1.
    *
    * @param[in] pin
    */
    uint8 Gpio_Pin_Red(uint32 pin)
    {
    #if defined ( NRF51822 )
    return ((uint8) nrf_gpio_pin_read(pin));
    #elif defined ( STM32F205 )
    return (uint8) GPIO_ReadInputDataBit(GPIO_PORT , pin);
    #endif
    } /**@brief IIC communication to prepare.
    *
    * @param[in] i2c_master
    */
    uint8 I2c_Start(void)
    {
    /* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
    I2c_Reset(); SCL_SET_OUT;
    SDA_SET_OUT; SDA_OUT_H;
    SCL_OUT_H;
    Iic_Delay(I2C_DELAY_TM);
    if( !RD_SDA_IN ) {
    return I2C_BUSY; //BUSY
    }
    SDA_OUT_L;
    Iic_Delay(I2C_DELAY_TM);
    if( RD_SDA_IN ) {
    return I2C_ERROR; //ERROR
    }
    SDA_OUT_L;/*CPU占线*/
    return I2C_TRUE;
    } /**@brief IIC stop the communication .
    *
    * @param[in] i2c_master
    */
    void I2c_Stop(void)
    {
    /* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
    SCL_OUT_L;
    Iic_Delay(I2C_DELAY_TM);
    SDA_OUT_L;
    Iic_Delay(I2C_DELAY_TM);
    SCL_OUT_H;
    Iic_Delay(I2C_DELAY_TM);
    SDA_OUT_H;
    } /**@brief reset
    */
    void I2c_Reset (void)
    {
    if( RD_SDA_IN ==
    //|| READ_SCL_IN == 0
    )
    {
    SCL_SET_OUT;
    SDA_SET_OUT;
    Iic_Delay(I2C_DELAY_TM); SDA_OUT_H;
    SCL_OUT_H;
    Iic_Delay(I2C_DELAY_TM); I2c_SendByte(0xFF);
    I2c_NoAck();
    I2c_Stop();
    }
    }
    /**@brief Send the data 'c' out,either as data or as an address.
    *
    * @param[in] C
    */
    void I2c_SendByte(uint8 c)
    {
    uint8 i;
    i = ; while(i--)
    {
    SCL_OUT_L; /*允许SDA 数据跳变*/
    Iic_Delay(I2C_DELAY_TM);
    if( c&0x80) {
    SDA_OUT_H;
    }
    else {
    SDA_OUT_L;
    } c<<=;
    SCL_OUT_H; /*SDA 数据保持 1us*/
    Iic_Delay(I2C_DELAY_TM);
    }
    SCL_OUT_L;
    SDA_OUT_H; /*CPU 释放总线*/
    } /**@brief Reads a bytes of data and returns it.
    *
    * @param[out] retc
    */
    uint8 I2c_RcvByte(void)
    {
    uint8 i = ;
    uint8 retc = ; SDA_SET_IN;
    SDA_OUT_L;/*总线默认拉高*/
    while(i--)
    {
    retc<<=;
    SCL_OUT_L; /*CPU 允许 SDA 数据跳变*/
    Iic_Delay(I2C_DELAY_TM);
    SCL_OUT_H; /*CPU 保持 SDA 数据1us*/
    Iic_Delay(I2C_DELAY_TM);
    if( RD_SDA_IN ) {
    retc |= 0x01;
    }
    }
    SCL_OUT_L;
    SDA_SET_OUT;
    SDA_OUT_H; /*CPU 释放总线*/ return retc;
    }
    /**@brief watint ACK
    */
    uint8 I2c_WaitACK(void)
    {
    uint8 errTime = ;
    uint8 retFlag = I2C_TRUE; SCL_OUT_L;
    SDA_OUT_H; /*CPU 释放总线*/
    Iic_Delay(I2C_DELAY_TM); SCL_OUT_H; /*CPU驱动SCL = 1, 此时器件会返回ACK应答*/
    SDA_SET_IN;
    Iic_Delay(I2C_DELAY_TM);
    while((RD_SDA_IN)&&((errTime--) > ))
    {
    if(errTime == ){
    retFlag = I2C_FALSE;
    }
    }
    SCL_OUT_L;
    SDA_SET_OUT;
    return retFlag;
    }
    /**@brief ACK response
    */
    void I2c_RecAck(void)
    {
    SCL_OUT_L; /*CPU 允许 SDA 数据跳变*/
    Iic_Delay(I2C_DELAY_TM);
    SDA_OUT_L; /*CPU 拉低SDA ,ACK器件*/
    Iic_Delay(I2C_DELAY_TM);
    SCL_OUT_H; /*CPU 保持 SDA 数据1us*/
    Iic_Delay(I2C_DELAY_TM);
    SCL_OUT_L;
    Iic_Delay(I2C_DELAY_TM);
    SDA_OUT_H; /* CPU释放SDA总线 */
    } /**@brief Don't reply
    */
    void I2c_NoAck(void)
    {
    SCL_OUT_L;
    Iic_Delay(I2C_DELAY_TM);
    SDA_OUT_H;
    Iic_Delay(I2C_DELAY_TM);
    SCL_OUT_H;
    Iic_Delay(I2C_DELAY_TM);
    SCL_OUT_L;
    Iic_Delay(I2C_DELAY_TM);
    }

    SIMCU_IIC.h对外发布函数接口和一些宏定义 ,  函数 void IIC_confing_Pin( uint32 sda,uint32 scl, uint32 gpo);//设置 sda 和 scl 的引脚设置. 是主要调用的文件,它是基础。

  • #ifndef SIMU_IIC_H_
    #define SIMU_IIC_H_ /*
    *** Define some of the things ...*/
    #define _NULL 0
    #define _TRUE 1
    #define I2C_ERROR _NULL //returns
    #define I2C_FALSE _NULL
    #define I2C_TRUE _TRUE
    #define I2C_BUSY 3//3
    #define I2C_DELAY_TM 5 //slot time (4.7us) /*
    *** Define variables and structures ... */
    typedef unsigned char uint8;
    typedef unsigned int uint32; void I2c_Stop(void);
    void I2c_NoAck(void);
    void I2c_RecAck(void);
    void I2c_Reset (void);
    uint8 I2c_Start(void);
    uint8 I2c_WaitACK(void);
    uint8 I2c_RcvByte(void);
    void I2c_SendByte(uint8 c);
    uint8 Gpio_Pin_Red(uint32 pin);
    void delay_ms(uint32 volatile number_of_ms);
    void delay_us(uint32 volatile number_of_us);
    void IIC_confing_Pin( uint32 sda,uint32 scl, uint32 gpo);//设置 sda 和 scl 的引脚设置. #endif /*SIMU_IIC_H_*/

    device_i2c.c 文件是给出的write和read实例文件。

  • #include "SIMU_IIC.h"
    #include "usart.h" #define DEVICE_SLAVEADDR_W 0xA0//0x40
    #define DEVICE_SLAVEADDR_R 0xA1
    uint8_t busy;
    uint8_t DEVICE_WriteByte (uint8_t addr,uint8_t *c,uint8_t len )
    {
    uint8_t i = ;
    lock_busy = ;
    i = I2c_Start(); if(I2C_BUSY == i )
    {
    printf(" \r\n I2C_BUSY \r\n");
    goto DEVICE_WriteByte_FAIL; //return Fail;
    }
    else if (I2C_ERROR == i)
    {
    printf(" \r\n I2C_ERROR \r\n");
    goto DEVICE_WriteByte_FAIL; //return Fail;
    } I2c_SendByte(DEVICE_SLAVEADDR_W); //0x40 写
    if(!I2c_WaitACK()) {
    I2c_Stop();
    printf(" \r\n 地址失败 :%02x !\r\n",DEVICE_SLAVEADDR_W);
    goto DEVICE_WriteByte_FAIL; //return Fail;
    }
    I2c_SendByte(addr); //addr
    if(!I2c_WaitACK()) {
    I2c_Stop();
    printf(" \r\n 寄存器地址错误 :%02x !\r\n",DEVICE_SLAVEADDR_W);
    goto DEVICE_WriteByte_FAIL; //return Fail;
    }
    for(i=;i<len;i++){
    I2c_SendByte(*c);
    if(!I2c_WaitACK())
    {
    printf(" \r\n 写入数据失败 \r\n");
    }
    c++;
    }
    I2c_Stop();
    busy = ;
    return ;
    DEVICE_WriteByte_FAIL:
    I2c_Stop();
    busy = ;
    return ;
    }
    uint8 DEVICE_II_ReadByte (uint8_t addr,uint8_t * c,uint8_t len)
    {
    uint8 i;
    busy = ;
    if(!I2c_Start()) goto DEVICE_II_ReadByte_FAIL; //return Fail; I2c_SendByte (DEVICE_SLAVEADDR_W);
    if(!I2c_WaitACK())
    {
    I2c_Stop();
    goto DEVICE_II_ReadByte_FAIL; //return Fail;
    }
    I2c_SendByte(addr);
    I2c_WaitACK();
    I2c_Start();
    I2c_SendByte(DEVICE_SLAVEADDR_R);
    I2c_WaitACK();
    for(i=;i<len;i++)
    {
    *c = I2c_RcvByte();
    c++;
    } I2c_NoAck();
    I2c_Stop();
    busy = ;
    return ; DEVICE_II_ReadByte_FAIL:
    busy = ;
    return ;
    }

《我的嵌入式开发》---- IIC 通信的更多相关文章

  1. ARM-linux嵌入式开发平台搭建1

    初学嵌入式开发,由于是自学,走了很多弯路,现总结一下嵌入式ARM-LINUX开发环境搭建步骤: 1.安装linux系统,由于初学,我选择fedora 14.安装的具体步骤就不详细说了. 2.安装NFS ...

  2. linux交叉环境的搭建以及嵌入式开发概述

    嵌入式开发概述 由嵌入式本身的特性所影响,嵌入式系统开发与通用系统的开发有很大的区别,嵌入式的开发分为系统总体开发,嵌入式硬件开发,嵌入式系统软件开发3大部分 在系统总体开发中,由于嵌入式系统与硬件依 ...

  3. C语言面试题(嵌入式开发方向,附答案及点评)

    整理自C语言面试题(嵌入式开发方向,附答案及点评) 预处理器(Preprocessor) 1. 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题) #define SEC ...

  4. 与一线Linux嵌入式开发project师的对话

    转:与一线Linux嵌入式开发project师的对话 陈project师一直做Linux的嵌入式开发.作为在开发一线的project师.他对非常多问题的看法可能更切合实际需求,于是,通过邮件.就嵌入式 ...

  5. 【嵌入式开发】写入开发板Linux系统-模型S3C6410

    笔者 : 万境绝尘 转载请著名出处 最终拿到板子了, 嵌入式开发正式开启. 板子型号 : 三星 S3C6410 基于ARM11, 指令集基于arm6指令集; 为毛不是 Cortext A9的板子; 烧 ...

  6. 应聘linux/ARM嵌入式开发岗位

    **************************************************************** 因为发在中华英才和智联招聘没有人采我所以我 在这里发布我的个人简历希望 ...

  7. 基于51单片机IIC通信的PCF8591学习笔记

    引言 PCF8591 是单电源,低功耗8 位CMOS 数据采集器件,具有4 个模拟输入.一个输出和一个串行I2C 总线接口.3 个地址引脚A0.A1 和A2 用于编程硬件地址,允许将最多8 个器件连接 ...

  8. 【嵌入式开发】向开发板中烧写Linux系统-型号S3C6410

    作者 : 万境绝尘 转载请著名出处 终于拿到板子了, 嵌入式开发正式开启. 板子型号 : 三星 S3C6410 基于ARM11, 指令集基于arm6指令集; 为毛不是 Cortext A9的板子; 烧 ...

  9. 嵌入式开发 MCU

    From: http://www.infoq.com/cn/articles/intelligent-embedded-os-Internet-of-things-and-robots 嵌入式开发是一 ...

  10. Node.js进阶篇-koa、钩子函数、websocket、嵌入式开发

    代码地址如下:http://www.demodashi.com/demo/12932.html 一.简介     koa是由Express原班人马打造的,致力于成为一个更小.更富有表现力.更健壮的We ...

随机推荐

  1. json格式字符串用Uncaught SyntaxError: Unexpected token ' Uncaught SyntaxError: Unexpected number

    Unexpected number(index)的错误用的json字符串如 var jsonStr = "{1:'北京note备注信息',2:'上海note备注信息',3:'广东note备注 ...

  2. C# 从配置文件中读取/写入信息

    读取: var currMemberID = System.Configuration.ConfigurationManager.AppSettings["tolunaMemberID&qu ...

  3. 移动端click事件无反应或反应慢 touchend事件页面滑动时频繁触发

    H5页面的点击事件click 无论在浏览器 iframe还是小程序里面 都会出现点击无反应或者反应慢的情况出现 所以决定用touchend事件来代替click 但是touchend事件触发比较灵敏 在 ...

  4. [Paper][Link note]

    http://ieeexplore.ieee.org/document/6974670/

  5. maven源码打包

    1.打包时附加外部Jar包 <!--编译+外部 Jar打包-->          <plugin>            <artifactId>maven-co ...

  6. [模板] 无旋Treap (C++ class)

    注意!本帖不是算法介绍!只是贴代码(逃) //嫌stdlib的rand太慢,手打了一个 /* Author: hotwords */ typedef unsigned int tkey; class ...

  7. better-scroll

    better-scroll会将默认事件阻止掉,如果自己写的部分需要有点击事件,需要在参数里加上click:true. 同时,在PC上或某些手机端,由于未成功将touchend事件move掉,点击事件会 ...

  8. callback回调函数的理解

    callback采用的设计模式是:模板模式,他的设计理念是基于面向对象中的多态的. 我们的程序中走到某个地方他会出现不一样的动作的时候,我们在这儿就使用回调函数.我们利用的就是 多态的原理,我们传递不 ...

  9. KMP算法详细分解

    1. 引言 给定一个主串(以 S 代替)和模式串(以 P 代替),要求找出 P 在 S 中出现的位置,此即串的模式匹配问题. Knuth-Morris-Pratt 算法(简称 KMP)是解决这一问题的 ...

  10. Qt坑点汇总

    1.场景:假如我们想在layout中的qlabel中设置一个图片 1.1 如果简单地使用border-image,我们可以做到,并且拖动界面时,label可以随布局正常变化,这里需要注意的是,修改ui ...