STM32: Cờ bận được đặt sau khi khởi tạo I2C


8

Đối với tài liệu tham khảo: vấn đề tương tự được mô tả ở đó, nhưng giải pháp của tác giả không phù hợp với tôi - I2C bận cờ hành vi lạ

Tôi đã sử dụng STM32CubeMX để tạo mẫu dự án với khởi tạo ngoại vi I2C. Thật không may, nó hoạt động bằng cách nào đó kỳ lạ: sau khi HAL_I2C_MspInit(I2C1)được gọi, xe buýt được coi là bận rộn vĩnh viễn.

Nếu tôi cố gắng áp dụng

__HAL_RCC_I2C1_FORCE_RESET();
HAL_Delay(1000);
__HAL_RCC_I2C1_RELEASE_RESET();

Điều đó giải quyết vấn đề với BUSYcờ, nhưng gây ra sự cố - SBbit không được đặt sau khi STARTđược tạo. Theo trình gỡ lỗi, các thanh ghi I2C bị xóa hoàn toàn sau khi thiết lập lại - tôi nghi ngờ đây là vấn đề với phương thức đó.

Tôi cũng thú nhận sự sụt giảm điện áp ngắn ở đường SDA trong khi khởi động, đó có lẽ là nguyên nhân của vấn đề. Tôi đã xem xét kỹ hơn về mã khởi tạo chân SDA / SCL được tạo bởi CubeMX:

void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
{

  GPIO_InitTypeDef GPIO_InitStruct;
  if(hi2c->Instance==I2C1)
  {
  /* USER CODE BEGIN I2C1_MspInit 0 */

  /* USER CODE END I2C1_MspInit 0 */

    /**I2C1 GPIO Configuration    
    PB6     ------> I2C1_SCL
    PB7     ------> I2C1_SDA 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* Peripheral clock enable */
    __HAL_RCC_I2C1_CLK_ENABLE();

  /* USER CODE BEGIN I2C1_MspInit 1 */

  /* USER CODE END I2C1_MspInit 1 */
  }

}

Tôi đã thay đổi nó để kích hoạt đồng hồ trước khi HAL_GPIO_Init()gọi và bây giờ giao tiếp I2C của tôi hoạt động (ít nhất là tôi chưa nhận thấy điều gì kỳ lạ).

Cuối cùng, câu hỏi của tôi là - có giải pháp nào tốt hơn cho việc này không? CubeMX đặt mã kích hoạt đồng hồ sau khi gọi phương thức khởi tạo GPIO. Tôi có thể ở lại với hai yêu cầu __HAL_RCC_I2C1_CLK_ENABLE(), nhưng theo tôi thì khá là xấu, vì vậy tôi đang tìm kiếm bất kỳ giải pháp nào tốt hơn, cả phần mềm hoặc phần cứng.

Thiết bị là STM32F100RB trên bảng STM32VLDiscovery (với STLink v1), trong trường hợp có vấn đề.


Lưu ý, bảng errata chính xác được đặt tại st.com/content/ccc/resource/technical/document/errata_sheet/a9/ Kẻ nào dành cho STM32F100x4 / 6/8 / B đó là những gì bạn có. Mục 2.10.7 liên quan đến khóa BẬN. Vì bất kỳ lý do gì, mọi người đều đăng errata từ các chip 10xxC / D / E và 10 (1-3) 8 / B. Có, đôi khi tất cả các chip trong một chuỗi sẽ có cùng một lỗi, nhưng bạn không thể tin vào điều đó.
GB - AE7OO

Câu trả lời:


6

Theo tôi, mã STM32CubeMX không nên được coi là mã sẵn sàng để sử dụng, nhưng một số ví dụ như bạn có thể bắt đầu. Với hầu hết các bộ vi điều khiển, nó hoạt động, nhưng có một số trường hợp hiếm khi không.

Nếu bạn biết nó không hoạt động và bạn cũng đã tìm ra giải pháp, bạn không cần phải tuân theo mã gốc. Trong trường hợp của bạn, bạn có thể bỏ qua __HAL_RCC_I2C1_CLK_ENABLE()cuộc gọi sau khi khởi tạo GPIO và để lại cuộc gọi trước nó. Nếu nó hoạt động, và bạn đã nói nó hoạt động, sau đó sử dụng cách làm việc. Ngay cả phần mềm của ST cũng có thể có lỗi.

