Это старая версия документа!
На главную, Исходники, Примеры кода с использованием STM8L
Работа с шиной I2C на STM8L
В этой заметке будет рассматриваться работа контроллера STM8L15x с шиной I2C в режиме мастер. Для этих микроконтроллеров существует библиотека STM8L15x/16x/05x/AL3Lx/AL31x standard peripheral library. Однако, в силу своей универсальности, код работы с шиной I2C в этой библиотеке занимает относительно много места и имеет низкую производительность.
Поэтому, на основе AN3281 Application note. STM8 8-bit MCUs I2C optimized examples был написан код, фрагменты которого приведены ниже. Код рассчитан на работу в режиме Master с одним Slave устройством. При несложной доработке, он подойдет и для работы с несколькими slave'ами.
Для начала, макросы зависящие от конкретного контроллера и/или включения на плате:
// Выводы используемые под I2C #define TS_I2C_GPIO_PORT GPIOC #define TS_I2C_SDA_PIN GPIO_Pin_0 #define TS_I2C_SCL_PIN GPIO_Pin_1 // Идентификатор для используемого интерфейса I2C // Чаще всего, у контроллеров STM8L один интерфейс I2C, но я обычно в коде не использую прямое имя интерфейса. #define TS_I2C I2C1 // Peripheral Clock Enable for I2C1 #define TS_I2C_CLK CLK_Peripheral_I2C1 // Скорость в Гц #define TS_I2C_SPEED 50000 // Slave адрес устройства с которым будем работать. #define TS_I2C_SLAVE_ADDR (0xDA) #define ON_I2C_GPIO_PORT GPIOE // Этот вывод управляет ключом для питания I2C периферии. Актуально для батарейного питания. #define ON_I2C_GPIO_PIN GPIO_Pin_2 #define I2C_PowerOn() GPIO_ResetBits(ON_I2C_GPIO_PORT, ON_I2C_GPIO_PIN); #define I2C_PowerOff() GPIO_SetBits(ON_I2C_GPIO_PORT, ON_I2C_GPIO_PIN) //! Get I2C Power State: true - Power on, false - Power off #define I2C_PowerState() ((GPIO_ReadInputData(ON_I2C_GPIO_PORT) & ON_I2C_GPIO_PIN) ? FALSE : TRUE )
Определим ряд макросов для повышения читабельности кода:
#include "stm8l15x.h"
/* Disable the STOP condition generation */
#define TS_I2C_STOP_Disable() TS_I2C->CR2 &= (uint8_t)(~I2C_CR2_STOP)
/* Generate a STOP condition */
#define TS_I2C_STOP_Enable() TS_I2C->CR2 |= I2C_CR2_STOP
/* Generate a START condition */
#define TS_I2C_START_Enable() TS_I2C->CR2 |= I2C_CR2_START;
/* Disable the START condition generation */
#define TS_I2C_START_Disable() TS_I2C->CR2 &= (uint8_t)(~I2C_CR2_START);
/* Enable I2C peripheral */
#define TS_I2C_Enable() TS_I2C->CR1 |= I2C_CR1_PE
/* Disable I2C peripheral */
#define TS_I2C_Disable() TS_I2C->CR1 &= (uint8_t)(~I2C_CR1_PE)
#define REPEATE_CNT 10
// Число повторных попыток чтения датчика
static uint8_t Rep = REPEATE_CNT;
Функции инициаизации и сброса интерфейса:
void TS_I2CInfReset(void)
{
//I2C_SoftwareResetCmd(TS_I2C, ENABLE);
/* Peripheral under reset */
TS_I2C->CR2 |= I2C_CR2_SWRST;
//I2C_SoftwareResetCmd(TS_I2C, DISABLE);
/* Peripheral not under reset */
TS_I2C->CR2 &= (uint8_t)(~I2C_CR2_SWRST);
}
void TS_I2C_Init(uint32_t OutputClockFrequency )
{
uint32_t result = 0x0004;
uint8_t input_clock = 0;
/* Get system clock frequency */
input_clock = (uint8_t) (CLK_GetClockFreq() / 1000000);
/*------------------------- I2C FREQ Configuration ------------------------*/
/* Clear frequency bits */
TS_I2C->FREQR &= (uint8_t)(~I2C_FREQR_FREQ);
/* Write new value */
TS_I2C->FREQR |= input_clock;
/*--------------------------- I2C CCR Configuration ------------------------*/
/* Disable I2C to configure TRISER */
TS_I2C->CR1 &= (uint8_t)(~I2C_CR1_PE);
/* Clear CCRH & CCRL */
TS_I2C->CCRH &= (uint8_t)(~(I2C_CCRH_FS | I2C_CCRH_DUTY | I2C_CCRH_CCR));
TS_I2C->CCRL &= (uint8_t)(~I2C_CCRL_CCR);
/* Calculate standard mode speed */
result = (uint16_t)((input_clock * 1000000) / (OutputClockFrequency << (uint8_t)1));
/* Verify and correct CCR value if below minimum value */
if (result < (uint16_t)0x0004)
{
/* Set the minimum allowed value */
result = (uint16_t)0x0004;
}
/* Set Maximum Rise Time: 1000ns max in Standard Mode
= [1000ns/(1/input_clock.10e6)]+1
= input_clock+1 */
TS_I2C->TRISER = (uint8_t)((uint8_t)input_clock + (uint8_t)1);
/* Write CCR with new calculated value */
TS_I2C->CCRL = (uint8_t)result;
TS_I2C->CCRH = (uint8_t)((uint8_t)((uint8_t)((uint8_t)result >> 8) & I2C_CCRH_CCR));
/* Enable I2C and Configure its mode*/
TS_I2C->CR1 |= (uint8_t)(I2C_CR1_PE );
/* Configure I2C acknowledgement */
TS_I2C->CR2 |= (uint8_t)I2C_CR2_ACK;
/*--------------------------- I2C OAR Configuration ------------------------*/
TS_I2C->OARL = (uint8_t)(0);
TS_I2C->OARH = (uint8_t)((uint8_t)(0 | I2C_OARH_ADDCONF ) | \
(uint8_t)((uint16_t)( (uint16_t)0 & (uint16_t)0x0300) >> 7));
}
/**
* @brief Configures I2C peripheral for Termo Sensor .
* @param None
* @retval None
*/
void TS_I2C_Config(void)
{
CLK_PeripheralClockConfig(TS_I2C_CLK, ENABLE);
TS_I2C_Disable();
if (Rep != REPEATE_CNT) {
TS_I2CInfReset();
}
TS_I2C_Init( TS_I2C_SPEED );
TS_I2C_Enable();
}
void TS_I2C_DeInit(I2C_TypeDef* I2Cx)
{
TS_I2C->CR1 = I2C_CR1_RESET_VALUE;
TS_I2C->CR2 = I2C_CR2_RESET_VALUE;
TS_I2C->FREQR = I2C_FREQR_RESET_VALUE;
TS_I2C->OARL = I2C_OARL_RESET_VALUE;
TS_I2C->OARH = I2C_OARH_RESET_VALUE;
TS_I2C->OAR2 = I2C_OAR2_RESET_VALUE;
TS_I2C->ITR = I2C_ITR_RESET_VALUE;
TS_I2C->CCRL = I2C_CCRL_RESET_VALUE;
TS_I2C->CCRH = I2C_CCRH_RESET_VALUE;
TS_I2C->TRISER = I2C_TRISER_RESET_VALUE;
}
Для экономии энергии (при батарейном питании) может быть актуально отключать I2C когда в ней нет необходимости:
/**
Отключение I2C перед переходом в режим микропотребления
*/
void TS_I2C_Stop(void)
{
TS_I2C_DeInit(TS_I2C);
CLK_PeripheralClockConfig(TS_I2C_CLK, DISABLE);
I2C_PowerOff();
// GPIO_Init(TS_I2C_GPIO_PORT, TS_I2C_SCL_PIN | TS_I2C_SDA_PIN, GPIO_Mode_Out_OD_Low_Slow);
}
/**
Включение I2C при выходе из режима микропотребления
*/
void TS_I2C_Start(void)
{
GPIO_Init(TS_I2C_GPIO_PORT, TS_I2C_SCL_PIN | TS_I2C_SDA_PIN, GPIO_Mode_In_FL_No_IT);
I2C_PowerOn();
TS_I2C_Config();
}
Чтение и запись в периферийное I2C устройство. Тут надо оговорить один момент. Функции чтения и записи, в качестве последнего аргумента, принимают указатель на переменную типа uint16_t. Если значение этой переменной достигнет 0, то выполнение операции будет прервано по таймауту.
В идеале, создается volatile переменная, например Delay1ms. Инициализируется таймер на прерывания, например, 1мс. В обработчике прерывания переменная Delay1ms уменьшается, пока не достигнет 0. Указатель на переменную Delay1ms и передается в функции I2C.
#define Wait_While_Flag_Set(SReg,Flag,break_cond) while ( (TS_I2C->SReg & (Flag)) ) \
if (break_cond) { \
return FALSE; \
}
#define Wait_Flag(SReg,Flag,break_cond) while ( !(TS_I2C->SReg & (Flag)) ) \
if (break_cond) { \
TS_I2C_STOP_Enable(); \
return FALSE; \
}
/* Write in the DR register the data to be sent */
#define TS_I2C_SendData(Data) TS_I2C->DR = Data
#define TS_I2C_ReceiveData() ((uint8_t)TS_I2C->DR)
/**
* Запись в периферийное I2C устройство
*
* @param Offset - адрес внутреннего регистра
* @param dat - указатель на массив данных
* @param len - длина массива данных для записи
* @param timeout - таймаут операции в мс.
*
* @return bool TRUE - успешно, FALSE - ошибка или таймаут
*/
bool TS_I2C_Write(uint8_t Offset, uint8_t *dat, uint8_t len, uint16_t volatile *timeout)
{
uint8_t k;
/*!< While the bus is busy */
Wait_While_Flag_Set(SR3,I2C_SR3_BUSY,(*timeout == 0));
/*
while ( (TS_I2C->SR3 & I2C_FLAG_BUSY) ) ;
if (*timeout == 0) {
return FALSE;
}
*/
//I2C_GenerateSTOP(TS_I2C, DISABLE);
TS_I2C_STOP_Disable();
// Clear Acknowledge failure flag
TS_I2C->SR2 &= (uint8_t)(~I2C_SR2_AF);
/*!< Send START condition */
TS_I2C_START_Enable();
/* Poll SB bit */
Wait_Flag(SR1,I2C_SR1_SB,*timeout == 0);
/* Send TS_I2C slave address for write */
//I2C_Send7bitAddress(TS_I2C, TS_I2C_SLAVE_ADDR, I2C_Direction_Transmitter);
/* Send the address with direction bit for write */
TS_I2C->DR = TS_I2C_SLAVE_ADDR & 0xFE;
Wait_Flag(SR1,I2C_SR1_ADDR,(*timeout == 0) || (TS_I2C->SR2 & I2C_SR2_AF));
// Clear ADDR flag
(void)TS_I2C->SR3; // This bit is cleared by software reading SR1 register followed reading SR3, or by hardware when PE=0.
Wait_Flag(SR1,I2C_SR1_TXE,*timeout == 0);
TS_I2C_SendData( Offset);
k=0;
while (1) {
if (TS_I2C->SR2 & I2C_SR2_AF) {
TS_I2C->SR2 &= (uint8_t)(~I2C_SR2_AF);
/* Send I2C STOP Condition */
TS_I2C_STOP_Enable();
return FALSE;
}
if (len == 0) {
Wait_Flag(SR1,I2C_SR1_TXE | I2C_SR1_BTF,*timeout == 0);
/* Send I2C STOP Condition */
TS_I2C_STOP_Enable();
break;
} else {
Wait_Flag(SR1,I2C_SR1_TXE,*timeout == 0);
TS_I2C_SendData( dat[k++] );
len--;
}
}
return TRUE;
}
/**
* Чтение периферийного I2C устройства
*
* @param Offset - адрес внутреннего регистра
* @param dat - указатель на массив данных
* @param len - длина массива данных для записи
* @param timeout - таймаут операции в мс.
*
* @return bool
*/
bool TS_I2C_Read(uint8_t Offset, uint8_t *dat, uint8_t len, uint16_t volatile *timeout)
{
uint8_t k;
if (TS_I2C_Write(Offset,0,0,timeout) ) {
/**************************************************************************/
/*!< While the bus is busy */
Wait_While_Flag_Set(SR3,I2C_SR3_BUSY,*timeout == 0);
TS_I2C_STOP_Disable();
// Clear Acknowledge failure flag
TS_I2C->SR2 &= (uint8_t)(~I2C_SR2_AF);
/***** Wait DataReady *******/
/*!< Send START condition */
TS_I2C_START_Enable();
/* Poll SB bit */
Wait_Flag(SR1,I2C_SR1_SB,*timeout == 0);
/* Send TS_I2C slave address for write */
//I2C_Send7bitAddress(TS_I2C, TS_I2C_SLAVE_ADDR, I2C_Direction_Transmitter);
/* Send the address with direction bit for read */
TS_I2C->DR = TS_I2C_SLAVE_ADDR | 0x01;
Wait_Flag(SR1,I2C_SR1_ADDR,(*timeout == 0) || (TS_I2C->SR2 & I2C_SR2_AF));
// Clear ADDR flag
(void)TS_I2C->SR3;
// Set ACK
TS_I2C->CR2 |= (uint8_t)(I2C_CR2_ACK);
k=0;
if (len>2) {
do {
if (len == 3) {
//Poll BTF
Wait_Flag(SR1,I2C_SR1_BTF,*timeout == 0);
// Clear ACK
TS_I2C->CR2 &= (uint8_t)(~I2C_CR2_ACK);
disableInterrupts();
dat[k++] = TS_I2C_ReceiveData();
len--;
TS_I2C_STOP_Enable();
dat[k++] = TS_I2C_ReceiveData();
len--;
enableInterrupts();
//Poll RXNE
Wait_Flag(SR1,I2C_SR1_RXNE,*timeout == 0);
dat[k++] = TS_I2C_ReceiveData();
len--;
} else {
Wait_Flag(SR1,I2C_SR1_BTF,*timeout == 0);
/*!< Read a byte from the Sensor */
dat[k++] = TS_I2C_ReceiveData();
len--;
}
} while (len);
} else {
if (len == 2) {
// Set POS
TS_I2C->CR2 |= I2C_CR2_POS;
disableInterrupts();
// Clear ACK
TS_I2C->CR2 &= (uint8_t)(~I2C_CR2_ACK);
enableInterrupts();
//Poll BTF
Wait_Flag(SR1,I2C_SR1_BTF,*timeout == 0);
disableInterrupts();
TS_I2C_STOP_Enable();
dat[k++] = TS_I2C_ReceiveData();
enableInterrupts();
dat[k++] = TS_I2C_ReceiveData();
} else {
// Clear ACK
TS_I2C->CR2 &= (uint8_t)(~I2C_CR2_ACK);
disableInterrupts();
// Clear ADDR flag
(void)TS_I2C->SR3;
TS_I2C_STOP_Enable();
enableInterrupts();
Wait_Flag(SR1,I2C_SR1_RXNE,*timeout == 0);
dat[k++] = TS_I2C_ReceiveData();
}
}
return TRUE;
} else {
return FALSE;
}
}
Обсуждение