历史上的今天
返回首页

历史上的今天

今天是:2025年04月08日(星期二)

正在发生

2019年04月08日 | STM32F103RC硬件I2C从入坑到出坑

2019-04-08 来源:eefocus

第一天

手上有啥资源可利用的?

1.一块MPU6050

2.一块stm32f103rc最小系统版,上面移植了ucos3,跑了几个任务,包括一个LCD任务(优先级最低)

3.st的固件库

4.ALIENTEK的MPU6050例程

5.stm32各种数据手册文档,ucos各种资料


好,开工。


先阅读stm32数据手册了解研究I2C模块原理架构,懵懵懂懂,若有所知的样子,行了不看了。


然后看看ALIENTEK的MPU6050例程。咦?怎么是用软件模拟I2C?stm32集成了I2C控制器都不用,这么浪费,不看了。。。还是再看一下,看看MPU6050大致是怎么操作的。大概就是有个设备地址0x68,有很多寄存器,数据通信通的就是寄存器的数据,每次传输数据都是先发设备地址0x68,然后发寄存器地址,最后收/发数据。


然后复制ALIENTEK例程中操作MPU6050的代码,改写里面所有的函数,用库函数操作STM32硬件I2C重新编制I2C的通信流程。打码完成,测试一下,果然不行。用fprintf打印信息到串口看看吧(没调试器,穷得只剩串口板)。然后开始艰辛的探索(省略一万字)。。。。。。


经过串口打印信息、查阅资料、网上搜索,终于成功改写ALIENTEK例程中的MPU_Write_Byte(INT08U reg, INT08U data) 函数如下,


INT08U MPU_Write_Byte(INT08U reg, INT08U data)  

{

    while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));


    I2C_GenerateSTART(I2C1, ENABLE);

    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));


    I2C_Send7bitAddress(I2C1, (MPU6050_ADDR<<1), I2C_Direction_Transmitter);

    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));


    I2C_SendData(I2C1, reg);

    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));


    I2C_SendData(I2C1, data);

    I2C_GenerateSTOP(I2C1, ENABLE);


    return 1;

}


遇到的主要问题有:

1.设备地址0x68要左移一位,然后后面补上读写位,读是1,写是0。注意,不是0x68+1(或+0),而是(0x68<<1)|0x01(或0x00),也就是0xd1是从MPU6050读数据,发0xd0是向MPU6050写数据。

2.**严格按照st的例程给出的通信流程来编码。**网上各种说stm32硬件I2C有问题,好吧可能是有点问题,但是对于我等渣渣是完全不会遇到的,有问题都是自己的问题。严格按照st的例程来,绝对没问题。不要自己想当然的去编码,也不要觉得自己写的代码跟st的例程中的代码是等效的就坚信自己是对的。我参考的是st的EEPROM的那个例程。


然后改写了MPU_Read_Byte(INT08U reg)函数,OK。

就差MPU_Read_Len(INT08U addr, INT08U reg, INT08U len, INT08U *buf)这条函数就可以读MPU6050的数据了。改完,测试一下,果然不行。果断每行代码后面插一条fprintf。测试一下,电脑蓝屏。重启,测试一下,电脑蓝屏。。。OMG!!!唉12点了躺床吃鸡睡觉。


第二天

开电脑,测试一下,电脑蓝屏。。。唉还搞什么,放弃吧。。。去掉fprintf看看,不蓝屏,可是我怎么调试程序能?(没调试器,只能靠串口板打印信息)网上搜索一下。。。苦思冥想。。。(省略一万字)

终于,改好了!!!代码如下,


INT08U MPU_Read_Len(INT08U addr, INT08U reg, INT08U len, INT08U *buf)

