PROGCONT.RU

Форма входа







Регистрация Вход/

Пример Пример Пример


STM8S I2C настройка и использование.

 При разработке нового проекта "Контроллер управления освещением аквариума" мне пришлось использовать микросхему DS1307( часы реального времени) с которой происходит обмен данными по I2C, что и привело к изучению данного интерфейса.
 I2C последовательная асимметричная шина для связи между интегральными схемами внутри электронных приборов, использует две двунаправленные линии связи SDA и SCL, стандартная скорость обмена данными может доходить до 100KHz и быстрая до 400KHz. Линия связи в I2C является общей и каждое находящееся на ней устройство может быть как главным который инициализирует передачу так же подчинённым отвечающим на запросы главного. Трудность при организации связи для этого интерфейса это особые процедуры старта передачи данных и окончания, которые должно строго выполнять как главное так и подчинённое устройство, что вы должны будете организовать в коде программы для этого читаем тут описание STM8 I2C.

 Я же из за сложности I2C и упрощения вам задачи написал рабочие функции, для работы контроллера как главного на линии и обменом информацией с устройствами по семибитному адресу.
 Для лучшего понимания, я выкладываю схемы порядка действия для отправки, приёма данных и ниже функции которые должны это делать, сопоставив действия схемы и функции вы поймёте как это работает, а если нет то просто берите мои функции и используйте в своих целях.

 Внимание! В функции имеется переменная time_out, которая предотвращает зависание программы из проблем на линии I2C. Она подбирается в зависимости от частоты тактирования контроллера и которую обязательно определяем в #define TimErr.
 Адрес устройства нужно сместить на один бит влево перед передачей его в функцию. В нулевом бите байта адреса помечается направление, если этот бит установлен, то читаем данные из устройства, если нет устройство должно принять данные.

 Опишу лишь одну функцию, инициализация I2C.
   I2C_Init( OutputClockFrequencyHz, OwnAddress, I2C_DutyCycle, Ack, AddMode, InputClockFrequencyMHz );
    OutputClockFrequencyHz-выходная частота тактирования линии SCL, от 0 до 400000;
    OwnAddress-адрес, необходим если контроллер будет работать как SLAVE( подчинённый), в MASTER( главный) в нём нет необходимости может быть любым числом;
    I2C_DutyCycle-параметр необходимы при выходной частоте свыше 100kHz( быстрый режим), задает соотношение между tLOW и tHIGH линии SCL. Может принимать значения I2C_DUTYCYCLE_2 и I2C_DUTYCYCLE_16_9 для указания рабочих циклов 2:1 и 16:9 соответственно. Выбирая заданный режим синхронизации, мы можем поделить частоту тактирования периферии, чтобы достичь желаемой тактовой частоты I2C;
    Ack-подтверждение принятого байта данных или адреса подчинённым. Возможны варианты: I2C_ACK_NONE-не подтверждать, I2C_ACK_CURR-подтверждаем каждый байт и I2C_ACK_NEXT-подтверждаем первый принятый второй нет( для приёма двух байт данных);
    AddMode-длина используемого адреса, I2C_ADDMODE_7BIT-семибитный адрес или I2C_ADDMODE_10BIT- десятибитный;
    InputClockFrequencyMHz-частота тактирования I2C.


 Начнём с функции отправки данных контроллером устройству.

нет рисунка

 Функция принимает на вход семибитный адрес устройства, количество отправляемых данных и массив, в котором будут отправлены данные. На схеме показано, что отправляется байт Send offset command нулевая ячейка отправляемого массива, это команда или просто количество байт, принимающее устройство должно знать что делать с данными или просто сколько их принимать, но может быть, что в отправке данного байта нет необходимости как с DS1307.

