STM32
直播中

张涛

7年用户 1313经验值
私信 关注
[问答]

STM32L0 LL库IIC发送不出我想要的数据是怎么回事?

FW版本是1.12;
使用IC是STM32L0F4U;
CUBEMX配置,使用LL库。
前提:因为从机的IIC是非标准协议,第一个字节不是控制R/W, 不能用HAL库解决,想使用LL库或者寄存器方式来实现,从设备没有问题,使用模拟方式能够实现。
问题:I2C_TXDR这个寄存器的数据,发送不出去。
完全采用LL库函数,其初始化函数如下,频率是400K 、

  • /* I2C1 init function */
  • void MX_I2C1_Init(void)
  • {

  •   /* USER CODE BEGIN I2C1_Init 0 */
  • //        LL_RCC_ClocksTypeDef rcc_clocks;
  •   /* USER CODE END I2C1_Init 0 */

  •   LL_I2C_InitTypeDef I2C_InitStruct = {0};

  •   LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

  •   LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
  •   /**I2C1 GPIO Configuration
  •   PA9   ------> I2C1_SCL
  •   PA10   ------> I2C1_SDA
  •   */
  •   GPIO_InitStruct.Pin = LL_GPIO_PIN_9;
  •   GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  •   GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
  •   GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
  •   GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
  •   GPIO_InitStruct.Alternate = LL_GPIO_AF_1;
  •   LL_GPIO_Init(GPIOA,  GPIO_InitStruct);

  •   GPIO_InitStruct.Pin = LL_GPIO_PIN_10;
  •   GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  •   GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
  •   GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
  •   GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
  •   GPIO_InitStruct.Alternate = LL_GPIO_AF_1;
  •   LL_GPIO_Init(GPIOA,  GPIO_InitStruct);

  •   /* Peripheral clock enable */
  •   LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);

  •   /* USER CODE BEGIN I2C1_Init 1 */
  • //
  • ////        LL_I2C_Disable(I2C1);
  •   /* USER CODE END I2C1_Init 1 */
  •   /** I2C Initialization
  •   */
  •   LL_I2C_EnableAutoEndMode(I2C1);
  •   LL_I2C_DisableOwnAddress2(I2C1);
  •   LL_I2C_DisableGeneralCall(I2C1);
  •   LL_I2C_EnableClockStretching(I2C1);
  •   I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
  •   I2C_InitStruct.Timing = 0x00601B28;
  •   I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
  •   I2C_InitStruct.DigitalFilter = 0;
  •   I2C_InitStruct.OwnAddress1 = 0;
  •   I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
  •   I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
  •   LL_I2C_Init(I2C1,  I2C_InitStruct);
  •   LL_I2C_SetOwnAddress2(I2C1, 0, LL_I2C_OWNADDRESS2_NOMASK);
  •   /* USER CODE BEGIN I2C1_Init 2 */

  •         LL_I2C_Enable(I2C1);

  •         I2C_write();
  •         /* 5.使能I2C外设. */
  •   /* USER CODE END I2C1_Init 2 */

  • }

其中I2C_write()的函数内容是

  • while(LL_I2C_IsActiveFlag_BUSY(I2Cx) == SET){
  •                 counter++;
  •                 if( counter == 25000 ) {//
  •                         Error_Handler();
  •                         return ERROR;
  •                 }
  •         }
  •         LL_I2C_TransmitData8(I2Cx, addr_reg);
  • //        I2Cx->CR2 |= I2C_CR2_START; /* 启动 */
  •         while(LL_I2C_IsActiveFlag_TXE(I2Cx)==RESET)
  •         {
  •                 LL_mDelay(1);
  •                 LL_I2C_TransmitData8(I2Cx, addr_reg);
  • //                I2Cx->CR2 |= I2C_CR2_START; /* 启动 */
  •         }