{

    INT08U rlen = 0;

    CPU_SR_ALLOC();


    while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));


    I2C_GenerateSTART(I2C1, ENABLE);

    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));


    I2C_Send7bitAddress(I2C1, (addr<<1), I2C_Direction_Transmitter);

    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));


    I2C_SendData(I2C1, reg);

    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

    I2C_GenerateSTOP(I2C1, ENABLE);

    

    while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));


    I2C_GenerateSTART(I2C1, ENABLE);

    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));


    I2C_Send7bitAddress(I2C1, (addr<<1), I2C_Direction_Receiver);

    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));


    I2C_AcknowledgeConfig(I2C1, ENABLE);

    GPIO_SetBits(GPIOB,GPIO_Pin_7);


    OS_CRITICAL_ENTER();

    for(rlen = 0; rlen < len; rlen++)

    {

        if(len == 1+rlen)

        {

            I2C_AcknowledgeConfig(I2C1, DISABLE);

            (void)I2C1->SR2;

            I2C_GenerateSTOP(I2C1, ENABLE);

        }

        while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));

        *buf  = I2C_ReceiveData(I2C1);

        buf++;

    }

    OS_CRITICAL_EXIT();


    return rlen;

}


遇到的主要问题是:

1.接收数据的时候SDA要输出高电平!!!(3个感叹号代表重要的事情说3遍)stm32的SDA脚已选择了GPIO_Mode_AF_OD模拟开漏输出,MPU6050模块的SDA已外接了上拉电阻。如果stm32的SDA输出低电平,那么SDA就拉不高了。

2.网上看到有种做法是

I2C_AcknowledgeConfig(I2C1, DISABLE);

(void)I2C1->SR2;

I2C_GenerateSTOP(I2C1, ENABLE);

这3句要连着写,一是必须读SR2寄存器才能完成某些清寄存器标志的操作(看数据手册),这确实是是stm32做的坑,但不能说它有问题,反正正确流程是怎样就怎样,按正确流程来编码。

3.接收数据阶段我就用OS_CRITICAL_ENTER();关中断好了,保证I2C时序正确,保险一点。(其实这样理解也不对,应该整个I2C通信时序都有因为被打断而发生故障的风险,关键是在哪些地方可以被打断(如等待某些标志位置位的时候),哪些地方一定不能被打断(如等到了某事件后要读写寄存器时))

测试一下,串口打印出加速度、陀螺仪、温度等数据,OK,收工。

咦?怎么LCD显示一卡一卡的。看来I2C读数据太耗时了,还阻止了任务切换。改写一下MPU_Read_Len(INT08U addr, INT08U reg, INT08U len, INT08U *buf)函数吧。把它里面的等待标志位置位的那些while循环,改为pend一个信号量,标志位置位触发中断后再post一个信号量,等待交给中断来做,期间把CPU让给其他任务。

编码编码编码。。。又到12点了,躺床吃鸡睡觉。


第三天

一觉睡到中午饭,下午继续躺床吃鸡睡觉。晚饭后才编码完成,代码如下,


INT08U MPU6050_Read(INT08U addr, INT08U reg, INT08U len, INT08U *buf)

