mirror of
https://github.com/silenty4ng/k5web
synced 2025-04-02 05:35:10 +00:00
344 lines
16 KiB
Vue
344 lines
16 KiB
Vue
<template>
|
||
<div class="container">
|
||
<Breadcrumb :items="['指南', '使用117P6版']" />
|
||
<a-row :gutter="20" align="stretch">
|
||
<a-col :span="24">
|
||
<a-card class="general-card" title="使用117P6版">
|
||
<a-steps :current="state.step">
|
||
<a-step>选择固件类型</a-step>
|
||
<a-step>使用自定义固件</a-step>
|
||
<a-step>完成</a-step>
|
||
</a-steps>
|
||
<a-divider/>
|
||
<div v-show="state.step == 1" style="min-height: 300px; margin: 50px;">
|
||
<p style="font-size: 1.2em; font-weight: bold;">你是否想要扩容你的设备以支持中文信道存储:</p>
|
||
<a-radio-group type="button" v-model="state.kIt">
|
||
<a-radio value="yes">是的,我想要硬件改造我的设备</a-radio>
|
||
<a-radio value="no">不,我不需要中文信道</a-radio>
|
||
</a-radio-group>
|
||
<br>
|
||
<div v-show="state.kIt == 'yes'">
|
||
<p style="color: #ff0000; font-weight: bold;">声明:本方案及相应固件均为技术探索用途。对原机进行改造需要相应专业知识且可能存在安全和法律风险。使用本方案和固件代表您已认可风险并自行承担后果,包括且不限于财产损失、人员伤亡、违法犯罪等。</p>
|
||
<p style="font-weight: bold; font-size: 1.2em;">一、相关法律</p>
|
||
<p>
|
||
自制、改装、拼装的无线电发射设备,应符合国家相关技术标准,并按照工信部官网链接(<a href="http://www.miit.gov.cn/jgsj/wgj/kpzs/art/2022/art_c1ffd3c47e3f455dad38246579092136.html" target="_blank" rel="noopener noreferrer">http://www.miit.gov.cn/jgsj/wgj/kpzs/art/2022/art_c1ffd3c47e3f455dad38246579092136.html</a>,或通过工信部官网-工业和信息化部-机关司局-无管局-科普知识-《关于申请设置、使用业余无线电台所用无线电发射设备相关事宜的说明》)所列情况,提供相应材料。
|
||
</p>
|
||
<p style="font-weight: bold; font-size: 1.2em;">二、所需工具</p>
|
||
<p>
|
||
写频线、螺丝刀套装、电烙铁、焊锡丝、助焊剂、精密电器清洁剂(可选)、撬棒(可选)、万用表(可选)。
|
||
</p>
|
||
<p style="font-weight: bold; font-size: 1.2em;">三、购买扩容芯片</p>
|
||
<p>芯片型号:<span style="color: #ff0000;">M24M02-DRMN6</span> 或其它 SOP-8 封装 I²C 总线 2Mbit EEPROM 芯片。考虑到芯片质量及操作失误可能导致的损坏,建议多买几片备用。</p>
|
||
<p>
|
||
该教程由 BH3RVG 火星人整理提供。
|
||
</p>
|
||
</div>
|
||
<div v-show="state.kIt == 'no'">
|
||
<p style="color: #ff0000; font-weight: bold;">声明:本方案及相应固件均为技术探索用途。对原机进行改造需要相应专业知识且可能存在安全和法律风险。使用本方案和固件代表您已认可风险并自行承担后果,包括且不限于财产损失、人员伤亡、违法犯罪等。</p>
|
||
<p style="font-weight: bold; font-size: 1.2em;">一、所需工具</p>
|
||
<p>
|
||
写频线。
|
||
</p>
|
||
<p>
|
||
该教程由 BH3RVG 火星人整理提供。
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div v-show="state.step == 1" style="text-align: center;">
|
||
<a-button type="primary" :disabled="!state.kIt" @click="state.step = 2">下一步</a-button>
|
||
</div>
|
||
<div v-show="state.step == 2" style="min-height: 300px; text-align: center; color: #C9CDD4; ">
|
||
<a-spin :loading="state.loading" tip="处理中..." style="width: 100%;">
|
||
<a-collapse :activeKey="state.flashStep" @change="(e: any)=>{state.flashStep = e; if(e[0] == 'six'){state.finish = true;}}" accordion>
|
||
<a-collapse-item header="备份原机数据" key="one">
|
||
<div style="text-align: left;">
|
||
<p>在对设备进行操作前,应备份原机出厂的配置、校准数据,以保证发射性能符合国家标准。(请妥善保存备份文件)</p>
|
||
<p>
|
||
<a-space>
|
||
<a-button type="primary" :disabled="state.backed" @click="backupIt">备份</a-button><span v-show="state.backed" style="color: #C9CDD4;">已备份✔</span>
|
||
<a-button type="primary" :disabled="!state.backed" @click="state.kIt == 'yes' ? state.flashStep[0] = 'two' : state.flashStep[0] = 'three'">下一步</a-button>
|
||
</a-space>
|
||
</p>
|
||
</div>
|
||
</a-collapse-item>
|
||
<a-collapse-item v-show="state.kIt == 'yes'" header="拆机" key="two">
|
||
<div style="text-align: left;">
|
||
<p>视频教程链接:
|
||
<a href="https://www.bilibili.com/video/BV1ib4y137Ah" target="_blank" rel="noopener noreferrer">https://www.bilibili.com/video/BV1ib4y137Ah</a>
|
||
<span style="color: #ff0000;">(硬件拆解及焊接部分可参照本视频,软件刷机部分请务必以本指南为准。)</span>
|
||
</p>
|
||
<div>
|
||
①拆掉电池、天线和旋钮盖。<br>
|
||
②用撬棒插入主机背面底部正中位置缝隙,向上撬出铝制背板。③向下后方抽出背板。前盖和主板间有扬声器导线连接,此处用力不要过猛,控制幅度,以免拉断导线。<br>
|
||
④拿掉耳机口挡板。<br>
|
||
⑤建议将扬声器导线拆焊,以免阻碍后续拆解和焊接,导致拉断导线。最后组装时再对扬声器导线进行焊接。<br>
|
||
⑥拆卸屏幕(难点,请认真看视频教程!)。在屏幕左下角卡扣位置,用撬棒向内按压同时向上抬起即可拆卸左侧,屏幕左侧松脱后另一侧拆卸相对简单。此处用力不要过猛,控制幅度,屏幕完全拆卸后应妥善固定,以免拉断、折断背面上方排线。<br>
|
||
⑦拆卸全部 5 颗螺丝并分离背板。
|
||
</div>
|
||
<img :src="cj1">
|
||
<div>
|
||
需要更换的芯片位于主板背面右下角,型号为 BL24C64A。
|
||
</div>
|
||
<img :src="cj2">
|
||
<div>
|
||
①拆焊及焊接。有动手能力的朋友自行操作,要求芯片方向正确(以第1 脚圆点为准),焊点饱满,无虚焊、短路,芯片周围的电子元器件保持完好。建议用300℃以上小刀头烙铁配合助焊剂,不建议用热风枪(高手除外)。手残党可以去手机维修店更换,费用5-30 元不等。若周围元件遭到破坏,可按下图参数更换补救。
|
||
</div>
|
||
<img :src="cj3">
|
||
<div>
|
||
②将主板装回背板。(背板上 3x5mm 导热硅胶垫若脱落,请务必装回对应凸起位置;电池触点部分过背板孔时当心压弯。)<br>
|
||
③装入电池,按住 PTT 键开机,进入刷机模式(此时手电筒常亮、屏幕无显示)。如无法进入刷机模式,检查电池接触片是否错位、焊点及周边元件是否完好。
|
||
</div>
|
||
<div style="color: #ff0000; font-weight: bold;">
|
||
此时先不要完全组装手台,待后续工作全部完成后再行组装,以便故障返工。此阶段如尝试正常开机后显示异常、电量异常、接收异常等均为正常情况,不用担心。后续操作后会恢复正常。
|
||
</div>
|
||
<div>
|
||
<a-button type="primary" @click="state.flashStep[0] = 'three'">下一步</a-button>
|
||
</div>
|
||
</div>
|
||
</a-collapse-item>
|
||
<a-collapse-item header="刷入固件" key="three">
|
||
<div style="text-align: left;">
|
||
<p>断开写频线,按住 PTT 键开机,进入刷机模式(此时手电筒常亮、屏幕无显示),手电筒常亮后插回写频线。</p>
|
||
<p>
|
||
<a-space>
|
||
<a-button type="primary" :disabled="state.flashIt" @click="iFlashIt">刷入固件</a-button>
|
||
<a-button type="primary" :disabled="!state.flashIt" @click="state.kIt == 'yes' ? state.flashStep[0] = 'four' : finishIt()">下一步</a-button>
|
||
</a-space>
|
||
</p>
|
||
</div>
|
||
</a-collapse-item>
|
||
<a-collapse-item v-show="state.kIt == 'yes'" header="刷回原机数据" key="four">
|
||
<div style="text-align: left;">
|
||
<p>正常开机,使设备处于开机状态,点击刷回备份数据。</p>
|
||
<p>
|
||
<a-space>
|
||
<a-button type="primary" :disabled="state.restoreBackupIt" @click="restoreBackup">刷回备份数据</a-button>
|
||
<a-button type="primary" :disabled="!state.restoreBackupIt" @click="state.flashStep[0] = 'five'">下一步</a-button>
|
||
</a-space>
|
||
</p>
|
||
</div>
|
||
</a-collapse-item>
|
||
<a-collapse-item v-show="state.kIt == 'yes'" header="刷入字库" key="five">
|
||
<div style="text-align: left;">
|
||
<p>正常开机,使设备处于开机状态,点击刷入字库。</p>
|
||
<p>
|
||
<a-space>
|
||
<a-button type="primary" :disabled="state.flashFontIt" @click="flashFont">刷入字库</a-button>
|
||
<a-button type="primary" :disabled="!state.flashFontIt" @click="finishIt">下一步</a-button>
|
||
</a-space>
|
||
</p>
|
||
</div>
|
||
</a-collapse-item>
|
||
<a-collapse-item header="完全组装" key="six">
|
||
<div style="text-align: left;">如果扩容,按拆解顺序进行焊接和组装。</div>
|
||
</a-collapse-item>
|
||
</a-collapse>
|
||
</a-spin>
|
||
</div>
|
||
<div v-show="state.step == 2" style="text-align: center;">
|
||
<a-space>
|
||
<a-button @click="state.step = 1">上一步</a-button>
|
||
<a-button type="primary" :disabled="!state.finish" @click="state.step = 3">完成</a-button>
|
||
</a-space>
|
||
</div>
|
||
<div v-show="state.step == 3" style="min-height: 300px; text-align: center; color: #C9CDD4; ">
|
||
<a-result
|
||
class="result"
|
||
status="success"
|
||
subtitle="刷入成功"
|
||
/>
|
||
<a-button type="primary" @click="()=>{router.push('/chirp/base');}">
|
||
返回首页
|
||
</a-button>
|
||
</div>
|
||
</a-card>
|
||
</a-col>
|
||
</a-row>
|
||
</div>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import { reactive, nextTick } from 'vue';
|
||
import { useRouter, useRoute } from 'vue-router';
|
||
import { useAppStore } from '@/store';
|
||
import { eeprom_write, eeprom_reboot, eeprom_init, eeprom_read, flash_flashFirmware, connect, disconnect, sendPacket, unpackVersion, readPacket, unpack } from '@/utils/serial.js';
|
||
import cj1 from './assets/cj1.png'
|
||
import cj2 from './assets/cj2.png'
|
||
import cj3 from './assets/cj3.png'
|
||
|
||
const appStore = useAppStore();
|
||
const router = useRouter();
|
||
|
||
const state : {
|
||
step: any,
|
||
flashStep: any,
|
||
backed: any,
|
||
kIt: any,
|
||
flashIt: any,
|
||
restoreBackupIt: any,
|
||
loading: any,
|
||
flashFontIt: any,
|
||
finish: any
|
||
} = reactive({
|
||
step: 1,
|
||
flashStep: ['one'],
|
||
backed: undefined,
|
||
kIt: undefined,
|
||
flashIt: false,
|
||
restoreBackupIt: false,
|
||
loading: false,
|
||
flashFontIt: false,
|
||
finish: false
|
||
})
|
||
|
||
const backupRange = async (start: any, end: any, name: any = new Date() + '_backup.bin') =>{
|
||
await eeprom_init(appStore.connectPort);
|
||
let rawEEPROM = new Uint8Array(end - start);
|
||
for (let i = start; i < end; i += 0x40) {
|
||
const data = await eeprom_read(appStore.connectPort, i, 0x40, appStore.configuration?.uart);
|
||
rawEEPROM.set(data, i - start);
|
||
}
|
||
const blob = new Blob([rawEEPROM], { type: 'application/octet-stream' });
|
||
const url = URL.createObjectURL(blob);
|
||
const a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = name;
|
||
document.body.appendChild(a);
|
||
a.click();
|
||
document.body.removeChild(a);
|
||
URL.revokeObjectURL(url);
|
||
return rawEEPROM;
|
||
}
|
||
|
||
const restoreRange = async (start: any = 0, uint8Array: any) => {
|
||
await eeprom_init(appStore.connectPort);
|
||
for (let i = start; i < uint8Array.length + start; i += 0x40) {
|
||
await eeprom_write(appStore.connectPort, i, uint8Array.slice(i - start, i - start + 0x40), uint8Array.slice(i - start, i - start + 0x40).length, appStore.configuration?.uart);
|
||
}
|
||
await eeprom_reboot(appStore.connectPort);
|
||
}
|
||
|
||
const finishIt = () => {
|
||
state.flashStep[0] = 'six'
|
||
state.finish = true
|
||
}
|
||
|
||
const backupIt = async () => {
|
||
if(appStore.connectState != true){alert('点击右上角“连接”按钮连接手台。'); return;};
|
||
state.loading = true
|
||
state.backed = await backupRange(0, 0x2000);
|
||
console.log(state.backed)
|
||
state.loading = false
|
||
}
|
||
|
||
const iFlashIt = async () => {
|
||
state.loading = true
|
||
let fontPacket = undefined
|
||
if(state.kIt == 'yes'){
|
||
fontPacket = await fetch('/LOSEHU117P6K.bin')
|
||
}else{
|
||
fontPacket = await fetch('/LOSEHU117P6.bin')
|
||
}
|
||
const reader = fontPacket.body.getReader();
|
||
const chunks = [];
|
||
while(true) {
|
||
const {done, value} = await reader.read();
|
||
if (done) {
|
||
break;
|
||
}
|
||
chunks.push(...value)
|
||
}
|
||
const binary = new Uint8Array(chunks)
|
||
if(appStore.connectPort){
|
||
await disconnect(appStore.connectPort);
|
||
}
|
||
let _connect = await connect();
|
||
await readPacket(_connect, 0x18, 1000);
|
||
const rawVersion = unpackVersion(binary);
|
||
const _data = new Uint8Array([0x30, 0x5, rawVersion.length, 0x0, ...rawVersion]);
|
||
await sendPacket(_connect, _data);
|
||
await readPacket(_connect, 0x18)
|
||
await flash_flashFirmware(_connect, unpack(binary))
|
||
appStore.updateSettings({ connectPort: _connect });
|
||
state.flashIt = true
|
||
state.loading = false
|
||
}
|
||
|
||
const restoreBackup = async () => {
|
||
if(appStore.connectState != true){alert('点击右上角“连接”按钮连接手台。'); return;};
|
||
state.loading = true
|
||
await restoreRange(0, state.backed);
|
||
state.restoreBackupIt = true;
|
||
state.loading = false
|
||
}
|
||
|
||
const flashFont = async () => {
|
||
state.loading = true
|
||
const fontPacket = await fetch('/old_font.bin')
|
||
const reader = fontPacket.body.getReader();
|
||
const chunks = [];
|
||
while(true) {
|
||
const {done, value} = await reader.read();
|
||
if (done) {
|
||
break;
|
||
}
|
||
chunks.push(...value)
|
||
}
|
||
const binary = new Uint8Array(chunks)
|
||
await restoreRange(0x02000, binary)
|
||
state.flashFontIt = true;
|
||
state.loading = false
|
||
}
|
||
|
||
</script>
|
||
|
||
<script lang="ts">
|
||
export default {
|
||
name: 'Chi',
|
||
};
|
||
</script>
|
||
|
||
<style scoped lang="less">
|
||
.container {
|
||
padding: 0 20px 20px 20px;
|
||
:deep(.arco-list-content) {
|
||
overflow-x: hidden;
|
||
}
|
||
|
||
:deep(.arco-card-meta-title) {
|
||
font-size: 14px;
|
||
}
|
||
}
|
||
:deep(.arco-list-col) {
|
||
display: flex;
|
||
flex-direction: row;
|
||
flex-wrap: wrap;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
:deep(.arco-list-item) {
|
||
width: 33%;
|
||
}
|
||
|
||
:deep(.block-title) {
|
||
margin: 0 0 12px 0;
|
||
font-size: 14px;
|
||
}
|
||
:deep(.list-wrap) {
|
||
// min-height: 140px;
|
||
.list-row {
|
||
align-items: stretch;
|
||
.list-col {
|
||
margin-bottom: 16px;
|
||
}
|
||
}
|
||
:deep(.arco-space) {
|
||
width: 100%;
|
||
.arco-space-item {
|
||
&:last-child {
|
||
flex: 1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|