Add MLXtest, 4.8ms for parsing image in standard range

This commit is contained in:
Edward Emelianov 2025-09-11 20:13:53 +03:00
parent 8be4f0975e
commit 93e0b16f79
40 changed files with 3128 additions and 2 deletions

View File

@ -367,9 +367,9 @@ SEND("\n070a="); printuhex(IMD_VAL(REG_IGAIN)); newline();
Tar = Tar*Tar*Tar*Tar; Tar = Tar*Tar*Tar*Tar;
float ac3 = alphaComp*alphaComp*alphaComp; float ac3 = alphaComp*alphaComp*alphaComp;
float Sx = ac3*IRcompens + alphaComp*ac3*Tar; float Sx = ac3*IRcompens + alphaComp*ac3*Tar;
Sx = params.KsTa * sqrt(sqrt(Sx)); Sx = params.KsTa * sqrtf(sqrtf(Sx));
float To = IRcompens / (alphaComp * (1.f - params.ksTo[1]) + Sx) + Tar; float To = IRcompens / (alphaComp * (1.f - params.ksTo[1]) + Sx) + Tar;
curval = sqrt(sqrt(To)) - 273.15; // To curval = sqrtf(sqrtf(To)) - 273.15; // To
// TODO: extended // TODO: extended
} }
mlx_image[pixno] = curval; mlx_image[pixno] = curval;

View File

@ -0,0 +1,10 @@
BINARY := mlxtest
# MCU code
MCU := F303xb
# change this linking script depending on particular MCU model,
LDSCRIPT := stm32f303xB.ld
DEFINES := -DUSB1_16
LDLIBS := -lm
include ../makefile.f3
include ../../makefile.stm32

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,34 @@
/*
* This file is part of the mlxtest project.
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "hardware.h"
static inline void gpio_setup(){
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN; // for USART and LEDs
for(int i = 0; i < 10000; ++i) nop();
// USB - alternate function 14 @ pins PA11/PA12; SWD - AF0 @PA13/14
GPIOA->AFR[1] = AFRf(14, 11) | AFRf(14, 12);
GPIOA->MODER = MODER_AF(11) | MODER_AF(12) | MODER_AF(13) | MODER_AF(14) | MODER_O(15);
GPIOB->MODER = MODER_O(0) | MODER_O(1);
GPIOB->ODR = 1;
}
void hw_setup(){
gpio_setup();
}

View File

@ -0,0 +1,31 @@
/*
* This file is part of the mlxtest project.
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stm32f3.h>
#define USBPU_port GPIOA
#define USBPU_pin (1<<15)
#define USBPU_ON() pin_clear(USBPU_port, USBPU_pin)
#define USBPU_OFF() pin_set(USBPU_port, USBPU_pin)
extern volatile uint32_t Tms;
void hw_setup();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,56 @@
/*
* This file is part of the mlxtest project.
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "hardware.h"
#include "proto.h"
#include "usb_dev.h"
#define MAXSTRLEN RBINSZ
volatile uint32_t Tms = 0;
void sys_tick_handler(void){
++Tms;
}
int main(void){
char inbuff[MAXSTRLEN+1];
if(StartHSE()){
SysTick_Config((uint32_t)72000); // 1ms
}else{
StartHSI();
SysTick_Config((uint32_t)48000); // 1ms
}
hw_setup();
USBPU_OFF();
USB_setup();
uint32_t ctr = Tms;
USBPU_ON();
while(1){
if(Tms - ctr > 499){
ctr = Tms;
pin_toggle(GPIOB, 1 << 1 | 1 << 0); // toggle LED @ PB0
}
int l = USB_receivestr(inbuff, MAXSTRLEN);
if(l < 0) USB_sendstr("ERROR: USB buffer overflow or string was too long\n");
else if(l){
const char *ans = parse_cmd(inbuff);
if(ans) USB_sendstr(ans);
}
}
}

View File

@ -0,0 +1,448 @@
/*
* This file is part of the mlxtest project.
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <math.h>
#include <stdint.h>
#include <string.h>
#include "strfunc.h"
#include "mlx90640.h"
#include "mlx90640_regs.h"
#include "testdata.h"
static const char *OK = "OK\n", *OKs = "OK ", *NOTEQ = "NOT equal!\n", *NOTEQi = "NOT equal on index ";
// tolerance of floating point comparison
#define FP_TOLERANCE (1e-3)
static fp_t mlx_image[MLX_PIXNO] = {0}; // ready image
void dumpIma(const fp_t im[MLX_PIXNO]){
for(int row = 0; row < MLX_H; ++row){
for(int col = 0; col < MLX_W; ++col){
printfl(*im++, 1);
USB_putbyte(' ');
}
newline();
}
}
#define GRAY_LEVELS (16)
// 16-level character set ordered by fill percentage (provided by user)
static const char* CHARS_16 = " .':;+*oxX#&%B$@";
void drawIma(const fp_t im[MLX_PIXNO]){
// Find min and max values
fp_t min_val = im[0], max_val = im[0];
const fp_t *iptr = im;
for(int row = 0; row < MLX_H; ++row){
for(int col = 0; col < MLX_W; ++col){
fp_t cur = *iptr++;
if(cur < min_val) min_val = cur;
else if(cur > max_val) max_val = cur;
}
}
fp_t range = max_val - min_val;
if(fabsf(range) < 0.001) range = 1.; // solid fill -> blank
// Generate and print ASCII art
iptr = im;
newline();
for(int row = 0; row < MLX_H; ++row){
for(int col = 0; col < MLX_W; ++col){
fp_t normalized = ((*iptr++) - min_val) / range;
// Map to character index (0 to 15)
int index = (int)(normalized * (GRAY_LEVELS-1) + 0.5);
// Ensure we stay within bounds
if(index < 0) index = 0;
else if(index > (GRAY_LEVELS-1)) index = (GRAY_LEVELS-1);
USB_putbyte(CHARS_16[index]);
}
newline();
}
newline();
}
static void chki(const char *name, int16_t param, int16_t standard){
USB_sendstr(name); USB_sendstr(" - ");
printi(param); USB_sendstr(" - ");
printi(standard); USB_sendstr(" - ");
if(param != standard){
USB_sendstr(NOTEQ);
}
USB_sendstr(OK);
}
static void chkf(const char *name, fp_t param, fp_t standard){
USB_sendstr(name); USB_sendstr(" - ");
printfl(param, 3); USB_sendstr(" - ");
printfl(standard, 3); USB_sendstr(" - ");
fp_t diff = (fabsf(param) + fabsf(standard)) * FP_TOLERANCE;
if(fabsf(param - standard) > diff){
USB_sendstr(NOTEQ);
}
USB_sendstr(OK);
}
static void chkfa(const char *name, const fp_t *ap, const fp_t *as, int n){
USB_sendstr(name); USB_sendstr(" - (array) - (size");
printi(n); USB_sendstr(") - ");
for(int i = 0; i < n; ++i){
fp_t diff = (fabsf(as[i]) + fabsf(ap[i])) * FP_TOLERANCE;
if(fabsf(ap[i] - as[i]) > diff){
USB_sendstr(NOTEQi); printi(i); newline();
return;
}
}
USB_sendstr(OKs);
int nmax = (n < 5) ? n : 5;
for(int i = 0; i < nmax; ++i){
printfl(ap[i], 2); USB_putbyte(' ');
}
newline();
}
static void chku8a(const char *name, const uint8_t *ap, const uint8_t *as, int n){
USB_sendstr(name); USB_sendstr(" - (array) - (size");
printi(n); USB_sendstr(") - ");
for(int i = 0; i < n; ++i){
if(ap[i] != as[i]){
USB_sendstr(NOTEQi); printi(i); newline();
return;
}
}
USB_sendstr(OKs);
int nmax = (n < 5) ? n : 5;
for(int i = 0; i < nmax; ++i){
printu(ap[i]); USB_putbyte(' ');
}
newline();
}
void chkImage(const fp_t Image[MLX_PIXNO], const fp_t To[MLX_PIXNO]){
chkfa("Image", Image, To, MLX_PIXNO);
}
void dump_parameters(MLX90640_params *params, const MLX90640_params *standard){
USB_sendstr("\n############################################\n# name - value - standard - test condition #\n");
USB_sendstr("############################################\n");
#define CHKI(f) do{chki(#f, params->f, standard->f);}while(0)
#define CHKF(f) do{chkf(#f, params->f, standard->f);}while(0)
#define CHKFA(f, n) do{chkfa(#f, params->f, standard->f, n);}while(0)
#define CHKU8A(f, n) do{chku8a(#f, params->f, standard->f, n);}while(0)
CHKI(kVdd);
CHKI(vdd25);
CHKF(KvPTAT);
CHKI(vPTAT25);
CHKF(alphaPTAT);
CHKI(gainEE);
CHKF(tgc);
CHKF(cpKv);
CHKF(cpKta);
CHKF(KsTa);
CHKFA(CT, 3);
CHKFA(KsTo, 4);
CHKFA(alpha, MLX_PIXNO);
CHKFA(offset, MLX_PIXNO);
CHKFA(kta, MLX_PIXNO);
CHKFA(kv, 4);
CHKFA(cpAlpha, 2);
CHKI(resolEE);
CHKI(cpOffset[0]); CHKI(cpOffset[1]);
CHKU8A(outliers, MLX_PIXNO);
#undef CHKI
#undef CHKF
}
/*****************************************************************************
Calculate parameters & values
*****************************************************************************/
// fill OCC/ACC row/col arrays
static void occacc(int8_t *arr, int l, const uint16_t *regstart){
int n = l >> 2; // divide by 4
int8_t *p = arr;
for(int i = 0; i < n; ++i){
register uint16_t val = *regstart++;
*p++ = (val & 0x000F) >> 0;
*p++ = (val & 0x00F0) >> 4;
*p++ = (val & 0x0F00) >> 8;
*p++ = (val ) >> 12;
}
for(int i = 0; i < l; ++i, ++arr){
if(*arr > 0x07) *arr -= 0x10;
}
}
// get all parameters' values from `dataarray`, return FALSE if something failed
int get_parameters(const uint16_t dataarray[MLX_DMA_MAXLEN], MLX90640_params *params){
#define CREG_VAL(reg) dataarray[CREG_IDX(reg)]
int8_t i8;
int16_t i16;
uint16_t *pu16;
uint16_t val = CREG_VAL(REG_VDD);
i8 = (int8_t) (val >> 8);
params->kVdd = i8 * 32; // keep sign
if(params->kVdd == 0) return FALSE;
i16 = val & 0xFF;
params->vdd25 = ((i16 - 0x100) * 32) - (1<<13);
val = CREG_VAL(REG_KVTPTAT);
i16 = (val & 0xFC00) >> 10;
if(i16 > 0x1F) i16 -= 0x40;
params->KvPTAT = (fp_t)i16 / (1<<12);
i16 = (val & 0x03FF);
if(i16 > 0x1FF) i16 -= 0x400;
params->KtPTAT = (fp_t)i16 / 8.;
params->vPTAT25 = (int16_t) CREG_VAL(REG_PTAT);
val = CREG_VAL(REG_APTATOCCS) >> 12;
params->alphaPTAT = val / 4. + 8.;
params->gainEE = (int16_t)CREG_VAL(REG_GAIN);
if(params->gainEE == 0) return FALSE;
int8_t occRow[MLX_H];
int8_t occColumn[MLX_W];
occacc(occRow, MLX_H, &CREG_VAL(REG_OCCROW14));
occacc(occColumn, MLX_W, &CREG_VAL(REG_OCCCOL14));
int8_t accRow[MLX_H];
int8_t accColumn[MLX_W];
occacc(accRow, MLX_H, &CREG_VAL(REG_ACCROW14));
occacc(accColumn, MLX_W, &CREG_VAL(REG_ACCCOL14));
val = CREG_VAL(REG_APTATOCCS);
// need to do multiplication instead of bitshift, so:
fp_t occRemScale = 1<<(val&0x0F),
occColumnScale = 1<<((val>>4)&0x0F),
occRowScale = 1<<((val>>8)&0x0F);
int16_t offavg = (int16_t) CREG_VAL(REG_OSAVG);
// even/odd column/row numbers are for starting from 1, so for starting from 0 we should swap them:
// even - for 1,3,5,...; odd - for 0,2,4,... etc
int8_t ktaavg[4];
// 0 - odd row, odd col; 1 - odd row even col; 2 - even row, odd col; 3 - even row, even col
val = CREG_VAL(REG_KTAAVGODDCOL);
ktaavg[2] = (int8_t)(val & 0xFF); // odd col (1,3,..), even row (2,4,..) -> col 0,2,..; row 1,3,..
ktaavg[0] = (int8_t)(val >> 8); // odd col, odd row -> col 0,2,..; row 0,2,..
val = CREG_VAL(REG_KTAAVGEVENCOL);
ktaavg[3] = (int8_t)(val & 0xFF); // even col, even row -> col 1,3,..; row 1,3,..
ktaavg[1] = (int8_t)(val >> 8); // even col, odd row -> col 1,3,..; row 0,2,..
// so index of ktaavg is 2*(row&1)+(col&1)
val = CREG_VAL(REG_KTAVSCALE);
uint8_t scale1 = ((val & 0xFF)>>4) + 8, scale2 = (val&0xF);
if(scale1 == 0 || scale2 == 0) return FALSE;
fp_t mul = (fp_t)(1<<scale2), div = (fp_t)(1<<scale1); // kta_scales
uint16_t a_r = CREG_VAL(REG_SENSIVITY); // alpha_ref
val = CREG_VAL(REG_SCALEACC);
fp_t *a = params->alpha;
uint32_t diva32 = 1 << (val >> 12);
fp_t diva = (fp_t)(diva32);
diva *= (fp_t)(1<<30); // alpha_scale
fp_t accRowScale = 1<<((val & 0x0f00)>>8),
accColumnScale = 1<<((val & 0x00f0)>>4),
accRemScale = 1<<(val & 0x0f);
pu16 = (uint16_t*)&CREG_VAL(REG_OFFAK1);
fp_t *kta = params->kta, *offset = params->offset;
uint8_t *ol = params->outliers;
for(int row = 0; row < MLX_H; ++row){
int idx = (row&1)<<1;
for(int col = 0; col < MLX_W; ++col){
// offset
register uint16_t rv = *pu16++;
i16 = (rv & 0xFC00) >> 10;
if(i16 > 0x1F) i16 -= 0x40;
*offset++ = (fp_t)offavg + (fp_t)occRow[row]*occRowScale + (fp_t)occColumn[col]*occColumnScale + (fp_t)i16*occRemScale;
// kta
i16 = (rv & 0xF) >> 1;
if(i16 > 0x03) i16 -= 0x08;
*kta++ = (ktaavg[idx|(col&1)] + i16*mul) / div;
// alpha
i16 = (rv & 0x3F0) >> 4;
if(i16 > 0x1F) i16 -= 0x40;
fp_t oft = (fp_t)a_r + accRow[row]*accRowScale + accColumn[col]*accColumnScale +i16*accRemScale;
*a++ = oft / diva;
*ol++ = (rv&1) ? 1 : 0;
}
}
scale1 = (CREG_VAL(REG_KTAVSCALE) >> 8) & 0xF; // kvscale
div = (fp_t)(1<<scale1);
val = CREG_VAL(REG_KVAVG);
// kv indexes: +2 for odd (ÎÅÞÅÔÎÙÈ) rows, +1 for odd columns, so:
// [ 3, 2; 1, 0] for left upper corner (because datashit counts from 1, not from 0!)
i16 = val >> 12; if(i16 > 0x07) i16 -= 0x10;
ktaavg[0] = (int8_t)i16; // odd col, odd row
i16 = (val & 0xF0) >> 4; if(i16 > 0x07) i16 -= 0x10;
ktaavg[1] = (int8_t)i16; // even col, odd row
i16 = (val & 0x0F00) >> 8; if(i16 > 0x07) i16 -= 0x10;
ktaavg[2] = (int8_t)i16; // odd col, even row
i16 = val & 0x0F; if(i16 > 0x07) i16 -= 0x10;
ktaavg[3] = (int8_t)i16; // even col, even row
for(int i = 0; i < 4; ++i) params->kv[i] = ktaavg[i] / div;
val = CREG_VAL(REG_CPOFF);
params->cpOffset[0] = (val & 0x03ff);
if(params->cpOffset[0] > 0x1ff) params->cpOffset[0] -= 0x400;
params->cpOffset[1] = val >> 10;
if(params->cpOffset[1] > 0x1f) params->cpOffset[1] -= 0x40;
params->cpOffset[1] += params->cpOffset[0];
val = ((CREG_VAL(REG_KTAVSCALE) & 0xF0) >> 4) + 8;
i8 = (int8_t)(CREG_VAL(REG_KVTACP) & 0xFF);
params->cpKta = (fp_t)i8 / (1<<val);
val = (CREG_VAL(REG_KTAVSCALE) & 0x0F00) >> 8;
i16 = CREG_VAL(REG_KVTACP) >> 8;
if(i16 > 0x7F) i16 -= 0x100;
params->cpKv = (fp_t)i16 / (1<<val);
i16 = CREG_VAL(REG_KSTATGC) & 0xFF;
if(i16 > 0x7F) i16 -= 0x100;
params->tgc = (fp_t)i16;
params->tgc /= 32.;
val = (CREG_VAL(REG_SCALEACC)>>12); // alpha_scale_CP
i16 = CREG_VAL(REG_ALPHA)>>10; // cp_P1_P0_ratio
if(i16 > 0x1F) i16 -= 0x40;
div = (fp_t)(1<<val);
div *= (fp_t)(1<<27);
params->cpAlpha[0] = (fp_t)(CREG_VAL(REG_ALPHA) & 0x03FF) / div;
div = (fp_t)(1<<7);
params->cpAlpha[1] = params->cpAlpha[0] * (1. + (fp_t)i16/div);
i8 = (int8_t)(CREG_VAL(REG_KSTATGC) >> 8);
params->KsTa = (fp_t)i8/(1<<13);
div = 1<<((CREG_VAL(REG_CT34) & 0x0F) + 8); // kstoscale
val = CREG_VAL(REG_KSTO12);
i8 = (int8_t)(val & 0xFF);
params->KsTo[0] = i8 / div;
i8 = (int8_t)(val >> 8);
params->KsTo[1] = i8 / div;
val = CREG_VAL(REG_KSTO34);
i8 = (int8_t)(val & 0xFF);
params->KsTo[2] = i8 / div;
i8 = (int8_t)(val >> 8);
params->KsTo[3] = i8 / div;
// CT1 = -40, CT2 = 0 -> start from zero index, so CT[0] is CT2, CT[1] is CT3, CT[2] is CT4
params->CT[0] = 0.; // 0degr - between ranges 1 and 2
val = CREG_VAL(REG_CT34);
mul = ((val & 0x3000)>>12)*10.; // step
params->CT[1] = ((val & 0xF0)>>4)*mul; // CT3 - between ranges 2 and 3
params->CT[2] = ((val & 0x0F00) >> 8)*mul + params->CT[1]; // CT4 - between ranges 3 and 4
// alphacorr for each range: 11.1.11
params->alphacorr[0] = 1./(1. + params->KsTo[0] * 40.);
params->alphacorr[1] = 1.;
params->alphacorr[2] = (1. + params->KsTo[1] * params->CT[1]);
params->alphacorr[3] = (1. + params->KsTo[2] * (params->CT[2] - params->CT[1])) * params->alphacorr[2];
params->resolEE = (uint8_t)((CREG_VAL(REG_KTAVSCALE) & 0x3000) >> 12);
// Don't forget to check 'outlier' flags for wide purpose
return TRUE;
#undef CREG_VAL
}
/**
* @brief process_subpage - calculate all parameters from `dataarray` into `mlx_image`
* @param subpageno - number of subpage
* @param simpleimage == 0 - simplest, 1 - narrow range, 2 - extended range
*/
fp_t *process_subpage(MLX90640_params *params, const int16_t Frame[MLX_DMA_MAXLEN], int subpageno, int simpleimage){
#define IMD_VAL(reg) Frame[IMD_IDX(reg)]
// 11.2.2.1. Resolution restore
// temporary:
fp_t resol_corr = (fp_t)(1<<params->resolEE) / (1<<2); // calibrated resol/current resol
//fp_t resol_corr = (fp_t)(1<<params->resolEE) / (1<<((reg_control_val[subpageno]&0x0C00)>>10)); // calibrated resol/current resol
//DBG("resolEE=%d, resolCur=%d", params->resolEE, ((reg_control_val[subpageno]&0x0C00)>>10));
// 11.2.2.2. Supply voltage value calculation
int16_t i16a = (int16_t)IMD_VAL(REG_IVDDPIX);
fp_t dvdd = resol_corr*i16a - params->vdd25;
dvdd /= params->kVdd;
fp_t dV = i16a - params->vdd25; // for next step
dV /= params->kVdd;
// 11.2.2.3. Ambient temperature calculation
i16a = (int16_t)IMD_VAL(REG_ITAPTAT);
int16_t i16b = (int16_t)IMD_VAL(REG_ITAVBE);
fp_t dTa = (fp_t)i16a / (i16a * params->alphaPTAT + i16b); // vptatart
dTa *= (fp_t)(1<<18);
dTa = (dTa / (1. + params->KvPTAT*dV)) - params->vPTAT25;
dTa = dTa / params->KtPTAT; // without 25degr - Ta0
// 11.2.2.4. Gain parameter calculation
i16a = (int16_t)IMD_VAL(REG_IGAIN);
fp_t Kgain = params->gainEE / (fp_t)i16a;
fp_t pixOS[2]; // pix_gain_CP_SPx
// 11.2.2.6.1
pixOS[0] = ((int16_t)IMD_VAL(REG_ICPSP0))*Kgain; // pix_OS_CP_SPx
pixOS[1] = ((int16_t)IMD_VAL(REG_ICPSP1))*Kgain;
for(int i = 0; i < 2; ++i){ // calc pixOS by gain
// 11.2.2.6.2
pixOS[i] -= params->cpOffset[i]*(1. + params->cpKta*dTa)*(1. + params->cpKv*dvdd);
}
// now make first approximation to image
uint16_t pixno = 0; // current pixel number - for indexing in parameters etc
for(int row = 0, rowidx = 0; row < MLX_H; ++row, rowidx ^= 2){
for(int col = 0, idx = rowidx; col < MLX_W; ++col, ++pixno, idx ^= 1){
uint8_t sp = (row&1)^(col&1); // subpage of current pixel
if(sp != subpageno) continue;
// 11.2.2.5.1
fp_t curval = (fp_t)(Frame[pixno]) * Kgain; // gain compensation
// 11.2.2.5.3
curval -= params->offset[pixno] * (1. + params->kta[pixno]*dTa) *
(1. + params->kv[idx]*dvdd); // add offset
// now `curval` is pix_OS == V_IR_emiss_comp (we can divide it by `emissivity` to compensate for it)
// 11.2.2.7: 'Pattern' is just subpage number!
fp_t IRcompens = curval - params->tgc * pixOS[subpageno]; // 11.2.2.8. Normalizing to sensitivity
if(simpleimage == 0){ // 13.3. Using the device in ?image mode?
curval = IRcompens;
}else{
// 11.2.2.8
fp_t alphaComp = params->alpha[pixno] - params->tgc * params->cpAlpha[subpageno];
alphaComp *= 1. + params->KsTa * dTa;
// 11.2.2.9: calculate To for basic range
fp_t Tar = dTa + 273.15 + 25.; // Ta+273.15
Tar = Tar*Tar*Tar*Tar; // T_aK4 (when \epsilon==1 this is T_{a-r} too)
fp_t ac3 = alphaComp*alphaComp*alphaComp;
fp_t Sx = ac3*IRcompens + alphaComp*ac3*Tar;
Sx = params->KsTo[1] * SQRT(SQRT(Sx));
fp_t To4 = IRcompens / (alphaComp * (1. - 273.15*params->KsTo[1]) + Sx) + Tar;
curval = SQRT(SQRT(To4)) - 273.15;
if(simpleimage == 2){ // 11.2.2.9.1.3. Extended To range calculation
int r = 0; // range 1 by default
fp_t ctx = -40.;
if(curval > params->CT[2]){ // range 4
r = 3; ctx = params->CT[2];
}else if(curval > params->CT[1]){ // range 3
r = 2; ctx = params->CT[1];
}else if(curval > params->CT[0]){ // range 2, default
r = 1; ctx = params->CT[0];
}
if(r != 1){ // recalculate for extended range if we are out of standard range
To4 = IRcompens / (alphaComp * params->alphacorr[r] * (1. + params->KsTo[r]*(curval - ctx))) + Tar;
curval = SQRT(SQRT(To4)) - 273.15;
}
}
}
mlx_image[pixno] = curval;
}
}
return mlx_image;
#undef IMD_VAL
}
int MLXtest(){
MLX90640_params p;
USB_sendstr(" Extract parameters - ");
if(!get_parameters(EEPROM, &p)) return 2;
USB_sendstr(OK);
dump_parameters(&p, &extracted_parameters);
fp_t *sp;
for(int i = 0; i < 2; ++i){
USB_sendstr(" 100 times process subpage - "); printi(i); USB_putbyte(' ');
uint32_t Tstart = Tms;
for(int _ = 0; _ < 100; ++_){
sp = process_subpage(&p, DataFrame[i], i, 2);
if(!sp) return 1;
}
USB_sendstr(OKs); printfl((Tms - Tstart)/100.f, 3); USB_sendstr(" ms\n");
dumpIma(sp);
chkImage(sp, ToFrame[i]);
}
drawIma(sp);
return 0;
}

