1、  触摸屏驱动

触摸屏驱动的原理非常简单,从硬件得到坐标数据,数据加工(适配屏幕分辨率,偏移量调整),最后调用rtgui_server_post_event()函数向GUI服务端发送坐标信息。

奋斗板V3,使用的AD芯片是XPT2046,是RTGUI bsp/stm32f10x下的ADS7846芯片的下一代产品,功能及硬件连接上完全兼容。因此,我们只要确认与MCU的管脚连接,在代码上正确配置即可。

注:奋斗板的源代码是以模数转换(ADS)方式直接读取的,而RTGUI里,则是以SPI方式读取,并采用中断(EXT INT)方式。另外,SPI1上接了三个设备,分别是触摸屏,10M LAN, 2M Flash,使用了三根MCU管脚作为片选(CS)。RTT中,使用的CS脚是PC4,IRQ是PB1,而奋斗板的CS是PB7,IRQ是PB6。

驱动代码对比:

touch.c

touch.rtt.c

1

#include <stdbool.h>

1

#include <stdbool.h>

2

#include "stm32f10x.h"

2

#include "stm32f10x.h"

3

3

4

#include "board.h"

4

#include "board.h"

5

#include "touch.h"

5

#include "touch.h"

6

6

7

#include <rtthread.h>

7

#include <rtthread.h>

8

#include <rtgui/event.h>

8

#include <rtgui/event.h>

9

#include <rtgui/kbddef.h>

9

#include <rtgui/kbddef.h>

10

#include <rtgui/rtgui_server.h>

10

#include <rtgui/rtgui_server.h>

11

#include <rtgui/rtgui_system.h>

11

#include <rtgui/rtgui_system.h>

12

12

13

/*

13

/*

14

MISO PA6

14

MISO PA6

15

MOSI PA7

15

MOSI PA7

16

CLK  PA5

16

CLK  PA5

17

CS   PB7

17

CS   PC4

18

*/

18

*/

19

19

20

#define   CS_0()          GPIO_ResetBits(GPIOB,GPIO_Pin_7)

20

#define   CS_0()          GPIO_ResetBits(GPIOC,GPIO_Pin_4)

21

#define   CS_1()          GPIO_SetBits(GPIOB,GPIO_Pin_7)

21

#define   CS_1()          GPIO_SetBits(GPIOC,GPIO_Pin_4)

22

22

23

/*

23

/*

24

7  6 - 4  3      2     1-0

24

7  6 - 4  3      2     1-0

25

s  A2-A0 MODE SER/DFR PD1-PD0

25

s  A2-A0 MODE SER/DFR PD1-PD0

26

*/

26

*/

27

#define TOUCH_MSR_Y  0x90   //读X轴坐标指令 addr:1

27

#define TOUCH_MSR_Y  0x90   //读X轴坐标指令 addr:1

28

#define TOUCH_MSR_X  0xD0   //读Y轴坐标指令 addr:3

28

#define TOUCH_MSR_X  0xD0   //读Y轴坐标指令 addr:3

29

29

30

structrtgui_touch_device

30

structrtgui_touch_device

31

{

31

{

32

structrt_deviceparent;

32

structrt_deviceparent;

33

33

34

rt_timer_tpoll_timer;

34

rt_timer_tpoll_timer;

35

rt_uint16_tx, y;

35

rt_uint16_tx, y;

36

36

37

rt_bool_tcalibrating;

37

rt_bool_tcalibrating;

38

rt_touch_calibration_func_tcalibration_func;

38

rt_touch_calibration_func_tcalibration_func;

39

39

40

rt_uint16_tmin_x, max_x;

40

rt_uint16_tmin_x, max_x;

41

rt_uint16_tmin_y, max_y;

41

rt_uint16_tmin_y, max_y;

42

};

42

};

43

staticstructrtgui_touch_device *touch = RT_NULL;

43

staticstructrtgui_touch_device *touch = RT_NULL;

44

44

45

externunsignedcharSPI_WriteByte(unsignedchardata);

45

externunsignedcharSPI_WriteByte(unsignedchardata);

46

rt_inlinevoidEXTI_Enable(rt_uint32_tenable);

46

rt_inlinevoidEXTI_Enable(rt_uint32_tenable);

47

47

48

structrt_semaphorespi1_lock;

48

structrt_semaphorespi1_lock;

49

49

50

voidrt_hw_spi1_baud_rate(uint16_tSPI_BaudRatePrescaler)

50

voidrt_hw_spi1_baud_rate(uint16_tSPI_BaudRatePrescaler)

51

{

51

{

52

SPI1->CR1 &= ~SPI_BaudRatePrescaler_256;

52

SPI1->CR1 &= ~SPI_BaudRatePrescaler_256;

53

SPI1->CR1 |= SPI_BaudRatePrescaler;

53

SPI1->CR1 |= SPI_BaudRatePrescaler;

54

}

54

}

55

55

56

uint8_tSPI_WriteByte(unsignedchardata)

56

uint8_tSPI_WriteByte(unsignedchardata)

57

{

57

{

58

//Wait until the transmit buffer is empty

58

//Wait until the transmit buffer is empty

59

while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);

59

while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);

60

// Send the byte

60

// Send the byte

61

SPI_I2S_SendData(SPI1, data);

61

SPI_I2S_SendData(SPI1, data);

62

62

63

//Wait until a data is received

63

//Wait until a data is received

64

while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);

64

while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);

65

// Get the received data

65

// Get the received data

66

data = SPI_I2S_ReceiveData(SPI1);

66

data = SPI_I2S_ReceiveData(SPI1);

67

67

68

// Return the shifted data

68

// Return the shifted data

69

returndata;

