mirror of
https://github.com/DashyFox/StackSport.git
synced 2025-05-04 07:10:17 +00:00
prepare EEPROM and logic funcs
This commit is contained in:
parent
962333bfec
commit
400ee6dee0
@ -26,54 +26,103 @@
|
||||
|
||||
#define MEMORY_END (START_ADR_MACRO + (MACRO_BLOCKSIZE*MAX_NUMBER_MACRO)) // 0x78FA = 30970
|
||||
|
||||
typedef enum MemoryStatus {
|
||||
EEPROM_FAIL,
|
||||
EEPROM_MISSING_ELEMENT,
|
||||
EEPROM_OUT_OF_RANGE,
|
||||
EEPROM_WRONG_STARTADDR,
|
||||
EEPROM_OK
|
||||
} MemoryStatus;
|
||||
|
||||
typedef struct Shot {
|
||||
unsigned char isExist;
|
||||
unsigned char countRepeatShot;
|
||||
unsigned char speedRollerTop; // 0 .. 100 .. 200
|
||||
unsigned char speedRollerBottom; // 0 .. 100 .. 200
|
||||
unsigned char speedScrew; // 0 .. 100
|
||||
unsigned char rotationAxial; // 0 .. 90 .. 180
|
||||
unsigned char rotationHorizontal; // 0 .. 90 .. 180
|
||||
unsigned char rotationVertical; // 0 .. 90 .. 180
|
||||
}Shot;
|
||||
unsigned char isExist;
|
||||
unsigned char countRepeatShot; // 0 = inf
|
||||
unsigned char speedRollerTop; // 0 .. 100 .. 200
|
||||
unsigned char speedRollerBottom; // 0 .. 100 .. 200
|
||||
unsigned char speedScrew; // 0 .. 100
|
||||
unsigned char rotationAxial; // 0 .. 90 .. 180
|
||||
unsigned char rotationHorizontal; // 0 .. 90 .. 180
|
||||
unsigned char rotationVertical; // 0 .. 90 .. 180
|
||||
} Shot;
|
||||
|
||||
typedef struct ProgramHeader {
|
||||
unsigned char isExist;
|
||||
unsigned char countRepeat;
|
||||
unsigned char options;
|
||||
}ProgramHeader;
|
||||
unsigned char isExist;
|
||||
unsigned char countRepeat;
|
||||
unsigned char options;
|
||||
} ProgramHeader;
|
||||
|
||||
typedef struct ProgramShot {
|
||||
unsigned char id;
|
||||
unsigned char speedScrew;
|
||||
}ProgramShot;
|
||||
unsigned char id;
|
||||
unsigned char speedScrew;
|
||||
} ProgramShot;
|
||||
|
||||
typedef struct Program {
|
||||
ProgramHeader header;
|
||||
ProgramShot shots[MAX_NUMBER_SHOTS_IN_PROGRAMS];
|
||||
}Program;
|
||||
ProgramHeader header;
|
||||
ProgramShot shots[MAX_NUMBER_SHOTS_IN_PROGRAMS];
|
||||
} Program;
|
||||
|
||||
typedef struct MacroHeader {
|
||||
unsigned char isExist;
|
||||
}MacroHeader;
|
||||
unsigned char isExist;
|
||||
} MacroHeader;
|
||||
|
||||
typedef struct MacroProgram {
|
||||
unsigned char id;
|
||||
unsigned char speedScrew;
|
||||
unsigned char countRepeat;
|
||||
unsigned char options;
|
||||
}MacroProgram;
|
||||
} MacroProgram;
|
||||
|
||||
typedef struct Macro {
|
||||
MacroHeader header;
|
||||
MacroProgram programs[MAX_NUMBER_PROGRAMS_IN_MACRO];
|
||||
} Macro;
|
||||
MacroHeader header;
|
||||
MacroProgram programs[MAX_NUMBER_PROGRAMS_IN_MACRO];
|
||||
} Macro;
|
||||
|
||||
void FLASH_WriteBlock(uint16_t startAddr, uint8_t number,uint8_t *writeData);
|
||||
void FLASH_ReadBlock(uint16_t startAddr, uint8_t number, uint8_t *outData);
|
||||
typedef struct ServoSetting {
|
||||
uint8_t invert;
|
||||
uint16_t min;
|
||||
uint16_t def;
|
||||
uint16_t max;
|
||||
} ServoSetting;
|
||||
|
||||
typedef struct DelayTimes{
|
||||
uint16_t preRun;
|
||||
}DelayTimes;
|
||||
|
||||
typedef struct Motors{
|
||||
uint16_t speed_Rollers_min;
|
||||
// uint16_t speed_Rollers_max;
|
||||
|
||||
uint16_t speed_Screw_min;
|
||||
// uint16_t speed_Screw_max;
|
||||
}Motors;
|
||||
|
||||
typedef struct HardwareInit_t{
|
||||
DelayTimes timings;
|
||||
ServoSetting servos[3];
|
||||
Motors motors;
|
||||
}HardwareInit_t;
|
||||
|
||||
typedef struct Statistics{
|
||||
uint16_t totalShots;
|
||||
uint16_t totalPrograms;
|
||||
uint16_t totalMacros;
|
||||
}Statistics;
|
||||
|
||||
|
||||
void SaveShot(unsigned char number, struct Shot* shot);
|
||||
Shot GetShot( unsigned char number );
|
||||
MemoryStatus FLASH_WriteBlock(uint16_t startAddr, uint8_t number,
|
||||
uint8_t *writeData);
|
||||
MemoryStatus FLASH_ReadBlock(uint16_t startAddr, uint8_t number,
|
||||
uint8_t *outData);
|
||||
|
||||
MemoryStatus saveShot(unsigned char number, Shot *shot);
|
||||
MemoryStatus getShot(unsigned char number, Shot *shot);
|
||||
|
||||
MemoryStatus saveProg(unsigned char number, Program *prog);
|
||||
MemoryStatus getProg(unsigned char number, Program *prog);
|
||||
|
||||
MemoryStatus saveMacro(unsigned char number, Macro *macro);
|
||||
MemoryStatus getMacro(unsigned char number, Macro *macro);
|
||||
|
||||
MemoryStatus getHardwareInit();
|
||||
|
||||
#endif /* INC_EEPROM_H_ */
|
||||
|
@ -11,6 +11,13 @@
|
||||
#include "pca9685.h"
|
||||
#include "EEPROM.h"
|
||||
|
||||
typedef enum ServoMap{
|
||||
SERVO_AXIAL = 0,
|
||||
SERVO_HORIZONTAL = 1,
|
||||
SERVO_VERTICAL = 2
|
||||
}ServoMap;
|
||||
|
||||
|
||||
|
||||
void doShot(Shot*);
|
||||
void doShotForever(uint8_t number);
|
||||
@ -20,7 +27,9 @@ uint8_t prepareShot(uint8_t number);
|
||||
void startShooting();
|
||||
void stopShooting();
|
||||
|
||||
void setPos();
|
||||
|
||||
|
||||
void setPos(uint8_t axial, uint8_t horizontal, uint8_t vertical);
|
||||
void setPosDefault();
|
||||
|
||||
// 0 .. 100
|
||||
|
@ -4,45 +4,95 @@
|
||||
#include "Print.h"
|
||||
|
||||
|
||||
void SaveShot(unsigned char number, struct Shot* shot)
|
||||
MemoryStatus saveShot(unsigned char number, Shot* shot)
|
||||
{
|
||||
print("SaveShot ");
|
||||
printNumber(number);
|
||||
unsigned char Buf[SHOT_BLOCKSIZE];
|
||||
memset(Buf, 0x00, sizeof(Buf));
|
||||
|
||||
Buf[0] = number;
|
||||
Buf[1] = shot->countRepeatShot;
|
||||
Buf[2] = shot->speedRollerTop;
|
||||
Buf[3] = shot->speedRollerBottom;
|
||||
Buf[4] = shot->speedScrew;
|
||||
Buf[5] = shot->rotationAxial;
|
||||
Buf[6] = shot->rotationHorizontal;
|
||||
Buf[7] = shot->rotationVertical;
|
||||
|
||||
FLASH_WriteBlock(START_ADR_SHOT, number, Buf);
|
||||
if(FLASH_WriteBlock(START_ADR_SHOT, number, (uint8_t*)shot) == EEPROM_OK){
|
||||
return EEPROM_OK;
|
||||
}
|
||||
return EEPROM_FAIL;
|
||||
}
|
||||
|
||||
struct Shot GetShot( unsigned char number)
|
||||
MemoryStatus getShot(unsigned char number, Shot *shot)
|
||||
{
|
||||
struct Shot shot;
|
||||
|
||||
unsigned char Buf[SHOT_BLOCKSIZE];
|
||||
FLASH_ReadBlock(START_ADR_SHOT, number, Buf);
|
||||
shot.isExist = Buf[0];
|
||||
shot.countRepeatShot = Buf[1];
|
||||
shot.speedRollerTop = Buf[2];
|
||||
shot.speedRollerBottom = Buf[3];
|
||||
shot.speedScrew = Buf[4];
|
||||
shot.rotationAxial = Buf[5];
|
||||
shot.rotationHorizontal = Buf[6];
|
||||
shot.rotationVertical = Buf[7];
|
||||
|
||||
return shot;
|
||||
if(FLASH_ReadBlock(START_ADR_SHOT, number, (uint8_t*)&shot) != EEPROM_OK){
|
||||
return EEPROM_FAIL;
|
||||
}
|
||||
if(!shot->isExist){
|
||||
return EEPROM_MISSING_ELEMENT;
|
||||
}
|
||||
return EEPROM_OK;
|
||||
}
|
||||
|
||||
void FLASH_WriteBlock(uint16_t startAddr, uint8_t number, uint8_t *writeData)
|
||||
MemoryStatus saveProg(unsigned char number, Program* prog)
|
||||
{
|
||||
MemoryStatus result = EEPROM_OK;
|
||||
for(uint16_t i = 0; i < MAX_NUMBER_SHOTS_IN_PROGRAMS; ++i){
|
||||
Shot shot;
|
||||
MemoryStatus stat = getShot(prog->shots[i].id, &shot);
|
||||
if(!(stat == EEPROM_OK || stat == EEPROM_MISSING_ELEMENT)){
|
||||
return EEPROM_FAIL;
|
||||
}
|
||||
if(!shot.isExist){
|
||||
//todo: add to shotRequest order
|
||||
result = EEPROM_MISSING_ELEMENT;
|
||||
}
|
||||
}
|
||||
if(FLASH_WriteBlock(START_ADR_PROGRAM, number, (uint8_t*)prog) != EEPROM_OK){
|
||||
return EEPROM_FAIL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MemoryStatus getProg( unsigned char number, Program* prog)
|
||||
{
|
||||
if(FLASH_ReadBlock(START_ADR_PROGRAM, number, (uint8_t*)&prog) != EEPROM_OK){
|
||||
return EEPROM_FAIL;
|
||||
}
|
||||
|
||||
if(!prog->header.isExist){
|
||||
return EEPROM_MISSING_ELEMENT;
|
||||
}
|
||||
|
||||
return EEPROM_OK;
|
||||
}
|
||||
|
||||
MemoryStatus saveMacro(unsigned char number, Macro* macro)
|
||||
{
|
||||
MemoryStatus result = EEPROM_OK;
|
||||
for (uint16_t i = 0; i < MAX_NUMBER_PROGRAMS_IN_MACRO; ++i) {
|
||||
Program prog;
|
||||
MemoryStatus stat = getProg(macro->programs[i].id, &prog);
|
||||
if(!(stat == EEPROM_OK || stat == EEPROM_MISSING_ELEMENT)){
|
||||
return EEPROM_FAIL;
|
||||
}
|
||||
if(!prog.header.isExist){
|
||||
result = EEPROM_MISSING_ELEMENT;
|
||||
//todo: add to programRequest order
|
||||
}
|
||||
}
|
||||
|
||||
if(FLASH_WriteBlock(START_ADR_PROGRAM, number, (uint8_t*)macro) != EEPROM_OK){
|
||||
return EEPROM_FAIL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
MemoryStatus getMacro( unsigned char number, Macro* macro)
|
||||
{
|
||||
if(FLASH_ReadBlock(START_ADR_PROGRAM, number, (uint8_t*)¯o) != EEPROM_OK){
|
||||
return EEPROM_FAIL;
|
||||
}
|
||||
|
||||
if(!macro->header.isExist){
|
||||
return EEPROM_MISSING_ELEMENT;
|
||||
}
|
||||
return EEPROM_OK;
|
||||
}
|
||||
|
||||
MemoryStatus FLASH_WriteBlock(uint16_t startAddr, uint8_t number, uint8_t *writeData)
|
||||
{
|
||||
HAL_StatusTypeDef result;
|
||||
uint8_t dataSize;
|
||||
// protect and select
|
||||
switch (startAddr) {
|
||||
@ -53,20 +103,20 @@ void FLASH_WriteBlock(uint16_t startAddr, uint8_t number, uint8_t *writeData)
|
||||
case START_ADR_SHOT:
|
||||
dataSize = SHOT_BLOCKSIZE;
|
||||
if(number > MAX_NUMBER_SHOTS)
|
||||
return;
|
||||
return EEPROM_OUT_OF_RANGE;
|
||||
break;
|
||||
case START_ADR_PROGRAM:
|
||||
dataSize = PROGRAM_BLOCKSIZE;
|
||||
if(number > MAX_NUMBER_PROGRAMS)
|
||||
return;
|
||||
return EEPROM_OUT_OF_RANGE;
|
||||
break;
|
||||
case START_ADR_MACRO:
|
||||
dataSize = MACRO_BLOCKSIZE;
|
||||
if(number > MAX_NUMBER_MACRO)
|
||||
return;
|
||||
return EEPROM_OUT_OF_RANGE;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
return EEPROM_WRONG_STARTADDR;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -80,11 +130,13 @@ void FLASH_WriteBlock(uint16_t startAddr, uint8_t number, uint8_t *writeData)
|
||||
|
||||
for( unsigned char i = 0; i < (dataSize); i++ ) Buf[i+2] = writeData[i];
|
||||
|
||||
HAL_I2C_Master_Transmit(&hi2c1, (AT24C_ADRESS << 1), Buf, (dataSize + 2), 10);
|
||||
result = HAL_I2C_Master_Transmit(&hi2c1, (AT24C_ADRESS << 1), Buf, (dataSize + 2), 10);
|
||||
HAL_Delay(1);
|
||||
return result;
|
||||
}
|
||||
|
||||
void FLASH_ReadBlock(uint16_t startAddr, uint8_t number, uint8_t *readData){
|
||||
MemoryStatus FLASH_ReadBlock(uint16_t startAddr, uint8_t number, uint8_t *readData){
|
||||
HAL_StatusTypeDef result;
|
||||
uint8_t dataSize;
|
||||
// protect and select
|
||||
switch (startAddr) {
|
||||
@ -95,20 +147,20 @@ void FLASH_ReadBlock(uint16_t startAddr, uint8_t number, uint8_t *readData){
|
||||
case START_ADR_SHOT:
|
||||
dataSize = SHOT_BLOCKSIZE;
|
||||
if(number > MAX_NUMBER_SHOTS)
|
||||
return;
|
||||
return EEPROM_OUT_OF_RANGE;
|
||||
break;
|
||||
case START_ADR_PROGRAM:
|
||||
dataSize = PROGRAM_BLOCKSIZE;
|
||||
if(number > MAX_NUMBER_PROGRAMS)
|
||||
return;
|
||||
return EEPROM_OUT_OF_RANGE;
|
||||
break;
|
||||
case START_ADR_MACRO:
|
||||
dataSize = MACRO_BLOCKSIZE;
|
||||
if(number > MAX_NUMBER_MACRO)
|
||||
return;
|
||||
return EEPROM_OUT_OF_RANGE;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
return EEPROM_WRONG_STARTADDR;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -117,8 +169,9 @@ void FLASH_ReadBlock(uint16_t startAddr, uint8_t number, uint8_t *readData){
|
||||
uint16_t blockAddr16 = (uint16_t)(startAddr + (uint16_t)(number*dataSize));
|
||||
uint8_t blockAddr[2] = {HIBYTE(blockAddr16), LOBYTE(blockAddr16)};
|
||||
|
||||
HAL_I2C_Master_Transmit(&hi2c1, (AT24C_ADRESS << 1), blockAddr, 2, 10);
|
||||
result = HAL_I2C_Master_Transmit(&hi2c1, (AT24C_ADRESS << 1), blockAddr, 2, 10);
|
||||
HAL_Delay(1);
|
||||
HAL_I2C_Master_Receive(&hi2c1, (AT24C_ADRESS << 1), readData, dataSize, 10);
|
||||
result = HAL_I2C_Master_Receive(&hi2c1, (AT24C_ADRESS << 1), readData, dataSize, 10);
|
||||
HAL_Delay(1);
|
||||
return result;
|
||||
}
|
||||
|
@ -9,23 +9,19 @@
|
||||
#include "EEPROM.h"
|
||||
#include "Print.h"
|
||||
|
||||
|
||||
typedef enum Mode {
|
||||
NoneMode,
|
||||
ShotMode,
|
||||
ProgramMode,
|
||||
MacroMode,
|
||||
NoneMode, ShotMode, ProgramMode, MacroMode,
|
||||
} Mode;
|
||||
|
||||
uint8_t isPause = 0;
|
||||
uint8_t isShooting = 0;
|
||||
|
||||
typedef struct CurrentProgram{
|
||||
typedef struct CurrentProgram {
|
||||
Program program;
|
||||
uint8_t shot_index;
|
||||
} CurrentProgram;
|
||||
|
||||
typedef struct CurrentMacro{
|
||||
typedef struct CurrentMacro {
|
||||
Macro macro;
|
||||
uint8_t program_index;
|
||||
} CurrentMacro;
|
||||
@ -39,10 +35,24 @@ typedef struct Current {
|
||||
|
||||
Current current;
|
||||
|
||||
HardwareInit_t hwSettings = {
|
||||
/*DelayTimes*/ {
|
||||
/*preRun*/ 0
|
||||
},
|
||||
/*ServoSetting*/{
|
||||
{0, 0, 90, 180},
|
||||
{0, 0, 90, 180},
|
||||
{0, 0, 90, 180}
|
||||
},
|
||||
/*Motors*/{
|
||||
0, 0
|
||||
}
|
||||
};
|
||||
|
||||
extern int16_t Vz1;
|
||||
extern int16_t Vz2;
|
||||
void doShot(Shot* shot){
|
||||
|
||||
void doShot(Shot *shot) {
|
||||
SetServo(0, shot->rotationHorizontal);
|
||||
SetServo(1, shot->rotationVertical);
|
||||
SetServo(2, shot->rotationAxial);
|
||||
@ -51,7 +61,7 @@ void doShot(Shot* shot){
|
||||
setScrewkSpeed(shot->speedScrew);
|
||||
}
|
||||
|
||||
void startShooting(){
|
||||
void startShooting() {
|
||||
switch (current.mode) {
|
||||
case ShotMode:
|
||||
print("StartShooting\n");
|
||||
@ -86,11 +96,11 @@ void startShooting(){
|
||||
}
|
||||
}
|
||||
|
||||
void stopShooting(){
|
||||
void stopShooting() {
|
||||
isShooting = 0;
|
||||
isPause = 0;
|
||||
setScrewkSpeed(0);
|
||||
setRollersSpeed(100,100);
|
||||
setRollersSpeed(100, 100);
|
||||
setPosDefault();
|
||||
Vz1 = 100;
|
||||
Vz2 = 100;
|
||||
@ -98,13 +108,14 @@ void stopShooting(){
|
||||
TIM1->CCR2 = 0;
|
||||
}
|
||||
|
||||
void doShotForever(uint8_t number){
|
||||
void doShotForever(uint8_t number) {
|
||||
|
||||
}
|
||||
|
||||
uint8_t prepareShot(uint8_t number){
|
||||
Shot shot = GetShot(number);
|
||||
if(shot.isExist){
|
||||
uint8_t prepareShot(uint8_t number) {
|
||||
Shot shot;
|
||||
getShot(number, &shot);
|
||||
if (shot.isExist) {
|
||||
current.mode = ShotMode;
|
||||
current.shot = shot;
|
||||
return 1;
|
||||
@ -114,32 +125,41 @@ uint8_t prepareShot(uint8_t number){
|
||||
}
|
||||
}
|
||||
|
||||
void setPos(uint8_t axial, uint8_t horizontal, uint8_t vertical) {
|
||||
|
||||
void setPos(){
|
||||
//todo:
|
||||
// hwSettings.servos[SERVO_AXIAL].invert
|
||||
|
||||
SetServo(SERVO_AXIAL, axial); // Axial
|
||||
SetServo(SERVO_HORIZONTAL, horizontal); // Horizontal
|
||||
SetServo(SERVO_VERTICAL, vertical); // Vertical
|
||||
}
|
||||
void setPosDefault(){
|
||||
SetServo(0, 90);
|
||||
SetServo(1, 90);
|
||||
SetServo(2, 90);
|
||||
void setPosDefault() {
|
||||
SetServo(0, 90); // Axial
|
||||
SetServo(1, 90); // Horizontal
|
||||
SetServo(2, 90); // Vertical
|
||||
}
|
||||
|
||||
// 0 .. 100
|
||||
void setScrewkSpeed(uint8_t speed){
|
||||
void setScrewkSpeed(uint8_t speed) {
|
||||
|
||||
if(speed && speed < hwSettings.motors.speed_Screw_min)
|
||||
speed = hwSettings.motors.speed_Screw_min;
|
||||
|
||||
TIM1->CCR1 = 0;
|
||||
TIM1->CCR2 = (uint16_t)(40 * speed);
|
||||
}
|
||||
|
||||
//(-v) 0 .. 100(stop) .. 200(+v)
|
||||
void setRollersSpeed(uint8_t up, uint8_t down){
|
||||
void setRollersSpeed(uint8_t up, uint8_t down) {
|
||||
Vz1 = up;
|
||||
Vz2 = down;
|
||||
}
|
||||
// shot sequence
|
||||
void startProgram(){
|
||||
void startProgram() {
|
||||
|
||||
}
|
||||
// shot sequence
|
||||
void startMacro(){
|
||||
void startMacro() {
|
||||
|
||||
}
|
||||
|
@ -201,7 +201,8 @@ initcomlete = 1;
|
||||
/* Infinite loop */
|
||||
/* USER CODE BEGIN WHILE */
|
||||
|
||||
Shot testShot = GetShot(3);
|
||||
Shot testShot;
|
||||
getShot(3, &testShot);
|
||||
if(!testShot.isExist){
|
||||
testShot.countRepeatShot = 1;
|
||||
testShot.speedRollerTop = 200;
|
||||
@ -211,7 +212,7 @@ initcomlete = 1;
|
||||
testShot.rotationHorizontal = 90;
|
||||
testShot.rotationVertical = 90;
|
||||
|
||||
SaveShot(3, &testShot);
|
||||
saveShot(3, &testShot);
|
||||
}
|
||||
|
||||
while (1)
|
||||
|
@ -672,8 +672,10 @@ void I2C1_ER_IRQHandler(void)
|
||||
void USART3_IRQHandler(void)
|
||||
{
|
||||
/* USER CODE BEGIN USART3_IRQn 0 */
|
||||
// CDC_Transmit_FS(uart_rx_buffer, UART_BUFFER_SIZE);
|
||||
HAL_UART_Receive_IT(&huart3, uart_rx_buffer, UART_BUFFER_SIZE);
|
||||
// CDC_Transmit_FS(uart_rx_buffer, UART_BUFFER_SIZE);
|
||||
|
||||
|
||||
|
||||
/* USER CODE END USART3_IRQn 0 */
|
||||
HAL_UART_IRQHandler(&huart3);
|
||||
|
8
TODO.md
8
TODO.md
@ -17,10 +17,11 @@ IR:
|
||||
doShot
|
||||
|
||||
Логика работы:
|
||||
Ограничение минимальных скоростей!
|
||||
Правильное переключение выстрелов с учётом repeatCount
|
||||
Переключение выстрелов в программе
|
||||
Переключение программ в макро
|
||||
Правильная пауза
|
||||
Правильная функция паузы
|
||||
|
||||
Индикация:
|
||||
Текущий буфер индикации
|
||||
@ -30,8 +31,9 @@ IR:
|
||||
|
||||
Звук:
|
||||
Звук приёма IR
|
||||
Звук ошибки
|
||||
Звук включения
|
||||
Звук ошибки
|
||||
Звук включения
|
||||
|
||||
|
||||
|
||||
Ошибки:
|
||||
|
Loading…
x
Reference in New Issue
Block a user