View File

@ -0,0 +1,68 @@
/*
* This file is part of the mlxtest project.
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
// floating type & sqrt operator
typedef float fp_t;
#define SQRT(x) sqrtf((x))
// amount of pixels
#define MLX_W (32)
#define MLX_H (24)
#define MLX_PIXNO (MLX_W*MLX_H)
// pixels + service data
#define MLX_PIXARRSZ (MLX_PIXNO + 64)
typedef struct{
int16_t kVdd;
int16_t vdd25;
fp_t KvPTAT;
fp_t KtPTAT;
int16_t vPTAT25;
fp_t alphaPTAT;
int16_t gainEE;
fp_t tgc;
fp_t cpKv; // K_V_CP
fp_t cpKta; // K_Ta_CP
fp_t KsTa;
fp_t CT[3]; // range borders (0, 160, 320 degrC?)
fp_t KsTo[4]; // K_S_To for each range * 273.15
fp_t alphacorr[4]; // Alpha_corr for each range
fp_t alpha[MLX_PIXNO]; // full - with alpha_scale
fp_t offset[MLX_PIXNO];
fp_t kta[MLX_PIXNO]; // full K_ta - with scale1&2
fp_t kv[4]; // full - with scale; 0 - odd row, odd col; 1 - odd row even col; 2 - even row, odd col; 3 - even row, even col
fp_t cpAlpha[2]; // alpha_CP_subpage 0 and 1
uint8_t resolEE; // resolution_EE
int16_t cpOffset[2];
uint8_t outliers[MLX_PIXNO]; // outliers - bad pixels (if == 1)
} MLX90640_params;
// full amount of IMAGE data + EXTRA data (counts of uint16_t!)
#define MLX_DMA_MAXLEN (834)
int get_parameters(const uint16_t dataarray[MLX_DMA_MAXLEN], MLX90640_params *params);
void dump_parameters(MLX90640_params *params, const MLX90640_params *standard);
fp_t *process_subpage(MLX90640_params *params, const int16_t Frame[MLX_DMA_MAXLEN], int subpageno, int simpleimage);
void chkImage(const fp_t Image[MLX_PIXNO], const fp_t ToFrame[MLX_PIXNO]);
void dumpIma(const fp_t im[MLX_PIXNO]);
void drawIma(const fp_t im[MLX_PIXNO]);
int MLXtest();

View File

@ -0,0 +1,90 @@
/*
* This file is part of the mlxtest project.
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#define REG_STATUS 0x8000
#define REG_STATUS_OVWEN (1<<4)
#define REG_STATUS_NEWDATA (1<<3)
#define REG_STATUS_SPNO (1<<0)
#define REG_STATUS_SPMASK (3<<0)
#define REG_CONTROL 0x800D
#define REG_CONTROL_CHESS (1<<12)
#define REG_CONTROL_RES16 (0<<10)
#define REG_CONTROL_RES17 (1<<10)
#define REG_CONTROL_RES18 (2<<10)
#define REG_CONTROL_RES19 (3<<10)
#define REG_CONTROL_RESMASK (3<<10)
#define REG_CONTROL_REFR_05HZ (0<<7)
#define REG_CONTROL_REFR_1HZ (1<<7)
#define REG_CONTROL_REFR_2HZ (2<<7)
#define REG_CONTROL_REFR_4HZ (3<<7)
#define REG_CONTROL_REFR_8HZ (4<<7)
#define REG_CONTROL_REFR_16HZ (5<<7)
#define REG_CONTROL_REFR_32HZ (6<<7)
#define REG_CONTROL_REFR_64HZ (7<<7)
#define REG_CONTROL_SUBP1 (1<<4)
#define REG_CONTROL_SUBPMASK (3<<4)
#define REG_CONTROL_SUBPSEL (1<<3)
#define REG_CONTROL_DATAHOLD (1<<2)
#define REG_CONTROL_SUBPEN (1<<0)
// default value
#define REG_CONTROL_DEFAULT (REG_CONTROL_CHESS|REG_CONTROL_RES18|REG_CONTROL_REFR_2HZ|REG_CONTROL_SUBPEN)
// calibration data start & len
#define REG_CALIDATA 0x2400
#define REG_CALIDATA_LEN 832
#define REG_APTATOCCS 0x2410
#define REG_OSAVG 0x2411
#define REG_OCCROW14 0x2412
#define REG_OCCCOL14 0x2418
#define REG_SCALEACC 0x2420
#define REG_SENSIVITY 0x2421
#define REG_ACCROW14 0x2422
#define REG_ACCCOL14 0x2428
#define REG_GAIN 0x2430
#define REG_PTAT 0x2431
#define REG_KVTPTAT 0x2432
#define REG_VDD 0x2433
#define REG_KVAVG 0x2434
#define REG_ILCHESS 0x2435
#define REG_KTAAVGODDCOL 0x2436
#define REG_KTAAVGEVENCOL 0x2437
#define REG_KTAVSCALE 0x2438
#define REG_ALPHA 0x2439
#define REG_CPOFF 0x243A
#define REG_KVTACP 0x243B
#define REG_KSTATGC 0x243C
#define REG_KSTO12 0x243D
#define REG_KSTO34 0x243E
#define REG_CT34 0x243F
#define REG_OFFAK1 0x2440
// index of register in array (from REG_CALIDATA)
#define CREG_IDX(addr) ((addr)-REG_CALIDATA)
#define REG_IMAGEDATA 0x0400
#define REG_ITAVBE 0x0700
#define REG_ICPSP0 0x0708
#define REG_IGAIN 0x070A
#define REG_ITAPTAT 0x0720
#define REG_ICPSP1 0x0728
#define REG_IVDDPIX 0x072A
// index of register in array (from REG_IMAGEDATA)
#define IMD_IDX(addr) ((addr)-REG_IMAGEDATA)

BIN
F3:F303/MLX90640test/mlxtest.bin Executable file

Binary file not shown.

View File

@ -0,0 +1 @@
-std=c17

View File

@ -0,0 +1,7 @@
// Add predefined macros for your project here. For example:
// #define THE_ANSWER 42
#define EBUG
#define STM32F3
#define STM32F303xb
#define __thumb2__ 1
#define __ARM_ARCH_7M__

View File

@ -0,0 +1 @@
[General]

View File

@ -0,0 +1,215 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 17.0.1, 2025-09-11T19:08:14. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{7bd84e39-ca37-46d3-be9d-99ebea85bc0d}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="qlonglong">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoDetect">true</value>
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">KOI8-R</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.LineEndingBehavior">0</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="int" key="EditorConfiguration.PreferAfterWhitespaceComments">0</value>
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">false</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
<value type="bool" key="EditorConfiguration.tintMarginArea">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
<value type="bool" key="AutoTest.Framework.Boost">true</value>
<value type="bool" key="AutoTest.Framework.CTest">false</value>
<value type="bool" key="AutoTest.Framework.Catch">true</value>
<value type="bool" key="AutoTest.Framework.GTest">true</value>
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
</valuemap>
<value type="bool" key="AutoTest.ApplyFilter">false</value>
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
<valuelist type="QVariantList" key="AutoTest.PathFilters"/>
<value type="int" key="AutoTest.RunAfterBuild">0</value>
<value type="bool" key="AutoTest.UseGlobal">true</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">2</value>
<value type="bool" key="ClangTools.PreferConfigFile">false</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="DeviceType">Desktop</value>
<value type="bool" key="HasPerBcDcs">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{65a14f9e-e008-4c1b-89df-4eaa4774b6e3}</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Big/Data/00__Electronics/STM32/F303-nolib/blink</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Default</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="qlonglong">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

View File

@ -0,0 +1 @@
-std=c++17

View File

@ -0,0 +1,22 @@
hardware.c
hardware.h
main.c
mlx90640.c
mlx90640.h
mlx90640_regs.h
proto.c
proto.h
ringbuffer.c
ringbuffer.h
strfunc.c
strfunc.h
testdata.h
usb.c
usb.h
usb_descr.c
usb_descr.h
usb_dev.c
usb_dev.h
usb_lib.c
usb_lib.h
usbhw.h

View File

@ -0,0 +1,6 @@
.
../inc
../inc/Fx
../inc/cm
../inc/ld
../inc/startup

View File

@ -0,0 +1 @@
-54,-56,-50,-60,-50,-56,-48,-60,-49,-57,-48,-61,-49,-59,-48,-63,-46,-59,-48,-65,-49,-61,-49,-66,-47,-64,-50,-69,-53,-66,-51,-75,-61,-64,-65,-61,-57,-63,-63,-61,-56,-63,-63,-62,-56,-65,-63,-64,-53,-65,-63,-65,-55,-66,-63,-66,-53,-69,-63,-69,-58,-70,-64,-74,-53,-56,-50,-60,-51,-55,-48,-60,-48,-56,-47,-61,-47,-58,-47,-62,-45,-58,-48,-64,-47,-59,-47,-67,-46,-62,-49,-68,-52,-65,-50,-75,-60,-62,-65,-61,-58,-61,-63,-61,-55,-62,-62,-61,-54,-64,-62,-62,-51,-64,-62,-63,-53,-66,-62,-66,-52,-67,-63,-67,-59,-71,-64,-75,-50,-54,-49,-58,-48,-54,-48,-59,-46,-55,-47,-60,-47,-55,-47,-61,-43,-57,-46,-62,-46,-59,-47,-65,-46,-61,-48,-67,-52,-65,-50,-74,-57,-63,-65,-60,-56,-62,-64,-61,-54,-63,-62,-62,-54,-62,-62,-63,-51,-64,-62,-63,-52,-66,-62,-65,-53,-69,-62,-69,-59,-71,-64,-75,-53,-54,-49,-59,-49,-54,-47,-58,-45,-54,-47,-60,-47,-55,-46,-62,-46,-57,-47,-62,-47,-58,-47,-64,-46,-61,-48,-66,-50,-64,-49,-73,-62,-64,-66,-62,-59,-63,-64,-61,-54,-63,-64,-62,-55,-63,-63,-63,-54,-65,-63,-64,-55,-65,-63,-65,-54,-68,-64,-68,-58,-72,-64,-75,-52,-53,-49,-58,-50,-53,-47,-58,-48,-54,-47,-60,-47,-55,-46,-61,-45,-57,-47,-63,-46,-58,-48,-65,-46,-61,-49,-67,-50,-64,-50,-73,-62,-65,-67,-63,-59,-63,-65,-63,-57,-64,-64,-63,-56,-65,-64,-64,-54,-66,-64,-65,-55,-68,-64,-66,-54,-69,-65,-69,-58,-72,-66,-75,-50,-54,-49,-58,-47,-53,-49,-59,-45,-55,-48,-60,-45,-55,-49,-62,-44,-57,-47,-63,-45,-58,-48,-65,-47,-62,-51,-69,-52,-65,-52,-75,-61,-66,-68,-64,-58,-65,-67,-64,-56,-66,-66,-65,-55,-66,-65,-65,-54,-67,-64,-66,-55,-68,-65,-68,-54,-71,-66,-71,-60,-73,-67,-77,-54,-54,-52,-59,-51,-54,-50,-59,-49,-55,-49,-60,-48,-55,-49,-62,-46,-58,-49,-65,-48,-60,-49,-66,-46,-63,-50,-68,-52,-64,-52,-75,-66,-69,-73,-66,-62,-68,-69,-65,-60,-67,-68,-65,-59,-67,-69,-66,-56,-69,-67,-68,-57,-69,-66,-69,-54,-71,-66,-70,-60,-73,-69,-77,-56,-54,-52,-59,-53,-54,-51,-60,-50,-56,-51,-61,-49,-56,-50,-63,-47,-58,-50,-65,-48,-59,-51,-66,-47,-62,-52,-68,-54,-66,-53,-76,-69,-71,-73,-69,-66,-69,-72,-68,-62,-69,-71,-67,-61,-69,-70,-68,-58,-70,-68,-70,-58,-70,-68,-69,-56,-71,-68,-71,-62,-74,-69,-79,-54,-55,-53,-60,-52,-55,-53,-61,-51,-57,-54,-63,-50,-57,-53,-64,-49,-59,-51,-65,-50,-60,-53,-66,-49,-63,-54,-71,-55,-66,-55,-76,-69,-73,-76,-70,-66,-71,-75,-69,-65,-72,-74,-71,-63,-71,-71,-71,-60,-71,-70,-70,-61,-71,-70,-70,-59,-74,-70,-75,-63,-76,-70,-80,-62,-57,-58,-62,-59,-57,-56,-62,-56,-59,-54,-64,-55,-60,-54,-65,-52,-61,-54,-67,-53,-62,-54,-68,-53,-64,-56,-71,-55,-67,-56,-75,-77,-77,-83,-74,-74,-74,-79,-73,-70,-74,-76,-73,-68,-74,-75,-73,-64,-75,-74,-73,-64,-74,-73,-74,-63,-76,-74,-75,-64,-78,-74,-80,-66,-58,-61,-65,-61,-59,-57,-64,-57,-60,-56,-66,-55,-60,-56,-67,-55,-63,-57,-69,-54,-63,-56,-69,-54,-64,-57,-72,-59,-67,-58,-77,-82,-80,-86,-78,-77,-78,-81,-76,-73,-78,-79,-76,-69,-76,-78,-75,-68,-78,-78,-76,-67,-76,-77,-76,-66,-78,-77,-77,-70,-80,-77,-84,-67,-60,-65,-66,-62,-61,-63,-68,-62,-62,-62,-67,-60,-64,-61,-70,-57,-65,-59,-69,-56,-65,-59,-71,-58,-67,-62,-74,-63,-69,-63,-78,-94,-91,-100,-88,-88,-88,-95,-87,-85,-87,-92,-86,-82,-88,-90,-88,-78,-87,-88,-84,-77,-87,-87,-86,-76,-89,-87,-90,-80,-90,-87,-93
1 -54 -56 -50 -60 -50 -56 -48 -60 -49 -57 -48 -61 -49 -59 -48 -63 -46 -59 -48 -65 -49 -61 -49 -66 -47 -64 -50 -69 -53 -66 -51 -75 -61 -64 -65 -61 -57 -63 -63 -61 -56 -63 -63 -62 -56 -65 -63 -64 -53 -65 -63 -65 -55 -66 -63 -66 -53 -69 -63 -69 -58 -70 -64 -74 -53 -56 -50 -60 -51 -55 -48 -60 -48 -56 -47 -61 -47 -58 -47 -62 -45 -58 -48 -64 -47 -59 -47 -67 -46 -62 -49 -68 -52 -65 -50 -75 -60 -62 -65 -61 -58 -61 -63 -61 -55 -62 -62 -61 -54 -64 -62 -62 -51 -64 -62 -63 -53 -66 -62 -66 -52 -67 -63 -67 -59 -71 -64 -75 -50 -54 -49 -58 -48 -54 -48 -59 -46 -55 -47 -60 -47 -55 -47 -61 -43 -57 -46 -62 -46 -59 -47 -65 -46 -61 -48 -67 -52 -65 -50 -74 -57 -63 -65 -60 -56 -62 -64 -61 -54 -63 -62 -62 -54 -62 -62 -63 -51 -64 -62 -63 -52 -66 -62 -65 -53 -69 -62 -69 -59 -71 -64 -75 -53 -54 -49 -59 -49 -54 -47 -58 -45 -54 -47 -60 -47 -55 -46 -62 -46 -57 -47 -62 -47 -58 -47 -64 -46 -61 -48 -66 -50 -64 -49 -73 -62 -64 -66 -62 -59 -63 -64 -61 -54 -63 -64 -62 -55 -63 -63 -63 -54 -65 -63 -64 -55 -65 -63 -65 -54 -68 -64 -68 -58 -72 -64 -75 -52 -53 -49 -58 -50 -53 -47 -58 -48 -54 -47 -60 -47 -55 -46 -61 -45 -57 -47 -63 -46 -58 -48 -65 -46 -61 -49 -67 -50 -64 -50 -73 -62 -65 -67 -63 -59 -63 -65 -63 -57 -64 -64 -63 -56 -65 -64 -64 -54 -66 -64 -65 -55 -68 -64 -66 -54 -69 -65 -69 -58 -72 -66 -75 -50 -54 -49 -58 -47 -53 -49 -59 -45 -55 -48 -60 -45 -55 -49 -62 -44 -57 -47 -63 -45 -58 -48 -65 -47 -62 -51 -69 -52 -65 -52 -75 -61 -66 -68 -64 -58 -65 -67 -64 -56 -66 -66 -65 -55 -66 -65 -65 -54 -67 -64 -66 -55 -68 -65 -68 -54 -71 -66 -71 -60 -73 -67 -77 -54 -54 -52 -59 -51 -54 -50 -59 -49 -55 -49 -60 -48 -55 -49 -62 -46 -58 -49 -65 -48 -60 -49 -66 -46 -63 -50 -68 -52 -64 -52 -75 -66 -69 -73 -66 -62 -68 -69 -65 -60 -67 -68 -65 -59 -67 -69 -66 -56 -69 -67 -68 -57 -69 -66 -69 -54 -71 -66 -70 -60 -73 -69 -77 -56 -54 -52 -59 -53 -54 -51 -60 -50 -56 -51 -61 -49 -56 -50 -63 -47 -58 -50 -65 -48 -59 -51 -66 -47 -62 -52 -68 -54 -66 -53 -76 -69 -71 -73 -69 -66 -69 -72 -68 -62 -69 -71 -67 -61 -69 -70 -68 -58 -70 -68 -70 -58 -70 -68 -69 -56 -71 -68 -71 -62 -74 -69 -79 -54 -55 -53 -60 -52 -55 -53 -61 -51 -57 -54 -63 -50 -57 -53 -64 -49 -59 -51 -65 -50 -60 -53 -66 -49 -63 -54 -71 -55 -66 -55 -76 -69 -73 -76 -70 -66 -71 -75 -69 -65 -72 -74 -71 -63 -71 -71 -71 -60 -71 -70 -70 -61 -71 -70 -70 -59 -74 -70 -75 -63 -76 -70 -80 -62 -57 -58 -62 -59 -57 -56 -62 -56 -59 -54 -64 -55 -60 -54 -65 -52 -61 -54 -67 -53 -62 -54 -68 -53 -64 -56 -71 -55 -67 -56 -75 -77 -77 -83 -74 -74 -74 -79 -73 -70 -74 -76 -73 -68 -74 -75 -73 -64 -75 -74 -73 -64 -74 -73 -74 -63 -76 -74 -75 -64 -78 -74 -80 -66 -58 -61 -65 -61 -59 -57 -64 -57 -60 -56 -66 -55 -60 -56 -67 -55 -63 -57 -69 -54 -63 -56 -69 -54 -64 -57 -72 -59 -67 -58 -77 -82 -80 -86 -78 -77 -78 -81 -76 -73 -78 -79 -76 -69 -76 -78 -75 -68 -78 -78 -76 -67 -76 -77 -76 -66 -78 -77 -77 -70 -80 -77 -84 -67 -60 -65 -66 -62 -61 -63 -68 -62 -62 -62 -67 -60 -64 -61 -70 -57 -65 -59 -69 -56 -65 -59 -71 -58 -67 -62 -74 -63 -69 -63 -78 -94 -91 -100 -88 -88 -88 -95 -87 -85 -87 -92 -86 -82 -88 -90 -88 -78 -87 -88 -84 -77 -87 -87 -86 -76 -89 -87 -90 -80 -90 -87 -93

View File

@ -0,0 +1,119 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# script for stm32f3x family
#
# stm32 devices support both JTAG and SWD transports.
#
source [find interface/stlink-v2-1.cfg]
source [find target/swj-dp.tcl]
source [find mem_helper.tcl]
if { [info exists CHIPNAME] } {
set _CHIPNAME $CHIPNAME
} else {
set _CHIPNAME stm32f3x
}
set _ENDIAN little
# Work-area is a space in RAM used for flash programming
# By default use 16kB
if { [info exists WORKAREASIZE] } {
set _WORKAREASIZE $WORKAREASIZE
} else {
set _WORKAREASIZE 0x4000
}
# JTAG speed should be <= F_CPU/6. F_CPU after reset is 8MHz, so use F_JTAG = 1MHz
#
# Since we may be running of an RC oscilator, we crank down the speed a
# bit more to be on the safe side. Perhaps superstition, but if are
# running off a crystal, we can run closer to the limit. Note
# that there can be a pretty wide band where things are more or less stable.
adapter speed 1000
adapter srst delay 100
if {[using_jtag]} {
jtag_ntrst_delay 100
}
#jtag scan chain
if { [info exists CPUTAPID] } {
set _CPUTAPID $CPUTAPID
} else {
if { [using_jtag] } {
# See STM Document RM0316
# Section 29.6.3 - corresponds to Cortex-M4 r0p1
set _CPUTAPID 0x4ba00477
} {
set _CPUTAPID 0x2ba01477
}
}
swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu
if {[using_jtag]} {
jtag newtap $_CHIPNAME bs -irlen 5
}
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap
$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
set _FLASHNAME $_CHIPNAME.flash
flash bank $_FLASHNAME stm32f1x 0 0 0 0 $_TARGETNAME
reset_config srst_nogate
if {![using_hla]} {
# if srst is not fitted use SYSRESETREQ to
# perform a soft reset
cortex_m reset_config sysresetreq
}
proc stm32f3x_default_reset_start {} {
# Reset clock is HSI (8 MHz)
adapter speed 1000
}
proc stm32f3x_default_examine_end {} {
# Enable debug during low power modes (uses more power)
mmw 0xe0042004 0x00000007 0 ;# DBGMCU_CR |= DBG_STANDBY | DBG_STOP | DBG_SLEEP
# Stop watchdog counters during halt
mmw 0xe0042008 0x00001800 0 ;# DBGMCU_APB1_FZ |= DBG_IWDG_STOP | DBG_WWDG_STOP
}
proc stm32f3x_default_reset_init {} {
# Configure PLL to boost clock to HSI x 8 (64 MHz)
mww 0x40021004 0x00380400 ;# RCC_CFGR = PLLMUL[3:1] | PPRE1[2]
mmw 0x40021000 0x01000000 0 ;# RCC_CR |= PLLON
mww 0x40022000 0x00000012 ;# FLASH_ACR = PRFTBE | LATENCY[1]
sleep 10 ;# Wait for PLL to lock
mmw 0x40021004 0x00000002 0 ;# RCC_CFGR |= SW[1]
# Boost JTAG frequency
adapter speed 8000
}
# Default hooks
$_TARGETNAME configure -event examine-end { stm32f3x_default_examine_end }
$_TARGETNAME configure -event reset-start { stm32f3x_default_reset_start }
$_TARGETNAME configure -event reset-init { stm32f3x_default_reset_init }
tpiu create $_CHIPNAME.tpiu -dap $_CHIPNAME.dap -ap-num 0 -baseaddr 0xE0040000
lappend _telnet_autocomplete_skip _proc_pre_enable_$_CHIPNAME.tpiu
proc _proc_pre_enable_$_CHIPNAME.tpiu {_targetname} {
targets $_targetname
# Set TRACE_IOEN; TRACE_MODE is set to async; when using sync
# change this value accordingly to configure trace pins
# assignment
mmw 0xe0042004 0x00000020 0
}
$_CHIPNAME.tpiu configure -event pre-enable "_proc_pre_enable_$_CHIPNAME.tpiu $_TARGETNAME"

View File

@ -0,0 +1,49 @@
/*
* This file is part of the mlxtest project.
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stm32f3.h>
#include <string.h>
#include "mlx90640.h"
#include "strfunc.h"
#include "usb_dev.h"
#include "version.inc"
const char *helpstring =
"https://github.com/eddyem/stm32samples/tree/master/F3:F303/USB_template build#" BUILD_NUMBER " @ " BUILD_DATE "\n"
"T - print current Tms\n"
"R - run MLX test\n"
;
const char *parse_cmd(const char *buf){
if(buf[1]) return buf; // echo wrong data
switch(*buf){
case 'T':
USB_sendstr("T=");
USB_sendstr(u2str(Tms));
newline();
break;
case 'R':
if(MLXtest()) USB_sendstr("error!\n");
break;
default: // help
USB_sendstr(helpstring);
break;
}
return NULL;
}

View File

@ -0,0 +1,22 @@
/*
* This file is part of the mlxtest project.
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
char *parse_cmd(char *buf);

View File

@ -0,0 +1,166 @@
/*
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ringbuffer.h"
static int datalen(ringbuffer *b){
if(b->tail >= b->head) return (b->tail - b->head);
else return (b->length - b->head + b->tail);
}
// stored data length
int RB_datalen(ringbuffer *b){
if(b->busy) return -1;
b->busy = 1;
int l = datalen(b);
b->busy = 0;
return l;
}
static int hasbyte(ringbuffer *b, uint8_t byte){
if(b->head == b->tail) return -1; // no data in buffer
int startidx = b->head;
if(b->head > b->tail){ //
for(int found = b->head; found < b->length; ++found)
if(b->data[found] == byte) return found;
startidx = 0;
}
for(int found = startidx; found < b->tail; ++found)
if(b->data[found] == byte) return found;
return -1;
}
/**
* @brief RB_hasbyte - check if buffer has given byte stored
* @param b - buffer
* @param byte - byte to find
* @return index if found, -1 if none or busy
*/
int RB_hasbyte(ringbuffer *b, uint8_t byte){
if(b->busy) return -1;
b->busy = 1;
int ret = hasbyte(b, byte);
b->busy = 0;
return ret;
}
// poor memcpy
static void mcpy(uint8_t *targ, const uint8_t *src, int l){
while(l--) *targ++ = *src++;
}
// increment head or tail
TRUE_INLINE void incr(ringbuffer *b, volatile int *what, int n){
*what += n;
if(*what >= b->length) *what -= b->length;
}
static int read(ringbuffer *b, uint8_t *s, int len){
int l = datalen(b);
if(!l) return 0;
if(l > len) l = len;
int _1st = b->length - b->head;
if(_1st > l) _1st = l;
if(_1st > len) _1st = len;
mcpy(s, b->data + b->head, _1st);
if(_1st < len && l > _1st){
mcpy(s+_1st, b->data, l - _1st);
incr(b, &b->head, l);
return l;
}
incr(b, &b->head, _1st);
return _1st;
}
/**
* @brief RB_read - read data from ringbuffer
* @param b - buffer
* @param s - array to write data
* @param len - max len of `s`
* @return bytes read or -1 if busy
*/
int RB_read(ringbuffer *b, uint8_t *s, int len){
if(b->busy) return -1;
b->busy = 1;
int r = read(b, s, len);
b->busy = 0;
return r;
}
static int readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len){
int idx = hasbyte(b, byte);
if(idx < 0) return 0;
int partlen = idx + 1 - b->head;
// now calculate length of new data portion
if(idx < b->head) partlen += b->length;
if(partlen > len) return -read(b, s, len);
return read(b, s, partlen);
}
/**
* @brief RB_readto fill array `s` with data until byte `byte` (with it)
* @param b - ringbuffer
* @param byte - check byte
* @param s - buffer to write data
* @param len - length of `s`
* @return amount of bytes written (negative, if len<data in buffer or buffer is busy)
*/
int RB_readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len){
if(b->busy) return -1;
b->busy = 1;
int n = readto(b, byte, s, len);
b->busy = 0;
return n;
}
static int write(ringbuffer *b, const uint8_t *str, int l){
int r = b->length - 1 - datalen(b); // rest length
if(l > r || !l) return 0;
int _1st = b->length - b->tail;
if(_1st > l) _1st = l;
mcpy(b->data + b->tail, str, _1st);
if(_1st < l){ // add another piece from start
mcpy(b->data, str+_1st, l-_1st);
}
incr(b, &b->tail, l);
return l;
}
/**
* @brief RB_write - write some data to ringbuffer
* @param b - buffer
* @param str - data
* @param l - length
* @return amount of bytes written or -1 if busy
*/
int RB_write(ringbuffer *b, const uint8_t *str, int l){
if(b->busy) return -1;
b->busy = 1;
int w = write(b, str, l);
b->busy = 0;
return w;
}
// just delete all information in buffer `b`
int RB_clearbuf(ringbuffer *b){
if(b->busy) return -1;
b->busy = 1;
b->head = 0;
b->tail = 0;
b->busy = 0;
return 1;
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#if defined STM32F0
#include <stm32f0.h>
#elif defined STM32F1
#include <stm32f1.h>
#elif defined STM32F3
#include <stm32f3.h>
#endif
typedef struct{
uint8_t *data; // data buffer
const int length; // its length
int head; // head index
int tail; // tail index
volatile int busy; // == TRUE if buffer is busy now
} ringbuffer;
int RB_read(ringbuffer *b, uint8_t *s, int len);
int RB_readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len);
int RB_hasbyte(ringbuffer *b, uint8_t byte);
int RB_write(ringbuffer *b, const uint8_t *str, int l);
int RB_datalen(ringbuffer *b);
int RB_clearbuf(ringbuffer *b);

