mirror of
https://github.com/eddyem/stm32samples.git
synced 2025-12-06 10:45:11 +03:00
SPI1/2 works
This commit is contained in:
parent
c4529a8447
commit
33497f79b5
4
F1:F103/BISS_C_encoders/99-myHW.rules
Normal file
4
F1:F103/BISS_C_encoders/99-myHW.rules
Normal file
@ -0,0 +1,4 @@
|
||||
ACTION=="add", DRIVERS=="usb", ENV{USB_IDS}="%s{idVendor}:%s{idProduct}"
|
||||
ACTION=="add", ENV{USB_IDS}=="067b:2303", ATTRS{interface}=="?*", PROGRAM="/bin/bash -c \"ls /dev | grep $attr{interface} | wc -l \"", SYMLINK+="$attr{interface}%c", MODE="0666", GROUP="tty"
|
||||
ACTION=="add", ENV{USB_IDS}=="0483:5740", ATTRS{interface}=="?*", PROGRAM="/bin/bash -c \"ls /dev | grep $attr{interface} | wc -l \"", SYMLINK+="$attr{interface}%c", MODE="0666", GROUP="tty"
|
||||
|
||||
Binary file not shown.
@ -1,3 +1,5 @@
|
||||
flash.c
|
||||
flash.h
|
||||
hardware.c
|
||||
hardware.h
|
||||
main.c
|
||||
@ -5,6 +7,8 @@ proto.c
|
||||
proto.h
|
||||
ringbuffer.c
|
||||
ringbuffer.h
|
||||
spi.c
|
||||
spi.h
|
||||
strfunc.c
|
||||
strfunc.h
|
||||
usart.c
|
||||
|
||||
196
F1:F103/BISS_C_encoders/flash.c
Normal file
196
F1:F103/BISS_C_encoders/flash.c
Normal file
@ -0,0 +1,196 @@
|
||||
/*
|
||||
* This file is part of the encoders 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 "stm32f1.h"
|
||||
|
||||
#include "flash.h"
|
||||
#include "strfunc.h"
|
||||
#include "usb_dev.h" // printout
|
||||
#include <string.h> // memcpy
|
||||
|
||||
extern const uint32_t __varsstart, _BLOCKSIZE;
|
||||
static const uint32_t FLASH_blocksize = (uint32_t)&_BLOCKSIZE;
|
||||
static uint32_t maxCnum = 1024 / sizeof(user_conf); // can't use blocksize here
|
||||
|
||||
#define USERCONF_INITIALIZER { \
|
||||
.userconf_sz = sizeof(user_conf) \
|
||||
}
|
||||
|
||||
static int write2flash(const void*, const void*, uint32_t);
|
||||
const user_conf *Flash_Data = (const user_conf *)(&__varsstart);
|
||||
user_conf the_conf = USERCONF_INITIALIZER;
|
||||
|
||||
int currentconfidx = -1; // index of current configuration
|
||||
|
||||
/**
|
||||
* @brief binarySearch - binary search in flash for last non-empty cell
|
||||
* any struct searched should have its sizeof() @ the first field!!!
|
||||
* @param l - left index
|
||||
* @param r - right index (should be @1 less than last index!)
|
||||
* @param start - starting address
|
||||
* @param stor_size - size of structure to search
|
||||
* @return index of non-empty cell or -1
|
||||
*/
|
||||
static int binarySearch(int r, const uint8_t *start, int stor_size){
|
||||
int l = 0;
|
||||
while(r >= l){
|
||||
int mid = l + (r - l) / 2;
|
||||
#ifdef EBUG
|
||||
CMDWR("mid/l/r=");
|
||||
CMDWR(u2str(mid)); CMDWR("/");
|
||||
CMDWR(u2str(l)); CMDWR("/");
|
||||
CMDWR(u2str(r)); CMDWR("/");
|
||||
CMDn();
|
||||
#endif
|
||||
const uint8_t *s = start + mid * stor_size;
|
||||
if(*((const uint16_t*)s) == stor_size){
|
||||
if(*((const uint16_t*)(s + stor_size)) == 0xffff){ // next is free
|
||||
return mid;
|
||||
}else{ // element is to the right
|
||||
l = mid + 1;
|
||||
}
|
||||
}else{ // element is to the left
|
||||
r = mid - 1;
|
||||
}
|
||||
}
|
||||
return -1; // not found
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief flashstorage_init - initialization of user conf storage
|
||||
* run in once @ start
|
||||
*/
|
||||
void flashstorage_init(){
|
||||
if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){
|
||||
uint32_t flsz = FLASH_SIZE * 1024; // size in bytes
|
||||
flsz -= (uint32_t)(&__varsstart) - FLASH_BASE;
|
||||
maxCnum = flsz / sizeof(user_conf);
|
||||
}
|
||||
#ifdef EBUG
|
||||
CMDWR("INIT\n");
|
||||
#endif
|
||||
// -1 if there's no data at all & flash is clear; maxnum-1 if flash is full
|
||||
currentconfidx = binarySearch((int)maxCnum-2, (const uint8_t*)Flash_Data, sizeof(user_conf));
|
||||
if(currentconfidx > -1){
|
||||
memcpy(&the_conf, &Flash_Data[currentconfidx], sizeof(user_conf));
|
||||
}
|
||||
#ifdef EBUG
|
||||
CMDWR("currentconfidx="); CMDWR(i2str(currentconfidx)); CMDn();
|
||||
#endif
|
||||
}
|
||||
|
||||
// store new configuration
|
||||
// @return 0 if all OK
|
||||
int store_userconf(){
|
||||
// maxnum - 3 means that there always should be at least one empty record after last data
|
||||
// for binarySearch() checking that there's nothing more after it!
|
||||
if(currentconfidx > (int)maxCnum - 3){ // there's no more place
|
||||
currentconfidx = 0;
|
||||
if(erase_storage(-1)) return 1;
|
||||
}else ++currentconfidx; // take next data position (0 - within first run after firmware flashing)
|
||||
return write2flash((const void*)&Flash_Data[currentconfidx], &the_conf, sizeof(the_conf));
|
||||
}
|
||||
|
||||
static int write2flash(const void *start, const void *wrdata, uint32_t stor_size){
|
||||
int ret = 0;
|
||||
if (FLASH->CR & FLASH_CR_LOCK){ // unloch flash
|
||||
FLASH->KEYR = FLASH_KEY1;
|
||||
FLASH->KEYR = FLASH_KEY2;
|
||||
}
|
||||
while (FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH;
|
||||
FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR; // clear all flags
|
||||
FLASH->CR |= FLASH_CR_PG;
|
||||
const uint16_t *data = (const uint16_t*) wrdata;
|
||||
volatile uint16_t *address = (volatile uint16_t*) start;
|
||||
uint32_t i, count = (stor_size + 1) / 2;
|
||||
for(i = 0; i < count; ++i){
|
||||
*(volatile uint16_t*)(address + i) = data[i];
|
||||
while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH;
|
||||
if(*(volatile uint16_t*)(address + i) != data[i]){
|
||||
CMDWR("DON'T MATCH!\n");
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
if(FLASH->SR & FLASH_SR_PGERR){
|
||||
CMDWR("Prog err\n");
|
||||
ret = 1; // program error - meet not 0xffff
|
||||
break;
|
||||
}
|
||||
FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR;
|
||||
}
|
||||
FLASH->CR &= ~(FLASH_CR_PG);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// erase Nth page of flash storage (flash should be prepared!)
|
||||
static int erase_pageN(int N){
|
||||
int ret = 0;
|
||||
#ifdef EBUG
|
||||
CMDWR("Erase page #"); CMDWR(u2str(N)); CMDn();
|
||||
#endif
|
||||
FLASH->AR = (uint32_t)Flash_Data + N*FLASH_blocksize;
|
||||
FLASH->CR |= FLASH_CR_STRT;
|
||||
while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH;
|
||||
FLASH->SR = FLASH_SR_EOP;
|
||||
if(FLASH->SR & FLASH_SR_WRPRTERR){ /* Check Write protection error */
|
||||
ret = 1;
|
||||
FLASH->SR = FLASH_SR_WRPRTERR; /* Clear the flag by software by writing it at 1*/
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// erase full storage (npage < 0) or its nth page; @return 0 if all OK
|
||||
int erase_storage(int npage){
|
||||
int ret = 0;
|
||||
uint32_t end = 1, start = 0, flsz = 0;
|
||||
if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){
|
||||
flsz = FLASH_SIZE * 1024; // size in bytes
|
||||
flsz -= (uint32_t)Flash_Data - FLASH_BASE;
|
||||
}
|
||||
end = flsz / FLASH_blocksize;
|
||||
#ifdef EBUG
|
||||
CMDWR("FLASH_SIZE="); CMDWR(u2str(FLASH_SIZE));
|
||||
CMDWR("\nflsz="); CMDWR(u2str(flsz));
|
||||
CMDWR("\nend="); CMDWR(u2str(end));
|
||||
CMDWR("\ncurrentconfidx="); CMDWR(u2str(currentconfidx));
|
||||
CMDWR("\nmaxCnum="); CMDWR(u2str(maxCnum));
|
||||
CMDn();
|
||||
#endif
|
||||
if(end == 0 || end >= FLASH_SIZE) return 1;
|
||||
if(npage > -1){ // erase only one page
|
||||
if((uint32_t)npage >= end) return 1;
|
||||
start = npage;
|
||||
end = start + 1;
|
||||
}
|
||||
if((FLASH->CR & FLASH_CR_LOCK) != 0){
|
||||
FLASH->KEYR = FLASH_KEY1;
|
||||
FLASH->KEYR = FLASH_KEY2;
|
||||
}
|
||||
while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH;
|
||||
FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR;
|
||||
FLASH->CR |= FLASH_CR_PER;
|
||||
for(uint32_t i = start; i < end; ++i){
|
||||
if(erase_pageN(i)){
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
FLASH->CR &= ~FLASH_CR_PER;
|
||||
return ret;
|
||||
}
|
||||
|
||||
46
F1:F103/BISS_C_encoders/flash.h
Normal file
46
F1:F103/BISS_C_encoders/flash.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* This file is part of the encoders 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 <stdint.h>
|
||||
#include "usb_descr.h"
|
||||
|
||||
#define FLASH_SIZE_REG ((uint32_t)0x1FFFF7E0)
|
||||
#define FLASH_SIZE *((uint16_t*)FLASH_SIZE_REG)
|
||||
|
||||
// maximal size (in letters) of iInterface for settings
|
||||
#define MAX_IINTERFACE_SZ (16)
|
||||
|
||||
/*
|
||||
* struct to save user configurations
|
||||
*/
|
||||
typedef struct __attribute__((packed, aligned(4))){
|
||||
uint16_t userconf_sz; // "magick number"
|
||||
uint16_t iInterface[bTotNumEndpoints][MAX_IINTERFACE_SZ]; // hryunikod!
|
||||
uint8_t iIlengths[bTotNumEndpoints];
|
||||
} user_conf;
|
||||
|
||||
extern user_conf the_conf;
|
||||
extern int currentconfidx;
|
||||
|
||||
void flashstorage_init();
|
||||
int store_userconf();
|
||||
int erase_storage(int npage);
|
||||
|
||||
|
||||
@ -16,21 +16,27 @@
|
||||
*/
|
||||
|
||||
#include "hardware.h"
|
||||
#include "spi.h"
|
||||
|
||||
static inline void gpio_setup(){
|
||||
// Enable clocks to the GPIO subsystems (PB for ADC), turn on AFIO clocking to disable SWD/JTAG
|
||||
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_AFIOEN;
|
||||
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_AFIOEN;
|
||||
// turn off SWJ/JTAG
|
||||
// AFIO->MAPR = AFIO_MAPR_SWJ_CFG_DISABLE;
|
||||
AFIO->MAPR = AFIO_MAPR_SWJ_CFG_JTAGDISABLE; // for PA15
|
||||
// Set led as opendrain output
|
||||
GPIOC->CRH |= CRH(13, CNF_ODOUTPUT|MODE_SLOW);
|
||||
GPIOA->CRL = CRL(5, CNF_AFPP|MODE_FAST) | CRL(6, CNF_FLINPUT);
|
||||
// USB pullup (PA15) - pushpull output
|
||||
GPIOA->CRH = CRH(15, CNF_PPOUTPUT|MODE_SLOW);
|
||||
GPIOB->CRH = CRH(13, CNF_AFPP|MODE_FAST) | CRH(14, CNF_FLINPUT);
|
||||
}
|
||||
|
||||
void hw_setup(){
|
||||
gpio_setup();
|
||||
// setup both SPI channels
|
||||
spi_setup(1);
|
||||
spi_setup(2);
|
||||
}
|
||||
|
||||
#ifndef EBUG
|
||||
|
||||
@ -16,8 +16,10 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "flash.h"
|
||||
#include "hardware.h"
|
||||
#include "proto.h"
|
||||
#include "spi.h"
|
||||
#include "strfunc.h"
|
||||
#include "usart.h"
|
||||
#include "usb_dev.h"
|
||||
@ -32,7 +34,9 @@ void sys_tick_handler(void){
|
||||
int main(){
|
||||
char inbuff[RBINSZ];
|
||||
uint32_t lastT = 0, lastS = 0;
|
||||
uint8_t encbuf[ENCODER_BUFSZ];
|
||||
StartHSE();
|
||||
flashstorage_init();
|
||||
hw_setup();
|
||||
USBPU_OFF();
|
||||
SysTick_Config(72000);
|
||||
@ -79,6 +83,9 @@ int main(){
|
||||
CMDWR(inbuff);
|
||||
CMDWR("'\n");
|
||||
}
|
||||
if(spi_read_enc(0, encbuf)){ // send encoder data
|
||||
hexdump(I_X, encbuf, ENCODER_BUFSZ);
|
||||
}
|
||||
}
|
||||
if(CDCready[I_Y]){
|
||||
int l = USB_receivestr(I_Y, inbuff, RBINSZ);
|
||||
@ -88,6 +95,9 @@ int main(){
|
||||
CMDWR(inbuff);
|
||||
CMDWR("'\n");
|
||||
}
|
||||
if(spi_read_enc(1, encbuf)){ // send encoder data
|
||||
hexdump(I_Y, encbuf, ENCODER_BUFSZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
@ -1,4 +1,94 @@
|
||||
set FLASH_SIZE 0x10000
|
||||
|
||||
source [find interface/stlink-v2-1.cfg]
|
||||
source [find target/stm32f1x.cfg]
|
||||
source [find interface/stlink.cfg]
|
||||
|
||||
|
||||
# script for stm32f1x family
|
||||
|
||||
#
|
||||
# stm32 devices support both JTAG and SWD transports.
|
||||
#
|
||||
source [find target/swj-dp.tcl]
|
||||
source [find mem_helper.tcl]
|
||||
|
||||
if { [info exists CHIPNAME] } {
|
||||
set _CHIPNAME $CHIPNAME
|
||||
} else {
|
||||
set _CHIPNAME stm32f1x
|
||||
}
|
||||
|
||||
set _ENDIAN little
|
||||
|
||||
# Work-area is a space in RAM used for flash programming
|
||||
# By default use 4kB (as found on some STM32F100s)
|
||||
if { [info exists WORKAREASIZE] } {
|
||||
set _WORKAREASIZE $WORKAREASIZE
|
||||
} else {
|
||||
set _WORKAREASIZE 0x1000
|
||||
}
|
||||
|
||||
# Allow overriding the Flash bank size
|
||||
if { [info exists FLASH_SIZE] } {
|
||||
set _FLASH_SIZE $FLASH_SIZE
|
||||
} else {
|
||||
# autodetect size
|
||||
set _FLASH_SIZE 0
|
||||
}
|
||||
|
||||
#jtag scan chain
|
||||
if { [info exists CPUTAPID] } {
|
||||
set _CPUTAPID $CPUTAPID
|
||||
} else {
|
||||
if { [using_jtag] } {
|
||||
# See STM Document RM0008 Section 26.6.3
|
||||
set _CPUTAPID 0x2ba01477
|
||||
} {
|
||||
# this is the SW-DP tap id not the jtag tap id
|
||||
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
|
||||
|
||||
# flash size will be probed
|
||||
set _FLASHNAME $_CHIPNAME.flash
|
||||
flash bank $_FLASHNAME stm32f1x 0x08000000 $_FLASH_SIZE 0 0 $_TARGETNAME
|
||||
|
||||
# JTAG speed should be <= F_CPU/6. F_CPU after reset is 8MHz, so use F_JTAG = 1MHz
|
||||
adapter_khz 1000
|
||||
|
||||
adapter_nsrst_delay 100
|
||||
if {[using_jtag]} {
|
||||
jtag_ntrst_delay 100
|
||||
}
|
||||
|
||||
reset_config srst_nogate
|
||||
|
||||
if {![using_hla]} {
|
||||
# if srst is not fitted use SYSRESETREQ to
|
||||
# perform a soft reset
|
||||
cortex_m reset_config sysresetreq
|
||||
}
|
||||
|
||||
$_TARGETNAME configure -event examine-end {
|
||||
# DBGMCU_CR |= DBG_WWDG_STOP | DBG_IWDG_STOP |
|
||||
# DBG_STANDBY | DBG_STOP | DBG_SLEEP
|
||||
mmw 0xE0042004 0x00000307 0
|
||||
}
|
||||
|
||||
$_TARGETNAME configure -event trace-config {
|
||||
# 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
|
||||
}
|
||||
|
||||
4
F1:F103/BISS_C_encoders/openocd.cfg_
Normal file
4
F1:F103/BISS_C_encoders/openocd.cfg_
Normal file
@ -0,0 +1,4 @@
|
||||
set FLASH_SIZE 0x10000
|
||||
|
||||
source [find interface/stlink.cfg]
|
||||
source [find target/stm32f1x.cfg]
|
||||
@ -18,20 +18,13 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "flash.h"
|
||||
#include "proto.h"
|
||||
#include "spi.h"
|
||||
#include "strfunc.h"
|
||||
#include "usb_dev.h"
|
||||
#include "version.inc"
|
||||
|
||||
// commands indexes
|
||||
typedef enum{
|
||||
C_dummy,
|
||||
C_help,
|
||||
C_sendX,
|
||||
C_sendY,
|
||||
C_AMOUNT
|
||||
} cmd_e;
|
||||
|
||||
// error codes
|
||||
typedef enum {
|
||||
ERR_OK, // no errors
|
||||
@ -50,6 +43,27 @@ static const char* const errors[ERR_AMOUNT] = {
|
||||
[ERR_SILENCE] = "",
|
||||
};
|
||||
|
||||
// commands indexes
|
||||
typedef enum{
|
||||
C_dummy,
|
||||
C_help,
|
||||
C_sendX,
|
||||
C_sendY,
|
||||
C_setiface1,
|
||||
C_setiface2,
|
||||
C_setiface3,
|
||||
C_dumpconf,
|
||||
C_erasestorage,
|
||||
C_storeconf,
|
||||
C_reboot,
|
||||
C_fin,
|
||||
C_encstart,
|
||||
C_spistat,
|
||||
C_spiinit,
|
||||
C_spideinit,
|
||||
C_AMOUNT
|
||||
} cmd_e;
|
||||
|
||||
// command handler (idx - index of command in list, par - all after equal sign in "cmd = par")
|
||||
typedef errcode_e (*handler_t)(cmd_e idx, char *par);
|
||||
|
||||
@ -65,6 +79,7 @@ static const funcdescr_t commands[C_AMOUNT];
|
||||
CMDWR(i2str(ival)); CMDn(); return ERR_SILENCE; }while(0)
|
||||
|
||||
static errcode_e help(cmd_e idx, char* par);
|
||||
static errcode_e dumpconf(cmd_e idx, char *par);
|
||||
|
||||
static errcode_e dummy(cmd_e idx, char *par){
|
||||
static int32_t val = -111;
|
||||
@ -98,13 +113,132 @@ static errcode_e sendenc(cmd_e idx, char *par){
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static errcode_e setiface(cmd_e idx, char *par){
|
||||
if(idx < C_setiface1 || idx >= C_setiface1 + bTotNumEndpoints) return ERR_BADCMD;
|
||||
idx -= C_setiface1; // now it is an index of iIlengths
|
||||
if(par && *par){
|
||||
int l = strlen(par);
|
||||
DBGs(i2str(l));
|
||||
if(l > MAX_IINTERFACE_SZ) return ERR_BADPAR; // too long
|
||||
the_conf.iIlengths[idx] = (uint8_t) l * 2;
|
||||
char *ptr = (char*)the_conf.iInterface[idx];
|
||||
for(int i = 0; i < l; ++i){ // make fucking hryunicode
|
||||
*ptr++ = *par++;
|
||||
*ptr++ = 0;
|
||||
}
|
||||
}
|
||||
// getter
|
||||
int l = the_conf.iIlengths[idx] / 2;
|
||||
char *ptr = (char*)the_conf.iInterface[idx];
|
||||
CMDWR(commands[idx + C_setiface1].cmd);
|
||||
CMDWR("=");
|
||||
for(int i = 0; i < l; ++i){
|
||||
USB_putbyte(I_CMD, *ptr);
|
||||
ptr += 2;
|
||||
}
|
||||
CMDn();
|
||||
return ERR_SILENCE;
|
||||
}
|
||||
|
||||
static errcode_e erasestor(cmd_e _U_ idx, char *par){
|
||||
int32_t npage = -1;
|
||||
if(par){
|
||||
if(par == getint(par, &npage)) return ERR_BADPAR;
|
||||
}
|
||||
if(erase_storage(npage)) return ERR_FAIL;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static errcode_e storeconf(_U_ cmd_e idx, _U_ char *par){
|
||||
if(store_userconf()) return ERR_FAIL;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static errcode_e reboot(_U_ cmd_e idx, _U_ char *par){
|
||||
NVIC_SystemReset();
|
||||
return ERR_OK; // never reached
|
||||
}
|
||||
|
||||
static errcode_e fini(_U_ cmd_e idx, _U_ char *par){
|
||||
flashstorage_init();
|
||||
return ERR_OK; // never reached
|
||||
}
|
||||
|
||||
static errcode_e encstart(_U_ cmd_e idx, _U_ char *par){
|
||||
if(!spi_start_enc(0)) return ERR_FAIL;
|
||||
if(!spi_start_enc(1)) return ERR_FAIL;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static errcode_e spistat(_U_ cmd_e idx, _U_ char *par){
|
||||
for(int i = 1; i < 3; ++i){
|
||||
USB_sendstr(I_CMD, "SPI");
|
||||
USB_putbyte(I_CMD, '0' + i);
|
||||
USB_sendstr(I_CMD, ": ");
|
||||
switch(spi_status[i]){
|
||||
case SPI_NOTREADY:
|
||||
USB_sendstr(I_CMD, "not ready");
|
||||
break;
|
||||
case SPI_BUSY:
|
||||
USB_sendstr(I_CMD, "busy");
|
||||
break;
|
||||
case SPI_READY:
|
||||
USB_sendstr(I_CMD, "ready");
|
||||
break;
|
||||
}
|
||||
newline(I_CMD);
|
||||
}
|
||||
return ERR_SILENCE;
|
||||
}
|
||||
|
||||
static errcode_e spiinit(_U_ cmd_e idx, _U_ char *par){
|
||||
for(int i = 1; i < 3; ++i){
|
||||
USB_sendstr(I_CMD, "Init SPI");
|
||||
USB_putbyte(I_CMD, '0' + i);
|
||||
newline(I_CMD);
|
||||
spi_setup(i);
|
||||
}
|
||||
return ERR_SILENCE;
|
||||
}
|
||||
|
||||
static errcode_e spideinit(_U_ cmd_e idx, _U_ char *par){
|
||||
for(int i = 1; i < 3; ++i){
|
||||
USB_sendstr(I_CMD, "DEinit SPI");
|
||||
USB_putbyte(I_CMD, '0' + i);
|
||||
newline(I_CMD);
|
||||
spi_deinit(i);
|
||||
}
|
||||
return ERR_SILENCE;
|
||||
}
|
||||
|
||||
// text commands
|
||||
static const funcdescr_t commands[C_AMOUNT] = {
|
||||
[C_dummy] = {"dummy", dummy},
|
||||
[C_help] = {"help", help},
|
||||
[C_sendX] = {"sendx", sendenc},
|
||||
[C_sendY] = {"sendy", sendenc},
|
||||
};
|
||||
[C_setiface1] = {"setiface1", setiface},
|
||||
[C_setiface2] = {"setiface2", setiface},
|
||||
[C_setiface3] = {"setiface3", setiface},
|
||||
[C_dumpconf] = {"dumpconf", dumpconf},
|
||||
[C_erasestorage] = {"erasestorage", erasestor},
|
||||
[C_storeconf] = {"storeconf", storeconf},
|
||||
[C_reboot] = {"reboot", reboot},
|
||||
[C_fin] = {"fin", fini},
|
||||
[C_encstart] = {"readenc", encstart},
|
||||
[C_spistat] = {"spistat", spistat},
|
||||
[C_spiinit] = {"spiinit", spiinit},
|
||||
[C_spideinit] = {"spideinit", spideinit},
|
||||
};
|
||||
|
||||
static errcode_e dumpconf(cmd_e _U_ idx, char _U_ *par){
|
||||
CMDWR("userconf_sz="); CMDWR(u2str(the_conf.userconf_sz));
|
||||
CMDWR("\ncurrentconfidx="); CMDWR(i2str(currentconfidx));
|
||||
CMDn();
|
||||
for(int i = 0; i < bTotNumEndpoints; ++i)
|
||||
setiface(C_setiface1 + i, NULL);
|
||||
return ERR_SILENCE;
|
||||
}
|
||||
|
||||
typedef struct{
|
||||
int idx; // command index (if < 0 - just display `help` as grouping header)
|
||||
@ -113,18 +247,31 @@ typedef struct{
|
||||
|
||||
// SHOUL be sorted and grouped
|
||||
static const help_t helpmessages[] = {
|
||||
{-1, "Configuration"},
|
||||
{C_dumpconf, "dump current configuration"},
|
||||
{C_erasestorage, "erase full storage or current page (=n)"},
|
||||
{C_setiface1, "set name of first (command) interface"},
|
||||
{C_setiface2, "set name of second (axis X) interface"},
|
||||
{C_setiface3, "set name of third (axis Y) interface"},
|
||||
{C_storeconf, "store configuration in flash memory"},
|
||||
{-1, "Different commands"},
|
||||
{C_dummy, "dummy integer setter/getter"},
|
||||
{C_encstart, "start reading encoders"},
|
||||
{C_help, "show this help"},
|
||||
{-1, "Debug commands"},
|
||||
{C_sendX, "send text string to X encoder"},
|
||||
{C_sendY, "send text string to Y encoder"},
|
||||
{C_reboot, "reboot MCU"},
|
||||
{C_spideinit, "deinit SPI"},
|
||||
{C_spiinit, "init SPI"},
|
||||
{C_spistat, "get status of both SPI interfaces"},
|
||||
{-1, "Debug"},
|
||||
{C_sendX, "send text string to X encoder's terminal"},
|
||||
{C_sendY, "send text string to Y encoder's terminal"},
|
||||
{C_fin, "reinit flash"},
|
||||
{-1, NULL},
|
||||
};
|
||||
|
||||
static errcode_e help(_U_ cmd_e idx, _U_ char* par){
|
||||
CMDWRn("https://github.com/eddyem/stm32samples/tree/master/F1:F103/ build #" BUILD_NUMBER " @ " BUILD_DATE);
|
||||
CMDWRn("commands format: command[=setter]\\n");
|
||||
CMDWRn("https://github.com/eddyem/stm32samples/tree/master/F1:F103/BISS_C_encoders build #" BUILD_NUMBER " @ " BUILD_DATE);
|
||||
CMDWRn("\ncommands format: 'command[=setter]\\n'");
|
||||
const help_t *c = helpmessages;
|
||||
while(c->help){
|
||||
if(c->idx < 0){ // header
|
||||
@ -139,7 +286,7 @@ static errcode_e help(_U_ cmd_e idx, _U_ char* par){
|
||||
CMDn();
|
||||
++c;
|
||||
}
|
||||
return ERR_OK;
|
||||
return ERR_SILENCE;
|
||||
}
|
||||
|
||||
// *cmd is "command" for commands/getters or "parameter=value" for setters
|
||||
@ -149,7 +296,7 @@ void parse_cmd(char *cmd){
|
||||
char *cmdstart = omit_spaces(cmd), *parstart = NULL;
|
||||
if(!cmdstart) goto retn;
|
||||
char *ptr = cmdstart;
|
||||
while(*ptr > '@') ++ptr;
|
||||
while(*ptr > ' ' && *ptr != '=') ++ptr;
|
||||
if(*ptr && *ptr <= ' '){ // there's spaces after command
|
||||
*ptr++ = 0;
|
||||
ptr = omit_spaces(ptr);
|
||||
|
||||
174
F1:F103/BISS_C_encoders/spi.c
Normal file
174
F1:F103/BISS_C_encoders/spi.c
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* This file is part of the encoders 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 <stm32f1.h>
|
||||
|
||||
#include "spi.h"
|
||||
#include <string.h> // memcpy
|
||||
#include "usb_dev.h"
|
||||
#ifdef EBUG
|
||||
#include "strfunc.h"
|
||||
#endif
|
||||
|
||||
#define CHKIDX(idx) do{if(idx == 0 || idx > AMOUNT_OF_SPI) return;}while(0)
|
||||
#define CHKIDXR(idx) do{if(idx == 0 || idx > AMOUNT_OF_SPI) return 0;}while(0)
|
||||
|
||||
spiStatus spi_status[AMOUNT_OF_SPI+1] = {0, SPI_NOTREADY, SPI_NOTREADY};
|
||||
static volatile SPI_TypeDef* const SPIs[AMOUNT_OF_SPI+1] = {NULL, SPI1, SPI2};
|
||||
static volatile DMA_Channel_TypeDef * const DMAs[AMOUNT_OF_SPI+1] = {NULL, DMA1_Channel2, DMA1_Channel4};
|
||||
#define WAITX(x) do{volatile uint32_t wctr = 0; while((x) && (++wctr < 360000)) IWDG->KR = IWDG_REFRESH; if(wctr==360000){ DBG("timeout"); return 0;}}while(0)
|
||||
|
||||
static uint8_t encoderbuf[AMOUNT_OF_SPI][ENCODER_BUFSZ] = {0};
|
||||
static uint8_t freshdata[AMOUNT_OF_SPI] = {0};
|
||||
|
||||
// init SPI to work RX-only with DMA
|
||||
// SPI1 (PA5/PA6): DMA1_Channel2
|
||||
// SPI2 (PB13/PB14): DMA1_Channel4
|
||||
void spi_setup(uint8_t idx){
|
||||
CHKIDX(idx);
|
||||
volatile SPI_TypeDef *SPI = SPIs[idx];
|
||||
SPI->CR1 = 0; // clear EN
|
||||
SPI->CR2 = 0;
|
||||
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
|
||||
volatile DMA_Channel_TypeDef *DMA = DMAs[idx];
|
||||
if(idx == 1){ // PA5/PA6; 72MHz
|
||||
RCC->APB2RSTR = RCC_APB2RSTR_SPI1RST;
|
||||
RCC->APB2RSTR = 0; // clear reset
|
||||
GPIOA->CRL = (GPIOA->CRL & ~(GPIO_CRL_CNF5 | GPIO_CRL_CNF6))
|
||||
| CRL(5, CNF_AFPP|MODE_FAST) | CRL(6, CNF_FLINPUT);
|
||||
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
|
||||
SPI->CR1 = SPI_CR1_BR_0 | SPI_CR1_BR_2; // Fpclk/64
|
||||
NVIC_EnableIRQ(DMA1_Channel2_IRQn); // enable Rx interrupt
|
||||
}else if(idx == 2){ // PB12..PB15; 36MHz
|
||||
RCC->APB1RSTR = RCC_APB1RSTR_SPI2RST;
|
||||
RCC->APB1RSTR = 0;
|
||||
GPIOB->CRH = (GPIOB->CRH & ~(GPIO_CRH_CNF13 | GPIO_CRH_CNF14))
|
||||
| CRH(13, CNF_AFPP|MODE_FAST) | CRH(14, CNF_FLINPUT);
|
||||
RCC->APB1ENR |= RCC_APB1ENR_SPI2EN;
|
||||
SPI->CR1 = SPI_CR1_BR_2; // Fpclk/32
|
||||
NVIC_EnableIRQ(DMA1_Channel4_IRQn);
|
||||
}else return; // err
|
||||
// Baudrate = 0b110 - fpclk/128
|
||||
SPI->CR1 |= SPI_CR1_MSTR | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_RXONLY;
|
||||
SPI->CR2 = SPI_CR2_RXDMAEN;
|
||||
DMA->CPAR = (uint32_t)&(SPI->DR);
|
||||
DMA->CCR = DMA_CCR_MINC | DMA_CCR_TCIE | DMA_CCR_TEIE; // mem inc, hw->mem, Rx complete and error interrupt
|
||||
spi_status[idx] = SPI_READY;
|
||||
DBG("SPI works");
|
||||
}
|
||||
|
||||
void spi_onoff(uint8_t idx, uint8_t on){
|
||||
CHKIDX(idx);
|
||||
volatile SPI_TypeDef *SPI = SPIs[idx];
|
||||
if(on){
|
||||
DBGs(u2str(idx));
|
||||
DBG("turn on SPI");
|
||||
SPI->CR1 |= SPI_CR1_SPE;
|
||||
spi_status[idx] = SPI_BUSY;
|
||||
}else{
|
||||
SPI->CR1 &= ~SPI_CR1_SPE;
|
||||
spi_status[idx] = SPI_READY;
|
||||
}
|
||||
}
|
||||
|
||||
// turn off given SPI channel and release GPIO
|
||||
void spi_deinit(uint8_t idx){
|
||||
CHKIDX(idx);
|
||||
DBG("deinit SPI");
|
||||
volatile SPI_TypeDef *SPI = SPIs[idx];
|
||||
SPI->CR1 = 0;
|
||||
SPI->CR2 = 0;
|
||||
if(idx == 1){
|
||||
RCC->APB2ENR &= ~RCC_APB2ENR_SPI1EN;
|
||||
GPIOA->CRL &= ~(GPIO_CRL_CNF5 | GPIO_CRL_CNF6);
|
||||
}else if(idx == 2){
|
||||
RCC->APB1ENR &= ~RCC_APB1ENR_SPI2EN;
|
||||
GPIOB->CRH &= ~(GPIO_CRH_CNF13 | GPIO_CRH_CNF14);
|
||||
}
|
||||
spi_status[idx] = SPI_NOTREADY;
|
||||
}
|
||||
|
||||
int spi_waitbsy(uint8_t idx){
|
||||
CHKIDXR(idx);
|
||||
DBGs(u2str(idx));
|
||||
DBG("wait busy");
|
||||
WAITX(SPIs[idx]->SR & SPI_SR_BSY);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// just copy last read encoder value into `buf`
|
||||
// @return TRUE if got fresh data
|
||||
int spi_read_enc(uint8_t encno, uint8_t buf[8]){
|
||||
if(encno > 1 || !freshdata[encno]) return FALSE;
|
||||
DBGs(u2str(encno)); DBG("Read encoder data");
|
||||
memcpy(buf, encoderbuf[encno], ENCODER_BUFSZ);
|
||||
freshdata[encno] = 0; // clear fresh status
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// start encoder reading over DMA
|
||||
// @return FALSE if SPI is busy
|
||||
// here `encodernum` is 0 (SPI1) or 1 (SPI2), not 1/2 as SPI index!
|
||||
int spi_start_enc(int encodernum){
|
||||
int spiidx = encodernum + 1;
|
||||
DBG("start enc");
|
||||
if(spiidx < 1 || spiidx > AMOUNT_OF_SPI) return FALSE;
|
||||
if(spi_status[spiidx] != SPI_READY) return FALSE;
|
||||
if(!spi_waitbsy(spiidx)) return FALSE;
|
||||
if(SPI1->CR1 & SPI_CR1_SPE) DBG("spi1 works!");
|
||||
if(SPI2->CR1 & SPI_CR1_SPE) DBG("spi2 works!");
|
||||
volatile DMA_Channel_TypeDef *DMA = DMAs[spiidx];
|
||||
DMA->CMAR = (uint32_t) encoderbuf[encodernum];
|
||||
DMA->CNDTR = ENCODER_BUFSZ;
|
||||
DBG("turn on spi");
|
||||
spi_onoff(spiidx, 1);
|
||||
DMA->CCR |= DMA_CCR_EN;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// SSI got fresh data
|
||||
void dma1_channel2_isr(){
|
||||
if(DMA1->ISR & DMA_ISR_TEIF2){
|
||||
DMA1->IFCR = DMA_IFCR_CTEIF2;
|
||||
}
|
||||
if(DMA1->ISR & DMA_ISR_TCIF2){
|
||||
//uint32_t ctr = TIM2->CNT;
|
||||
DMA1->IFCR = DMA_IFCR_CTCIF2;
|
||||
freshdata[0] = 1;
|
||||
//encoderbuf[5] = (ctr >> 16) & 0xff;
|
||||
//encoderbuf[6] = (ctr >> 8 ) & 0xff;
|
||||
//encoderbuf[7] = (ctr >> 0 ) & 0xff;
|
||||
}
|
||||
spi_onoff(1, 0);
|
||||
// turn off DMA
|
||||
DMA1_Channel2->CCR &= ~DMA_CCR_EN;
|
||||
}
|
||||
|
||||
void dma1_channel4_isr(){
|
||||
if(DMA1->ISR & DMA_ISR_TEIF4){
|
||||
DMA1->IFCR = DMA_IFCR_CTEIF4;
|
||||
}
|
||||
if(DMA1->ISR & DMA_ISR_TCIF4){
|
||||
DMA1->IFCR = DMA_IFCR_CTCIF4;
|
||||
freshdata[1] = 1;
|
||||
}
|
||||
spi_onoff(2, 0);
|
||||
// turn off DMA
|
||||
DMA1_Channel4->CCR &= ~DMA_CCR_EN;
|
||||
}
|
||||
|
||||
22
F1:F103/BISS_C_encoders/spi.h
Normal file
22
F1:F103/BISS_C_encoders/spi.h
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#define AMOUNT_OF_SPI (2)
|
||||
|
||||
#define ENCODER_BUFSZ (8)
|
||||
|
||||
typedef enum{
|
||||
SPI_NOTREADY,
|
||||
SPI_READY,
|
||||
SPI_BUSY
|
||||
} spiStatus;
|
||||
|
||||
extern spiStatus spi_status[AMOUNT_OF_SPI+1];
|
||||
|
||||
void spi_onoff(uint8_t idx, uint8_t on);
|
||||
void spi_deinit(uint8_t idx);
|
||||
void spi_setup(uint8_t idx);
|
||||
int spi_waitbsy(uint8_t idx);
|
||||
int spi_start_enc(int encodernum);
|
||||
int spi_read_enc(uint8_t encno, uint8_t buf[8]);
|
||||
@ -18,14 +18,15 @@
|
||||
|
||||
#include <stm32f1.h>
|
||||
#include <string.h>
|
||||
#include "usb_dev.h"
|
||||
|
||||
/**
|
||||
* @brief hexdump - dump hex array by 16 bytes in string
|
||||
* @param sendfun - function to send data
|
||||
* @param ifno - number of interface
|
||||
* @param arr - array to dump
|
||||
* @param len - length of `arr`
|
||||
*/
|
||||
void hexdump(int (*sendfun)(const char *s), uint8_t *arr, uint16_t len){
|
||||
void hexdump(int ifno, 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){
|
||||
@ -36,14 +37,14 @@ void hexdump(int (*sendfun)(const char *s), uint8_t *arr, uint16_t len){
|
||||
if(l % 16 == 15){
|
||||
*bptr++ = '\n';
|
||||
*bptr = 0;
|
||||
sendfun(buf);
|
||||
USB_sendstr(ifno, buf);
|
||||
bptr = buf;
|
||||
}else *bptr++ = ' ';
|
||||
}
|
||||
if(bptr != buf){
|
||||
*bptr++ = '\n';
|
||||
*bptr = 0;
|
||||
sendfun(buf);
|
||||
USB_sendstr(ifno, buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void hexdump(int (*sendfun)(const char *s), uint8_t *arr, uint16_t len);
|
||||
void hexdump(int ifno, uint8_t *arr, uint16_t len);
|
||||
char *u2str(uint32_t val);
|
||||
char *i2str(int32_t i);
|
||||
char *uhex2str(uint32_t val);
|
||||
|
||||
@ -15,6 +15,9 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "flash.h"
|
||||
#include "usb_descr.h"
|
||||
|
||||
#undef DBG
|
||||
@ -166,18 +169,28 @@ _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 BISS-C encoders controller");
|
||||
_USB_STRING_(ID1, u"encoder_cmd");
|
||||
_USB_STRING_(ID2, u"encoder_X");
|
||||
_USB_STRING_(ID3, u"encoder_Y");
|
||||
|
||||
// iInterface will change on initialisation by config
|
||||
#define _USB_IIDESCR_(str) {sizeof(str), 0x03, str}
|
||||
typedef struct{
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint16_t bString[MAX_IINTERFACE_SZ];
|
||||
}iidescr_t;
|
||||
static iidescr_t iids[bTotNumEndpoints] = {
|
||||
_USB_IIDESCR_(u"encoder_cmd"),
|
||||
_USB_IIDESCR_(u"encoder_X"),
|
||||
_USB_IIDESCR_(u"encoder_Y"),
|
||||
};
|
||||
|
||||
static const void* const StringDescriptor[iDESCR_AMOUNT] = {
|
||||
[iLANGUAGE_DESCR] = &LD,
|
||||
[iMANUFACTURER_DESCR] = &MD,
|
||||
[iPRODUCT_DESCR] = &PD,
|
||||
[iSERIAL_DESCR] = &SD,
|
||||
[iINTERFACE_DESCR1] = &ID1,
|
||||
[iINTERFACE_DESCR2] = &ID2,
|
||||
[iINTERFACE_DESCR3] = &ID3,
|
||||
[iINTERFACE_DESCR1] = &iids[0],
|
||||
[iINTERFACE_DESCR2] = &iids[1],
|
||||
[iINTERFACE_DESCR3] = &iids[2],
|
||||
};
|
||||
|
||||
static void wr0(const uint8_t *buf, uint16_t size, uint16_t askedsize){
|
||||
@ -246,3 +259,14 @@ void get_descriptor(config_pack_t *pack){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// change values of iInterface by content of global config
|
||||
void setup_interfaces(){
|
||||
for(int i = 0; i < bTotNumEndpoints; ++i){
|
||||
if(the_conf.iIlengths[i]){
|
||||
iids[i].bLength = the_conf.iIlengths[i];
|
||||
memcpy(iids[i].bString, the_conf.iInterface[i], the_conf.iIlengths[i]);
|
||||
}
|
||||
iids[i].bDescriptorType = 0x03;
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,3 +62,4 @@ enum{
|
||||
};
|
||||
|
||||
void get_descriptor(config_pack_t *pack);
|
||||
void setup_interfaces();
|
||||
|
||||
@ -401,6 +401,7 @@ void USB_setup(){
|
||||
USB->BCDR |= USB_BCDR_DPPU;
|
||||
NVIC_EnableIRQ(USB_IRQn);
|
||||
#endif
|
||||
setup_interfaces();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
#define BUILD_NUMBER "19"
|
||||
#define BUILD_DATE "2025-03-25"
|
||||
#define BUILD_NUMBER "63"
|
||||
#define BUILD_DATE "2025-03-26"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user