/*Функция для отправка данных подчинённому.*/
void I2C_Send_data( uint8_t Addr, uint16_t countData, uint8_t* dataBuffer){
uint32_t time_out= TimErr;//Счетчик от зависания функции.
//Poll busy bit
while(( I2C_GetFlagStatus( I2C_FLAG_BUSBUSY)) && ( time_out--));//Проверяем занятость линии I2C.
//Set START
I2C_GenerateSTART( ENABLE);//Запуск I2C для передачи данных.
time_out= TimErr;//Счетчик от зависания функции.
//Poll SB
while(( !I2C_CheckEvent( I2C_EVENT_MASTER_MODE_SELECT)) && ( time_out--));//Ждём установки бита MASTER.
//Send 7- bit address+ RW = 0
I2C_Send7bitAddress(( uint8_t)Addr, I2C_DIRECTION_TX);//Отсылаем адрес вызываемого устройства и бит отсылки данных.
time_out= TimErr;//Счетчик от зависания функции.
//Poll ADDR
while(( !I2C_GetFlagStatus( I2C_FLAG_ADDRESSSENTMATCHED)) && ( time_out--));//Ждём когда нужное устройство подтвердит.
//Clear ADDR flag
(void)I2C->SR1; (void)I2C->SR3;//Комбинация для сброса ADDR.
while( countData){//Повторяем пока не отправятся все данные.
time_out= TimErr;//Счетчик от зависания функции.
//Poll TxE
while( !(I2C->SR1 & 128) && ( time_out--));//Ждём когда буфер передающего регистра будет пуст.
//Send offset command and data
I2C_SendData( *dataBuffer);
*dataBuffer++;
countData--;
}
time_out= TimErr;//Счетчик от зависания функции.
//Poll TxE and BTF
while(( !I2C_CheckEvent( I2C_EVENT_MASTER_BYTE_TRANSMITTED)) && ( time_out--));//Ждём окончания отправки данных.
//Set STOP
I2C_GenerateSTOP( ENABLE);//Установка STOP бита на линии.
time_out= TimErr;//Счетчик от зависания функции.
//Poll STOPF
while(( I2C->CR2 & I2C_CR2_STOP) && ( time_out--));//Ждём остановки передачи и STOP на линии.
}

 С принятием данных немножко по сложней, тут должно быть три разных функции это принятие одного байта данных, два байта и три или более.

нет рисунка

 Чтение одного байта из устройства, функция возвращает один байт данных принимает на вход адрес устройства.

/*Функция читает из устройства один байт данных.*/
uint8_t I2C_Read_one_byte( uint8_t Addr){
uint32_t time_out= TimErr;//Счетчик от зависания функции.
while(( I2C_GetFlagStatus( I2C_FLAG_BUSBUSY))&& (time_out--));//Проверяем занятость линии I2C.
I2C_GenerateSTART( ENABLE);//Запуск I2C для передачи данных.
time_out= TimErr;//Счетчик от зависания функции.
while(( !I2C_CheckEvent( I2C_EVENT_MASTER_MODE_SELECT))&& (time_out--));//Ждём установки бита MASTER.
I2C_Send7bitAddress(( uint8_t)Addr, I2C_DIRECTION_RX);//Отсылаем адрес вызываемого устройства и бит приёма данных.
time_out= TimErr;//Счетчик от зависания функции.
while(( !I2C_GetFlagStatus( I2C_FLAG_ADDRESSSENTMATCHED))&& (time_out--));//Ждём когда нужное устройство подтвердит.
/*Функционал для приёма одного байта.*/
//Clear ACK
I2C_AcknowledgeConfig( I2C_ACK_NONE);//Отключаем ACK после приёма последнего байта, что бы не отправлялись данные.
//Clear ADDR flag
(void)I2C->SR1; (void)I2C->SR3;//Комбинация для сброса ADDR.
//Set STOP
I2C_GenerateSTOP( ENABLE);//Установка STOP бита на линии.
time_out= TimErr;//Счетчик от зависания функции.
//Poll RXNE
while(( !I2C_GetFlagStatus( I2C_FLAG_RXNOTEMPTY))&& (time_out--));//Ждём когда придут данные.
//Read byte
uint8_t read_byte= I2C_ReceiveData();//Читаем данные.
time_out= TimErr;//Счетчик от зависания функции.
//Poll STOPF
while(( I2C->CR2 & I2C_CR2_STOP)&& (time_out--));//Ждём остановки передачи и STOP на линии.
I2C_AcknowledgeConfig( I2C_ACK_CURR);//Устанавливаем контроль бита ACK.
return read_byte;
}

 Чтение двух байтов из устройства, функция принимает адрес устройства и массив где сохранятся данные.

