I2C: различия между версиями

Материал из me-robotics wiki
Строка 3: Строка 3:
 
#include "i2c_loop_common.h"
 
#include "i2c_loop_common.h"
 
#include "stdbool.h"
 
#include "stdbool.h"
 +
#include "wakeup.h"
  
 
void i2c_init(I2C_TypeDef* i2c)
 
void i2c_init(I2C_TypeDef* i2c)
Строка 9: Строка 10:
 
     PM->CLK_APB_P_SET |= PM_CLOCK_GPIO_0_M | PM_CLOCK_I2C_0_M;
 
     PM->CLK_APB_P_SET |= PM_CLOCK_GPIO_0_M | PM_CLOCK_I2C_0_M;
 
     PM->CLK_APB_M_SET |= PM_CLOCK_PAD_CONFIG_M | PM_CLOCK_WU_M | PM_CLOCK_PM_M ;
 
     PM->CLK_APB_M_SET |= PM_CLOCK_PAD_CONFIG_M | PM_CLOCK_WU_M | PM_CLOCK_PM_M ;
 +
    for (volatile int i = 0; i < 10; i++);
  
  
Строка 43: Строка 45:
 
     */
 
     */
 
     i2c->CR1 = I2C_CR1_PE_M;
 
     i2c->CR1 = I2C_CR1_PE_M;
 +
    xprintf("\nМастер. Старт\n");
 
}
 
}
  
 
void i2c_master_write(I2C_TypeDef* i2c, uint8_t slave_adr, uint8_t data[], uint8_t byte_count, bool shift)
 
void i2c_master_write(I2C_TypeDef* i2c, uint8_t slave_adr, uint8_t data[], uint8_t byte_count, bool shift)
 
{
 
{
 +
    xprintf("\nОтправка\n");
 +
    uint8_t slave_adr_print = slave_adr;
 
     // shift - true когда адрес ведомого должен быть сдвинут на 1 бит
 
     // shift - true когда адрес ведомого должен быть сдвинут на 1 бит
 
     if(!shift)
 
     if(!shift)
Строка 53: Строка 58:
 
     }
 
     }
  
    xprintf("Отправка по адресу 0x%02x байта  %d\n", slave_adr, data[0]);
 
 
     /*
 
     /*
 
     *
 
     *
Строка 66: Строка 70:
 
     */
 
     */
 
     i2c->CR2 = I2C_CR2_SADD(slave_adr) | I2C_CR2_WR_M | I2C_CR2_NBYTES(byte_count) | I2C_CR2_AUTOEND_M; // sizeof(data)
 
     i2c->CR2 = I2C_CR2_SADD(slave_adr) | I2C_CR2_WR_M | I2C_CR2_NBYTES(byte_count) | I2C_CR2_AUTOEND_M; // sizeof(data)
    i2c->TXDR = data[0]; // первая загрузка в буфер
+
 
    while(i2c->ISR & I2C_ISR_TXE_M); // TXE = 0 - регистр TXDR заполнен
 
 
     i2c->CR2 |= I2C_CR2_START_M; // старт отправки адреса, а затем данных  
 
     i2c->CR2 |= I2C_CR2_START_M; // старт отправки адреса, а затем данных  
  
     for (uint8_t i = 1; i < byte_count; i++)