{

    OS_ERR err;

    CPU_TS ts;

    CPU_SR_ALLOC();

    INT08U rlen = 0;

    INT16U I2CTimeout;

    I2CReadState = I2C_CHECK_BUSY;

    I2C_ITConfig(I2C1, I2C_IT_ERR, ENABLE);

    while(I2C_READ_END > I2CReadState)

    {

        switch(I2CReadState)

        {

            case I2C_CHECK_BUSY:

                I2CTimeout = I2C_TIMEOUT*((INT16U)(SystemCoreClock/1000000));

                while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)&&(I2CTimeout--));

                if(0 == I2CTimeout) delay_ms(fac_ms);

                else

                {

                    CPU_CRITICAL_ENTER();

                    if(I2C_CHECK_BUSY == I2CReadState) I2CReadState = I2C_SEND_START;//I2C bus is not busy

                    else I2CReadState = I2C_READ_END;

                    CPU_CRITICAL_EXIT();

                }

                break;

            case I2C_SEND_START:

                I2C_GenerateSTART(I2C1, ENABLE);

                I2C_ITConfig(I2C1, I2C_IT_EVT, ENABLE);

                OSSemPend(&MPU6050_SEM, I2C_TIMEOUT*fac_us, OS_OPT_PEND_BLOCKING, &ts, &err);

                CPU_CRITICAL_ENTER();

                if((OS_ERR_NONE == err) && (I2C_SEND_START == I2CReadState)) I2CReadState = I2C_SEND_ADDR_SEND;

                else I2CReadState = I2C_READ_END;

                CPU_CRITICAL_EXIT();

                break;

            case I2C_SEND_ADDR_SEND:

                I2C_Send7bitAddress(I2C1, (addr<<1), I2C_Direction_Transmitter);

                I2C_ITConfig(I2C1, I2C_IT_EVT, ENABLE);

                OSSemPend(&MPU6050_SEM, I2C_TIMEOUT*fac_us, OS_OPT_PEND_BLOCKING, &ts, &err);

                CPU_CRITICAL_ENTER();

                if((OS_ERR_NONE == err) && (I2C_SEND_ADDR_SEND == I2CReadState)) I2CReadState = I2C_SELECT_REG;

                else I2CReadState = I2C_READ_END;

                CPU_CRITICAL_EXIT();

                break;

            case I2C_SELECT_REG:

                I2C_SendData(I2C1, reg);

                I2C_ITConfig(I2C1, I2C_IT_EVT, ENABLE);

                OSSemPend(&MPU6050_SEM, I2C_TIMEOUT*fac_us, OS_OPT_PEND_BLOCKING, &ts, &err);

                CPU_CRITICAL_ENTER();

                if((OS_ERR_NONE == err) && (I2C_SELECT_REG == I2CReadState)) I2CReadState = I2C_CHECK_BUSY_AGAIN;

                else I2CReadState = I2C_READ_END;

                CPU_CRITICAL_EXIT();

                I2C_GenerateSTOP(I2C1, ENABLE);

                break;

            case I2C_CHECK_BUSY_AGAIN:

                I2CTimeout = I2C_TIMEOUT*((INT16U)(SystemCoreClock/1000000));

                while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)&&(I2CTimeout--));

                if(0 == I2CTimeout) delay_ms(fac_ms);

                else

                {

                    CPU_CRITICAL_ENTER();

                    if(I2C_CHECK_BUSY_AGAIN == I2CReadState) I2CReadState = I2C_SEND_START_AGAIN;//I2C bus is not busy

                    else I2CReadState = I2C_READ_END;

                    CPU_CRITICAL_EXIT();

                }

                break;

            case I2C_SEND_START_AGAIN:

                I2C_GenerateSTART(I2C1, ENABLE);

                I2C_ITConfig(I2C1, I2C_IT_EVT, ENABLE);

                OSSemPend(&MPU6050_SEM, I2C_TIMEOUT*fac_us, OS_OPT_PEND_BLOCKING, &ts, &err);

                CPU_CRITICAL_ENTER();

                if((OS_ERR_NONE == err) && (I2C_SEND_START_AGAIN == I2CReadState)) I2CReadState = I2C_SEND_ADDR_RECV;

                else I2CReadState = I2C_READ_END;

                CPU_CRITICAL_EXIT();

                break;

            case I2C_SEND_ADDR_RECV:

                I2C_Send7bitAddress(I2C1, (addr<<1), I2C_Direction_Receiver);

                I2C_ITConfig(I2C1, I2C_IT_EVT, ENABLE);

                OSSemPend(&MPU6050_SEM, I2C_TIMEOUT*fac_us, OS_OPT_PEND_BLOCKING, &ts, &err);

                CPU_CRITICAL_ENTER();

                if((OS_ERR_NONE == err) && (I2C_SEND_ADDR_RECV == I2CReadState)) I2CReadState = I2C_RECV_DATA;

                else I2CReadState = I2C_READ_END;

                CPU_CRITICAL_EXIT();

                I2C_AcknowledgeConfig(I2C1, ENABLE);

                GPIO_SetBits(GPIOB,GPIO_Pin_7);

                break;

            case I2C_RECV_DATA:

                if(len == 1+rlen)

                {

                    I2C_AcknowledgeConfig(I2C1, DISABLE);

                    (void)I2C1->SR2;

                    I2C_GenerateSTOP(I2C1, ENABLE);

                }

                I2C_ITConfig(I2C1, I2C_IT_EVT, ENABLE);

                OSSemPend(&MPU6050_SEM, I2C_TIMEOUT*fac_us, OS_OPT_PEND_BLOCKING, &ts, &err);

                CPU_CRITICAL_ENTER();

                if((OS_ERR_NONE != err) || (I2C_RECV_DATA != I2CReadState)) I2CReadState = I2C_READ_END;

                CPU_CRITICAL_EXIT();

                *buf  = I2C_ReceiveData(I2C1);

                buf++;

                rlen++;

                if(len == rlen) I2CReadState = I2C_READ_END;

                break;

            default:

                break;

        }

    }

    I2C_ITConfig(I2C1, I2C_IT_ERR, DISABLE);

    return rlen;

}


