# ATSAM / ASF4 - how to use HRI driver

While trying to change the analog reference voltage in ATMEL Studio for ATSAMC21 I found no way to configure this in ATMEL start and in the higher level drivers.

So I searched around the ASF a bit and found, that this function is only implemented in the HRI drivers.

However the usage of these drivers is not well documented (or I don't find the right document). The documentation gives these drivers just a general description, mostly on how the functions are named and what are available.

After some searching I found the HRI driver file HRI_SUPC_C21.h. In it there are several functions for changing the VREF register such as

static inline void hri_supc_write_VREF_reg(const void *const hw, hri_supc_vref_reg_t data)
{
SUPC_CRITICAL_SECTION_ENTER();
((Supc *)hw)->VREF.reg = data;
SUPC_CRITICAL_SECTION_LEAVE();
}


What confuses me, and also what the documentation does not explain, is:

1. What is the "onst void *const hw" pointer?
2. When I call this function, what value do I use here?
3. If there is some initializing function for this "hw" how do I find this? (I'm asking in general, because most functions in the HRI driver files require this "hw" pointer
4. If it is some constant value or address, how do I find this?

As this question is kind of related to my other question, for good measure here the link ATSAMC21 - Change ADC reference voltage

Overall, I would strongly recommend not to use ASF. The source code quality is low and the documentation, when at all present, is questionable at best. It is not a HAL, it is bloat.

1. What is the "const void *const hw" pointer?

It's an indication of a confused programmer. The right-most const is just bloat and means that the function won't alter it's own local variable, which nobody cares about. Including, apparently, the person who wrote the code.

The void* itself means that the function accepts any object pointer, without any type safety.

(Supc *)hw invokes undefined behavior since the const qualifiers are "cast away" - it's an invalid conversion.

Also, if any other part of the program has already accessed that memory location prior to the hri_supc_write_VREF_reg call and its conversion and de-reference, this code violates "strict aliasing". Again, undefined behavior.

1. When I call this function, what value do I use here?

You can't use any value. C does not allow implicit conversions from integers to pointers. See “Pointer from integer/integer from pointer without a cast” issues.

The sane way to write the function would have been hri_supc_write_VREF_reg(Supc* hw, ... or at least uintptr_t hw.

1. If there is some initializing function for this "hw" how do I find this?

In a properly written driver, it would have been found in the same file as all other functions prefixed hri. That is, hri_supc_c21.h or similar. But the problem with this file is that it doesn't actually provide any driver functionality or anything useful at all. It's just providing bloat inline wrappers around "scary register access", and doing so in unsafe, non-standard ways.

1. If it is some constant value or address, how do I find this?

See 2.

After some searching around in the HAL code and some trial and error I figured it out:

The necessary memory address to use the hri drives is defined in the "'devicename'.h" file of the project.

In above example, that would be

#define SUPC                 (0x40001800) /**< \brief (SUPC) APB Base Address */


And the usage would be:

hri_supc_write_VREF_reg(SUPC, data);

• You can't pass an integer to a function expecting a pointer. It's invalid/non-standard C. These libs are really bad, so I'd recommend to write your own routines instead. By all means peek at how they are doing things, but don't use the actual code. – Lundin Jun 26 '20 at 6:59