69

returndata;

70

}

70

}

71

71

72

//SPI写数据

72

//SPI写数据

73

staticvoidWriteDataTo7843(unsignedcharnum)

73

staticvoidWriteDataTo7843(unsignedcharnum)

74

{

74

{

75

SPI_WriteByte(num);

75

SPI_WriteByte(num);

76

}

76

}

77

77

78

#define X_WIDTH 480

78

#define X_WIDTH 240

79

#define Y_WIDTH 272

79

#define Y_WIDTH 320

80

80

81

staticvoidrtgui_touch_calculate()

81

staticvoidrtgui_touch_calculate()

82

{

82

{

83

if (touch != RT_NULL)

83

if (touch != RT_NULL)

84

{

84

{

85

rt_sem_take(&spi1_lock, RT_WAITING_FOREVER);

85

rt_sem_take(&spi1_lock, RT_WAITING_FOREVER);

86

/* SPI1 configure */

86

/* SPI1 configure */

87

rt_hw_spi1_baud_rate(SPI_BaudRatePrescaler_64);/* 72M/64=1.125M */

87

rt_hw_spi1_baud_rate(SPI_BaudRatePrescaler_64);/* 72M/64=1.125M */

88

88

89

//读取触摸值

89

//读取触摸值

90

{

90

{

91

rt_uint16_ttmpx[10];

91

rt_uint16_ttmpx[10];

92

rt_uint16_ttmpy[10];

92

rt_uint16_ttmpy[10];

93

unsignedinti;

93

unsignedinti;

94

94

95

/* From the datasheet:

95

/* From the datasheet:

96

* When the very first CLK after the control byte comes in, the

96

* When the very first CLK after the control byte comes in, the

97

* DOUT of ADS7843 is not valid. So we could only get 7bits from

97

* DOUT of ADS7843 is not valid. So we could only get 7bits from

98

* the first SPI_WriteByte. And the got the following 5 bits from

98

* the first SPI_WriteByte. And the got the following 5 bits from

99

* another SPI_WriteByte.(aligned MSB)

99

* another SPI_WriteByte.(aligned MSB)

100

*/

100

*/

101

for(i=0; i<10; i++)

101

for(i=0; i<10; i++)

102

{

102

{

103

CS_0();

103

CS_0();

104

WriteDataTo7843(TOUCH_MSR_X);

104

WriteDataTo7843(TOUCH_MSR_X);

105

tmpx[i] = (SPI_WriteByte(0x00) & 0x7F) << 5;

105

tmpx[i] = (SPI_WriteByte(0x00) & 0x7F) << 5;

106

tmpx[i] |= (SPI_WriteByte(TOUCH_MSR_Y) >> 3) & 0x1F;

106

tmpx[i] |= (SPI_WriteByte(TOUCH_MSR_Y) >> 3) & 0x1F;

107

107

108

tmpy[i] = (SPI_WriteByte(0x00) & 0x7F) << 5;

108

tmpy[i] = (SPI_WriteByte(0x00) & 0x7F) << 5;

109

tmpy[i] |= (SPI_WriteByte(0x00) >> 3) & 0x1F;

109

tmpy[i] |= (SPI_WriteByte(0x00) >> 3) & 0x1F;

110

110

111

WriteDataTo7843( 1<<7 ); /* 打开中断 */

111

WriteDataTo7843( 1<<7 ); /* 打开中断 */

112

CS_1();

112

CS_1();

113

}

113

}

114

114

115

//去最高值与最低值,再取平均值

115

//去最高值与最低值,再取平均值

116

{

116

{

117

rt_uint32_tmin_x = 0xFFFF,min_y = 0xFFFF;

117

rt_uint32_tmin_x = 0xFFFF,min_y = 0xFFFF;

118

rt_uint32_tmax_x = 0,max_y = 0;

118

rt_uint32_tmax_x = 0,max_y = 0;

119

rt_uint32_ttotal_x = 0;

119

rt_uint32_ttotal_x = 0;

120

rt_uint32_ttotal_y = 0;

120

rt_uint32_ttotal_y = 0;

121

unsignedinti;

121

unsignedinti;

122

122

123

for(i=0;i<10;i++)

123

for(i=0;i<10;i++)

124

{

124

{

125

if( tmpx[i] < min_x )

125

if( tmpx[i] < min_x )

126

{

126

{

127

min_x = tmpx[i];

127

min_x = tmpx[i];

128

}

128

}

129

if( tmpx[i] > max_x )

129

if( tmpx[i] > max_x )

130

{

130

{

131

max_x = tmpx[i];

131

max_x = tmpx[i];

132

}

132

}

133

total_x += tmpx[i];

133

total_x += tmpx[i];

134

134

135

if( tmpy[i] < min_y )

135

if( tmpy[i] < min_y )

136

{

136

{

137

min_y = tmpy[i];

137

min_y = tmpy[i];

138

}

138

}

139

if( tmpy[i] > max_y )

139

if( tmpy[i] > max_y )

140

{

140

{

141

max_y = tmpy[i];

141

max_y = tmpy[i];

142

}

142

}

143

total_y += tmpy[i];

143

total_y += tmpy[i];

144

}

144

}

145

total_x = total_x - min_x - max_x;

145

total_x = total_x - min_x - max_x;

146

total_y = total_y - min_y - max_y;

146

total_y = total_y - min_y - max_y;

147

touch->y = total_x / 8;

147

touch->x = total_x / 8;

148

touch->x = total_y / 8;

148

touch->y = total_y / 8;

149

}//去最高值与最低值,再取平均值

149

}//去最高值与最低值,再取平均值

150

}//读取触摸值

150

}//读取触摸值

151

151

152

rt_sem_release(&spi1_lock);

152

rt_sem_release(&spi1_lock);

153

153

154

rt_kprintf("physical position: (%d, %d)\n", touch->x, touch->y);

155

156

/* if it's not in calibration status  */

154

/* if it's not in calibration status  */

157

if (touch->calibrating != RT_TRUE)

155

if (touch->calibrating != RT_TRUE)

158

{

156

{

159

if (touch->max_x > touch->min_x)

157

if (touch->max_x > touch->min_x)

160

{

158

{

161

touch->x = (touch->x - touch->min_x) * X_WIDTH/(touch->max_x - touch->min_x);

159

touch->x = (touch->x - touch->min_x) * X_WIDTH/(touch->max_x - touch->min_x);

162

}

160

}

163

elseif (touch->max_x < touch->min_x)

161

elseif (touch->max_x < touch->min_x)

164

{

162

{

165

touch->x = (touch->min_x - touch->x) * X_WIDTH/(touch->min_x - touch->max_x);

163

touch->x = (touch->min_x - touch->x) * X_WIDTH/(touch->min_x - touch->max_x);

166

}

164

}

167

165

168

if (touch->max_y > touch->min_y)

166

if (touch->max_y > touch->min_y)

169

{

167

{

170

touch->y = (touch->y - touch->min_y) * Y_WIDTH /(touch->max_y - touch->min_y);

168

touch->y = (touch->y - touch->min_y) * Y_WIDTH /(touch->max_y - touch->min_y);

171

}

169

}

172

elseif (touch->max_y < touch->min_y)

170

elseif (touch->max_y < touch->min_y)

173

{

171

{

174

touch->y = (touch->min_y - touch->y) * Y_WIDTH /(touch->min_y - touch->max_y);

172

touch->y = (touch->min_y - touch->y) * Y_WIDTH /(touch->min_y - touch->max_y);

175

}

173

}

176

174

177

// normalize the data

175

// normalize the data

178

if (touch->x & 0x8000)

176

if (touch->x & 0x8000)

179

touch->x = 0;

177

touch->x = 0;

180

elseif (touch->x > X_WIDTH)

178

elseif (touch->x > X_WIDTH)

181

touch->x = X_WIDTH - 1;

179

touch->x = X_WIDTH - 1;

182

180

183

if (touch->y & 0x8000)

181

if (touch->y & 0x8000)

184

touch->y = 0;

182

touch->y = 0;

185

elseif (touch->y > Y_WIDTH)

183

elseif (touch->y > Y_WIDTH)

186

touch->y = Y_WIDTH - 1;

184

touch->y = Y_WIDTH - 1;

187

}

185

}

188

}

186

}

189

}

187

}

