请选择 进入手机版 | 继续访问电脑版

安富莱电子论坛

 找回密码
 立即注册

扫一扫,访问微社区

查看: 372|回复: 17
收起左侧

请教下如果只有一路 I2C 控制信号,但是此信号上接了很多外设如电容触摸屏,eeprom等等?

[复制链接]

323

主题

1480

帖子

2131

积分

至尊会员

积分
2131
发表于 2018-12-24 21:30:02 | 显示全部楼层 |阅读模式
本帖最后由 hpdell 于 2018-12-24 21:36 编辑

请教下如果只有一路 I2C 控制信号,但是此信号上接了很多外设如电容触摸屏,wm8978, mpu9250, 24c256, 外扩 IO 扩展芯片 等等?

这样的话,再加上 FreeRTOS 系统,这些外设在使用中会不会发生冲突啊 ?

大神们有什么解决方法啊 ??
回复

使用道具 举报

0

主题

3

帖子

3

积分

新手上路

积分
3
发表于 2018-12-24 21:40:05 | 显示全部楼层
本帖最后由 xdj0818 于 2018-12-25 09:35 编辑

建议好好看下I2C技术协议文档,主机发起的第一个字节包含两个信息,一是从机器件地址,二是读还是写操作。
之前理解错了,有RTOS的情况下可以使用互斥量或信号量;没有RTOS的情况,增加一个变量做为I2C总线空闲标志,操作总线时,标志成忙状态,读写完成变为空闲;读写操作前先判断标志是否为忙。
回复

使用道具 举报

0

主题

2

帖子

2

积分

新手上路

积分
2
发表于 2018-12-24 23:06:43 | 显示全部楼层
有可能冲突的,一个任务在使用I2c时可能会被高优先级任务抢占,如果高优先级也使用I2c的话,就会把I2c总线时序和数据扰乱。可以加个全局变量或者互斥信号量来区分是否在使用I2c
本人也是小白,个人理解,仅供参考
回复

使用道具 举报

5547

主题

3万

帖子

4万

积分

管理员

做人第一,工作第二

Rank: 9Rank: 9Rank: 9

积分
44793
QQ
发表于 2018-12-27 01:22:11 | 显示全部楼层
这个东西有个简单的办法,比互斥之类的操作方式好使,专门开辟一个I2C任务,需要操作什么设备,给此任务发消息即可。
淘宝小店: https://armfly.taobao.com/
专注,努力,用心的做好每一件事情,Fighting!
回复

使用道具 举报

323

主题

1480

帖子

2131

积分

至尊会员

积分
2131
 楼主| 发表于 2018-12-28 09:41:00 | 显示全部楼层
本帖最后由 hpdell 于 2018-12-28 09:44 编辑
eric2013 发表于 2018-12-27 01:22
这个东西有个简单的办法,比互斥之类的操作方式好使,专门开辟一个I2C任务,需要操作什么设备,给此任务发 ...

这个方法貌似不错啊,我试试看
请教下是不是类似如下的方法啊 :

