I was thinking the same as you. I had one thought for a way to get this
functionality. Im actually pretty excited about it. Hope this makes sense.
We could declare a structure in hal_xxx.h
struct hal_xxx_device;
This could be the ³this" argument to all the hal_xxx functions.
Example
int hal_adc_read( struct hal_adc_device *padc);
In the hal_xxx_int.h we can define this like this.
Struct hal_xxx_driver Š (same as we have now)..
Struct hal_xxx_device {
Struct hal_xxx_driver *pdrv;
uint8_t devid; /* really only need 8 bits but
structures PAD */
uint8_t reserved[3];
}
And then define the shims as something like in hal_adc_int.c:
inline int hal_adc_read(struct hal_xxx_device *pd) {
If(pd && pd->pdrv && pd->pdrv->adc_read) {
return pd->pdrv->adc_read(pd->pdrv, pd->devid);
}
Return -1;
}
Whether these are inline or not I don¹t care as long as the are NOT
MACROS.
Now suppose I am writing a library. The library needs some hal devices to
do battery charging control.
Typedef struct my_battery_library_state {
Extern const struct hal_*gpio_device pon; // to set the
LED status of
the battery
Extern const struct hal_*adc_device ptemp; // to read the
battery
temperature
Extern const struct hal_*adc_device pvoltage; // to read the
battery
voltage
Extern const struct hal_*pwm_device pcurrent; // to set the
output
current level
}
The library would have to get these values from the BSP via function call.
Undefined variables won¹t consistently work in case the library author
intends there to be multiple battery chargers.
int bsp_get_power_library_led_gpio(int charger_instance, Struct
hal_gpio_device **out);
The bsp could typically implement as a const structure (since in most
cases this is probably fixed by the board design).
Const struct hal_adc_device temp_adc_for_battery_monitor = {
&samd21_adc_controller, /* this type of driver */
0, /* device ID 0 */
}
int bsp_get_power_library_led_gpio(int instance, const struct
hal_gpio_device **out) {
/* This BSP only knows how to provision one battery monitor. */
If(instance != 0) {
Return -1;
}
*out = & temp_adc_for_battery_monitor;
Return 0;
}
Analysis:
For the sake of analysis, I¹ll consider two things using the HAL:
1) A library which doesn¹t know at compile time which hal_device it will
use.
2) An application which does know at compile time.
* the structure definition could be totally hidden from the application
writer with the hal_shim. They just need to keep a pointer.
* This structure API is probably faster than the sysid API since we don¹t
have the BSP dispatch on every API call.
* This would use 8 bytes of code space for each device (const) used and
simple function to fetch the device from the library.
* This uses way less code space as we don¹t have all the shim code for
every hal. The hal shim could be a simple inline like above.
* This would dispense with the sysid concept which might make the whole
BSP mapping simpler. There¹s really no need to map all devices to
Some arbitrary sysid just so we can map them back to a device.
* An application could just extern the const structures and use
&my_hal_device in the code which would not take RAM. With the sysid
approach these
Would be constants and also take no RAM.
* A library would have to store space for all the pointers which is 4
bytes * number of devices. The sysid model would depend on the size of
the sysid.
Could be as small as 1 byte, so the sysid approach is BETTER for RAM
usage in a library. Imagine 20 GPIOs. Its either 20*1 = 20 bytes of RAM
(sysid) or
20 * 4 = 80 bytes of RAM.
The RAM usage concerns me a bit, but I think the idea of removing the
sysid seems like it would make things overall simpler for the user.
Comments?
On 3/23/16, 4:59 PM, "will sanfilippo" <[email protected]> wrote:
>I think the hardest part for me to ³get over² (if you will) is the fact
>that hal_xxx_init() does not return a pointer to something and that each
>of the API has to call the BSP function every time. However, I do
>understand why you would want to do it the way you did (at least I think
>I understand).
>
>I do wish that some of the API were a bit more abbreviated but that is
>only because i dont like typing :-)
>
>And btw, I am interested in hearing the answer to marko¹s questionŠ
>
>Will
>
>> On Mar 23, 2016, at 4:03 PM, marko kiiskila <[email protected]> wrote:
>>
>> Good stuff.
>>
>>> On Mar 22, 2016, at 5:21 PM, [email protected] wrote:
>>>
>>> All,
>>>
>>> I'm having so much fun with my newt. Please comment and help me
>>>improve this work.
>>>
>>> I've submitted two HAL API pull requests. They are to add new
>>>HAL_xxx.h files for two new sets of core functionality: ADC and PWM.
>>>
>>> When designing these hal_xxx.h interfaces, I considered the APIs from
>>>mbed-hal and from arduino-hal. I treated these as "enough" with a few
>>>caveats.
>>>
>>> 1. Generally, APIs that set specific state of devices seems hard to
>>>maintain and the system designer will have to know about them anyway.
>>>So I made Apis in the ADC hal to query the resolution and reference
>>>voltage rather than set them. They will be set by the MCU unless
>>>configurable, then they can be set by the BSP
>>> 2. There were duplicate ways in mbed to set PWM duty cycle. Rather
>>>than implement them all, I implemented a sufficient subset. Future
>>>versions could expand on this, or someone could write a helper library
>>>on top of it to covert between the various methods.
>>>
>>> https://github.com/apache/incubator-mynewt-core/pull/22 - this is a
>>>pull requests for the hal api for the Pulse Width Modulation. .
>>> https://github.com/apache/incubator-mynewt-core/pull/21 - this is the
>>>pull request for the hal API for the Analog to Digital Converters.
>>>
>>> Underlying HAL philosophy. I tried to following this philosophy when
>>>doing the interface.
>>>
>>> 1. A given device may support multiple channels (I.e. One ADC
>>>controller can sample 8 ports). Its needs a single driver since its one
>>>device multiplexed.
>>> 2. A number of different PWM/ADC devices can be supported at the same
>>>time.
>>> 3. Its possible and likely that there will be N instances of the same
>>>Device driver with just different state (e.g. Two ADC devices with 8
>>>channels each that are identical except for memory map).
>>
>> That¹s good.
>>
>>> So I implemented the HAL as follows:
>>>
>>> 1. for each hal_xxx.h there is a set of sysid (system_ids). These
>>>represent individual xxx (e.g. ADC) resources in the system. The
>>>adc_sysid and pwm_sydid are different number spaces.
>>> 2. The hal_xxx apis all take a sysid to reference the device and port.
>>> 3. There is an underlying hal_xxx.c implementation in the hal that
>>>resolves the sysid into a device_id and a driver interface. This is
>>>not the implementation of the hal, just a shim to call the BSP to get
>>>the driver and devid before calling back into the driver itself.
>>> 4. There is an underlying driver that implements the driver interface
>>>that can support up to N device Ids.
>>>
>>> As a concrete example, imagine a system with two ADC devices.
>>> 1. A SPI ADC with 10-bits precision and 8 pins
>>> 2. An internal MCU ADC with 8 bits precision 16 pins
>>>
>>> The BSP will be in charge of mapping a system ID into the device
>>>drivers and Ids. For example:
>>>
>>> 1. sysID 0-7 belong to SPIADC 0-7
>>> 2. sysID 8-24 belong to MCUADC 0-15
>>>
>>> Each individual driver will manage their devices and not need to know
>>>how they map to system Ids.
>>
>> How do you map from devid to particulars of HW configuration? For
>>example, how would tell that for my Olimex board, I want
>> to use pin X as input for my ADC? Or that my PWM for sysid 0 should go
>>out to pin Y?
>