190

188

191

voidtouch_timeout(void* parameter)

189

voidtouch_timeout(void* parameter)

192

{

190

{

193

staticunsignedinttouched_down = 0;

191

staticunsignedinttouched_down = 0;

194

structrtgui_event_mouseemouse;

192

structrtgui_event_mouseemouse;

195

staticstruct_touch_previous

193

staticstruct_touch_previous

196

{

194

{

197

rt_uint32_tx;

195

rt_uint32_tx;

198

rt_uint32_ty;

196

rt_uint32_ty;

199

} touch_previous;

197

} touch_previous;

200

198

201

/* touch time is too short and we lost the position already. */

199

/* touch time is too short and we lost the position already. */

202

if ((!touched_down) && GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_6) != 0)

200

if ((!touched_down) && GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) != 0)

203

return;

201

return;

204

202

205

if (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_6) != 0)

203

if (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) != 0)

206

{

204

{

207

inttmer = RT_TICK_PER_SECOND/8 ;

205

inttmer = RT_TICK_PER_SECOND/8 ;

208

EXTI_Enable(1);

206

EXTI_Enable(1);

209

emouse.parent.type = RTGUI_EVENT_MOUSE_BUTTON;

207

emouse.parent.type = RTGUI_EVENT_MOUSE_BUTTON;

210

emouse.button = (RTGUI_MOUSE_BUTTON_LEFT |RTGUI_MOUSE_BUTTON_UP);

208

emouse.button = (RTGUI_MOUSE_BUTTON_LEFT |RTGUI_MOUSE_BUTTON_UP);

211

209

212

/* use old value */

210

/* use old value */

213

emouse.x = touch->x;

211

emouse.x = touch->x;

214

emouse.y = touch->y;

212

emouse.y = touch->y;

215

213

216

/* stop timer */

214

/* stop timer */

217

rt_timer_stop(touch->poll_timer);

215

rt_timer_stop(touch->poll_timer);

218

rt_kprintf("touch up: (%d, %d)\n", emouse.x, emouse.y);

216

rt_kprintf("touch up: (%d, %d)\n", emouse.x, emouse.y);

219

touched_down = 0;

217

touched_down = 0;

220

218

221

if ((touch->calibrating == RT_TRUE) && (touch->calibration_func != RT_NULL))

219

if ((touch->calibrating == RT_TRUE) && (touch->calibration_func != RT_NULL))

222

{

220

{

223

/* callback function */

221

/* callback function */

224

touch->calibration_func(emouse.x, emouse.y);

222

touch->calibration_func(emouse.x, emouse.y);

225

}

223

}

226

rt_timer_control(touch->poll_timer , RT_TIMER_CTRL_SET_TIME , &tmer);

224

rt_timer_control(touch->poll_timer , RT_TIMER_CTRL_SET_TIME , &tmer);

227

}

225

}

228

else

226

else

229

