diff --git a/Firmware/Chameleon-Mini/Memory.c b/Firmware/Chameleon-Mini/Memory.c index 1a4e3ba..7b2ca6c 100644 --- a/Firmware/Chameleon-Mini/Memory.c +++ b/Firmware/Chameleon-Mini/Memory.c @@ -455,7 +455,7 @@ bool MemoryUploadBlock(void *Buffer, uint32_t BlockAddress, uint16_t ByteCount) } bool MemoryDownloadBlock(void *Buffer, uint32_t BlockAddress, uint16_t ByteCount) { - if (BlockAddress >= MEMORY_SIZE_PER_SETTING) { + if ((BlockAddress >= MEMORY_SIZE_PER_SETTING) || (BlockAddress >= ActiveConfiguration.MemorySize)) { /* There are bytes out of bounds to be read. Notify that we are done. */ return false; } else { diff --git a/Firmware/Chameleon-Mini/Uart.c b/Firmware/Chameleon-Mini/Uart.c new file mode 100644 index 0000000..ce065c7 --- /dev/null +++ b/Firmware/Chameleon-Mini/Uart.c @@ -0,0 +1,227 @@ +/* + * Uart.c + * + * Created on: 20.03.2013 + * Author: Willok + * + * ChangeLog + * 2019-09-22 Willok Add some UART PROCESS + * + */ + +#include "Chameleon-Mini.h" +#include "Uart.h" +#include "uartcmd.h" + +// This defines the buffer used by the UART + +#define RBUF_SIZE 512 +typedef struct _buf_rx { + uint16_t in; + uint16_t out; + uint8_t buf[RBUF_SIZE]; +}buf_rx; + +#define TBUF_SIZE 512 +typedef struct _buf_tx { + uint16_t in; + uint16_t out; + uint8_t buf[TBUF_SIZE]; +}buf_tx; + +buf_rx rbuf = { 0, 0, }; + +buf_tx tbuf = { 0, 0, }; + +//#pragma GCC push_options +//#pragma GCC optimize ("O0") + +ISR(USARTE0_RXC_vect) +{ + if (0 == (USART.STATUS & (USART_BUFOVF_bm | USART_FERR_bm | USART_PERR_bm))) + { + if (((rbuf.in - rbuf.out) & ~(RBUF_SIZE - 1)) == 0) + { + rbuf.buf[rbuf.in & (RBUF_SIZE - 1)] = USART_GetChar(&USART); + rbuf.in++; + } + else + { + // It means that the buffer is full. Find a way to deal with it + uart_putc('E'); + uart_putc('0'); + uart_putc(USART_GetChar(&USART)); + } + } + else + { + uart_putc('E'); + uart_putc('1'); + uart_putc(USART.STATUS); + uart_putc(USART_GetChar(&USART)); + } +} + +int16_t uart_fifo_get(void) +{ + if (rbuf.in - rbuf.out) + return rbuf.buf[(rbuf.out++) & (RBUF_SIZE - 1)]; + + return -1; +} + +void uart_fifo_put(uint8_t* buf, uint16_t len) +{ + while(len) + { + if (((tbuf.in - tbuf.out) & ~(TBUF_SIZE - 1)) == 0) + { + tbuf.buf[tbuf.in & (TBUF_SIZE - 1)] = *buf; + tbuf.in++; + } + else + { + // It means that the buffer is full. Find a way to deal with it + uart_putc('E'); + uart_putc('2'); + } + + buf++; + len--; + } +} + +// UART loop, sending data, fetching data from sending buffer +void uart_task(void) +{ + uint8_t sendbuf[sizeof(CMD_HEAD) + 64]; + uint16_t DataLen = tbuf.in - tbuf.out; + uint16_t CurrentLen; + PCMD_HEAD SendHead = (PCMD_HEAD)sendbuf; + uint8_t i; + + while (DataLen) + { + // Maximum 64 bytes at one time + CurrentLen = DataLen; + if (CurrentLen > 64) + CurrentLen = 64; + + SendHead->bSign = CMD_HEAD_SIGN; + SendHead->bCmd = CMD_UART_RXTX; + SendHead->bCmdLen = CurrentLen; + SendHead->bChkSum = 0; + + // It has to be copied here because the circular buffer is discontinuous + for (i = 0; i < CurrentLen; i++) + { + sendbuf[sizeof(CMD_HEAD) + i] = tbuf.buf[(tbuf.out++) & (TBUF_SIZE - 1)]; + } + // Check Summing + SendHead->bChkSum = GetChkSum(SendHead, NULL); + + // Send filled buffer + uart_putb(sendbuf, CurrentLen + sizeof(CMD_HEAD)); + + // Resize data + DataLen -= CurrentLen; + } +} + +// Send a byte +void uart_putc(uint8_t c) +{ + // Wait buffer empty + while(!(USART.STATUS & USART_DREIF_bm)); + + /* send next byte */ + USART.DATA = c; +} + +// Send a data block +void uart_putb(uint8_t* data, uint8_t len) +{ + while (len) + { + uart_putc(*data); + data++; + len--; + } +} + + +uint32_t dwBaudRate = 115200; +// Set UART rate +uint8_t uart_baudrate(uint32_t NewBaudrate) +{ + uint8_t bRet = 1; + + switch (NewBaudrate) + { + case 115200: + USART_Baudrate_Set(&USART, 878 , -6); // 115200 -0.04% + break; + case 230400: + USART_Baudrate_Set(&USART, 407 , -6); // 230400 -0.04% + break; + case 460800: + USART_Baudrate_Set(&USART, 343 , -7); // 460800 -0.04% + break; + default: + dwBaudRate = 460800; + USART_Baudrate_Set(&USART, 343 , -7); // 460800 -0.04% + bRet = 0; + break; + } + + if (bRet) + { + dwBaudRate = NewBaudrate; + } + + return bRet; +} + +// UART port initialization +void uart_init(void) +{ +#ifdef CONFIG_UART_MODE + // Buffer initialization + rbuf.in = 0; + rbuf.out = 0; + rbuf.buf[0] = 0; + tbuf.in = 0; + tbuf.out = 0; + tbuf.buf[0] = 0; + + /* PE3 (TXD0) output */ + PORTE.DIRSET = PIN3_bm; + PORTE.OUTSET = PIN3_bm; + /* PE2 (RXD0) input */ + PORTE.DIRCLR = PIN2_bm; + PORTE.PIN2CTRL = PORT_OPC_PULLUP_gc; + + /* USART Mode - Asynchronous*/ + USART_SetMode(&USART, USART_CMODE_ASYNCHRONOUS_gc); + /* USARTE0 Frame structure, 8-bit data bits, no check, 1 stop bit */ + USART_Format_Set(&USART, USART_CHSIZE_8BIT_gc,USART_PMODE_DISABLED_gc, 0); + + // Set baud rate @ 27.12M = 13.56 * 2 + uart_baudrate(460800); // 460800 +// uart_baudrate(230400); // 115200 +// uart_baudrate(115200); // 115200 + + /* USART enable TX*/ + USART_Tx_Enable(&USART); + /* USART enable RX*/ + USART_Rx_Enable(&USART); + + ///* USART Receive interrupt level*/ + //USART_RxdInterruptLevel_Set(&USART,USART_RXCINTLVL_LO_gc); + USART_RxdInterruptLevel_Set(&USART,USART_RXCINTLVL_HI_gc); +#endif +} + + +//#pragma GCC pop_options + diff --git a/Firmware/Chameleon-Mini/Uart.h b/Firmware/Chameleon-Mini/Uart.h new file mode 100644 index 0000000..084886b --- /dev/null +++ b/Firmware/Chameleon-Mini/Uart.h @@ -0,0 +1,191 @@ +#ifndef UART_H +#define UART_H + +#include +#include + +#define USART USARTE0 + +void uart_init(void); +void uart_task(void); +void uart_putc(uint8_t c); +void uart_putb(uint8_t* data, uint8_t len); + +void uart_fifo_put(uint8_t* buf, uint16_t len); + +int16_t uart_fifo_get(void); +//uint16_t user_get(uint8_t *buf); +//uint8_t user_read(uint8_t *rx, uint16_t *len); + + +//+------------------------------------------------------------------------------ + + /*Macros. */ + +/*! \brief Macro that sets the USART frame format. + * + * Sets the frame format, Frame Size, parity mode and number of stop bits. + * + * \param _usart Pointer to the USART module + * \param _charSize The character size. Use USART_CHSIZE_t type. + * \param _parityMode The parity Mode. Use USART_PMODE_t type. + * \param _twoStopBits Enable two stop bit mode. Use bool type. + */ +#define USART_Format_Set(_usart, _charSize, _parityMode, _twoStopBits) \ + (_usart)->CTRLC = (uint8_t) _charSize | _parityMode | \ + (_twoStopBits ? USART_SBMODE_bm : 0) + + +/*! \brief Set USART baud rate. + * + * Sets the USART's baud rate register. + * + * UBRR_Value : Value written to UBRR + * ScaleFactor : Time Base Generator Scale Factor + * + * Equation for calculation of BSEL value in asynchronous normal speed mode: + * If ScaleFactor >= 0 + * BSEL = ((I/O clock frequency)/(2^(ScaleFactor)*16*Baudrate))-1 + * If ScaleFactor < 0 + * BSEL = (1/(2^(ScaleFactor)*16))*(((I/O clock frequency)/Baudrate)-1) + * + * \note See XMEGA manual for equations for calculation of BSEL value in other + * modes. + * + * \param _usart Pointer to the USART module. + * \param _bselValue Value to write to BSEL part of Baud control register. + * Use uint16_t type. + * \param _bScaleFactor USART baud rate scale factor. + * Use uint8_t type + */ +#define USART_Baudrate_Set(_usart, _bselValue, _bScaleFactor) \ + (_usart)->BAUDCTRLA =(uint8_t)_bselValue; \ + (_usart)->BAUDCTRLB =(_bScaleFactor << USART_BSCALE0_bp)|(_bselValue >> 8) + + +/*! \brief Enable USART receiver. + * + * \param _usart Pointer to the USART module + */ +#define USART_Rx_Enable(_usart) ((_usart)->CTRLB |= USART_RXEN_bm) + + +/*! \brief Disable USART receiver. + * + * \param _usart Pointer to the USART module. + */ +#define USART_Rx_Disable(_usart) ((_usart)->CTRLB &= ~USART_RXEN_bm) + + +/*! \brief Enable USART transmitter. + * + * \param _usart Pointer to the USART module. + */ +#define USART_Tx_Enable(_usart) ((_usart)->CTRLB |= USART_TXEN_bm) + + +/*! \brief Disable USART transmitter. + * + * \param _usart Pointer to the USART module. + */ +#define USART_Tx_Disable(_usart) ((_usart)->CTRLB &= ~USART_TXEN_bm) + + +/*! \brief Set USART RXD interrupt level. + * + * Sets the interrupt level on RX Complete interrupt. + * + * \param _usart Pointer to the USART module. + * \param _rxdIntLevel Interrupt level of the RXD interrupt. + * Use USART_RXCINTLVL_t type. + */ +#define USART_RxdInterruptLevel_Set(_usart, _rxdIntLevel) \ + ((_usart)->CTRLA = ((_usart)->CTRLA & ~USART_RXCINTLVL_gm) | _rxdIntLevel) + + +/*! \brief Set USART TXD interrupt level. + * + * Sets the interrupt level on TX Complete interrupt. + * + * \param _usart Pointer to the USART module. + * \param _txdIntLevel Interrupt level of the TXD interrupt. + * Use USART_TXCINTLVL_t type. + */ +#define USART_TxdInterruptLevel_Set(_usart, _txdIntLevel) \ + (_usart)->CTRLA = ((_usart)->CTRLA & ~USART_TXCINTLVL_gm) | _txdIntLevel + + + +/*! \brief Set USART DRE interrupt level. + * + * Sets the interrupt level on Data Register interrupt. + * + * \param _usart Pointer to the USART module. + * \param _dreIntLevel Interrupt level of the DRE interrupt. + * Use USART_DREINTLVL_t type. + */ +#define USART_DreInterruptLevel_Set(_usart, _dreIntLevel) \ + (_usart)->CTRLA = ((_usart)->CTRLA & ~USART_DREINTLVL_gm) | _dreIntLevel + + +/*! \brief Set the mode the USART run in. + * + * Set the mode the USART run in. The default mode is asynchronous mode. + * + * \param _usart Pointer to the USART module register section. + * \param _usartMode Selects the USART mode. Use USART_CMODE_t type. + * + * USART modes: + * - 0x0 : Asynchronous mode. + * - 0x1 : Synchronous mode. + * - 0x2 : IrDA mode. + * - 0x3 : Master SPI mode. + */ +#define USART_SetMode(_usart, _usartMode) \ + ((_usart)->CTRLC = ((_usart)->CTRLC & (~USART_CMODE_gm)) | _usartMode) + + + +/*! \brief Check if data register empty flag is set. + * + * \param _usart The USART module. + */ +#define USART_IsTXDataRegisterEmpty(_usart) (((_usart)->STATUS & USART_DREIF_bm) != 0) + + + +/*! \brief Put data (5-8 bit character). + * + * Use the macro USART_IsTXDataRegisterEmpty before using this function to + * put data to the TX register. + * + * \param _usart The USART module. + * \param _data The data to send. + */ +#define USART_PutChar(_usart, _data) ((_usart)->DATA = _data) + + + +/*! \brief Checks if the RX complete interrupt flag is set. + * + * Checks if the RX complete interrupt flag is set. + * + * \param _usart The USART module. + */ +#define USART_IsRXComplete(_usart) (((_usart)->STATUS & USART_RXCIF_bm) != 0) + + + + +/*! \brief Get received data (5-8 bit character). + * + * This macro reads out the RX register. + * Use the macro USART_RX_Complete to check if anything is received. + * + * \param _usart The USART module. + * + * \retval Received data. + */ +#define USART_GetChar(_usart) ((_usart)->DATA) + +#endif diff --git a/Firmware/Chameleon-Mini/uartcmd.c b/Firmware/Chameleon-Mini/uartcmd.c new file mode 100644 index 0000000..d449107 --- /dev/null +++ b/Firmware/Chameleon-Mini/uartcmd.c @@ -0,0 +1,195 @@ +/* + * uartcmd.c + * + * Created on: 20.03.2013 + * Author: Willok + * + * ChangeLog + * 2019-09-22 Willok Added processing of UART command + * + */ + +#include "Uart.h" +#include "uartcmd.h" +#include "Settings.h" +#include "Configuration.h" +#include "Application/Application.h" + +#include "System.h" + + +uint16_t CmdLen = 0; +uint8_t CmdBuffer[256]; + +// Check Summing +uint8_t GetChkSum(PCMD_HEAD pCmdHead, uint8_t* DataPtr) +{ + uint8_t bRet = 0; + + if (!DataPtr) + DataPtr = (uint8_t*)(pCmdHead + 1); + + bRet -= pCmdHead->bSign; + bRet -= pCmdHead->bCmd; + bRet -= pCmdHead->bCmdLen; + + for (uint8_t i = 0; i < pCmdHead->bCmdLen; i++) + { + bRet -= DataPtr[i]; + } + + return bRet; +} + +// UART command initialization +void uartcmd_init(void) +{ + CmdLen = 0; + CmdBuffer[0] = 0; +} + + + +// USBmode or UARTmode +extern uint8_t bUSBTerminal; + +// Received UART command data. Need to be handed over to the command line +void CmdUartRx(PCMD_HEAD CmdHead, uint8_t* UartData) +{ + if (!bUSBTerminal) + { + while (CmdHead->bCmdLen) + { + if (XModemProcessByte(*UartData)) { + /* XModem handled the byte */ + } else if (CommandLineProcessByte(*UartData)) { + /* CommandLine handled the byte */ + } + + UartData++; + CmdHead->bCmdLen--; + } + } +} + +// Heartbeat marker +uint8_t bKeepAlive = 1; + +// Version number with compilation date +char bVersion[] = "v1.0 " BUILD_DATE " " __TIME__; + +// Command tick, send heartbeat packet regularly +void uartcmd_tick(void) +{ + static uint8_t KeepAliveTick = 200; + + // heartbeat every 2S + if (++KeepAliveTick >= 20) + { + KeepAliveTick = 0; + + if (bKeepAlive) + { + uint8_t Buffer[sizeof(CMD_HEAD) + sizeof(CMD_KEEP_ALIVE)]; + PCMD_HEAD CmdHead = (PCMD_HEAD)Buffer; + PCMD_KEEP_ALIVE pKeepAlive = (PCMD_KEEP_ALIVE)(CmdHead + 1); + + // Fill handshake package + CmdHead->bSign = 0xA5; + CmdHead->bCmd = CMD_TYPE_ALIVE; + CmdHead->bCmdLen = sizeof(CMD_KEEP_ALIVE); + pKeepAlive->bMajVer = 1; + pKeepAlive->bMinVer = 0; + memcpy(pKeepAlive->bVerStr, bVersion, sizeof(bVersion)); + + CmdHead->bChkSum = GetChkSum(CmdHead, NULL); + // Send handshake package + uart_putb((uint8_t*)CmdHead, sizeof(CMD_HEAD) + CmdHead->bCmdLen); + } + } +} + +// Receive shutdown notification +void CmdUartPowerDown(PCMD_HEAD CmdHead, uint8_t* UartData) +{ + // Switch to empty configuration, not saved + ConfigurationSetById(CONFIG_NONE); + + // Save log on demand + LogTick(); + + // Flash two LED to warning power off + LED_PORT.OUTSET = PIN4_bm; + LED_PORT.OUTCLR = PIN3_bm; + while (1) + { + if (SystemTick100ms()) + { + LED_PORT.OUTTGL = PIN4_bm; + LED_PORT.OUTTGL = PIN3_bm; + } + } +} + + +// Command processing callback +void uartcmd_task(void) +{ + int16_t RecvData; + // Loop data in receive buffer + while ((RecvData = uart_fifo_get()) >= 0) + { + CmdBuffer[CmdLen] = (uint8_t)RecvData; + + // The first character must be special, otherwise it will be discarded + if (CmdLen == 0 && CmdBuffer[0] != CMD_HEAD_SIGN) + continue; + + + // Statistics received + CmdLen++; + + // At least one head is needed, then we can handle it. + if (CmdLen >= sizeof(CMD_HEAD)) + { + PCMD_HEAD CmdHead = (PCMD_HEAD)CmdBuffer; + + // If the command length is too long, there must be error. Discard the data and wait for resynchronization. + if (CmdHead->bCmdLen >= (sizeof(CmdBuffer) - sizeof(CMD_HEAD))) + { + CmdLen = 0; + continue; + } + + // Judge whether the received data is complete + if (CmdLen >= sizeof(CMD_HEAD) + CmdHead->bCmdLen) + { + // Calculate the check sum, otherwise discard it. + if (CmdHead->bChkSum != GetChkSum(CmdHead, NULL)) + { + CmdLen = 0; + continue; + } + + // Processing command + switch (CmdHead->bCmd) + { + case CMD_UART_RXTX: // Uart data + CmdUartRx(CmdHead, (uint8_t*)(CmdHead+1)); + break; + case CMD_TYPE_POWERDOWN: // Switch to empty configuration and wait for power failure + CmdUartPowerDown(CmdHead, (uint8_t*)(CmdHead+1)); + break; + case CMD_TYPE_ALIVE: // Receive the heartbeat package response, turn off the heartbeat + bKeepAlive = 0; + break; + default: + break; + } + + // Clear command buffer after command processing + CmdLen = 0; + } + } + } +} diff --git a/Firmware/Chameleon-Mini/uartcmd.h b/Firmware/Chameleon-Mini/uartcmd.h new file mode 100644 index 0000000..00c1f34 --- /dev/null +++ b/Firmware/Chameleon-Mini/uartcmd.h @@ -0,0 +1,55 @@ +#include "Uart.h" + +void ProcessByte(void); + +void uartcmd_init(void); +void uartcmd_tick(void); +void uartcmd_task(void); + +// Command header ID +#define CMD_HEAD_SIGN 0xA5 + +// Command reply ID +#define CMD_SEND_ACK 0x80 + +// UART data command 'U' +#define CMD_UART_RXTX 0x75 + +// TEST data command 't' +#define CMD_BLE_TEST 0x74 + +// Heartbeat command 'a' +#define CMD_TYPE_ALIVE 0x61 + +// Power down command 'p' +#define CMD_TYPE_POWERDOWN 0x70 + + + +//#pragma pack(push,1) +// GCC Use another alignment command __attribute__ ((aligned (1))) +// Command structure +typedef struct { + uint8_t bSign; // Head sign + uint8_t bCmd; // Command type + uint8_t bCmdLen; // Command length (without header) + uint8_t bChkSum; // Command checksum +} CMD_HEAD, *PCMD_HEAD __attribute__ ((aligned (1))); + +// 心跳命令 +typedef struct { + uint8_t bMajVer; // main version + uint8_t bMinVer; // Sub version number + uint8_t bVerStr[32]; // Version string +}CMD_KEEP_ALIVE, *PCMD_KEEP_ALIVE __attribute__ ((aligned (1))); + +// 串口数据发送 +typedef struct { + uint16_t wBitCount; // Length of data sent, unit: bit + uint8_t bFlag; // Generate check bit, CRC and other flags +}CMD_SEND_DATA, *PCMD_SEND_DATA __attribute__ ((aligned (1))); + + +uint8_t GetChkSum(PCMD_HEAD pCmdHead, uint8_t* DataPtr); + +//#pragma pack(pop)