static  void vTaskMusic(void *pvParameters)
{
        MusicMsg_T *pMsg;
        BaseType_t xResult;

        ( void )pvParameters;                  /* 避免编译器告警 */
        while(1)
        {
                /* 接受数据,播放MP3文件或者WAV文件 */
                xResult = xQueueReceive(xQueue,                    /* 消息队列句柄 */
                                                                                                                (void *)&pMsg,                     /* 这里获取的是结构体的地址 */
                                                                                                                (TickType_t)portMAX_DELAY);/* 设置阻塞时间 */

                if( xResult == pdPASS )
                {
                        /* DMA禁止后要等到当前转换完毕才可以停止,所以这里需要清除相应标志 */
                        xEventGroupWaitBits(xCreatedEventGroup, /* 事件标志组句柄 */
                                                                                                        MusicTaskWaitFlag,  /* 等待MusicTaskWaitFlag某位被设置 */
                                                                                                        pdTRUE,             /* 退出前MusicTaskWaitFlag被清除,这里是任意MusicTaskWaitFlag位被设置就“退出”*/
                                                                                                        pdFALSE,            /* 设置为pdFALSE表示等待MusicTaskWaitFlag任意位被设置*/
                                                                                                        0);                                  /* 等待延迟时间 */

      switch(pMsg->ucType)
      {
        case MusicType_WAV:
        {
           WavMusicPlay((const char *)pMsg->ucName);                                       
        } break;

        case  MusicType_MP3:
        {
           MP3MusicPlay((const char *)pMsg->ucName);               
        } break;

        case  MusicType_FLAC:
        {
           FLACMusicPlay((const char *)pMsg->ucName);
        } break;

        case  MusicType_APE:
        {
          APEMusicPlay((const char *)pMsg->ucName);
        } break;
                               
        case  MusicType_REC:
        {
                                        /* 录音机 */
                                        WavRecPlay((const char *)pMsg->ucName);
        }         break;                               

                               
        case  MusicType_RADIO:
        {
                                        /* 收音机 */
                                        RadioPlay();
        }         break;               

                                case   MusicType_AVI:
                                {
                                                /* avi 视频播放 */
                                                AVIMusicPlay((const char *)pMsg->ucName);
                                }  break;
                                               

        default : break;
      }
                }
        }
}

回复

使用道具 举报

5547

主题

3万

帖子

4万

积分

管理员

做人第一,工作第二

Rank: 9Rank: 9Rank: 9

积分
44793
QQ
发表于 2018-12-28 11:24:40 | 显示全部楼层
hpdell 发表于 2018-12-28 09:41
这个方法貌似不错啊,我试试看
请教下是不是类似如下的方法啊 :

是的,就是我音乐播放器的方式。
淘宝小店: https://armfly.taobao.com/
专注,努力,用心的做好每一件事情,Fighting!
回复

使用道具 举报

323

主题

1480

帖子

2131

积分

至尊会员

积分
2131
 楼主| 发表于 2018-12-28 12:26:54 | 显示全部楼层
eric2013 发表于 2018-12-28 11:24
是的,就是我音乐播放器的方式。

好的,我捣鼓看看
回复

使用道具 举报

323

主题

1480

帖子

2131

积分

至尊会员

积分
2131
 楼主| 发表于 2018-12-28 14:50:19 | 显示全部楼层
eric2013 发表于 2018-12-27 01:22
这个东西有个简单的办法,比互斥之类的操作方式好使,专门开辟一个I2C任务,需要操作什么设备,给此任务发 ...

你好,请教下,是使用消息邮箱 好还是使用事件标志组好哇 ?

回复

使用道具 举报

24

主题

1271

帖子

1319

积分

至尊会员

积分
1319
发表于 2018-12-28 15:00:59 | 显示全部楼层
hpdell 发表于 2018-12-28 14:50
你好,请教下,是使用消息邮箱 好还是使用事件标志组好哇 ?

你用的是FreeRTOS还是什么,如果是FreeRTOS,直接用消息队列,因为FreeRTOS的事件标志是基于消息队列实现的,所以直接使用消息队列不用绕个弯了。
Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better.
回复

使用道具 举报

323

主题

1480

帖子

2131

积分

至尊会员

积分
2131
 楼主| 发表于 2018-12-28 16:45:07 | 显示全部楼层
byccc 发表于 2018-12-28 15:00
你用的是FreeRTOS还是什么,如果是FreeRTOS,直接用消息队列,因为FreeRTOS的事件标志是基于消息队列实现 ...

我使用的是 FreeRTOS 系统,

我试试看看效果如何,多谢多谢了啊


回复

使用道具 举报

323

主题

1480

帖子

2131

积分

至尊会员

积分
2131
 楼主| 发表于 2018-12-29 10:56:05 | 显示全部楼层