{

227

{

230

if(touched_down == 0)

228

if(touched_down == 0)

231

{

229

{

232

inttmer = RT_TICK_PER_SECOND/20 ;

230

inttmer = RT_TICK_PER_SECOND/20 ;

233

/* calculation */

231

/* calculation */

234

rtgui_touch_calculate();

232

rtgui_touch_calculate();

235

233

236

/* send mouse event */

234

/* send mouse event */

237

emouse.parent.type = RTGUI_EVENT_MOUSE_BUTTON;

235

emouse.parent.type = RTGUI_EVENT_MOUSE_BUTTON;

238

emouse.parent.sender = RT_NULL;

236

emouse.parent.sender = RT_NULL;

239

237

240

emouse.x = touch->x;

238

emouse.x = touch->x;

241

emouse.y = touch->y;

239

emouse.y = touch->y;

242

240

243

touch_previous.x = touch->x;

241

touch_previous.x = touch->x;

244

touch_previous.y = touch->y;

242

touch_previous.y = touch->y;

245

243

246

/* init mouse button */

244

/* init mouse button */

247

emouse.button = (RTGUI_MOUSE_BUTTON_LEFT |RTGUI_MOUSE_BUTTON_DOWN);

245

emouse.button = (RTGUI_MOUSE_BUTTON_LEFT |RTGUI_MOUSE_BUTTON_DOWN);

248

246

249

//            rt_kprintf("touch down: (%d, %d)\n", emouse.x, emouse.y);

247

//            rt_kprintf("touch down: (%d, %d)\n", emouse.x, emouse.y);

250

touched_down = 1;

248

touched_down = 1;

251

rt_timer_control(touch->poll_timer , RT_TIMER_CTRL_SET_TIME , &tmer);

249

rt_timer_control(touch->poll_timer , RT_TIMER_CTRL_SET_TIME , &tmer);

252

}

250

}

253

else

251

else

254

{

252

{

255

/* calculation */

253

/* calculation */

256

rtgui_touch_calculate();

254

rtgui_touch_calculate();

257

255

258

#define previous_keep      8

256

#define previous_keep      8

259

//判断移动距离是否小于previous_keep,减少误动作.

257

//判断移动距离是否小于previous_keep,减少误动作.

260

if(

258

if(

261

(touch_previous.x<touch->x+previous_keep)

259

(touch_previous.x<touch->x+previous_keep)

262

&& (touch_previous.x>touch->x-previous_keep)

260

&& (touch_previous.x>touch->x-previous_keep)

263

&& (touch_previous.y<touch->y+previous_keep)

261

&& (touch_previous.y<touch->y+previous_keep)

264

&& (touch_previous.y>touch->y-previous_keep)  )

262

&& (touch_previous.y>touch->y-previous_keep)  )

265

{

263

{

266

return;

264

return;

267

}

265

}

268

266

269

touch_previous.x = touch->x;

267

touch_previous.x = touch->x;

270

touch_previous.y = touch->y;

268

touch_previous.y = touch->y;

271

269

272

/* send mouse event */

270

/* send mouse event */

273

emouse.parent.type = RTGUI_EVENT_MOUSE_BUTTON ;

271

emouse.parent.type = RTGUI_EVENT_MOUSE_BUTTON ;

274

emouse.parent.sender = RT_NULL;

272

emouse.parent.sender = RT_NULL;

275

273

276

emouse.x = touch->x;

274

emouse.x = touch->x;

277

emouse.y = touch->y;

275

emouse.y = touch->y;

278

276

279

/* init mouse button */

277

/* init mouse button */

280

emouse.button = (RTGUI_MOUSE_BUTTON_RIGHT |RTGUI_MOUSE_BUTTON_DOWN);

278

emouse.button = (RTGUI_MOUSE_BUTTON_RIGHT |RTGUI_MOUSE_BUTTON_DOWN);

281

//            rt_kprintf("touch motion: (%d, %d)\n", emouse.x, emouse.y);

279

//            rt_kprintf("touch motion: (%d, %d)\n", emouse.x, emouse.y);

282

}

280

}

283

}

281

}

284

282

285

/* send event to server */

283

/* send event to server */

286

if (touch->calibrating != RT_TRUE)

284

if (touch->calibrating != RT_TRUE)

287

rtgui_server_post_event(&emouse.parent, sizeof(structrtgui_event_mouse));

285

rtgui_server_post_event(&emouse.parent, sizeof(structrtgui_event_mouse));

288

}

286

}

289

287

290

staticvoidNVIC_Configuration(void)

288

staticvoidNVIC_Configuration(void)

291

{

289

{

292

NVIC_InitTypeDefNVIC_InitStructure;

290

NVIC_InitTypeDefNVIC_InitStructure;

293

291

294

/* Enable the EXTI9_5 Interrupt */

292

/* Enable the EXTI0 Interrupt */

295

NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;

293

NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;

296

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;

294

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;

297

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

295

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

298

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

296

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

299

NVIC_Init(&NVIC_InitStructure);

297

NVIC_Init(&NVIC_InitStructure);

300

}

298

}

301

299

302

rt_inlinevoidEXTI_Enable(rt_uint32_tenable)

300

rt_inlinevoidEXTI_Enable(rt_uint32_tenable)

303

