Site icon TAPIT

Giao tiếp I2C trên STM32F103 với module RTC DS3231

I2C – Inter- Intergrated Circuit là chuẩn truyền thông nối tiếp gồm 2 dây Clock và  Data được phát minh bởi hãng Philips. Chuẩn I2C trở nên thông dụng với nhiều module và IC sử dụng rộng rãi như: IC nhớ (24Cxxx), cảm biến góc nghiêng(MPU6050), module giao tiếp LCD (dùng IC PCF8574), IC thời gian thực (DS1307, DS3231, BQ32000), IC chuyển đổi tín hiệu số, tương tự… Chuẩn giao tiếp này hỗ trợ giao tiếp ở tốc độ 100Khz hoặc 400Khz. 

[HỌC ONLINE: LẬP TRÌNH VI ĐIỀU KHIỂN STM32, VI XỬ LÝ ARM CORTEX – M]

STM32F103C8T6 có 2 bộ chuyển đổi I2C với các tính năng được tóm tắt như sau:

Quá trình truyền data tùy thuộc vào modde cấu hình của I2C là master hoặc slave, ở chế độ 10 bit địa chỉ hay 7 bit địa chỉ (phổ biến hơn). Dưới đây là ví dụ về cấu hình I2C là master:

Tóm tắt quá trình truyền như sau:

Trên đây là tóm lược về quá trình truyền, nhận. Các bạn có thể tham khảo trong Reference Manual, mục I2C – Function description.

* Một vài thanh ghi quan trọng.

        1.   I2C_CR1 – I2C control register 1.

2. I2C_CR1 – I2C control register 1.

      3. I2C_OAR1 – I2C own address register 1.

     4. I2C_DR – I2C data register.

Thanh ghi này gồm 8 bit, chứa data của quá trình truyền hoặc nhận.

       5. I2C_SR1 – I2C status register 1:

Thanh ghi này chứa các cờ báo trạng thái của quá trình giao tiếp I2C:

       6.  I2C_CCR – I2C clock control register:

Tiếp theo bài viết sẽ hướng dẫn ví dụ thực hành sử dụng board STM32F103C8T6 để giao tiếp với module DS3231 qua giao thức I2C.

Ở bài này mình sẽ sẽ dùng 2 ngoại vi:

                + I2C: Giao tiếp với module DS3231

                + UART: Sử dụng để retarget/redirect hàm printf để in ra màn hình.

 Các bạn nào chưa hiểu về UART hay retarget/redirect có thể đọc lại các bài viết sau:

Đầu tiên các bạn mở CubeMX và thực hiện các bước cấu hình như sau để sinh code.

Bước 1:

Đầu tiên, chúng ta sẽ cấu hình chân nạp dữ liệu SWD.

Bước 2:

Sử dụng UART1 với mục đích in dữ liệu thời gian thực lên phần mềm Hercules. Chỉnh sửa lại tốc độ baud là 9600 Bits/s

Bước 3:

Tiếp chúng ta bật chức năng I2C

Bước 4:

Ở phần cấu hình I2C, chúng ta giữ mặc định:

Bước 5:

Tiếp theo, Click vào tab NVIC Settings để bật ngắt I2C khi có sự kiện xảy ra hoặc có lỗi đường truyền xảy ra.

Bước 6:

Chuyển sang tab NVIC Settings để bật ngắt UART

Bước 7:

Đến đây, các bạn Setting project của mình sau đó sinh code từ CubeMX

Bước 8:

 Copy đoạn code retarget/redirect hàm printf để sử dụng cho UART. 

Bước 9:

         – 0x00: Chứa dữ liệu về giây. Thanh ghi ứng với địa chỉ 0x00 này sử dụng 8 bit chính, từ 0-3 cho hàng đơn vị và từ 4-6 cho hàng chục, giá trị của giây đếm từ 0-59, sau khi đếm đến 59 sẽ bị reset về 0.

         – 0x01: Chứa dữ liệu về phút. Thanh ghi ứng với địa chỉ 0x01 này sử dụng 4 bit chính từ 0-3 cho hàng đơn vị và 4-6 cho hàng chục, giá trị của phút từ 0-59, sau khi đếm đến 59 sẽ bị reset về 0 

         – 0x02: Chứa dữ liệu về giờ. Thanh ghi ứng với địa chỉ 0x02 sử dụng 8 bit chính, từ 0-3 cho hàng đơn vị và từ 4-5 cho hàng chục, giá trị của giờ từ 0-12 tùy thuộc vào việc set bit 6 trong thanh ghi, thông thường mình sẽ để nguyên thanh ghi chỉ đọc giá trị giờ ra thì giá trị của giờ sẽ từ 0-23, sẽ bị reset về 0 nếu qua 23h

         – 0x03: Chứa dữ liệu về thứ trong tuần (daysofweek). Thanh ghi ứng với địa chỉ 0x03 sử dụng 3 bit chính, từ 0-3 cho hàng đơn vị, giá trị thứ trong tuần từ 0-7 tương ứng 0 là thứ 2, 1 là thứ 3…

         – 0x04: Chứa dữ liệu về ngày. Thanh ghi ứng với địa chỉ 0x04 sẽ sử dụng 8 bit chính, từ 0-3 cho hàng đơn vị của ngày và bit 4-5 cho hàng chục, giá trị ngày từ 0-31.

         – 0x05: Chứa dữ liệu về tháng. Thanh ghi ứng với địa chỉ 0x05 sẽ sử dụng 8bit chính, từ 0-3 cho hàng đon vị và bit 4 cho hàng chục, giá trị của tháng là 0-12.

         – 0x06: Chứa dữ liệu về giờ. Thanh ghi ứng với địa chỉ 0x06 sử dụng 8bit chính, từ 0-3 cho hàng đơn vị và 4-7 cho hàng chục, giá trị của năm là 0-99.

 