Bạn đang sử dụng bảng chính thức nên phần cứng sẽ ổn, nhưng bạn có thể kiểm tra xem các giá trị điện trở kéo lên có chính xác không. Hoặc nếu một thiết bị nô lệ làm một cái gì đó trong quá trình khởi tạo.

Cách tốt nhất là chạy mã của bạn với mọi thứ bị ngắt kết nối với Discovery (ngoài các pull-up) và kiểm tra xem nó có bị kẹt trong bận không. Nếu có, sẽ ổn nếu bạn thay thế dòng đó trong mã được tạo. Nó không phải là sửa đổi lớn.

Thật không may, không có bất kỳ exapmle I2C nào trong gói ví dụ STM32CubeF1 (đây không phải là trình tạo mã), trong STM32Cube_FW_F1_V1.4.0 \ Project \ STM32VL-Discovery \ Ví dụ. Nhưng nếu bạn kiểm tra các MspInitchức năng của UART hoặc SPI. Đồng hồ được bật trong cả hai trước khi khởi động GPIO .

void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
  GPIO_InitTypeDef  GPIO_InitStruct;

  if (hspi->Instance == SPIx)
  {
    /*##-1- Enable peripherals and GPIO Clocks #################################*/
    /* Enable GPIO TX/RX clock */
    SPIx_SCK_GPIO_CLK_ENABLE();
    SPIx_MISO_GPIO_CLK_ENABLE();
    SPIx_MOSI_GPIO_CLK_ENABLE();
    /* Enable SPI clock */
    SPIx_CLK_ENABLE();

    /*##-2- Configure peripheral GPIO ##########################################*/
    /* SPI SCK GPIO pin configuration  */
    GPIO_InitStruct.Pin       = SPIx_SCK_PIN;
    GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull      = GPIO_PULLDOWN;
    GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(SPIx_SCK_GPIO_PORT, &GPIO_InitStruct);

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{  
  GPIO_InitTypeDef  GPIO_InitStruct;

  /*##-1- Enable peripherals and GPIO Clocks #################################*/
  /* Enable GPIO TX/RX clock */
  USARTx_TX_GPIO_CLK_ENABLE();
  USARTx_RX_GPIO_CLK_ENABLE();


  /* Enable USARTx clock */
  USARTx_CLK_ENABLE(); 

  /*##-2- Configure peripheral GPIO ##########################################*/  
  /* UART TX GPIO pin configuration  */
  GPIO_InitStruct.Pin       = USARTx_TX_PIN;
  GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull      = GPIO_PULLUP;
  GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_HIGH;

  HAL_GPIO_Init(USARTx_TX_GPIO_PORT, &GPIO_InitStruct);

Vì vậy, tôi nghĩ rằng giải pháp của bạn là hoàn toàn tốt.


Tôi đã kiểm tra hành vi đó không có thiết bị nào khác ngoài bảng Discovery trên các dòng SDA và SCL, vẫn BUSY:( Đối số cuối cùng của bạn khá chắc chắn, thực sự không có gì để phản đối :) Cảm ơn.
Alexey Malev

1
Tôi không sử dụng thư viện HAL của ST, nhưng tôi gặp rắc rối tương tự - cờ bận luôn luôn BẬT. Và tôi có thể xác nhận rằng khởi tạo GPIO sau khi đồng hồ I2C thực sự có ích.
klasyc

1
Tương tự ở đây, dòng vẫn bận, trên một "viên thuốc màu xanh" stm32 f103. Đã sửa bằng cách khởi tạo đồng hồ trước.
kalmiya

3

Đây là một số mã có thể giúp bạn ra ngoài. Về cơ bản, đó là sự hiện thực hóa bảng Errata (phần 2.14.7) được đề cập trong câu trả lời trước. Tôi đang sử dụng thư viện HAL và có một số tham chiếu đến các định nghĩa tiêu đề trình điều khiển IKS01A1 (sự cố của tôi với vấn đề là con quay hồi chuyển trên bảng đó).

/* USER CODE BEGIN 1 */
/**
1. Disable the I2C peripheral by clearing the PE bit in I2Cx_CR1 register.
2. Configure the SCL and SDA I/Os as General Purpose Output Open-Drain, High level
(Write 1 to GPIOx_ODR).
3. Check SCL and SDA High level in GPIOx_IDR.
4. Configure the SDA I/O as General Purpose Output Open-Drain, Low level (Write 0 to
GPIOx_ODR).
5. Check SDA Low level in GPIOx_IDR.
6. Configure the SCL I/O as General Purpose Output Open-Drain, Low level (Write 0 to
GPIOx_ODR).
7. Check SCL Low level in GPIOx_IDR.
8. Configure the SCL I/O as General Purpose Output Open-Drain, High level (Write 1 to
GPIOx_ODR).
9. Check SCL High level in GPIOx_IDR.
10. Configure the SDA I/O as General Purpose Output Open-Drain , High level (Write 1 to
GPIOx_ODR).
11. Check SDA High level in GPIOx_IDR.
12. Configure the SCL and SDA I/Os as Alternate function Open-Drain.
13. Set SWRST bit in I2Cx_CR1 register.
14. Clear SWRST bit in I2Cx_CR1 register.
15. Enable the I2C peripheral by setting the PE bit in I2Cx_CR1 register.
**/
void HAL_I2C_ClearBusyFlagErrata_2_14_7(I2C_HandleTypeDef *hi2c) {

    static uint8_t resetTried = 0;
    if (resetTried == 1) {
        return ;
    }
    uint32_t SDA_PIN = NUCLEO_I2C_EXPBD_SDA_PIN;
    uint32_t SCL_PIN = NUCLEO_I2C_EXPBD_SCL_PIN;
    GPIO_InitTypeDef GPIO_InitStruct;

    // 1
    __HAL_I2C_DISABLE(hi2c);

    // 2
    GPIO_InitStruct.Pin = SDA_PIN|SCL_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    HAL_GPIO_WRITE_ODR(GPIOB, SDA_PIN);
    HAL_GPIO_WRITE_ODR(GPIOB, SCL_PIN);

    // 3
    GPIO_PinState pinState;
    if (HAL_GPIO_ReadPin(GPIOB, SDA_PIN) == GPIO_PIN_RESET) {
        for(;;){}
    }
    if (HAL_GPIO_ReadPin(GPIOB, SCL_PIN) == GPIO_PIN_RESET) {
        for(;;){}
    }

    // 4
    GPIO_InitStruct.Pin = SDA_PIN;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    HAL_GPIO_TogglePin(GPIOB, SDA_PIN);

    // 5
    if (HAL_GPIO_ReadPin(GPIOB, SDA_PIN) == GPIO_PIN_SET) {
        for(;;){}
    }

    // 6
    GPIO_InitStruct.Pin = SCL_PIN;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    HAL_GPIO_TogglePin(GPIOB, SCL_PIN);

    // 7
    if (HAL_GPIO_ReadPin(GPIOB, SCL_PIN) == GPIO_PIN_SET) {
        for(;;){}
    }

    // 8
    GPIO_InitStruct.Pin = SDA_PIN;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    HAL_GPIO_WRITE_ODR(GPIOB, SDA_PIN);

    // 9
    if (HAL_GPIO_ReadPin(GPIOB, SDA_PIN) == GPIO_PIN_RESET) {
        for(;;){}
    }

    // 10
    GPIO_InitStruct.Pin = SCL_PIN;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    HAL_GPIO_WRITE_ODR(GPIOB, SCL_PIN);

    // 11
    if (HAL_GPIO_ReadPin(GPIOB, SCL_PIN) == GPIO_PIN_RESET) {
        for(;;){}
    }

    // 12
    GPIO_InitStruct.Pin = SDA_PIN|SCL_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Alternate = NUCLEO_I2C_EXPBD_SCL_SDA_AF;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

   // 13
   hi2c->Instance->CR1 |= I2C_CR1_SWRST;

   // 14
   hi2c->Instance->CR1 ^= I2C_CR1_SWRST;

   // 15
   __HAL_I2C_ENABLE(hi2c);

   resetTried = 1;
}

void HAL_GPIO_WRITE_ODR(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  /* Check the parameters */
  assert_param(IS_GPIO_PIN(GPIO_Pin));

  GPIOx->ODR |= GPIO_Pin;
}

2

Một điều nữa cần xem xét: Trong tài liệu ERRATA này (trang 24), bạn có thể thấy có một trục trặc trong bộ lọc tương tự I2C có thể gây BUSYtreo cờ. Ngoài ra còn có một cách giải quyết bạn có thể thử - nó hoạt động với tôi.


1

Xem tờ eratta : tờ eratta

Giải pháp thay thế: Đầu ra bộ lọc tương tự SCL và SDA được cập nhật sau khi quá trình chuyển đổi xảy ra trên dòng SCL và SDA tương ứng. Quá trình chuyển đổi SCL và SDA có thể bị ép buộc bởi phần mềm cấu hình I / O I2C ở chế độ đầu ra. Sau đó, khi các bộ lọc tương tự được mở khóa và xuất mức dòng SCL và SDA, cờ BusY có thể được đặt lại bằng cài đặt lại phần mềm và I2C có thể vào chế độ chính. Do đó, trình tự sau phải được áp dụng:

  1. Vô hiệu hóa thiết bị ngoại vi I2C bằng cách xóa bit PE trong thanh ghi I2Cx_CR1.
  2. Định cấu hình I / O SCL và SDA dưới dạng Thoát nước đầu ra mục đích chung, Cấp độ cao (Viết 1 đến GPIOx_ODR).

  3. Kiểm tra SCL và SDA Cấp cao trong GPIOx_IDR.

  4. Định cấu hình I / O SDA là Thoát nước đầu ra mục đích chung, Mức thấp (Viết 0 đến GPIOx_ODR).
  5. Kiểm tra SDA Mức thấp trong GPIOx_IDR.
  6. Định cấu hình I / O SCL dưới dạng Thoát nước đầu ra mục đích chung, Mức thấp (Viết 0 đến GPIOx_ODR).
  7. Kiểm tra mức độ thấp của SCL trong GPIOx_IDR.
  8. Định cấu hình I / O SCL là Thoát nước đầu ra mục đích chung, Cấp độ cao (Viết 1 đến GPIOx_ODR).
  9. Kiểm tra SCL Cấp cao trong GPIOx_IDR.
  10. Định cấu hình I / O SDA là Thoát nước đầu ra mục đích chung, Cấp độ cao (Viết 1 đến GPIOx_ODR).
  11. Kiểm tra SDA Cấp cao trong GPIOx_IDR.
  12. Định cấu hình I / O SCL và SDA dưới dạng chức năng thay thế Open-Drain.
  13. Đặt bit SWRST trong thanh ghi I2Cx_CR1.
  14. Xóa bit SWRST trong thanh ghi I2Cx_CR1.
  15. Kích hoạt I2C periphe ral bằng cách đặt bit PE trong thanh ghi I2Cx_CR1.

0

Tôi gặp vấn đề tương tự trên STM32F429, sử dụng khối lập phương V1.15.0.

Tôi vẫn nhận thấy rằng khi thiết lập lại mềm (ví dụ như khi gỡ lỗi), SCL sẽ ở mức THẤP ngay sau khi bắt đầu HAL_GPIO_Init()cuộc gọi.

Tôi đã thử thiết lập lại xe buýt bằng cách gửi 16 đồng hồ lúc init, theo khuyến nghị của i2c-bus.org .

Nhưng nó không giúp được gì. Thêm mã "thiết lập lại" của bạn đã giải quyết được mánh khóe:

__HAL_RCC_I2C1_FORCE_RESET();
HAL_Delay(2);
__HAL_RCC_I2C1_RELEASE_RESET();

Sau một số thử nghiệm tôi phát hiện ra rằng độ trễ 2 ms là đủ. Tôi giữ lại quá trình tái cấu trúc đồng hồ thủ công vì một đường truyền có thể bị treo khi đặt lại CPU.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.