干货 | DIY 一个 ARM 学习机

我对底层的东西特别感兴趣。十多年前有了自己的电脑,一直保留DOS用了好些年。喜欢汇编,讨厌Java。接触的第一个单片机是AT89S52,下程序是用串口CTS/RTS以及DSR/DTR控制线来模拟SPI,在DOS下把时钟中断调到几十kHz来定时确定时序。后来就用89S52做成了2051的编程器,再后来用2051做了串口转SPI下载器玩起AVR了,玩过最多的还是AVR.

不过51、AVR这类Harvard结构MCU不能干的事情是自己Load程序来运行,只能从ROM执行啊(51可以有办法映射,但是51学习机计划没有实施)。我小时候听说过“中华学习机”,不同于小霸王学习机那样ROM固化死了的,人家是从软盘装载程序的。家里头的旧杂志上介绍过TD-I型8031的学习机,是用开关来二进制编程RAM然后执行的,倘若放到今天……

我生活的时代已经不是穿孔纸带了,DIY还是玩现代的东西吧。言归正传,我的第一个“学习机”作品是ARM7TDMI的,尽管这个核已经早过时了。我选择了NXP的LPC2220作为处理器,它自带64kB的RAM,在单片机中算是大的了。核心比较简单,作为学习机适宜,而且有外部总线,比较方便扩展RAM,类似PC那样来玩。这个PCB做得比较早了,搁置没玩起来(手头没时间玩的板子多了,这是另外的问题),上面除了MCU还有一块16bit*256k的SRAM,一片8bit*128k的NOR Flash ROM,预留16-bit总线插针扩展,具备一个计算机的配置了(除了缺少DMA支持)。下面是线路图:

PCB的顶层图

 PCB的底层:

作为MCU自带的外设是可以引出来的,所以UART, SPI, I2C基本I/O通信用分组接到插针,再另外放了16个GPIO在一侧。核心部分先焊上就可以玩了。

上面介绍的是硬件部分。LPC2220是为数不多的ROMless单片机,里面没有Flash哦,OTP也没有。这也是我选择它的原因,作为学习机不是做任何应用的原型板,烧写Flash尽量避免吧。虽然Flash的烧写次数已经够多了,能省一事算一事。NXP的ARM7 (LPC21xx系列)有个特点是可以从PC直接ISP下载到RAM,然后运行。我在第一次玩ARM (LPC2103)的时候发现Flash工具有这个功能。

读NXP的手册,发现Bootloader是使用串口命令进行交互的,命令也不复杂。

所以,只要连接MCU的UART0到PC的串口,上电或者复位就进入Bootloader里面的ISP程序(因为没有可执行的ROM嘛),就可以从PC把代码直接下载到ROM然后运行了。64kB的SRAM哦,可以实现不少东西了吧。

