If you are new to PID controllers - heck, it took quite some years for me to actually understand the literature, and the math which always seemed too complex even though it's actually simple - I recommend you start with a super simple I controller.
Why I controller? I think it looks most intuitive. We have this mental state - the position of "gas pedal". If we are going too slowly, we push it further - if we go too fast, we pull it a bit back. An I controller can be implemented in a typical C oneliner, without the PID terminology, without the programmer even knowing they are making an I controller:
PWM += speed_setpoint - measured_speed;
That's an I controller!
Now, to adjust how fast it reacts, this becomes, intuitively:
PWM += something * (speed_setpoint - measured_speed);
Tuning this is intuitive and straightforward. If it's oscillating randomly, you lower the multiplier until it works.
And it works very well if you use really small multipliers. It's slow as hell, however.
This is a good form to represent an I controller, because it shows the necessity - and triviality - of implementing wind-up limitation:
PWM += something * (speed_setpoint - measured_speed);
if(PWM > BIGGEST_ALLOWED_PWM_VALUE) PWM = BIGGEST_ALLOWED_PWM_VALUE;
else if(PWM < 0) PWM = 0;
This is a no-brainer - no one expects me to explain why these limits are needed. But when you turn this into the actual PID mathematical representation, this operation moves elsewhere and needs extra scaling factor so that the end-result is still the same - the need for this limit is still there, but now, suddenly, this has become an "advanced topic" "second year class" like problem, and this being the case, integrator wind-up limitation is not discussed in the basic PID primers!
Now, P is added simply to bring the speed to the system.
Alone, P is seldom usable unless you know how the system behaves physically, in an exact way. Equivalently, a P controller is: "I know exactly that I need to press the gas pedal to this exact particular absolute location to correct my speed." Since this is impossible to know exactly, this way of thinking gives you the right ballpark of the control very quickly - and without thinking about the past! - but alone, it results in the motor running at the right ballpark speed only, which can be easily off by, say, even 50%, even if you tried to tune it. Even the tiniest amount of I term will automatically fix any steady-state error. So, in PI, you get to the ballpark quickly, and then to the correct value more slowly, so to say.
I don't know how people typically tune their PID controllers - according to the literature, "slowly, in a difficult way, with complex equations all over the place", seems the correct answer. But, I recommend that you implement any kind of interface to adjust PID coeffs on-the-fly. An UART serial console with simple keypresses, such as A/Z increase/decrease P, S/X increase/decrease I, D/C increase/decrease D, and a continuous printout of the current values, will do. Add a button to change between a few typical setpoints, since a quick setpoint change is often easier to test the transient response with, compared to a load change. This way, you'll be able to learn quickly how everything works. I recommend something like this as well:
#define TRACE_LEN 128
volatile int dbg_trace[TRACE_LEN]
volatile int trace_at;
control_isr()
{
int err = setpoint - measured;
stuff...;
if(trace_at < TRACE_LEN)
{
dbg_trace[trace_at] = err;
trace_at++;
}
}
void main_program()
{
if(cmd == 't')
{
trace_at = 0; // start tracing
setpoint_change();
while(trace_at < TRACE_LEN) ; // wait until full
for(...) // print the table to uart as comma-separated list you can copy-paste in Excel for comparing different traces, seeing min/max, plotting a graph, etc...
}
}
You seem to be pondering about the interval you do the calculation. Do it every time you have some relevant input data to process, or on a similar timescale. This is non-critical - you just need to scale the P,I,D terms linearly if you change your timebase.