inital commit
This commit is contained in:
54
CMakeLists.txt
Normal file
54
CMakeLists.txt
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# Project name
|
||||||
|
project(asteleMotor)
|
||||||
|
|
||||||
|
# CMake version
|
||||||
|
cmake_minimum_required(VERSION 2.6)
|
||||||
|
|
||||||
|
# Options
|
||||||
|
set(MCU "attiny2313" CACHE STRING "Processor Type")
|
||||||
|
set(CPU_SPEED "4000000" CACHE STRING "Speed of the CPU")
|
||||||
|
set(PORT "/dev/ttyUSB0" CACHE STRING "USB Port")
|
||||||
|
set(PORT_SPEED "57600" CACHE STRING "Serial Port Speed")
|
||||||
|
set(PROGRAMMER "usbasp" CACHE STRING "Programmer Type")
|
||||||
|
set(COMPILE_FLAGS "" CACHE STRING "Additional Compiler Flags")
|
||||||
|
|
||||||
|
# Set own source files
|
||||||
|
set(SRC_FILES main.cpp writepin.cpp comperator.cpp stepper.cpp)
|
||||||
|
|
||||||
|
# Compiler suite specification
|
||||||
|
set(CMAKE_C_COMPILER /usr/bin/avr-gcc)
|
||||||
|
set(CMAKE_CXX_COMPILER /usr/bin/avr-g++)
|
||||||
|
set(CMAKE_OBJCOPY /usr/bin/avr-objcopy)
|
||||||
|
set(CMAKE_OBJDUMP /usr/bin/avr-objdump)
|
||||||
|
set(CMAKE_RANLIB /usr/bin/avr-ranlib)
|
||||||
|
set(CMAKE_LINKER /usr/bin/avr-ld)
|
||||||
|
|
||||||
|
# Compiler flags
|
||||||
|
add_definitions(-mmcu=${MCU} -DF_CPU=${CPU_SPEED})
|
||||||
|
add_definitions(-c -g -Os -flto -Wall -std=c++17 )
|
||||||
|
add_definitions(-fno-exceptions -ffunction-sections -fno-rtti -fdata-sections)
|
||||||
|
|
||||||
|
# Linker flags
|
||||||
|
set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") # remove -rdynamic for C
|
||||||
|
set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") # remove -rdynamic for CXX
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "-s -Os -Wl,--gc-sections -mmcu=${MCU}")
|
||||||
|
|
||||||
|
add_executable(${PROJECT_NAME} ${ARDUINO_CORE_SRC} ${SRC_FILES})
|
||||||
|
|
||||||
|
find_program(AR_AVRDUDE NAMES avrdude PATHS /usr/bin NO_DEFAULT_PATH)
|
||||||
|
find_program(AR_AVRSIZE NAMES avr-size PATHS /usr/bin NO_DEFAULT_PATH)
|
||||||
|
|
||||||
|
add_custom_target(download
|
||||||
|
COMMAND ${CMAKE_OBJCOPY} -j .text -j .data -O ihex ${PROJECT_NAME} ${PROJECT_NAME}.hex
|
||||||
|
COMMAND ${AR_AVRSIZE} -C ${PROJECT_NAME}
|
||||||
|
COMMAND ${AR_AVRDUDE} -v -p ${MCU} -c ${PROGRAMMER} -D -U flash:w:${PROJECT_NAME}.hex
|
||||||
|
DEPENDS ${PROJECT_NAME}
|
||||||
|
)
|
||||||
|
|
||||||
|
message(STATUS "${CPU_SPEED}")
|
||||||
|
|
||||||
|
add_custom_target(export
|
||||||
|
COMMAND ${CMAKE_OBJCOPY} -j .text -j .data -O ihex ${PROJECT_NAME} ${PROJECT_NAME}.hex
|
||||||
|
COMMAND ${AR_AVRSIZE} -C ${PROJECT_NAME}
|
||||||
|
DEPENDS ${PROJECT_NAME}
|
||||||
|
)
|
44
buttons.h
Normal file
44
buttons.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include "writepin.h"
|
||||||
|
|
||||||
|
class Buttons
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
static constexpr uint8_t PRESSED = 0;
|
||||||
|
static constexpr uint8_t LONG_PRESSED = 1;
|
||||||
|
static constexpr uint8_t RELEASED = 2;
|
||||||
|
|
||||||
|
private:
|
||||||
|
volatile uint8_t * const pinReg = &PIND;
|
||||||
|
static constexpr uint8_t button[] = { PD5, PD6 };
|
||||||
|
static constexpr uint8_t buttonsAmount = sizeof(button);
|
||||||
|
uint8_t buttonCount[buttonsAmount] = {};
|
||||||
|
|
||||||
|
void* _userData;
|
||||||
|
void (*_eventHandler)(uint8_t index, uint8_t type, void* data);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Buttons(void (*eventHandler)(uint8_t index, uint8_t type, void* data), void* userData = nullptr): _userData(userData), _eventHandler(eventHandler){}
|
||||||
|
void tick();
|
||||||
|
};
|
||||||
|
|
||||||
|
void Buttons::tick()
|
||||||
|
{
|
||||||
|
for(uint8_t i = 0; i < buttonsAmount; ++i)
|
||||||
|
{
|
||||||
|
if(readPin(pinReg, button[i]) == true)
|
||||||
|
{
|
||||||
|
if(buttonCount[i] > 2) _eventHandler(i, RELEASED, _userData);
|
||||||
|
buttonCount[i] = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(buttonCount[i] == 3) _eventHandler(i, PRESSED, _userData);
|
||||||
|
else if(buttonCount[i] == 100) _eventHandler(i, LONG_PRESSED, _userData);
|
||||||
|
++buttonCount[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
comperator.cpp
Normal file
22
comperator.cpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#include "comperator.h"
|
||||||
|
|
||||||
|
Comperator::Comperator()
|
||||||
|
{
|
||||||
|
if constexpr(INTERNAL_REFERANCE) ACSR = 0b01000001;
|
||||||
|
else ACSR = 0b00000011;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Comperator::on()
|
||||||
|
{
|
||||||
|
ACSR &= 0b01111111;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Comperator::off()
|
||||||
|
{
|
||||||
|
ACSR |= 0b10000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Comperator::compare()
|
||||||
|
{
|
||||||
|
return ACSR & 0b00100000;
|
||||||
|
}
|
13
comperator.h
Normal file
13
comperator.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <avr/io.h>
|
||||||
|
|
||||||
|
class Comperator
|
||||||
|
{
|
||||||
|
static constexpr bool INTERNAL_REFERANCE = true;
|
||||||
|
public:
|
||||||
|
Comperator();
|
||||||
|
|
||||||
|
static void on();
|
||||||
|
static void off();
|
||||||
|
static bool compare();
|
||||||
|
};
|
51
eeprom.h
Normal file
51
eeprom.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
void EEPROM_write_char(uint16_t address, unsigned char data)
|
||||||
|
{
|
||||||
|
//Wait for completion of previous write
|
||||||
|
while(EECR & (1<<EEPE));
|
||||||
|
//Set up address and Data Registers
|
||||||
|
EEAR = address;
|
||||||
|
EEDR = data;
|
||||||
|
//Write logical one to EEMPE
|
||||||
|
EECR |= (1<<EEMPE);
|
||||||
|
//Start eeprom write by setting EEPE
|
||||||
|
EECR |= (1<<EEPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char EEPROM_read_char(uint16_t uiAddress)
|
||||||
|
{
|
||||||
|
// Wait for completion of previous write
|
||||||
|
while(EECR & (1<<EEPE));
|
||||||
|
//Set up address register
|
||||||
|
EEAR = uiAddress;
|
||||||
|
//Start eeprom read by writing EERE
|
||||||
|
EECR |= (1<<EERE);
|
||||||
|
//Return data from Data Register
|
||||||
|
return EEDR;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EEPROM_write_string(uint16_t address, char* buffer, uint16_t length)
|
||||||
|
{
|
||||||
|
for(uint16_t i = 0; i < length; i++) EEPROM_write_char( address+i, buffer[i] );
|
||||||
|
}
|
||||||
|
|
||||||
|
void EEPROM_read_string(uint16_t address, char* buffer, uint16_t length)
|
||||||
|
{
|
||||||
|
for(uint16_t i = 0; i < length; i++) buffer[i] = EEPROM_read_char( address+i);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T> void EEPROM_write_class(uint16_t address, T& in)
|
||||||
|
{
|
||||||
|
EEPROM_write_string( address, reinterpret_cast<char*>(&in), sizeof(in));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T> T EEPROM_read_class(uint16_t address)
|
||||||
|
{
|
||||||
|
char data[sizeof(T)];
|
||||||
|
EEPROM_read_string( address, data, sizeof(T) );
|
||||||
|
return *reinterpret_cast<T*>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T> void EEPROM_read_class(uint16_t address, T* in)
|
||||||
|
{
|
||||||
|
EEPROM_read_string( address, reinterpret_cast<char*>(in), sizeof(T) );
|
||||||
|
}
|
137
main.cpp
Normal file
137
main.cpp
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <util/delay.h>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
#include <avr/sleep.h>
|
||||||
|
#include "stepper.h"
|
||||||
|
#include "writepin.h"
|
||||||
|
#include "comperator.h"
|
||||||
|
#include "buttons.h"
|
||||||
|
#include "eeprom.h"
|
||||||
|
|
||||||
|
#define LED_1_PIN PB3
|
||||||
|
#define LED_2_PIN PB4
|
||||||
|
|
||||||
|
#define BUTTON_1_PIN PD5
|
||||||
|
#define BUTTON_2_PIN PD6
|
||||||
|
|
||||||
|
bool setup = false;
|
||||||
|
static bool buttonLongpressed[2];
|
||||||
|
uint8_t lockedSpeed = 180;
|
||||||
|
|
||||||
|
void debugBlink(bool fast = true)
|
||||||
|
{
|
||||||
|
_delay_ms(100);
|
||||||
|
if(!fast) _delay_ms(200);
|
||||||
|
PORTB |= 1 << LED_1_PIN;
|
||||||
|
_delay_ms(100);
|
||||||
|
if(!fast) _delay_ms(200);
|
||||||
|
PORTB &= ~(1 << LED_1_PIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
void buttonHandler(uint8_t index, uint8_t type, void* data)
|
||||||
|
{
|
||||||
|
Stepper* stepper = reinterpret_cast<Stepper*>(data);
|
||||||
|
if(setup == false)
|
||||||
|
{
|
||||||
|
if(type == Buttons::PRESSED)
|
||||||
|
{
|
||||||
|
if(index == 0)
|
||||||
|
{
|
||||||
|
stepper->setSpeed(230);
|
||||||
|
stepper->setEndlesMove(true, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stepper->setSpeed(230);
|
||||||
|
stepper->setEndlesMove(true, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(type == Buttons::RELEASED)
|
||||||
|
{
|
||||||
|
stepper->setSpeed(lockedSpeed);
|
||||||
|
stepper->setEndlesMove(true, true);
|
||||||
|
buttonLongpressed[0] = false;
|
||||||
|
buttonLongpressed[1] = false;
|
||||||
|
}
|
||||||
|
else if(type == Buttons::LONG_PRESSED)
|
||||||
|
{
|
||||||
|
buttonLongpressed[index] = true;
|
||||||
|
if(buttonLongpressed[0] && buttonLongpressed[1])
|
||||||
|
{
|
||||||
|
writePin(&PORTB, LED_2_PIN, true);
|
||||||
|
writePin(&PORTB, LED_1_PIN, false);
|
||||||
|
setup = true;
|
||||||
|
buttonLongpressed[0] = false;
|
||||||
|
buttonLongpressed[1] = false;
|
||||||
|
stepper->setSpeed(lockedSpeed);
|
||||||
|
stepper->setEndlesMove(true, true);
|
||||||
|
debugBlink();
|
||||||
|
debugBlink();
|
||||||
|
debugBlink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(type == Buttons::PRESSED) writePin(&PORTB, LED_1_PIN, true);
|
||||||
|
if(type == Buttons::RELEASED)
|
||||||
|
{
|
||||||
|
writePin(&PORTB, LED_1_PIN, false);
|
||||||
|
if(index == 0)
|
||||||
|
{
|
||||||
|
lockedSpeed+=5;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lockedSpeed-=5;
|
||||||
|
}
|
||||||
|
EEPROM_write_char(0, lockedSpeed);
|
||||||
|
stepper->setSpeed(lockedSpeed);
|
||||||
|
}
|
||||||
|
else if(type == Buttons::LONG_PRESSED)
|
||||||
|
{
|
||||||
|
buttonLongpressed[index] = true;
|
||||||
|
if(buttonLongpressed[0] && buttonLongpressed[1])
|
||||||
|
{
|
||||||
|
writePin(&PORTB, LED_2_PIN, false);
|
||||||
|
writePin(&PORTB, LED_1_PIN, true);
|
||||||
|
setup = false;
|
||||||
|
buttonLongpressed[0] = false;
|
||||||
|
buttonLongpressed[1] = false;
|
||||||
|
stepper->setSpeed(lockedSpeed);
|
||||||
|
stepper->setEndlesMove(true, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
DDRB = 1 << LED_1_PIN | 1 << LED_2_PIN;
|
||||||
|
DDRD = 1 << PD0 | 1 << PD1 | 1 << PD2 | 1 << PD3;
|
||||||
|
PORTD = 1 << BUTTON_1_PIN | 1 << BUTTON_2_PIN;
|
||||||
|
|
||||||
|
if(EEPROM_read_char(0) != 0) lockedSpeed = EEPROM_read_char(0);
|
||||||
|
|
||||||
|
Stepper stepper(&PORTD, PD0, PD1, PD2, PD3);
|
||||||
|
|
||||||
|
debugBlink(false);
|
||||||
|
debugBlink(false);
|
||||||
|
|
||||||
|
writePin(&PORTB, LED_1_PIN, true);
|
||||||
|
stepper.setSpeed(180);
|
||||||
|
stepper.setEndlesMove(true);
|
||||||
|
|
||||||
|
Buttons buttons(&buttonHandler, reinterpret_cast<void*>(&stepper));
|
||||||
|
|
||||||
|
uint8_t timer = 0;
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
if(++timer == 0)buttons.tick();
|
||||||
|
stepper.tick();
|
||||||
|
_delay_us(50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
92
stepper.cpp
Normal file
92
stepper.cpp
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
|
||||||
|
#include "stepper.h"
|
||||||
|
#include "writepin.h"
|
||||||
|
|
||||||
|
void Stepper::tick()
|
||||||
|
{
|
||||||
|
if(++_tickCounter > UINT8_MAX - (_speed>>8) )
|
||||||
|
{
|
||||||
|
if(_targetStep != _currentStep)
|
||||||
|
{
|
||||||
|
step(_targetStep > _currentStep);
|
||||||
|
}
|
||||||
|
else if(endlessMove) step(endlessDriection);
|
||||||
|
_tickCounter = 0;
|
||||||
|
}
|
||||||
|
if(_speed>>8 < _targetSpeed) _speed = _speed + acceleration;
|
||||||
|
else if(_speed>>8 > _targetSpeed)_speed = _speed - acceleration;;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stepper::moveTo(const int32_t step)
|
||||||
|
{
|
||||||
|
_targetStep = step;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stepper::moveRelative(const int16_t dist)
|
||||||
|
{
|
||||||
|
moveTo(_targetStep + dist);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stepper::setSpeed(const uint8_t speed)
|
||||||
|
{
|
||||||
|
_targetSpeed = speed;
|
||||||
|
if(_targetSpeed < speedFloor) _targetSpeed = speedFloor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stepper::setEndlesMove(bool endless, bool direction)
|
||||||
|
{
|
||||||
|
endlessMove = endless;
|
||||||
|
if(direction != endlessDriection)
|
||||||
|
{
|
||||||
|
_speed = speedFloor;
|
||||||
|
endlessDriection = direction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Stepper::isAt()
|
||||||
|
{
|
||||||
|
return _currentStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Stepper::isStoped()
|
||||||
|
{
|
||||||
|
return _currentStep == _targetStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stepper::step(bool direction)
|
||||||
|
{
|
||||||
|
switch(_currentStep & 0x03)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
writePin(_port, _pinA, true);
|
||||||
|
writePin(_port, _pinB, false);
|
||||||
|
writePin(_port, _pinC, false);
|
||||||
|
writePin(_port, _pinD, true);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
writePin(_port, _pinA, true);
|
||||||
|
writePin(_port, _pinB, true);
|
||||||
|
writePin(_port, _pinC, false);
|
||||||
|
writePin(_port, _pinD, false);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
writePin(_port, _pinA, false);
|
||||||
|
writePin(_port, _pinB, true);
|
||||||
|
writePin(_port, _pinC, true);
|
||||||
|
writePin(_port, _pinD, false);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
writePin(_port, _pinA, false);
|
||||||
|
writePin(_port, _pinB, false);
|
||||||
|
writePin(_port, _pinC, true);
|
||||||
|
writePin(_port, _pinD, true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
writePin(_port, _pinA, false);
|
||||||
|
writePin(_port, _pinB, false);
|
||||||
|
writePin(_port, _pinC, false);
|
||||||
|
writePin(_port, _pinD, false);
|
||||||
|
}
|
||||||
|
_currentStep = _currentStep + (direction ? 1 : -1);
|
||||||
|
if(endlessMove) _targetStep = _currentStep;
|
||||||
|
}
|
42
stepper.h
Normal file
42
stepper.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
class Stepper
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
volatile uint8_t * const _port;
|
||||||
|
const uint8_t _pinA;
|
||||||
|
const uint8_t _pinB;
|
||||||
|
const uint8_t _pinC;
|
||||||
|
const uint8_t _pinD;
|
||||||
|
|
||||||
|
static constexpr uint8_t acceleration = 5;
|
||||||
|
static constexpr uint8_t speedFloor = 50;
|
||||||
|
|
||||||
|
bool endlessMove = false;
|
||||||
|
bool endlessDriection = true;
|
||||||
|
|
||||||
|
uint16_t _speed = speedFloor;
|
||||||
|
uint8_t _targetSpeed = 220;
|
||||||
|
uint8_t _stepInterval = 0;
|
||||||
|
|
||||||
|
uint16_t _tickCounter = 0;
|
||||||
|
|
||||||
|
int32_t _currentStep = 0;
|
||||||
|
int32_t _targetStep = 0;
|
||||||
|
|
||||||
|
void step(bool direction);
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Stepper( volatile uint8_t * const port, const uint8_t pinA, const uint8_t pinB, const uint8_t pinC, const uint8_t pinD): _port(port), _pinA(pinA), _pinB(pinB), _pinC(pinC), _pinD(pinD){}
|
||||||
|
void tick();
|
||||||
|
void moveTo(const int32_t step);
|
||||||
|
void moveRelative(const int16_t dist);
|
||||||
|
void setSpeed(const uint8_t speed);
|
||||||
|
uint32_t isAt();
|
||||||
|
bool isStoped();
|
||||||
|
void setEndlesMove(bool endless, bool direction = true);
|
||||||
|
};
|
20
writepin.cpp
Normal file
20
writepin.cpp
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#include "writepin.h"
|
||||||
|
|
||||||
|
void writePin(volatile unsigned char * const port, const unsigned char pin, const bool state)
|
||||||
|
{
|
||||||
|
*port = (*port & ~(1 << pin)) | (1 << pin)*state;
|
||||||
|
//if(!state) *port &= ~(1 << pin);
|
||||||
|
//else *port |= (1 << pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBit( volatile unsigned char * const reg, const unsigned char bit, const bool value )
|
||||||
|
{
|
||||||
|
writePin(reg, bit, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDirection( volatile unsigned char * const portDirReg, const unsigned char pin, const bool makeOutput )
|
||||||
|
{
|
||||||
|
writePin(portDirReg, pin, makeOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readPin( volatile const unsigned char * const inPort, const unsigned char pin){ return (bool) (*inPort & (1 << pin));}
|
14
writepin.h
Normal file
14
writepin.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#ifndef WRITEPIN_H
|
||||||
|
#define WRITEPIN_H
|
||||||
|
#include <avr/io.h>
|
||||||
|
|
||||||
|
|
||||||
|
void writePin(volatile unsigned char * const port, const unsigned char pin, const bool state);
|
||||||
|
|
||||||
|
void setBit( volatile unsigned char * const reg, const unsigned char bit, const bool value );
|
||||||
|
|
||||||
|
void setDirection( volatile unsigned char * const portDirReg, const unsigned char pin, const bool makeOutput );
|
||||||
|
|
||||||
|
bool readPin( volatile const unsigned char * const inPort, const unsigned char pin);
|
||||||
|
|
||||||
|
#endif
|
Reference in New Issue
Block a user