里面的delay_ms函数在延时时间超过OS时间片时间时可以引起任务切换。

测试一下,OK,串口输出数据,收工。

咦?怎么LCD卡住完全了。研究一下。原来如此:

我获取MPU6050数据的任务代码如下,

while(1)

{

while(0 < sendmpudata)

{

sendmpudata–;

MPU_Get_Accelerometer(&aacx,&aacy,&aacz);

fprintf(UART_OUT, "ax:%5d, ay:%5d, az:%5d ", aacx, aacy, aacz);

res=MPU_Get_Gyroscope(&gyrox,&gyroy,&gyroz);

fprintf(UART_OUT, "gx:%5d, gy:%5d, gz:%5d ", gyrox, gyroy, gyroz);

temp=MPU_Get_Temperature();

fprintf(UART_OUT, “temp:%5dnn”, temp);

}

}

sendmpudata是在OS的定时器中递增的,用来定时一段时间就从MPU6050收数据然后发到串口上。问题是当sendmpudata=0时也就是还没到时间去收发MPU6050数据时,当前任务还在while(1)中跑,没有把CPU让给低优先级的LCD任务。于是我在while(0 < sendmpudata)前面插一条pend等待一个信号量。测试一下,LCD不卡,OK,收工。

不是说I2C通信流程要严格按照st的例程,不要打断吗?其实,当你理解这整个流程,就会很容易地把握哪些地方可以被打断,切换到其它任务,让I2C真正地融入多任务系统中。


突然发现,干嘛要搞那么复杂!!!耗时的步骤都交给中断来做,剩下在任务级完成的步骤不就是读写一下DR寄存器吗?把剩下这些极其简单的步骤也交给中断处理函数来做也可以啊,那任务里面就只需pend等待一个信号量,等中断处理函数处理完I2C通信流程post释放出一个信号量,任务级读写I2C数据函数就可以返回了。再改改代码。。。又到12点了,躺床吃鸡睡觉。


第四天

廿二八洗邋遢,晚上继续改代码。改完如下,


INT08U I2C_Process(INT08U direction, INT08U addr, INT08U len, INT08U *pbuf)

