打印
[蓝牙芯片]

CH582F+AHT30 软件i2c实现蓝牙notify温湿度数据

[复制链接]
851|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 xiaoqi976633690 于 2023-12-6 19:55 编辑

一、项目描述
1、项目介绍
  沁恒的蓝牙mcu还是很有性价比的,自己买了个芯片CH582F

嘉立创做了个小板子来学习蓝牙通信。并记录下来。
2、设计思路
本次设计思路比较的简单
a.通信部分是蓝牙ble5.0通信协议
b.TMOS 系统的任务和事件的创建
c.I2C读写实现(硬件搞半天没成功,直接用软件I2C)
d.温度传感器AHT30
通过tmos将这些模块组合起来使用
3、硬件
CH582F最小系统开发板+AHT30

二、软件流程图及各功能对应的主要代码片段及说明
介绍
1、流程图


2、代码片段说明
软件i2c实现
#include "I2C.h"


//模拟IIC初始化
void I2CInit(void)
{
    SCL_out;
    SDA_out;
}


void I2C_delay(void)
{
   mDelayuS(100);
}

int I2C_Start(void)
{
    SDA_H;
    SDA_out;
    SCL_H;
    I2C_delay();
    if(!SDA_read)return 0;    //SDA线为低电平则总线忙,退出
    SDA_L;
    I2C_delay();
    if(SDA_read) return 0;    //SDA线为高电平则总线出错,退出
    SDA_L;
    I2C_delay();
    return 1;
}

void I2C_Stop(void)
{
    SCL_L;
    I2C_delay();
    SDA_L;
    SDA_out;
    I2C_delay();
    SCL_H;
    I2C_delay();
    SDA_H;
    I2C_delay();
}

void I2C_Ack(void)
{
    SCL_L;
    I2C_delay();
    SDA_L;
    SDA_out;
    I2C_delay();
    SCL_H;
    I2C_delay();
    I2C_delay();
    SCL_L;
    I2C_delay();
}   

void I2C_NoAck(void)
{
    SCL_L;
    I2C_delay();
    SDA_H;
    SDA_out;
    I2C_delay();
    SCL_H;
    I2C_delay();
    SCL_L;
    I2C_delay();
}

int I2C_WaitAck(void)      //返回为:=1有ACK,=0无ACK
{
    SCL_L;
    I2C_delay();
    I2C_delay();
    SDA_in;
    SCL_H;
    I2C_delay();
    I2C_delay();
    if(SDA_read)
    {
        SCL_L;
        return 0;
    }
    SCL_L;
    return 1;
}

void I2C_SendByte(u8 SendByte) //数据从高位到低位//
{
    u8 i=8;
    SDA_out;
    while(i--)
    {
        SCL_L;
        I2C_delay();
        if(SendByte&0x80)
            SDA_H;
        else
            SDA_L;
        SendByte<<=1;
        I2C_delay();
        SCL_H;
        I2C_delay();
        I2C_delay();
    }
    SCL_L;
}  

u8 I2C_ReadByte(void)  //数据从高位到低位//
{
    u8 i=8;
    u8 ReceiveByte=0;

    SDA_H;
    SDA_out;
    while(i--)
    {
        ReceiveByte<<=1;
        SCL_L;
        I2C_delay();
        I2C_delay();
        SCL_H;
        SDA_in;
        I2C_delay();
        I2C_delay();
        if(SDA_read)
        {
            ReceiveByte|=0x01;
        }
    }
    SCL_L;
    return ReceiveByte;
}

//7bit地址单字节写入*******************************************
int I2C_7bit_Single_Write(u8 SlaveAddress, u8 REG_data)
{
    if(!I2C_Start())return 0;
    I2C_SendByte(SlaveAddress);   //发送设备地址+写信号//I2C_SendByte(((REG_Address & 0x0700) >>7) | SlaveAddress & 0xFFFE);//设置高起始地址+器件地址
    if(!I2C_WaitAck()){I2C_Stop(); return 0;}
    I2C_SendByte(REG_data);
    I2C_WaitAck();    I2C_Stop();
    return 1;
}

//7bit地址单字节读取*****************************************
u8 I2C_7bit_Single_Read(u8 SlaveAddress)
{   
    unsigned char REG_data;
    if(!I2C_Start())return 0;
    I2C_SendByte(SlaveAddress); //I2C_SendByte(((REG_Address & 0x0700) >>7) | REG_Address & 0xFFFE);//设置高起始地址+器件地址
    if(!I2C_WaitAck())
    {
        I2C_Stop();
        return 0;
    }
    I2C_Start();
    I2C_SendByte(SlaveAddress+1);
    I2C_WaitAck();

    REG_data= I2C_ReadByte();
    I2C_NoAck();
    I2C_Stop();
    //return TRUE;
    return REG_data;

}

