What would be the final location of these packages? I imagine that x/exp is just a temporary home for them.
What about x/hardware? I think Dave's serial port package also belongs there.
And the terminal handling packages should go to x/sys instead of x/term.
What would be the final location of these packages? I imagine that x/exp is just a temporary home for them.
What about x/hardware? I think Dave's serial port package also belongs there.
--
You received this message because you are subscribed to the Google Groups "golang-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
On Mar 24, 2016 10:08 PM, "Brad Fitzpatrick" <brad...@golang.org> wrote:
> "peripheral" is a mouthful.
What about x/hardware/spi or x/io/spi?
Those are both shorter and easier to spell.
On Fri, 25 Mar 2016 14:30:44 -0700
Jaana Burcu Dogan <j...@golang.org> wrote:
> I am also moving the experimental packages to x/exp/io.
Have you looked at goserial at all? Or at contributing to it?
--
Benny Siegert <bsie...@gmail.com>
I can use the fragmentation in HTTP packages in Python as an analogy for GPIO packages in Go. Go is great when we agree and combine efforts around a single API as a community. Interoperability and compatibility is a major goal.
Manlio
Fair enough.
The question is whether there is a high demand for a serial port implementation, so that we can consider it in the scope of x/io.
On Mar 30, 2016 12:27 AM, "Jaana Burcu Dogan" <j...@golang.org> wrote:
> If the demand is to have USB, we should have a USB implementation rather than a USB over virtual serial port backend.
I'm definitely interested in having a pure Go devusbfs interface in x/io/usb that implements the libusb API interface, but in idiomatic Go.
I'd also like a USB gadget package in x/io/usb/gadget.
I think we also need a Bluetooth hci package in x/io.
I like the idea of pushing an api! thank you for going that way.
today i saw your first contributions to exp/io/spi and have a question.
Why is a Transfer api better than a Reader/Writer interface?
On Wed, Mar 30, 2016 at 12:16 PM, sperberstephan via golang-dev <golan...@googlegroups.com> wrote:I like the idea of pushing an api! thank you for going that way.
today i saw your first contributions to exp/io/spi and have a question.
Why is a Transfer api better than a Reader/Writer interface?SPI is full duplex, so it doesn't really fit the Reader/Writer interface.Because the read/write in one SPI transaction is logically one thing,I don't think it's wise to break them up into two functions calls justto satisfy existing Go interfaces.
here: https://github.com/quinte17/spi
i did a hackup a few months ago so someone could try it out.
the drawback of my approach is that you always need to read after a write to clean up unneeded bytes.
an example hw driver which is using it isnt public yet and i dont have the time to reach a "running" state in the next months..
but reading and writing to and from my device is working.
--
Well, for one thing, it's a lot longer to type.
You received this message because you are subscribed to a topic in the Google Groups "golang-dev" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-dev/ofaaIJPWRKg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-dev+...@googlegroups.com.
Hi, Burcu
I've been following your commits, looking really cool.
Where is the best place to provide specific feedback on interfaces etc? For example, regarding your comment in commit d5aecea7ffa68142f6a4cdc95761c25c135cf607
"Investigate if command-less read/write is valid."
There are a few oddball devices for which the trigger for the i2c data is some data line on the device itself. For example, the "Hover". In that case, the reading operation does not writes any bytes for the register before peforming a read.
An opposite case would be one of n-DOF devices (I cannot recall which right this moment) that requires sending the register along with some additional command bytes before performing the read.
Perhaps having both a direct Read(), as well as the helpful and often necessary ReadFromRegister(). Likewise Write() and WriteToRegister().
Thanks for listening!
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+unsubscribe@googlegroups.com.
Marc-Antoine, thanks for the wall of text! I very much share the goals you state. I started with embd because I didn't want to reinvent the wheel and needed SPI, gpio, and interrupts real fast to implement an RFM69 radio driver. The good news is that it works beautifully. The bad news is that the library structure is not what it needs to be. I contacted the author to see whether he's open to let someone else move it forward and I'm now a committer on the repo but I'm not sure where that's really going. I found that embd's SPI and GPIO interfaces, which is what code using embd actually cares about, are very pleasant to use. But the top-level package has all this driver junk in it, etc. So my plan was to try and refactor, keeping these key interfaces, and changing everything else around. I was hoping to be able to keep all the sensor and other device code this way with minimal changes.An area that I've been particularly stumped by is testing. I'm less concerned about the testing of the bus drivers than about the testing of attached devices. One approach I've considered is to create a test board that can easily be attached to most any embedded computer and that contains a small assortment of popular sensors. It shouldn't be too hard to host one that can run test as part of a travis build. The other approach I've thought of is to add a recording feature to the low-level bus layer so the author of a device driver can record a test run that uses the real device and then the recording can be used to perform future automated test runs. I didn't notice a lot of _test files in your repo ;-). I do like hosttest, maybe some mocking support or recording support could be added.
Overall after spending a couple of hours browsing your work I'm impressed. If you would break it out into its own repo (on github I hope) I'd take a stab at porting my work to see the rubber meet the road... At a high level the things I wasn't thrilled by are the following:
- I'd really like to see a top-level mapping of names to interfaces, what I mean by that is that a program should be able to take a string provided by the user that corresponds to the host's or OS's naming convention and turn that into a handle onto a bus or pin. So if the documentation or the convention on a particular platform is to talk about "the SPI2 bus" then there should be a way to pass "SPI2" (or "spi2") into a commandline flag and have the code go from there. As far as I can tell (...) this fails at two levels. One is that for I2C and SPI the code embeds import paths that lock down which access method is to be used for the bus, e.g. sysfs vs bitbang. The other is that other than the host.Headers.All() mapping there is no general string -> device handle mapping (that I saw).
- Related to the first point, I would really like the same binary to be able to run on rPi, ODROID or CHIP. At least the source code shouldn't have to be changed to swap out import paths.
- The I2C and SPI interfaces are pretty sparse. On the one hand minimal is good, on the other I do find the convenience methods exposed by embd very handy and they save beginners from a bunch of head-scratching. I mean all the Read and Write methods in https://godoc.org/github.com/tve/embd#I2CBus but I realize this is a matter of taste in the end.
- I'm not a fan of the internal packages. I get that something outside of the library shouldn't reach in there, but it also prevents exposing very platform specific things or being able to easily tinker with stuff that's in there. You may not have a use-case for it but that doesn't mean there are no such use-cases. I guess the question could be posed differently: what does the "internal" gain? (I think the only thing it gains is that it's not obvious at the outset where one would start using the library and so it hides those portions, but that's a really bad argument.)
Details I noticed while browsing the code:
- The host package has some type with very generic names that are very special purpose, for example, the Key and Message types that are very IR specific, or the Mode type that is very SPI specific.
- There doesn't seem to be a way to get an error for a PinIn or a PinOut, I'm not sure that's good.
- PinIn should have a way to change the pull (although that can be added once someone implements it).
- For PinIn.Edges() I do not understand "It is important to stop the querying loop by sending a Low to the channel to stop it. The channel will then immediately be closed."
- PinOut is too limited: it needs pull resistor control and there should be a way to set the initial value when initializing the pin.
- In host.sysfs the constructors for I2C and SPI take a bus parameter that is not documented and where it's not obvious which values are available on the current host
- I'm not sure why "Make" is used for constructors instead of "New"
- I didn't understand what you wrote about SPI speed. The rfm69 radio I'm writing a device driver for can run at up to 10Mhz. Why is it not the role of the device driver to set that bus speed? (Maybe the linux sysfs driver doesn't allow different devices on one bus to have different speeds? Is that a reason not to support something like that?)
- host/internal/allwinner needs a more specific name, for example, C.H.I.P uses an allwinner R8 processor and I don't think much carries over
Ugh, I didn't intend to respond with a wall of text, but here I am. I think overall as far as I can tell you achieved your goals, which is no mean feat. Please let me know whether you're going ahead with breaking your library out and whether you'd be interested in a CHIP port.
This will happen soonish, based on the interest.
- Related to the first point, I would really like the same binary to be able to run on rPi, ODROID or CHIP. At least the source code shouldn't have to be changed to swap out import paths.
That's currently the case IFF you only use linux. It's compatible between debian based distributions.
- The I2C and SPI interfaces are pretty sparse. On the one hand minimal is good, on the other I do find the convenience methods exposed by embd very handy and they save beginners from a bunch of head-scratching. I mean all the Read and Write methods in https://godoc.org/github.com/tve/embd#I2CBus but I realize this is a matter of taste in the end.
host.I2C does not implement host.Bus on purpose as is meant to be used via i2cdev.Dev: it saves from continuously typing the device address and presents ReadReg() in a fully generic way, without having to have 5 different functions.On the other hand, host.SPI implements Bus on purpose, which inherits io.Writer, which makes it trivial to dump to it, like apa102 is doing.The unstated thing here is that host.Bus is not a bus but really a point-to-point connection. Probably worth renaming host.Bus accordingly (?) and i2cdev.Dev should be made more prominent / obvious in its usefulness.
Initially everything was in a flat namespace. I've cargo moved code into internal as I wanted to reduce the "attack surface" until I had figured things out. The idea was to force myself to make proper interfaces so that all the functionality is still available.
I added pins.ByFunction() to retrieve a pin by its function. It's probably only useful to return the pin number (?)
- There doesn't seem to be a way to get an error for a PinIn or a PinOut, I'm not sure that's good.
- PinIn should have a way to change the pull (although that can be added once someone implements it).
The use case is that you call pin.In() or pin.Out(). If it succeeded, it means that you succeeded in setting the pin in the mode you want and can call Read() / Set() safely from that point on. If it failed, it failed and game over.The point here is that for in memory mapped drivers, Read() and Set() are literally reading or setting a register. For sysfs gpio, they are writing and reading to an already open handle. Even for sysfs, the chance of failing is nearly nil and not worth bothering. If you ever see a case where reading or writing to /gpioN/value first succeeds then fails, tell me and I'm open to change this but otherwise, this makes the code so much more readable.I've thought about this a lot. It's a thin line between usability and correctness.
- PinOut is too limited: it needs pull resistor control and there should be a way to set the initial value when initializing the pin.
What do you mean by "pull resistor" on an output pin? By definition, an output pin acts as a buffer, most CPUs can source ~20mA. A pull resistor can not source power. Only input pins can have a pull resistor set.
I guess Out() should probably take a parameter but I feared people would continuously call Out() instead of Set().
- I'm not sure why "Make" is used for constructors instead of "New"
I felt that New implies dumb memory allocation and Make implies construction.New shouldn't return an error. wdyt?
- I didn't understand what you wrote about SPI speed. The rfm69 radio I'm writing a device driver for can run at up to 10Mhz. Why is it not the role of the device driver to set that bus speed? (Maybe the linux sysfs driver doesn't allow different devices on one bus to have different speeds? Is that a reason not to support something like that?)
I've rechecked /usr/include/linux/spi/spidev.h and spi_ioc_transfer does allow to change the speed on a per transaction basis. The problem is that you still need to mangle the interface and can't support io.Writer if the speed is not an invariant.I'm open to make this device configurable instead of bus configurable.
- host/internal/allwinner needs a more specific name, for example, C.H.I.P uses an allwinner R8 processor and I don't think much carries over
You'd be surprised. Allwinner is relatively consistent with their register mapping at least for the chips I looked at. If you look at R8 user manual (arm7) and compare with the A64 user manual (arm64), you'll see that it uses the same base register of 0x01C20800 for pins in group PB to BH. The main difference is that the R8 doesn't have pins of group PL.I think the module can be adapted easily, i.e. not fail atmem, err := gpiomem.OpenMem(getBaseAddressPL())and handles this gracefully through the code, hiding this pins group.
Ugh, I didn't intend to respond with a wall of text, but here I am. I think overall as far as I can tell you achieved your goals, which is no mean feat. Please let me know whether you're going ahead with breaking your library out and whether you'd be interested in a CHIP port.I ordered two CHIP to confirm the support, we'll see in several months since it's "estimated November". It actually could be useful for my project.
On Tuesday, September 20, 2016 at 1:38:52 PM UTC-7, Marc-Antoine Ruel wrote:Isn't that only true because you're sweeping the two implementations of gpio (mem-mapped vs sysfs) under the rug? So for gpio you hide the difference but for i2c/spi you're exposing the different implementations.
Similarly, on the gpio side you're hiding the lack of HW interrupts by burning up CPU cycles (which I forgot to flag as one of the things I'm not a fan of).
I guess my point is that I find that the I2C and SPI interfaces in embd lead to more readable code. They're not minimal, but sometimes expressivity also counts.
Initially everything was in a flat namespace. I've cargo moved code into internal as I wanted to reduce the "attack surface" until I had figured things out. The idea was to force myself to make proper interfaces so that all the functionality is still available.Makes sense, I'm just thinking that in the end it does get in the way of special projects and the overall structure needs to be intuitive or documented enough so users naturally do the normal stuff the "right way".
I added pins.ByFunction() to retrieve a pin by its function. It's probably only useful to return the pin number (?)Yup, that looks nice for GPIO. I expect that I would do something like "redLed := pins.ByFunction("XIO-2"); redLed.Out(); redLed.Set(true);" The "XIO-2" string is gpio pin 2 on CHIP and would have to be configured by the user. The gain here is that (a) ByFunction can accept the strings that are shown in the CHIP manual and tutorials, (b) I get to name the pin however I like in my program (redLed in this example).
Yup, having every function return an error just because pollutes code everywhere. Q: assuming sysfs, what happens if some other app unexports a pin I'm using? Or also opens the pin and changes its direction?I'm not necessarily arguing that these are cases that warrant the addition of error returns everywhere, but at the same time it would be good to understand what happens and document it, ideally in a way where this can be caught somehow.
- PinOut is too limited: it needs pull resistor control and there should be a way to set the initial value when initializing the pin.
What do you mean by "pull resistor" on an output pin? By definition, an output pin acts as a buffer, most CPUs can source ~20mA. A pull resistor can not source power. Only input pins can have a pull resistor set.Mhh, most GPIO pins I'm familiar with support open drain/collector outputs. To drive the i2c clock you need an open drain output, for example, 'cause the slave can do clock stretching. In this case the internal pull-up can be turned on and may or may not suffice. If the gpio does not support a true open drain output this can be simulated by setting the pin value to 0 and turning the output on to output low, and turning the output off to output a high. In those cases the "write" operation turns into a direction switch instead of setting the output value. Mhh, I'm sure you know all this, we must be mis-communicating somewhere?
I guess Out() should probably take a parameter but I feared people would continuously call Out() instead of Set().Mhhh, maybe it should be Out(drvier driverType, initial Level) error, where driverType is TotemPole, OpenDrain, OpenDrainPullup (mhh, is totem-pole what's used for cmos?).
- I'm not sure why "Make" is used for constructors instead of "New"
I felt that New implies dumb memory allocation and Make implies construction.New shouldn't return an error. wdyt?Is there precedent for this in the stdlib? What you describe is what went though my head, so you did convey the right thing to me, but I was wondering whether this is idiomatic go or not.
- I didn't understand what you wrote about SPI speed. The rfm69 radio I'm writing a device driver for can run at up to 10Mhz. Why is it not the role of the device driver to set that bus speed? (Maybe the linux sysfs driver doesn't allow different devices on one bus to have different speeds? Is that a reason not to support something like that?)
I've rechecked /usr/include/linux/spi/spidev.h and spi_ioc_transfer does allow to change the speed on a per transaction basis. The problem is that you still need to mangle the interface and can't support io.Writer if the speed is not an invariant.I'm open to make this device configurable instead of bus configurable.I think that would be a good step and probably sufficient for almost all cases. Thanks for checking (I was too lazy).
OK, I think I should stop theorizing. What I'd like to do is to port pio to CHIP and port my rfm69 driver (SPI) to it. That's the only way for me to separate theory from practice :-). I'm not in a rush, I still have to improve the interrupt handling and stuff like that that is independent. Can you set some expectation for when it would make sense for me to do this work? I'm not looking for a finished library or anything stable, I just think it would be dumb for me to fork and then you do a major reorg two days later...
Thanks for sharing your work!
But making it a factory blind to the implementation is non trivial; you may have to pass the handle of a kernel driver, GPIO pins to use (like bitbang), bus number for sysfs, etc.Will think about that to refactor but as I said, I'm not a fan of abstracting until there's at least two implementations which would benefit from the exact same constructor.
Similarly, on the gpio side you're hiding the lack of HW interrupts by burning up CPU cycles (which I forgot to flag as one of the things I'm not a fan of).That's not true. The gpio side transparently leverages sysfs under the hood. It's the best of both world: setup the pull resistor via gpio memory mapped registers, then leverage sysfs to trigger on edge detection. Look at Pin.edge for both allwinner and bcm283x. Interestingly on allwinner only a subset of the pins support edge detection.There's no busy loop at all.
I couldn't find any other library doing that (please tell me if there's one).
I guess my point is that I find that the I2C and SPI interfaces in embd lead to more readable code. They're not minimal, but sometimes expressivity also counts.embd encourages you to write inefficient code. :/ Having a bus implement a large interfaces makes creating new implementations or fakes much harder than necessary.
Actually it's too late, I've already pushed the change. :) wdyt?
Writing as "out" defaults to initializing the value as low. To ensure glitch free operation, values "low" and "high" may be written to configure the GPIO as an output with that initial value.
If the GPIO pin is being used as in input (by default) then the value in the SET{n} field is ignored. However, if the pin is subsequently defined as an output then the bit will be set according to the last set/clear operation.
It's effectively impossible to write an I²C bitbanging driver without input pull resistors. On Allwinner, this is quite rapid to do but on BCM, the caller has to waste 150 cycles to set pull resistors and I still haven't figured a good way to burn exactly 150 cycles in Go.
I felt that New implies dumb memory allocation and Make implies construction.New shouldn't return an error. wdyt?
- spi/driver.Conn let the device driver change too much things. IMHO it's not to the device driver to change the CS line or the speed to communicate at (you are welcome to disagree with me).
- bus interfaces should not have a Close() method. The clients of a bus have no say to close the bus, it's who who created the object in the first place who owns the object and who should close it.
- I've put Bus "factories" in their relevant package by their source of interface, i.e. sysfs or bitbang, not by their protocol.
Instead I create one single host.Bus interface. Since I2C requires specific device addressing, it has an trivial adapter to convert a host.I2C to host.Bus. This is because for I²C, it's generally the device driver that knows the device address, while for SPI, it's the calling app that knows which CS line to use. Compare ssd1306.MakeI2C() and ssd1306.MakeSPI(). tm1637.Make() and bitbang.MakeSPI() are examples of pure pin based communication.The i2cdev.Dev is what adds all the ReadReg() style function, not the bus itself. It's pure raw code, not yet-another-interface. That keeps the host.I2C interface clean and it makes it easier to write the fake hosttest.I2C.But like with embd, I felt I arrived too late and wasn't in a position to tell others that their code is not in the right way™ so I felt it was better to try it out by myself and see if there's actual value.My main fear is figuring out what is being really good design vs what is just being design zealot.
On Wednesday, September 21, 2016 at 1:09:50 PM UTC-7, Marc-Antoine Ruel wrote:I'm curious why you decided to implement the edge detection yourself instead of letting the sysfs driver do it, which allows the function to pushed down into the HW. You ask sysfs to generate an interrupt on both rising and falling edges. That's 2x the necessary interrupts. Then you read the value, which takes extra time (specially when gpiomem is not available). And the fact that you do the edge detection means that a pulse that is shorter than the interrupt response time (allthe way into user-space is missed).
I guess my point is that I find that the I2C and SPI interfaces in embd lead to more readable code. They're not minimal, but sometimes expressivity also counts.embd encourages you to write inefficient code. :/ Having a bus implement a large interfaces makes creating new implementations or fakes much harder than necessary.By inefficient code, do you mean the fact that a WriteReg implementation would have to copy the array being written in order to prepend the register number? Given that you're going through a system call afterwards, does that matter? Do you expect the caller to do any better by not exposing such a helper function?
WRT pull-up/pull-down resistors and all that, let me try a fresh start. I'm looking at https://www.kernel.org/doc/Documentation/gpio/sysfs.txt and in the section on "direction" is says:Writing as "out" defaults to initializing the value as low. To ensure glitch free operation, values "low" and "high" may be written to configure the GPIO as an output with that initial value.
I know that turning a pin to an output starting at a known value without a glitch is something I've needed in the past. I believe that a good library should support that. I think you could either add an initial param to Out() or you could allow Set() before out and that saves the value in the object so it can start out with the right value when Out() is called. The latter is definitely what one would do at the register level on most uC's (i.e. set the output value and then turn the output section on).
In terms of performance, doesn't the fact that you combine the pull-up specification with the In() function cause a performance hit? For example, in the bitbang I2C implementation https://github.com/maruel/dlibox/blob/master/go/pio/host/drivers/bitbang/i2c.go#L169 instead of just doing a quick register update to switch the pin from output to input you are doing the whole 2x 150 cycles dance to set the pull-up. When you implement the clock stretching 8 lines up you'll do this for every bit. Note how the bcm2835 chip has the pull-up/pull-down completely separate from direction, you can even enable the pull-up if you use an alternate function.
I'm a bit confused by the presence of pine64 and rpi in host/hal. I thought "hal" meant the platform independent abstraction, the ones I should zoom in on and use. As opposed to host/driver, which is the platform-specific abstractions that I should ignore, unless I'm implementing a new platform. Hence I'd move pine64 and rpi into driver. But maybe I'm off track...
I also find devices/i2cdev a little odd. I totally get the separation of the driver providing a bus and i2cdev providing access to one device on the bus, but I would expect to find i2cdev in host. It's one of the core abstractions that the library exposes, isn't it?
The fact that to get started with GPIO one has to dive down into host/hal/pins (or host/hal/headers) is a bit unintuitive. There's not much at higher levels to guide someone there. Not sure I have a good suggestion other than move hal/pins & hal/headers into host and move the rest of what's in hal into drivers. What would happen if you hoisted all the .go files in host to the top level? This way the functions that the user should use would be right there at the top-level of the library.
Marc-Antoine, thanks for the wall of text! I very much share the goals you state. I started with embd because I didn't want to reinvent the wheel and needed SPI, gpio, and interrupts real fast to implement an RFM69 radio driver. The good news is that it works beautifully. The bad news is that the library structure is not what it needs to be. I contacted the author to see whether he's open to let someone else move it forward and I'm now a committer on the repo but I'm not sure where that's really going. I found that embd's SPI and GPIO interfaces, which is what code using embd actually cares about, are very pleasant to use. But the top-level package has all this driver junk in it, etc. So my plan was to try and refactor, keeping these key interfaces, and changing everything else around. I was hoping to be able to keep all the sensor and other device code this way with minimal changes.An area that I've been particularly stumped by is testing. I'm less concerned about the testing of the bus drivers than about the testing of attached devices. One approach I've considered is to create a test board that can easily be attached to most any embedded computer and that contains a small assortment of popular sensors. It shouldn't be too hard to host one that can run test as part of a travis build. The other approach I've thought of is to add a recording feature to the low-level bus layer so the author of a device driver can record a test run that uses the real device and then the recording can be used to perform future automated test runs. I didn't notice a lot of _test files in your repo ;-). I do like hosttest, maybe some mocking support or recording support could be added.Overall after spending a couple of hours browsing your work I'm impressed. If you would break it out into its own repo (on github I hope) I'd take a stab at porting my work to see the rubber meet the road... At a high level the things I wasn't thrilled by are the following:
- I'd really like to see a top-level mapping of names to interfaces, what I mean by that is that a program should be able to take a string provided by the user that corresponds to the host's or OS's naming convention and turn that into a handle onto a bus or pin. So if the documentation or the convention on a particular platform is to talk about "the SPI2 bus" then there should be a way to pass "SPI2" (or "spi2") into a commandline flag and have the code go from there. As far as I can tell (...) this fails at two levels. One is that for I2C and SPI the code embeds import paths that lock down which access method is to be used for the bus, e.g. sysfs vs bitbang. The other is that other than the host.Headers.All() mapping there is no general string -> device handle mapping (that I saw).
On Mon, Sep 19, 2016 at 5:18 PM, Marc-Antoine Ruel <mar...@chromium.org> wrote:- it has the "factory feeling" with the openers. More often than not, a simple function call is sufficient.What do you mean by "factory feeling"? You construct an opener and pass it only at Open. This is crazily simply if you compare what relevant libraries are trying to achieve abstractions over abstractions. Given the portability requirement, I ask you write a single line of Opener construction :)
Because these are only predictable and a proper implementation shouldn't require that you have capabilities to run i2cdetect. Until we have have a proper Go implementation of i2cdetect, we should be any opinionated on caring about calculating any paths.
- spi/driver.Conn let the device driver change too much things. IMHO it's not to the device driver to change the CS line or the speed to communicate at (you are welcome to disagree with me).A Conn implementation is a proper implementation of the protocol on a specific env. It implements every aspect of the protocol. Changing the CS or the speed is a basic requirement to implement SPI. Who's going to implement the calls to change these things? What's your replacement proposal? I am not sure I can totally follow this item.- bus interfaces should not have a Close() method. The clients of a bus have no say to close the bus, it's who who created the object in the first place who owns the object and who should close it.That's why there are not called busses but connections to busses. You are entitled to close the connection to a bus not the bus' itself. The original package don't have any concept of busses for that reason. Buses are something belongs to the system but a connection is owned by the client program.
- I've put Bus "factories" in their relevant package by their source of interface, i.e. sysfs or bitbang, not by their protocol.There are some boards that only specifically have a bus for SPI but nothing else. How are we support such devices with a centralized host definition.
Instead I create one single host.Bus interface. Since I2C requires specific device addressing, it has an trivial adapter to convert a host.I2C to host.Bus. This is because for I²C, it's generally the device driver that knows the device address, while for SPI, it's the calling app that knows which CS line to use. Compare ssd1306.MakeI2C() and ssd1306.MakeSPI(). tm1637.Make() and bitbang.MakeSPI() are examples of pure pin based communication.The i2cdev.Dev is what adds all the ReadReg() style function, not the bus itself. It's pure raw code, not yet-another-interface. That keeps the host.I2C interface clean and it makes it easier to write the fake hosttest.I2C.But like with embd, I felt I arrived too late and wasn't in a position to tell others that their code is not in the right way™ so I felt it was better to try it out by myself and see if there's actual value.My main fear is figuring out what is being really good design vs what is just being design zealot.This is the main challenge in this experimentation. If a good design is convergable, we should commit work on the project. If not, we live on by living in our own packages and stop wasting more brain cycles on a pio package.
My suggestion to this is simply:func Map(virtual string, physical int)Then you can override the virtual space with different board configurations depending on what's the host.
Oops our messages crossed.2016-09-22 17:31 GMT-04:00 Jaana Burcu Dogan <j...@golang.org>:On Mon, Sep 19, 2016 at 5:18 PM, Marc-Antoine Ruel <mar...@chromium.org> wrote:- it has the "factory feeling" with the openers. More often than not, a simple function call is sufficient.What do you mean by "factory feeling"? You construct an opener and pass it only at Open. This is crazily simply if you compare what relevant libraries are trying to achieve abstractions over abstractions. Given the portability requirement, I ask you write a single line of Opener construction :)Fair enough, I tried to get rid of the struct and see if I could make it work without this in practice.
The fact that to get started with GPIO one has to dive down into host/hal/pins (or host/hal/headers) is a bit unintuitive. There's not much at higher levels to guide someone there. Not sure I have a good suggestion other than move hal/pins & hal/headers into host and move the rest of what's in hal into drivers. What would happen if you hoisted all the .go files in host to the top level? This way the functions that the user should use would be right there at the top-level of the library.
That's true. I removed 'hal' completely. I moved pin constants to protocols/pins (this sounds weird though).I finally added the magic host.NewI2C() and host.NewSPI() as you had asked. To make this work, I augmented i2c.Bus and spi.Bus to be able to query the actual pin used. This complicated the import orders a lot but I think it was worth it. i2c.Dev now makes more sense than the previous i2cdev.Dev.This makes simple utilization trivial. Here's an actual execution:~$ go run a.go23.580°C~$ cat a.gopackage mainimport ("fmt")func main() {bus, _ := host.NewI2C()b, _ := bme280.NewI2C(bus, bme280.O1x, bme280.O1x, bme280.O1x, bme280.S500ms, bme280.FOff)env := &devices.Environment{}b.Read(env)fmt.Printf("%6.3f°C\n", float32(env.MilliCelcius)*0.001)}Does that start to look good?
On Thursday, September 22, 2016 at 2:32:31 PM UTC-7, Marc-Antoine Ruel wrote:This makes simple utilization trivial. Here's an actual execution:~$ go run a.go23.580°C~$ cat a.gopackage mainimport ("fmt")func main() {bus, _ := host.NewI2C()b, _ := bme280.NewI2C(bus, bme280.O1x, bme280.O1x, bme280.O1x, bme280.S500ms, bme280.FOff)env := &devices.Environment{}b.Read(env)fmt.Printf("%6.3f°C\n", float32(env.MilliCelcius)*0.001)}Does that start to look good?I think you can refactor faster than I can comprehend ;-)It does look pretty awesome, I must say! The only thing I don't understand is why you don't allow the bus number (and CS number) to be specified for host.NewI2c and host.NewSPI.
One musing I had was whether it's best to put the generic selection logic into 'host' using the Enumerate() function you have, or whether it's better to push the selection down into the device-specific code. Maybe there's no difference.
I have to admit that this factory discussion is a bit too abstract for me to follow :-). Would it be possible to state a couple of concrete examples & use-cases that should or show not be supported? (Negative examples are good to reach minimalism.)I believe that over the set of rPi, ODROID, CHIP, Pine64, BBB, I would hope the following can be supported without any code changes:- get a handle onto a gpio pin called X, where X is the name used in the platform's documentation- get a handle onto SPI connection on bus N with chip select M, where N and M are numbers as used in the platform's documentation- get a handle onto I2C connection on bus N to device with address A, where N & A are numbers as documented
- get a handle onto I2C bus N, the use-case being to detect devices on that busFor each of the above handles, I expect that the expected HW capabilities can be invoked, such as setting bus speed, receiving edge-triggered interrupts, etc. A good question is (a) how to handle missing features in some implementation, and (b) how to handle additional features.A concrete example for a missing feature might be:- An SPI or I2C bus implementation that cannot set a speed, or that cannot set the desired speed- A gpio pin that does not support interruptsA concrete example for an extra feature that is not handled by the STD interface might be:- A gpio pin that supports edge triggered interrupts with debouncingA thought: would it be acceptable to access such extra features by querying the bus/pin handle for implemented interfaces?
Since I mentioned negative examples:- rule out the capability to query by capability, e.g., can't request "gimme a gpio pin that supports interrupts", "gimme an spi bus that has 2 chip selects", "gimme an spi bus that is unused", ...
Am I missing the point completely?
So there's 3 utilization levels:
- I know what I'm doing and I want this specific implementation, e.g. sysfs.NewSPI(busNumber, CS) and bitbang.NewSPI(clk, mosi, miso, cs).
- I know the concept of bus number but I don't care which driver implements it, host.NewSPI(busNumber, CS)
- I don't care, give me something that works, host.NewSPI() without any arguments. (It could live in protocols directly).
I implemented #1 and #3. I agree #2 may be interesting for users.
- get a handle onto I2C connection on bus N to device with address A, where N & A are numbers as documentedI want to call out that pio separates the bus i2c.Conn and the device as referenced by address i2c.Dev. It is very frequent that I²C have hardcoded addresses (FWIU unlike many SMBus devices which has reprogrammable addresses) so in that case, the device driver accepts an i2c.Conn, not an i2c.Dev.
A thought: would it be acceptable to access such extra features by querying the bus/pin handle for implemented interfaces?That's a good question. A concrete example: bcm283x supports edge triggering on all pins but Allwinner processors do not. The only way to know if a pin supports it or not on Allwinner is to try it, there's no way to query this information. I tried to make the error message clear about that.
I've now implemented all three. #2 and #3 are at https://godoc.org/github.com/maruel/dlibox/go/pio/hostIn the preceding example, you'd have to replace host.NewI2C() with host.NewI2CAuto().
On Wed, Mar 30, 2016 at 12:16 PM, sperberstephan via golang-dev <golan...@googlegroups.com> wrote:I like the idea of pushing an api! thank you for going that way.
today i saw your first contributions to exp/io/spi and have a question.
Why is a Transfer api better than a Reader/Writer interface?SPI is full duplex, so it doesn't really fit the Reader/Writer interface.Because the read/write in one SPI transaction is logically one thing,I don't think it's wise to break them up into two functions calls justto satisfy existing Go interfaces.
here: https://github.com/quinte17/spi
i did a hackup a few months ago so someone could try it out.
the drawback of my approach is that you always need to read after a write to clean up unneeded bytes.
an example hw driver which is using it isnt public yet and i dont have the time to reach a "running" state in the next months..
but reading and writing to and from my device is working.