20. STM32. Програмування STM32F103. I2C Slave


25.10.2016

У попередній статті ми розглянули роботу STM32 з шиною I2C у якості Майстра. Тобто, він був ведучий і опитував сенсор. Тепер зробимо так, щоб STM32 був Slave-ом і відповідав на запити, тобто сам працював як сенсор. Ми виділимо 255 байт пам`яті під регістри з адресами від 0 до 0xFF, і дозволимо Майстру в них писати/читати. А щоб приклад був не таким примітивним, зробимо з нашого STM32, ще і аналого-цифровий перетворювач з інтерфейсом I2C. ADC буде обробляти 8 каналів. Результати перетворень контролер буде віддавати Майстру при читанні з регістрів. Оскільки результат перетворення ADC займає 12 біт, нам потрібно буде 2 регістра (2 байта) на кожний канал ADC.

Весь функціонал який стосується I2C знаходиться у файлах i2c_slave.h, i2c_slave.c. Скачати приклад можна тут: STM32. Скачати приклади

i2c_slave.h містить налаштування:

I2CSLAVE_ADDR - адреса нашого пристрою; ADC_ADDR_START - початкова адреса регістрів, які відповідають за результати перетворень ADC.

У файлі i2c_slave.c нас більш за все цікавлять функції get_i2c1_ram та set_i2c1_ram. Функція get_i2c1_ram відповідає за зчитування даних з регістрів. Вона повертає дані з зазначеної адреси, які віддаються Майстру. У нашому випадку дані зчитуються з масиву i2c1_ram, але, якщо Майстер запитує адреси регістрів з діапазону відведеного для результатів ADC, то відправляються дані перетворень ADC.

get_i2c1_ram:


uint8_t get_i2c1_ram(uint8_t adr) {
	//ADC data
	if ((ADC_ADDR_START <= adr) & (adr < ADC_ADDR_START + ADC_CHANNELS*2)) {
		return ADCBuffer[adr - ADC_ADDR_START];
	}
	else {
		// Other addresses
		return i2c1_ram[adr];
	}
}

Функція set_i2c1_ram - записує дані прийняті від Майстра у регістри з вказаною адресою. В нашому випадку дані просто записуються у масив i2c1_ram. Але це не обов`язково. Ви можете, наприклад, додати перевірку, і, коли на певну адресу приходить певне число, виконати якісь дії. Таким чином, Ви зможете подавати різні команди мікроконтролеру.

set_i2c1_ram:


void set_i2c1_ram(uint8_t adr, uint8_t val) {
	i2c1_ram[adr] = val;
	return;
}

Ініціалізація досить проста:


int main(void)
{
	SetSysClockTo72();

	ADC_DMA_init();
	I2C1_Slave_init();

    while(1)
    {

    }
}

Спочатку ми встановлюємо максимальну частоту роботи контролера. Максимальна швидкість необхідна, коли треба уникнути будь-яких затримок на шині I2C. Потім запускаємо роботу ADC з використанням DMA. Про ADC читайте тут . Про DMA читайте тут. І, насамкінець, виконуємо ініціалізацію шини I2C як Slave. Як бачите, нічого складного.

Тепер підключимо наш модуль STM32 до Raspberry Pi. До каналів ADC підключимо потенціометри. І будемо зчитувати з нашого контролера показники ADC. Не забуваємо, що для роботи шини I2C потрібно на кожну лінію шини встановити підтягуючі резистори.

iic_slave_circuit

У консолі Raspberry перевіримо чи взагалі видно наш пристрій на шині I2C (про те, як використовувати шину I2C на Raspberry читайте тут):


i2cdetect -y 1

stm32_iic_slave_01

Як бачите, адреса пристрою 0x27, хоча ми вказали 0x4E. Як матимете час, подумайте - чому так сталося.

Для зчитування з регістрів I2C-Slave приладу виконуємо команду:


i2cget -y 1 0x27 0x00

Де: 0x27 - адреса пристрою, 0x00 - адреса регістру (0x00...0xFF).

stm32_iic_slave_02

Для запису у регістри I2C-Slave приладу виконуємо команду:


i2cset -y 1 0x27 0xA0 0xDD

Де: 0x27 - адреса пристрою, 0xA0 - адреса регістру 0xDD -8-bit дані (0x00...0xFF)

Попередня команда записала число 0xDD у регістр 0xA0 (писати у перші 16 регістрів можна, та сенсу немає, по вони відведені під ADC). Тепер прочитаємо:


i2cget -y 1 0x27 0xA0

stm32_iic_slave_03

Щоб спростити процес зчитування даних ADC каналів я написав скрипт:


#!/usr/bin/env python
import smbus
import time

bus = smbus.SMBus(1)
address = 0x27

while (1):
	ADC = {};
	for i in range(0, 8):
		LBS = bus.read_byte_data(address, 0x00+i*2)
		MBS = bus.read_byte_data(address, 0x00+i*2+1)
		ADC[i] = MBS*256 + LBS

	print ADC
	time.sleep(0.2)

Він опитує і виводить у консоль результати усіх 8-ми ADC-каналів.

stm32_iic_slave_04

Аналогічним чином можна об`єднати декілька мікроконтролерів. Один з них має бути Master (дивись попередню статтю), інші Slave.

Бажаю успіхів!

Дивись також:

STM32
Коментарі:
Додати коментар
Code
* - обов'язкові поля

Архіви