//7bit地址多字节读取*****************************************
int I2C_7bit_Mult_Read(u8 SlaveAddress,u8 * ptChar,u8 size)
{
    u8 i;

    if(size < 1)
        return 0;
    if(!I2C_Start())
        return 0;
    I2C_SendByte(SlaveAddress);
    if(!I2C_WaitAck())
    {
        I2C_Stop();
        return 0;
    }
    I2C_Start();
    I2C_SendByte(SlaveAddress+1);
    I2C_WaitAck();

    for(i=1;i<size; i++)
    {
        *ptChar++ = I2C_ReadByte();
        I2C_Ack();
    }
    *ptChar++ = I2C_ReadByte();
    I2C_NoAck();
    I2C_Stop();
    return 1;
}



//10bit地址单字节写入*******************************************
int I2C_10bit_Single_Write(u8 SlaveAddress,u8 REG_Address,u8 REG_data)
{
    if(!I2C_Start())return 0;
    I2C_SendByte(SlaveAddress);   //发送设备地址+写信号//I2C_SendByte(((REG_Address & 0x0700) >>7) | SlaveAddress & 0xFFFE);//设置高起始地址+器件地址
    if(!I2C_WaitAck()){I2C_Stop(); return 0;}
    I2C_SendByte(REG_Address );   //设置低起始地址
    I2C_WaitAck();
    I2C_SendByte(REG_data);
    I2C_WaitAck();
    I2C_Stop();
    return 1;
}

//10bit地址单字节读取*****************************************
u8 I2C_10bit_Single_Read(u8 SlaveAddress,u8 REG_Address)
{   
    unsigned char REG_data;
    if(!I2C_Start())return 0;
    I2C_SendByte(SlaveAddress); //I2C_SendByte(((REG_Address & 0x0700) >>7) | REG_Address & 0xFFFE);//设置高起始地址+器件地址
    if(!I2C_WaitAck())
    {
        I2C_Stop();
        return 0;
    }
    I2C_SendByte((u8) REG_Address);   //设置低起始地址
    I2C_WaitAck();
    I2C_Start();
    I2C_SendByte(SlaveAddress+1);
    I2C_WaitAck();

    REG_data= I2C_ReadByte();
    I2C_NoAck();
    I2C_Stop();
    //return TRUE;
    return REG_data;

}

//10bit地址多字节读取*****************************************
int I2C_10bit_Mult_Read(u8 SlaveAddress,u8 REG_Address,u8 * ptChar,u8 size)
{
    u8 i;

    if(size < 1)
        return 0;
    if(!I2C_Start())
        return 0;
    I2C_SendByte(SlaveAddress);
    if(!I2C_WaitAck())
    {
        I2C_Stop();
        return 0;
    }
    I2C_SendByte(REG_Address);
    I2C_WaitAck();

    I2C_Start();
    I2C_SendByte(SlaveAddress+1);
    I2C_WaitAck();

    for(i=1;i<size; i++)
    {
        *ptChar++ = I2C_ReadByte();
        I2C_Ack();
    }
    *ptChar++ = I2C_ReadByte();
    I2C_NoAck();
    I2C_Stop();
    return 1;
}
/********************************** (C) COPYRIGHT *******************************
* File Name          : I2C.h
* Author             : WCH
* Version            : V1.0
* Date               : 2018/12/15
* Description                  :
*******************************************************************************/

#ifndef __I2C_SOFT2_H__
#define __I2C_SOFT2_H__
#include "CH58x_common.h"

#ifndef UINT8
typedef unsigned char           u8;
#endif
#define I2C_Pin_SCL                GPIO_Pin_13
#define I2C_Pin_SDA                GPIO_Pin_12

#define SCL_H         GPIOB_SetBits( I2C_Pin_SCL )
#define SCL_L         GPIOB_ResetBits( I2C_Pin_SCL )
#define SDA_H         GPIOB_SetBits( I2C_Pin_SDA )
#define SDA_L         GPIOB_ResetBits( I2C_Pin_SDA )
#define SCL_read      GPIOB_ReadPortPin( I2C_Pin_SCL )
#define SDA_read      GPIOB_ReadPortPin( I2C_Pin_SDA )
#define SCL_out              GPIOB_ModeCfg(I2C_Pin_SCL, GPIO_ModeOut_PP_5mA)
#define SCL_in              GPIOB_ModeCfg(I2C_Pin_SCL, GPIO_ModeIN_PU)
#define SDA_out              GPIOB_ModeCfg(I2C_Pin_SDA, GPIO_ModeOut_PP_5mA)
#define SDA_in              GPIOB_ModeCfg(I2C_Pin_SDA, GPIO_ModeIN_PU)
                        