eric2013 发表于 2018-12-27 01:22
这个东西有个简单的办法,比互斥之类的操作方式好使,专门开辟一个I2C任务,需要操作什么设备,给此任务发 ...

你好,使用这个发送信息给 i2c 任务的方法,感觉程序比较分散啊,
另外貌似读取 io 口扩展芯片 tca 6424 程序还不行 ?????
电容触摸屏运行正常


如下:

i2c 任务:
static void vTaskI2C(void *pvParameters)
{
        I2CMsg_T *pMsg;
        BaseType_t xResult;
        ( void )pvParameters;                  /* 避免编译器告警 */
       
        while(1)
        {
    #if   1
        /* 接受数据,播放MP3文件或者WAV文件 */
        xResult = xQueueReceive(xQueueI2C,                    /* 消息队列句柄 */
                                (void *)&pMsg,                     /* 这里获取的是结构体的地址 */
                                (TickType_t)portMAX_DELAY);/* 设置阻塞时间 */
        if( xResult == pdPASS )
        {
          switch(pMsg->ucType)
          {
            case I2CType_Touch:
              GT911_OnePiontScan();                // 每10ms 运行一次电容触摸屏,
            break;

            case I2CType_Tca6424:
                                       TCA6424_KeyScanRTOS();   // io 口扩展芯片读取,目前这个地方貌似还不行,按照程序裸机看应该是没有错的,但就是读取不到数据,具体还没有整明白 ??
            break;
                                               
                                                case  I2CType_Wm8978_PlayAvi:   //播放avi视频
                                                AUDIO_EnterPlayMode();  // VIDEO_EnterPlayMode();
                                                WM8978_I2S_Cfg(I2S_MODE_PHILIPS,I2S_MODE_CFG_16BIT);        //飞利浦标准,16位数据长度
            break;
                                               
            default : break;
          }
        }
    #else
        GT911_OnePiontScan();               
        TCA6424_KeyScan();
        vTaskDelay(10);
    #endif
        }
}


// 初始化一根 rtos 软件定时器
int  FreeRTOS_TimerInit(void)
{
/* 周期模式的软件定时器 1,定时器周期 1000(tick)*/
Timer1_Handle = xTimerCreate((const char*)"AutoReloadTimer",
                                                                                                                         (TickType_t)10,                                        /* 定时器周期 10(tick) */
                                                                                                                         (UBaseType_t)pdTRUE,                        /* 周期模式 */
                                                                                                                         (void* )1,                                                                /* 为每个计时器分配一个索引的唯一 ID */
                                                                                                                         (TimerCallbackFunction_t)Timer1_Callback);         /* 回调函数 */
        if(Timer1_Handle != NULL)
        {
                /********************************************************************
                 * xTicksToWait:如果在调用 xTimerStart()时队列已满,则以 tick 为单位指定调用任务应保持
                 * 在 Blocked(阻塞)状态以等待 start 命令成功发送到 timer 命令队列的时间。
                 * 如果在启动调度程序之前调用 xTimerStart(),则忽略 xTicksToWait。在这里设置等待时间为 0.
                 **********************************************************************/
                xTimerStart(Timer1_Handle,0); //开启周期定时器
        }               
        else
        {
                return 1;          //失败
        }                                                                         
        return 0;                  // 成功
}