{

301

{

304

EXTI_InitTypeDefEXTI_InitStructure;

302

EXTI_InitTypeDefEXTI_InitStructure;

305

303

306

/* Configure  EXTI  */

304

/* Configure  EXTI  */

307

EXTI_InitStructure.EXTI_Line = EXTI_Line6;

305

EXTI_InitStructure.EXTI_Line = EXTI_Line1;

308

EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;

306

EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;

309

EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//Falling下降沿 Rising上升

307

EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//Falling下降沿 Rising上升

310

308

311

if (enable)

309

if (enable)

312

{

310

{

313

/* enable */

311

/* enable */

314

EXTI_InitStructure.EXTI_LineCmd = ENABLE;

312

EXTI_InitStructure.EXTI_LineCmd = ENABLE;

315

}

313

}

316

else

314

else

317

{

315

{

318

/* disable */

316

/* disable */

319

EXTI_InitStructure.EXTI_LineCmd = DISABLE;

317

EXTI_InitStructure.EXTI_LineCmd = DISABLE;

320

}

318

}

321

319

322

EXTI_Init(&EXTI_InitStructure);

320

EXTI_Init(&EXTI_InitStructure);

323

EXTI_ClearITPendingBit(EXTI_Line6);

321

EXTI_ClearITPendingBit(EXTI_Line1);

324

}

322

}

325

323

326

staticvoidEXTI_Configuration(void)

324

staticvoidEXTI_Configuration(void)

327

{

325

{

328

/* PB6 touch INT */

326

/* PB1 touch INT */

329

{

327

{

330

GPIO_InitTypeDefGPIO_InitStructure;

328

GPIO_InitTypeDefGPIO_InitStructure;

331

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

329

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

332

330

333

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;

331

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;

334

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;

332

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;

335

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;

333

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;

336

GPIO_Init(GPIOB,&GPIO_InitStructure);

334

GPIO_Init(GPIOB,&GPIO_InitStructure);

337

}

335

}

338

336

339

GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource6);

337

GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);

340

338

341

/* Configure  EXTI  */

339

/* Configure  EXTI  */

342

EXTI_Enable(1);

340

EXTI_Enable(1);

343

}

341

}

344

342

345

/* RT-Thread Device Interface */

343

/* RT-Thread Device Interface */

346

staticrt_err_trtgui_touch_init (rt_device_tdev)

344

staticrt_err_trtgui_touch_init (rt_device_tdev)

347

{

345

{

348

NVIC_Configuration();

346

NVIC_Configuration();

349

EXTI_Configuration();

347

EXTI_Configuration();

350

348

351

// enable touch, disable other SPI1 device

349

/* PC4 touch CS */

352

{

350

{

353

GPIO_InitTypeDefGPIO_InitStructure;

351

GPIO_InitTypeDefGPIO_InitStructure;

354

352

355

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOB, ENABLE);

353

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);

356

354

357

GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;

355

GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;

358

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

356

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

359

GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_4;

357

GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_4;

360

GPIO_Init(GPIOA,&GPIO_InitStructure);

361

GPIO_Init(GPIOC,&GPIO_InitStructure);

358

GPIO_Init(GPIOC,&GPIO_InitStructure);

362

359

CS_1();

363

GPIO_SetBits(GPIOA, GPIO_Pin_4);            // disable ENC28J60(LAN)

364

GPIO_SetBits(GPIOB, GPIO_Pin_4);            // disable SST25VF016B(2M Flash)

365

366

/* PB7 touch CS */

367

{

368

GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_7;

369

GPIO_Init(GPIOB,&GPIO_InitStructure);

370

CS_1();

371

}

372

}

360

}

373

361

374

CS_0();

362

CS_0();

375

WriteDataTo7843( 1<<7 ); /* 打开中断 */

363

WriteDataTo7843( 1<<7 ); /* 打开中断 */

376

CS_1();

364

CS_1();

377

365

378

returnRT_EOK;

366

returnRT_EOK;

379

}

367

}

380

368

381

staticrt_err_trtgui_touch_control (rt_device_tdev, rt_uint8_tcmd, void *args)

369

staticrt_err_trtgui_touch_control (rt_device_tdev, rt_uint8_tcmd, void *args)

382

{

370

{

383

switch (cmd)

371

switch (cmd)

384

{

372

{

385

caseRT_TOUCH_CALIBRATION:

373

caseRT_TOUCH_CALIBRATION:

386

touch->calibrating = RT_TRUE;

374

touch->calibrating = RT_TRUE;

387

touch->calibration_func = (rt_touch_calibration_func_t)args;

375

touch->calibration_func = (rt_touch_calibration_func_t)args;

388

break;

376

break;

389

377

390

caseRT_TOUCH_NORMAL:

378

caseRT_TOUCH_NORMAL:

391

touch->calibrating = RT_FALSE;

379

touch->calibrating = RT_FALSE;

392

break;

380

break;

393

381

394

caseRT_TOUCH_CALIBRATION_DATA:

382

caseRT_TOUCH_CALIBRATION_DATA:

395

{

383

{

396

structcalibration_data* data;

384

structcalibration_data* data;

397

385

398

data = (structcalibration_data*) args;

386

data = (structcalibration_data*) args;

399

387

400

//update

388

//update

401

touch->min_x = data->min_x;

389

touch->min_x = data->min_x;

402

touch->max_x = data->max_x;

390

touch->max_x = data->max_x;

403

touch->min_y = data->min_y;

391

touch->min_y = data->min_y;

404

touch->max_y = data->max_y;

392

touch->max_y = data->max_y;

405

}

393

}

406

break;

394

break;

407

}

395

}

408

396

409

returnRT_EOK;

397

returnRT_EOK;

410

}

398

}

411

399

412

void EXTI9_5_IRQHandler(void)

400

void EXTI1_IRQHandler(void)

413

{

401

{

414

/* disable interrupt */

402

/* disable interrupt */

415

EXTI_Enable(0);

403

EXTI_Enable(0);

416

404

417

/* start timer */

405

/* start timer */

418

rt_timer_start(touch->poll_timer);

406

rt_timer_start(touch->poll_timer);

419

407

420

EXTI_ClearITPendingBit(EXTI_Line6);

408

EXTI_ClearITPendingBit(EXTI_Line1);

421

}

409

}

