Faken,
The UBW32 is a very good way to go, from what I can tell of your requirements. It will support exactly what you need, as long as you're OK with 3.3V I/O (some are 5V tolerant, but not all.) It is inexpensive ($40) and is very easy to talk to using any language that can support serial ports (which is pretty much all of them - Basic, C, C#, Processing, etc.)
You can use any of the 76 I/O pins as inputs or outputs. The stock firmware as shipped will allow you to do what you want to do, no programming required on the embedded side. Getting that data up to the PC over USB (at only 10Hz) will be no problem. Getting outputs to go at 1KHz will probably work just fine as well.
If you have any questions, please let me know. I'm happy to help you out.
*Brian Schmalz
UBW32 creator
Structures are unsuitable for writing register maps, because a struct can have padding bytes added anywhere inside it, for alignment purposes. This depends on the compiler - you have to ensure that no padding is used (for example through a static assert).
Furthermore, it is easy to make mistakes when writing large structures that are supposed to reflect a register map, you have to get every single byte right or you'll break everything. This makes the struct vulnerable during maintenance.
I would strongly suggest you to write register maps through macros, such as:
#define PERIPH_A_DID0 (*(volatile uint32_t*)0x400FE000u))
#define PERIPH_A_DID1 (*(volatile uint32_t*)0x400FE004u))
This also has the advantage of being 100% portable to any C compiler.
Alternatively, you could do something more intricate such as this:
typedef volatile uint8_t* SCI_port;
#ifndef SCI0
#define SCI0 (&SCI0BDH)
#define SCI1 (&SCI1BDH)
#endif
#define SCIBDH(x) (*((SCI_port)x + 0x00)) /* SCI Baud Rate Register High */
#define SCIBDL(x) (*((SCI_port)x + 0x01)) /* SCI Baud Rate Register Low */
#define SCICR1(x) (*((SCI_port)x + 0x02)) /* SCI Control Register1 */
#define SCICR2(x) (*((SCI_port)x + 0x03)) /* SCI Control Register 2 */
#define SCISR1(x) (*((SCI_port)x + 0x04)) /* SCI Status Register 1 */
#define SCISR2(x) (*((SCI_port)x + 0x05)) /* SCI Status Register 2 */
#define SCIDRH(x) (*((SCI_port)x + 0x06)) /* SCI Data Register High */
#define SCIDRL(x) (*((SCI_port)x + 0x07)) /* SCI Data Register Low */
Then when writing your driver, you can do like this:
void sci_init (SCI_port port, ...)
{
SCICR1(port) = THIS | THAT;
SCICR2(port) = SOMETHING_ELSE;
...
}
This is very useful when you have many identical peripherals on the MCU, with the same registers, but only want one code to control them.
Best Answer
The most basic maneuver for complicated functions is to utilize the onboard timers.
Depending on how you set them up, you can get them to count exactly the number of clock cycles it takes for a certain function to execute. Simply read the timer before the function call and then again after the function call.
You will have to look through the assembly code to know how much time to take off the timer for the read calls but if it is a lengthy function or if you don't mind being by a couple of clock cycles then it is ok.
The only error in the conversion from clock cycles back to time is the possible error in the system clock's oscillating frequency. Again, this will be negligible for functions which take significant time to execute.
It sounds like in your case you want something around 30Hz (30 FPS) which is a very long time compared time cycle of the processor clock.