void I2CInit(void);  
int I2C_7bit_Single_Write(u8 SlaveAddress, u8 REG_data);

u8 I2C_7bit_Single_Read(u8 Slave7BitAddress);

int I2C_7bit_Mult_Read(u8 Slave7BitAddress,u8 * ptChar,u8 size);

int I2C_10bit_Single_Write(u8 SlaveAddress,u8 REG_Address,u8 REG_data);        
u8 I2C_10bit_Single_Read(u8 SlaveAddress,u8 REG_Address);
int I2C_10bit_Mult_Read(u8 SlaveAddress,u8 REG_Address,u8 * ptChar,u8 size);

void I2C_delay(void);
int I2C_Start(void);
void I2C_Stop(void);
void I2C_Ack(void);
void I2C_NoAck(void);
int I2C_WaitAck(void);          //返回为:=1有ACK,=0无ACK
void I2C_SendByte(u8 SendByte);
u8 I2C_ReadByte(void);  //数据从高位到低位//


#endif


AHT30初始化
void Init_AHTx0(uint8_t address)
{
    uint8_t com[3]={0xBE,0x08,0x00};
    I2C_Write_Byte(address,com,3);
    printf("Init_AHTx0 OK\n");
    DelayMs(100);
}
AHT30读写函数
uint8_t I2C_Write_Byte(uint8_t addr_7bit ,uint8_t  * data, uint8_t len)
{
    uint8_t i=0;
    if(!I2C_Start())return 0;
    I2C_SendByte(addr_7bit<<1);
    if(!I2C_WaitAck()){I2C_Stop(); return 0;}
    for(i=0;i<len;i++)
    {
        I2C_SendByte(data[i]);
        I2C_WaitAck();
    }
    I2C_Stop();
    return 1;

}
uint8_t I2C_Read_Byte(uint8_t addr_7bit, uint8_t *data, uint8_t len)
{
    uint8_t i=0;
    if(!I2C_Start())return 0;
    I2C_SendByte((addr_7bit<<1)|0x01);   //bit7:1 为读
    if(!I2C_WaitAck()){I2C_Stop(); return 0;}
    while(i<(len-1))
    {
        data[i]= I2C_ReadByte();
        I2C_Ack();
        i++;
    }
    data[i]= I2C_ReadByte();
    I2C_NoAck();
    I2C_Stop();
    return 1;
}

1.上电后要等待40ms,读取温湿度值之前, 首先要看状态字的校准使能位Bit[3]是否为 1(通过发送0x71可以获取一个字节的状态字),如果不为1,要发送0xBE命令(初始化),此命令参数有两个字节, 第一个字节为0x08,第二个字节为0x00。
 uint8_t com_init[3]={0xbe,0x08,0x00};//0xbe

2.直接发送 0xAC命令(触发测量),此命令参数有两个字节,第一个字节为 0x33,第二个字节为0x00。
3.等待75ms待测量完成,忙状态Bit[7]为0,然后可以读取六个字节(发0X71即可以读取)。
4.计算温湿度值。
#define  buff_size 6
  uint8_t com_read[3]={0xac,0x33,0x00};//0xac
  uint8_t temprature_buff[buff_size]={0x00,0x00,0x00,0x00,0x00,0x00};
  uint8_t t[20];
void Get_tmprature(void)
{

    I2C_Write_Byte(0x38,com_read,3);//开始测量
    DelayMs(150);
    I2C_Read_Byte(0x38,temprature_buff,buff_size);//读取温湿度
    Temp_Huimt(temprature_buff,t);

}
void Temp_Huimt(uint8_t * arr,uint8_t * t)
{
    int32_t  temp;
    int32_t  humit;
    humit =arr[1];
    humit=((humit)<<8)|arr[2];
    humit=((humit)<<4)|(arr[3]>>4);
    temp =arr[3]&0x0f;
    temp=((temp)<<8)|arr[4];
    temp=((temp)<<8)|arr[5];
    sprintf(t,"h=%.2f,t=%.2f\n\r",(float)humit*100/1048576,(float)temp*200/1048576-50);
}



蓝牙部分
创建server
创建特性
<blockquote>#define SIMPLEPROFILE_CHAR1         0           // RW uint8_t - Profile Characteristic 1 value

// Simple Profile Characteristic 4 Properties
static uint8_t simpleProfileChar4Props = GATT_PROP_NOTIFY;