/*Функция читает из устройства два байта данных.*/
void I2C_Read_two_byte( uint8_t Addr, uint8_t* dataBuffer){
uint32_t time_out= TimErr;//Счетчик от зависания функции.
while(( I2C_GetFlagStatus( I2C_FLAG_BUSBUSY))&& (time_out--));//Проверяем занятость линии I2C.
I2C_GenerateSTART( ENABLE);//Запуск I2C для передачи данных.
time_out= TimErr;//Счетчик от зависания функции.
while(( !I2C_CheckEvent( I2C_EVENT_MASTER_MODE_SELECT))&& (time_out--));//Ждём установки бита MASTER.
I2C_Send7bitAddress(( uint8_t)Addr, I2C_DIRECTION_RX);//Отсылаем адрес вызываемого устройства.
time_out= TimErr;//Счетчик от зависания функции.
while(( !I2C_GetFlagStatus( I2C_FLAG_ADDRESSSENTMATCHED))&& (time_out--));//Ждём когда нужное устройство подтвердит.
/*Функционал для приёма двух байтов.*/
//Set POS
I2C_AcknowledgeConfig( I2C_ACK_NEXT);//Автоматическое отключение ACK для второго принятого байта.
//Clear ADDR flag
(void)I2C->SR1; (void)I2C->SR3;//Комбинация для сброса ADDR.
//Clear ACK
I2C_AcknowledgeConfig( I2C_ACK_NONE);//Отключаем ACK после приёма последнего байта, что бы не отправлялись данные. time_out= TimErr;//Счетчик от зависания функции.
//Poll BTF
while (( !I2C_GetFlagStatus( I2C_FLAG_TRANSFERFINISHED))&&(time_out--));//Ждём отправку всех данных устройству.
//Set STOP
I2C_GenerateSTOP( ENABLE);//Установка STOP бита на линии.
//Read 1 st byte
*dataBuffer= I2C_ReceiveData();
*dataBuffer++;
//Read 2 nd byte
*dataBuffer= I2C_ReceiveData();
time_out= TimErr;//Счетчик от зависания функции.
//Poll STOPF
while(( I2C->CR2 & I2C_CR2_STOP)&& (time_out--));//Ждём остановки передачи и STOP на линии.
I2C_AcknowledgeConfig( I2C_ACK_CURR);//Устанавливаем контроль бита ACK.
}

 Чтение трёх и более байт данных, функция принимает адрес устройства, количество байт для чтение которое должно быть не меньше трёх и массив куда будут сохранены данные.

/*Функция читает из устройства три или больше байта данных.*/
void I2C_Read_three_more_byte( uint8_t Addr, uint16_t countData, uint8_t* dataBuffer){
uint32_t time_out= TimErr;//Счетчик от зависания функции.
while(( I2C_GetFlagStatus( I2C_FLAG_BUSBUSY))&& (time_out--));//Проверяем занятость линии I2C.
I2C_GenerateSTART( ENABLE);//Запуск I2C для передачи данных.
time_out= TimErr;//Счетчик от зависания функции.
while(( !I2C_CheckEvent( I2C_EVENT_MASTER_MODE_SELECT))&& (time_out--));//Ждём установки бита MASTER.
I2C_Send7bitAddress(( uint8_t) Addr, I2C_DIRECTION_RX);//Отсылаем адрес вызываемого устройства.
time_out= TimErr;//Счетчик от зависания функции.
while(( !I2C_GetFlagStatus( I2C_FLAG_ADDRESSSENTMATCHED))&& (time_out--));//Ждём когда нужное устройство подтвердит.
/*Функционал для приёма трёх байтов.*/
//Clear ADDR flag
(void)I2C->SR1; (void)I2C->SR3;//Комбинация для сброса ADDR.
//NumByteToRead> 3
if( countData>3){//Если принимаем больше трёх байтов.
while( countData>3){
time_out= TimErr;//Счетчик от зависания функции.
//Poll BTF
while (( !I2C_GetFlagStatus( I2C_FLAG_TRANSFERFINISHED))&&(time_out--));//Ждём отправку всех данных устройству.
*dataBuffer= I2C_ReceiveData();
*dataBuffer++;
countData--;
}
}
time_out= TimErr;//Счетчик от зависания функции.
//NumByteToRead= 3
//Poll BTF
while (( !I2C_GetFlagStatus( I2C_FLAG_TRANSFERFINISHED))&&( time_out--));//Ждём отправку всех данных устройству.
//Clear ACK
I2C_AcknowledgeConfig( I2C_ACK_NONE);//Отключаем ACK после приёма последнего байта, что бы не отправлялись данные.
//Read 1st byte
*dataBuffer= I2C_ReceiveData();
*dataBuffer++;
//Set STOP
I2C_GenerateSTOP( ENABLE);//Установка STOP бита на линии.
//Read 2nd byte
*dataBuffer= I2C_ReceiveData();
*dataBuffer++;
time_out= TimErr;//Счетчик от зависания функции.
//Poll RXNE
while(( !I2C_GetFlagStatus( I2C_FLAG_RXNOTEMPTY))&& ( time_out--));//Ждём когда придут данные.
//Read 3rd byte
*dataBuffer= I2C_ReceiveData();
time_out= TimErr;//Счетчик от зависания функции.
//Poll STOPF
while(( I2C->CR2 & I2C_CR2_STOP)&& ( time_out--));//Ждём остановки передачи и STOP на линии.
I2C_AcknowledgeConfig( I2C_ACK_CURR);//Устанавливаем контроль бита ACK.
}

