الرئيسية قاعدة المعرفة الكهرباء والإلكترون بروتوكولات الاتصال: UART و SPI و I2C — كيف تتحدث المكونات
الكهرباء والإلكترون

بروتوكولات الاتصال: UART و SPI و I2C — كيف تتحدث المكونات

UART: الاتصال التسلسلي الأبسط

UART (Universal Asynchronous Receiver/Transmitter) هو أقدم وأبسط بروتوكول اتصال تسلسلي. يستخدم سلكين فقط: TX للإرسال و RX للاستقبال. لا يحتاج إشارة ساعة مشتركة لأن الطرفين يتفقان مسبقاً على سرعة الاتصال (Baud Rate).

معاملات الاتصال

  • Baud Rate: عدد البتات في الثانية (9600, 115200, الخ)
  • Data Bits: عادة 8 بت
  • Parity: None, Even, أو Odd (للكشف عن الأخطاء)
  • Stop Bits: 1 أو 2

التكوين الأشهر صناعياً: 9600 8N1 (9600 baud, 8 data bits, No parity, 1 stop bit) لاتصالات Modbus، و 115200 8N1 لأغراض التشخيص.

#include "stm32f4xx_hal.h"

UART_HandleTypeDef huart2;

void uart_init(uint32_t baudrate) {
    __HAL_RCC_USART2_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitTypeDef gpio = {0};
    gpio.Pin = GPIO_PIN_2 | GPIO_PIN_3;  // PA2=TX, PA3=RX
    gpio.Mode = GPIO_MODE_AF_PP;
    gpio.Pull = GPIO_PULLUP;
    gpio.Speed = GPIO_SPEED_FREQ_HIGH;
    gpio.Alternate = GPIO_AF7_USART2;
    HAL_GPIO_Init(GPIOA, &gpio);

    huart2.Instance = USART2;
    huart2.Init.BaudRate = baudrate;
    huart2.Init.WordLength = UART_WORDLENGTH_8B;
    huart2.Init.StopBits = UART_STOPBITS_1;
    huart2.Init.Parity = UART_PARITY_NONE;
    huart2.Init.Mode = UART_MODE_TX_RX;
    HAL_UART_Init(&huart2);
}

void uart_send(const char *data, uint16_t len) {
    HAL_UART_Transmit(&huart2, (uint8_t *)data, len, 100);
}

RS-485: UART للمسافات الطويلة

في المصانع، يُحوَّل UART إلى RS-485 عبر شريحة محوّل (مثل MAX485). RS-485 يدعم مسافات حتى 1200 متر وربط 32 جهازاً على نفس الخط. بروتوكول Modbus RTU يعمل فوق RS-485.

SPI: اتصال سريع مع عدة أجهزة

SPI (Serial Peripheral Interface) بروتوكول متزامن يستخدم 4 أسلاك:

  • MOSI: بيانات من المتحكم (Master) إلى الطرفي (Slave)
  • MISO: بيانات من الطرفي إلى المتحكم
  • SCK: إشارة الساعة
  • CS/SS: اختيار الجهاز (طرف لكل جهاز)

SPI سريع جداً (حتى عشرات MHz) ويُستخدم مع شاشات TFT، وبطاقات SD، ومحولات ADC الخارجية عالية الدقة.

SPI_HandleTypeDef hspi1;

void spi_init(void) {
    __HAL_RCC_SPI1_CLK_ENABLE();
    hspi1.Instance = SPI1;
    hspi1.Init.Mode = SPI_MODE_MASTER;
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;
    hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
    HAL_SPI_Init(&hspi1);
}

uint8_t spi_transfer(uint8_t tx_byte) {
    uint8_t rx_byte;
    HAL_SPI_TransmitReceive(&hspi1, &tx_byte, &rx_byte, 1, 10);
    return rx_byte;
}

I2C: ناقل بسلكين لعشرات المكونات

I2C (Inter-Integrated Circuit) يستخدم سلكين فقط:

  • SDA: خط البيانات
  • SCL: خط الساعة

كل جهاز على الناقل له عنوان فريد (7-bit: حتى 127 جهاز). سرعته أبطأ من SPI (100-400 KHz عادة) لكنه يوفر الأسلاك. يُستخدم مع مستشعرات الحرارة والرطوبة، شاشات OLED، وساعات RTC.

I2C_HandleTypeDef hi2c1;

void i2c_init(void) {
    __HAL_RCC_I2C1_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();

    GPIO_InitTypeDef gpio = {0};
    gpio.Pin = GPIO_PIN_6 | GPIO_PIN_7;  // PB6=SCL, PB7=SDA
    gpio.Mode = GPIO_MODE_AF_OD;
    gpio.Pull = GPIO_PULLUP;
    gpio.Speed = GPIO_SPEED_FREQ_HIGH;
    gpio.Alternate = GPIO_AF4_I2C1;
    HAL_GPIO_Init(GPIOB, &gpio);

    hi2c1.Instance = I2C1;
    hi2c1.Init.ClockSpeed = 400000;
    hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
    hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
    HAL_I2C_Init(&hi2c1);
}