Bước 10:

     #define DS3231_ADRESS (0x68<<1)

     #define DS3231_REG_TIME 0x00

Sau đó bạn hãy khai báo 1 struct để phục vụ cho việc đọc ghi dữ liệu thời gian thực. Sau khi tạo xong struct thì chúng ta sẽ khai báo luôn.

DS3231_t DS3231;

Bước 11:

Vì dữ liệu ở trong thanh ghi của DS3231 là mã BCD (Binary code decimal) vì vậy chúng ta phải chuyển sang mã DEC (Decimal). Tiếp theo chúng ta sẽ viết 2 hàm, hàm thứ nhất là chuyển đổi mã BCD sang mã DEC mục đích để khi đọc dữ liệu từ DS3231 chúng ta sẽ chuyển sang hệ 10, hàm thứ 2 là chuyển mã DEC sang mã BCD mục đích để khi cấu hình thời gian ban đầu chúng ta sẽ chuyển đổi thời gian từ mã sang mã nhị phân để thực hiện động tác ghi vào thanh ghi.

Bước 12:

            + void I2C_WriteBuffer(I2C_HandleTypeDef hi, uint8_t DEV_ADDR, uint8_t sizebuf)

            + void I2C_ReadBuffer(I2C_HandleTypeDef hi, uint8_t DEV_ADDR, uint8_t sizebuf)

            + I2C_HandleTypeDef hi: I2C hiện tại bạn đang sử dụng

            + DEV_ADDR: Địa chỉ thanh ghi bắt đầu ghi dữ liệu

             + Sizebuf: Size dữ liệu mà bạn ghi vào bộ nhớ

Hàm truyền I2C đến địa chỉ DS323, dữ liệu truyền đi nằm trong bộ đệm DS3231.I2C_Buffer

HAL_I2C_Master_Transmit(&hi, (uint16_t) DEV_ADDR, (uint8_t*) &DS3231.I2C_Buffer, (uint16_t) sizebuf, (uint32_t)1000);

Hàm đọc I2C từ địa chỉ DS3231, dữ liệu đọc được nằm trong bộ đệm DS3231.I2C_Buffer

HAL_I2C_Master_Receive(&hi, (uint16_t) DEV_ADDR, (uint8_t*) &DS3231.I2C_Buffer, (uint16_t) sizebuf, (uint32_t)1000)

Bước 13:

Tiếp theo chúng ta sẽ thực hiện hàm đọc dữ liệu, để bắt đầu đọc dữ liệu thì đầu tiên chúng ta sẽ ghi vào phần tử đầu tiên bộ đệm I2C_Buffer giá trị 0x00 sau đó truyền đến địa chỉ DS3231 để bắt đầu thực hiện quá trình  đọc/ghi dữ liệu. Đợi cho trạng thái của I2C sẵn sàng rồi mới đọc dữ liệu. Vì dữ liệu đọc được ở mã BCD nên chúng ta sẽ chuyển sáng mã DEC bằng hàm RTC_BCD2DEC();

Bước 14:

Tiếp theo là hàm cài đặt dữ liệu thời gian thực, để ghi vào bộ nhớ DS3231 đầu tiên các bạn hãy ghi 0x00 vào phần tử đầu tiên của bộ đệm để bắt đầu quá trình ghi, sau đó ghi dữ liệu cần cấu hình.

Bước 15:

Tiếp theo chúng ta sẽ cấu hình giờ ban đầu cho DS3231 và ở trong vòng while(1) của hàm main mình đã đã dữ liệu thời gian thực 1s 1 lần và sử dụng hàm printf để in lên trên màn hình Hercules.

Bước 16:

Mở phần mềm Hercules lên và quan sát dữ liệu.

Chúc các bạn thành công!

Xem thêm: Tổng hợp các bài hướng dẫn Lập trình vi điều khiển STM32 tại đây.

Nhóm TAPIT ARM R&D