422

410

423

voidrtgui_touch_hw_init(void)

411

voidrtgui_touch_hw_init(void)

424

{

412

{

425

/* SPI1 config */

413

/* SPI1 config */

426

{

414

{

427

GPIO_InitTypeDefGPIO_InitStructure;

415

GPIO_InitTypeDefGPIO_InitStructure;

428

SPI_InitTypeDefSPI_InitStructure;

416

SPI_InitTypeDefSPI_InitStructure;

429

417

430

/* Enable SPI1 Periph clock */

418

/* Enable SPI1 Periph clock */

431

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA

419

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA

432

| RCC_APB2Periph_AFIO | RCC_APB2Periph_SPI1,

420

| RCC_APB2Periph_AFIO | RCC_APB2Periph_SPI1,

433

ENABLE);

421

ENABLE);

434

422

435

/* Configure SPI1 pins: PA5-SCK, PA6-MISO and PA7-MOSI */

423

/* Configure SPI1 pins: PA5-SCK, PA6-MISO and PA7-MOSI */

436

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;

424

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;

437

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

425

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

438

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

426

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

439

GPIO_Init(GPIOA, &GPIO_InitStructure);

427

GPIO_Init(GPIOA, &GPIO_InitStructure);

440

428

441

/*------------------------ SPI1 configuration ------------------------*/

429

/*------------------------ SPI1 configuration ------------------------*/

442

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//SPI_Direction_1Line_Tx;

430

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//SPI_Direction_1Line_Tx;

443

SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

431

SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

444

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;

432

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;

445

SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;

433

SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;

446

SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;

434

SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;

447

SPI_InitStructure.SPI_NSS  = SPI_NSS_Soft;

435

SPI_InitStructure.SPI_NSS  = SPI_NSS_Soft;

448

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;/* 72M/64=1.125M */

436

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;/* 72M/64=1.125M */

449

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;

437

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;

450

SPI_InitStructure.SPI_CRCPolynomial = 7;

438

SPI_InitStructure.SPI_CRCPolynomial = 7;

451

439

452

SPI_I2S_DeInit(SPI1);

440

SPI_I2S_DeInit(SPI1);

453

SPI_Init(SPI1, &SPI_InitStructure);

441

SPI_Init(SPI1, &SPI_InitStructure);

454

442

455

/* Enable SPI_MASTER */

443

/* Enable SPI_MASTER */

456

SPI_Cmd(SPI1, ENABLE);

444

SPI_Cmd(SPI1, ENABLE);

457

SPI_CalculateCRC(SPI1, DISABLE);

445

SPI_CalculateCRC(SPI1, DISABLE);

458

446

459

if (rt_sem_init(&spi1_lock, "spi1lock", 1, RT_IPC_FLAG_FIFO) != RT_EOK)

447

if (rt_sem_init(&spi1_lock, "spi1lock", 1, RT_IPC_FLAG_FIFO) != RT_EOK)

460

{

448

{

461

rt_kprintf("init spi1 lock semaphore failed\n");

449

rt_kprintf("init spi1 lock semaphore failed\n");

462

}

450

}

463

} /* SPI1 config */

451

} /* SPI1 config */

464

452

465

touch = (structrtgui_touch_device*)rt_malloc (sizeof(structrtgui_touch_device));

453

touch = (structrtgui_touch_device*)rt_malloc (sizeof(structrtgui_touch_device));

466

if (touch == RT_NULL) return; /* no memory yet */

454

if (touch == RT_NULL) return; /* no memory yet */

467

455

468

/* clear device structure */

456

/* clear device structure */

469

rt_memset(&(touch->parent), 0, sizeof(structrt_device));

457

rt_memset(&(touch->parent), 0, sizeof(structrt_device));

470

touch->calibrating = false;

458

touch->calibrating = false;

471

459

472

/* init device structure */

460

/* init device structure */

473

touch->parent.type = RT_Device_Class_Unknown;

461

touch->parent.type = RT_Device_Class_Unknown;

474

touch->parent.init = rtgui_touch_init;

462

touch->parent.init = rtgui_touch_init;

475

touch->parent.control = rtgui_touch_control;

463

touch->parent.control = rtgui_touch_control;

476

touch->parent.user_data = RT_NULL;

464

touch->parent.user_data = RT_NULL;

477

465

478

/* create 1/8 second timer */

466

/* create 1/8 second timer */

479

touch->poll_timer = rt_timer_create("touch", touch_timeout, RT_NULL,

467

touch->poll_timer = rt_timer_create("touch", touch_timeout, RT_NULL,

480

RT_TICK_PER_SECOND/8, RT_TIMER_FLAG_PERIODIC);

468

RT_TICK_PER_SECOND/8, RT_TIMER_FLAG_PERIODIC);

481

469

482

/* register touch device to RT-Thread */

470

/* register touch device to RT-Thread */

483

rt_device_register(&(touch->parent), "touch", RT_DEVICE_FLAG_RDWR);

471

rt_device_register(&(touch->parent), "touch", RT_DEVICE_FLAG_RDWR);

484

}

472

}

485

473

486

#ifdef RT_USING_FINSH

474

#ifdef RT_USING_FINSH

487

#include <finsh.h>

475

#include <finsh.h>

488

476

489

voidtouch_t( rt_uint16_tx , rt_uint16_ty )

477

voidtouch_t( rt_uint16_tx , rt_uint16_ty )

490

