From d4107fb0d958f870e79421f507908be5f20d13e2 Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Tue, 26 Feb 2013 00:14:44 +0100 Subject: digital/ucoolib/ucoolib/hal/i2c: add host implementation --- digital/ucoolib/ucoolib/hal/i2c/Module | 2 +- digital/ucoolib/ucoolib/hal/i2c/i2c.hh | 4 +- digital/ucoolib/ucoolib/hal/i2c/i2c.host.cc | 213 +++++++++++++++++++++++ digital/ucoolib/ucoolib/hal/i2c/i2c.host.hh | 66 +++++++ digital/ucoolib/ucoolib/hal/i2c/test/Makefile | 2 +- digital/ucoolib/ucoolib/hal/i2c/test/hub.py | 6 + digital/ucoolib/ucoolib/hal/i2c/test/test_i2c.cc | 45 +++-- 7 files changed, 320 insertions(+), 18 deletions(-) create mode 100644 digital/ucoolib/ucoolib/hal/i2c/i2c.host.cc create mode 100644 digital/ucoolib/ucoolib/hal/i2c/i2c.host.hh create mode 100644 digital/ucoolib/ucoolib/hal/i2c/test/hub.py diff --git a/digital/ucoolib/ucoolib/hal/i2c/Module b/digital/ucoolib/ucoolib/hal/i2c/Module index d502c4a0..ae93f228 100644 --- a/digital/ucoolib/ucoolib/hal/i2c/Module +++ b/digital/ucoolib/ucoolib/hal/i2c/Module @@ -1 +1 @@ -hal_i2c_SOURCES := i2c_slave_data_buffer.cc i2c_hard.stm32.cc +hal_i2c_SOURCES := i2c.host.cc i2c_slave_data_buffer.cc i2c_hard.stm32.cc diff --git a/digital/ucoolib/ucoolib/hal/i2c/i2c.hh b/digital/ucoolib/ucoolib/hal/i2c/i2c.hh index 963eb767..5fad9988 100644 --- a/digital/ucoolib/ucoolib/hal/i2c/i2c.hh +++ b/digital/ucoolib/ucoolib/hal/i2c/i2c.hh @@ -24,7 +24,9 @@ // // }}} -#if defined (TARGET_stm32) +#if defined (TARGET_host) +# include "i2c.host.hh" +#elif defined (TARGET_stm32) # include "i2c_hard.stm32.hh" #else # error "not implemented for this target" diff --git a/digital/ucoolib/ucoolib/hal/i2c/i2c.host.cc b/digital/ucoolib/ucoolib/hal/i2c/i2c.host.cc new file mode 100644 index 00000000..1ad5f9e9 --- /dev/null +++ b/digital/ucoolib/ucoolib/hal/i2c/i2c.host.cc @@ -0,0 +1,213 @@ +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2013 Nicolas Schodet +// +// APBTeam: +// Web: http://apbteam.org/ +// Email: team AT apbteam DOT org +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// }}} +#include "i2c.host.hh" + +#include "config/hal/i2c.hh" + +namespace ucoo { + +/// Shared context between instances. +class I2cHostShared +{ + public: + /// Constructor, connect to mex node. + I2cHostShared (Host &host); + /// Register a new driver instance. + void register_instance (Host &host, I2cHost &instance); + /// Send message, return new master status. + int send (uint8_t addr, const char *buf, int count); + /// Receive message, return new master status. + int recv (uint8_t addr, char *buf, int count); + /// Handle read requests from master. + void handle_read (mex::Msg &msg); + /// Handle write requests from master. + void handle_write (mex::Msg &msg); + private: + Host &host_; + mex::Node &node_; + mex::mtype_t read_mtype_, write_mtype_; + typedef std::list Instances; + Instances instances_; +}; + +I2cHostShared::I2cHostShared (Host &host) + : host_ (host), node_ (host.get_node ()) +{ + std::string instance = host_.get_instance (1); + read_mtype_ = node_.reserve (instance + ":read"); + write_mtype_ = node_.reserve (instance + ":write"); + node_.handler_register (read_mtype_, *this, &I2cHostShared::handle_read); + node_.handler_register (write_mtype_, *this, &I2cHostShared::handle_write); +} + +void +I2cHostShared::register_instance (Host &host, I2cHost &instance) +{ + assert (&host == &host_); + instances_.push_back (&instance); +} + +int +I2cHostShared::send (uint8_t addr, const char *buf, int count) +{ + // Test for this slave in the same program. + for (Instances::const_iterator i = instances_.begin (); + i != instances_.end (); ++i) + { + if (addr == (*i)->slave_addr_) + { + assert (count <= UCOO_CONFIG_HAL_I2C_SLAVE_BUFFER_SIZE); + (*i)->slave_data_handler_->to_recv (buf, count); + return count; + } + } + // Else, send message. + mex::Msg msg (write_mtype_); + msg.push ("B") << addr; + msg.push (buf, count); + node_.send (msg); + return count; +} + +int +I2cHostShared::recv (uint8_t addr, char *buf, int count) +{ + // Test for this slave in the same program. + for (Instances::const_iterator i = instances_.begin (); + i != instances_.end (); ++i) + { + if (addr == (*i)->slave_addr_) + { + assert (count <= UCOO_CONFIG_HAL_I2C_SLAVE_BUFFER_SIZE); + return (*i)->slave_data_handler_->to_send (buf, count); + } + } + // Else, send request and wait for response. + mex::Msg msg (read_mtype_); + msg.push ("BB") << addr << count; + std::auto_ptr rsp = node_.request (msg); + int rcount = rsp->len (); + assert (rcount <= count); + const char *rbuf = rsp->pop (rcount); + std::copy (rbuf, rbuf + rcount, buf); + return rcount; +} + +void +I2cHostShared::handle_read (mex::Msg &msg) +{ + uint8_t addr; + int size; + msg.pop ("BB") >> addr >> size; + for (Instances::const_iterator i = instances_.begin (); + i != instances_.end (); ++i) + { + if (addr == (*i)->slave_addr_) + { + assert (size <= UCOO_CONFIG_HAL_I2C_SLAVE_BUFFER_SIZE); + char buf[size]; + int n = (*i)->slave_data_handler_->to_send (buf, size); + mex::Msg rsp (read_mtype_); + rsp.push (buf, n); + node_.response (rsp); + break; + } + } +} + +void +I2cHostShared::handle_write (mex::Msg &msg) +{ + uint8_t addr; + msg.pop ("B") >> addr; + for (Instances::const_iterator i = instances_.begin (); + i != instances_.end (); ++i) + { + if (addr == (*i)->slave_addr_) + { + int size = msg.len (); + assert (size <= UCOO_CONFIG_HAL_I2C_SLAVE_BUFFER_SIZE); + (*i)->slave_data_handler_->to_recv (msg.pop (size), size); + break; + } + } +} + +I2cHostShared *I2cHost::shared_; + +I2cHost::I2cHost (Host &host, int n) + : n_ (n), slave_addr_ (0), slave_data_handler_ (0), + master_status_ (STATUS_ERROR) +{ + if (!shared_) + { + static I2cHostShared shared (host); + shared_ = &shared; + } + shared_->register_instance (host, *this); + // n is not used, there is no support for several buses in the MEX + // messages at the moment. +} + +void +I2cHost::send (uint8_t addr, const char *buf, int count) +{ + // Update status, there is no background task. + master_status_ = shared_->send (addr, buf, count); + // If needed, call callback. + if (finished_handler_) + finished_handler_->finished (master_status_); +} + +void +I2cHost::recv (uint8_t addr, char *buf, int count) +{ + // Update status, there is no background task. + master_status_ = shared_->recv (addr, buf, count); + // If needed, call callback. + if (finished_handler_) + finished_handler_->finished (master_status_); +} + +int +I2cHost::status () +{ + return master_status_; +} + +int +I2cHost::wait () +{ + // Transfers are immediate. + return status (); +} + +void +I2cHost::register_data (uint8_t addr, DataHandler &data_handler) +{ + slave_addr_ = addr; + slave_data_handler_ = &data_handler; +} + +} // namespace ucoo diff --git a/digital/ucoolib/ucoolib/hal/i2c/i2c.host.hh b/digital/ucoolib/ucoolib/hal/i2c/i2c.host.hh new file mode 100644 index 00000000..47a802b9 --- /dev/null +++ b/digital/ucoolib/ucoolib/hal/i2c/i2c.host.hh @@ -0,0 +1,66 @@ +#ifndef ucoolib_hal_i2c_i2c_host_hh +#define ucoolib_hal_i2c_i2c_host_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2013 Nicolas Schodet +// +// APBTeam: +// Web: http://apbteam.org/ +// Email: team AT apbteam DOT org +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// }}} +#include "ucoolib/intf/i2c.hh" +#include "ucoolib/arch/host/host.hh" + +namespace ucoo { + +class I2cHostShared; + +/// I2C interface, host version. +class I2cHost : public I2c +{ + public: + /// Initialise the Nth I2C. + I2cHost (Host &host, int n); + /// See I2cMaster::send. + void send (uint8_t addr, const char *buf, int count); + /// See I2cMaster::recv. + void recv (uint8_t addr, char *buf, int count); + /// See I2cMaster::status. + int status (); + /// See I2cMaster::wait. + int wait (); + /// See I2cSlave::register_data. + void register_data (uint8_t addr, DataHandler &data_handler); + private: + /// I2C number. + int n_; + /// Slave address. + uint8_t slave_addr_; + /// Handler called to source or sink data for slave exchanges. + DataHandler *slave_data_handler_; + /// Current master transfer status. + int master_status_; + /// Shared context. + static I2cHostShared *shared_; + friend class I2cHostShared; +}; + +} // namespace ucoo + + +#endif // ucoolib_hal_i2c_i2c_host_hh diff --git a/digital/ucoolib/ucoolib/hal/i2c/test/Makefile b/digital/ucoolib/ucoolib/hal/i2c/test/Makefile index 14a46120..50468624 100644 --- a/digital/ucoolib/ucoolib/hal/i2c/test/Makefile +++ b/digital/ucoolib/ucoolib/hal/i2c/test/Makefile @@ -1,6 +1,6 @@ BASE = ../../../.. -TARGETS = stm32f4 +TARGETS = host stm32f4 PROGS = test_i2c test_i2c_SOURCES = test_i2c.cc diff --git a/digital/ucoolib/ucoolib/hal/i2c/test/hub.py b/digital/ucoolib/ucoolib/hal/i2c/test/hub.py new file mode 100644 index 00000000..c6c77f77 --- /dev/null +++ b/digital/ucoolib/ucoolib/hal/i2c/test/hub.py @@ -0,0 +1,6 @@ +# Hub for test. +from mex.hub import Hub +def log (x): + print x +h = Hub (min_clients = 1, log = log) +h.wait () diff --git a/digital/ucoolib/ucoolib/hal/i2c/test/test_i2c.cc b/digital/ucoolib/ucoolib/hal/i2c/test/test_i2c.cc index dd043f32..4a99a419 100644 --- a/digital/ucoolib/ucoolib/hal/i2c/test/test_i2c.cc +++ b/digital/ucoolib/ucoolib/hal/i2c/test/test_i2c.cc @@ -24,11 +24,14 @@ #include "ucoolib/hal/i2c/i2c.hh" #include "ucoolib/arch/arch.hh" -#include "ucoolib/hal/gpio/gpio.hh" -#include "ucoolib/utils/delay.hh" #include "ucoolib/base/test/test.hh" -#include +#ifdef TARGET_stm32 +# include +# include "ucoolib/hal/gpio/gpio.hh" +#endif + +#include "ucoolib/utils/delay.hh" #include #include @@ -44,23 +47,28 @@ test_basic (ucoo::TestSuite &tsuite, ucoo::I2cMaster &m, tsuite.group ("basic"); { ucoo::Test test (tsuite, "recv"); - // Slave is not able to signal its buffer end. Extra bytes will be - // read as 0xff. const char *hello = "Hello world!"; char buf[buffer_size + margin]; d.update (hello, std::strlen (hello) + 1); char ref[buffer_size + margin]; - std::copy (hello, hello + std::strlen (hello) + 1, ref); - std::fill (ref + std::strlen (hello) + 1, ref + sizeof (ref), 0xff); + int ref_size = std::strlen (hello) + 1; + std::copy (hello, hello + ref_size, ref); +#ifdef TARGET_stm32 + // Slave is not able to signal its buffer end. Extra bytes will be + // read as 0xff. + std::fill (ref + ref_size, ref + sizeof (ref), 0xff); + ref_size = sizeof (ref); +#endif for (int len = 2; len < (int) sizeof (buf); len++) { std::fill (buf, buf + sizeof (buf), 42); m.recv (addr, buf, len); int r = m.wait (); - test_fail_break_unless (test, r == len); - test_fail_break_unless (test, std::equal (buf, buf + len, ref)); + int rexp = std::min (len, ref_size); + test_fail_break_unless (test, r == rexp); + test_fail_break_unless (test, std::equal (buf, buf + rexp, ref)); test_fail_break_unless (test, std::count (buf, buf + sizeof (buf), 42) - == (int) sizeof (buf) - len); + == (int) sizeof (buf) - rexp); } } { @@ -112,12 +120,12 @@ test_basic (ucoo::TestSuite &tsuite, ucoo::I2cMaster &m, switch (step_) { case 0: - m_.recv (addr_, buf_, buffer_size); step_++; + m_.recv (addr_, buf_, buffer_size); break; case 1: - m_.send (addr_, buf_, buffer_size); step_++; + m_.send (addr_, buf_, buffer_size); break; case 2: // Nothing, stop. @@ -154,6 +162,12 @@ main (int argc, const char **argv) { ucoo::arch_init (argc, argv); ucoo::TestSuite tsuite ("i2c"); +#if defined (TARGET_host) + ucoo::Host host ("test_i2c"); + host.parse_options (); + ucoo::I2cHost i2c1 (host, 0); + ucoo::I2cHost i2c2 (host, 1); +#elif defined (TARGET_stm32) // I2C1: B6: SCL, B9: SDA // I2C3: A8: SCL, C9: SDA rcc_peripheral_enable_clock (&RCC_AHB1ENR, RCC_AHB1ENR_IOPAEN); @@ -168,13 +182,14 @@ main (int argc, const char **argv) gpio_set_af (GPIOB, GPIO_AF4, GPIO6 | GPIO9); gpio_set_af (GPIOA, GPIO_AF4, GPIO8); gpio_set_af (GPIOC, GPIO_AF4, GPIO9); - ucoo::I2cSlaveDataBufferSize<16, 16> data1, data2; ucoo::I2cHard i2c1 (0); ucoo::I2cHard i2c2 (2); - i2c1.register_data (a1, data1); - i2c2.register_data (a2, data2); i2c1.enable (); i2c2.enable (); +#endif + ucoo::I2cSlaveDataBufferSize<16, 16> data1, data2; + i2c1.register_data (a1, data1); + i2c2.register_data (a2, data2); // Run tests. test_basic (tsuite, i2c1, data2, a2); test_basic (tsuite, i2c2, data1, a1); -- cgit v1.2.3