// Characteristic 4 Value
static uint8_t simpleProfileChar4[SIMPLEPROFILE_CHAR4_LEN] = {0};

// Simple Profile Characteristic 4 Configuration Each client has its own
// instantiation of the Client Characteristic Configuration. Reads of the
// Client Characteristic Configuration only shows the configuration for
// that client and writes only affect the configuration of that client.
static gattCharCfg_t simpleProfileChar4Config[PERIPHERAL_MAX_CONNECTION];

// Simple Profile Characteristic 4 User Description
static uint8_t simpleProfileChar4UserDesp[] = "Characteristic 4\0";


添加server服务
    SimpleProfile_AddService(GATT_ALL_SERVICES); // Simple GATT Profile
初始化蓝牙
void Peripheral_Init()
{
    //将事件的回调函数注册到 TMOS 中
    Peripheral_TaskID = TMOS_ProcessEventRegister(Peripheral_ProcessEvent);

    // Setup the GAP Peripheral Role Profile
    {
        uint8_t  initial_advertising_enable = TRUE;
        uint16_t desired_min_interval = DEFAULT_DESIRED_MIN_CONN_INTERVAL;
        uint16_t desired_max_interval = DEFAULT_DESIRED_MAX_CONN_INTERVAL;

        // Set the GAP Role Parameters
        GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initial_advertising_enable);//使能广播
        GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData), scanRspData);//扫描回复数据,数据格式要遵循广播数据格式
        GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData);//扫描回复数据,数据格式要遵循广播数据格式
        GAPRole_SetParameter(GAPROLE_MIN_CONN_INTERVAL, sizeof(uint16_t), &desired_min_interval);//最小连接间隔
        GAPRole_SetParameter(GAPROLE_MAX_CONN_INTERVAL, sizeof(uint16_t), &desired_max_interval);//最大连接间隔
    }

    // Set the GAP Characteristics
    GGS_SetParameter(GGS_DEVICE_NAME_ATT, sizeof(attDeviceName), attDeviceName);//GAP特征,设备名称

    {
        uint16_t advInt = DEFAULT_ADVERTISING_INTERVAL;

        // Set advertising interval
        GAP_SetParamValue(TGAP_DISC_ADV_INT_MIN, advInt);
        GAP_SetParamValue(TGAP_DISC_ADV_INT_MAX, advInt);

        // Enable scan req notify
        GAP_SetParamValue(TGAP_ADV_SCAN_REQ_NOTIFY, ENABLE);
    }

    // Setup the GAP Bond Manager
    {
        uint32_t passkey = 0; // passkey "000000"
        uint8_t  pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;
        uint8_t  mitm = TRUE;
        uint8_t  bonding = TRUE;
        uint8_t  ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY;
        GAPBondMgr_SetParameter(GAPBOND_PERI_DEFAULT_PASSCODE, sizeof(uint32_t), &passkey);
        GAPBondMgr_SetParameter(GAPBOND_PERI_PAIRING_MODE, sizeof(uint8_t), &pairMode);
        GAPBondMgr_SetParameter(GAPBOND_PERI_MITM_PROTECTION, sizeof(uint8_t), &mitm);
        GAPBondMgr_SetParameter(GAPBOND_PERI_IO_CAPABILITIES, sizeof(uint8_t), &ioCap);
        GAPBondMgr_SetParameter(GAPBOND_PERI_BONDING_ENABLED, sizeof(uint8_t), &bonding);
    }

    // Initialize GATT attributes