{

478

{

491

structrtgui_event_mouseemouse ;

479

structrtgui_event_mouseemouse ;

492

emouse.parent.type = RTGUI_EVENT_MOUSE_BUTTON;

480

emouse.parent.type = RTGUI_EVENT_MOUSE_BUTTON;

493

emouse.parent.sender = RT_NULL;

481

emouse.parent.sender = RT_NULL;

494

482

495

emouse.x = x ;

483

emouse.x = x ;

496

emouse.y = y ;

484

emouse.y = y ;

497

/* init mouse button */

485

/* init mouse button */

498

emouse.button = (RTGUI_MOUSE_BUTTON_LEFT |RTGUI_MOUSE_BUTTON_DOWN );

486

emouse.button = (RTGUI_MOUSE_BUTTON_LEFT |RTGUI_MOUSE_BUTTON_DOWN );

499

rtgui_server_post_event(&emouse.parent, sizeof(structrtgui_event_mouse));

487

rtgui_server_post_event(&emouse.parent, sizeof(structrtgui_event_mouse));

500

488

501

rt_thread_delay(2) ;

489

rt_thread_delay(2) ;

502

emouse.button = (RTGUI_MOUSE_BUTTON_LEFT |RTGUI_MOUSE_BUTTON_UP );

490

emouse.button = (RTGUI_MOUSE_BUTTON_LEFT |RTGUI_MOUSE_BUTTON_UP );

503

rtgui_server_post_event(&emouse.parent, sizeof(structrtgui_event_mouse));

491

rtgui_server_post_event(&emouse.parent, sizeof(structrtgui_event_mouse));

504

}

492

}

505

493

506

FINSH_FUNCTION_EXPORT(touch_t, x & y ) ;

494

FINSH_FUNCTION_EXPORT(touch_t, x & y ) ;

507

#endif

495

#endif

508

496

补丁:

17c17

< CS   PB7

---

> CS   PC4

20,21c20,21

< #define   CS_0()          GPIO_ResetBits(GPIOB,GPIO_Pin_7)

< #define   CS_1()          GPIO_SetBits(GPIOB,GPIO_Pin_7)

---

> #define   CS_0()          GPIO_ResetBits(GPIOC,GPIO_Pin_4)

> #define   CS_1()          GPIO_SetBits(GPIOC,GPIO_Pin_4)

78,79c78,79

< #define X_WIDTH 480

< #define Y_WIDTH 272

---

> #define X_WIDTH 240

> #define Y_WIDTH 320

147,148c147,148

<                 touch->y = total_x / 8;

<                 touch->x = total_y / 8;

---

>                 touch->x = total_x / 8;

>                 touch->y = total_y / 8;

154,155d153

<         rt_kprintf("physical position: (%d, %d)\n", touch->x, touch->y);

<

202c200

<     if ((!touched_down) && GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_6) != 0)

---

>     if ((!touched_down) && GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) != 0)

205c203

<     if (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_6) != 0)

---

>     if (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) != 0)

294,295c292,293

<     /* Enable the EXTI9_5 Interrupt */

<     NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;

---

>     /* Enable the EXTI0 Interrupt */

>     NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;

307c305

<     EXTI_InitStructure.EXTI_Line = EXTI_Line6;

---

>     EXTI_InitStructure.EXTI_Line = EXTI_Line1;

323c321

<     EXTI_ClearITPendingBit(EXTI_Line6);

---

>     EXTI_ClearITPendingBit(EXTI_Line1);

328c326

<     /* PB6 touch INT */

---

>     /* PB1 touch INT */

333c331

<         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;

---

>         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;

339c337

<     GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource6);

---

>     GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);

351c349

<     // enable touch, disable other SPI1 device

---

>     /* PC4 touch CS */

355c353

<         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOB, ENABLE);

---

>         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);

360d357

<         GPIO_Init(GPIOA,&GPIO_InitStructure);

362,369d358

<

<         GPIO_SetBits(GPIOA, GPIO_Pin_4);                      // disable ENC28J60(LAN)

<         GPIO_SetBits(GPIOB, GPIO_Pin_4);            // disable SST25VF016B(2M Flash)

<

<         /* PB7 touch CS */

<         {

<             GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_7;

<             GPIO_Init(GPIOB,&GPIO_InitStructure);

372d360

<     }

412c400

< void EXTI9_5_IRQHandler(void)

---

> void EXTI1_IRQHandler(void)

420c408

<     EXTI_ClearITPendingBit(EXTI_Line6);

---

>     EXTI_ClearITPendingBit(EXTI_Line1);

在 touch.c 中,第30行定义了一个结构体,其中最后两行,对于每个触摸屏,不同时期,可能采用不同的值才会取得最佳效果,这就是触摸屏校准程序要采集并计算的最佳数据。

struct rtgui_touch_device

{

struct rt_device parent;

rt_timer_t poll_timer;

rt_uint16_t x, y;

rt_bool_t calibrating;

rt_touch_calibration_func_t calibration_func;

rt_uint16_t min_x, max_x;

rt_uint16_t min_y, max_y;

};

数值例:

点触摸屏右下角,硬件返回的坐标值:3955, 3463

校准数据:min_x = 208, max_x = 4069, min_y = 187, max_y = 3590

最后,综合硬件值,校准数据,屏幕分辨率,计算出一个相对精确的坐标数据(x, y),如果屏幕分辨率为480 X 640,则x < 480,y < 640。

注1:由于屏幕显示方向与触摸屏方向不一致,在代码的第147行,将 x, y 值对调。

注2:为了屏幕与触摸屏方向保持一致,修改了LCD驱动,将显示方向转了180度。

Ssd1963.c 第188行,参数值改为0x0003

LCD_WR_REG(0x0036);            //rotation

LCD_WR_DAT(0x0003);

具体参数意义,请参考PDF文件说明。