+
     for (uint8_t i = 0; i < byte_count; i++)
 
     {
 
     {
         xprintf("Отправка по адресу 0x%02x байта  %d\n", slave_adr, data[i]);
+
        //xprintf("Отправка\n");
 +
        while(!(i2c->ISR & I2C_ISR_TXIS_M))// TXIS = 1 - предыдущий байт доставлен
 +
        {
 +
            if(i2c->ISR & I2C_ISR_NACKF_M)//при записи байта слейв прислал NACK
 +
            {
 +
                xprintf("Запись. NACKF = %d\n", (i2c->ISR & I2C_ISR_NACKF_M) >> I2C_ISR_NACKF_S);
 +
                break;
 +
            }
 +
        }
 +
       
 +
        if(i2c->ISR & I2C_ISR_NACKF_M)//при записи байта слейв прислал NACK
 +
        {
 +
            i2c->ICR |=  I2C_ICR_NACKCF_M; // сброс флага STOPF и сброс флага NACKF
 +
            break;
 +
        }
 +
         xprintf("Отправка по адресу 0x%02x байта  0x%02x\n", slave_adr_print, data[i]);
 
         i2c->TXDR = data[i];
 
         i2c->TXDR = data[i];
        while(i2c->ISR & I2C_ISR_TXE_M); // TXE = 0 - регистр TXDR заполнен
 
 
     }
 
     }
   
 
  
    //while (!(i2c->ISR & I2C_ISR_TC_M)); // TC = 1 - все байты переданы
 
   
 
    //while(!(i2c->ISR & I2C_ISR_TXIS_M)); // TXIS - уставляется после оправки каждого байта и полученя ACK
 
    //while(!(i2c->ISR & I2C_ISR_NACKF_M )); // NACKF - уставляется после полученя NACK
 
   
 
    //i2c->CR2 |= I2C_CR2_STOP_M; // отправка бита stop вручную
 
 
}
 
}
 +
 +
  
 
void i2c_master_read(I2C_TypeDef* i2c, uint8_t slave_adr, uint8_t data[], uint8_t byte_count, bool shift)
 
void i2c_master_read(I2C_TypeDef* i2c, uint8_t slave_adr, uint8_t data[], uint8_t byte_count, bool shift)
 
{
 
{
 +
    uint8_t slave_adr_print = slave_adr;
 
     // shift - true когда адрес ведомого должен быть сдвинут на 1 бит
 
     // shift - true когда адрес ведомого должен быть сдвинут на 1 бит
 
     if(!shift)
 
     if(!shift)
Строка 93: Строка 106:
 
         slave_adr = slave_adr << 1;
 
         slave_adr = slave_adr << 1;
 
     }
 
     }
     xprintf("\nI2C reads bytes back\n");
+
     xprintf("\nЧтение\n");
     i2c->CR2 = I2C_CR2_SADD(slave_adr) | I2C_CR2_RD_M | I2C_CR2_NBYTES(byte_count) | I2C_CR2_AUTOEND_M; // I2C_CR2_NBYTES(sizeof(data)/sizeof(data[0]))
+
     i2c->CR2 = I2C_CR2_SADD(slave_adr) | I2C_CR2_RD_M | I2C_CR2_NBYTES(byte_count) | I2C_CR2_AUTOEND_M;  
 
     i2c->CR2 |= I2C_CR2_START_M;
 
     i2c->CR2 |= I2C_CR2_START_M;
   
+
 
   
 
 
     for(uint8_t i = 0; i < byte_count; i++)
 
     for(uint8_t i = 0; i < byte_count; i++)
 
     {
 
     {
         while(!(i2c->ISR & I2C_ISR_RXNE_M)); // байт принят когда RXNE = 1
+
       
 +
         while(!(i2c->ISR & I2C_ISR_RXNE_M)) // байт принят когда RXNE = 1
 +
        {
 +
            // if(i2c->ISR & I2C_ISR_NACKF_M)
 +
            // xprintf("Чтение. NACF = %d\n", (i2c->ISR & I2C_ISR_NACKF_M) >> I2C_ISR_NACKF_S);
 +
 
 +
            if((i2c->ISR & I2C_ISR_STOPF_M) && (i2c->ISR & I2C_ISR_NACKF_M))
 +
            {
 +
                xprintf("Чтение. STOPF = %d, NACKF = %d\n", (i2c->ISR & I2C_ISR_STOPF_M) >> I2C_ISR_STOPF_S, (i2c->ISR & I2C_ISR_NACKF_M) >> I2C_ISR_NACKF_S);
 +
                break;
 +
            }
 +
           
 +
        }
 +
 
 +
        if((i2c->ISR & I2C_ISR_STOPF_M) && (i2c->ISR & I2C_ISR_NACKF_M))
 +
        {
 +
            i2c->ICR |= I2C_ICR_STOPCF_M | I2C_ICR_NACKCF_M; // сброс флага STOPF и сброс флага NACKF
 +
            break;
 +
        }
 
         data[i] = i2c->RXDR; // чтение байта и сброс RXNE
 
         data[i] = i2c->RXDR; // чтение байта и сброс RXNE
 +
        xprintf("Чтение по адресу 0x%02x байта  0x%02x\n", slave_adr_print, data[i]);
 
     }
 
     }
   
