From 7a540fb4ab547fcb6bc6b56057ab8968fab12390 Mon Sep 17 00:00:00 2001 From: Karl Palsson Date: Wed, 22 Mar 2017 22:50:37 +0000 Subject: f0 working with repeated start properly. need to squish lots of this like, you left the f0 makefiles out earlier, and it's all boring history anwyay --- tests/i2c-master/Makefile.stm32f072-disco | 23 +++ tests/i2c-master/i2c-master.c | 236 ++++++++++++++++++++++++++---- tests/i2c-master/main-stm32f072-disco.c | 101 +++++++++++++ 3 files changed, 334 insertions(+), 26 deletions(-) create mode 100644 tests/i2c-master/Makefile.stm32f072-disco create mode 100644 tests/i2c-master/main-stm32f072-disco.c (limited to 'tests') diff --git a/tests/i2c-master/Makefile.stm32f072-disco b/tests/i2c-master/Makefile.stm32f072-disco new file mode 100644 index 0000000..02e987c --- /dev/null +++ b/tests/i2c-master/Makefile.stm32f072-disco @@ -0,0 +1,23 @@ +BOARD = stm32f072-disco +PROJECT = i2c-master-$(BOARD) +BUILD_DIR = bin-$(BOARD) + +SHARED_DIR = ../../shared + +CFILES = main-$(BOARD).c +CFILES += i2c-master.c +# No trace on cm0! +#CFILES += trace.c trace_stdio.c +CFILES += usart_stdio.c + +VPATH += $(SHARED_DIR) + +INCLUDES += $(patsubst %,-I%, . $(SHARED_DIR)) + +OPENCM3_DIR=../../libopencm3 +DEVICE=stm32f072rb +#OOCD_INTERFACE = stlink-v2 +#OOCD_TARGET = stm32f0x +OOCD_FILE = ../../openocd/openocd.stm32f072-disco.cfg + +include ../../rules.mk diff --git a/tests/i2c-master/i2c-master.c b/tests/i2c-master/i2c-master.c index 92b0b77..b140b74 100644 --- a/tests/i2c-master/i2c-master.c +++ b/tests/i2c-master/i2c-master.c @@ -25,35 +25,210 @@ enum sht21_cmd_e { /* 0xfa, 0x0f to read serial */ }; +// ------------------ section proposed to go up to libopencm3 + +enum i2c_speeds { + i2c_speed_sm_100k, + i2c_speed_fm_400k, + i2c_speed_fmp_1m, + i2c_speed_unknown +}; + +/* to go to i2c-v1 impl file, with common name.... */ +static void i2c_set_speed_v1(uint32_t p, enum i2c_speeds speed, uint32_t clock_megahz) +{ +#if defined(I2C_SR2) + switch(speed) { + case i2c_speed_fm_400k: + // FIXME + printf("oops, haven't gotten 400k yet!, grab chucks code from pr470!"); + break; + default: + /* fall back to standard mode */ + case i2c_speed_sm_100k: + i2c_set_clock_frequency(p, clock_megahz); + i2c_set_standard_mode(p); + /* x Mhz / (100kHz * 2) */ + i2c_set_ccr(p, clock_megahz * 5); + /* Sm mode, (100kHz) freqMhz + 1 */ + i2c_set_trise(p, clock_megahz + 1); + break; + } +#else + (void)p; + (void)speed; + (void)clock_megahz; +#endif +} + +/* to go to i2c-v2 impl file, with common name.... */ +static void i2c_set_speed_v2(uint32_t p, enum i2c_speeds speed, uint32_t clock_megahz) +{ +#if !defined(I2C_SR2) + int prescaler; + switch(speed) { + case i2c_speed_fmp_1m: + case i2c_speed_fm_400k: + // FIXME + printf("oops, haven't gotten to those speeds yet!"); + break; + default: + /* fall back to standard mode */ + case i2c_speed_sm_100k: + /* target 2Mhz input, so tpresc = 500ns */ + prescaler = clock_megahz / 2 - 1; + i2c_set_prescaler(p, prescaler); + i2c_set_scl_low_period(p, 9); // 5usecs + i2c_set_scl_high_period(p, 7); // 4usecs + i2c_set_data_hold_time(p, 1); // 0.5usecs + i2c_set_data_setup_time(p, 2); // 1.25usecs + break; + } +#endif +} + + /* requires clock to be a multiple of 2 ! */ -// TODO - plausible to pull up to library, with extraction of 100k param too? -static void i2c_set_speed_100k(uint32_t p, uint32_t clock_megahz) +/** + * Set the i2c line speed as optimally as possible + * @param p + * @param speed + * @param clock_megahz _normally_ provide: rcc_apb1_frequency / 1000000 here + * TODO: clock must be a multiple of 2meg for -v2 at least, improve docs here! + */ +static void i2c_set_speed(uint32_t p, enum i2c_speeds speed, uint32_t clock_megahz) { #if defined I2C_SR2 - /* target 2Mhz input, so tpresc = 500ns */ - int prescaler = clock_megahz / 2 - 1; - i2c_set_prescaler(p, prescaler); - i2c_set_scl_low_period(p, 9); // 5usecs - i2c_set_scl_high_period(p, 7); // 4usecs - i2c_set_data_hold_time(p, 1); // 0.5usecs - i2c_set_data_setup_time(p, 2); // 1.25usecs + i2c_set_speed_v1(p, speed, clock_megahz); #else - i2c_set_clock_frequency(p, clock_megahz); - i2c_set_standard_mode(p); - /* x Mhz / (100kHz * 2) */ - i2c_set_ccr(p, clock_megahz * 5); - /* Sm mode, (100kHz) freqMhz + 1 */ - i2c_set_trise(p, clock_megahz + 1); + i2c_set_speed_v2(p, speed, clock_megahz); #endif } +// read/write are almost identical! +//static void i2c__transfer_helper(uint32_p, uint8_t addr, uint8_t *buf) +static +void i2c_transfer7(uint32_t i2c, uint8_t addr, uint8_t *w, size_t wn, uint8_t *r, size_t rn) +{ + int wait; + size_t i; + /* waiting for busy is unnecessary. read the RM */ + if (wn) { + i2c_set_7bit_address(i2c, addr); + i2c_set_write_transfer_dir(i2c); + i2c_set_bytes_to_transfer(i2c, wn); + i2c_disable_autoend(i2c); + i2c_send_start(i2c); + + while (wn--) { + wait = true; + while (wait) { + if (i2c_transmit_int_status(i2c)) { + wait = false; + } + while (i2c_nack(i2c)); /* FIXME Some error */ + } + i2c_send_data(i2c, *w++); + } + /* not entirely sure this is really necessary. + * RM implies it will stall until it can write out the later bits + */ + while (!i2c_transfer_complete(i2c)); + } + + if (rn) { + /*Setting transfer properties*/ + i2c_set_7bit_address(i2c, addr); + i2c_set_read_transfer_dir(i2c); + i2c_set_bytes_to_transfer(i2c, rn); + /*start transfer*/ + i2c_send_start(i2c); + /* important to do it afterwards to do a proper repeated start! */ + i2c_enable_autoend(i2c); + + for (i = 0; i < rn; i++) { + while (i2c_received_data(i2c) == 0); + r[i] = i2c_get_data(i2c); + } + } + +} + +/** + * Write bytes to a 7bit address + * @param p i2c peripheral of interest + * @param addr 7bit i2c slave address (unshifted) + * @param buf data to send + * @param count how many bytes to send + * @param end true if you wish to send a stop bit. false if you intend to + * continue sending data with a repeated start + */ +static +void i2c_write_bytes7(uint32_t p, uint8_t addr, uint8_t *buf, size_t count, bool end) +{ + bool wait; + size_t i; + /* start transfer */ + i2c_send_start(p); + while (i2c_busy(p) == 1); +// while (i2c_is_start(p) == 1); + /* Setting transfer properties */ + i2c_set_bytes_to_transfer(p, count); + i2c_set_7bit_address(p, addr); + i2c_set_write_transfer_dir(p); + if (end) { + i2c_enable_autoend(p); + } else { + i2c_disable_autoend(p); + } + + for (i = 0; i < count; i++) { + wait = true; + while (wait) { + if (i2c_transmit_int_status(p)) { + wait = false; + } + while (i2c_nack(p)); + } + i2c_send_data(p, buf[i]); + } +} + +static +void i2c_read_bytes7(uint32_t p, uint8_t addr, uint8_t *buf, size_t count, bool end) +{ + bool wait; + size_t i; + //while (i2c_busy(p) == 1); // FIXME - repeated start vs not + //while (i2c_is_start(p) == 1); + /* Setting transfer properties */ + i2c_set_bytes_to_transfer(p, 1); + i2c_set_7bit_address(p, addr); + i2c_set_read_transfer_dir(p); + if (end) { + i2c_enable_autoend(p); + } else { + i2c_disable_autoend(p); + } + /* start transfer */ + i2c_send_start(p); + + for (i = 0; i < count; i++) { + while (i2c_received_data(p) == 0); + buf[i] = i2c_get_data(p); + } +} + + +// --------------- end of upstream planned section + void i2cm_init(void) { rcc_periph_clock_enable(hw_details.periph_rcc); rcc_periph_reset_pulse(hw_details.periph_rst); // i2c_enable_ack(hw_details.periph); /* NO ACK FOR SHT21! */ - i2c_set_speed_100k(hw_details.periph, hw_details.i2c_clock_megahz); + i2c_set_speed(hw_details.periph, i2c_speed_sm_100k, hw_details.i2c_clock_megahz); i2c_peripheral_enable(hw_details.periph); } @@ -86,16 +261,17 @@ static void sht21_send_data(uint32_t i2c, size_t n, uint8_t *data) } } +// FIXME - this is dumb, just need send_data with len==1! static void sht21_send_cmd(uint32_t i2c, uint8_t cmd) { while ((I2C_SR2(i2c) & I2C_SR2_BUSY)) { - } + } // wait for not busy! i2c_send_start(i2c); /* Wait for master mode selected */ - while (!((I2C_SR1(i2c) & I2C_SR1_SB) - & (I2C_SR2(i2c) & (I2C_SR2_MSL | I2C_SR2_BUSY)))); + while (!((I2C_SR1(i2c) & I2C_SR1_SB) // waiting for start bit to hav ebeen sent + & (I2C_SR2(i2c) & (I2C_SR2_MSL | I2C_SR2_BUSY)))); // and waiting for no longer busy again i2c_send_7bit_address(i2c, SENSOR_ADDRESS, I2C_WRITE); @@ -197,7 +373,12 @@ static void sht21_readid(void) sht21_send_cmd(I2C1, SHT21_CMD_READ_REG); sht21_readn(I2C1, 1, &raw); #else - read_i2c(hw_details.periph, SENSOR_ADDRESS, SHT21_CMD_READ_REG, 1, &raw); + //read_i2c(hw_details.periph, SENSOR_ADDRESS, SHT21_CMD_READ_REG, 1, &raw); + uint8_t cmd = SHT21_CMD_READ_REG; +// i2c_write_bytes7(hw_details.periph, SENSOR_ADDRESS, &cmd, 1, false); +// i2c_read_bytes7(hw_details.periph, SENSOR_ADDRESS, &raw, 1, true); + i2c_transfer7(hw_details.periph, SENSOR_ADDRESS, &cmd, 1, &raw, 1); + //read_i2c(hw_details.periph, SENSOR_ADDRESS, SHT21_CMD_READ_REG, 1, &raw); #endif printf("raw user reg = %#x\n", raw); int resolution = ((raw & 0x80) >> 6) | (raw & 1); @@ -205,18 +386,21 @@ static void sht21_readid(void) printf("battery status: %s\n", (raw & (1 << 6) ? "failing" : "good")); printf("On chip heater: %s\n", (raw & 0x2) ? "on" : "off"); -#if 0 uint8_t req1[] = {0xfa, 0x0f}; uint8_t res[8]; - sht21_send_data(I2C1, 2, req1); - sht21_readn(I2C1, sizeof(res), res); +// sht21_send_data(I2C1, 2, req1); +// sht21_readn(I2C1, sizeof(res), res); + i2c_write_bytes7(hw_details.periph, SENSOR_ADDRESS, req1, 2, false); + i2c_read_bytes7(hw_details.periph, SENSOR_ADDRESS, res, 8, true); uint8_t req2[] = {0xfc, 0xc9}; uint8_t res2[8]; - sht21_send_data(I2C1, 2, req2); - sht21_readn(I2C1, sizeof(res), res2); +// sht21_send_data(I2C1, 2, req2); +// sht21_readn(I2C1, sizeof(res), res2); + i2c_write_bytes7(hw_details.periph, SENSOR_ADDRESS, req2, 2, false); + i2c_read_bytes7(hw_details.periph, SENSOR_ADDRESS, res2, 8, true); + printf("Serial = %02x%02x %02x%02x %02x%02x %02x%02x\n", res2[3], res2[4], res[0], res[2], res[4], res[6], res2[0], res2[1]); -#endif } void i2cm_task(void) diff --git a/tests/i2c-master/main-stm32f072-disco.c b/tests/i2c-master/main-stm32f072-disco.c new file mode 100644 index 0000000..8818665 --- /dev/null +++ b/tests/i2c-master/main-stm32f072-disco.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2017 Karl Palsson + */ + +#include +#include +#include +#include +#include +#include + +#include +#include "hw.h" +#include "i2c-master.h" + +#define LED_DISCO_GREEN_PORT GPIOC +#define LED_DISCO_GREEN_PIN GPIO9 + + + +struct hw_detail hw_details = { + .periph = I2C1, + .periph_rcc = RCC_I2C1, + .periph_rst = RST_I2C1, + .pins = GPIO8 | GPIO9, /* our external i2c device on I2c1 */ + .port = GPIOB, + .port_rcc = RCC_GPIOB, + .trigger_rcc = RCC_GPIOB, + .trigger_port = GPIOB, + .trigger_pin = GPIO12, + .i2c_clock_megahz = 48, // FIXME +}; + + +static +void setup_usart(void) +{ + uint32_t dev = USART1; + rcc_periph_clock_enable(RCC_USART1); + rcc_periph_clock_enable(RCC_GPIOA); + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO9); + gpio_set_af(GPIOA, GPIO_AF1, GPIO9); + + usart_set_baudrate(dev, 115200); + usart_set_databits(dev, 8); + usart_set_parity(dev, USART_PARITY_NONE); + usart_set_stopbits(dev, USART_CR2_STOP_1_0BIT); + usart_set_mode(dev, USART_MODE_TX); + usart_set_flow_control(dev, USART_FLOWCONTROL_NONE); + + /* Finally enable the USART. */ + usart_enable(dev); +} + +/** + * Setup any gpios or anything hardware specific. + * Should _only_ be things that can't be done in shared i2cm_init! + */ +static void i2cm_hw_init(void) +{ + /* trigger pin gpio */ + rcc_periph_clock_enable(hw_details.trigger_rcc); + gpio_mode_setup(hw_details.trigger_port, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, hw_details.trigger_pin); + + /* i2c control lines */ + rcc_periph_clock_enable(hw_details.port_rcc); + gpio_mode_setup(hw_details.port, GPIO_MODE_AF, GPIO_PUPD_NONE, hw_details.pins); + gpio_set_output_options(hw_details.port, GPIO_OTYPE_OD, GPIO_OSPEED_HIGH, hw_details.pins); + gpio_set_af(hw_details.port, GPIO_AF1, hw_details.pins); +} + + +static void setup(void) +{ + setup_usart(); + printf("hi guys!\n"); + i2cm_hw_init(); + i2cm_init(); +} + +int main(void) +{ + int i; + rcc_clock_setup_in_hsi48_out_48mhz(); + /* green led for ticking */ + rcc_periph_clock_enable(RCC_GPIOC); + gpio_mode_setup(LED_DISCO_GREEN_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_DISCO_GREEN_PORT); + gpio_set(LED_DISCO_GREEN_PORT, LED_DISCO_GREEN_PORT); + + setup(); + + while (1) { + i2cm_task(); + gpio_toggle(LED_DISCO_GREEN_PORT, LED_DISCO_GREEN_PIN); + for (i = 0; i < 0x800000; i++) { /* Wait a bit. */ + __asm__("NOP"); + } + } + +} + -- cgit