LL_I2C_TransmitData8()    这个库函数完全发送不出来数据,示波器抓取不到波形,TXE位一直是reset。如果使用这一条I2Cx->CR2 |= I2C_CR2_START; /* 启动 */,这能发出波形,但是数据类容并非LL_I2C_TransmitData8中的数据,而是配置的从机地址。
如果调用LL_I2C_HandleTransfer() 这个函数,则能发出来波形,但是这个函数会自动调整R/W位,所以不能在第一个字节使用。
使用这个方法未能实现我需要的功能。
方法二:利用手册中的示例代码,完全寄存器操作,配置好IIC和GPIIO,配置如下:

  •   RCC->IOPENR |= RCC_IOPENR_GPIOBEN;

  •   /* (1) PU for I2C signals */
  •   /* (2) open drain for I2C signals */
  •   /* (3) AF5 for I2C signals */
  •   /* (4) Select AF mode (10) on PB13 and PB14 */
  •   GPIOA->PUPDR = (GPIOA->PUPDR   ~(GPIO_PUPDR_PUPD9 | GPIO_PUPDR_PUPD10))
  •                  | (GPIO_PUPDR_PUPD9_0 | GPIO_PUPDR_PUPD10_0); /* (1) */
  •   GPIOA->OTYPER |= GPIO_OTYPER_OT_9 | GPIO_OTYPER_OT_10; /* (2) */
  •   GPIOA->AFR[1] = (GPIOA->AFR[1]   ~((0xF << ( 1 * 4 )) | (0xF << ( 2 * 4 ))))
  •                   | (1 << ( 1 * 4 )) | (1 << ( 2 * 4 )); /* (3) */
  •   GPIOA->MODER = (GPIOA->MODER   ~(GPIO_MODER_MODE9 | GPIO_MODER_MODE10))
  •                  | (GPIO_MODER_MODE9_1 | GPIO_MODER_MODE10_1); /* (4) */
  •     /* Enable the peripheral clock I2C2 */
  •   RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;

  •   /* Configure I2C2, master */
  •   /* (1) Timing register value is computed with the AN4235 xls file,
  •          fast Mode @400kHz with I2CCLK = 16MHz, rise time = 100ns,
  •          fall time = 10ns */
  •   /* (2) Periph enable, receive interrupt enable */
  •   /* (3) Slave address = 0x5A, read transfer, 1 byte to receive, autoend */
  •   I2C1->TIMINGR = (uint32_t)0x00300619; /* (1) */
  •   I2C1->CR1 = I2C_CR1_PE; /* (2) */
  •   I2C1->CR2 =  I2C_CR2_AUTOEND | (1<<16) | (0X5A<<1); /* (3) */

调用如下:

  • while(1){
  •                 if((I2Cx->ISR   I2C_ISR_TXE) == (I2C_ISR_TXE)) /* Check Tx empty */
  •                 {
  •                   I2Cx->TXDR = 0x48; /* Byte to send */
  •                   I2Cx->CR2 |= I2C_CR2_START; /* Go */
  •                 }
  •                 LL_mDelay(1);
  •         }

发的数据也不是0x48,而是从机地址0x5A<<1 .
实在没招了,求大佬指点

回帖(2)

韩俊

2024-4-3 15:06:43
第一个字节不是标准地址和控制读写位,那么可以按照从机的需求把需要的值配给相关的地址寄存器I2C-CR2 ,使得主机发送的地址就是从机需要的值。这个外设是按照标准I2C协议设计的,所以一使能就会发出START+ADDRESS。试试看能不能变通一下。从手册上看,省略不了地址值的发送
举报

klysa

2024-4-3 16:03:10

首先,确保你的I2C总线上没有其他设备正在使用,并且确认从设备的地址正确。然后,你可以尝试执行以下步骤来解决问题:

1. 确保I2C时钟已经正确配置。你可以使用`LL_RCC_GetSystemClocksFreq(&rcc_clocks)`函数来获取系统时钟频率并进行检查。

2. 初始化I2C控制器。

```c
void MX_I2C1_Init(void)
{
    // 使能I2C1的时钟
    LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);

    // 配置I2C1的时钟源和时钟速度
    LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_SYSCLK);
    LL_I2C_SetClockSpeedMode(I2C1, LL_I2C_CLOCK_SPEED_STANDARD, SystemCoreClock, 100000, 0);

    // 配置I2C模式和主/从模式
    LL_I2C_SetMode(I2C1, LL_I2C_MODE_I2C);
    LL_I2C_SetMasterMode(I2C1, LL_I2C_MASTER_MODE_SINGLE);

    // 使能I2C总线
    LL_I2C_Enable(I2C1);
}
```

3. 配置I2C传输。

```c
void I2C1_Transmit(uint8_t address, uint8_t* data, uint16_t size)
{
    // 等待总线空闲
    while (!LL_I2C_IsActiveFlag_BUSY(I2C1))
        ;

    // 生成START信号
    LL_I2C_GenerateStartCondition(I2C1);
    while (!LL_I2C_IsActiveFlag_SB(I2C1))
        ;

    // 发送从设备地址和读写位
    LL_I2C_TransmitData8(I2C1, (address << 1) | LL_I2C_DIRECTION_WRITE);
    while (!LL_I2C_IsActiveFlag_ADDR(I2C1))
        ;
    LL_I2C_ClearFlag_ADDR(I2C1);

    // 发送数据
    for (uint16_t i = 0; i < size; i++)
    {
        LL_I2C_TransmitData8(I2C1, data[i]);
        while (!LL_I2C_IsActiveFlag_TXE(I2C1))
            ;
    }
   
    // 生成STOP信号
    LL_I2C_GenerateStopCondition(I2C1);
    while (!LL_I2C_IsActiveFlag_STOP(I2C1))
        ;
}
```

你可以调用`I2C1_Transmit()`函数来发送数据。确保地址数据和待发送数据的格式是正确的。


举报

更多回帖

发帖
×
20
完善资料,
赚取积分