当然,用NXP官方的FlashMagic来下载的效率太低了,不适应“学习机"要快速载入程序的要求。所以我自己做一个程序:

  1. #include<windows.h>

  2. #include<stdio.h>

  3. #include<string.h>

  4. #include<stdlib.h>

  5. HANDLE dev;

  6. OVERLAPPED oWR = {0}, oRD={0};

  7. void errexit(char *s)

  8. {

  9. fprintf(stderr,"%s",s);

  10. exit(1);

  11. }

  12. struct ROM

  13. {

  14. unsigned char b;

  15. char oc;

  16. };

  17. int readcomm(unsigned char *buf, int maxlen, int timeout);

  18. int writecomm(unsigned char *buf, int len);

  19. int loadhexfile(char *fn,int szlimit,int *poff,struct ROM *rom);

  20. int showbinary(int szlimit, struct ROM *rom);

  21. int uuencode(int szlimit, struct ROM *rom, char *buf, int slen, int linelen);

  22. int checksum(int szlimit, struct ROM *rom);

  23. main(int argc, char *argv[])

  24. {

  25. DCB dcb;

  26. int i,r;

  27. if(argc<2)

  28. {

  29. printf("Supply a HEX file name.\n");

  30. return;

  31. }

  32. dev=CreateFile("COM3", GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,  FILE_FLAG_OVERLAPPED,0);

  33. if(dev==INVALID_HANDLE_VALUE)

  34. {

  35. printf("Cannot open COM3 port.\n");

  36. exit(1);

  37. }

  38. FillMemory(&dcb, sizeof(dcb), 0);

  39. dcb.DCBlength = sizeof(dcb);

  40. BuildCommDCB("57600,n,8,1",&dcb);

  41. if (!SetCommState(dev, &dcb))

  42. {

  43. printf("Cannot set port parameters.\n");

  44. exit(1);

  45. }

  46. oWR.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

  47. oRD.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

  48. for(i=0;i<1000;i++)

  49. {

  50. int len0;

  51. char c, buf[32];

  52. char done=0;

  53. writecomm("?",1);

  54. r=readcomm(buf,4,20);

  55. if(r>0)

  56. {

  57. int j;

  58. for(j=0;j<r;j++)

  59. {

  60. printf("%c",buf[j]);

  61. if(buf[j]=='S')

  62. {

  63. r=readcomm(buf+r,14-(4-j),20);

  64. if(r==14-(4-j) && strncmp(buf+j,"Synchronized\r\n",14)==0)

  65. {

  66. printf("SYNC\n");

  67. writecomm("Synchronized\r\n",14);

  68. r=readcomm(buf,32,50);

  69. if(r==18 && strncmp(buf,"Synchronized\r\nOK\r\n",18)==0)

  70. {

  71. printf("Synchronization done\n");

  72. done=1;

  73. break;

  74. }

  75. else

  76. printf("(%d) %s",r,buf);

  77. }

  78. }

  79. }

  80. if(done) break;

  81. }

  82. else

  83. {

  84. printf(".");

  85. continue;

  86. }

  87. }

  88. {

  89. char c;

  90. writecomm("11095\r\n",7);

  91. while(readcomm(&c,1,10)>0)

  92. {

  93. printf("%c",c);

  94. }

  95. }

  96. {

  97. char c;

  98. writecomm("J\r\n",3);

  99. while(readcomm(&c,1,10)>0)

  100. {

  101. printf("%c",c);

  102. }

  103. }

  104. {

  105. unsigned int progaddr=0x40000200;

  106. char c;

  107. char str[128];

  108. char buf[512];

  109. int slen;

  110. int pgsize;

  111. struct ROM rom[1024];

  112. int offset=-1;

  113. pgsize=loadhexfile(argv[1],1024,&offset,rom);

  114. printf("Program Size: %d bytes from %X\n", pgsize, offset);

  115. showbinary(1024,rom);

  116. sprintf(str,"W %d %d\r\n",progaddr, pgsize);

  117. slen=strlen(str);

  118. writecomm(str,slen);

  119. while(readcomm(&c,1,10)>0)

  120. {

  121. printf("%c",c);

  122. }

  123. slen=uuencode(1024, rom, buf, 512, 61);

  124. writecomm(buf,slen);

  125. sprintf(str,"%d\r\n",checksum(1024,rom));

  126. slen=strlen(str);

  127. writecomm(str,slen);

  128. while(readcomm(&c,1,10)>0)

  129. {

  130. printf("%c",c);

  131. }

  132. writecomm("U 23130\r\n",9);

  133. while(readcomm(&c,1,10)>0)

  134. {

  135. printf("%c",c);

  136. }

  137. sprintf(str,"G %d T\r\n",progaddr);

  138. slen=strlen(str);

  139. writecomm(str,slen);

  140. while(readcomm(&c,1,10)>0)

  141. {

  142. printf("%c",c);

  143. }

  144. }

  145. CloseHandle(oWR.hEvent);

  146. CloseHandle(oRD.hEvent);

  147. CloseHandle(dev);

  148. }

  149. int writecomm(unsigned char *buf, int len)

  150. {

  151. int r;

  152. int len0;

  153. r=WriteFile(dev,buf,len,&len0, &oWR);

  154. if(r==0)

  155. {

  156. if(GetLastError()!=ERROR_IO_PENDING)

  157. {

  158. printf("COM port access error!\n");

  159. exit(1);

  160. }

  161. GetOverlappedResult(dev, &oWR, &len0, TRUE);

  162. }

  163. return len0;

  164. }

  165. int readcomm(unsigned char *buf, int maxlen, int tmout)

  166. {

  167. static unsigned wait=0;

  168. static unsigned char ch;

  169. int i, len0, r;

  170. if(tmout<=0)

  171. return 0;

  172. for(i=0;i<maxlen;i++)

  173. {

  174. if(!wait)

  175. {

  176. if(ReadFile(dev,&ch,1,&len0, &oRD))

  177. {

  178. wait=0;

  179. buf[i]=ch;

  180. continue;

  181. }

  182. if(GetLastError()!=ERROR_IO_PENDING)

  183. {

  184. printf("Read COM port error!\n");

  185. exit(1);

  186. }

  187. }

  188. r=WaitForSingleObject(oRD.hEvent,1);

  189. if(r==WAIT_TIMEOUT)

  190. {

  191. wait=1;

  192. tmout--;

  193. if(tmout==0)

  194. return i;

  195. else

  196. {

  197. i--;

  198. continue;

  199. }

  200. }

  201. if(r==WAIT_OBJECT_0)

  202. {

  203. wait=0;

  204. buf[i]=ch;

  205. continue;

  206. }

  207. }

  208. return i;

  209. }

  210. int loadhexfile(char *fn,int szlimit,int *poff,struct ROM *rom)

  211. {

  212. int i,j,n,count,addr,waddr,bsz;

  213. char line[512];

  214. unsigned char bb,sum,hex[256];

  215. FILE *fp;

  216. memset(rom,0,sizeof(struct ROM)*szlimit);

  217. fp=fopen(fn,"r");

  218. if(fp==NULL)

  219. {

  220. printf("Cannot open HEX file %s\n",fn);

  221. return 0;

  222. }

  223. printf("Opening file %s for reading... ",fn);

  224. count=0;

  225. sum=0;

  226. for(n=1;;n++)

  227. {

  228. if(!fgets(line,512,fp))

  229. errexit("read error\n");

  230. if(line[0]!=':')

  231. errexit("FORMAT ERROR\n");

  232. j=0;

  233. for(i=1;;i+=2)  // convert ASCII to binary

  234. {

  235. switch(line[i])

  236. {

  237. case 'A':  bb=0xa0;  break;

  238. case 'B':  bb=0xb0;  break;

  239. case 'C':  bb=0xc0;  break;

  240. case 'D':  bb=0xd0;  break;

  241. case 'E':  bb=0xe0;  break;

  242. case 'F':  bb=0xf0;  break;

  243. case '\0':  case '\r':  case '\n': bb=0x77;  break;

  244. default:  if(line[i]>='0' && line[i]<='9')

  245. bb=(line[i]-'0')<<4;

  246. else

  247. bb=0xff;

  248. }

  249. if(bb==0x77)

  250. break;

  251. if(bb==0xff)

  252. errexit("INVALID CHARACTER\n");

  253. switch(line[i+1])

  254. {

  255. case 'A':  bb+=0xa;  break;

  256. case 'B':  bb+=0xb;  break;

  257. case 'C':  bb+=0xc;  break;

  258. case 'D':  bb+=0xd;  break;

  259. case 'E':  bb+=0xe;  break;

  260. case 'F':  bb+=0xf;  break;

  261. default:  if(line[i+1]>='0' && line[i+1]<='9')

  262. bb+=(line[i+1]-'0');

  263. else

  264. errexit("BAD BYTE\n");

  265. }

  266. sum+=bb;

  267. hex[j]=bb;

  268. j++;

  269. } // converted ASCII to binary

  270. if(sum!=0)

  271. errexit("CHECKSUM ERROR\n");

  272. if(j<5)

  273. errexit("LINE TOO SHORT\n");

  274. if(hex[0]!=j-5)

  275. errexit("FORMAT ERROR\n");

  276. bsz=j-5;

  277. switch(hex[3])

  278. {

  279. case 0:   addr=hex[1];   addr<<=8;  addr+=hex[2];

  280. if(*poff==-1)

  281. {

  282. *poff=addr;

  283. }

  284. for(i=0;i<bsz;i++)

  285. {

  286. int waddr=addr-(*poff);

  287. if(waddr>szlimit)

  288. {

  289. printf("address %X beyond range (base %X) \n",addr, *poff);

  290. }

  291. else

  292. {

  293. rom[waddr].b=hex[4+i];

  294. rom[waddr].oc=1;

  295. }

  296. addr++;

  297. count++;

  298. }

  299. break;

  300. case 1:   if(j!=5)

  301. errexit("UNEXPECTED END\n");

  302. fclose(fp);

  303. return count;

  304. case 2:   if(hex[1]!=0 || hex[2]!=0 || hex[4]!=0 || hex[5]!=0)

  305. printf("UNSUPPORTED PARAGRAPH\n");

  306. break;

  307. case 3:

  308. default:  printf("UNSUPPORTED RECODE TYPE (%X)\n", hex[3]); break;

  309. }

  310. }

  311. }

  312. int showbinary(int szlimit, struct ROM *rom)

  313. {

  314. int i;

  315. for(i=0;i<szlimit;)

  316. {

  317. int j;

  318. char blank=1;

  319. for(j=0;j<16;j++)

  320. {

  321. if(rom[i+j].oc)

  322. {

  323. blank=0;

  324. break;

  325. }

  326. }

  327. if(blank)

  328. {

  329. i+=16;

  330. continue;

  331. }

  332. printf("%04X: ",i);

  333. for(j=0;j<16;j++)

  334. {

  335. if(rom[i+j].oc)

  336. printf("%02X",rom[i+j].b);

  337. else

  338. printf("..");

  339. if(j==7)

  340. printf(" - ");

  341. else

  342. printf(" ");

  343. }

  344. printf(" | ");

  345. for(j=0;j<16;j++)

  346. {

  347. if(rom[i+j].oc)

  348. {

  349. if(rom[i+j].b>=0x20 && rom[i+j].b<=0x7f)

  350. printf("%c",rom[i+j].b);

  351. else

  352. printf("?");

  353. }

  354. else

  355. printf(".");

  356. }

  357. i+=16;

  358. printf("\n");

  359. }

  360. }

  361. int uuencode(int szlimit, struct ROM *rom, char *buf, int slen, int linelen)

  362. {

  363. int i, sz=0,mem;

  364. int lmax, lc, brem;

  365. int index=0;

  366. for(i=0;i<szlimit;i++)

  367. {

  368. if(rom[i].oc)

  369. sz=i+1;

  370. }

  371. lmax=((linelen-1)/4)*3;

  372. lc=sz/lmax;

  373. brem=sz%lmax;

  374. mem=lc*(((linelen-1)/4)*4+3)+3+(brem/3*4)+(brem%3+1);  // include CR LF

  375. if(mem>=slen)

  376. {

  377. printf("UUEncode: buffer too small! need %d bytes\n",mem);

  378. return 0;

  379. }

  380. for(i=0;i<sz;)

  381. {

  382. int j, len;

  383. char r=0;

  384. unsigned char b, a=0;

  385. if(i+lmax<sz)

  386. len=lmax;

  387. else

  388. len=brem;

  389. buf[index++]=0x20+len;

  390. for(j=0;j<len;j++)

  391. {

  392. if(rom[i+j].oc)

  393. b=rom[i+j].b;

  394. else

  395. b=0xff;

  396. a|=(b>>(2+r));

  397. if(a) buf[index++]=0x20+a; else buf[index++]='`';

  398. r+=2;

  399. a=((b&((1<<r)-1))<<(6-r));

  400. if(r==6)

  401. {

  402. if(a) buf[index++]=0x20+a; else buf[index++]='`';

  403. r=0;

  404. a=0;

  405. }

  406. }

  407. if(r!=0)

  408. {

  409. if(a) buf[index++]=0x20+a; else buf[index++]='`';

  410. }

  411. i+=lmax;

  412. buf[index++]='\r';

  413. buf[index++]='\n';

  414. }

  415. printf("MEM %d, actual %d\n",mem,index);

  416. return index;

  417. }

  418. int checksum(int szlimit, struct ROM *rom)

  419. {

  420. int i, sz, sum;

  421. sz=0;

  422. for(i=0;i<szlimit;i++)

  423. {

  424. if(rom[i].oc)

  425. sz=i+1;

  426. }

  427. sum=0;

  428. for(i=0;i<sz;i++)

  429. sum+=rom[i].b;

  430. return sum;

  431. }

复制代码

这个程序做的工作是打开串口, 和单片机Bootloader ISP命令同步,然后读取指定的HEX文件,下载到LPC2220的RAM地址 0x40000200, 再解除锁定,最后发送执行程序的命令,单片机从 0x40000200 开始运行。如果要重新下载程序,把单片机Reset了,再运行以上的程序即可。命令行一步搞定,容易吧!

注: LPC2220的RAM地址从 0x40000000 开始,但是BootLoader会用一部分RAM,所以为了避免冲突,程序下载从RAM的512字节之后起。NXP的Bootloader是用UUEncode编码传输二进制数据的,上面的程序包含编码的子程序。这个程序也写得不完善,还有比如代码长度检查都没有做进去,目前做到的只是能下载一小段并执行。

(0)

相关推荐