Примеры использования функций.

 Практиковать будем с контроллером STM8S903K3 и китайской платой на микросхеме DS1307, схема подключения ниже.

нет рисунка

 Пример программы будет отправлять запросы к DS1307 для получения секунд, минут и часов. Для визуализации используйте UART-USB конвертер, мою программу терминал UART_assistant_AAW_v00(находится в Программы) и раскомментируйте код для работы с UART.

#include "stm8s.h"
void I2C_Send_data( uint8_t Addr, uint16_t countData, uint8_t* dataBuffer);
void I2C_Read_three_more_byte( uint8_t Addr, uint16_t countData, uint8_t* dataBuffer);
#define FreqOut 100000//Частота на которой буду передаваться данные HZ.
#define FreqIn 16//Частота тактирования I2C MHZ.
#define TimErr 1000//Время ожидания ошибки или выход из функции принудительно, спасает от зависания.
uint8_t TxBuffer[4];//Отправной массив.
uint8_t RxBuffer[4];//Приёмный массив.

int main( void ){
CLK_HSIPrescalerConfig( CLK_PRESCALER_HSIDIV1);
//UART1_Init( 9600, UART1_WORDLENGTH_8D, UART1_STOPBITS_1, UART1_PARITY_NO, UART1_SYNCMODE_CLOCK_DISABLE, UART1_MODE_TXRX_ENABLE);
//Настройка I2C.
I2C_Init( FreqOut, 10, I2C_DUTYCYCLE_2, I2C_ACK_CURR, I2C_ADDMODE_7BIT, FreqIn);
TxBuffer[0]=0;
TxBuffer[1]=0;
TxBuffer[2]=0;
TxBuffer[3]=0;
I2C_Send_data( 208, 4, TxBuffer);
while(1){
I2C_Send_data( 208, 1, TxBuffer);
I2C_Read_three_more_byte( 208, 3, RxBuffer);
uint8_t seconds= RxBuffer[0];//Секунды.
uint8_t minutes= RxBuffer[1];//Минуты.
uint8_t hour= RxBuffer[2];//Часы.
/* UART1_SendData8( seconds);
while(!UART1_GetFlagStatus( UART1_FLAG_TXE));
UART1_SendData8( minutes);
while(!UART1_GetFlagStatus( UART1_FLAG_TXE));
UART1_SendData8( hour);
while(!UART1_GetFlagStatus( UART1_FLAG_TXE));
for( uint32_t a=0; a<300000; a++);*/
}
return 0;
}

