20. STM32. Программирование STM32F103. I2C Slave


26.10.2016

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

Весь функционал, касающийся I2C Slave, находится в файлах 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.

Желаю успехов!

Смотри также:

Коментарі:
Zolhod говорить:
08.05.2017 08:41
Здравствуйте, можно ли переписать этот пример, используя HAL?
andre говорить:
08.05.2017 18:49
Конечно можно. Примеры для HAL будут позже.
Павло говорить:
11.08.2017 15:14
Добрый день, действительно, очень хотелось бы увидеть данный проект на HALe. Особенно интересует логика работы обработчика прерывания события по i2c. Заранее благодарен.

Додати коментар

* - обов'язкові поля

Архіви

Підписка