View File

@ -0,0 +1,334 @@
/*
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <math.h>
#include <stdint.h>
#include <string.h>
#include "usb_dev.h"
/**
* @brief hexdump - dump hex array by 16 bytes in string
* @param arr - array to dump
* @param len - length of `arr`
*/
void hexdump(uint8_t *arr, uint16_t len){
char buf[52], *bptr = buf;
for(uint16_t l = 0; l < len; ++l, ++arr){
for(int16_t j = 1; j > -1; --j){
register uint8_t half = (*arr >> (4*j)) & 0x0f;
if(half < 10) *bptr++ = half + '0';
else *bptr++ = half - 10 + 'a';
}
if(l % 16 == 15){
*bptr++ = '\n';
*bptr = 0;
USB_sendstr(buf);
bptr = buf;
}else *bptr++ = ' ';
}
if(bptr != buf){
*bptr++ = '\n';
*bptr = 0;
USB_sendstr(buf);
}
}
/**
* @brief _2str - convert value into string buffer
* @param val - |value|
* @param minus - ==0 if value > 0
* @return buffer with number
*/
static char *_2str(uint32_t val, uint8_t minus){
static char strbuf[12];
char *bufptr = &strbuf[11];
*bufptr = 0;
if(!val){
*(--bufptr) = '0';
}else{
while(val){
uint32_t x = val / 10;
*(--bufptr) = (val - 10*x) + '0';
val = x;
}
}
if(minus) *(--bufptr) = '-';
return bufptr;
}
// return string with number `val`
char *u2str(uint32_t val){
return _2str(val, 0);
}
char *i2str(int32_t i){
uint8_t minus = 0;
uint32_t val;
if(i < 0){
minus = 1;
val = -i;
}else val = i;
return _2str(val, minus);
}
/**
* @brief uhex2str - print 32bit unsigned int as hex
* @param val - value
* @return string with number
*/
char *uhex2str(uint32_t val){
static char buf[12] = "0x";
int npos = 2;
uint8_t *ptr = (uint8_t*)&val + 3;
int8_t i, j, z=1;
for(i = 0; i < 4; ++i, --ptr){
if(*ptr == 0){ // omit leading zeros
if(i == 3) z = 0;
if(z) continue;
}
else z = 0;
for(j = 1; j > -1; --j){
uint8_t half = (*ptr >> (4*j)) & 0x0f;
if(half < 10) buf[npos++] = half + '0';
else buf[npos++] = half - 10 + 'a';
}
}
buf[npos] = 0;
return buf;
}
/**
* @brief omit_spaces - eliminate leading spaces and other trash in string
* @param buf - string
* @return - pointer to first character in `buf` > ' '
*/
const char *omit_spaces(const char *buf){
while(*buf){
if(*buf > ' ') break;
++buf;
}
return buf;
}
/**
* @brief getdec - read decimal number & return pointer to next non-number symbol
* @param buf - string
* @param N - number read
* @return Next non-number symbol. In case of overflow return `buf` and N==0xffffffff
*/
static const char *getdec(const char *buf, uint32_t *N){
char *start = (char*)buf;
uint32_t num = 0;
while(*buf){
char c = *buf;
if(c < '0' || c > '9'){
break;
}
if(num > 429496729 || (num == 429496729 && c > '5')){ // overflow
*N = 0xffffff;
return start;
}
num *= 10;
num += c - '0';
++buf;
}
*N = num;
return buf;
}
// read hexadecimal number (without 0x prefix!)
static const char *gethex(const char *buf, uint32_t *N){
const char *start = buf;
uint32_t num = 0;
while(*buf){
char c = *buf;
uint8_t M = 0;
if(c >= '0' && c <= '9'){
M = '0';
}else if(c >= 'A' && c <= 'F'){
M = 'A' - 10;
}else if(c >= 'a' && c <= 'f'){
M = 'a' - 10;
}
if(M){
if(num & 0xf0000000){ // overflow
*N = 0xffffff;
return start;
}
num <<= 4;
num += c - M;
}else{
break;
}
++buf;
}
*N = num;
return buf;
}
// read octal number (without 0 prefix!)
static const char *getoct(const char *buf, uint32_t *N){
const char *start = (char*)buf;
uint32_t num = 0;
while(*buf){
char c = *buf;
if(c < '0' || c > '7'){
break;
}
if(num & 0xe0000000){ // overflow
*N = 0xffffff;
return start;
}
num <<= 3;
num += c - '0';
++buf;
}
*N = num;
return buf;
}
// read binary number (without b prefix!)
static const char *getbin(const char *buf, uint32_t *N){
const char *start = (char*)buf;
uint32_t num = 0;
while(*buf){
char c = *buf;
if(c < '0' || c > '1'){
break;
}
if(num & 0x80000000){ // overflow
*N = 0xffffff;
return start;
}
num <<= 1;
if(c == '1') num |= 1;
++buf;
}
*N = num;
return buf;
}
/**
* @brief getnum - read uint32_t from string (dec, hex or bin: 127, 0x7f, 0b1111111)
* @param buf - buffer with number and so on
* @param N - the number read
* @return pointer to first non-number symbol in buf
* (if it is == buf, there's no number or if *N==0xffffffff there was overflow)
*/
const char *getnum(const char *txt, uint32_t *N){
const char *nxt = NULL;
const char *s = omit_spaces(txt);
if(*s == '0'){ // hex, oct or 0
if(s[1] == 'x' || s[1] == 'X'){ // hex
nxt = gethex(s+2, N);
if(nxt == s+2) nxt = (char*)txt;
}else if(s[1] > '0'-1 && s[1] < '8'){ // oct
nxt = getoct(s+1, N);
if(nxt == s+1) nxt = (char*)txt;
}else{ // 0
nxt = s+1;
*N = 0;
}
}else if(*s == 'b' || *s == 'B'){
nxt = getbin(s+1, N);
if(nxt == s+1) nxt = (char*)txt;
}else{
nxt = getdec(s, N);
if(nxt == s) nxt = (char*)txt;
}
return nxt;
}
// get signed integer
const char *getint(const char *txt, int32_t *I){
const char *s = omit_spaces(txt);
int32_t sign = 1;
uint32_t U;
if(*s == '-'){
sign = -1;
++s;
}
const char *nxt = getnum(s, &U);
if(nxt == s) return txt;
if(U & 0x80000000) return txt; // overfull
*I = sign * (int32_t)U;
return nxt;
}
// be careful: if pow10 would be bigger you should change str[] size!
static const float pwr10[] = {1.f, 10.f, 100.f, 1000.f, 10000.f};
static const float rounds[] = {0.5f, 0.05f, 0.005f, 0.0005f, 0.00005f};
#define P10L (sizeof(pwr10)/sizeof(uint32_t) - 1)
char *float2str(float x, uint8_t prec){
static char str[16] = {0}; // -117.5494E-36\0 - 14 symbols max!
if(prec > P10L) prec = P10L;
if(isnan(x)){ memcpy(str, "NAN", 4); return str;}
else{
int i = isinf(x);
if(i){memcpy(str, "-INF", 5); if(i == 1) return str+1; else return str;}
}
char *s = str + 14; // go to end of buffer
uint8_t minus = 0;
if(x < 0){
x = -x;
minus = 1;
}
int pow = 0; // xxxEpow
// now convert float to 1.xxxE3y
while(x > 1000.f){
x /= 1000.f;
pow += 3;
}
if(x > 0.) while(x < 1.){
x *= 1000.f;
pow -= 3;
}
// print Eyy
if(pow){
uint8_t m = 0;
if(pow < 0){pow = -pow; m = 1;}
while(pow){
register int p10 = pow/10;
*s-- = '0' + (pow - 10*p10);
pow = p10;
}
if(m) *s-- = '-';
*s-- = 'E';
}
// now our number is in [1, 1000]
uint32_t units;
if(prec){
units = (uint32_t) x;
uint32_t decimals = (uint32_t)((x-units+rounds[prec])*pwr10[prec]);
// print decimals
while(prec){
register int d10 = decimals / 10;
*s-- = '0' + (decimals - 10*d10);
decimals = d10;
--prec;
}
// decimal point
*s-- = '.';
}else{ // without decimal part
units = (uint32_t) (x + 0.5);
}
// print main units
if(units == 0) *s-- = '0';
else while(units){
register uint32_t u10 = units / 10;
*s-- = '0' + (units - 10*u10);
units = u10;
}
if(minus) *s-- = '-';
return s+1;
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include "usb_dev.h"
extern volatile uint32_t Tms;
#define STR_HELPER(s) #s
#define STR(s) STR_HELPER(s)
#ifdef EBUG
#define DBG(str) do{USB_sendstr(__FILE__ " (L" STR(__LINE__) "): " str); newline();}while(0)
#else
#define DBG(str)
#endif
#define printu(x) do{USB_sendstr(u2str(x));}while(0)
#define printi(x) do{USB_sendstr(i2str(x));}while(0)
#define printuhex(x) do{USB_sendstr(uhex2str(x));}while(0)
#define printfl(x,n) do{USB_sendstr(float2str(x, n));}while(0)
void hexdump(int ifno, uint8_t *arr, uint16_t len);
const char *u2str(uint32_t val);
const char *i2str(int32_t i);
const char *uhex2str(uint32_t val);
const char *getnum(const char *txt, uint32_t *N);
const char *omit_spaces(const char *buf);
const char *getint(const char *txt, int32_t *I);
char *float2str(float x, uint8_t prec);

View File

@ -0,0 +1,82 @@
/*
* This file is part of the mlxtest project.
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// data from MLX documentation for algorithms testing
// this file should be included only once!
#ifdef _TESTDATA_H__
#error "Don't include this file several times!"
#endif
#define _TESTDATA_H__
#include "mlx90640.h"
static const uint16_t EEPROM[MLX_DMA_MAXLEN] = {
#include "eeprom.csv"
};
static const int16_t DataFrame[2][MLX_DMA_MAXLEN] = {
{
#include "frame0.csv"
},
{
#include "frame1.csv"
}
};
static const MLX90640_params extracted_parameters = {
.kVdd = -3200
,.vdd25 = -12544
,.KvPTAT = 0.002197
,.KtPTAT = 42.625000
,.vPTAT25 = 12196
,.alphaPTAT = 9.0
,.gainEE = 5580
,.tgc = 0.000000
,.cpKv = 0.3750
,.cpKta = 0.004272
// ,.calibrationModeEE = 128
,.KsTa = -0.002441
,.CT = {0., 300., 500.}
,.KsTo = {-0.0002, -0.0002, -0.0002, -0.0002}
,.alphacorr = {}
,.alpha = {
#include "alpha.csv"
}
,.offset = {
#include "offset.csv"
}
,.kta = {
#include "kta.csv"
}
,.kv = {0.4375, 0.3750, 0.3750, 0.3750}
,.cpAlpha = {0.0000000028812792152, 0.0000000029037892091}
,.resolEE = 2
,.cpOffset = {-69, -65}
,.outliers = {0}
};
static const fp_t ToFrame[2][MLX_PIXNO] = {
{
#include "to_frame0.csv"
},
{
#include "to_frame1.csv"
}
};

View File

@ -0,0 +1 @@
28.711,0.000,28.178,0.000,28.313,0.000,28.631,0.000,28.295,0.000,28.586,0.000,28.336,0.000,28.683,0.000,28.248,0.000,28.573,0.000,29.127,0.000,28.629,0.000,29.070,0.000,28.894,0.000,28.883,0.000,29.687,0.000,0.000,28.618,0.000,28.134,0.000,28.517,0.000,27.982,0.000,28.092,0.000,27.914,0.000,28.156,0.000,28.261,0.000,28.198,0.000,28.528,0.000,28.837,0.000,28.730,0.000,28.814,0.000,28.558,0.000,28.605,0.000,29.005,28.513,0.000,28.482,0.000,28.428,0.000,27.946,0.000,28.248,0.000,28.387,0.000,28.340,0.000,28.128,0.000,28.544,0.000,28.321,0.000,28.986,0.000,28.445,0.000,28.728,0.000,28.778,0.000,29.035,0.000,28.639,0.000,0.000,28.116,0.000,28.237,0.000,28.173,0.000,28.519,0.000,28.303,0.000,28.049,0.000,28.246,0.000,28.466,0.000,28.247,0.000,28.378,0.000,28.835,0.000,28.480,0.000,28.828,0.000,28.428,0.000,28.528,0.000,29.030,28.163,0.000,28.719,0.000,28.268,0.000,28.439,0.000,28.607,0.000,28.199,0.000,28.440,0.000,28.730,0.000,28.361,0.000,28.256,0.000,28.253,0.000,28.563,0.000,28.593,0.000,28.962,0.000,28.746,0.000,28.917,0.000,0.000,28.112,0.000,27.955,0.000,28.365,0.000,28.291,0.000,28.335,0.000,28.610,0.000,28.281,0.000,28.303,0.000,28.246,0.000,28.710,0.000,28.723,0.000,28.532,0.000,28.493,0.000,28.686,0.000,28.565,0.000,28.197,28.489,0.000,28.295,0.000,28.610,0.000,28.497,0.000,29.183,0.000,29.365,0.000,28.763,0.000,28.320,0.000,28.594,0.000,28.641,0.000,28.629,0.000,28.950,0.000,27.980,0.000,28.349,0.000,28.453,0.000,28.852,0.000,0.000,28.364,0.000,28.244,0.000,28.023,0.000,28.247,0.000,29.572,0.000,30.034,0.000,28.667,0.000,28.642,0.000,28.316,0.000,28.311,0.000,28.444,0.000,28.168,0.000,28.403,0.000,28.438,0.000,28.800,0.000,27.913,28.091,0.000,28.598,0.000,28.313,0.000,28.813,0.000,30.533,0.000,32.362,0.000,31.328,0.000,29.314,0.000,28.126,0.000,28.517,0.000,28.644,0.000,28.679,0.000,28.485,0.000,28.714,0.000,28.549,0.000,28.739,0.000,0.000,28.275,0.000,27.955,0.000,28.448,0.000,28.965,0.000,31.879,0.000,32.094,0.000,31.286,0.000,29.814,0.000,28.593,0.000,28.303,0.000,28.808,0.000,28.596,0.000,28.363,0.000,28.585,0.000,28.420,0.000,28.341,28.595,0.000,28.668,0.000,28.724,0.000,29.526,0.000,32.332,0.000,32.878,0.000,32.587,0.000,32.080,0.000,30.319,0.000,29.259,0.000,28.347,0.000,28.371,0.000,28.591,0.000,28.779,0.000,28.650,0.000,28.577,0.000,0.000,28.330,0.000,28.480,0.000,28.753,0.000,30.412,0.000,33.225,0.000,33.175,0.000,32.848,0.000,31.967,0.000,30.751,0.000,29.090,0.000,28.461,0.000,28.636,0.000,28.437,0.000,28.707,0.000,28.479,0.000,28.493,28.450,0.000,28.205,0.000,28.945,0.000,30.841,0.000,33.628,0.000,34.155,0.000,33.454,0.000,32.621,0.000,32.219,0.000,31.181,0.000,28.945,0.000,28.535,0.000,28.354,0.000,28.338,0.000,28.487,0.000,28.645,0.000,0.000,28.420,0.000,28.235,0.000,29.189,0.000,32.258,0.000,34.054,0.000,34.386,0.000,33.366,0.000,32.502,0.000,32.534,0.000,30.553,0.000,28.703,0.000,28.123,0.000,28.465,0.000,28.435,0.000,28.666,0.000,28.458,28.412,0.000,28.618,0.000,29.437,0.000,32.515,0.000,33.633,0.000,34.348,0.000,33.460,0.000,33.297,0.000,32.441,0.000,30.583,0.000,28.901,0.000,28.751,0.000,28.455,0.000,28.551,0.000,28.674,0.000,28.816,0.000,0.000,28.379,0.000,28.929,0.000,30.135,0.000,32.781,0.000,33.364,0.000,33.703,0.000,33.035,0.000,32.681,0.000,32.508,0.000,29.271,0.000,28.813,0.000,28.176,0.000,28.281,0.000,28.308,0.000,28.441,0.000,28.851,28.266,0.000,28.759,0.000,30.103,0.000,32.329,0.000,32.560,0.000,32.963,0.000,32.587,0.000,32.781,0.000,32.408,0.000,29.431,0.000,28.536,0.000,28.849,0.000,28.187,0.000,28.597,0.000,29.227,0.000,28.880,0.000,0.000,28.259,0.000,28.992,0.000,30.442,0.000,32.244,0.000,32.250,0.000,32.312,0.000,32.854,0.000,32.680,0.000,31.065,0.000,28.621,0.000,28.344,0.000,28.447,0.000,28.667,0.000,28.698,0.000,29.718,0.000,28.963,28.429,0.000,28.570,0.000,29.163,0.000,29.828,0.000,31.671,0.000,32.379,0.000,32.265,0.000,32.592,0.000,31.175,0.000,29.218,0.000,28.587,0.000,28.508,0.000,28.957,0.000,29.226,0.000,31.199,0.000,31.547,0.000,0.000,28.137,0.000,28.463,0.000,28.557,0.000,29.704,0.000,30.460,0.000,31.603,0.000,31.990,0.000,32.004,0.000,29.626,0.000,28.473,0.000,28.220,0.000,28.694,0.000,28.703,0.000,29.831,0.000,32.548,0.000,32.176,28.473,0.000,28.264,0.000,28.662,0.000,28.372,0.000,29.225,0.000,29.797,0.000,30.999,0.000,31.244,0.000,29.575,0.000,28.510,0.000,28.530,0.000,28.449,0.000,28.642,0.000,30.602,0.000,32.418,0.000,34.239,0.000,0.000,28.322,0.000,28.078,0.000,28.183,0.000,28.517,0.000,28.593,0.000,29.005,0.000,30.394,0.000,30.257,0.000,28.746,0.000,28.295,0.000,28.561,0.000,28.250,0.000,28.593,0.000,31.333,0.000,33.254,0.000,33.224,28.516,0.000,28.317,0.000,28.465,0.000,28.845,0.000,28.642,0.000,28.464,0.000,28.858,0.000,28.979,0.000,29.039,0.000,28.832,0.000,28.280,0.000,28.837,0.000,29.056,0.000,31.946,0.000,33.101,0.000,32.988,0.000,0.000,28.161,0.000,28.257,0.000,28.346,0.000,28.301,0.000,28.101,0.000,28.288,0.000,28.615,0.000,28.926,0.000,28.512,0.000,28.178,0.000,28.392,0.000,28.412,0.000,30.166,0.000,32.614,0.000,32.530,0.000,32.028
1 28.711 0.000 28.178 0.000 28.313 0.000 28.631 0.000 28.295 0.000 28.586 0.000 28.336 0.000 28.683 0.000 28.248 0.000 28.573 0.000 29.127 0.000 28.629 0.000 29.070 0.000 28.894 0.000 28.883 0.000 29.687 0.000 0.000 28.618 0.000 28.134 0.000 28.517 0.000 27.982 0.000 28.092 0.000 27.914 0.000 28.156 0.000 28.261 0.000 28.198 0.000 28.528 0.000 28.837 0.000 28.730 0.000 28.814 0.000 28.558 0.000 28.605 0.000 29.005 28.513 0.000 28.482 0.000 28.428 0.000 27.946 0.000 28.248 0.000 28.387 0.000 28.340 0.000 28.128 0.000 28.544 0.000 28.321 0.000 28.986 0.000 28.445 0.000 28.728 0.000 28.778 0.000 29.035 0.000 28.639 0.000 0.000 28.116 0.000 28.237 0.000 28.173 0.000 28.519 0.000 28.303 0.000 28.049 0.000 28.246 0.000 28.466 0.000 28.247 0.000 28.378 0.000 28.835 0.000 28.480 0.000 28.828 0.000 28.428 0.000 28.528 0.000 29.030 28.163 0.000 28.719 0.000 28.268 0.000 28.439 0.000 28.607 0.000 28.199 0.000 28.440 0.000 28.730 0.000 28.361 0.000 28.256 0.000 28.253 0.000 28.563 0.000 28.593 0.000 28.962 0.000 28.746 0.000 28.917 0.000 0.000 28.112 0.000 27.955 0.000 28.365 0.000 28.291 0.000 28.335 0.000 28.610 0.000 28.281 0.000 28.303 0.000 28.246 0.000 28.710 0.000 28.723 0.000 28.532 0.000 28.493 0.000 28.686 0.000 28.565 0.000 28.197 28.489 0.000 28.295 0.000 28.610 0.000 28.497 0.000 29.183 0.000 29.365 0.000 28.763 0.000 28.320 0.000 28.594 0.000 28.641 0.000 28.629 0.000 28.950 0.000 27.980 0.000 28.349 0.000 28.453 0.000 28.852 0.000 0.000 28.364 0.000 28.244 0.000 28.023 0.000 28.247 0.000 29.572 0.000 30.034 0.000 28.667 0.000 28.642 0.000 28.316 0.000 28.311 0.000 28.444 0.000 28.168 0.000 28.403 0.000 28.438 0.000 28.800 0.000 27.913 28.091 0.000 28.598 0.000 28.313 0.000 28.813 0.000 30.533 0.000 32.362 0.000 31.328 0.000 29.314 0.000 28.126 0.000 28.517 0.000 28.644 0.000 28.679 0.000 28.485 0.000 28.714 0.000 28.549 0.000 28.739 0.000 0.000 28.275 0.000 27.955 0.000 28.448 0.000 28.965 0.000 31.879 0.000 32.094 0.000 31.286 0.000 29.814 0.000 28.593 0.000 28.303 0.000 28.808 0.000 28.596 0.000 28.363 0.000 28.585 0.000 28.420 0.000 28.341 28.595 0.000 28.668 0.000 28.724 0.000 29.526 0.000 32.332 0.000 32.878 0.000 32.587 0.000 32.080 0.000 30.319 0.000 29.259 0.000 28.347 0.000 28.371 0.000 28.591 0.000 28.779 0.000 28.650 0.000 28.577 0.000 0.000 28.330 0.000 28.480 0.000 28.753 0.000 30.412 0.000 33.225 0.000 33.175 0.000 32.848 0.000 31.967 0.000 30.751 0.000 29.090 0.000 28.461 0.000 28.636 0.000 28.437 0.000 28.707 0.000 28.479 0.000 28.493 28.450 0.000 28.205 0.000 28.945 0.000 30.841 0.000 33.628 0.000 34.155 0.000 33.454 0.000 32.621 0.000 32.219 0.000 31.181 0.000 28.945 0.000 28.535 0.000 28.354 0.000 28.338 0.000 28.487 0.000 28.645 0.000 0.000 28.420 0.000 28.235 0.000 29.189 0.000 32.258 0.000 34.054 0.000 34.386 0.000 33.366 0.000 32.502 0.000 32.534 0.000 30.553 0.000 28.703 0.000 28.123 0.000 28.465 0.000 28.435 0.000 28.666 0.000 28.458 28.412 0.000 28.618 0.000 29.437 0.000 32.515 0.000 33.633 0.000 34.348 0.000 33.460 0.000 33.297 0.000 32.441 0.000 30.583 0.000 28.901 0.000 28.751 0.000 28.455 0.000 28.551 0.000 28.674 0.000 28.816 0.000 0.000 28.379 0.000 28.929 0.000 30.135 0.000 32.781 0.000 33.364 0.000 33.703 0.000 33.035 0.000 32.681 0.000 32.508 0.000 29.271 0.000 28.813 0.000 28.176 0.000 28.281 0.000 28.308 0.000 28.441 0.000 28.851 28.266 0.000 28.759 0.000 30.103 0.000 32.329 0.000 32.560 0.000 32.963 0.000 32.587 0.000 32.781 0.000 32.408 0.000 29.431 0.000 28.536 0.000 28.849 0.000 28.187 0.000 28.597 0.000 29.227 0.000 28.880 0.000 0.000 28.259 0.000 28.992 0.000 30.442 0.000 32.244 0.000 32.250 0.000 32.312 0.000 32.854 0.000 32.680 0.000 31.065 0.000 28.621 0.000 28.344 0.000 28.447 0.000 28.667 0.000 28.698 0.000 29.718 0.000 28.963 28.429 0.000 28.570 0.000 29.163 0.000 29.828 0.000 31.671 0.000 32.379 0.000 32.265 0.000 32.592 0.000 31.175 0.000 29.218 0.000 28.587 0.000 28.508 0.000 28.957 0.000 29.226 0.000 31.199 0.000 31.547 0.000 0.000 28.137 0.000 28.463 0.000 28.557 0.000 29.704 0.000 30.460 0.000 31.603 0.000 31.990 0.000 32.004 0.000 29.626 0.000 28.473 0.000 28.220 0.000 28.694 0.000 28.703 0.000 29.831 0.000 32.548 0.000 32.176 28.473 0.000 28.264 0.000 28.662 0.000 28.372 0.000 29.225 0.000 29.797 0.000 30.999 0.000 31.244 0.000 29.575 0.000 28.510 0.000 28.530 0.000 28.449 0.000 28.642 0.000 30.602 0.000 32.418 0.000 34.239 0.000 0.000 28.322 0.000 28.078 0.000 28.183 0.000 28.517 0.000 28.593 0.000 29.005 0.000 30.394 0.000 30.257 0.000 28.746 0.000 28.295 0.000 28.561 0.000 28.250 0.000 28.593 0.000 31.333 0.000 33.254 0.000 33.224 28.516 0.000 28.317 0.000 28.465 0.000 28.845 0.000 28.642 0.000 28.464 0.000 28.858 0.000 28.979 0.000 29.039 0.000 28.832 0.000 28.280 0.000 28.837 0.000 29.056 0.000 31.946 0.000 33.101 0.000 32.988 0.000 0.000 28.161 0.000 28.257 0.000 28.346 0.000 28.301 0.000 28.101 0.000 28.288 0.000 28.615 0.000 28.926 0.000 28.512 0.000 28.178 0.000 28.392 0.000 28.412 0.000 30.166 0.000 32.614 0.000 32.530 0.000 32.028

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,210 @@
/*
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "usb_descr.h"
// low/high for uint16_t
#define L16(x) (x & 0xff)
#define H16(x) (x >> 8)
static const uint8_t USB_DeviceDescriptor[] = {
USB_DT_DEVICE_SIZE, // bLength
USB_DT_DEVICE, // bDescriptorType
L16(bcdUSB), // bcdUSB_L
H16(bcdUSB), // bcdUSB_H
USB_CLASS_MISC, // bDeviceClass
bDeviceSubClass, // bDeviceSubClass
bDeviceProtocol, // bDeviceProtocol
USB_EP0BUFSZ, // bMaxPacketSize
L16(idVendor), // idVendor_L
H16(idVendor), // idVendor_H
L16(idProduct), // idProduct_L
H16(idProduct), // idProduct_H
L16(bcdDevice_Ver), // bcdDevice_Ver_L
H16(bcdDevice_Ver), // bcdDevice_Ver_H
iMANUFACTURER_DESCR, // iManufacturer - indexes of string descriptors in array
iPRODUCT_DESCR, // iProduct
iSERIAL_DESCR, // iSerial
bNumConfigurations // bNumConfigurations
};
static const uint8_t USB_DeviceQualifierDescriptor[] = {
USB_DT_QUALIFIER_SIZE, //bLength
USB_DT_QUALIFIER, // bDescriptorType
L16(bcdUSB), // bcdUSB_L
H16(bcdUSB), // bcdUSB_H
USB_CLASS_PER_INTERFACE, // bDeviceClass
bDeviceSubClass, // bDeviceSubClass
bDeviceProtocol, // bDeviceProtocol
USB_EP0BUFSZ, // bMaxPacketSize0
bNumConfigurations, // bNumConfigurations
0 // Reserved
};
#define wTotalLength (USB_DT_CONFIG_SIZE + (bNumInterfaces * USB_DT_INTERFACE_SIZE) + (bTotNumEndpoints * USB_DT_ENDPOINT_SIZE) + (bNumCsInterfaces * USB_DT_CS_INTERFACE_SIZE) - 1)
static const uint8_t USB_ConfigDescriptor[] = {
// Configuration Descriptor
USB_DT_CONFIG_SIZE, // bLength: Configuration Descriptor size
USB_DT_CONFIG, // bDescriptorType: Configuration
L16(wTotalLength), // wTotalLength.L :no of returned bytes
H16(wTotalLength), // wTotalLength.H
bNumInterfaces, // bNumInterfaces
1, // bConfigurationValue: Current configuration value
0, // iConfiguration: Index of string descriptor describing the configuration or 0
BusPowered, // bmAttributes - Bus powered
50, // MaxPower in 2mA units
//---------------------------------------------------------------------------
// Virtual command Interface Descriptor
USB_DT_INTERFACE_SIZE, // bLength: Interface Descriptor size
USB_DT_INTERFACE, // bDescriptorType: Interface
0, // bInterfaceNumber: Number of Interface
0, // bAlternateSetting: Alternate setting
1, // bNumEndpoints: one for this
USB_CLASS_COMM, // bInterfaceClass
2, // bInterfaceSubClass: ACM
1, // bInterfaceProtocol: Common AT commands
iINTERFACE_DESCR1, // iInterface
// ---- CS Interfaces
USB_DT_CS_INTERFACE_SIZE, // bLength
USB_DT_CS_INTERFACE, // bDescriptorType: CS_INTERFACE
0, // bDescriptorSubtype: Header Func Desc
0x10, // bcdCDC: spec release number
1, // bDataInterface
USB_DT_CS_INTERFACE_SIZE, // bLength
USB_DT_CS_INTERFACE, // bDescriptorType: CS_INTERFACE
1, // bDescriptorSubtype: Call Management Func Desc
0, // bmCapabilities: D0+D1
1, // bDataInterface
USB_DT_CS_INTERFACE_SIZE-1, // bLength
USB_DT_CS_INTERFACE, // bDescriptorType: CS_INTERFACE
2, // bDescriptorSubtype: Abstract Control Management desc
2, // bmCapabilities
USB_DT_CS_INTERFACE_SIZE, // bLength
USB_DT_CS_INTERFACE, // bDescriptorType: CS_INTERFACE
6, // bDescriptorSubtype: Union func desc
0, // bMasterInterface: Communication class interface
1, // bSlaveInterface0: Data Class Interface
// Virtual endpoint 1 Descriptor
USB_DT_ENDPOINT_SIZE, // bLength: Endpoint Descriptor size
USB_DT_ENDPOINT, // bDescriptorType: Endpoint
0x8A, // bEndpointAddress IN10
USB_BM_ATTR_INTERRUPT, // bmAttributes: Interrupt
L16(USB_EP1BUFSZ), // wMaxPacketSize LO
H16(USB_EP1BUFSZ), // wMaxPacketSize HI
0x10, // bInterval: 16ms
//---------------------------------------------------------------------------
// Data interface
USB_DT_INTERFACE_SIZE, // bLength: Interface Descriptor size
USB_DT_INTERFACE, // bDescriptorType: Interface
1, // bInterfaceNumber: Number of Interface
0, // bAlternateSetting: Alternate setting
2, // bNumEndpoints: in and out
USB_CLASS_DATA, // bInterfaceClass
2, // bInterfaceSubClass: ACM
0, // bInterfaceProtocol
0, // iInterface
//Endpoint IN1 Descriptor
USB_DT_ENDPOINT_SIZE, // bLength: Endpoint Descriptor size
USB_DT_ENDPOINT, // bDescriptorType: Endpoint
0x81, // bEndpointAddress: IN1
USB_BM_ATTR_BULK, // bmAttributes: Bulk
L16(USB_TXBUFSZ), // wMaxPacketSize LO
H16(USB_TXBUFSZ), // wMaxPacketSize HI
0, // bInterval: ignore for Bulk transfer
// Endpoint OUT1 Descriptor
USB_DT_ENDPOINT_SIZE, // bLength: Endpoint Descriptor size
USB_DT_ENDPOINT, // bDescriptorType: Endpoint
0x01, // bEndpointAddress: OUT1
USB_BM_ATTR_BULK, // bmAttributes: Bulk
L16(USB_RXBUFSZ), // wMaxPacketSize LO
H16(USB_RXBUFSZ), // wMaxPacketSize HI
0, // bInterval: ignore for Bulk transfer
};
//const uint8_t HID_ReportDescriptor[];
_USB_LANG_ID_(LD, LANG_US);
_USB_STRING_(SD, u"0.0.1");
_USB_STRING_(MD, u"eddy@sao.ru");
_USB_STRING_(PD, u"USB-CDC");
_USB_STRING_(ID, u"usbcdc");
static const void* const StringDescriptor[iDESCR_AMOUNT] = {
[iLANGUAGE_DESCR] = &LD,
[iMANUFACTURER_DESCR] = &MD,
[iPRODUCT_DESCR] = &PD,
[iSERIAL_DESCR] = &SD,
[iINTERFACE_DESCR1] = &ID
};
static void wr0(const uint8_t *buf, uint16_t size, uint16_t askedsize){
if(askedsize < size) size = askedsize; // shortened request
if(size < USB_EP0BUFSZ){
EP_WriteIRQ(0, buf, size);
return;
}
while(size){
uint16_t l = size;
if(l > USB_EP0BUFSZ) l = USB_EP0BUFSZ;
EP_WriteIRQ(0, buf, l);
buf += l;
size -= l;
uint8_t needzlp = (l == USB_EP0BUFSZ) ? 1 : 0;
if(size || needzlp){ // send last data buffer
uint16_t epstatus = KEEP_DTOG(USB->EPnR[0]);
// keep DTOGs, clear CTR_RX,TX, set TX VALID, leave stat_Rx
USB->EPnR[0] = (epstatus & ~(USB_EPnR_CTR_RX|USB_EPnR_CTR_TX|USB_EPnR_STAT_RX))
^ USB_EPnR_STAT_TX;
uint32_t ctr = 1000000;
while(--ctr && (USB->ISTR & USB_ISTR_CTR) == 0){IWDG->KR = IWDG_REFRESH;};
if((USB->ISTR & USB_ISTR_CTR) == 0){
return;
}
if(needzlp) EP_WriteIRQ(0, NULL, 0);
}
}
}
void get_descriptor(config_pack_t *pack){
uint8_t descrtype = pack->wValue >> 8,
descridx = pack->wValue & 0xff;
switch(descrtype){
case DEVICE_DESCRIPTOR:
wr0(USB_DeviceDescriptor, sizeof(USB_DeviceDescriptor), pack->wLength);
break;
case CONFIGURATION_DESCRIPTOR:
wr0(USB_ConfigDescriptor, sizeof(USB_ConfigDescriptor), pack->wLength);
break;
case STRING_DESCRIPTOR:
if(descridx < iDESCR_AMOUNT){
wr0((const uint8_t *)StringDescriptor[descridx], *((uint8_t*)StringDescriptor[descridx]), pack->wLength);
}else{
EP_WriteIRQ(0, NULL, 0);
}
break;
case DEVICE_QUALIFIER_DESCRIPTOR:
wr0(USB_DeviceQualifierDescriptor, sizeof(USB_DeviceQualifierDescriptor), pack->wLength);
break;
/* case HID_REPORT_DESCRIPTOR:
wr0(HID_ReportDescriptor, sizeof(HID_ReportDescriptor), pack->wLength);
break;*/
default:
break;
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include "usb_lib.h"
// definition of parts common for USB_DeviceDescriptor & USB_DeviceQualifierDescriptor
// bcdUSB: 1.10
#define bcdUSB 0x0110
// Class - Misc (EF), subclass - common (2), protocol - interface association descr (1)
#define bDeviceSubClass 0x02
#define bDeviceProtocol 0x01
#define idVendor 0x0483
#define idProduct 0x5740
#define bcdDevice_Ver 0x0200
#define bNumConfigurations 1
// amount of interfaces and endpoints (except 0) used
#define bNumInterfaces 2
#define bTotNumEndpoints 3
#define bNumCsInterfaces 4
// powered
#define BusPowered (1<<7)
#define SelfPowered (1<<6)
#define RemoteWakeup (1<<5)
// buffer sizes
// for USB FS EP0 buffers are from 8 to 64 bytes long
#define USB_EP0BUFSZ 64
#define USB_EP1BUFSZ 10
// Rx/Tx EPs
#define USB_RXBUFSZ 64
#define USB_TXBUFSZ 64
// string descriptors
enum{
iLANGUAGE_DESCR,
iMANUFACTURER_DESCR,
iPRODUCT_DESCR,
iSERIAL_DESCR,
iINTERFACE_DESCR1,
iDESCR_AMOUNT
};
void get_descriptor(config_pack_t *pack);

View File

@ -0,0 +1,240 @@
/*
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include "ringbuffer.h"
#include "usb_descr.h"
#include "usb_dev.h"
// Class-Specific Control Requests
#define SEND_ENCAPSULATED_COMMAND 0x00 // unused
#define GET_ENCAPSULATED_RESPONSE 0x01 // unused
#define SET_COMM_FEATURE 0x02 // unused
#define GET_COMM_FEATURE 0x03 // unused
#define CLEAR_COMM_FEATURE 0x04 // unused
#define SET_LINE_CODING 0x20
#define GET_LINE_CODING 0x21
#define SET_CONTROL_LINE_STATE 0x22
#define SEND_BREAK 0x23
// control line states
#define CONTROL_DTR 0x01
#define CONTROL_RTS 0x02
// inbuf overflow when receiving
static volatile uint8_t bufovrfl = 0;
// receive buffer: hold data until chkin() call
static uint8_t volatile rcvbuf[USB_RXBUFSZ];
static uint8_t volatile rcvbuflen = 0;
// line coding
usb_LineCoding WEAK lineCoding = {115200, 0, 0, 8};
// CDC configured and ready to use
volatile uint8_t CDCready = 0;
// ring buffers for incoming and outgoing data
static uint8_t obuf[RBOUTSZ], ibuf[RBINSZ];
static volatile ringbuffer rbout = {.data = obuf, .length = RBOUTSZ, .head = 0, .tail = 0};
static volatile ringbuffer rbin = {.data = ibuf, .length = RBINSZ, .head = 0, .tail = 0};
// last send data size
static volatile int lastdsz = 0;
static void chkin(){
if(bufovrfl) return; // allow user to know that previous buffer was overflowed and cleared
if(!rcvbuflen) return;
int w = RB_write((ringbuffer*)&rbin, (uint8_t*)rcvbuf, rcvbuflen);
if(w < 0){
return;
}
if(w != rcvbuflen) bufovrfl = 1;
rcvbuflen = 0;
uint16_t status = KEEP_DTOG(USB->EPnR[1]); // don't change DTOG
USB->EPnR[1] = (status & ~(USB_EPnR_STAT_TX|USB_EPnR_CTR_RX)) ^ USB_EPnR_STAT_RX; // prepare to get next data portion
}
// called from transmit EP to send next data portion or by user - when new transmission starts
static void send_next(){
uint8_t usbbuff[USB_TXBUFSZ];
int buflen = RB_read((ringbuffer*)&rbout, (uint8_t*)usbbuff, USB_TXBUFSZ);
if(buflen == 0){
if(lastdsz == 64) EP_Write(1, NULL, 0); // send ZLP after 64 bits packet when nothing more to send
lastdsz = 0;
return;
}else if(buflen < 0){
lastdsz = 0;
return;
}
EP_Write(1, (uint8_t*)usbbuff, buflen);
lastdsz = buflen;
}
// data IN/OUT handler
static void rxtx_handler(){
uint16_t epstatus = KEEP_DTOG(USB->EPnR[1]);
if(RX_FLAG(epstatus)){ // receive data
if(rcvbuflen){
bufovrfl = 1; // lost last data
rcvbuflen = 0;
}
rcvbuflen = EP_Read(1, (uint8_t*)rcvbuf);
USB->EPnR[1] = epstatus & ~(USB_EPnR_CTR_RX | USB_EPnR_STAT_RX | USB_EPnR_STAT_TX); // keep RX in STALL state until read data
chkin(); // try to write current data into RXbuf if it's not busy
}else{ // tx successfull
USB->EPnR[1] = (epstatus & ~(USB_EPnR_CTR_TX | USB_EPnR_STAT_TX)) ^ USB_EPnR_STAT_RX;
send_next();
}
}
// weak handlers: change them somewhere else if you want to setup USART
// SET_LINE_CODING
void WEAK linecoding_handler(usb_LineCoding *lc){
lineCoding = *lc;
}
// SET_CONTROL_LINE_STATE
void WEAK clstate_handler(uint16_t val){
CDCready = val; // CONTROL_DTR | CONTROL_RTS -> interface connected; 0 -> disconnected
}
// SEND_BREAK
void WEAK break_handler(){
CDCready = 0;
}
// USB is configured: setup endpoints
void set_configuration(){
EP_Init(1, EP_TYPE_BULK, USB_TXBUFSZ, USB_RXBUFSZ, rxtx_handler); // IN1 and OUT1
}
// PL2303 CLASS request
void usb_class_request(config_pack_t *req, uint8_t *data, uint16_t datalen){
uint8_t recipient = REQUEST_RECIPIENT(req->bmRequestType);
uint8_t dev2host = (req->bmRequestType & 0x80) ? 1 : 0;
switch(recipient){
case REQ_RECIPIENT_INTERFACE:
switch(req->bRequest){
case SET_LINE_CODING:
if(!data || !datalen) break; // wait for data
if(datalen == sizeof(usb_LineCoding))
linecoding_handler((usb_LineCoding*)data);
break;
case GET_LINE_CODING:
EP_WriteIRQ(0, (uint8_t*)&lineCoding, sizeof(lineCoding));
break;
case SET_CONTROL_LINE_STATE:
clstate_handler(req->wValue);
break;
case SEND_BREAK:
break_handler();
break;
default:
break;
}
break;
default:
if(dev2host) EP_WriteIRQ(0, NULL, 0);
}
if(!dev2host) EP_WriteIRQ(0, NULL, 0);
}
// blocking send full content of ring buffer
int USB_sendall(){
while(lastdsz > 0){
if(!CDCready) return FALSE;
}
return TRUE;
}
// put `buf` into queue to send
int USB_send(const uint8_t *buf, int len){
if(!buf || !CDCready || !len) return FALSE;
while(len){
int a = RB_write((ringbuffer*)&rbout, buf, len);
if(a > 0){
len -= a;
buf += a;
} else if (a < 0) continue; // do nothing if buffer is in reading state
if(lastdsz == 0) send_next(); // need to run manually - all data sent, so no IRQ on IN
}
return TRUE;
}
int USB_putbyte(uint8_t byte){
if(!CDCready) return FALSE;
int l = 0;
while((l = RB_write((ringbuffer*)&rbout, &byte, 1)) != 1){
if(l < 0) continue;
}
if(lastdsz == 0) send_next(); // need to run manually - all data sent, so no IRQ on IN
return TRUE;
}
int USB_sendstr(const char *string){
if(!string || !CDCready) return FALSE;
int len = 0;
const char *b = string;
while(*b++) ++len;
if(!len) return FALSE;
return USB_send((const uint8_t*)string, len);
}
/**
* @brief USB_receive - get binary data from receiving ring-buffer
* @param buf (i) - buffer for received data
* @param len - length of `buf`
* @return amount of received bytes (negative, if overfull happened)
*/
int USB_receive(uint8_t *buf, int len){
chkin();
if(bufovrfl){
while(1 != RB_clearbuf((ringbuffer*)&rbin));
bufovrfl = 0;
return -1;
}
int sz = RB_read((ringbuffer*)&rbin, buf, len);
if(sz < 0) return 0; // buffer in writting state
return sz;
}
/**
* @brief USB_receivestr - get string up to '\n' and replace '\n' with 0
* @param buf - receiving buffer
* @param len - its length
* @return strlen or negative value indicating overflow (if so, string won't be ends with 0 and buffer should be cleared)
*/
int USB_receivestr(char *buf, int len){
chkin();
if(bufovrfl){
while(1 != RB_clearbuf((ringbuffer*)&rbin));
bufovrfl = 0;
return -1;
}
int l = RB_readto((ringbuffer*)&rbin, '\n', (uint8_t*)buf, len);
if(l < 1){
if(rbin.length == RB_datalen((ringbuffer*)&rbin)){ // buffer is full but no '\n' found
while(1 != RB_clearbuf((ringbuffer*)&rbin));
return -1;
}
return 0;
}
if(l == 0) return 0;
buf[l-1] = 0; // replace '\n' with strend
return l;
}

View File

@ -0,0 +1,57 @@
/*
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stm32f3.h>
#include "usb_lib.h"
typedef struct {
uint32_t dwDTERate;
uint8_t bCharFormat;
#define USB_CDC_1_STOP_BITS 0
#define USB_CDC_1_5_STOP_BITS 1
#define USB_CDC_2_STOP_BITS 2
uint8_t bParityType;
#define USB_CDC_NO_PARITY 0
#define USB_CDC_ODD_PARITY 1
#define USB_CDC_EVEN_PARITY 2
#define USB_CDC_MARK_PARITY 3
#define USB_CDC_SPACE_PARITY 4
uint8_t bDataBits;
} __attribute__ ((packed)) usb_LineCoding;
extern usb_LineCoding lineCoding;
extern volatile uint8_t CDCready;
void break_handler();
void clstate_handler(uint16_t val);
void linecoding_handler(usb_LineCoding *lc);
// sizes of ringbuffers for outgoing and incoming data
#define RBOUTSZ (1024)
#define RBINSZ (1024)
#define newline() USB_putbyte('\n')
#define USND(s) do{USB_sendstr(s); USB_putbyte('\n');}while(0)
int USB_sendall();
int USB_send(const uint8_t *buf, int len);
int USB_putbyte(uint8_t byte);
int USB_sendstr(const char *string);
int USB_receive(uint8_t *buf, int len);
int USB_receivestr(char *buf, int len);

View File

@ -0,0 +1,368 @@
/*
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include "usb_lib.h"
#include "usb_descr.h"
#include "usb_dev.h"
static ep_t endpoints[STM32ENDPOINTS];
static uint16_t USB_Addr = 0;
static uint8_t setupdatabuf[EP0DATABUF_SIZE];
static config_pack_t *setup_packet = (config_pack_t*) setupdatabuf;
volatile uint8_t usbON = 0; // device is configured and active
static uint16_t configuration = 0; // reply for GET_CONFIGURATION (==1 if configured)
static inline void std_d2h_req(){
uint16_t st = 0;
switch(setup_packet->bRequest){
case GET_DESCRIPTOR:
get_descriptor(setup_packet);
break;
case GET_STATUS:
EP_WriteIRQ(0, (uint8_t *)&st, 2); // send status: Bus Powered
break;
case GET_CONFIGURATION:
EP_WriteIRQ(0, (uint8_t*)&configuration, 1);
break;
default:
EP_WriteIRQ(0, NULL, 0);
break;
}
}
static inline void std_h2d_req(){
switch(setup_packet->bRequest){
case SET_ADDRESS:
// new address will be assigned later - after acknowlegement or request to host
USB_Addr = setup_packet->wValue;
break;
case SET_CONFIGURATION:
// Now device configured
configuration = setup_packet->wValue;
set_configuration();
usbON = 1;
break;
default:
break;
}
}
void WEAK usb_standard_request(){
uint8_t recipient = REQUEST_RECIPIENT(setup_packet->bmRequestType);
uint8_t dev2host = (setup_packet->bmRequestType & 0x80) ? 1 : 0;
switch(recipient){
case REQ_RECIPIENT_DEVICE:
if(dev2host){
std_d2h_req();
}else{
std_h2d_req();
}
break;
case REQ_RECIPIENT_INTERFACE:
if(dev2host && setup_packet->bRequest == GET_DESCRIPTOR){
get_descriptor(setup_packet);
}
break;
case REQ_RECIPIENT_ENDPOINT:
if(setup_packet->bRequest == CLEAR_FEATURE){
}else{ /* wrong */ }
break;
default:
break;
}
if(!dev2host) EP_WriteIRQ(0, NULL, 0);
}
void WEAK usb_class_request(config_pack_t *req, uint8_t _U_ *data, uint16_t _U_ datalen){
switch(req->bRequest){
case GET_INTERFACE:
break;
case SET_CONFIGURATION: // set featuring by req->wValue
break;
default:
break;
}
if(0 == (setup_packet->bmRequestType & 0x80)) // host2dev
EP_WriteIRQ(0, NULL, 0);
}
void WEAK usb_vendor_request(config_pack_t _U_ *packet, uint8_t _U_ *data, uint16_t _U_ datalen){
if(0 == (setup_packet->bmRequestType & 0x80)) // host2dev
EP_WriteIRQ(0, NULL, 0);
}
/*
bmRequestType: 76543210
7 direction: 0 - host->device, 1 - device->host
65 type: 0 - standard, 1 - class, 2 - vendor
4..0 getter: 0 - device, 1 - interface, 2 - endpoint, 3 - other
*/
/**
* Endpoint0 (control) handler
*/
static void EP0_Handler(){
uint8_t ep0dbuflen = 0;
uint8_t ep0databuf[EP0DATABUF_SIZE];
uint16_t epstatus = KEEP_DTOG(USB->EPnR[0]); // EP0R on input -> return this value after modifications
int rxflag = RX_FLAG(epstatus);
//if(rxflag){ }
// check direction
if(USB->ISTR & USB_ISTR_DIR){ // OUT interrupt - receive data, CTR_RX==1 (if CTR_TX == 1 - two pending transactions: receive following by transmit)
if(epstatus & USB_EPnR_SETUP){ // setup packet -> copy data to conf_pack
EP_Read(0, setupdatabuf);
// interrupt handler will be called later
}else if(epstatus & USB_EPnR_CTR_RX){ // data packet -> push received data to ep0databuf
//if(endpoints[0].rx_cnt){ }
ep0dbuflen = EP_Read(0, ep0databuf);
}
}
if(rxflag){
uint8_t reqtype = REQUEST_TYPE(setup_packet->bmRequestType);
switch(reqtype){
case REQ_TYPE_STANDARD:
if(SETUP_FLAG(epstatus)){
usb_standard_request();
}else{ }
break;
case REQ_TYPE_CLASS:
usb_class_request(setup_packet, ep0databuf, ep0dbuflen);
break;
case REQ_TYPE_VENDOR:
usb_vendor_request(setup_packet, ep0databuf, ep0dbuflen);
break;
default:
EP_WriteIRQ(0, NULL, 0);
break;
}
}
if(TX_FLAG(epstatus)){
// now we can change address after enumeration
if ((USB->DADDR & USB_DADDR_ADD) != USB_Addr){
USB->DADDR = USB_DADDR_EF | USB_Addr;
usbON = 0;
}
}
//epstatus = KEEP_DTOG(USB->EPnR[0]);
if(rxflag) epstatus ^= USB_EPnR_STAT_TX; // start ZLP or data transmission
else epstatus &= ~USB_EPnR_STAT_TX; // or leave unchanged
// keep DTOGs, clear CTR_RX,TX, set RX VALID
USB->EPnR[0] = (epstatus & ~(USB_EPnR_CTR_RX|USB_EPnR_CTR_TX)) ^ USB_EPnR_STAT_RX;
}
/**
* Write data to EP buffer (called from IRQ handler)
* @param number - EP number
* @param *buf - array with data
* @param size - its size
*/
void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size){
if(size > endpoints[number].txbufsz) size = endpoints[number].txbufsz;
uint16_t N2 = (size + 1) >> 1;
// the buffer is 16-bit, so we should copy data as it would be uint16_t
uint16_t *buf16 = (uint16_t *)buf;
#if defined USB1_16
// very bad: what if `size` is odd?
uint32_t *out = (uint32_t *)endpoints[number].tx_buf;
for(int i = 0; i < N2; ++i, ++out){
*out = buf16[i];
}
#elif defined USB2_16
// use memcpy instead?
for(int i = 0; i < N2; i++){
endpoints[number].tx_buf[i] = buf16[i];
}
#else
#error "Define USB1_16 or USB2_16"
#endif
USB_BTABLE->EP[number].USB_COUNT_TX = size;
}
/**
* Write data to EP buffer (called outside IRQ handler)
* @param number - EP number
* @param *buf - array with data
* @param size - its size
*/
void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size){
EP_WriteIRQ(number, buf, size);
uint16_t epstatus = KEEP_DTOG(USB->EPnR[number]);
// keep DTOGs and RX stat, clear CTR_TX & set TX VALID to start transmission
USB->EPnR[number] = (epstatus & ~(USB_EPnR_CTR_TX | USB_EPnR_STAT_RX)) ^ USB_EPnR_STAT_TX;
}
/*
* Copy data from EP buffer into user buffer area
* @param *buf - user array for data
* @return amount of data read
*/
int EP_Read(uint8_t number, uint8_t *buf){
int sz = endpoints[number].rx_cnt;
if(!sz) return 0;
endpoints[number].rx_cnt = 0;
#if defined USB1_16
int n = (sz + 1) >> 1;
uint32_t *in = (uint32_t*)endpoints[number].rx_buf;
uint16_t *out = (uint16_t*)buf;
for(int i = 0; i < n; ++i, ++in)
out[i] = *(uint16_t*)in;
#elif defined USB2_16
// use memcpy instead?
for(int i = 0; i < sz; ++i)
buf[i] = endpoints[number].rx_buf[i];
#else
#error "Define USB1_16 or USB2_16"
#endif
return sz;
}
static uint16_t lastaddr = LASTADDR_DEFAULT;
/**
* Endpoint initialisation
* @param number - EP num (0...7)
* @param type - EP type (EP_TYPE_BULK, EP_TYPE_CONTROL, EP_TYPE_ISO, EP_TYPE_INTERRUPT)
* @param txsz - transmission buffer size @ USB/CAN buffer
* @param rxsz - reception buffer size @ USB/CAN buffer
* @param uint16_t (*func)(ep_t *ep) - EP handler function
* @return 0 if all OK
*/
int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, void (*func)(ep_t ep)){
if(number >= STM32ENDPOINTS) return 4; // out of configured amount
if(txsz > USB_BTABLE_SIZE/ACCESSZ || rxsz > USB_BTABLE_SIZE/ACCESSZ) return 1; // buffer too large
if(lastaddr + txsz + rxsz >= USB_BTABLE_SIZE/ACCESSZ) return 2; // out of btable
USB->EPnR[number] = (type << 9) | (number & USB_EPnR_EA);
USB->EPnR[number] ^= USB_EPnR_STAT_RX | USB_EPnR_STAT_TX_1;
if(rxsz & 1) return 3; // wrong rx buffer size
uint16_t countrx = 0;
if(rxsz < 64) countrx = rxsz / 2;
else{
if(rxsz & 0x1f) return 3; // should be multiple of 32
countrx = 31 + rxsz / 32;
}
USB_BTABLE->EP[number].USB_ADDR_TX = lastaddr;
endpoints[number].tx_buf = (uint16_t *)(USB_BTABLE_BASE + lastaddr * ACCESSZ);
endpoints[number].txbufsz = txsz;
lastaddr += txsz;
USB_BTABLE->EP[number].USB_COUNT_TX = 0;
USB_BTABLE->EP[number].USB_ADDR_RX = lastaddr;
endpoints[number].rx_buf = (uint8_t *)(USB_BTABLE_BASE + lastaddr * ACCESSZ);
lastaddr += rxsz;
USB_BTABLE->EP[number].USB_COUNT_RX = countrx << 10;
endpoints[number].func = func;
return 0;
}
// standard IRQ handler
void USB_IRQ(){
uint32_t CNTR = USB->CNTR;
USB->CNTR = 0;
if(USB->ISTR & USB_ISTR_RESET){
usbON = 0;
// Reinit registers
CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM | USB_CNTR_SUSPM;
// Endpoint 0 - CONTROL
// ON USB LS size of EP0 may be 8 bytes, but on FS it should be 64 bytes!
lastaddr = LASTADDR_DEFAULT;
// clear address, leave only enable bit
USB->DADDR = USB_DADDR_EF;
USB->ISTR = ~USB_ISTR_RESET;
if(EP_Init(0, EP_TYPE_CONTROL, USB_EP0BUFSZ, USB_EP0BUFSZ, EP0_Handler)){
return;
};
}
if(USB->ISTR & USB_ISTR_CTR){
// EP number
uint8_t n = USB->ISTR & USB_ISTR_EPID;
// copy received bytes amount
endpoints[n].rx_cnt = USB_BTABLE->EP[n].USB_COUNT_RX & 0x3FF; // low 10 bits is counter
// call EP handler
if(endpoints[n].func) endpoints[n].func();
}
if(USB->ISTR & USB_ISTR_WKUP){ // wakeup
#ifndef STM32F0
CNTR &= ~(USB_CNTR_FSUSP | USB_CNTR_LP_MODE | USB_CNTR_WKUPM); // clear suspend flags
#else
CNTR &= ~(USB_CNTR_FSUSP | USB_CNTR_LPMODE | USB_CNTR_WKUPM);
#endif
USB->ISTR = ~USB_ISTR_WKUP;
}
if(USB->ISTR & USB_ISTR_SUSP){ // suspend -> still no connection, may sleep
usbON = 0;
#ifndef STM32F0
CNTR |= USB_CNTR_FSUSP | USB_CNTR_LP_MODE | USB_CNTR_WKUPM;
#else
CNTR |= USB_CNTR_FSUSP | USB_CNTR_LPMODE | USB_CNTR_WKUPM;
#endif
CNTR &= ~(USB_CNTR_SUSPM);
USB->ISTR = ~USB_ISTR_SUSP;
}
USB->CNTR = CNTR; // rewoke interrupts
}
// here we suppose that all PIN settings done in hw_setup earlier
void USB_setup(){
#if defined STM32F3
NVIC_DisableIRQ(USB_LP_IRQn);
// remap USB LP & Wakeup interrupts to 75 and 76 - works only on pure F303
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; // enable tacting of SYSCFG
SYSCFG->CFGR1 |= SYSCFG_CFGR1_USB_IT_RMP;
#elif defined STM32F1
NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
NVIC_DisableIRQ(USB_HP_CAN1_TX_IRQn);
#elif defined STM32F0
NVIC_DisableIRQ(USB_IRQn);
RCC->APB1ENR |= RCC_APB1ENR_CRSEN;
RCC->CFGR3 &= ~RCC_CFGR3_USBSW; // reset USB
RCC->CR2 |= RCC_CR2_HSI48ON; // turn ON HSI48
uint32_t tmout = 16000000;
while(!(RCC->CR2 & RCC_CR2_HSI48RDY)){if(--tmout == 0) break;}
FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;
CRS->CFGR &= ~CRS_CFGR_SYNCSRC;
CRS->CFGR |= CRS_CFGR_SYNCSRC_1; // USB SOF selected as sync source
CRS->CR |= CRS_CR_AUTOTRIMEN; // enable auto trim
CRS->CR |= CRS_CR_CEN; // enable freq counter & block CRS->CFGR as read-only
RCC->CFGR |= RCC_CFGR_SW;
#endif
RCC->APB1ENR |= RCC_APB1ENR_USBEN;
//??
USB->CNTR = USB_CNTR_FRES; // Force USB Reset
for(uint32_t ctr = 0; ctr < 72000; ++ctr) nop(); // wait >1ms
USB->CNTR = 0;
USB->BTABLE = 0;
USB->DADDR = 0;
USB->ISTR = 0;
USB->CNTR = USB_CNTR_RESETM; // allow only reset interrupts
#if defined STM32F3
NVIC_EnableIRQ(USB_LP_IRQn);
#elif defined STM32F1
NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
#elif defined STM32F0
USB->BCDR |= USB_BCDR_DPPU;
NVIC_EnableIRQ(USB_IRQn);
#endif
}
#if defined STM32F3
void usb_lp_isr() __attribute__ ((alias ("USB_IRQ")));
#elif defined STM32F1
void usb_lp_can_rx0_isr() __attribute__ ((alias ("USB_IRQ")));
#elif defined STM32F0
void usb_isr() __attribute__ ((alias ("USB_IRQ")));
#endif