/*Функция для отправка данных в DS1307.*/
void I2C_Send_data( uint8_t Addr, uint16_t countData, uint8_t* dataBuffer){
uint32_t time_out= TimErr;//Счетчик от зависания функции.
while((I2C_GetFlagStatus( I2C_FLAG_BUSBUSY))&& (time_out--));//Проверяем занятость линии I2C.
I2C_GenerateSTART(ENABLE);//Запуск I2C для передачи данных.
time_out= TimErr;//Счетчик от зависания функции.
while(( !I2C_CheckEvent( I2C_EVENT_MASTER_MODE_SELECT))&& ( time_out--));//Ждём установки бита MASTER.
I2C_Send7bitAddress(( uint8_t)Addr, I2C_DIRECTION_TX);//Отсылаем адрес вызываемого устройства.
time_out= TimErr;//Счетчик от зависания функции.
while(( !I2C_GetFlagStatus( I2C_FLAG_ADDRESSSENTMATCHED))&& (time_out--));//Ждём когда нужное устройство подтвердит.
(void)I2C->SR1; (void)I2C->SR3;//Комбинация для сброса ADDR.
while( countData){//Повторяем пока не отправятся все данные.
time_out= TimErr;//Счетчик от зависания функции.
while( !(I2C->SR1 & 128)&& ( time_out--));//Ждём когда буфер передающего регистра будет пуст.
I2C_SendData( *dataBuffer);
*dataBuffer++;
countData--;
}
time_out= TimErr;//Счетчик от зависания функции.
while(( !I2C_CheckEvent( I2C_EVENT_MASTER_BYTE_TRANSMITTED))&& ( time_out--));//Ждём окончания отправки данных.
I2C_GenerateSTOP( ENABLE);//Установка STOP бита на линии.
time_out= TimErr;//Счетчик от зависания функции.
while(( I2C->CR2 & I2C_CR2_STOP)&& ( time_out--));//Ждём остановки передачи и STOP на линии.
}

/*Функция читает из DS1307 три байта данных.*/
void I2C_Read_three_more_byte( uint8_t Addr, uint16_t countData, uint8_t* dataBuffer){
uint32_t time_out= TimErr;//Счетчик от зависания функции.
while(( I2C_GetFlagStatus( I2C_FLAG_BUSBUSY))&& (time_out--));//Проверяем занятость линии I2C.
I2C_GenerateSTART( ENABLE);//Запуск I2C для передачи данных.
time_out= TimErr;//Счетчик от зависания функции.
while(( !I2C_CheckEvent( I2C_EVENT_MASTER_MODE_SELECT))&& (time_out--));//Ждём установки бита MASTER.
I2C_Send7bitAddress(( uint8_t) Addr, I2C_DIRECTION_RX);//Отсылаем адрес вызываемого устройства.
time_out= TimErr;//Счетчик от зависания функции.
while(( !I2C_GetFlagStatus( I2C_FLAG_ADDRESSSENTMATCHED))&& (time_out--));//Ждём когда нужное устройство подтвердит.
/*Функционал для приёма трёх байтов.*/
(void)I2C->SR1; (void)I2C->SR3;//Комбинация для сброса ADDR.
if( countData>3){//Если принимаем больше трёх байтов.
while( countData>3){
time_out= TimErr;//Счетчик от зависания функции.
while (( !I2C_GetFlagStatus( I2C_FLAG_TRANSFERFINISHED))&&(time_out--));//Ждём отправку всех данных устройству.
*dataBuffer= I2C_ReceiveData();
*dataBuffer++;
countData--;
}
}
time_out= TimErr;//Счетчик от зависания функции.
while (( !I2C_GetFlagStatus( I2C_FLAG_TRANSFERFINISHED))&&( time_out--));//Ждём отправку всех данных устройству.
I2C_AcknowledgeConfig( I2C_ACK_NONE);//Отключаем ACK после приёма последнего байта, что бы не отправлялись данные.
*dataBuffer= I2C_ReceiveData();
*dataBuffer++;
I2C_GenerateSTOP( ENABLE);//Установка STOP бита на линии.
*dataBuffer= I2C_ReceiveData();
*dataBuffer++;
time_out= TimErr;//Счетчик от зависания функции.
while(( !I2C_GetFlagStatus( I2C_FLAG_RXNOTEMPTY))&& ( time_out--));//Ждём когда придут данные.
*dataBuffer= I2C_ReceiveData();
time_out= TimErr;//Счетчик от зависания функции.
while(( I2C->CR2 & I2C_CR2_STOP)&& ( time_out--));//Ждём остановки передачи и STOP на линии.
I2C_AcknowledgeConfig( I2C_ACK_CURR);//Устанавливаем контроль бита ACK.
}

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{
while (1){}
}
#endif

Комментариев нет  Только зарегистрированные пользователи могут оставлять комментарии!