{

    OS_ERR err;

    CPU_TS ts;

    INT08U res;

    INT16U I2CTimeout;


    I2CDirection = direction;

    I2CStage = I2C_DETECT_START;

    I2CError = I2C_SUCCESS;


    I2CAddress = addr;

    I2CLength = len;

    pI2CBuffer = pbuf;


    I2CIndex = 0;


    I2CTimeout = I2C_TIMEOUT*((INT16U)(SystemCoreClock/1000000));

    while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)&&(I2CTimeout--));

    if(0 < I2CTimeout)

    {

        I2C_GenerateSTART(I2C1, ENABLE);

        I2C_ITConfig(I2C1, I2C_IT_EVT|I2C_IT_ERR, ENABLE);

        OSSemPend(&I2C_SEM, I2C_TIMEOUT*fac_us, OS_OPT_PEND_BLOCKING, &ts, &err);

        I2C_ITConfig(I2C1, I2C_IT_EVT|I2C_IT_ERR, DISABLE);

        if(OS_ERR_NONE == err) res = I2C_SUCCESS;

        else if(OS_ERR_TIMEOUT == err)

        {

            if(OS_ERR_NONE == I2CError) res = I2C_OS_TIMEOUT;

            else res = I2CError;

        }

        else res = I2C_OS_ERROR;

    }

    else res = I2C_HW_BUSY;

    return res;

}



static INT08U MPU6050_Write(INT08U addr, INT08U reg, INT08U len, INT08U *pbuf)

{

    INT08U buf[MPU6050_WRITE_MAX], i, res;

    buf[0] = reg;

    for(i = 0; i < len; i++) buf[i+1] = pbuf[i];

    res = I2C_Process(I2C_SEND, addr, len+1, buf);

    return res;

}


static INT08U MPU6050_Read(INT08U addr, INT08U reg, INT08U len, INT08U *pbuf)

{

    INT08U res;

    res = I2C_Process(I2C_SEND, addr, 1, ®);

    if(I2C_SUCCESS == res) res = I2C_Process(I2C_RECV, addr, len, pbuf);

    return res;

}


void I2C1_EV_IRQHandler(void)

{

    OS_ERR err;

    CPU_SR_ALLOC();

    CPU_CRITICAL_ENTER();

    OSIntEnter();

    CPU_CRITICAL_EXIT();

    switch(I2CStage)

    {

        case I2C_DETECT_START:

            if(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))

            {

                if(I2C_SEND == I2CDirection) I2C_Send7bitAddress(I2C1, (I2CAddress<<1), I2C_Direction_Transmitter);

                else I2C_Send7bitAddress(I2C1, (I2CAddress<<1), I2C_Direction_Receiver);

                I2CStage = I2C_DETECT_ADDR;

            }

            break;

        case I2C_DETECT_ADDR:

            if(I2C_SEND == I2CDirection)

            {

                if(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))

                {

                    I2C_SendData(I2C1, *pI2CBuffer);

                    pI2CBuffer++;

                    I2CIndex++;

                    if(I2CLength > I2CIndex) I2CStage = I2C_DETECT_DATA;

                    else

                    {

                        I2C_GenerateSTOP(I2C1, ENABLE);

                        OSSemPost(&I2C_SEM, OS_OPT_POST_1, &err);

                    }

                }

            }

            else

            {

                if(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))

                {

                    if(I2CLength > I2CIndex+1) I2C_AcknowledgeConfig(I2C1, ENABLE);

                    else

                    {

                        I2C_AcknowledgeConfig(I2C1, DISABLE);

                        (void)I2C1->SR2;

                        I2C_GenerateSTOP(I2C1, ENABLE);

                    }

                    GPIO_SetBits(GPIOB,GPIO_Pin_7);

                    I2CStage = I2C_DETECT_DATA;

                }

            }

            break;

        case I2C_DETECT_DATA:

            if(I2C_SEND == I2CDirection)

            {

                if(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED))

                {

                    I2C_SendData(I2C1, *pI2CBuffer);

                    pI2CBuffer++;

                    I2CIndex++;

                    if(I2CLength <= I2CIndex)

                    {

                        I2C_GenerateSTOP(I2C1, ENABLE);

                        OSSemPost(&I2C_SEM, OS_OPT_POST_1, &err);

                    }

                }

            }

            else

            {

                if(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))

                {

                    *pI2CBuffer  = I2C_ReceiveData(I2C1);

                    pI2CBuffer++;

                    I2CIndex++;

                    if(I2CLength == I2CIndex+1)

                    {

                        I2C_AcknowledgeConfig(I2C1, DISABLE);

                        (void)I2C1->SR2;

                        I2C_GenerateSTOP(I2C1, ENABLE);

                    }

                    else if(I2CLength <= I2CIndex) OSSemPost(&I2C_SEM, OS_OPT_POST_1, &err);

                    else{}

                }

            }

            break;

        default:

            break;

    }

    OSIntExit();

}


