On Wed, Jul 20, 2011 at 3:45 AM, Martin Guy <[hidden email]> wrote:
> We seem to be talking about four separate questions here: > - turning the _op primitives into separate functions > - allowing more precise low sampling frequencies, like Etna's > seismographs at 27.27Hz, for ADC (and I guess PWM). > - getting higher precision for duty cycles > - use of floating point Yep, sorry for complicating things a bit. As far as the PWM stuff goes, I was just using that as a reference point. Discussion of modifying that should go under another thread (resurrecting an earlier discussion or tracker issue). > > in order: > > - turning the _op primitives into separate functions > > The _op primitives seem to be imitating the ioctl() system call. > However, the ioctl system call, with hundreds of "operations" and a > single pointer parameter, was designed to collect hundreds of > different device-specific functions without having to have hundreds of > new system calls, and to allow the addition of new operations for new > hardware devices without having to change the system call interface > each time. In some Unix platforms the number of system calls is > limited by the hardware/instruction set, so they were forced to put > hundreds of different functions through a single hole. > eLua doesn't have that limitation, since functions are cheap and not > limited in number. Having one C function per operation also makes the > documentation clearer, since each operation has its own paragraph > instead of trying to document 6 different things mixed together. > > I assume there's no objection to turning these into separate functions > where possible. Bogdan may or may not object to doing that in other places. I think the reasons you have laid out, especially for ADC are pretty compelling. > > - allowing more precise low sampling frequencies like Etna's > seismographs at 27.27Hz, for ADC (and I guess PWM). > > I agree with not making floating point mandatory because one of eLua's > design objectives is to run on embedded platforms where code size can > be critical, and the floating point emulator adds 50K to the code > size. At the end of the day, I think this is mainly what needs to remain consistent. > > On 19 July 2011 01:35, James Snyder <[hidden email]> wrote: >> Indeed. My only point here is that so far we've intentionally kept >> types that are defined by Lua out of the platform layer so that they >> don't depend on Lua itself. > > Is that so that the C platform layer can be more easily re-used, say > as the platform-driver layer for a different language, or in custom > applications? I'm not sure what Dado and Bogdan might have to say on the matter, but given the way it is designed, it is structurally not horribly complicated to simply jump into a different main function on start and instead of starting Lua start something else and use the platform API. At certain points I've wanted to investigate the work that might be involved in separating out the lower level API and maybe build system as a multiplatform hardware abstraction layer that would make it easy to build C applications or use other VMs on top of the platform interface (like PyMite/Python-on-a-Chip) for example. Even if one isn't interested in Lua per-se the project does provide working examples of how to build with open toolchains across a variety of platforms. I'd like it if we can keep this flexibility. > >> There are two ways I can think of that could remain exclusively integer: >> 1) Passing optional divisors. For PWM, at one point, I suggested that >> we could allow finer resolution duty cyles by allowing other fractions >> than per cent such as /1000 (per mil) or 10000 (per basis point). >> Clock frequencies could have this as well, either fixed or arbitrary >> divisors passed as a parameter. > > I sort of like this idea - using an optional extra parameter in Lua > which by default would be 100 (for percent) for duty cycles. > The PWM setup function is currently: > frequency = pwm.setup(id, frequency, duty_cycle) > and, as far as duty cycle goes, this is OK, since it is the last > parameter, but it does nothing for frequencies unless we add two > optional parameters as divisors with default values of 100 and 1. Or 1 > and 100. It would need some adaptation for frequencies, since the values one is using are generally greater than 1 Hz, so the divisors would likely be smaller (or arbitrary). I haven't thought through an implementation to decide which would be easier. When you say two optional parameters are you meaning two different values that could be placed in a divisor parameter, or multiple additional parameters? I would be thinking of essentially having two parameters, the first being the numerator, the latter being the divisor with a default value of 1 if not passed. If the user passes a divisor then the platform will attempt to match the fractional frequency as closely as hardware allows. > >> 2) Something similar to POSIX's timer API > > POSIX is not a manual of good interface design. It was born as an > attempt to unite the different ways that manufacturers had extended > the original Unix kernel interfaces. All the new stuff since then > comes from design-by-committee, which usually produces awful > interfaces. Google POSIX timer API. Well, I don't mean to actually _implement_ the posix timer API, merely borrow the concept of second sized ticks, and mircosecond sized ticks as a compound value/structure or multiple parameters that could be passed. There's some horrible stuff in the POSIX API due to munging of various implementations together that you mention plus some things were clearly meant for different purposes in a different time, like POSIX RS-323 support, but I've come to appreciate it over dealing with some Win32 API stuff that just feels baroque by comparison. > >> which allows specification >> of a low and higher resolution component. i.e.: integer Hz + integer >> µHz or nHz. This came up a while ago, and wasn't for setting clock >> frequencies, more for timer intervals, but one could use it for clock >> frequencies as well. So that you would define that you wanted 27 Hz + >> 270000 µHz or something similar > >> Just a few thoughts. In some ways the gymnastics necessary for the >> user, interface implementer, and backend implementer start making the >> floating point version sound appealing :-) > > Not if floating point becomes necessary, since that goes against the > "embedded" objective of eLua. I'm definitely not suggesting tossing integer eLua :-) While most of the builds I use may be floating point ones, none of the platforms I'm using even have hardware FP, and there's a steep cost in code size. > > I only suggested that for when FP is already enabled in Lua. > If it isn't, the user has no way to express a floating point value > from Lua anyway, or to receive an FP value back. Right. Essentially the function for setting frequency would take a lua_Number (or something like it) and the code on the backend would be carefully written to work with both floating point and integer builds. > What's the lesser of these evils: > - no way to express low frequencies with precision, limited PWM duty > cycle resolution (current situation) > - higher precision in the FP version of Lua, lower precision in the > integer one, for duty cycles and low frequencies > - interfaces in Lua and C that express floating point values as some > combination of integers This is the tough question. Generally I would be trying to minimize pain for implementation and ugliness in the interface I think. The divisor/fraction stuff doesn't actually sound too bad to me, especially if it's done as an optional parameter with a default that's the same as current behavior. This way users can blithely ignore the functionality, and current code continues to work, but even the integer port can define non-integer frequencies. It does have one other complication, which is what do we return as the actual frequency acquired, especially on the integer port. I suppose it should be in the form passed, with two returned values, the first being the numerator and the second being the denominator. This wouldn't break existing code, and those who just receive one value would get just the numerator, plus we can still tell them, with reasonable accuracy, what frequency they got. Anyone else have opinions here? > > M > _______________________________________________ > eLua-dev mailing list > [hidden email] > https://lists.berlios.de/mailman/listinfo/elua-dev > _______________________________________________ eLua-dev mailing list [hidden email] https://lists.berlios.de/mailman/listinfo/elua-dev |
Dado Sutter |
Hi,
On Wed, Jul 20, 2011 at 15:14, James Snyder <[hidden email]> wrote:
Yes. Personally I would prefer to have an independent, generic and as portable as possible platform layer. Marcelo Politzer and Thiago Naves (@LedLab) have been experimenting with this concept and extending implementations to other targets but some implementation details have been making it harder. It would be nice to hear them here for some more details, in a different thread. I would love to be able to use the platform layer for C-only applications. It is certainly possible to do it today but not in an easy way IMHO.
+1 The I can't say now if this is the best final solution, in the sense that it should be adopted for similar needs in the future too. But it sounds like an elegant and simple solution, although the return-value issue will seem pretty strange on the higher level. But I can't think of anything better now, without forcing some major refactoring on the platform layer.
Best Dado
_______________________________________________ eLua-dev mailing list [hidden email] https://lists.berlios.de/mailman/listinfo/elua-dev |
On Wed, Jul 20, 2011 at 5:38 PM, Dado Sutter <[hidden email]> wrote:
> Hi, > > On Wed, Jul 20, 2011 at 15:14, James Snyder <[hidden email]> wrote: >> >> On Wed, Jul 20, 2011 at 3:45 AM, Martin Guy <[hidden email]> wrote: >> > We seem to be talking about four separate questions here: >> > - turning the _op primitives into separate functions >> > - allowing more precise low sampling frequencies, like Etna's >> > seismographs at 27.27Hz, for ADC (and I guess PWM). >> > - getting higher precision for duty cycles >> > - use of floating point >> >> Yep, sorry for complicating things a bit. As far as the PWM stuff >> goes, I was just using that as a reference point. Discussion of >> modifying that should go under another thread (resurrecting an earlier >> discussion or tracker issue). >> >> > >> > in order: >> > >> > - turning the _op primitives into separate functions >> > >> > The _op primitives seem to be imitating the ioctl() system call. >> > However, the ioctl system call, with hundreds of "operations" and a >> > single pointer parameter, was designed to collect hundreds of >> > different device-specific functions without having to have hundreds of >> > new system calls, and to allow the addition of new operations for new >> > hardware devices without having to change the system call interface >> > each time. In some Unix platforms the number of system calls is >> > limited by the hardware/instruction set, so they were forced to put >> > hundreds of different functions through a single hole. >> > eLua doesn't have that limitation, since functions are cheap and not >> > limited in number. Having one C function per operation also makes the >> > documentation clearer, since each operation has its own paragraph >> > instead of trying to document 6 different things mixed together. >> > >> > I assume there's no objection to turning these into separate functions >> > where possible. >> >> Bogdan may or may not object to doing that in other places. I think >> the reasons you have laid out, especially for ADC are pretty >> compelling. >> >> > >> > - allowing more precise low sampling frequencies like Etna's >> > seismographs at 27.27Hz, for ADC (and I guess PWM). >> > >> > I agree with not making floating point mandatory because one of eLua's >> > design objectives is to run on embedded platforms where code size can >> > be critical, and the floating point emulator adds 50K to the code >> > size. >> >> At the end of the day, I think this is mainly what needs to remain >> consistent. >> >> > >> > On 19 July 2011 01:35, James Snyder <[hidden email]> wrote: >> >> Indeed. My only point here is that so far we've intentionally kept >> >> types that are defined by Lua out of the platform layer so that they >> >> don't depend on Lua itself. >> > >> > Is that so that the C platform layer can be more easily re-used, say >> > as the platform-driver layer for a different language, or in custom >> > applications? >> >> I'm not sure what Dado and Bogdan might have to say on the matter, but >> given the way it is designed, it is structurally not horribly >> complicated to simply jump into a different main function on start and >> instead of starting Lua start something else and use the platform API. >> At certain points I've wanted to investigate the work that might be >> involved in separating out the lower level API and maybe build system >> as a multiplatform hardware abstraction layer that would make it easy >> to build C applications or use other VMs on top of the platform >> interface (like PyMite/Python-on-a-Chip) for example. Even if one >> isn't interested in Lua per-se the project does provide working >> examples of how to build with open toolchains across a variety of >> platforms. I'd like it if we can keep this flexibility. > > Yes. Personally I would prefer to have an independent, generic and as > portable as possible platform layer. Marcelo Politzer and Thiago Naves > (@LedLab) have been experimenting with this concept and extending > implementations to other targets but some implementation details have been > making it harder. > It would be nice to hear them here for some more details, in a different > thread. > I would love to be able to use the platform layer for C-only applications. > It is certainly possible to do it today but not in an easy way IMHO. > >> >> > >> >> There are two ways I can think of that could remain exclusively >> >> integer: >> >> 1) Passing optional divisors. For PWM, at one point, I suggested that >> >> we could allow finer resolution duty cyles by allowing other fractions >> >> than per cent such as /1000 (per mil) or 10000 (per basis point). >> >> Clock frequencies could have this as well, either fixed or arbitrary >> >> divisors passed as a parameter. >> > >> > I sort of like this idea - using an optional extra parameter in Lua >> > which by default would be 100 (for percent) for duty cycles. >> > The PWM setup function is currently: >> > frequency = pwm.setup(id, frequency, duty_cycle) >> > and, as far as duty cycle goes, this is OK, since it is the last >> > parameter, but it does nothing for frequencies unless we add two >> > optional parameters as divisors with default values of 100 and 1. Or 1 >> > and 100. >> >> It would need some adaptation for frequencies, since the values one is >> using are generally greater than 1 Hz, so the divisors would likely be >> smaller (or arbitrary). I haven't thought through an implementation >> to decide which would be easier. >> >> When you say two optional parameters are you meaning two different >> values that could be placed in a divisor parameter, or multiple >> additional parameters? I would be thinking of essentially having two >> parameters, the first being the numerator, the latter being the >> divisor with a default value of 1 if not passed. If the user passes a >> divisor then the platform will attempt to match the fractional >> frequency as closely as hardware allows. >> >> > >> >> 2) Something similar to POSIX's timer API >> > >> > POSIX is not a manual of good interface design. It was born as an >> > attempt to unite the different ways that manufacturers had extended >> > the original Unix kernel interfaces. All the new stuff since then >> > comes from design-by-committee, which usually produces awful >> > interfaces. Google POSIX timer API. >> >> Well, I don't mean to actually _implement_ the posix timer API, merely >> borrow the concept of second sized ticks, and mircosecond sized ticks >> as a compound value/structure or multiple parameters that could be >> passed. >> >> There's some horrible stuff in the POSIX API due to munging of various >> implementations together that you mention plus some things were >> clearly meant for different purposes in a different time, like POSIX >> RS-323 support, but I've come to appreciate it over dealing with some >> Win32 API stuff that just feels baroque by comparison. >> >> > >> >> which allows specification >> >> of a low and higher resolution component. i.e.: integer Hz + integer >> >> µHz or nHz. This came up a while ago, and wasn't for setting clock >> >> frequencies, more for timer intervals, but one could use it for clock >> >> frequencies as well. So that you would define that you wanted 27 Hz + >> >> 270000 µHz or something similar >> > >> >> Just a few thoughts. In some ways the gymnastics necessary for the >> >> user, interface implementer, and backend implementer start making the >> >> floating point version sound appealing :-) >> > >> > Not if floating point becomes necessary, since that goes against the >> > "embedded" objective of eLua. >> >> I'm definitely not suggesting tossing integer eLua :-) While most of >> the builds I use may be floating point ones, none of the platforms I'm >> using even have hardware FP, and there's a steep cost in code size. >> >> > >> > I only suggested that for when FP is already enabled in Lua. >> > If it isn't, the user has no way to express a floating point value >> > from Lua anyway, or to receive an FP value back. >> >> Right. Essentially the function for setting frequency would take a >> lua_Number (or something like it) and the code on the backend would be >> carefully written to work with both floating point and integer builds. >> >> >> > What's the lesser of these evils: >> > - no way to express low frequencies with precision, limited PWM duty >> > cycle resolution (current situation) >> > - higher precision in the FP version of Lua, lower precision in the >> > integer one, for duty cycles and low frequencies >> > - interfaces in Lua and C that express floating point values as some >> > combination of integers >> >> This is the tough question. Generally I would be trying to minimize >> pain for implementation and ugliness in the interface I think. > > +1 > >> >> The >> divisor/fraction stuff doesn't actually sound too bad to me, >> especially if it's done as an optional parameter with a default that's >> the same as current behavior. This way users can blithely ignore the >> functionality, and current code continues to work, but even the >> integer port can define non-integer frequencies. >> >> It does have one other complication, which is what do we return as the >> actual frequency acquired, especially on the integer port. I suppose >> it should be in the form passed, with two returned values, the first >> being the numerator and the second being the denominator. This >> wouldn't break existing code, and those who just receive one value >> would get just the numerator, plus we can still tell them, with >> reasonable accuracy, what frequency they got. >> >> Anyone else have opinions here? > > I can't say now if this is the best final solution, in the sense that it > should be adopted for similar needs in the future too. But it sounds like an > elegant and simple solution, although the return-value issue will seem > pretty strange on the higher level. But I can't think of anything better > now, without forcing some major refactoring on the platform layer. The fundamental issue there is returning a potentially non-integer frequency through integer values, since they've been requested using integer numerator and denominator. The are some possible restrictions that could be placed on the values returned: 1) for cases where the divisor is 1 or not provided, the returned divisor will always be 1 (to preserve existing behavior, even if the returned frequency can't precisely be represented as an integer value) For all other cases there are two options: 2) Always use the same divisor as passed in. In some ways this sounds a bit stupid to return the divisor in this case, but I think I prefer the idea that the units of the return value can be interpreted without having to refer to the passed values. The biggest downside to this is that there's a potential loss of precision if the divisor doesn't match up well with the actual clock frequency. 3) The divisor returned is determined by the low-level code in order to provide best accuracy in conveying the frequency selected given the available variable size for storing the values. Optionally, the low level implementor can use the same divisor originally passed. This would potentially allow providing the highest fidelity in conveying the frequency selected, and it might also be the easiest way to make this work given that you could probably just base the values directly on the main system clock, divisors applied to that, and the timer reload value and you provide the precise fraction for the clock frequency. As a potential additional option, 1) could be revised such that if no divisor is passed, return divisor is always 1, but if a 1 is passed than the divisor is allowed to float as in 3) above. This would provide consistency with current behavior and maximum potential return value accuracy. With either of these, determination of the clock frequency on a floating point MCU is one division operation away. On integer units, some more complex math would need to be employed to ensure the fraction is within desired tolerances. As far as rethinking the API is concerned, I think either way we come back to an issue of fractional frequencies with integer expressions problem, unless the integer builds are to have different functionality than the floating point builds. -jsnyder > >> >> > >> > M > > Best > Dado > > > > >> >> > _______________________________________________ >> > eLua-dev mailing list >> > [hidden email] >> > https://lists.berlios.de/mailman/listinfo/elua-dev >> > >> _______________________________________________ >> eLua-dev mailing list >> [hidden email] >> https://lists.berlios.de/mailman/listinfo/elua-dev > > > _______________________________________________ > eLua-dev mailing list > [hidden email] > https://lists.berlios.de/mailman/listinfo/elua-dev > > eLua-dev mailing list [hidden email] https://lists.berlios.de/mailman/listinfo/elua-dev |
Martin Guy |
On 25 July 2011 20:17, James Snyder <[hidden email]> wrote:
> On Wed, Jul 20, 2011 at 5:38 PM, Dado Sutter <[hidden email]> wrote: >>> > - allowing more precise low sampling frequencies, like Etna's >>> > seismographs at 27.27Hz, for ADC (and I guess PWM). >>> > - getting higher precision for duty cycles >>> > - use of floating point >>> This is the tough question. Generally I would be trying to minimize >>> pain for implementation and ugliness in the interface I think. >> +1 > 2) Always use the same divisor as passed in. There is already an industry convention for passing floating point values as integers. It's called fixed point arithmetic, and that is already in use in eLua: time delays are fixed-point with radix 1000000 (microseconds), duty cycles are fixed point with radix 100. The easiest-to-understand interface would be to allow people to specify the radix of the fixed-point values they are passing, and receive values back in that same radix they asked for. In eLua we have three separate usage cases of arbitrary numbers being passed/returned as integers: 1. high frequencies for clocks, used in PWM clock, timer clock (also used in ADC), maybe also for I2C and SPI clocks and (even more maybe) UART baud rates. 2. low frequencies for ADC sampling rates and PWM frequencies 3. duty cycles for PWM Let's look at these: The high frequencies already seem precise enough to me for our purposes, since they're in the 100,000s and 1,000,000, already giving a precision of 1 part in 100.000 except for baud rates, but I've only ever seen integer baud rates, and with a minimum of 110. Checking the actual baud rate that was set and caring about whether it is less than 0.5% different is Book 3 stuff, not really a design goal for eLua if it will make things more complicated. The resolution of the clock that the low frequencies depend on is already returned by their setclock() (or setup()) functions, which return the actual frequency set. This may be very different from the one requested, often a power-of-two divisor of some fixed system clock rate, still in the 100,000s or 1,000,000s range. [[[ As a separate issue, I notice that there is a function in the ADC module, also called setclock, that does something different from the other setclock() functions. i2c, spi and tmr.setclock() all set their own underlying hardware independently of each other, and they all have a setclock() function that works the same way as the others (nice!). adc.setclock() is different: it sets a lower frequency sampling rate based on a number of cycles of a chosen timer. The user can find the real frequency of the timer in question, since they can called tmr.setclock() (or tmr.getclock()) on the timer they are associating with the adc channel. I suggest adc.setclock() be renamed to something different, since it's a different function with different parameters from the other setclock() functions. In fact, it's more similar in what it does to the other modules' *.setup() functions. ]]] In the last case, the duty cycles' true precision is the period of one cycle of the PWM waveform expressed in PWM clock cycles. That information is available to the user through pwm.setclock() and getclock() interfaces. However, the underlying granularity of the available duty cycles as a fraction of the PWM wave period changes whenever you change the PWM frequency. Users who want to get the maximum precision from their PWM duty cycle will need to do some interesting math coding to achieve this, but it should be possible. The Lua code to do that would be good as an advanced example in the eLua manual. Such power users would need to figure out the period in clock cycles of the PWM at the actual frequency that was set, then set the duty cycle with a radix that is equal to that. That is awkward in the current pwm.setup() function, since it takes duty cycle and frequency at the same time, so they'd have to call it twice, once with the wrong duty cycle and once with the right one once you've found out the actual PWM frequency. Furthermore, on AVR32, being told to set both the frequency and duty cycle causes a glitch in the output because the stupid hardware interface can only set either the frequency or the duty cycle once per output cycle so you get one cycle either at the new frequency and old duty cycle or at the old frequency with the new duty cycle. Worse yet, pwm.setup(id, frequency, duty_cycle) takes two parameters both of which are in the proposed fixed-point format, which is ugly. Even in the naive case where you get higher precision by giving a duty cycle radix of 1000 or 1000000, you'd need to supply a bogus extra parameter for the undesired precision of the PWM output frequency. These are three arguments in favour of splitting pwm.setup() into two functions like pwm.setfreq() and pwm.setduty(). A fourth is that all the other modules' setup() functions are usually called once at start of program, then the output is varied as the program runs using module-specific functions, whereas the current pwm.setup() is used repeatedly while the programming is running to vary the output. > 3) The divisor returned is determined by the low-level code in order > to provide best accuracy in conveying the frequency selected given the > available variable size for storing the values. Trying to select the most precise pair of integers to represent an arbitrary value is actually quite a difficult math-theoretical problem, not to mention the difficullty in the user code that would be necessary to correctly handle them. > As a potential additional option, 1) could be revised such that if no > divisor is passed, return divisor is always 1, but if a 1 is passed > than the divisor is allowed to float as in 3) above. We already have the first part, and the defaults are 100 for the duty cycle, 1,000,000 for delays and 1 for frequencies. I hink the second idea is a nightmare for us and for the users. > provide consistency with current behavior and maximum potential return > value accuracy. I don't value euther of these. On the one hand I'm not afraid to change the platform (or the Lua) interfaces if that gives a worthwhile improvement in what you can do or how easy it is to understand, and the current interface did not consider the practical problems that we are trying to improve now. I seem to remember someone saying that, before we release eLua 1.0, maintaining backward compatability is not a problem, especially given the small user base. Of course, we don't want to break things for no significant gain. I don't think total laboratory-standard mathematical accuracy is as important a degisn goal for eLua as being simple to use and having an Lua interface that is pleasant to use and easy to understand. It's a design tradeoff, since lab accuracy requires a different set of interfaces than one that is easy-to-use but adequate for most purposes. Already, moving to optional variable radix fixed point for some values is a step towards higher accuracy and away from easy-to-use, but seems to bring some advantages. > As far as rethinking the API is concerned, I think either way we come > back to an issue of fractional frequencies with integer expressions > problem, unless the integer builds are to have different functionality > than the floating point builds. That was my first idea for low frequencies: optional FP, but I think your optional divisor (or radix in fixed-point math language) solution may be a better one. M _______________________________________________ eLua-dev mailing list [hidden email] https://lists.berlios.de/mailman/listinfo/elua-dev |
On Thu, Jul 28, 2011 at 12:01 PM, Martin Guy <[hidden email]> wrote:
> On 25 July 2011 20:17, James Snyder <[hidden email]> wrote: >> On Wed, Jul 20, 2011 at 5:38 PM, Dado Sutter <[hidden email]> wrote: >>>> > - allowing more precise low sampling frequencies, like Etna's >>>> > seismographs at 27.27Hz, for ADC (and I guess PWM). >>>> > - getting higher precision for duty cycles >>>> > - use of floating point > >>>> This is the tough question. Generally I would be trying to minimize >>>> pain for implementation and ugliness in the interface I think. >>> +1 > >> 2) Always use the same divisor as passed in. > > > There is already an industry convention for passing floating point > values as integers. It's called fixed point arithmetic, and that is > already in use in eLua: time delays are fixed-point with radix 1000000 > (microseconds), duty cycles are fixed point with radix 100. The > easiest-to-understand interface would be to allow people to specify > the radix of the fixed-point values they are passing, and receive > values back in that same radix they asked for. Right, perhaps with a range of radices that make sense for the application, since using microseconds would be overkill and would limit sampling rates to the kilohertz range with 32-bit integers, I think. > > In eLua we have three separate usage cases of arbitrary numbers being > passed/returned as integers: > 1. high frequencies for clocks, used in PWM clock, timer clock (also > used in ADC), maybe also for I2C and SPI clocks and (even more maybe) > UART baud rates. > 2. low frequencies for ADC sampling rates and PWM frequencies These don't have to be low frequencies unless hundreds of kilohertz or an order of magnitude higher is considered low :-) Of course, the distinction is relative and for the most part, especially for ADC, I would expect sampling rates to be low given the rate of bytecode execution in Lua and the cost of doing operations on samples. PWM can certainly run at a few megahertz on many of the platforms we use, though this doesn't provide excellent resolution in duty cycle. That said, higher frequencies can be convenient when you want to use it as a cheap DAC with an LPF, or to even generate audio. > 3. duty cycles for PWM > > Let's look at these: > > The high frequencies already seem precise enough to me for our > purposes, since they're in the 100,000s and 1,000,000, already giving > a precision of 1 part in 100.000 except for baud rates, but I've only > ever seen integer baud rates, and with a minimum of 110. Checking the > actual baud rate that was set and caring about whether it is less > than 0.5% different is Book 3 stuff, not really a design goal for eLua > if it will make things more complicated. I would agree. > > The resolution of the clock that the low frequencies depend on is > already returned by their setclock() (or setup()) functions, which > return the actual frequency set. This may be very different from the > one requested, often a power-of-two divisor of some fixed system clock > rate, still in the 100,000s or 1,000,000s range. Yep. Some platforms probably could do frequency matching a little better, depending on whether there are multiple divisors that can be mixed to generate the clock, but that's a separate issue that depends on the platform. When I've noticed that there are deficiencies of this sort, I've attempted to improve it. stm32, for example, was adjusting only one clock divisor to get its frequencies which both limited the range (couldn't go below around 1Khz) and limited the resolution of the selectable frequencies. There are established routines, sometimes provided by the vendor, for properly selecting clocks that should be used when finer integer resolution in clocks is important. > > [[[ > As a separate issue, I notice that there is a function in the ADC > module, also called setclock, that does something different from the > other setclock() functions. > i2c, spi and tmr.setclock() all set their own underlying hardware > independently of each other, and they all have a setclock() function > that works the same way as the others (nice!). > adc.setclock() is different: it sets a lower frequency sampling rate > based on a number of cycles of a chosen timer. The user can find the > real frequency of the timer in question, since they can called > tmr.setclock() (or tmr.getclock()) on the timer they are associating > with the adc channel. > I suggest adc.setclock() be renamed to something different, since it's > a different function with different parameters from the other > setclock() functions. In fact, it's more similar in what it does to > the other modules' *.setup() functions. > ]]] This is a good point. It does multiple things... It configures the ADC to trigger on the timer passed, and then sets up a timer so that it has a period close to the requested clock. Setup might be OK, but I actually have a minor dislike for the convention of the "setup" function since they take different parameters depending on the peripheral that's being setup. For consistency sake this would make sense though. As far as alternatives, maybe something like settimer or setuptimer might make more sense? Alternately if we wanted ADC to support other trigger sources, it could be named something like settrigger or setuptrigger? Ideally, maybe the tmr module could be adapted so that it's functions could be used to set up the period, and then the ADC function could just ensure that the ADC would trigger on match (or whatever event it can use from the timer). Besides some special configuration for timers to sometimes generate events or certain things like that, the current tmr API doesn't lend itself to configuring cycle durations. One could add another function, similar to set_match_int, or adapt set_match_int/rename it to allow it to configure hardware peripheral triggers in addition to interrupts. > > In the last case, the duty cycles' true precision is the period of one > cycle of the PWM waveform expressed in PWM clock cycles. That > information is available to the user through pwm.setclock() and > getclock() interfaces. > However, the underlying granularity of the available duty cycles as a > fraction of the PWM wave period changes whenever you change the PWM > frequency. > Users who want to get the maximum precision from their PWM duty cycle > will need to do some interesting math coding to achieve this, but it > should be possible. As I was mentioning before, there are some established/recommended routines that vendors provide for doing things like this. For STM32, for example, we do the following for PWM and I think ADC frequencies: period = (base peripheral clock) / desired_clock; prescaler = ( period / 65536 ) + 1; period = period/prescaler; period (automatic reload value) and prescaler are then set to registers after subtracting 1. The resulting clock can then be computed as: (base peripheral clock) / prescaler / period With a 32 MHz clock this gives error along these lines along with minimum step sizes (100/period) http://i.imgur.com/YwqB8.png This is a little platform specific in terms of register sizes and max prescaler sizes, but something similar could be adapted for most platforms. > The Lua code to do that would be good as an > advanced example in the eLua manual. Such power users would need to > figure out the period in clock cycles of the PWM at the actual > frequency that was set, then set the duty cycle with a radix that is > equal to that. That is awkward in the current pwm.setup() function, > since it takes duty cycle and frequency at the same time, so they'd > have to call it twice, once with the wrong duty cycle and once with > the right one once you've found out the actual PWM frequency. I think it would be nice to show how to do the manual approach from Lua and to maybe make the interface a little less awkward. I also like making the default behavior one that doesn't require that the user write an algorithm to set PWM up as long as we can do a pretty good job automatically. > > Furthermore, on AVR32, being told to set both the frequency and duty > cycle causes a glitch in the output because the stupid hardware > interface can only set either the frequency or the duty cycle once per > output cycle so you get one cycle either at the new frequency and old > duty cycle or at the old frequency with the new duty cycle. Interesting. I assume that you can temporarily stop the peripheral, set the values and then start it again though? Sure you might lose some clock cycles, but I'm not sure we're guaranteeing that PWM is continuous through successive calls. Not sure if we would need to do so either. > > Worse yet, pwm.setup(id, frequency, duty_cycle) takes two parameters > both of which are in the proposed fixed-point format, which is ugly. > Even in the naive case where you get higher precision by giving a duty > cycle radix of 1000 or 1000000, you'd need to supply a bogus extra > parameter for the undesired precision of the PWM output frequency. This is another reason why I'm not a massive fan of "setup" functions, I kindof think this should be two separate functions. Would we really need a radix other than 1 for PWM frequencies though? I'm not sure if non-integer frequencies are ever needed. > > These are three arguments in favour of splitting pwm.setup() into two > functions like pwm.setfreq() and pwm.setduty(). A fourth is that all > the other modules' setup() functions are usually called once at start > of program, then the output is varied as the program runs using > module-specific functions, whereas the current pwm.setup() is used > repeatedly while the programming is running to vary the output. Yeah, I've felt this way for a while now. Bogdan, any comments on this front? We're starting to get into a number of API changes and proposals here :-) > >> 3) The divisor returned is determined by the low-level code in order >> to provide best accuracy in conveying the frequency selected given the >> available variable size for storing the values. > > Trying to select the most precise pair of integers to represent an > arbitrary value is actually quite a difficult math-theoretical > problem, not to mention the difficullty in the user code that would be > necessary to correctly handle them. This is true. It's an optimization problem. There are some simple single-shot approaches that have what are most likely acceptable error levels like the one mentioned earlier. Really choosing things optimally is harder and is iterative unless one has a lookup table. I'd be OK with selecting one that's not perfect, but could be adjusted a little to provide low error in the ranges that the peripherals are generally used. > >> As a potential additional option, 1) could be revised such that if no >> divisor is passed, return divisor is always 1, but if a 1 is passed >> than the divisor is allowed to float as in 3) above. > > We already have the first part, and the defaults are 100 for the duty > cycle, 1,000,000 for delays and 1 for frequencies. I hink the second > idea is a nightmare for us and for the users. > >> provide consistency with current behavior and maximum potential return >> value accuracy. > > I don't value euther of these. On the one hand I'm not afraid to > change the platform (or the Lua) interfaces if that gives a worthwhile > improvement in what you can do or how easy it is to understand, and > the current interface did not consider the practical problems that we > are trying to improve now. > I seem to remember someone saying that, before we release eLua 1.0, > maintaining backward compatability is not a problem, especially given > the small user base. Of course, we don't want to break things for no > significant gain. I could have worded that better, and I agree with what you just said here, I believe I was just saying that it was a plus that it wouldn't break existing functionality, which does have some value. > I don't think total laboratory-standard mathematical accuracy is as > important a degisn goal for eLua as being simple to use and having an > Lua interface that is pleasant to use and easy to understand. It's a > design tradeoff, since lab accuracy requires a different set of > interfaces than one that is easy-to-use but adequate for most > purposes. I don't mean to say that they really need to be optimal, we would end up wasting computational time and memory with some iterative optimization algorithm or lookup tables. The reason that I said "maximum _potential_ return value accuracy" was that this would give the backend implementer the most precise representation of the frequency the user wanted. Again, I would be happy with a simple non-iterative algorithm with a known worst-case that is acceptable. > Already, moving to optional variable radix fixed point for some > values is a step towards higher accuracy and away from easy-to-use, > but seems to bring some advantages. > >> As far as rethinking the API is concerned, I think either way we come >> back to an issue of fractional frequencies with integer expressions >> problem, unless the integer builds are to have different functionality >> than the floating point builds. > > That was my first idea for low frequencies: optional FP, but I think > your optional divisor (or radix in fixed-point math language) solution > may be a better one. It's a tradeoff, but since we already have some pre-selected radix notation, I think making it an option that the user can provide to select different radices make sense. I would tend to agree that just going with fixed-point (powers of 10) might be the least messy to deal with, and also makes for easy mental digesting of parameters and return values for the programmer. _______________________________________________ eLua-dev mailing list [hidden email] https://lists.berlios.de/mailman/listinfo/elua-dev |
Martin Guy |
On 29 July 2011 02:51, James Snyder <[hidden email]> wrote:
> > In eLua we have three separate usage cases of arbitrary numbers being > > passed/returned as integers: > > 1. high frequencies for clocks, used in PWM clock, timer clock (also > > used in ADC), maybe also for I2C and SPI clocks and (even more maybe) > > UART baud rates. > > 2. low frequencies for ADC sampling rates and PWM frequencies > > These don't have to be low frequencies unless hundreds of kilohertz or > an order of magnitude higher is considered low. Sorry, I obviously wasn't very clear in my explanation. I'm trying to distinguish what seem to be three different types of data object in the platform interface that have have different characteristics and require differ kinds of reasoning about them: 1. clock frequencies derived from a crystal, that are used in pwm.setclock(), tmr.setclock() and elsewhere; 2. frequencies that are derived from these by dividing these down, usually with a hardware counter that resets every N clock cycles, as used in adc sampling rates, pwm output frequencies(), free-running timer interrupts and so on. I just used the words "high" and "low" because their typical ranges of frequencies are higher and lower. Shall we call then "clocking frequencies" and "output frequencies" or something, to avoid the subjective associations of the words "high" and "low"? By separating the different cases, we can reason more usefully about each one, rather than talking about a single solution to three different types of object. It may be that what I have called "low" frequencies are not worth making more precise, which would just leave PWM duty cycles. > PWM can > certainly run at a few megahertz on many of the platforms we use, > though this doesn't provide excellent resolution in duty cycle. As well as 1 or 2 hertz if used to flash christmas lights, where one would be able to produce a slowly-accelerating flashing rate. What I mean, again, is that these are lower frequencies derived from the clocking rate. Of course, the divisor used to produce the output frequency could even be 1, giving the same frequency as the clock rate, but in terms of the program interface, that data object is still an output frequency derived from the clocking frequency. > I actually have a minor dislike for the convention of the "setup" > function since they take different parameters depending on the > peripheral that's being setup. I have no problem aith that, since different devices need setting up with different parameters. For me, if the function sets the interface up (i.e. initializes it to make it ready for subsequent use), then "setup" is OK. I am concerned by a *.setup() function that doesn't set the interface up, but is used to drive it! > As far as alternatives, maybe something like settimer > or setuptimer might make more sense? Alternately if we wanted ADC to > support other trigger sources, it could be named something like > settrigger or setuptrigger? OK, I'll leave you to think about that. I'm happy as long as foo.setup() sets foo up ready to be used, foo.setclock() sets foo's clock frequency and so on, so that when you read the Lua code, it makes some kind of sense. > > Furthermore, on AVR32, being told to set both the frequency and duty > > cycle causes a glitch in the output because the stupid hardware > > interface can only set either the frequency or the duty cycle once per > > output cycle so you get one cycle either at the new frequency and old > > duty cycle or at the old frequency with the new duty cycle. > > Interesting. I assume that you can temporarily stop the peripheral, > set the values and then start it again though? Sure you might lose > some clock cycles, but I'm not sure we're guaranteeing that PWM is > continuous through successive calls. Not sure if we would need to do > so either. The most demanding application I can think of is when it is being used to signal data, such as in TV remote controls, where a blip at the wrong frequency or duty cycle might make it do the wrong thing. Yes, you can also stop the output, reprogram it and start again, though for applications like using PWM to implement an audio DAC, that causes an audible bubble in the output. I like producing good quality output when that is possible, so that when people push the envelope, the system doesn't let them down. > > Worse yet, pwm.setup(id, frequency, duty_cycle) takes two parameters > > both of which are in the proposed fixed-point format, which is ugly. > > Even in the naive case where you get higher precision by giving a duty > > cycle radix of 1000 or 1000000, you'd need to supply a bogus extra > > parameter for the undesired precision of the PWM output frequency. > > This is another reason why I'm not a massive fan of "setup" functions, > I kindof think this should be two separate functions. OK > Would we really need a radix other than 1 for PWM frequencies though? > I'm not sure if non-integer frequencies are ever needed. Flashing christmas lights? > > These are three arguments in favour of splitting pwm.setup() into two > > functions like pwm.setfreq() and pwm.setduty(). A fourth is that all > > the other modules' setup() functions are usually called once at start > > of program, then the output is varied as the program runs using > > module-specific functions, whereas the current pwm.setup() is used > > repeatedly while the programming is running to vary the output. > > Yeah, I've felt this way for a while now. Bogdan, any comments on > this front? We're starting to get into a number of API changes and > proposals here :-) > >> 3) The divisor returned is determined by the low-level code in order > >> to provide best accuracy in conveying the frequency selected given the > >> available variable size for storing the values. > > > > Trying to select the most precise pair of integers to represent an > > arbitrary value is actually quite a difficult math-theoretical > > problem, not to mention the difficullty in the user code that would be > > necessary to correctly handle them. > > This is true. It's an optimization problem. There are some simple > single-shot approaches that have what are most likely acceptable error > levels like the one mentioned earlier. Really choosing things > optimally is harder and is iterative unless one has a lookup table. > I'd be OK with selecting one that's not perfect, but could be adjusted > a little to provide low error in the ranges that the peripherals are > generally used. > > I don't mean to say that they really need to be optimal, we would end > up wasting computational time and memory with some iterative > optimization algorithm or lookup tables. The reason that I said > "maximum _potential_ return value accuracy" was that this would give > the backend implementer the most precise representation of the > frequency the user wanted. Again, I would be happy with a simple > non-iterative algorithm with a known worst-case that is acceptable. I would drop this idea and keep it simple, returning values in the same radix as the user passed, where that is appropriate. Or, if we drop the proposal to make "output" frequencies more precise (for the 27.27Hz ADCs in seismographs and for the xmas lights), that only leaves PWM duty cycles, and in that case we don't return the "actual" duty cycle set at all, so the problem goes away. > I would tend to agree that just > going with fixed-point (powers of 10) might be the least messy to deal > with. I have no problem if they want to express something in 360ths for some reason of their own, or, in units of the period of the PWM cycle counter to get max precision. Our code would be the same in any case, without needing to impose artificial restrictions. Can we focus on concrete proposals and decide? I'm in favour of adding optional extra parameter after duty cycles to get higher precision (indeed, hardware precision if you pick the right radix) I'm ambivalent about adding radix to frequencies for higher-precision low frequencies. It would be nice. Lastly, these kinds of changes are hard because they have to be implemented on every platform simultaneously, which either needs someone who has all the platforms, or some tight collaboration. James, have you been able to test on an ARm platform that patch to split platform_adc_op() M _______________________________________________ eLua-dev mailing list [hidden email] https://lists.berlios.de/mailman/listinfo/elua-dev |
Martin Guy |
On 31 July 2011 07:32, Martin Guy <[hidden email]> wrote:
> James, have you been able to test on an ARm platform that patch to > split platform_adc_op() Sorry, I mean platform_pwm_op() m _______________________________________________ eLua-dev mailing list [hidden email] https://lists.berlios.de/mailman/listinfo/elua-dev |
In reply to this post by Martin Guy
On Sun, Jul 31, 2011 at 12:32 AM, Martin Guy <[hidden email]> wrote:
> On 29 July 2011 02:51, James Snyder <[hidden email]> wrote: > >> > In eLua we have three separate usage cases of arbitrary numbers being >> > passed/returned as integers: >> > 1. high frequencies for clocks, used in PWM clock, timer clock (also >> > used in ADC), maybe also for I2C and SPI clocks and (even more maybe) >> > UART baud rates. >> > 2. low frequencies for ADC sampling rates and PWM frequencies >> >> These don't have to be low frequencies unless hundreds of kilohertz or >> an order of magnitude higher is considered low. > > Sorry, I obviously wasn't very clear in my explanation. > I'm trying to distinguish what seem to be three different types of > data object in the platform interface that have have different > characteristics and require differ kinds of reasoning about them: > 1. clock frequencies derived from a crystal, that are used in > pwm.setclock(), tmr.setclock() and elsewhere; > 2. frequencies that are derived from these by dividing these down, > usually with a hardware counter that resets every N clock cycles, as > used in adc sampling rates, pwm output frequencies(), free-running > timer interrupts and so on. Ah, gotcha. I could be a little bit pedantic here that in both 1 & 2 there is clock division going on that is configurable, but I see what you mean by the second class being ones that are based on something like a timer counter with match values or overflow or something like that that is being incremented by the first class. > > I just used the words "high" and "low" because their typical ranges of > frequencies are higher and lower. > Shall we call then "clocking frequencies" and "output frequencies" or > something, to avoid the subjective associations of the words "high" > and "low"? Sure. > > By separating the different cases, we can reason more usefully about > each one, rather than talking about a single solution to three > different types of object. > > It may be that what I have called "low" frequencies are not worth > making more precise, which would just leave PWM duty cycles. > >> PWM can >> certainly run at a few megahertz on many of the platforms we use, >> though this doesn't provide excellent resolution in duty cycle. > > As well as 1 or 2 hertz if used to flash christmas lights, where one > would be able to produce a slowly-accelerating flashing rate. > What I mean, again, is that these are lower frequencies derived from > the clocking rate. Of course, the divisor used to produce the output > frequency could even be 1, giving the same frequency as the clock > rate, but in terms of the program interface, that data object is still > an output frequency derived from the clocking frequency. > >> I actually have a minor dislike for the convention of the "setup" >> function since they take different parameters depending on the >> peripheral that's being setup. > > I have no problem aith that, since different devices need setting up > with different parameters. > For me, if the function sets the interface up (i.e. initializes it to > make it ready for subsequent use), then "setup" is OK. > I am concerned by a *.setup() function that doesn't set the interface > up, but is used to drive it! I would agree here that this particular case has a stronger motivator behind it. > >> As far as alternatives, maybe something like settimer >> or setuptimer might make more sense? Alternately if we wanted ADC to >> support other trigger sources, it could be named something like >> settrigger or setuptrigger? > > OK, I'll leave you to think about that. I'm happy as long as > foo.setup() sets foo up ready to be used, foo.setclock() sets foo's > clock frequency and so on, so that when you read the Lua code, it > makes some kind of sense. Gotcha. > >> > Furthermore, on AVR32, being told to set both the frequency and duty >> > cycle causes a glitch in the output because the stupid hardware >> > interface can only set either the frequency or the duty cycle once per >> > output cycle so you get one cycle either at the new frequency and old >> > duty cycle or at the old frequency with the new duty cycle. >> >> Interesting. I assume that you can temporarily stop the peripheral, >> set the values and then start it again though? Sure you might lose >> some clock cycles, but I'm not sure we're guaranteeing that PWM is >> continuous through successive calls. Not sure if we would need to do >> so either. > > The most demanding application I can think of is when it is being used > to signal data, such as in TV remote controls, where a blip at the > wrong frequency or duty cycle might make it do the wrong thing. Yes, > you can also stop the output, reprogram it and start again, though for > applications like using PWM to implement an audio DAC, that causes an > audible bubble in the output. I like producing good quality output > when that is possible, so that when people push the envelope, the > system doesn't let them down. I agree there as well. Audio is probably a good example where this sort of thing might become noticeable. > >> > Worse yet, pwm.setup(id, frequency, duty_cycle) takes two parameters >> > both of which are in the proposed fixed-point format, which is ugly. >> > Even in the naive case where you get higher precision by giving a duty >> > cycle radix of 1000 or 1000000, you'd need to supply a bogus extra >> > parameter for the undesired precision of the PWM output frequency. >> >> This is another reason why I'm not a massive fan of "setup" functions, >> I kindof think this should be two separate functions. > > OK > >> Would we really need a radix other than 1 for PWM frequencies though? >> I'm not sure if non-integer frequencies are ever needed. > > Flashing christmas lights? That's a valid example. I might have considered using other methods to support something in that frequency range, but that's a convenient way to handle that type of system without having software in the loop at each cycle. > >> > These are three arguments in favour of splitting pwm.setup() into two >> > functions like pwm.setfreq() and pwm.setduty(). A fourth is that all >> > the other modules' setup() functions are usually called once at start >> > of program, then the output is varied as the program runs using >> > module-specific functions, whereas the current pwm.setup() is used >> > repeatedly while the programming is running to vary the output. >> >> Yeah, I've felt this way for a while now. Bogdan, any comments on >> this front? We're starting to get into a number of API changes and >> proposals here :-) > >> >> 3) The divisor returned is determined by the low-level code in order >> >> to provide best accuracy in conveying the frequency selected given the >> >> available variable size for storing the values. >> > >> > Trying to select the most precise pair of integers to represent an >> > arbitrary value is actually quite a difficult math-theoretical >> > problem, not to mention the difficullty in the user code that would be >> > necessary to correctly handle them. >> >> This is true. It's an optimization problem. There are some simple >> single-shot approaches that have what are most likely acceptable error >> levels like the one mentioned earlier. Really choosing things >> optimally is harder and is iterative unless one has a lookup table. >> I'd be OK with selecting one that's not perfect, but could be adjusted >> a little to provide low error in the ranges that the peripherals are >> generally used. >> >> I don't mean to say that they really need to be optimal, we would end >> up wasting computational time and memory with some iterative >> optimization algorithm or lookup tables. The reason that I said >> "maximum _potential_ return value accuracy" was that this would give >> the backend implementer the most precise representation of the >> frequency the user wanted. Again, I would be happy with a simple >> non-iterative algorithm with a known worst-case that is acceptable. > > I would drop this idea and keep it simple, returning values in the > same radix as the user passed, where that is appropriate. Yep. > > Or, if we drop the proposal to make "output" frequencies more precise > (for the 27.27Hz ADCs in seismographs and for the xmas lights), that > only leaves PWM duty cycles, and in that case we don't return the > "actual" duty cycle set at all, so the problem goes away. > >> I would tend to agree that just >> going with fixed-point (powers of 10) might be the least messy to deal >> with. > > I have no problem if they want to express something in 360ths for some > reason of their own, or, in units of the period of the PWM cycle > counter to get max precision. Our code would be the same in any case, > without needing to impose artificial restrictions. > > Can we focus on concrete proposals and decide? Sure. > > I'm in favour of adding optional extra parameter after duty cycles to > get higher precision (indeed, hardware precision if you pick the right > radix) OK. So something like: duty, radix = pwm.setduty(id, duty, [radix=100]) Radix is free-form, but integer? > > I'm ambivalent about adding radix to frequencies for higher-precision > low frequencies. It would be nice. Does one need the christmas lights to flash at 0.43 Hz? I dunno. PWM can be used for triggering other things like, for example an external ADC where that might be useful? freq, radix = pwm.setfreq(id, freq, [radix=1]) For setting the clocking frequency, I don't think there's a good reason (pwm.setclock). Some platforms may not even support low frequencies on this front and the few multipliers/dividers in line would make it difficult to get precision on those that would make it worth it. Question: do we leave setup for now? Do we provide radix functionality with it? > > Lastly, these kinds of changes are hard because they have to be > implemented on every platform simultaneously, which either needs > someone who has all the platforms, or some tight collaboration. Indeed. I suppose we can stub out functionality for some platforms at first and fill in support, but it would be nice to have them all as much on the same page as possible. > James, have you been able to test on an ARm platform that patch to > split platform_adc_op() Yeah, sorry about not getting back to you on that. I ran out of time last week and then ended up driving to a wedding (which I brought some MCUs with me on, but had no time). I have tested some but not all of the platforms yet. It works fine for me on LM3S & STM32, but I'm having trouble finding the LPC 17xx board I have, however looking at the code that one shouldn't be a problem since there were already functions for providing the functionality that pwm_op was using. I guess I also have an LPC24xx in the form of the PUC rio board, and an STR9 board that I can test. I'll send results from those tonight since they're at home. I can do at least a build test for other platforms, but I don't have hardware. I suppose I can take a look at the assembler generated for the at91 platform and try to see if it has optimized out that section you had concerns about. I can try and poke Bogdan to see if he can test this? As far as getting changes like this implemented while I know I took a bit on checking platform compatibility, but I'm in town for the coming weeks and I'm willing to try and get this implemented/tested with tighter, at least closer to daily, evaluation and feedback. I'm also happy to implement some of this myself since at least for the duty cycles, I've wanted this for some time (I have a current project where to effectively get this I add random numbers on the interval -0.5 -> 0.5 to dither PWM duty cycles to get more precise output, and I'd like to _not_ have to do that.) We could also do this in stages on a testing branch if we can't get all platforms tested and implemented quickly and only merge it once all platforms perform in a satisfactory manner. Let me know what portions of this you would like to implement, if any. Thanks for both the work and the discussion. I'm glad to have some section of this moving forward now. I would suggest that we start maybe with PWM _or_ ADC for this and not try to do both in the same set of patches at the same time and move on to any other places we want to implement this after we have done these. Timers, for example, could do well to support the radix notation, and all of these should have common code within given platforms so that once we've done one the others should be easier on both platform implementation and common code sides. > > M > _______________________________________________ > eLua-dev mailing list > [hidden email] > https://lists.berlios.de/mailman/listinfo/elua-dev > _______________________________________________ eLua-dev mailing list [hidden email] https://lists.berlios.de/mailman/listinfo/elua-dev |
On Mon, Aug 1, 2011 at 3:40 PM, James Snyder <[hidden email]> wrote:
> On Sun, Jul 31, 2011 at 12:32 AM, Martin Guy <[hidden email]> wrote: >> On 29 July 2011 02:51, James Snyder <[hidden email]> wrote: >> James, have you been able to test on an ARm platform that patch to >> split platform_adc_op() > > Yeah, sorry about not getting back to you on that. I ran out of time > last week and then ended up driving to a wedding (which I brought some > MCUs with me on, but had no time). > > I have tested some but not all of the platforms yet. It works fine > for me on LM3S & STM32, but I'm having trouble finding the LPC 17xx > board I have, however looking at the code that one shouldn't be a > problem since there were already functions for providing the > functionality that pwm_op was using. I guess I also have an LPC24xx > in the form of the PUC rio board, and an STR9 board that I can test. > I'll send results from those tonight since they're at home. I can do > at least a build test for other platforms, but I don't have hardware. LPC1768: works. STR9: works. LPC24xx: works. _______________________________________________ eLua-dev mailing list [hidden email] https://lists.berlios.de/mailman/listinfo/elua-dev |
Martin Guy |
In reply to this post by jbsnyder
I'll send this, cos it's been sitting in my outbox for 4 days and my
brain is elsewhere at present. On 1 August 2011 22:40, James Snyder <[hidden email]> wrote: >> I'm in favour of adding optional extra parameter after duty cycles to >> get higher precision (indeed, hardware precision if you pick the right >> radix) > > OK. So something like: > > duty, radix = pwm.setduty(id, duty, [radix=100]) > > Radix is free-form, but integer? Integer, sure. A floating point radix is an interesting idea... :) I'm not sure the radix needs to be returned from the functions, since it will always be either 100 or what the user specified, so its value is 100% predictable and so contains no information. It also simplifies the documentation, so a beginner isn't immediately worried about "what's this extra radix thing that it might return" and only power users need to deal with funny radices. >> I'm ambivalent about adding radix to frequencies for higher-precision >> low frequencies. It would be nice. > > Does one need the christmas lights to flash at 0.43 Hz? I dunno. PWM > can be used for triggering other things like, for example an external > ADC where that might be useful? > > freq, radix = pwm.setfreq(id, freq, [radix=1]) OK. apart from returning radix again > For setting the clocking frequency, I don't think there's a good > reason (pwm.setclock). > > Question: do we leave setup for now? Do we provide radix functionality with it? OK, let's think about this. If we split into setfreq() and setduty(): - what does it do when they have set a frequency but not set a duty cycle yet? - What does it do if they set a duty cycle but haven't set a frequency yet? The platform code I have seen for the current pwm.setup() always goes: { set pwm period in the hardware, calculated from the requested frequency and clock rate; set duty cycle as a period in clock ticks calculated from the pwm period; } Maybe for this reason it is handy always to have the frequency and duty cycle on hand at the same time. However, from the platform code I've seen, when setting the duty cycle separately, you can fish the period (that you need for the calculation) back out of the hardware register. That's enough to be able to vary the duty cycle while keeping the frequency the same. However, if they decide to vary the frequency without changing the duty cycle, we would need to remember the duty cycle for each channel in a tiny array duty[NUM_PWM] and presumably radix[NUM_PWM] to be able to recalculate the duty cycle as a fraction of the PWM period when the frequency alone is changed. That doesn't seem too hard or costly if it gives a better Lua interface to the PWM module, and only needs to happen in src/modules/pwm.c, not in every platform. I hate to say it, but that suggests keeping the C platform interface as it is, with freq and duty always passed as a pair, otherwise we'll have the same code to cache the duty cycle and radix in a dozen different places. In the case of AVR32, with its inability to set both at once, I already check to see if the frequency (period) is the same as before and only update the duty cycle in that case. Question: do PWM applications change the frequency without changing the duty cycle? Well, I can think of an organ-like instrument where the duty cycle gives a tone timbre and the frequency the note. For that, the highest possible precision of the frequency setting is critical: a hundredth of a percent of the frequency in use is the target for musical pitches, so the more frequency precision the better in that application. That suggests in the Lua interface: pwm.setduty( id, duty[, radix=100] ) pwm.setfreq( id, freq[, radix=1] ) and keeping the C platform interface the same as it is now, modulo adding an extra duty/radix parameter. It would be good to add in the documentation recommended usage sequences. For instance, what happens if I call pwm.start() before setting the frequency and duty cycle for a channel? Is a call to pwm.setup() necessary to enable the pin as an output, even if we move the frequency and duty cycle stuff, that can happen during the program run, elsewhere? >> James, have you been able to test on an ARm platform that patch to >> split platform_adc_op() > > I suppose I can take a look at the assembler generated for the at91 > platform and try to see if it has optimized out that section you had > concerns about. Ah! I hadn't thought of that. Thanks. Yes, it is dutifully reading it (and storing it in the dummy variable that is immediately forgotten, der). Thanks. > I add random numbers on the interval -0.5 -> > 0.5 to dither PWM duty cycles to get more precise output, and I'd like > to _not_ have to do that.) Urk! It's a sign that something is wrong with the interface if you have to do such gymnastics. > We could also do this in stages on a testing branch if we can't get > all platforms tested and implemented quickly and only merge it once > all platforms perform in a satisfactory manner. When it means non-trivial reimplementation of platform code on a dozen platforms, which may or may not work, that sounds wise. When evolving the Lua interfaces, should we keep the current Lua interfaces also, for example under #ifdef ELUA_0_8_COMPAT to allow existing Lua code to continue working, or do we just change things to keep the code simpler and more manageable? > I would suggest that we start > maybe with PWM _or_ ADC for this and not try to do both in the same > set of patches at the same time Agreed. Let's hit PWM first > Let me know what portions of this you would like to implement, if any. Implementing the optional radix in the current pwm.setup() interface seems like the smallest self-contained step we can take in this direction, and the least disruptive. > Timers, for example, > could do well to support the radix notation, and all of these should > have common code within given platforms so that once we've done one > the others should be easier on both platform implementation and common > code sides. They could. But if you mean for time delays, that has as a prerequisite getting rid of the C platform_timer_op primitives, and this is the second most difficult example of this to refactor, after pio. Furthermore, they are already radix 1,000,000 (microseconds) which may already be precise enough for delays. I'm not in favour of implementing things just for a sense of symmetry, but only if the upheaval brings a practical advantage. If you mean the frequency of free-running interrupts, tmr already expresses that in microseconds, not Hz, so you already have high precision at low frequencies. But that also bring low precision at high frequencies :( so maybe this is useful, to be able to express free-running interrupt periods (or ADC conversion frequencies) at the precision of the underlying timer clock (tmr.getclock()) to be able to get the best out of the hardware, which may be clocking the timer at 200MHz. I'm not sure that having ADC sampling rates explicitly use a timers at the interface level is ideal. On the one hand, hardware that is able to sample at a given rate will needlessly occupy a timer just because you have to say so in the Lua interface. On the other, if there is no other way to implement it on a given platform it is better for it be explicit than hidden. With the ADCs that I've seen on these platforms, you just kick them, they convert as fast as they can and you the read the result back - very different from the audio ADCs I'm used to. That suggests, instead of hijackng a timer, making the *user* do the stuff with timers for periodic conversions, probably by an explicit periodic timer interrupt that calls a kick-the-ADC routine, and limit the ADC eLua interface to start-a-conversion, is-it-finished and read-the-value, eliminating the buffering and the smoothing parts. Already the DHCP server in UIP, and the serial console quietly hijack a timer each to do timeouts, and between them they occupy both of the usable timers on AVR32, leaving the user with only the virtual ones with 1/10th second granularity. DHCP can easily be changed to use the system tick while for the serial console it's only used to try and detect LFCR sequences that can never occur. Actually, it's a bit odd that free-running timer interrupts are the only frequency in eLua that is expressed as a period. In fact, on the subect of free-running timer interrupts, I think the tmr.set_match_int( id, period, type ) (where type = tmr.INT_ONESHOT or tmr.INT_CYCLIC for a single interrupts after a delay or to set free-running interrupts or free-running ADC conversions) could be rethought. The "type" parameter is surely always a constant in the user code, so we could turn this into two functions. That would let us express an interrupt after a delay as the usual time in microseconds, and the frequency of free-running interrupts as a frequency with optional radix, the same as all the other output-frequency operations. But let's see if that is a practical problem for anyone first. M _______________________________________________ eLua-dev mailing list [hidden email] https://lists.berlios.de/mailman/listinfo/elua-dev |
On Fri, Aug 5, 2011 at 1:25 AM, Martin Guy <[hidden email]> wrote:
I'll send this, cos it's been sitting in my outbox for 4 days and my Yeah, I'm trying to think of use cases where returning the radix would be essential, and the only things I think think of are situations where you wanted to feed the value to something else, but selected the radix in another scope (maybe like a coroutine or a closure, but you could divide by the radix before returning, or return both in an ad-hoc manner, so I guess that's not needed. I just liked the idea that the result could be interpreted unambiguously without access to the scope in which the parameters were passed.
So, count that as a "I guess we don't need it"
Good question. Going based on other peripherals, like if you just "start" a timer and possibly even pwm currently, it will start going, but the parameters you haven't set aren't guaranteed. You get either the default of the hardware or some default that we selected to initialize the hardware. This is a little less than ideal, perhaps we could standardize this a little more across platforms.. hmm..
The platform code I have seen for the current pwm.setup() always goes: Yeah, generally those registers are readable, I think we often use this in order to return what things were actually set to. In a worst case scenario there could be global state in the platform implementation to keep track of this after it has been set.
That's enough to be able to vary the duty cycle while keeping the Yep.
Yeah, and you would want to cache the passed numbers rather than fish them out to fix the duty cycle on the fly so as to remain closer to what the user asked for. Unfortunately this also has an icky side effect of potentially changing the exact duty cycle.
I hate to say it, I'd agree. There are a few ways to do it, but it is most convenient in the platform code to have both at the same time, and it would help keep. On the one hand that means less modification to the platforms and less code, but as you're pointing out it's problematic with AVR32.
Side note: is there some sort of pending or update flag that you could use to set the two sequentially then grind away in a loop watching a register for when that has been accepted, then you can change the other? Or can you just watch the counter? It certainly means burning extra time in the function, especially if the frequency is quite slow, but perhaps worth it?
Right. There are a lot of applications where you won't touch frequency, but will update duty cycle frequently, but audio applications would hold duty cycle constant (for the most part), but vary the frequency all over.
With this, we would store duty and radix vales in the module, maybe with some sane defaults set initially for whichever value isn't being set, like 50% for duty, and 1 KHz or something for frequency?
Yeah, this is a good idea, and something we should do for other peripherals like timers and the like as far as what state they're in without setting any parameters.
I think having some sane, documented defaults for the unset parameter is not a bad place to start (I guess we then might have to guarantee those won't change in future versions?). I suppose you could also do some things like set the initial values to 0 or -1 and only call the platform function when both have been filled in. Let me know what you think about this...
On the pin configuration front, we could probably do it when the user calls start, since this is the first point that the output actually needs to start being reflected on the pin. I suspect there shouldn't be any platform situations where the pin needs to be configured in advance of other stuff. Also, ultimately, I think we need more of a common interface for supporting various output pin configuration options, but that's a whole separate thread that should be picked up sometime.
Yep. It's ugly, but it did effectively increase resolution. That was around the point that I think I wrote the original suggestion for a variable radix.
We haven't used deprecation in the past. I'd vote for keeping things simple for now. I'd say that would be something that maybe might be necessary for post-1.0 revisions to the API?
We should go through and update examples we've published to the newer API and certainly make it clear in any release notes that updates are required for code using the older style.
Sounds good.
Yeah, I wouldn't just do it for symmetry, we can probably leave this alone for now. Given that we're using unsigned 32-bit integers the largest time chunk we can express is ~71 minutes if I did that correctly... that's probably fine for now.
I agree that there should be a mechanism to use interrupts to drive conversions, but that gets back into the whole issue of jitter and latency. I think it would be a mistake to not allow use of the built-in timers to trigger conversions since this is always going to be the most consistent, low-jitter way to start a conversion. It could be that setting up the timer could be moved to a separate module, maybe a platform-specific extension. Certainly what you're suggesting would simplify the code and support of it, but timing of conversions will be less ideal, and ditching the buffering + smoothing and replicating that in pure Lua is going to be orders of magnitude slower, especially if you're talking about maintaining that data in Lua tables which pre-allocate more elements than you request and have a high memory penalty.
Also, I'm not _that_ worried about tying up a timer for ADC conversions. Depending on the platform there are other things like PWM and encoder interfaces that by definition of hardware implementation must suck up a timer. At least for ADC, it's still operating as a running timer, ideally you can still request the frequency using the tmr module and use output from it.. it will just be running at a frequency that's selected to enable ADC conversions at the rate the user specified. Providing the option to not use one and drive it off of some other sort of interrupt is something that I do plan to implement shortly.
Speaking of which: I did get a reply from Atmel regarding the timer channels, and while I didn't get anything definitive, those that were asked thought that it should work, so I might experiment with it.
Yeah, this is stuff that clearly shouldn't be sucking up a timer, especially in the ways that they're implemented (doesn't need interrupts, just polling to get a measure of time). We should be using system tick or an RTC where available, and only if such a facility isn't available maybe document and suck up a timer and use that in place of the RTC to provide a common system clock that these things can run off of.
Regarding the serial console & uart, it's used for timeouts, which for the console might end up only coming down to that particular code (I can't remember), but configurable timeouts are definitely useful for other consumers of the UART services (LuaRPC, RFS & MUX all use them). That said and all that behavior should come off of something like a system tick. Regarding the linefeed and carriage return sequence detection, it does actually happen depending on the terminal being used. This allows us to automatically support various ways of ending a line without having some sort of compile time option. If I recall, before this was changed it was something like default behavior from TeraTerm or some other terminal vs using something like screen on Posix used different line endings (probably CRLF on Windows, LF on posix OSes) and supporting one but not the other would make it either irritating or impossible to use with one of the types of line endings, depending on which you supported. So, what it's looking for are CRLF sequences, but it also happens to handle LFCR if that happens to occur (which apparently, according to Wikipedia, was used on Acorn BBC spooled text output ;-) (http://en.wikipedia.org/wiki/Newline)
I think a lot of problems would be solved by having a system tick timer available as a resource for things like uarts, ethernet, mmcfs (which also wants timeouts), and anything that needs consistent but relatively coarse-grained timing intervals (running at a frequency between 10Hz and 1 Khz, maybe?). Users could also get most of the timing functionality they would normally derive from timers from this common facility as well. Then the remainder of timers are available for the user to select what they might be consumed by: dedicated timing with the tmr module, periodic interrupts, initiating scans/conversions on ADC, etc...
We could maybe look into this as one of the issues following on to the others we've discussed since I've wanted this for some time as well :-)
It does feel a little odd given that we have all other cyclic stuff in Hz, supporting the different notations with separate functions would be a fairly clean way to make things more consistent, but it also would also complicate the interfaces a bit to support both notations. Lets table that for now.
Best. -jsnyder _______________________________________________ eLua-dev mailing list [hidden email] https://lists.berlios.de/mailman/listinfo/elua-dev |
Martin Guy |
On 5 August 2011 22:25, James Snyder <[hidden email]> wrote:
> Side note: is there some sort of pending or update flag that you could use > to set the two sequentially then grind away in a loop watching a register > for when that has been accepted, then you can change the other? Or can you > just watch the counter? It certainly means burning extra time in the > function, especially if the frequency is quite slow, but perhaps worth it? There is a bit that tells you when the pending update has been flushed. For the moment, I'm just interested in getting the thing to work and not worried about the processing the application could do while the first update of the two is pending. > but audio applications would hold duty > cycle constant (for the most part), but vary the frequency all over. You mean organ-type audio. Using PWM as a crude ADC would keep the frequency at a fixed high and vary the duty cycle to varu the output value. > With this, we would store duty and radix vales in the module, maybe with > some sane defaults set initially for whichever value isn't being set, like > 50% for duty, and 1 KHz or something for frequency? I'm not sure there are any sane defaults. I'd prefer that, until they say what they want, we hold all output. In my experience, arbitrary defaults like 1kHz tend to become a ball and chain when it comes to software maintenance in the future. >> When evolving the Lua interfaces, should we keep the current Lua >> interfaces also, for example under >> #ifdef ELUA_0_8_COMPAT >> to allow existing Lua code to continue working, or do we just change >> things to keep the code simpler and more manageable? > > We haven't used deprecation in the past. I'd vote for keeping things simple > for now. I'd say that would be something that maybe might be necessary for > post-1.0 revisions to the API? > We should go through and update examples we've published to the newer API > and certainly make it clear in any release notes that updates are required > for code using the older style. OK. For me, maintaining backward compatibility is no problem - we just have to make sure we never make the old parameters take on different meanings, but that's a good defensive principle in any case. The only difference is in code size for the backward-compatible version. > I agree that there should be a mechanism to use interrupts to drive > conversions, but that gets back into the whole issue of jitter and latency. > I think it would be a mistake to not allow use of the built-in timers to > trigger conversions since this is always going to be the most consistent, > low-jitter way to start a conversion. Right. So what do we do for a Lua interface that can cope with hardware that had a built-in precise conversion rate and hardware where you have to fake it with timers, interrupts and kicking the ADC periodically? My feeling is to make a Lua interface that says "sample at 100Hz" and then let the platform-dependent code decide whether the underlying hardware has sample-at-100Hz hardware, or whether we need to fake that using timers, interrupts and stuff. I dunno. Being able to say "give me 100 samples per second" is nice, but optionally hijacking one of the timers on most platforms is nasty. Opinions? > ditching the buffering + > smoothing and replicating that in pure Lua is going to be orders of > magnitude slower My main problem with the current "smoothing" is that it is a hack. It just averages a few of the previous samples. On the one hand, this offends me as a DSP nerd, since "smoothing" should be a low-pass filter, with all the power math that finite-response filters involve, while averaging the last few samples is more of a distortion effect than a low-pass filter. It's something like a comb filter with a space between the peaks equal to the length of your averaging buffer. My feeling is that, if you have a noisy input signal that jitters due to noise above the sampling frequency, you should be low-pass filtering it in hardware, with capacitors, resistors, inductore and the like, to get a clean analog signal to sample with no frequency components above 1/2 of the sampling frequency. Sampling a raw noisy signal and averaging it to hide the worst of the artifacts seems like a kludge by comparison. > (http://en.wikipedia.org/wiki/Newline) Thanks. That concludes the discussion on this point. LFCR does not exist, so we can dump this code and save a timer. > I think a lot of problems would be solved by having a system tick timer > available as a resource for things like uarts, ethernet, mmcfs (which also > wants timeouts), and anything that needs consistent but relatively > coarse-grained timing intervals (running at a frequency between 10Hz and 1 > Khz, maybe?) We have that as long as virtual timers are enabled. It would be better if system tick were not virtual timer-dependent, but existed independently, of which Vtimers would be one of the clients. Anyone who wants to refactor that in the code is very welcome. > Users could also get most of the timing functionality they > would normally derive from timers from this common facility as well. Then > the remainder of timers are available for the user to select what they might > be consumed by: dedicated timing with the tmr module, periodic interrupts, > initiating scans/conversions on ADC, etc... Are you suggesting that we reserve one timer for internal purposes on all platforms? I'm not sure that would even be enough, since we want one for the system tick for coarse timing, another maybe to trigger arbitrary-frequency ADC conversions... should we allocate timers dynamically instead of presenting the user with a fixed number of them? Hum, this is getting scary... M _______________________________________________ eLua-dev mailing list [hidden email] https://lists.berlios.de/mailman/listinfo/elua-dev |
Sorry about the lack of reply on this one, I've been trying to get
ready for a qualifier recently and just finished it. On Mon, Aug 29, 2011 at 12:02 PM, Martin Guy <[hidden email]> wrote: > On 5 August 2011 22:25, James Snyder <[hidden email]> wrote: >> Side note: is there some sort of pending or update flag that you could use >> to set the two sequentially then grind away in a loop watching a register >> for when that has been accepted, then you can change the other? Or can you >> just watch the counter? It certainly means burning extra time in the >> function, especially if the frequency is quite slow, but perhaps worth it? > > There is a bit that tells you when the pending update has been flushed. > For the moment, I'm just interested in getting the thing to work and > not worried about the processing the application could do while the > first update of the two is pending. OK. > >> but audio applications would hold duty >> cycle constant (for the most part), but vary the frequency all over. > > You mean organ-type audio. Using PWM as a crude ADC would keep the > frequency at a fixed high and vary the duty cycle to varu the output > value. Right. There are different modes for this. For higher quality audio reproduction (not just square wave beep of varying frequencies (+harmonics)), one could use the PWM, vary duty cycle to treat it like a crude ADC and then low pass it to get desired output. > >> With this, we would store duty and radix vales in the module, maybe with >> some sane defaults set initially for whichever value isn't being set, like >> 50% for duty, and 1 KHz or something for frequency? > > I'm not sure there are any sane defaults. I'd prefer that, until they > say what they want, we hold all output. In my experience, arbitrary > defaults like 1kHz tend to become a ball and chain when it comes to > software maintenance in the future. Fair enough, that's fine with me. > >>> When evolving the Lua interfaces, should we keep the current Lua >>> interfaces also, for example under >>> #ifdef ELUA_0_8_COMPAT >>> to allow existing Lua code to continue working, or do we just change >>> things to keep the code simpler and more manageable? >> >> We haven't used deprecation in the past. I'd vote for keeping things simple >> for now. I'd say that would be something that maybe might be necessary for >> post-1.0 revisions to the API? >> We should go through and update examples we've published to the newer API >> and certainly make it clear in any release notes that updates are required >> for code using the older style. > > OK. For me, maintaining backward compatibility is no problem - we just > have to make sure we never make the old parameters take on different > meanings, but that's a good defensive principle in any case. The only > difference is in code size for the backward-compatible version. > >> I agree that there should be a mechanism to use interrupts to drive >> conversions, but that gets back into the whole issue of jitter and latency. >> I think it would be a mistake to not allow use of the built-in timers to >> trigger conversions since this is always going to be the most consistent, >> low-jitter way to start a conversion. > > Right. So what do we do for a Lua interface that can cope with > hardware that had a built-in precise conversion rate and hardware > where you have to fake it with timers, interrupts and kicking the ADC > periodically? > > My feeling is to make a Lua interface that says "sample at 100Hz" and > then let the platform-dependent code decide whether the underlying > hardware has sample-at-100Hz hardware, or whether we need to fake that > using timers, interrupts and stuff. Yeah, this complicates the interface a little bit in terms of letting the user know what resources have been consumed and what side effects are. At least if we're talking about systick/timer interrupts vs triggered ADC. Using systick ideally would not consume any named/id'd resource and would just be limited in time resolution by system systick configuration. For the timer interrupts, we might have to swallow a timer, or have one dedicated to providing this type of functionality, in which case it becomes like systick. Then for using triggered interrupts, we have only the option of swallowing a timer. I'd rather have this done at least somewhat explicitly, i.e.: the user asks for a given rate, and either dedicates a timer to it or not. If no timer is dedicated, then we fall to the resolution of some system timing facility. If a timer is dedicated then we use timer interrupts or ADC triggered by a timer? I realize that's not 100% explicit, but I don't think we can do this entirely automatically since we don't know whether the user expects certain resources to be left as they are already configured (we have no concept of locking, for example). > > I dunno. Being able to say "give me 100 samples per second" is nice, > but optionally hijacking one of the timers on most platforms is nasty. > Opinions? Well, I don't mind the hijacking that much actually, as long as it is done with the user's knowledge for the reason of getting precise timing. This is essentially how things are done with data acquisition equipment, though they probably just dedicate a timer and never use it for anything else. > >> ditching the buffering + >> smoothing and replicating that in pure Lua is going to be orders of >> magnitude slower > > My main problem with the current "smoothing" is that it is a hack. It > just averages a few of the previous samples. On the one hand, this > offends me as a DSP nerd, since "smoothing" should be a low-pass > filter, with all the power math that finite-response filters involve, > while averaging the last few samples is more of a distortion effect > than a low-pass filter. It's something like a comb filter with a space > between the peaks equal to the length of your averaging buffer. It's an FIR moving average filter, which admittedly is generally only useful if you're sampling constantly or toss the buffer between outputting samples if there's delay between sampling sessions. However, I think a lot of other filtering approaches, without significant complexity, will suffer from similar issues. Sure, it could be more complicated, or at least it could be easier to plug in other operations if the user desires. It could also be described differently or operation could be adjusted so that it can only be used as an oversampling filter. Enabling an oversampling mode, actually sounds like not a bad idea... I don't think hack is entirely the right term and frankly any filter is distortion of sampled signal. It's just an FIR moving average filter or boxcar filter, which has the corresponding advantages and disadvantages of that type of filter design. > My feeling is that, if you have a noisy input signal that jitters > due to noise above the sampling frequency, you should be low-pass > filtering it in hardware, with capacitors, resistors, inductore and > the like, to get a clean analog signal to sample with no frequency > components above 1/2 of the sampling frequency. Sampling a raw noisy > signal and averaging it to hide the worst of the artifacts seems like > a kludge by comparison. Well, it's a tradeoff. Ideally, yes, you get your signal perfect before presenting it to one of the pins for the mux for the ADC, but the ADC isn't perfect either, especially in lower priced microcontrollers. Many of these one can't do much about aside from be aware of the magnitude of their effect, but moving average filters can help reduce noise for signals that aren't cleaned up and low impedance inputs. It's only more of a "hack" than doing proper signal conditioning in the same way that the rest of post-sampling digital signal processing is. Additionally, oversampling (taking N samples at a time and averaging them), however can also increase the effective number of bits for an ADC (http://www.atmel.com/dyn/resources/prod_documents/doc8003.pdf). Some platforms (LM3S for example) even have hardware support for oversampling. > >> (http://en.wikipedia.org/wiki/Newline) > > Thanks. That concludes the discussion on this point. LFCR does not > exist, so we can dump this code and save a timer. Well, we can use a systick or system timer to deal with CRLF vs LF vs CR, which is what this code does. The reason a timer is needed is to process either the 1 or 2 char version without having the ambiguous situation where the user provides a 1 char line ending and then the system has to wait for the next char to decide whether it has gotten a real line ending or not. The timer provides a window so that if it doesn't get an LF after a CR during the inter-char limit, it assumes it has gotten the full line ending and can then finish processing the line. > >> I think a lot of problems would be solved by having a system tick timer >> available as a resource for things like uarts, ethernet, mmcfs (which also >> wants timeouts), and anything that needs consistent but relatively >> coarse-grained timing intervals (running at a frequency between 10Hz and 1 >> Khz, maybe?) > > We have that as long as virtual timers are enabled. > It would be better if system tick were not virtual timer-dependent, > but existed independently, of which Vtimers would be one of the > clients. > Anyone who wants to refactor that in the code is very welcome. Sounds good. If someone else doesn't take this up, I may look into it in the coming weeks since I'm digging around a bit while figuring out how to support UART over USB CDC. > >> Users could also get most of the timing functionality they >> would normally derive from timers from this common facility as well. Then >> the remainder of timers are available for the user to select what they might >> be consumed by: dedicated timing with the tmr module, periodic interrupts, >> initiating scans/conversions on ADC, etc... > > Are you suggesting that we reserve one timer for internal purposes on > all platforms? I'm not sure that would even be enough, since we want > one for the system tick for coarse timing, another maybe to trigger > arbitrary-frequency ADC conversions... should we allocate timers > dynamically instead of presenting the user with a fixed number of > them? Hum, this is getting scary... I'm not going to suggest dynamically allocating timers, aside from what we already do, where the user is saying "use this timer for this." I'm just suggesting that we can take many of the things that silently suck up one or more timers at startup and move them to a common coarse resolution timing facility so that 1) fewer or zero general purpose timers (replaced by systick if available) are consumed by things like providing UART functionality 2) either way coarse resolution timer can also be used for other arbitrary things and by the user. This would also be a read-only facility, so consumers of it can't change it's count or frequency on the fly (they're determined at compile time or boot time). -jsnyder > > M > _______________________________________________ > eLua-dev mailing list > [hidden email] > https://lists.berlios.de/mailman/listinfo/elua-dev > _______________________________________________ eLua-dev mailing list [hidden email] https://lists.berlios.de/mailman/listinfo/elua-dev |
Free forum by Nabble | Edit this page |