ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_touch_cal.c
Go to the documentation of this file.
1
27
28#include "ra8_touch_cal.h"
29
30#include <stddef.h>
31#include <stdint.h>
32#include <string.h>
33
34#include "ra8_attributes.h"
35#include "ra8_err.h"
36
37/* ===========================================================================
38 * Internal constants
39 * ===========================================================================
40 */
41
43typedef enum : uint8_t {
44 k_m3_00 = 0U,
45 k_m3_01 = 1U,
46 k_m3_02 = 2U,
47 k_m3_10 = 3U,
48 k_m3_11 = 4U,
49 k_m3_12 = 5U,
50 k_m3_20 = 6U,
51 k_m3_21 = 7U,
52 k_m3_22 = 8U,
53 k_m3_len = 9U,
55
57typedef enum : uint8_t {
58 k_coeff_a = 0U,
59 k_coeff_b = 1U,
60 k_coeff_c = 2U,
61 k_coeff_d = 3U,
62 k_coeff_e = 4U,
63 k_coeff_f = 5U,
65
80
93static const float s_min_det = 1.0e-3F;
94
96static const float s_round_bias = 0.5F;
97
98/* ===========================================================================
99 * Internal helpers
100 * ===========================================================================
101 */
102
118static void internal_pack_le32(uint8_t* dst, uint32_t word)
119{
120 dst[0] = (uint8_t)(word & (uint32_t)k_internal_byte_mask);
121 dst[1] = (uint8_t)((word >> (uint32_t)k_internal_byte_shift_8) & (uint32_t)k_internal_byte_mask);
122 dst[2] = (uint8_t)((word >> (uint32_t)k_internal_byte_shift_16) & (uint32_t)k_internal_byte_mask);
123 dst[3] = (uint8_t)((word >> (uint32_t)k_internal_byte_shift_24) & (uint32_t)k_internal_byte_mask);
124}
125
142static uint32_t internal_unpack_le32(const uint8_t* src)
143{
144 uint32_t w = (uint32_t)src[0];
145 w |= (uint32_t)src[1] << (uint32_t)k_internal_byte_shift_8;
146 w |= (uint32_t)src[2] << (uint32_t)k_internal_byte_shift_16;
147 w |= (uint32_t)src[3] << (uint32_t)k_internal_byte_shift_24;
148 return w;
149}
150
168static uint32_t internal_float_to_u32(float f)
169{
170 uint32_t out = 0U;
171 (void)memcpy(&out, &f, sizeof(out));
172 return out;
173}
174
191static float internal_u32_to_float(uint32_t w)
192{
193 float out = 0.0F;
194 (void)memcpy(&out, &w, sizeof(out));
195 return out;
196}
197
217static uint32_t internal_crc32(const uint8_t* data, size_t len)
218{
219 uint32_t crc = (uint32_t)k_internal_crc32_init;
220 for (size_t i = 0U; i < len; i++) {
221 crc ^= (uint32_t)data[i];
222 for (uint8_t b = 0U; b < (uint8_t)k_internal_bits_per_byte; b++) {
223 const uint32_t mask = (uint32_t)0U - (crc & 1U);
224 crc = (crc >> 1U) ^ ((uint32_t)k_internal_crc32_poly & mask);
225 }
226 }
227 return crc ^ (uint32_t)k_internal_crc32_init;
228}
229
251static void internal_solve3(const float a[k_m3_len], const float b[3], float x[3], bool* ok)
252{
253 const float det = (a[k_m3_00] * ((a[k_m3_11] * a[k_m3_22]) - (a[k_m3_12] * a[k_m3_21]))) -
254 (a[k_m3_01] * ((a[k_m3_10] * a[k_m3_22]) - (a[k_m3_12] * a[k_m3_20]))) +
255 (a[k_m3_02] * ((a[k_m3_10] * a[k_m3_21]) - (a[k_m3_11] * a[k_m3_20])));
256
257 const float abs_det = (det < 0.0F) ? -det : det;
258 if (abs_det < s_min_det) {
259 *ok = false;
260 return;
261 }
262
263 const float dx = (b[0] * ((a[k_m3_11] * a[k_m3_22]) - (a[k_m3_12] * a[k_m3_21]))) -
264 (a[k_m3_01] * ((b[1] * a[k_m3_22]) - (a[k_m3_12] * b[2]))) +
265 (a[k_m3_02] * ((b[1] * a[k_m3_21]) - (a[k_m3_11] * b[2])));
266
267 const float dy = (a[k_m3_00] * ((b[1] * a[k_m3_22]) - (a[k_m3_12] * b[2]))) -
268 (b[0] * ((a[k_m3_10] * a[k_m3_22]) - (a[k_m3_12] * a[k_m3_20]))) +
269 (a[k_m3_02] * ((a[k_m3_10] * b[2]) - (b[1] * a[k_m3_20])));
270
271 const float dz = (a[k_m3_00] * ((a[k_m3_11] * b[2]) - (b[1] * a[k_m3_21]))) -
272 (a[k_m3_01] * ((a[k_m3_10] * b[2]) - (b[1] * a[k_m3_20]))) +
273 (b[0] * ((a[k_m3_10] * a[k_m3_21]) - (a[k_m3_11] * a[k_m3_20])));
274
275 x[0] = dx / det;
276 x[1] = dy / det;
277 x[2] = dz / det;
278 *ok = true;
279}
280
298static int32_t internal_clip32(int32_t v, int32_t lo, int32_t hi)
299{
300 if (v < lo) {
301 return lo;
302 }
303 if (v > hi) {
304 return hi;
305 }
306 return v;
307}
308
309/* ===========================================================================
310 * Public API -- compute
311 * ===========================================================================
312 */
313
324typedef struct {
325 float Sx;
326 float Sy;
327 float Sxx;
328 float Syy;
329 float Sxy;
330 float Su;
331 float Sv;
332 float Sxu;
333 float Syu;
334 float Sxv;
335 float Syv;
337
361 const ra8_touch_cal_point_t* screen,
362 uint8_t n,
364{
365 s->Sx = 0.0F;
366 s->Sy = 0.0F;
367 s->Sxx = 0.0F;
368 s->Syy = 0.0F;
369 s->Sxy = 0.0F;
370 s->Su = 0.0F;
371 s->Sv = 0.0F;
372 s->Sxu = 0.0F;
373 s->Syu = 0.0F;
374 s->Sxv = 0.0F;
375 s->Syv = 0.0F;
376 for (uint8_t i = 0U; i < n; i++) {
377 const float xi = (float)raw[i].x;
378 const float yi = (float)raw[i].y;
379 const float ui = (float)screen[i].x;
380 const float vi = (float)screen[i].y;
381 s->Sx += xi;
382 s->Sy += yi;
383 s->Sxx += xi * xi;
384 s->Syy += yi * yi;
385 s->Sxy += xi * yi;
386 s->Su += ui;
387 s->Sv += vi;
388 s->Sxu += xi * ui;
389 s->Syu += yi * ui;
390 s->Sxv += xi * vi;
391 s->Syv += yi * vi;
392 }
393}
394
396 const ra8_touch_cal_point_t* screen,
397 uint8_t n,
398 ra8_touch_cal_matrix_t* out_mtx)
399{
400 if ((raw == nullptr) || (screen == nullptr) || (out_mtx == nullptr)) {
401 return k_ra8_err_null_ptr;
402 }
403 if ((n < (uint8_t)k_ra8_touch_cal_min_targets) || (n > (uint8_t)k_ra8_touch_cal_max_targets)) {
405 }
406
408 internal_accumulate_sums(raw, screen, n, &s);
409
410 const float fn = (float)n;
411 const float norm_mat[9] = {
412 s.Sxx,
413 s.Sxy,
414 s.Sx,
415 s.Sxy,
416 s.Syy,
417 s.Sy,
418 s.Sx,
419 s.Sy,
420 fn,
421 };
422 const float rhs_u[3] = {s.Sxu, s.Syu, s.Su};
423 const float rhs_v[3] = {s.Sxv, s.Syv, s.Sv};
424
425 float sol_u[3] = {0.0F, 0.0F, 0.0F};
426 float sol_v[3] = {0.0F, 0.0F, 0.0F};
427 bool ok_u = false;
428 bool ok_v = false;
429 internal_solve3(norm_mat, rhs_u, sol_u, &ok_u);
430 internal_solve3(norm_mat, rhs_v, sol_v, &ok_v);
431 // mcdc-deactivated: ra8_touch_cal_compute internal_solve3 success gate; A is the same 3x3 calibration matrix for both Bu and Bv solves, so internal_solve3 either succeeds for both right-hand sides (det(A) != 0) or fails for both (det(A) == 0) -- ok_u and ok_v are co-determined by the matrix conditioning.
432 if (!ok_u || !ok_v) {
434 }
435
436 out_mtx->a = sol_u[0];
437 out_mtx->b = sol_u[1];
438 out_mtx->c = sol_u[2];
439 out_mtx->d = sol_v[0];
440 out_mtx->e = sol_v[1];
441 out_mtx->f = sol_v[2];
442 return k_ra8_ok;
443}
444
445/* ===========================================================================
446 * Public API -- run (drives the on-screen sequence)
447 * ===========================================================================
448 */
449
451{
452 if ((cfg == nullptr) || (out_matrix == nullptr)) {
453 return k_ra8_err_null_ptr;
454 }
455 if ((cfg->draw_target == nullptr) || (cfg->read_raw == nullptr)) {
456 return k_ra8_err_null_ptr;
457 }
458 if ((cfg->screen_width == 0U) || (cfg->screen_height == 0U)) {
460 }
461 const uint32_t margin_total = (uint32_t)cfg->inset_px * 2U;
462 if ((margin_total >= (uint32_t)cfg->screen_width) ||
463 (margin_total >= (uint32_t)cfg->screen_height)) {
465 }
466
467 const int32_t w = (int32_t)cfg->screen_width;
468 const int32_t h = (int32_t)cfg->screen_height;
469 const int32_t ins = (int32_t)cfg->inset_px;
470
472 {ins, ins}, /* top-left */
473 {w - 1 - ins, ins}, /* top-right */
474 {w - 1 - ins, h - 1 - ins}, /* bottom-right */
475 {ins, h - 1 - ins}, /* bottom-left */
476 {w / (int32_t)k_internal_centre_div, h / (int32_t)k_internal_centre_div}, /* centre */
477 };
478
480 {0, 0},
481 {0, 0},
482 {0, 0},
483 {0, 0},
484 {0, 0},
485 };
486
487 for (uint8_t i = 0U; i < (uint8_t)k_ra8_touch_cal_n_targets; i++) {
488 const ra8_err_t er_draw = cfg->draw_target(cfg->draw_ctx, targets[i]);
489 if (er_draw != k_ra8_ok) {
490 return k_ra8_err_hw_error;
491 }
492 const ra8_err_t er_read = cfg->read_raw(cfg->read_ctx, &samples[i]);
493 if (er_read != k_ra8_ok) {
494 return k_ra8_err_hw_error;
495 }
496 }
497
498 return ra8_touch_cal_compute(samples, targets, (uint8_t)k_ra8_touch_cal_n_targets, out_matrix);
499}
500
501/* ===========================================================================
502 * Public API -- apply
503 * ===========================================================================
504 */
505
507 const ra8_touch_cal_matrix_t* matrix,
508 uint16_t screen_width,
509 uint16_t screen_height,
510 ra8_touch_cal_point_t* out_screen)
511{
512 if ((matrix == nullptr) || (out_screen == nullptr)) {
513 return k_ra8_err_null_ptr;
514 }
515 if ((screen_width == 0U) || (screen_height == 0U)) {
517 }
518
519 const float xf = (float)raw.x;
520 const float yf = (float)raw.y;
521 const float u = (matrix->a * xf) + (matrix->b * yf) + matrix->c;
522 const float v = (matrix->d * xf) + (matrix->e * yf) + matrix->f;
523
524 /* Round-to-nearest before clipping. */
525 const int32_t ui = (u >= 0.0F) ? (int32_t)(u + s_round_bias) : (int32_t)(u - s_round_bias);
526 const int32_t vi = (v >= 0.0F) ? (int32_t)(v + s_round_bias) : (int32_t)(v - s_round_bias);
527
528 out_screen->x = internal_clip32(ui, 0, (int32_t)screen_width - 1);
529 out_screen->y = internal_clip32(vi, 0, (int32_t)screen_height - 1);
530 return k_ra8_ok;
531}
532
533/* ===========================================================================
534 * Public API -- save / load
535 * ===========================================================================
536 */
537
538ra8_err_t ra8_touch_cal_save(const ra8_touch_cal_matrix_t* matrix, uint8_t* dst, size_t dst_size)
539{
540 if ((matrix == nullptr) || (dst == nullptr)) {
541 return k_ra8_err_null_ptr;
542 }
543 if (dst_size < (size_t)k_ra8_touch_cal_blob_size) {
545 }
546
547 dst[(size_t)k_ra8_touch_cal_off_magic + 0U] = (uint8_t)k_ra8_touch_cal_magic_b0;
548 dst[(size_t)k_ra8_touch_cal_off_magic + 1U] = (uint8_t)k_ra8_touch_cal_magic_b1;
549 dst[(size_t)k_ra8_touch_cal_off_magic + 2U] = (uint8_t)k_ra8_touch_cal_magic_b2;
550 dst[(size_t)k_ra8_touch_cal_off_magic + 3U] = (uint8_t)k_ra8_touch_cal_magic_b3;
552 dst[(size_t)k_ra8_touch_cal_off_reserved + 0U] = 0U;
553 dst[(size_t)k_ra8_touch_cal_off_reserved + 1U] = 0U;
554 dst[(size_t)k_ra8_touch_cal_off_reserved + 2U] = 0U;
555
556 const float coeffs[6] = {
557 matrix->a,
558 matrix->b,
559 matrix->c,
560 matrix->d,
561 matrix->e,
562 matrix->f,
563 };
564 for (uint8_t i = 0U; i < 6U; i++) {
565 const uint32_t bits = internal_float_to_u32(coeffs[i]);
566 internal_pack_le32(&dst[(size_t)k_ra8_touch_cal_off_coeffs + ((size_t)i * sizeof(uint32_t))],
567 bits);
568 }
569
570 const uint32_t crc = internal_crc32(dst, (size_t)k_ra8_touch_cal_off_crc32);
572 return k_ra8_ok;
573}
574
576ra8_touch_cal_load(const uint8_t* src, size_t src_size, ra8_touch_cal_matrix_t* out_matrix)
577{
578 if ((src == nullptr) || (out_matrix == nullptr)) {
579 return k_ra8_err_null_ptr;
580 }
581 if (src_size < (size_t)k_ra8_touch_cal_blob_size) {
583 }
584
585 if ((src[(size_t)k_ra8_touch_cal_off_magic + 0U] != (uint8_t)k_ra8_touch_cal_magic_b0) ||
586 (src[(size_t)k_ra8_touch_cal_off_magic + 1U] != (uint8_t)k_ra8_touch_cal_magic_b1) ||
587 (src[(size_t)k_ra8_touch_cal_off_magic + 2U] != (uint8_t)k_ra8_touch_cal_magic_b2) ||
588 (src[(size_t)k_ra8_touch_cal_off_magic + 3U] != (uint8_t)k_ra8_touch_cal_magic_b3)) {
590 }
593 }
594 if ((src[(size_t)k_ra8_touch_cal_off_reserved + 0U] != 0U) ||
595 (src[(size_t)k_ra8_touch_cal_off_reserved + 1U] != 0U) ||
596 (src[(size_t)k_ra8_touch_cal_off_reserved + 2U] != 0U)) {
598 }
599
600 const uint32_t want_crc = internal_unpack_le32(&src[(size_t)k_ra8_touch_cal_off_crc32]);
601 const uint32_t have_crc = internal_crc32(src, (size_t)k_ra8_touch_cal_off_crc32);
602 if (want_crc != have_crc) {
604 }
605
606 float coeffs[6] = {0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F};
607 for (uint8_t i = 0U; i < 6U; i++) {
608 const uint32_t bits = internal_unpack_le32(
609 &src[(size_t)k_ra8_touch_cal_off_coeffs + ((size_t)i * sizeof(uint32_t))]);
610 coeffs[i] = internal_u32_to_float(bits);
611 }
612 out_matrix->a = coeffs[k_coeff_a];
613 out_matrix->b = coeffs[k_coeff_b];
614 out_matrix->c = coeffs[k_coeff_c];
615 out_matrix->d = coeffs[k_coeff_d];
616 out_matrix->e = coeffs[k_coeff_e];
617 out_matrix->f = coeffs[k_coeff_f];
618 return k_ra8_ok;
619}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Error Code Definitions for ra8-firmware.
@ k_ra8_err_crc_mismatch
CRC mismatch detected on received data.
Definition ra8_err.h:423
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
@ k_ra8_err_hw_error
Generic hardware fault detected (error flag set, fault interrupt).
Definition ra8_err.h:310
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
Definition ra8_err.h:167
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
ra8_err_t ra8_touch_cal_compute(const ra8_touch_cal_point_t *raw, const ra8_touch_cal_point_t *screen, uint8_t n, ra8_touch_cal_matrix_t *out_mtx)
Compute an affine transform from N target/sample pairs.
static const float s_round_bias
Round-to-nearest bias for float->int conversion.
affine_coeff_idx_t
Affine calibration coefficient indices (a..f).
@ k_coeff_b
Coeff b.
@ k_coeff_c
Coeff c.
@ k_coeff_f
Coeff f.
@ k_coeff_e
Coeff e.
@ k_coeff_d
Coeff d.
@ k_coeff_a
Coeff a.
ra8_err_t ra8_touch_cal_save(const ra8_touch_cal_matrix_t *matrix, uint8_t *dst, size_t dst_size)
Serialise a calibration matrix to a fixed-size byte blob.
static const float s_min_det
Floor for |det| below which the normal equations are treated as singular.
ra8_err_t ra8_touch_cal_load(const uint8_t *src, size_t src_size, ra8_touch_cal_matrix_t *out_matrix)
Deserialise a calibration matrix from a byte blob.
static float internal_u32_to_float(uint32_t w)
Bitwise reinterpret a uint32_t as a float.
mat3_idx_t
Row-major flat indices of a 3x3 matrix (a[r*3+c]).
@ k_m3_22
M3 22.
@ k_m3_20
M3 20.
@ k_m3_00
M3 00.
@ k_m3_11
M3 11.
@ k_m3_02
M3 02.
@ k_m3_21
M3 21.
@ k_m3_01
M3 01.
@ k_m3_12
M3 12.
@ k_m3_10
M3 10.
@ k_m3_len
M3 length.
static int32_t internal_clip32(int32_t v, int32_t lo, int32_t hi)
Clip a value to [lo, hi].
static void internal_solve3(const float a[k_m3_len], const float b[3], float x[3], bool *ok)
Solve a 3x3 system A * x = b via Cramer's rule.
internal_const_t
Module-private numeric constants (no magic numbers).
@ k_internal_crc32_init
IEEE 802.3 CRC seed.
@ k_internal_byte_shift_16
Shift to byte 2.
@ k_internal_bits_per_byte
Bits in one byte.
@ k_internal_byte_shift_8
Shift to byte 1.
@ k_internal_centre_div
Halve to get panel centre.
@ k_internal_byte_shift_24
Shift to byte 3.
@ k_internal_crc32_poly
IEEE 802.3 reversed polynomial.
@ k_internal_byte_mask
8-bit mask for byte extracts.
ra8_err_t ra8_touch_cal_apply(ra8_touch_cal_point_t raw, const ra8_touch_cal_matrix_t *matrix, uint16_t screen_width, uint16_t screen_height, ra8_touch_cal_point_t *out_screen)
Apply a calibration matrix to a single raw touch sample.
static uint32_t internal_crc32(const uint8_t *data, size_t len)
Compute the IEEE 802.3 CRC32 of a byte range.
static uint32_t internal_unpack_le32(const uint8_t *src)
Unpack a 32-bit little-endian word from a byte buffer.
static void internal_pack_le32(uint8_t *dst, uint32_t word)
Pack a 32-bit little-endian word into a byte buffer.
static void internal_accumulate_sums(const ra8_touch_cal_point_t *raw, const ra8_touch_cal_point_t *screen, uint8_t n, internal_lsq_sums_t *s)
Accumulate the 11 least-squares sums over n sample pairs.
ra8_err_t ra8_touch_cal_run(const ra8_touch_cal_run_cfg_t *cfg, ra8_touch_cal_matrix_t *out_matrix)
Drive the on-screen calibration sequence and produce a matrix.
static uint32_t internal_float_to_u32(float f)
Bitwise reinterpret a float as a uint32_t.
Resistive/capacitive touch-screen calibration utility.
@ k_ra8_touch_cal_blob_size
Bytes in ra8_touch_cal_save.
@ k_ra8_touch_cal_min_targets
Floor for a unique solve.
@ k_ra8_touch_cal_n_targets
4 corners + centre.
@ k_ra8_touch_cal_max_targets
Ceiling for the fit.
@ k_ra8_touch_cal_storage_version
On-disk format version.
@ k_ra8_touch_cal_magic_b3
'L'
@ k_ra8_touch_cal_magic_b0
'T'
@ k_ra8_touch_cal_magic_b2
'A'
@ k_ra8_touch_cal_magic_b1
'C'
@ k_ra8_touch_cal_off_reserved
Offset of 3 reserved zero bytes.
@ k_ra8_touch_cal_off_magic
Offset of 'TCAL' magic.
@ k_ra8_touch_cal_off_version
Offset of version byte.
@ k_ra8_touch_cal_off_crc32
Offset of the CRC32 trailer.
@ k_ra8_touch_cal_off_coeffs
Offset of the 6-float coeff array.
Accumulated least-squares sums driving the calibration solve.
float Sx
Sum of raw x.
float Sv
Sum of screen y.
float Syu
Sum of raw y * screen x.
float Su
Sum of screen x.
float Sxx
Sum of raw x^2.
float Syy
Sum of raw y^2.
float Sxy
Sum of raw x*y.
float Sxu
Sum of raw x * screen x.
float Sxv
Sum of raw x * screen y.
float Sy
Sum of raw y.
float Syv
Sum of raw y * screen y.
2-D affine transform screen = [a b; d e] * raw + [c; f].
float e
Row 1, column 1: raw-Y gain on screen-Y.
float b
Row 0, column 1: raw-Y gain on screen-X.
float d
Row 1, column 0: raw-X gain on screen-Y.
float f
Row 1 offset: screen-Y bias.
float a
Row 0, column 0: raw-X gain on screen-X.
float c
Row 0 offset: screen-X bias.
One (x, y) integer coordinate pair.
int32_t y
Vertical coordinate.
int32_t x
Horizontal coordinate.
Configuration for ra8_touch_cal_run.
ra8_touch_cal_read_raw_fn_t read_raw
Raw sample reader.
ra8_touch_cal_draw_target_fn_t draw_target
Cross-hair painter.
void * draw_ctx
Forwarded to painter.
uint16_t inset_px
Margin from panel edge to corner targets.
uint16_t screen_height
Panel height, pixels.
void * read_ctx
Forwarded to reader.
uint16_t screen_width
Panel width, pixels.