/*
软件定时器,10ms 回调函数
*/
static void Timer1_Callback(void* parameter)
{
        static uint8_t   TimerCntKey =0;
        __IO I2CMsg_T  *pI2CMsg;
       
        if( !TCA6424_GetExtStatus())
        {
                TimerCntKey ++;
                if(TimerCntKey >= 3)  //消痘处理
                {
                        if( !TCA6424_GetExtStatus())
                        {
                                TimerCntKey =0;
                                s_tI2CMsg.ucType = I2CType_Tca6424;
                        }
                        else
                        {
                                TimerCntKey =0;
                               
                                // 如果没有按键按下,则扫描触摸屏
                                s_tI2CMsg.ucType = I2CType_Touch;         
                        }
                }
        }
        else
        {
                TimerCntKey =0;
               
                // 如果没有按键按下,则扫描触摸屏
                s_tI2CMsg.ucType = I2CType_Touch;          //发送触摸屏信息给任务
        }
       
        /* 发消息 */
        pI2CMsg = &s_tI2CMsg;
        if(xQueueSend(xQueueI2C,
                                   (void *)&pI2CMsg,
                                                 (TickType_t)portMAX_DELAY) == pdPASS)  //消息发送成功
        {

        }
        else  //消息发送失败
        {
       
        }
}



// 播放 avi 视频,发送信息给 i2c 任务配置 wm8978
                                        if(AVI_Handel.aviInfo.SampleRate)    // 有声音数据
                                        {
                                                #if   1
                                                __IO I2CMsg_T  *pI2CMsg;
                                                        /* 发消息,播放新歌 */
                                                pI2CMsg->ucType = I2CType_Wm8978_PlayAvi;
                                                xQueueSend(xQueueI2C,       //给 vTaskI2C 任务发送消息
                                                                                 (void *)&pI2CMsg,
                                                                                         (TickType_t)portMAX_DELAY);
                                                #else
                                                VIDEO_EnterPlayMode();
                                                WM8978_I2S_Cfg(I2S_MODE_PHILIPS,I2S_MODE_CFG_16BIT);        //飞利浦标准,16位数据长度
                                                #endif


                                                SAIA_Init(SAI_MODEMASTER_TX,
                                                                                        SAI_CLOCKSTROBING_RISINGEDGE,
                                                                                        SAI_DATASIZE_16,
                                                                                        AVI_Handel.aviInfo.SampleRate,
                                                                                        AVI_Handel.aviInfo.Channels,
                                                                                        16);
                                                sai_tx_callback = AUDIO_SAI_CompleteCallback;                            //回调函数指wav_sai_dma_callback  
                                                SAIA_TX_DMA_Init(
                                                                                                         (uint8_t *)AVI_Handel.pAudioBufferA , //  必须为内存申请地址,好触发中断
                                                                                                         (uint8_t *)AVI_Handel.pAudioBufferB , //  必须为内存申请地址,好触发中断                     
                                                                                                                 AVI_Handel.AudioBufferSize/2,   //预设为4096,这个音频数据大小后续再进行动态调整
                                                                                                                 SAI_DMA_WIDTH_16BIT);           //配置TX DMA,16位

}



回复

使用道具 举报

7

主题

214

帖子

228

积分

高级会员

积分
228
发表于 2018-12-30 08:51:54 | 显示全部楼层
我最近的新产品三个I2C同时工作,使用两个硬件I2C,一个模拟I2C。同样我也是用了FreeRTOS,先自我清晰三个任务的优先级定义,然后测试清楚各模块的时间消耗。OS引入互斥锁,main函数几个实时功能做好busy状态记录。
1.空闲的时候实时模拟I2C检测按键。
2.检测到有按键,给一个特定的busy倒计时,期间模拟I2C根据busy位不工作,B设备硬件I2C通信设置参数。
3.预留充足的时间给UI刷界面,
4.退出再设置busy倒计时,期间模拟I2C根据busy位不工作,配合OS互斥 I2C保存数据。
5.中途两个硬件I2C的小穿插,以及跟刷UI重叠部分自己要多调试,不让他们打架就行。

以上是我的实现步骤,欢迎交流。
回复

使用道具 举报

323

主题

1480

帖子

2131

积分

至尊会员

积分
2131
 楼主| 发表于 2018-12-30 09:29:33 | 显示全部楼层
本帖最后由 hpdell 于 2018-12-30 09:33 编辑
廷润 发表于 2018-12-30 08:51
我最近的新产品三个I2C同时工作,使用两个硬件I2C,一个模拟I2C。同样我也是用了FreeRTOS,先自我清晰三个任 ...

