SPI flash driver (write) & settings saving

This commit is contained in:
muzkr committed 2025-10-26 01:46:11 +08:00
1 parent 992a417e62
commit a5690b5bf4
14 files changed
+562 -151

No files matched your search

+3 -2
View File
@@ -12,7 +12,6 @@ target_sources(App INTERFACE
driver/gpio.c
driver/i2c.c
driver/keyboard.c
# driver/spi.c
driver/st7565.c
driver/system.c
driver/systick.c
@@ -106,7 +105,9 @@ enable_feature(ENABLE_AIRCOPY
ui/aircopy.c
)
enable_feature(ENABLE_NOAA)
# enable_feature(ENABLE_VOICE)
enable_feature(ENABLE_VOICE
driver/dac.c
)
enable_feature(ENABLE_VOX)
enable_feature(ENABLE_ALARM)
enable_feature(ENABLE_TX1750)
+1 -6
View File
@@ -119,12 +119,7 @@ void FM_TurnOff(void)
void FM_EraseChannels(void)
{
uint8_t Template[8];
memset(Template, 0xFF, sizeof(Template));
for (unsigned i = 0; i < 5; i++)
EEPROM_WriteBuffer(0x0E40 + (i * 8), Template);
PY25Q16_SectorErase(0x003000);
memset(gFM_Channels, 0xFF, sizeof(gFM_Channels));
}
+4 -1
View File
@@ -328,7 +328,10 @@ static void CMD_051D(const uint8_t *pBuffer)
bReloadEeprom = true;
if ((Offset < 0x0E98 || Offset >= 0x0EA0) || !bIsInLockScreen || pCmd->bAllowPassword)
EEPROM_WriteBuffer(Offset, &pCmd->Data[i * 8U]);
{
// EEPROM_WriteBuffer(Offset, &pCmd->Data[i * 8U]);
// TODO: To be implemented
}
}
if (bReloadEeprom)
+11 -1
View File
@@ -25,6 +25,7 @@
#include "py32f071_ll_gpio.h"
#include "py32f071_ll_rcc.h"
#include "py32f071_ll_adc.h"
#include "driver/dac.h"
#include "driver/backlight.h"
#ifdef ENABLE_FMRADIO
#include "driver/bk1080.h"
@@ -110,15 +111,21 @@ void BOARD_GPIO_Init(void)
InitStruct.Pin = LL_GPIO_PIN_9 | LL_GPIO_PIN_8 | LL_GPIO_PIN_2;
LL_GPIO_Init(GPIOB, &InitStruct);
// TODO: conditional compile per ENABLE_FLASHLIGHT
// Flashlight: PC13
InitStruct.Pin = LL_GPIO_PIN_13;
LL_GPIO_Init(GPIOC, &InitStruct);
#ifdef ENABLE_FMRADIO
// BK1080 SCK: PF5
// BK1080 SDA: PF6
InitStruct.Pin = LL_GPIO_PIN_6 | LL_GPIO_PIN_5;
LL_GPIO_Init(GPIOF, &InitStruct);
#endif
// Backlight: PF8
// BK4819 CS: PF9
InitStruct.Pin = LL_GPIO_PIN_9 | LL_GPIO_PIN_8 | LL_GPIO_PIN_6 | LL_GPIO_PIN_5;
InitStruct.Pin = LL_GPIO_PIN_9 | LL_GPIO_PIN_8 ;
LL_GPIO_Init(GPIOF, &InitStruct);
#ifndef ENABLE_SWD
@@ -172,6 +179,9 @@ void BOARD_Init(void)
BOARD_GPIO_Init();
BACKLIGHT_InitHardware();
BOARD_ADC_Init();
#ifdef ENABLE_VOICE
DAC_Init();
#endif
PY25Q16_Init();
ST7565_Init();
#ifdef ENABLE_FMRADIO
+3
View File
@@ -81,6 +81,9 @@ static void BACKLIGHT_Sound(void)
void BACKLIGHT_TurnOn(void)
{
// TODO: This is temporary. Will delete
GPIO_TurnOnBacklight();
#ifdef ENABLE_FEAT_F4HWN_SLEEP
gSleepModeCountdown_500ms = gSetting_set_off * 120;
#endif
+2 -1
View File
@@ -1,4 +1,5 @@
/* Copyright 2023 Dual Tachyon
/* Copyright 2025 muzkr https://github.com/muzkr
* Copyright 2023 Dual Tachyon
* https://github.com/DualTachyon
*
* Licensed under the Apache License, Version 2.0 (the "License");
+50
View File
@@ -0,0 +1,50 @@
/* Copyright 2025 muzkr https://github.com/muzkr
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "driver/dac.h"
#include "driver/systick.h"
#include "py32f071_ll_bus.h"
#include "py32f071_ll_gpio.h"
#include "py32f071_ll_dac.h"
#include "py32f071_ll_tim.h"
#define TIMx TIM6
#define DAC_CHANNEL LL_DAC_CHANNEL_1
static inline void TIM_Init()
{
LL_TIM_SetPrescaler(TIMx, 3);
LL_TIM_SetAutoReload(TIMx, 1499);
LL_TIM_SetTriggerOutput(TIMx, LL_TIM_TRGO_UPDATE);
}
void DAC_Init()
{
// Channel 1: PA4
LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_4, LL_GPIO_MODE_ANALOG);
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_DAC1);
LL_DAC_SetTriggerSource(DAC1, DAC_CHANNEL, LL_DAC_TRIG_EXT_TIM6_TRGO);
LL_DAC_SetOutputBuffer(DAC1, DAC_CHANNEL, LL_DAC_OUTPUT_BUFFER_ENABLE);
LL_DAC_EnableDMAReq(DAC1, DAC_CHANNEL);
LL_DAC_Enable(DAC1, DAC_CHANNEL);
SYSTICK_DelayUs(15);
TIM_Init();
LL_DAC_EnableTrigger(DAC1, DAC_CHANNEL);
}
+21
View File
@@ -0,0 +1,21 @@
/* Copyright 2025 muzkr https://github.com/muzkr
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef DRIVER_DAC_H
#define DRIVER_DAC_H
void DAC_Init();
#endif // DRIVER_DAC_H
+245 -8
View File
@@ -1,5 +1,5 @@
/* Copyright 2023 Dual Tachyon
* https://github.com/DualTachyon
/* Copyright 2025 muzkr
* https://github.com/muzkr
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
#include "py32f071_ll_spi.h"
#include "py32f071_ll_dma.h"
#include "driver/system.h"
#include "driver/systick.h"
#define SPIx SPI2
#define CHANNEL_RD LL_DMA_CHANNEL_4
@@ -31,7 +32,12 @@
#define CS_PIN GPIO_MAKE_PIN(GPIOA, LL_GPIO_PIN_3)
static uint8_t TX_Buf[256];
#define SECTOR_SIZE 0x1000
#define PAGE_SIZE 0x100
static uint32_t SectorCacheAddr = 0x1000000;
static uint8_t SectorCache[SECTOR_SIZE];
static uint8_t BlackHole[1];
static volatile bool TC_Flag;
static inline void CS_Assert()
@@ -126,7 +132,56 @@ static void SPI_ReadBuf(uint8_t *Buf, uint32_t Size)
LL_DMA_SetPeriphAddress(DMA1, CHANNEL_RD, LL_SPI_DMA_GetRegAddr(SPIx));
LL_DMA_SetDataLength(DMA1, CHANNEL_RD, Size);
LL_DMA_SetMemoryAddress(DMA1, CHANNEL_WR, (uint32_t)TX_Buf);
LL_DMA_SetMemoryAddress(DMA1, CHANNEL_WR, (uint32_t)BlackHole);
LL_DMA_SetPeriphAddress(DMA1, CHANNEL_WR, LL_SPI_DMA_GetRegAddr(SPIx));
LL_DMA_SetDataLength(DMA1, CHANNEL_WR, Size);
TC_Flag = false;
LL_DMA_EnableIT_TC(DMA1, CHANNEL_RD);
LL_DMA_EnableChannel(DMA1, CHANNEL_RD);
LL_DMA_EnableChannel(DMA1, CHANNEL_WR);
LL_SPI_EnableDMAReq_RX(SPIx);
LL_SPI_Enable(SPIx);
LL_SPI_EnableDMAReq_TX(SPIx);
while (!TC_Flag)
;
}
static void SPI_WriteBuf(const uint8_t *Buf, uint32_t Size)
{
LL_SPI_Disable(SPIx);
LL_DMA_DisableChannel(DMA1, CHANNEL_RD);
LL_DMA_DisableChannel(DMA1, CHANNEL_WR);
LL_DMA_ClearFlag_GI4(DMA1);
LL_DMA_ConfigTransfer(DMA1, CHANNEL_RD, //
LL_DMA_DIRECTION_PERIPH_TO_MEMORY //
| LL_DMA_MODE_NORMAL //
| LL_DMA_PERIPH_NOINCREMENT //
| LL_DMA_MEMORY_NOINCREMENT //
| LL_DMA_PDATAALIGN_BYTE //
| LL_DMA_MDATAALIGN_BYTE //
| LL_DMA_PRIORITY_LOW //
);
LL_DMA_ConfigTransfer(DMA1, CHANNEL_WR, //
LL_DMA_DIRECTION_MEMORY_TO_PERIPH //
| LL_DMA_MODE_NORMAL //
| LL_DMA_PERIPH_NOINCREMENT //
| LL_DMA_MEMORY_INCREMENT //
| LL_DMA_PDATAALIGN_BYTE //
| LL_DMA_MDATAALIGN_BYTE //
| LL_DMA_PRIORITY_LOW //
);
LL_DMA_SetMemoryAddress(DMA1, CHANNEL_RD, (uint32_t)BlackHole);
LL_DMA_SetPeriphAddress(DMA1, CHANNEL_RD, LL_SPI_DMA_GetRegAddr(SPIx));
LL_DMA_SetDataLength(DMA1, CHANNEL_RD, Size);
LL_DMA_SetMemoryAddress(DMA1, CHANNEL_WR, (uint32_t)Buf);
LL_DMA_SetPeriphAddress(DMA1, CHANNEL_WR, LL_SPI_DMA_GetRegAddr(SPIx));
LL_DMA_SetDataLength(DMA1, CHANNEL_WR, Size);
@@ -153,6 +208,14 @@ static uint8_t SPI_WriteByte(uint8_t Value)
return LL_SPI_ReceiveData8(SPIx);
}
static void WriteAddr(uint32_t Addr);
static uint8_t ReadStatusReg(uint32_t Which);
static void WaitWIP();
static void WriteEnable();
static void SectorErase(uint32_t Addr);
static void SectorProgram(uint32_t Addr, const uint8_t *Buf, uint32_t Size);
static void PageProgram(uint32_t Addr, const uint8_t *Buf, uint32_t Size);
void PY25Q16_Init()
{
CS_Release();
@@ -164,9 +227,7 @@ void PY25Q16_ReadBuffer(uint32_t Address, void *pBuffer, uint32_t Size)
CS_Assert();
SPI_WriteByte(0x03); // Fast read
SPI_WriteByte(0xff & (Address >> 16));
SPI_WriteByte(0xff & (Address >> 8));
SPI_WriteByte(0xff & Address);
WriteAddr(Address);
if (Size >= 16)
{
@@ -183,8 +244,184 @@ void PY25Q16_ReadBuffer(uint32_t Address, void *pBuffer, uint32_t Size)
CS_Release();
}
void PY25Q16_WriteBuffer(uint16_t Address, const void *pBuffer)
void PY25Q16_WriteBuffer(uint32_t Address, const void *pBuffer, uint32_t Size, bool Append)
{
while (Size)
{
uint32_t SecIndex = Address / SECTOR_SIZE;
uint32_t SecOffset = Address % SECTOR_SIZE;
uint32_t SecSize = SECTOR_SIZE - SecOffset;
if (Size < SecSize)
{
SecSize = Size;
}
uint32_t SecAddr = SecIndex * SECTOR_SIZE;
if (SecAddr != SectorCacheAddr)
{
PY25Q16_ReadBuffer(SecAddr, SectorCache, SECTOR_SIZE);
SectorCacheAddr = SecAddr;
}
if (0 != strncmp(pBuffer, (char *)SectorCache + SecOffset, SecSize))
{
bool Erase = false;
for (uint32_t i = 0; i < SecSize; i++)
{
if (0xff != SectorCache[SecOffset + i])
{
Erase = true;
break;
}
}
memcpy(SectorCache + SecOffset, pBuffer, SecSize);
if (Erase)
{
SectorErase(SecAddr);
if (Append)
{
SectorProgram(SecAddr, SectorCache, SecOffset + SecSize);
memset(SectorCache + SecOffset + SecSize, 0xff, SECTOR_SIZE - SecOffset - SecSize);
}
else
{
SectorProgram(SecAddr, SectorCache, SECTOR_SIZE);
}
}
else
{
SectorProgram(Address, pBuffer, SecSize);
}
}
Address += SecSize;
pBuffer += SecSize;
Size -= SecSize;
} // while
}
void PY25Q16_SectorErase(uint32_t Address)
{
Address -= (Address % SECTOR_SIZE);
SectorErase(Address);
if (SectorCacheAddr == Address)
{
memset(SectorCache, 0xff, SECTOR_SIZE);
}
}
static inline void WriteAddr(uint32_t Addr)
{
SPI_WriteByte(0xff & (Addr >> 16));
SPI_WriteByte(0xff & (Addr >> 8));
SPI_WriteByte(0xff & Addr);
}
static uint8_t ReadStatusReg(uint32_t Which)
{
uint8_t Cmd;
switch (Which)
{
case 0:
Cmd = 0x5;
break;
case 1:
Cmd = 0x35;
break;
case 2:
Cmd = 0x15;
break;
default:
return 0;
}
CS_Assert();
SPI_WriteByte(Cmd);
uint8_t Value = SPI_WriteByte(0xff);
CS_Release();
return Value;
}
static void WaitWIP()
{
for (int i = 0; i < 1000000; i++)
{
uint8_t Status = ReadStatusReg(0);
if (1 & Status) // WIP
{
SYSTICK_DelayUs(10);
continue;
}
break;
}
}
static void WriteEnable()
{
CS_Assert();
SPI_WriteByte(0x6);
CS_Release();
}
static void SectorErase(uint32_t Addr)
{
WriteEnable();
WaitWIP();
CS_Assert();
SPI_WriteByte(0x20);
WriteAddr(Addr);
CS_Release();
WaitWIP();
}
static void SectorProgram(uint32_t Addr, const uint8_t *Buf, uint32_t Size)
{
while (Size)
{
uint32_t Size1 = PAGE_SIZE - (Addr % PAGE_SIZE);
if (Size < Size1)
{
Size1 = Size;
}
PageProgram(Addr, Buf, Size1);
Addr += Size1;
Buf += Size1;
Size -= Size1;
}
}
static void PageProgram(uint32_t Addr, const uint8_t *Buf, uint32_t Size)
{
WriteEnable();
WaitWIP();
CS_Assert();
SPI_WriteByte(0x2);
WriteAddr(Addr);
if (Size >= 16)
{
SPI_WriteBuf(Buf, Size);
}
else
{
for (uint32_t i = 0; i < Size; i++)
{
SPI_WriteByte(Buf[i]);
}
}
CS_Release();
WaitWIP();
}
void DMA1_Channel4_5_6_7_IRQHandler()
+5 -5
View File
@@ -1,5 +1,5 @@
/* Copyright 2023 Dual Tachyon
* https://github.com/DualTachyon
/* Copyright 2025 muzkr
* https://github.com/muzkr
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,11 +18,11 @@
#define DRIVER_PY25Q16_H
#include <stdint.h>
#include <stdbool.h>
void PY25Q16_Init();
void PY25Q16_ReadBuffer(uint32_t Address, void *pBuffer, uint32_t Size);
void PY25Q16_WriteBuffer(uint16_t Address, const void *pBuffer);
#define EEPROM_WriteBuffer PY25Q16_WriteBuffer
void PY25Q16_WriteBuffer(uint32_t Address, const void *pBuffer, uint32_t Size, bool Append);
void PY25Q16_SectorErase(uint32_t Address);
#endif
+29 -26
View File
@@ -74,22 +74,22 @@ static void SPI_Init()
LL_SPI_Enable(SPIx);
}
static inline void AssertCS()
static inline void CS_Assert()
{
GPIO_ResetOutputPin(PIN_CS);
}
static inline void ReleaseCS()
static inline void CS_Release()
{
GPIO_SetOutputPin(PIN_CS);
}
static inline void SetA0()
static inline void A0_Set()
{
GPIO_SetOutputPin(PIN_A0);
}
static inline void ResetA0()
static inline void A0_Reset()
{
GPIO_ResetOutputPin(PIN_A0);
}
@@ -110,7 +110,7 @@ static uint8_t SPI_WriteByte(uint8_t Value)
static void DrawLine(uint8_t column, uint8_t line, const uint8_t * lineBuffer, unsigned size_defVal)
{
ST7565_SelectColumnAndLine(column + 4, line);
SetA0();
A0_Set();
for (unsigned i = 0; i < size_defVal; i++) {
SPI_WriteByte(lineBuffer ? lineBuffer[i] : size_defVal);
}
@@ -118,9 +118,9 @@ static void DrawLine(uint8_t column, uint8_t line, const uint8_t * lineBuffer, u
void ST7565_DrawLine(const unsigned int Column, const unsigned int Line, const uint8_t *pBitmap, const unsigned int Size)
{
AssertCS();
CS_Assert();
DrawLine(Column, Line, pBitmap, Size);
ReleaseCS();
CS_Release();
}
@@ -134,7 +134,7 @@ void ST7565_DrawLine(const unsigned int Column, const unsigned int Line, const u
static void ST7565_BlitScreen(uint8_t line)
{
AssertCS();
CS_Assert();
ST7565_WriteByte(0x40);
if(line == 0)
@@ -152,7 +152,7 @@ void ST7565_DrawLine(const unsigned int Column, const unsigned int Line, const u
}
}
ReleaseCS();
CS_Release();
}
void ST7565_BlitFullScreen(void)
@@ -172,38 +172,38 @@ void ST7565_DrawLine(const unsigned int Column, const unsigned int Line, const u
#else
void ST7565_BlitFullScreen(void)
{
AssertCS();
CS_Assert();
ST7565_WriteByte(0x40);
for (unsigned line = 0; line < FRAME_LINES; line++) {
DrawLine(0, line+1, gFrameBuffer[line], LCD_WIDTH);
}
ReleaseCS();
CS_Release();
}
void ST7565_BlitLine(unsigned line)
{
AssertCS();
CS_Assert();
ST7565_WriteByte(0x40); // start line ?
DrawLine(0, line+1, gFrameBuffer[line], LCD_WIDTH);
ReleaseCS();
CS_Release();
}
void ST7565_BlitStatusLine(void)
{ // the top small text line on the display
AssertCS();
CS_Assert();
ST7565_WriteByte(0x40); // start line ?
DrawLine(0, 0, gStatusLine, LCD_WIDTH);
ReleaseCS();
CS_Release();
}
#endif
void ST7565_FillScreen(uint8_t value)
{
AssertCS();
CS_Assert();
for (unsigned i = 0; i < 8; i++) {
DrawLine(0, i, NULL, value);
}
ReleaseCS();
CS_Release();
}
// Software reset
@@ -289,7 +289,7 @@ uint8_t cmds[] = {
#if defined(ENABLE_FEAT_F4HWN_CTR) || defined(ENABLE_FEAT_F4HWN_INV)
void ST7565_ContrastAndInv(void)
{
AssertCS();
CS_Assert();
ST7565_WriteByte(ST7565_CMD_SOFTWARE_RESET); // software reset
for(uint8_t i = 0; i < 8; i++)
@@ -327,7 +327,7 @@ void ST7565_Init(void)
{
SPI_Init();
ST7565_HardwareReset();
AssertCS();
CS_Assert();
ST7565_WriteByte(ST7565_CMD_SOFTWARE_RESET); // software reset
SYSTEM_DelayMs(120);
@@ -353,7 +353,7 @@ void ST7565_Init(void)
ST7565_WriteByte(ST7565_CMD_SET_START_LINE | 0); // line 0
ST7565_WriteByte(ST7565_CMD_DISPLAY_ON_OFF | 1); // D=1
ReleaseCS();
CS_Release();
ST7565_FillScreen(0x00);
}
@@ -361,17 +361,17 @@ void ST7565_Init(void)
#ifdef ENABLE_FEAT_F4HWN_SLEEP
void ST7565_ShutDown(void)
{
AssertCS();
CS_Assert();
ST7565_WriteByte(ST7565_CMD_POWER_CIRCUIT | 0b000); // VB=0 VR=1 VF=1
ST7565_WriteByte(ST7565_CMD_SET_START_LINE | 0); // line 0
ST7565_WriteByte(ST7565_CMD_DISPLAY_ON_OFF | 0); // D=1
ReleaseCS();
CS_Release();
}
#endif
void ST7565_FixInterfGlitch(void)
{
AssertCS();
CS_Assert();
for(uint8_t i = 0; i < ARRAY_SIZE(cmds); i++)
#ifdef ENABLE_FEAT_F4HWN
ST7565_Cmd(i);
@@ -379,7 +379,7 @@ void ST7565_FixInterfGlitch(void)
ST7565_WriteByte(cmds[i]);
#endif
ReleaseCS();
CS_Release();
}
void ST7565_HardwareReset(void)
@@ -390,14 +390,17 @@ void ST7565_HardwareReset(void)
void ST7565_SelectColumnAndLine(uint8_t Column, uint8_t Line)
{
ResetA0();
A0_Reset();
SPI_WriteByte(Line + 176);
SPI_WriteByte(((Column >> 4) & 0x0F) | 0x10);
SPI_WriteByte((Column >> 0) & 0x0F);
}
/**
* Write a command (rather than pixel data)
*/
void ST7565_WriteByte(uint8_t Value)
{
ResetA0();
A0_Reset();
SPI_WriteByte(Value);
}
+184 -100
View File
@@ -1,4 +1,5 @@
/* Copyright 2023 Dual Tachyon
/* Copyright 2025 muzkr https://github.com/muzkr
* Copyright 2023 Dual Tachyon
* https://github.com/DualTachyon
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -502,33 +503,54 @@ void SETTINGS_FetchChannelName(char *s, const int channel)
void SETTINGS_FactoryReset(bool bIsAll)
{
uint16_t i;
uint8_t Template[8];
memset(Template, 0xFF, sizeof(Template));
//for (i = 0x0C80; i < 0x1E00; i += 8)
for (i = 0x0000; i < 0x1E00; i += 8)
// 0000 - 0c80
PY25Q16_SectorErase(0);
// 0c80 - 0d60
PY25Q16_SectorErase(0x001000);
// 0d60 - 0e30
if (bIsAll)
{
if (
!(i >= 0x0EE0 && i < 0x0F18) && // ANI ID + DTMF codes
!(i >= 0x0F30 && i < 0x0F50) && // AES KEY + F LOCK + Scramble Enable
!(i >= 0x1C00 && i < 0x1E00) && // DTMF contacts
!(i >= 0x0EB0 && i < 0x0ED0) && // Welcome strings
!(i >= 0x0EA0 && i < 0x0EA8) && // Voice Prompt
(bIsAll ||
(
!(i >= 0x0D60 && i < 0x0E28) && // MR Channel Attributes
!(i >= 0x0F18 && i < 0x0F30) && // Scan List
!(i >= 0x0F50 && i < 0x1C00) && // MR Channel Names
!(i >= 0x0E40 && i < 0x0E70) && // FM Channels
!(i >= 0x0E88 && i < 0x0E90) // FM settings
))
)
{
EEPROM_WriteBuffer(i, Template);
}
PY25Q16_SectorErase(0x002000);
}
// 0e40 - 0e68
if (bIsAll)
{
PY25Q16_SectorErase(0x003000);
}
// 0e70 - 0e80
PY25Q16_SectorErase(0x004000);
// 0e80 - 0e88
PY25Q16_SectorErase(0x005000);
// 0e88 - 0e90
if (bIsAll)
{
PY25Q16_SectorErase(0x006000);
}
// 0e90 - 0ee0
do
{
uint8_t Buf[0x50];
memset(Buf, 0xff, 0x50);
// 0EA0 - 0EA8 : keep
PY25Q16_ReadBuffer(0x007000 + 0x10, Buf + 0x10, 8);
// 0EB0 - 0ED0 : keep
PY25Q16_ReadBuffer(0x007000 + 0x20, Buf + 0x20, 0x20);
PY25Q16_WriteBuffer(0x007000, Buf, 0x50, true);
} while (0);
// 0ee0 - 0f18 : keep
// 0f18 - 0f20
if (bIsAll)
{
PY25Q16_SectorErase(0x009000);
}
// 0f30 - 0f40 : keep
// 0f40 - 0f48 : keep
// 0f50 - 1bd0
if (bIsAll)
{
PY25Q16_SectorErase(0x00e000);
}
// 1c00 - 1d00 : keep
if (bIsAll)
{
@@ -547,7 +569,7 @@ void SETTINGS_FactoryReset(bool bIsAll)
#endif
#ifdef ENABLE_FEAT_F4HWN
EEPROM_WriteBuffer(0x1FF0, Template);
PY25Q16_SectorErase(0x00c000);
#endif
}
}
@@ -571,11 +593,12 @@ void SETTINGS_SaveFM(void)
fmCfg.selFreq = gEeprom.FM_SelectedFrequency;
fmCfg.isMrMode = gEeprom.FM_IsMrMode;
fmCfg.band = gEeprom.FM_Band;
//fmCfg.space = gEeprom.FM_Space;
EEPROM_WriteBuffer(0x0E88, fmCfg.__raw);
// fmCfg.space = gEeprom.FM_Space;
// 0E88
PY25Q16_WriteBuffer(0x006000, fmCfg.__raw, 8, true);
for (unsigned i = 0; i < 5; i++)
EEPROM_WriteBuffer(0x0E40 + (i * 8), &gFM_Channels[i * 4]);
// 0E40
PY25Q16_WriteBuffer(0x003000, gFM_Channels, sizeof(gFM_Channels), true);
}
#endif
@@ -599,18 +622,23 @@ void SETTINGS_SaveVfoIndices(void)
State[7] = gEeprom.NoaaChannel[1];
#endif
EEPROM_WriteBuffer(0x0E80, State);
// 0x0E80
PY25Q16_WriteBuffer(0x005000, State, 8, true);
}
void SETTINGS_SaveSettings(void)
{
uint8_t State[8];
uint8_t *State;
uint8_t tmp = 0;
uint8_t SecBuf[0x50];
#ifdef ENABLE_PWRON_PASSWORD
uint32_t Password[2];
#endif
// ----------------------
// 0e70 - 0e80
memset(SecBuf, 0xff, 0x10);
// 0x0E70
State = SecBuf;
State[0] = gEeprom.CHAN_1_CALL;
State[1] = gEeprom.SQUELCH_LEVEL;
State[2] = gEeprom.TX_TIMEOUT_TIMER;
@@ -634,8 +662,9 @@ void SETTINGS_SaveSettings(void)
State[6] = 0;
#endif
State[7] = gEeprom.MIC_SENSITIVITY;
EEPROM_WriteBuffer(0x0E70, State);
// 0x0E78
State = SecBuf + 0x8;
State[0] = (gEeprom.BACKLIGHT_MIN << 4) + gEeprom.BACKLIGHT_MAX;
State[1] = gEeprom.CHANNEL_DISPLAY_MODE;
State[2] = gEeprom.CROSS_BAND_RX_TX;
@@ -671,8 +700,17 @@ void SETTINGS_SaveSettings(void)
#else
State[7] = gEeprom.VFO_OPEN;
#endif
EEPROM_WriteBuffer(0x0E78, State);
PY25Q16_WriteBuffer(0x004000, SecBuf, 0x10, true);
// -------------------------
// 0e90 - 0ee0
// memset(SecBuf, 0xff, 0x50);
PY25Q16_ReadBuffer(0x007000, SecBuf, 0x50);
// 0x0E90
State = SecBuf;
State[0] = gEeprom.BEEP_CONTROL;
State[0] |= gEeprom.KEY_M_LONG_PRESS_ACTION << 1;
State[1] = gEeprom.KEY_1_SHORT_PRESS_ACTION;
@@ -682,15 +720,15 @@ void SETTINGS_SaveSettings(void)
State[5] = gEeprom.SCAN_RESUME_MODE;
State[6] = gEeprom.AUTO_KEYPAD_LOCK;
State[7] = gEeprom.POWER_ON_DISPLAY_MODE;
EEPROM_WriteBuffer(0x0E90, State);
// 0x0E98
#ifdef ENABLE_PWRON_PASSWORD
memset(Password, 0xFF, sizeof(Password));
Password[0] = gEeprom.POWER_ON_PASSWORD;
EEPROM_WriteBuffer(0x0E98, Password);
State = SecBuf + 0x8;
State[0] = gEeprom.POWER_ON_PASSWORD;
#endif
memset(State, 0xFF, sizeof(State));
// 0x0EA0
State = SecBuf + 0x10;
#ifdef ENABLE_VOICE
State[0] = gEeprom.VOICE_PROMPT;
#endif
@@ -698,9 +736,9 @@ void SETTINGS_SaveSettings(void)
State[1] = gEeprom.S0_LEVEL;
State[2] = gEeprom.S9_LEVEL;
#endif
EEPROM_WriteBuffer(0x0EA0, State);
// 0x0EA8
State = SecBuf + 0x18;
#if defined(ENABLE_ALARM) || defined(ENABLE_TX1750)
State[0] = gEeprom.ALARM_MODE;
#else
@@ -710,8 +748,9 @@ void SETTINGS_SaveSettings(void)
State[2] = gEeprom.REPEATER_TAIL_TONE_ELIMINATION;
State[3] = gEeprom.TX_VFO;
State[4] = gEeprom.BATTERY_TYPE;
EEPROM_WriteBuffer(0x0EA8, State);
// 0x0ED0
State = SecBuf + 0x40;
State[0] = gEeprom.DTMF_SIDE_TONE;
#ifdef ENABLE_DTMF_CALLING
State[1] = gEeprom.DTMF_SEPARATE_CODE;
@@ -722,16 +761,24 @@ void SETTINGS_SaveSettings(void)
State[5] = gEeprom.DTMF_PRELOAD_TIME / 10U;
State[6] = gEeprom.DTMF_FIRST_CODE_PERSIST_TIME / 10U;
State[7] = gEeprom.DTMF_HASH_CODE_PERSIST_TIME / 10U;
EEPROM_WriteBuffer(0x0ED0, State);
memset(State, 0xFF, sizeof(State));
// 0x0ED8
State = SecBuf + 0x48;
State[0] = gEeprom.DTMF_CODE_PERSIST_TIME / 10U;
State[1] = gEeprom.DTMF_CODE_INTERVAL_TIME / 10U;
#ifdef ENABLE_DTMF_CALLING
State[2] = gEeprom.PERMIT_REMOTE_KILL;
#endif
EEPROM_WriteBuffer(0x0ED8, State);
PY25Q16_WriteBuffer(0x007000, SecBuf, 0x50, true);
// -------------------------
// 0f18 - 0f20
memset(SecBuf, 0xff, 0x8);
// 0x0F18
State = SecBuf;
State[0] = gEeprom.SCAN_LIST_DEFAULT;
tmp = 0;
@@ -750,9 +797,16 @@ void SETTINGS_SaveSettings(void)
State[5] = gEeprom.SCANLIST_PRIORITY_CH2[1];
State[6] = gEeprom.SCANLIST_PRIORITY_CH1[2];
State[7] = gEeprom.SCANLIST_PRIORITY_CH2[2];
EEPROM_WriteBuffer(0x0F18, State);
memset(State, 0xFF, sizeof(State));
PY25Q16_WriteBuffer(0x009000, SecBuf, 8, true);
// ---------------------
// 0f40 - 0f48
memset(SecBuf, 0xff, 8);
// 0x0F40
State = SecBuf;
State[0] = gSetting_F_LOCK;
#ifndef ENABLE_FEAT_F4HWN
State[1] = gSetting_350TX;
@@ -784,12 +838,15 @@ void SETTINGS_SaveSettings(void)
#endif
State[7] = (State[7] & ~(3u << 6)) | ((gSetting_backlight_on_tx_rx & 3u) << 6);
EEPROM_WriteBuffer(0x0F40, State);
PY25Q16_WriteBuffer(0x00b000, SecBuf, 8, true);
// ------------------
#ifdef ENABLE_FEAT_F4HWN
// 0x1FF0
State = SecBuf;
// TODO: TBD
PY25Q16_ReadBuffer(0x00c000, State, sizeof(State));
PY25Q16_ReadBuffer(0x00c000, State, 8);
//memset(State, 0xFF, sizeof(State));
@@ -830,7 +887,7 @@ void SETTINGS_SaveSettings(void)
gEeprom.KEY_LOCK_PTT = gSetting_set_lck;
EEPROM_WriteBuffer(0x1FF0, State);
PY25Q16_WriteBuffer(0x00c000, SecBuf, 8, true);
#endif
#ifdef ENABLE_FEAT_F4HWN_VOL
@@ -845,45 +902,53 @@ void SETTINGS_SaveChannel(uint8_t Channel, uint8_t VFO, const VFO_Info_t *pVFO,
return;
#endif
uint16_t OffsetVFO = Channel * 16;
// 0
uint16_t OffsetVFO = 0 + Channel * 16;
if (IS_FREQ_CHANNEL(Channel)) { // it's a VFO, not a channel
OffsetVFO = (VFO == 0) ? 0x0C80 : 0x0C90;
// 0x0C80
OffsetVFO = (VFO == 0) ? 0x001000 : 0x001010;
OffsetVFO += (Channel - FREQ_CHANNEL_FIRST) * 32;
}
if (Mode >= 2 || IS_FREQ_CHANNEL(Channel)) { // copy VFO to a channel
union {
typedef union {
uint8_t _8[8];
uint32_t _32[2];
} State;
} State_t;
State_t *State;
State._32[0] = pVFO->freq_config_RX.Frequency;
State._32[1] = pVFO->TX_OFFSET_FREQUENCY;
EEPROM_WriteBuffer(OffsetVFO + 0, State._32);
uint8_t Buf[0x10];
State._8[0] = pVFO->freq_config_RX.Code;
State._8[1] = pVFO->freq_config_TX.Code;
State._8[2] = (pVFO->freq_config_TX.CodeType << 4) | pVFO->freq_config_RX.CodeType;
State._8[3] = (pVFO->Modulation << 4) | pVFO->TX_OFFSET_FREQUENCY_DIRECTION;
State._8[4] = 0
State = (State_t *)Buf;
State -> _32[0] = pVFO->freq_config_RX.Frequency;
State -> _32[1] = pVFO->TX_OFFSET_FREQUENCY;
State = (State_t *)(Buf + 0x8);
State -> _8[0] = pVFO->freq_config_RX.Code;
State -> _8[1] = pVFO->freq_config_TX.Code;
State -> _8[2] = (pVFO->freq_config_TX.CodeType << 4) | pVFO->freq_config_RX.CodeType;
State -> _8[3] = (pVFO->Modulation << 4) | pVFO->TX_OFFSET_FREQUENCY_DIRECTION;
State -> _8[4] = 0
| (pVFO->TX_LOCK << 6)
| (pVFO->BUSY_CHANNEL_LOCK << 5)
| (pVFO->OUTPUT_POWER << 2)
| (pVFO->CHANNEL_BANDWIDTH << 1)
| (pVFO->FrequencyReverse << 0);
State._8[5] = ((pVFO->DTMF_PTT_ID_TX_MODE & 7u) << 1)
State -> _8[5] = ((pVFO->DTMF_PTT_ID_TX_MODE & 7u) << 1)
#ifdef ENABLE_DTMF_CALLING
| ((pVFO->DTMF_DECODING_ENABLE & 1u) << 0)
#endif
;
State._8[6] = pVFO->STEP_SETTING;
State -> _8[6] = pVFO->STEP_SETTING;
#ifdef ENABLE_FEAT_F4HWN
State._8[7] = 0;
State -> _8[7] = 0;
#else
State._8[7] = pVFO->SCRAMBLING_TYPE;
State -> _8[7] = pVFO->SCRAMBLING_TYPE;
#endif
EEPROM_WriteBuffer(OffsetVFO + 8, State._8);
PY25Q16_WriteBuffer(OffsetVFO, Buf, 0x10, false);
SETTINGS_UpdateChannel(Channel, pVFO, true, true, true);
@@ -903,13 +968,14 @@ void SETTINGS_SaveChannel(uint8_t Channel, uint8_t VFO, const VFO_Info_t *pVFO,
void SETTINGS_SaveBatteryCalibration(const uint16_t * batteryCalibration)
{
uint16_t buf[4];
EEPROM_WriteBuffer(0x1F40, batteryCalibration);
uint16_t buf[8];
memcpy(buf, batteryCalibration, 8);
// 0x1F48
PY25Q16_ReadBuffer(0x010000 + 0x148, buf, sizeof(buf));
PY25Q16_ReadBuffer(0x010000 + 0x148, buf + 4, 8);
buf[0] = batteryCalibration[4];
buf[1] = batteryCalibration[5];
EEPROM_WriteBuffer(0x1F48, buf);
// 0x1F40
PY25Q16_WriteBuffer(0x010000, buf, 0x10, false);
}
void SETTINGS_SaveChannelName(uint8_t channel, const char * name)
@@ -917,8 +983,8 @@ void SETTINGS_SaveChannelName(uint8_t channel, const char * name)
uint16_t offset = channel * 16;
uint8_t buf[16] = {0};
memcpy(buf, name, MIN(strlen(name), 10u));
EEPROM_WriteBuffer(0x0F50 + offset, buf);
EEPROM_WriteBuffer(0x0F58 + offset, buf + 8);
// 0x0F50
PY25Q16_WriteBuffer(0x00e000 + offset, buf, 0x10, false);
}
void SETTINGS_UpdateChannel(uint8_t channel, const VFO_Info_t *pVFO, bool keep, bool check, bool save)
@@ -927,7 +993,7 @@ void SETTINGS_UpdateChannel(uint8_t channel, const VFO_Info_t *pVFO, bool keep,
if (!IS_NOAA_CHANNEL(channel))
#endif
{
uint8_t state[8];
ChannelAttributes_t state;
ChannelAttributes_t att = {
.band = 0x7,
.compander = 0,
@@ -937,8 +1003,7 @@ void SETTINGS_UpdateChannel(uint8_t channel, const VFO_Info_t *pVFO, bool keep,
}; // default attributes
// 0x0D60
uint16_t offset = 0x002000 + (channel & ~7u);
PY25Q16_ReadBuffer(offset, state, sizeof(state));
PY25Q16_ReadBuffer(0x002000 + channel, &state, 1);
if (keep) {
att.band = pVFO->Band;
@@ -946,20 +1011,22 @@ void SETTINGS_UpdateChannel(uint8_t channel, const VFO_Info_t *pVFO, bool keep,
att.scanlist2 = pVFO->SCANLIST2_PARTICIPATION;
att.scanlist3 = pVFO->SCANLIST3_PARTICIPATION;
att.compander = pVFO->Compander;
if (check && state[channel & 7u] == att.__val)
if (check && state.__val == att.__val)
return; // no change in the attributes
}
state[channel & 7u] = att.__val;
state.__val = att.__val;
#ifdef ENABLE_FEAT_F4HWN
#ifndef ENABLE_FEAT_F4HWN
save = true;
#endif
if(save)
{
EEPROM_WriteBuffer(offset, state);
uint8_t buf[224];
PY25Q16_ReadBuffer(0x002000, buf, sizeof(buf));
buf[channel] = state.__val;
PY25Q16_WriteBuffer(0x002000, buf, sizeof(buf), true);
}
#else
EEPROM_WriteBuffer(offset, state);
#endif
gMR_ChannelAttributes[channel] = att;
@@ -1031,18 +1098,18 @@ State[1] = 0
| (1 << 6)
#endif
;
EEPROM_WriteBuffer(0x1FF0, State);
PY25Q16_WriteBuffer(0x00c000, State, sizeof(State), true);
}
#ifdef ENABLE_FEAT_F4HWN_RESUME_STATE
void SETTINGS_WriteCurrentState(void)
{
uint8_t State[8];
uint8_t State[0x10];
// 0x0E78
PY25Q16_ReadBuffer(0x004000 + 0x8, State, sizeof(State));
//State[3] = (gEeprom.CURRENT_STATE << 4) | (gEeprom.BATTERY_SAVE & 0x0F);
State[7] = (gEeprom.VFO_OPEN & 0x01) | ((gEeprom.CURRENT_STATE & 0x07) << 1) | ((gEeprom.SCAN_LIST_DEFAULT & 0x07) << 4);
EEPROM_WriteBuffer(0x0E78, State);
PY25Q16_ReadBuffer(0x004000, State, sizeof(State));
//State[11] = (gEeprom.CURRENT_STATE << 4) | (gEeprom.BATTERY_SAVE & 0x0F);
State[15] = (gEeprom.VFO_OPEN & 0x01) | ((gEeprom.CURRENT_STATE & 0x07) << 1) | ((gEeprom.SCAN_LIST_DEFAULT & 0x07) << 4);
PY25Q16_WriteBuffer(0x004000, State, sizeof(State), true);
}
#endif
@@ -1053,20 +1120,37 @@ State[1] = 0
// 0x1F88
PY25Q16_ReadBuffer(0x010000 + 0x188, State, sizeof(State));
State[6] = gEeprom.VOLUME_GAIN;
EEPROM_WriteBuffer(0x1F88, State);
PY25Q16_WriteBuffer(0x010000 + 0x188, State, sizeof(State), false);
}
#endif
#ifdef ENABLE_FEAT_F4HWN
void SETTINGS_ResetTxLock(void)
{
uint8_t State[8];
for(uint8_t channel = 0; channel < 200; channel++)
// TODO: This is expensive operation!
#define SETTINGS_ResetTxLock_BATCH 10
uint8_t Buf[0xc80 / SETTINGS_ResetTxLock_BATCH];
const uint32_t BatchSize = 0xc80 / SETTINGS_ResetTxLock_BATCH;
const uint32_t BatchChCnt = BatchSize / 0x10;
for (uint32_t i = 0; i < SETTINGS_ResetTxLock_BATCH; i++)
{
uint16_t OffsetVFO = channel * 16;
PY25Q16_ReadBuffer(OffsetVFO + 8, State, sizeof(State));
State[4] |= (1 << 6);
EEPROM_WriteBuffer(OffsetVFO + 8, State);
uint32_t Offset = i * BatchSize;
PY25Q16_ReadBuffer(0 + Offset, Buf, sizeof(Buf));
uint8_t *State;
for (uint8_t channel = 0; channel < BatchChCnt; channel++)
{
uint16_t OffsetVFO = channel * 16;
State = Buf + OffsetVFO;
State[4] |= (1 << 6);
}
PY25Q16_WriteBuffer(0 + Offset, Buf, sizeof(Buf), false);
}
#undef SETTINGS_ResetTxLock_BATCH
}
#endif
+3 -1
View File
@@ -50,12 +50,14 @@ else()
endif()
endif()
if(${TARGET})
if(TARGET)
set(EXE_NAME ${TARGET})
else()
set(EXE_NAME "firmware")
endif()
message("EXE_NAME = " ${EXE_NAME})
add_executable(${EXE_NAME})
add_subdirectory(Drivers)
+1
View File
@@ -75,6 +75,7 @@
"name": "Custom",
"inherits": "Broadcast",
"cacheVariables": {
"ENABLE_VOICE": false,
"ENABLE_SWD": true,
"ENABLE_AIRCOPY": false
}