HAL_StatusTypeDef i2c_write_reg(uint8_t addr, uint8_t reg, uint8_t val) {
    uint8_t buf[2] = {reg, val};
    return HAL_I2C_Master_Transmit(&hi2c1, addr << 1, buf, 2, 100);
}

uint8_t i2c_read_reg(uint8_t addr, uint8_t reg) {
    uint8_t val;
    HAL_I2C_Master_Transmit(&hi2c1, addr << 1, &reg, 1, 100);
    HAL_I2C_Master_Receive(&hi2c1, addr << 1, &val, 1, 100);
    return val;
}

متى تستخدم كل بروتوكول؟

المعيار UART SPI I2C
عدد الأسلاك 2 4+ 2
السرعة حتى 1 Mbps حتى 50 MHz حتى 3.4 MHz
عدد الأجهزة نقطة لنقطة عدة (بأسلاك CS) حتى 127
المسافة قصيرة (RS-485 للطويلة) قصيرة جداً قصيرة
الاستخدام الصناعي Modbus، تشخيص شاشات، ADC خارجي مستشعرات رقمية

مكتبات HAL: تبسيط التعامل مع البروتوكولات

في Rust مع embedded-hal، يتم تجريد البروتوكولات في سمات (Traits) موحدة تعمل مع أي متحكم:

use embedded_hal::i2c::I2c;

fn read_temperature<I: I2c>(i2c: &mut I, addr: u8) -> Result<f32, I::Error> {
    let mut buf = [0u8; 2];
    i2c.write_read(addr, &[0xE3], &mut buf)?;
    let raw = ((buf[0] as u16) << 8) | buf[1] as u16;
    let temp = 175.72 * (raw as f32) / 65536.0 - 46.85;
    Ok(temp)
}

هذا الكود يعمل مع STM32 أو ESP32 أو أي متحكم يدعم embedded-hal دون تعديل. هذه هي قوة التجريد في Rust المدمج.

مثال عملي: قراءة مستشعر BME280 عبر I2C وإرسال البيانات عبر UART

مستشعر BME280 يقيس الحرارة والرطوبة والضغط الجوي. نقرأه عبر I2C ونرسل البيانات عبر UART للتشخيص.

#define BME280_ADDR  0x76

typedef struct {
    float temperature;
    float humidity;
    float pressure;
} BME280_Data;

void bme280_init(void) {
    // إعادة ضبط المستشعر
    i2c_write_reg(BME280_ADDR, 0xE0, 0xB6);
    HAL_Delay(10);
    // وضع القياس: حرارة x2، رطوبة x2، ضغط x2
    i2c_write_reg(BME280_ADDR, 0xF2, 0x02);
    i2c_write_reg(BME280_ADDR, 0xF4, 0x4B);
    i2c_write_reg(BME280_ADDR, 0xF5, 0x08);
}

BME280_Data bme280_read(void) {
    BME280_Data data;
    uint8_t raw[8];
    uint8_t reg = 0xF7;

    HAL_I2C_Master_Transmit(&hi2c1, BME280_ADDR << 1, &reg, 1, 100);
    HAL_I2C_Master_Receive(&hi2c1, BME280_ADDR << 1, raw, 8, 100);

    // معالجة البيانات الخام (مبسّطة)
    int32_t adc_t = ((int32_t)raw[3] << 12) | ((int32_t)raw[4] << 4) | (raw[5] >> 4);
    data.temperature = adc_t / 100.0f;  // تحتاج معاملات المعايرة الفعلية
    data.pressure = 1013.25f;
    data.humidity = 50.0f;
    return data;
}

int main(void) {
    HAL_Init();
    i2c_init();
    uart_init(115200);
    bme280_init();

    while (1) {
        BME280_Data d = bme280_read();
        char buf[80];
        int len = snprintf(buf, sizeof(buf),
            "T=%.1f C  H=%.1f %%  P=%.1f hPa\r\n",
            d.temperature, d.humidity, d.pressure);
        uart_send(buf, len);
        HAL_Delay(1000);
    }
}

الخلاصة

بروتوكولات UART و SPI و I2C هي اللغات التي تتحدث بها المكونات الإلكترونية مع بعضها. اختيار البروتوكول المناسب يعتمد على السرعة المطلوبة وعدد الأجهزة والمسافة. في الدرس القادم سنتعلم المؤقتات و PWM للتحكم الدقيق بالتوقيت وسرعة المحركات.

UART SPI I2C serial protocol bus الاتصال التسلسلي بروتوكول الناقل المستشعرات الشاشة الذاكرة