STM32 + RT Thread OS 学习笔记[五]相关推荐

  1. 哈工大操作系统学习笔记五——内核级线程实现

    哈工大os学习笔记五(内核级线程实现) 文章目录 哈工大os学习笔记五(内核级线程实现) 一. 中断入口.中断出口(前后两段) 1. 从int中断进入内核(中断入口第一段) 2.中断出口(最后一段) ...

  2. 【OS学习笔记】三十五 保护模式十:中断描述符表、中断门和陷阱门

    上一篇文章学习了中断与异常的概念:[OS学习笔记]三十四 保护模式十:中断和异常区别 本片文章接着学习以下内容: 中断描述符表 中断门 陷阱门 1 中断描述符表 我们前面讲了无数次,在实模式下,是由位 ...

  3. 【OS学习笔记】二十五 保护模式七:任务和特权级保护对应的汇编源代码

    本汇编代码是以下两篇文章讲解的内容的内核代码; [OS学习笔记]二十三 保护模式七:保护模式下任务的隔离与任务的特权级概念 [OS学习笔记]二十四 保护模式七:调用门与依从的代码段----特权级保护 ...

  4. StackExchange.Redis学习笔记(五) 发布和订阅

    StackExchange.Redis学习笔记(五) 发布和订阅 原文:StackExchange.Redis学习笔记(五) 发布和订阅 Redis命令中的Pub/Sub Redis在 2.0之后的版 ...

  5. 【OS学习笔记】四十 保护模式十:中断和异常的处理与抢占式多任务对应的汇编代码----动态加载的用户程序/任务二代码

    本文是以下几篇文章对应的微型动态加载的用户程序/任务二代码: [OS学习笔记]三十四 保护模式十:中断和异常区别 [OS学习笔记]三十五 保护模式十:中断描述符表.中断门和陷阱门 [OS学习笔记]三十 ...

  6. 【OS学习笔记】三十九 保护模式十:中断和异常的处理与抢占式多任务对应的汇编代码----动态加载的用户程序/任务一代码

    本文是以下几篇文章对应的动态加载的用户程序/任务一代码: [OS学习笔记]三十四 保护模式十:中断和异常区别 [OS学习笔记]三十五 保护模式十:中断描述符表.中断门和陷阱门 [OS学习笔记]三十六 ...

  7. 【OS学习笔记】三十八 保护模式十:中断和异常的处理与抢占式多任务对应的汇编代码----微型内核汇代码

    本文是以下几篇文章对应的微型内核代码汇编代码: [OS学习笔记]三十四 保护模式十:中断和异常区别 [OS学习笔记]三十五 保护模式十:中断描述符表.中断门和陷阱门 [OS学习笔记]三十六 保护模式十 ...

  8. 【OS学习笔记】三十七 保护模式十:中断和异常的处理与抢占式多任务对应的汇编代码----主引导扇区代码

    本文是以下几篇文章对应的主引导扇区代码汇编代码: [OS学习笔记]三十四 保护模式十:中断和异常区别 [OS学习笔记]三十五 保护模式十:中断描述符表.中断门和陷阱门 [OS学习笔记]三十六 保护模式 ...

  9. 【OS学习笔记】三十六 保护模式十:通过中断发起任务切换----中断任务

    上一篇文章学习了:OS学习笔记]三十五 保护模式十:中断描述符表.中断门和陷阱门 本篇文章接着上一篇文章学习中断任务. 我们在前面文章中一直在说通过中断发起任务切换,本文就是将之前没有说明白的内容:通 ...

最新文章

  1. C# Socket系列三 socket通信的封包和拆包
  2. OEM, ODM, OBM
  3. python入门必备指南-致Python初学者 Anaconda入门使用指南完整版
  4. 蓝桥杯 ALGO-42 算法训练 送分啦
  5. javaweb环境的配置 以及tomcat的安装
  6. JAVAWeb使用POI做导出Excel
  7. springzuul本地路由和跨服务器路由问题
  8. 2022电工杯:5G 网络环境下应急物资配送问题(优化)
  9. 图片转文字,手机摇身一变就是万能扫描仪!
  10. 微博评论数据爬取思路及代码分享
  11. 领导人怎样带领好团队
  12. python 当天零点的时间戳
  13. 什么是云计算云计算能干什么?云计算学习笔记工具素材
  14. 硬件视角看段页式存储
  15. 基于Java毕业设计写手管理平台源码+系统+mysql+lw文档+部署软件
  16. 计算机基础知识教程excel单元格拆分,电脑内怎么将excel表格中某个单元格的内容拆分至不同单元格里...
  17. 电子设计教程29:滞回比较器(施密特触发器)
  18. Random + Scanner 场景试炼
  19. 打印机与电脑文件服务器,电脑无法共享局域网打印机和文件的解决方法
  20. 思考乐教育的荣耀与忧虑:整体增速放缓,异地扩张未见成效

热门文章

  1. 嵌入式音频架构 - AudioWeaver 整体概念
  2. 涨握在线收评 | 沪指跌1.73%失守3000点 创两个月最大单日跌幅
  3. PyCharm中Github的使用
  4. linux+上的录屏软件下载,Linux下优秀的屏幕录像软件Kazam | 薄荷开源网
  5. 阿斯汤加瑜伽(Ashtanga Yoga)第一序列学习与实践笔记(一)
  6. verilog学习笔记- 12)触摸按键控制LED灯实验
  7. 【THREE源码解析篇】THREE.Matrix4源码详解
  8. 如何在termius安装linux窗口,XShell的神器—Termius使用ssh教程
  9. 如何查看声卡型号alc版本号、型号
  10. Unity 图标字体