+
 
 
}
 
}
 +
 +
 +
  
 
int main()
 
int main()
 
{
 
{
 +
    PM->AHB_CLK_MUX = 0; // выбор источника тактирования. 0 - внешний кварц
 +
    PM->CPU_RTC_CLK_MUX = 0;// выбор часового кварца как внешний
 +
    for (volatile int i = 0; i < 100; i++);
 +
    WU->CLOCKS_SYS = 1 << 1; // выключение внутреннего системного кварца
 +
    WU->CLOCKS_BU = 1 << 1; // выключение внутреннего часовго кварца
 +
    for (volatile int i = 0; i < 100; i++) ;
 +
   
 +
 
     uint8_t slave_adr = 0x36; // адрес ведомого
 
     uint8_t slave_adr = 0x36; // адрес ведомого
     uint16_t to_send = 4000; // данные для оптавки
+
     uint16_t to_send = 300; // данные для оптавки
     uint8_t data[2] = {to_send >> 8, to_send & 0b0000000011111111}; // массив байтов на отправку
+
     uint8_t to_send_byte = 1; // данные для оптавки
 +
   
 +
    //uint8_t data[2] = {to_send >> 8, to_send & 0b0000000011111111}; // массив байтов на отправку
  
 +
    uint8_t data[1] = {to_send_byte}; // массив байтов на отправку
 
     i2c_init(I2C_0); // инициализация блока i2c  
 
     i2c_init(I2C_0); // инициализация блока i2c  
 
+
      
     i2c_master_write(I2C_0, slave_adr, data, 2, false); // запись данных по адресу slave_adr = 0x36 без сдвига адреса
 
    i2c_master_read(I2C_0, slave_adr, data, 2, false); // чтение данных по адресу slave_adr = 0x36 без сдвига адреса
 
 
 
  
 
     while (1)
 
     while (1)
 
     {
 
     {
 +
       
 +
        //i2c_master_write(I2C_0, slave_adr, data, 2, false); // запись данных по адресу slave_adr = 0x36 без сдвига адреса
 +
        //i2c_master_write(I2C_0, slave_adr, data, 1, false); // запись данных по адресу slave_adr = 0x36 без сдвига адреса
 +
 +
        //i2c_master_read(I2C_0, slave_adr, data, 2, false); // чтение данных по адресу slave_adr = 0x36 без сдвига адреса
 +
        i2c_master_read(I2C_0, slave_adr, data, 1, false); // чтение данных по адресу slave_adr = 0x36 без сдвига адреса
  
 +
        for (volatile int i = 0; i < 1000000; i++);
 
     }
 
     }
 
      
 
      

Версия 22:32, 3 июня 2022

Пример использования I2C MIK32 в режиме мастер

#include "common.h"
#include "i2c_loop_common.h"
#include "stdbool.h"
#include "wakeup.h"

void i2c_init(I2C_TypeDef* i2c)
{
    //Включаем тактирование необходимых блоков - GPIO_0, GPIO_1, GPIO_2 и модуля выбора режима GPIO 
    PM->CLK_APB_P_SET |= PM_CLOCK_GPIO_0_M | PM_CLOCK_I2C_0_M;
    PM->CLK_APB_M_SET |= PM_CLOCK_PAD_CONFIG_M | PM_CLOCK_WU_M | PM_CLOCK_PM_M ;
    for (volatile int i = 0; i < 10; i++);


    // обнуление регистра управления
    i2c->CR1 = 0;

    /*
    * Инициализация i2c
    * TIMING - регистр таймингов
    * 
    * SCLDEL - Задержка между изменением SDA и фронтом SCL в режиме ведущего и ведомого при NOSTRETCH = 0
    * 
    * SDADEL - Задержка между спадом SCL и изменением SDA в режиме ведущего и ведомого при NOSTRETCH = 0
    * 
    * SCLL - Время удержания SCL в состоянии логического «0» в режиме веедущего
    * 
    * SCLH - Время удержания SCL в состоянии логической «1» в режиме веедущего
    * 
    * PRESC - Делитель частоты I2CCLK. Используется для вычесления периода сигнала TPRESC для счетчиков предустановки, 
    * удержания, уровня «0»и «1»
    * 
    */
    i2c->TIMINGR = I2C_TIMINGR_SCLDEL(1) | I2C_TIMINGR_SDADEL(1) |
        I2C_TIMINGR_SCLL(20) | I2C_TIMINGR_SCLH(20) | I2C_TIMINGR_PRESC(3); //частота 164,7 кГц tsync1 + tsync2 = 10^(-6)

    /*
    *
    * CR1 - Регистр управления
    * 
    * PE - Управление интерфейсом: 0 – интерфейс выключен; 1 – интерфейс включен
    * 
    * STOPIE - Разрешение прерывания детектировании STOP: 0 – прерывание запрещено; 1 – прерывание разрешено
    *
    */
    i2c->CR1 = I2C_CR1_PE_M;
    xprintf("\nМастер. Старт\n");
}

void i2c_master_write(I2C_TypeDef* i2c, uint8_t slave_adr, uint8_t data[], uint8_t byte_count, bool shift)
{
    xprintf("\nОтправка\n");
    uint8_t slave_adr_print = slave_adr;
    // shift - true когда адрес ведомого должен быть сдвинут на 1 бит
    if(!shift)
    {
        slave_adr = slave_adr << 1;
    }

    /*
    *
    * CR2 - регистр управления 2
    * 
    * SADD - адрес ведомого
    * 
    * RD_WRN - Направление передачи: 0 – ведущий в режиме записи; 1 – ведущий в режиме чтения
    * 
    * AUTOEND - Управление режимом автоматического окончания: 0 – автоматическое окончание выкл; 1 – автоматическе окончание вкл
    * 
    */
    i2c->CR2 = I2C_CR2_SADD(slave_adr) | I2C_CR2_WR_M | I2C_CR2_NBYTES(byte_count) | I2C_CR2_AUTOEND_M; // sizeof(data)

    i2c->CR2 |= I2C_CR2_START_M; // старт отправки адреса, а затем данных 

    for (uint8_t i = 0; i < byte_count; i++)
    {
        //xprintf("Отправка\n");
        while(!(i2c->ISR & I2C_ISR_TXIS_M))// TXIS = 1 - предыдущий байт доставлен
        {
            if(i2c->ISR & I2C_ISR_NACKF_M)//при записи байта слейв прислал NACK
            {
                xprintf("Запись. NACKF = %d\n", (i2c->ISR & I2C_ISR_NACKF_M) >> I2C_ISR_NACKF_S);
                break;
            }
        }
        
        if(i2c->ISR & I2C_ISR_NACKF_M)//при записи байта слейв прислал NACK
        {
            i2c->ICR |=  I2C_ICR_NACKCF_M; // сброс флага STOPF и сброс флага NACKF 
            break;
        }
        xprintf("Отправка по адресу 0x%02x байта  0x%02x\n", slave_adr_print, data[i]);
        i2c->TXDR = data[i];
    }

}



void i2c_master_read(I2C_TypeDef* i2c, uint8_t slave_adr, uint8_t data[], uint8_t byte_count, bool shift)
{
    uint8_t slave_adr_print = slave_adr;
    // shift - true когда адрес ведомого должен быть сдвинут на 1 бит
    if(!shift)
    {
        slave_adr = slave_adr << 1;
    }
    xprintf("\nЧтение\n");
    i2c->CR2 = I2C_CR2_SADD(slave_adr) | I2C_CR2_RD_M | I2C_CR2_NBYTES(byte_count) | I2C_CR2_AUTOEND_M; 
    i2c->CR2 |= I2C_CR2_START_M;

    for(uint8_t i = 0; i < byte_count; i++)
    {
        
        while(!(i2c->ISR & I2C_ISR_RXNE_M)) // байт принят когда RXNE = 1
        {
            // if(i2c->ISR & I2C_ISR_NACKF_M)
            // xprintf("Чтение. NACF = %d\n", (i2c->ISR & I2C_ISR_NACKF_M) >> I2C_ISR_NACKF_S);

            if((i2c->ISR & I2C_ISR_STOPF_M) && (i2c->ISR & I2C_ISR_NACKF_M))
            {
                xprintf("Чтение. STOPF = %d, NACKF = %d\n", (i2c->ISR & I2C_ISR_STOPF_M) >> I2C_ISR_STOPF_S, (i2c->ISR & I2C_ISR_NACKF_M) >> I2C_ISR_NACKF_S);
                break;
            }
            
        }

        if((i2c->ISR & I2C_ISR_STOPF_M) && (i2c->ISR & I2C_ISR_NACKF_M))
        {
            i2c->ICR |= I2C_ICR_STOPCF_M | I2C_ICR_NACKCF_M; // сброс флага STOPF и сброс флага NACKF
            break;
        }
        data[i] = i2c->RXDR; // чтение байта и сброс RXNE
        xprintf("Чтение по адресу 0x%02x байта  0x%02x\n", slave_adr_print, data[i]);
    }

}




int main()
{
    PM->AHB_CLK_MUX = 0; // выбор источника тактирования. 0 - внешний кварц
    PM->CPU_RTC_CLK_MUX = 0;// выбор часового кварца как внешний
    for (volatile int i = 0; i < 100; i++); 
    WU->CLOCKS_SYS = 1 << 1; // выключение внутреннего системного кварца
    WU->CLOCKS_BU = 1 << 1; // выключение внутреннего часовго кварца
    for (volatile int i = 0; i < 100; i++) ; 
    

    uint8_t slave_adr = 0x36; // адрес ведомого
    uint16_t to_send = 300; // данные для оптавки
    uint8_t to_send_byte = 1; // данные для оптавки
    
    //uint8_t data[2] = {to_send >> 8, to_send & 0b0000000011111111}; // массив байтов на отправку

    uint8_t data[1] = {to_send_byte}; // массив байтов на отправку
    i2c_init(I2C_0); // инициализация блока i2c 
    

    while (1)
    {
        
        //i2c_master_write(I2C_0, slave_adr, data, 2, false); // запись данных по адресу slave_adr = 0x36 без сдвига адреса
        //i2c_master_write(I2C_0, slave_adr, data, 1, false); // запись данных по адресу slave_adr = 0x36 без сдвига адреса

        //i2c_master_read(I2C_0, slave_adr, data, 2, false); // чтение данных по адресу slave_adr = 0x36 без сдвига адреса
        i2c_master_read(I2C_0, slave_adr, data, 1, false); // чтение данных по адресу slave_adr = 0x36 без сдвига адреса

        for (volatile int i = 0; i < 1000000; i++); 
    }
    
    
}