//    GGS_AddService(GATT_ALL_SERVICES);           // GAP
//    GATTServApp_AddService(GATT_ALL_SERVICES);   // GATT attributes
//    DevInfo_AddService();                        // Device Information Service
    SimpleProfile_AddService(GATT_ALL_SERVICES); // Simple GATT Profile

    // Setup the SimpleProfile Characteristic Values
    {
        uint8_t charValue1[SIMPLEPROFILE_CHAR1_LEN] = {1};
        uint8_t charValue2[SIMPLEPROFILE_CHAR2_LEN] = {2};
        uint8_t charValue3[SIMPLEPROFILE_CHAR3_LEN] = {3};
        uint8_t charValue4[SIMPLEPROFILE_CHAR4_LEN] = {4};
        uint8_t charValue5[SIMPLEPROFILE_CHAR5_LEN] = {1, 2, 3, 4, 5};
        uint8_t charValue6[SIMPLEPROFILE_CHAR6_LEN] = {'X','Q','-','B','L','E',0x0a};
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR1, SIMPLEPROFILE_CHAR1_LEN, charValue1);
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR2, SIMPLEPROFILE_CHAR2_LEN, charValue2);
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR3, SIMPLEPROFILE_CHAR3_LEN, charValue3);
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, SIMPLEPROFILE_CHAR4_LEN, charValue4);
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR5, SIMPLEPROFILE_CHAR5_LEN, charValue5);
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR6, SIMPLEPROFILE_CHAR6_LEN, charValue6);
    }

    // Init Connection Item
    peripheralInitConnItem(&peripheralConnList);

    // Register callback with SimpleGATTprofile
    SimpleProfile_RegisterAppCBs(&Peripheral_SimpleProfileCBs);

    // Register receive scan request callback
    GAPRole_BroadcasterSetCB(&Broadcaster_BroadcasterCBs);

    // Setup a delayed profile startup
    tmos_set_event(Peripheral_TaskID, SBP_START_DEVICE_EVT); //启动Peripheral_TaskID任务下的SBP_START_DEVICE_EVT事件,其中tmos_set_event函数用于立刻执行某个事件
    tmos_start_reload_task(Peripheral_TaskID, TASK_1,3200);//启动一个循环事件
    tmos_start_reload_task(Peripheral_TaskID,TASK_TEMPRATURE,2200);//启动一个温湿度循环事件
}
事件轮询
uint16_t Peripheral_ProcessEvent(uint8_t task_id, uint16_t events)
{
    //  VOID task_id; // TMOS required parameter that isn't used in this function

    if(events & SYS_EVENT_MSG)
    {
        uint8_t *pMsg;

        if((pMsg = tmos_msg_receive(Peripheral_TaskID)) != NULL)
        {
            Peripheral_ProcessTMOSMsg((tmos_event_hdr_t *)pMsg);
            // Release the TMOS message
            tmos_msg_deallocate(pMsg);
        }
        // return unprocessed events
        return (events ^ SYS_EVENT_MSG);
    }

    if(events & SBP_START_DEVICE_EVT)
    {
        // Start the Device
        GAPRole_PeripheralStartDevice(Peripheral_TaskID, &Peripheral_BondMgrCBs, &Peripheral_PeripheralCBs);
        return (events ^ SBP_START_DEVICE_EVT);
    }

    if(events & SBP_PERIODIC_EVT)
    {
        // Restart timer
        if(SBP_PERIODIC_EVT_PERIOD)
        {
            tmos_start_task(Peripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD);
        }
        // Perform periodic application task
        performPeriodicTask();

        return (events ^ SBP_PERIODIC_EVT);
    }

    if(events & SBP_PARAM_UPDATE_EVT)
    {
        // Send connect param update request
        GAPRole_PeripheralConnParamUpdateReq(peripheralConnList.connHandle,
                                             DEFAULT_DESIRED_MIN_CONN_INTERVAL,
                                             DEFAULT_DESIRED_MAX_CONN_INTERVAL,
                                             DEFAULT_DESIRED_SLAVE_LATENCY,
                                             DEFAULT_DESIRED_CONN_TIMEOUT,
                                             Peripheral_TaskID);

        return (events ^ SBP_PARAM_UPDATE_EVT);
    }

    if(events & SBP_PHY_UPDATE_EVT)
    {
        // start phy update
        PRINT("PHY Update %x...\n", GAPRole_UpdatePHY(peripheralConnList.connHandle, 0,
                    GAP_PHY_BIT_LE_2M, GAP_PHY_BIT_LE_2M, GAP_PHY_OPTIONS_NOPRE));

        return (events ^ SBP_PHY_UPDATE_EVT);
    }

    if(events & SBP_READ_RSSI_EVT)
    {
        GAPRole_ReadRssiCmd(peripheralConnList.connHandle);
        tmos_start_task(Peripheral_TaskID, SBP_READ_RSSI_EVT, SBP_READ_RSSI_EVT_PERIOD);
        return (events ^ SBP_READ_RSSI_EVT);
    }
    if(events & TASK_1)
    {

        Get_temp();
        return (events ^ TASK_1);
    }
    if(events & TASK_TEMPRATURE)
    {
        Get_tmprature();
        Temp_Huimt(temprature_buff,t);
        printf("%s\n",t);
        return (events ^ TASK_TEMPRATURE);
    }

    // Discard unknown events
    return 0;
}


三、功能展示及说明
手机链接蓝牙


使用特权

评论回复
沙发
chenjun89| | 2023-12-8 20:02 | 只看该作者
沁恒的产品线越来越丰富了

使用特权

评论回复
板凳
超能电子| | 2023-12-21 16:29 | 只看该作者
蓝牙的稳定性如何?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

27

主题

161

帖子

2

粉丝