void I2C1_ER_IRQHandler(void)

{

    if(SET == I2C_GetITStatus(I2C1, I2C_IT_BERR)) I2CError = I2C_HW_BERR;

    else if(SET == I2C_GetITStatus(I2C1, I2C_IT_ARLO)) I2CError = I2C_HW_ARLO;

    else if(SET == I2C_GetITStatus(I2C1, I2C_IT_AF)) I2CError = I2C_HW_AF;

    else if(SET == I2C_GetITStatus(I2C1, I2C_IT_OVR)) I2CError = I2C_HW_OVR;

    else if(SET == I2C_GetITStatus(I2C1, I2C_IT_PECERR)) I2CError = I2C_HW_PECERR;

    else if(SET == I2C_GetITStatus(I2C1, I2C_IT_TIMEOUT)) I2CError = I2C_HW_TIMEOUT;

    else if(SET == I2C_GetITStatus(I2C1, I2C_IT_SMBALERT)) I2CError = I2C_HW_SMBALERT;

    else{}

    I2C_ClearITPendingBit(I2C1, I2C_IT_SMBALERT|I2C_IT_TIMEOUT|I2C_IT_PECERR|I2C_IT_OVR|I2C_IT_AF|I2C_IT_ARLO|I2C_IT_BERR);

    I2C_ITConfig(I2C1, I2C_IT_ERR, DISABLE);

}


上面代码就是最终版本。主要思想是,STM32硬件I2C的读写数据流程大致相同,封装成一个函数,通过传入参数来区分读写,这是I2C层。向MPU6050写数据就是写寄存器地址、写数据。从MPU6050读数据就是写寄存器地址、读数据,分别封装成MPU6050读、写两个函数,供操作MPU6050的其他函数调用,这是MPU6050模块层。任务级调用操作MPU6050的函数,获取想要的数据,这是APP层。

效果如下,

在这里插入图片描述


推荐阅读

史海拾趣

DSP Group Inc公司的发展小趣事

为了进一步拓展业务领域和市场份额,DSP Group于2021年与Synaptics公司进行了合并。这次合并使DSP Group能够借助Synaptics在触控、显示和人机交互技术方面的优势,进一步拓展其在物联网、智能家居等领域的应用。同时,合并后的新实体也通过资源整合和优势互补,实现了更高的运行效率和更大的市场竞争力。

BB公司的发展小趣事

2000年,BB公司迎来了发展史上的一个重要时刻——被美国德州仪器公司收购。这一收购对于BB公司来说,既是挑战也是机遇。德州仪器作为全球领先的半导体公司,为BB公司提供了更广阔的平台和更丰富的资源。在德州仪器的支持下,BB公司得以继续深化技术创新和市场拓展,进一步提升了其在电子行业的地位。

宝乘(baocheng)公司的发展小趣事

作为淄博市的重大项目,宝乘公司得到了各级政府的大力支持。这些支持不仅体现在资金扶持上,还包括政策优惠、人才引进等多个方面。在政府的助力下,宝乘公司得以快速发展,不断壮大。同时,公司也积极响应政府的号召,参与新旧动能转换等重大项目,为地方经济的发展做出了贡献。

AEC Design公司的发展小趣事
在快速发展的过程中,AEC Design公司也面临着来自市场、技术、政策等多方面的挑战。然而,公司始终保持着清醒的头脑和稳健的步伐。通过不断调整战略、优化管理、加强研发等方式,公司成功应对了各种挑战,实现了稳健前行。
迈翔科技(COILMX)公司的发展小趣事