我的 i2c 只有一路模拟的,同时接有好几个 i2c 设备啊

特别是触摸屏不停的扫描,最多间隔 20-30ms 扫描一次
回复

使用道具 举报

5547

主题

3万

帖子

4万

积分

管理员

做人第一,工作第二

Rank: 9Rank: 9Rank: 9

积分
44793
QQ
发表于 2018-12-30 10:01:00 | 显示全部楼层
hpdell 发表于 2018-12-30 09:29
我的 i2c 只有一路模拟的,同时接有好几个 i2c 设备啊

特别是触摸屏不停的扫描,最多间隔 20-30ms 扫 ...

你的FreeRTOS程序设计出问题了,软件定时器回调函数里面不可以调用任何带有延迟参数的API,你调用了这个
if(xQueueSend(xQueueI2C,
                                   (void *)&pI2CMsg,
                                                 (TickType_t)portMAX_DELAY)

解决办法比较容易,你的GT911和TCA6424_KeyScanRTOS()都整成10ms一次,放在一个case里面,无需再为TCA6424单独创建一个软件定时器任务。
淘宝小店: https://armfly.taobao.com/
专注,努力,用心的做好每一件事情,Fighting!
回复

使用道具 举报

323

主题

1480

帖子

2131

积分

至尊会员

积分
2131
 楼主| 发表于 2018-12-30 18:44:32 | 显示全部楼层
本帖最后由 hpdell 于 2018-12-30 20:01 编辑
eric2013 发表于 2018-12-30 10:01
你的FreeRTOS程序设计出问题了,软件定时器回调函数里面不可以调用任何带有延迟参数的API,你调用了这个
...

你好,刚刚已经改成了 使用 中断方式发送了
        xResult = xQueueSendFromISR(xQueueI2C,
                                                                                (void *)&pI2CMsg,
                                                                                        &xHigherPriorityTaskWoken);
                // 消息被成功发出
                if( xResult != pdFAIL )
                {
                        // 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行
                        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
                }     

安静/触摸屏扫描也都修改了
          switch(pMsg->ucType)
          {
            case I2CType_Touch:
              GT911_OnePiontScan();  //电容触摸屏
      TCA6424_KeyScan();   // io 口扩展芯片读取,

            break;   




回复

使用道具 举报

5547

主题

3万

帖子

4万

积分

管理员

做人第一,工作第二

Rank: 9Rank: 9Rank: 9

积分
44793
QQ
发表于 2018-12-31 01:15:10 | 显示全部楼层
hpdell 发表于 2018-12-30 18:44
你好,刚刚已经改成了 使用 中断方式发送了
        xResult = xQueueSendFromISR(xQueueI2C,
         ...

可以,中断里面也可行的。
淘宝小店: https://armfly.taobao.com/
专注,努力,用心的做好每一件事情,Fighting!
回复

使用道具 举报

323

主题

1480

帖子

2131

积分

至尊会员

积分
2131
 楼主| 发表于 2018-12-31 18:45:02 | 显示全部楼层
eric2013 发表于 2018-12-31 01:15
可以,中断里面也可行的。

多谢多谢了啊
回复

使用道具 举报

7

主题

214

帖子

228

积分

高级会员

积分
228
发表于 2019-1-2 08:40:09 | 显示全部楼层
hpdell 发表于 2018-12-30 09:29
我的 i2c 只有一路模拟的,同时接有好几个 i2c 设备啊

特别是触摸屏不停的扫描,最多间隔 20-30ms 扫 ...

模拟I2C,挂这么多设备,内耗不小;建议数据错漏也要多调试验证。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|手机版|安富莱电子论坛 ( 鄂ICP备09023347号,公安机关备案号42010602000201 )

GMT+8, 2019-4-25 14:34 , Processed in 0.219339 second(s), 24 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表