View File

@ -0,0 +1,328 @@
/*
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <wchar.h>
#ifndef _U_
#define _U_ __attribute__((unused))
#endif
/******************************************************************
* Hardware registers etc *
*****************************************************************/
#if defined STM32F0
#include <stm32f0.h>
#elif defined STM32F1
#include <stm32f1.h>
// there's no this define in standard header
#define USB_BASE ((uint32_t)0x40005C00)
#elif defined STM32F3
#include <stm32f3.h>
#endif
// max endpoints number
#define STM32ENDPOINTS 8
/**
* Buffers size definition
**/
// F0 - USB2_16; F1 - USB1_16; F3 - 1/2 depending on series
#if !defined USB1_16 && !defined USB2_16
#if defined STM32F0
#define USB2_16
#elif defined STM32F1
#define USB1_16
#else
#error "Can't determine USB1_16 or USB2_16, define by hands"
#endif
#endif
// BTABLE_SIZE FOR STM32F3:
// In STM32F303/302xB/C, 512 bytes SRAM is not shared with CAN.
// In STM32F302x6/x8 and STM32F30xxD/E, 726 bytes dedicated SRAM and 256 bytes shared SRAM with CAN i.e.
// 1Kbytes dedicated SRAM in case CAN is disabled.
// remember, that USB_BTABLE_SIZE will be divided by ACCESSZ, so don't divide it twice for 32-bit addressing
#ifdef NOCAN
#if defined STM32F0
#define USB_BTABLE_SIZE 1024
#elif defined STM32F3
#define USB_BTABLE_SIZE 726
//#warning "Please, check real buffer size due to docs"
#else
#error "define STM32F0 or STM32F3"
#endif
#else // !NOCAN: F0/F3 with CAN or F1 (can't simultaneously run CAN and USB)
#if defined STM32F0
#define USB_BTABLE_SIZE 768
#elif defined STM32F3
#define USB_BTABLE_SIZE 726
//#warning "Please, check real buffer size due to docs"
#else // STM32F103: 1024 bytes but with 32-bit addressing
#define USB_BTABLE_SIZE 1024
#endif
#endif // NOCAN
// first 64 bytes of USB_BTABLE are registers!
#define USB_BTABLE_BASE 0x40006000
#define USB ((USB_TypeDef *) USB_BASE)
#ifdef USB_BTABLE
#undef USB_BTABLE
#endif
#define USB_BTABLE ((USB_BtableDef *)(USB_BTABLE_BASE))
#define USB_ISTR_EPID 0x0000000F
#define USB_FNR_LSOF_0 0x00000800
#define USB_FNR_lSOF_1 0x00001000
#define USB_LPMCSR_BESL_0 0x00000010
#define USB_LPMCSR_BESL_1 0x00000020
#define USB_LPMCSR_BESL_2 0x00000040
#define USB_LPMCSR_BESL_3 0x00000080
#define USB_EPnR_CTR_RX 0x00008000
#define USB_EPnR_DTOG_RX 0x00004000
#define USB_EPnR_STAT_RX 0x00003000
#define USB_EPnR_STAT_RX_0 0x00001000
#define USB_EPnR_STAT_RX_1 0x00002000
#define USB_EPnR_SETUP 0x00000800
#define USB_EPnR_EP_TYPE 0x00000600
#define USB_EPnR_EP_TYPE_0 0x00000200
#define USB_EPnR_EP_TYPE_1 0x00000400
#define USB_EPnR_EP_KIND 0x00000100
#define USB_EPnR_CTR_TX 0x00000080
#define USB_EPnR_DTOG_TX 0x00000040
#define USB_EPnR_STAT_TX 0x00000030
#define USB_EPnR_STAT_TX_0 0x00000010
#define USB_EPnR_STAT_TX_1 0x00000020
#define USB_EPnR_EA 0x0000000F
#define USB_COUNTn_RX_BLSIZE 0x00008000
#define USB_COUNTn_NUM_BLOCK 0x00007C00
#define USB_COUNTn_RX 0x0000003F
#define USB_TypeDef USB_TypeDef_custom
typedef struct {
__IO uint32_t EPnR[STM32ENDPOINTS];
__IO uint32_t RESERVED[STM32ENDPOINTS];
__IO uint32_t CNTR;
__IO uint32_t ISTR;
__IO uint32_t FNR;
__IO uint32_t DADDR;
__IO uint32_t BTABLE;
#ifdef STM32F0
__IO uint32_t LPMCSR;
__IO uint32_t BCDR;
#endif
} USB_TypeDef;
// F303 D/E have 2x16 access scheme
typedef struct{
#if defined USB2_16
__IO uint16_t USB_ADDR_TX;
__IO uint16_t USB_COUNT_TX;
__IO uint16_t USB_ADDR_RX;
__IO uint16_t USB_COUNT_RX;
#define ACCESSZ (1)
#define BUFTYPE uint8_t
#elif defined USB1_16
__IO uint32_t USB_ADDR_TX;
__IO uint32_t USB_COUNT_TX;
__IO uint32_t USB_ADDR_RX;
__IO uint32_t USB_COUNT_RX;
#define ACCESSZ (2)
#define BUFTYPE uint16_t
#else
#error "Define USB1_16 or USB2_16"
#endif
} USB_EPDATA_TypeDef;
typedef struct{
__IO USB_EPDATA_TypeDef EP[STM32ENDPOINTS];
} USB_BtableDef;
#define EP0DATABUF_SIZE (64)
#define LASTADDR_DEFAULT (STM32ENDPOINTS * 8)
/******************************************************************
* Defines from usb.h *
*****************************************************************/
/*
* Device and/or Interface Class codes
*/
#define USB_CLASS_PER_INTERFACE 0
#define USB_CLASS_AUDIO 1
#define USB_CLASS_COMM 2
#define USB_CLASS_HID 3
#define USB_CLASS_PRINTER 7
#define USB_CLASS_PTP 6
#define USB_CLASS_MASS_STORAGE 8
#define USB_CLASS_HUB 9
#define USB_CLASS_DATA 10
#define USB_CLASS_MISC 0xef
#define USB_CLASS_VENDOR_SPEC 0xff
/*
* Descriptor types
*/
#define USB_DT_DEVICE 0x01
#define USB_DT_CONFIG 0x02
#define USB_DT_STRING 0x03
#define USB_DT_INTERFACE 0x04
#define USB_DT_ENDPOINT 0x05
#define USB_DT_QUALIFIER 0x06
#define USB_DT_IAD 0x0B
#define USB_DT_HID 0x21
#define USB_DT_REPORT 0x22
#define USB_DT_PHYSICAL 0x23
#define USB_DT_CS_INTERFACE 0x24
#define USB_DT_HUB 0x29
/*
* Descriptor sizes per descriptor type
*/
#define USB_DT_DEVICE_SIZE 18
#define USB_DT_CONFIG_SIZE 9
#define USB_DT_INTERFACE_SIZE 9
#define USB_DT_HID_SIZE 9
#define USB_DT_ENDPOINT_SIZE 7
#define USB_DT_QUALIFIER_SIZE 10
#define USB_DT_CS_INTERFACE_SIZE 5
#define USB_DT_IAD_SIZE 8
// bmRequestType & 0x80 == dev2host (1) or host2dev (0)
// recipient: bmRequestType & 0x1f
#define REQUEST_RECIPIENT(b) (b & 0x1f)
#define REQ_RECIPIENT_DEVICE 0
#define REQ_RECIPIENT_INTERFACE 1
#define REQ_RECIPIENT_ENDPOINT 2
#define REQ_RECIPIENT_OTHER 3
// type: [bmRequestType & 0x60 >> 5]
#define REQUEST_TYPE(b) ((b&0x60)>>5)
#define REQ_TYPE_STANDARD 0
#define REQ_TYPE_CLASS 1
#define REQ_TYPE_VENDOR 2
#define REQ_TYPE_RESERVED 3
//#define VENDOR_REQUEST 0x01
// standard device requests
#define GET_STATUS 0x00
#define CLEAR_FEATURE 0x01
#define SET_FEATURE 0x03
#define SET_ADDRESS 0x05
#define GET_DESCRIPTOR 0x06
#define SET_DESCRIPTOR 0x07
#define GET_CONFIGURATION 0x08
#define SET_CONFIGURATION 0x09
// and some standard interface requests
#define GET_INTERFACE 0x0A
#define SET_INTERFACE 0x0B
// and some standard endpoint requests
#define SYNC_FRAME 0x0C
// Types of descriptors
#define DEVICE_DESCRIPTOR 0x01
#define CONFIGURATION_DESCRIPTOR 0x02
#define STRING_DESCRIPTOR 0x03
#define DEVICE_QUALIFIER_DESCRIPTOR 0x06
#define DEBUG_DESCRIPTOR 0x0a
#define HID_REPORT_DESCRIPTOR 0x22
// EP types for EP_init
#define EP_TYPE_BULK 0x00
#define EP_TYPE_CONTROL 0x01
#define EP_TYPE_ISO 0x02
#define EP_TYPE_INTERRUPT 0x03
// EP types for descriptors
#define USB_BM_ATTR_CONTROL 0x00
#define USB_BM_ATTR_ISO 0x01
#define USB_BM_ATTR_BULK 0x02
#define USB_BM_ATTR_INTERRUPT 0x03
/******************************************************************
* Other stuff *
*****************************************************************/
#define RX_FLAG(epstat) (epstat & USB_EPnR_CTR_RX)
#define TX_FLAG(epstat) (epstat & USB_EPnR_CTR_TX)
#define SETUP_FLAG(epstat) (epstat & USB_EPnR_SETUP)
// EPnR bits manipulation
#define KEEP_DTOG_STAT(EPnR) (EPnR & ~(USB_EPnR_STAT_RX|USB_EPnR_STAT_TX|USB_EPnR_DTOG_RX|USB_EPnR_DTOG_TX))
#define KEEP_DTOG(EPnR) (EPnR & ~(USB_EPnR_DTOG_RX|USB_EPnR_DTOG_TX))
#define LANG_US (uint16_t)0x0409
#define _USB_STRING_(name, str) \
static const struct name \
{ \
uint8_t bLength; \
uint8_t bDescriptorType; \
uint16_t bString[(sizeof(str) - 2) / 2]; \
\
} \
name = {sizeof(name), 0x03, str}
#define _USB_LANG_ID_(name, lng_id) \
static const struct name \
{ \
uint8_t bLength; \
uint8_t bDescriptorType; \
uint16_t bString; \
\
} \
name = {0x04, 0x03, lng_id}
// EP0 configuration packet
typedef struct {
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} config_pack_t;
// endpoints state
typedef struct{
uint16_t *tx_buf; // transmission buffer address
uint16_t txbufsz; // transmission buffer size
uint8_t *rx_buf; // reception buffer address
void (*func)(); // endpoint action function
unsigned rx_cnt : 10; // received data counter
} ep_t;
extern volatile uint8_t usbON;
void USB_setup();
int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, void (*func)());
void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size);
void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size);
int EP_Read(uint8_t number, uint8_t *buf);
// could be [re]defined in usb_dev.c
extern void usb_class_request(config_pack_t *packet, uint8_t *data, uint16_t datalen);
extern void usb_vendor_request(config_pack_t *packet, uint8_t *data, uint16_t datalen);
extern void set_configuration();

View File

@ -0,0 +1,2 @@
#define BUILD_NUMBER "10"
#define BUILD_DATE "2025-09-11"