迈翔科技深知品质对于企业的重要性。为了确保产品质量,公司引进了ISO9001管理体系,并严格按照该体系进行生产和管理。此外,公司还建立了完善的质量检测体系,对每一批产品进行严格的质量检测和控制,确保产品出厂合格率达到了行业领先水平。

Electroswitch公司的发展小趣事

进入21世纪后,Electroswitch开始积极实施国际化战略。公司通过收购和兼并等方式,不断拓展海外市场。同时,公司还加强了与国际知名企业的合作,共同开发新产品和新技术。这些举措使得Electroswitch的国际化水平得到了显著提升,也为公司的未来发展奠定了坚实的基础。

问答坊 | AI 解惑

工业控制器的设计

我司有一设备,需要定时控制它的开启与关闭,一位工程师便使用了一种集成块,输入完plc程序,制作出一个控制器,除了基本开启与关闭设备的功能外,还有定时报警和数显的功能。我也想学习这种技术,假如从零开始,设计制作出这样的控制器需要学习哪 ...…

查看全部问答>

如何利用isp给可编程器件下载程序?

大家好,小弟还是个学生,想学PLD,又没钱买昂贵的编程器,我想问一下如何给MAX7000S系列下载程序?是用并口isp下载线?像最简单的EPM7032SLC44-10引脚该如何连?用什么软件下载?那位懂得来详细说一下,万分感谢!!!!…

查看全部问答>

DM9000简单问题

我把wince 的DM9000驱动移植好了。可以用RDP那个组建和PC连接了。PC也可以PING通WINCE, 现在的问题是,在WINCE中,我用DHCP就一直获取不到IP,IP都是169.254.*.*那些,而我真正的IP应该是192.168.*.* 如果用静态IP一切都正常。在EBOOT中,通过DHC ...…

查看全部问答>

造成LED面板灯损坏的原因分析

LED面板灯由于有着许多优点,现今已经被越来越多的应用于人们的生活中,它不仅可以用来当作日常的照明工具,同时还可以作为装饰品为建筑增添特别的意义。LED面板灯具有高效、节能、环保的产品特性,而且它的寿命也比普通照明灯具要长很多 ...…

查看全部问答>

一个电源芯片的输出端用什么样的电感器?

我拿到一个原理图,里面电源芯片的输出端用了一个电感,220uH 1)我查了这个电源芯片的输出能力是3A,于是去查VISHAY的电感器产品,发现一般的“表贴电感”,220uH下没有那么大的工作电流。然后我查了“滤波线圈”,发现有220uH,3A的,但是滤波线 ...…

查看全部问答>

[7月11日,北京站] 2013 TI 多核DSP研讨会火热报名中

德州仪器 (TI) TMS320C66x 针对众多高性能市场进行了优化,可充分满足通信与网络、工业自动化、医疗影像、关键性任务、视频基础架构以及高性能计算等应用需求。围绕 KeyStone C66x系列, 2013 TI 多核 DSP 技术研讨会即将在北京 (7月11日) 召开, ...…

查看全部问答>

出9成新SATA品牌焊台,数显,恒温,无铅,原价1000多,400出

1、高频涡流发热,升降温速度快 2、特殊的传感器设计,保证快速响应,调温快捷 3、精确地温控电路,温度控制在±2°C 4、自动休眠,节能有延长寿命 5、ESD静电拆消设计,满足防静电工作需求 有意联系QQ2710917797 [ 本帖最后由 LCYGCS 于 20 ...…

查看全部问答>

大家来讨论一下模拟地和数字地的区别

如题 大家来讨论一下模拟地和数字地的区别\0\0\0eeworldpostqq…

查看全部问答>

【新手问题】蓝牙CC2540模块

说明书上说EN脚下地就可以开启广播,我不明白什么意思,下地是指跟GND脚接上吗?有高手帮忙解答一下吗?谢谢了…

查看全部问答>