LarduinoISP for LGT8FX8D SWD通信协议源码简析

LGT8FX8D/P系列的CPU可以指令级兼容avr芯片,引脚定义也相近.将avr的程序移植到LGT8FX8D/P只需作少量的修改,而且增强了一些性能,价格却更低,性价比高.
要将程序写入空片,其flash烧写方式与avr并不一样,需要专门的调试下载器.使用说明
LarduinoISP for LGT8FX8D公开了份代码,其中实现了通过SWD接口实现LGT8FX8D的读写.我们通过阅读这份代码来看看通过SWD通信方式来实现flash烧写的过程.

SWD通信硬件要求

需使用的引脚:

引脚传输方向描述
SWD输入与输出用作传输数据比特,双向
SWC输出用作时钟信号
RST输出SWD模式时RST拉低

SWD对应PB5,SWC对应PB4,RST对应PB2

#define SWDIF_PIN		PINB
#define SWDIF_DIR		DDRB
#define SWDIF_PORT		PORTB
#define SWDIF_CLK		(1 << 5)	// PB5
#define SWDIF_DAT	  	(1 << 4)	// PB4
#define SWDIF_RSTN		(1 << 2)	// PB2
SWD通信时序分析

所有时序的实现,由CPU操作IO口完成.

通信速率

在一个通信时钟周期会调用二次SWD_Delay() 12 ∗ 2 12*2 122NOP指令,再加上设置SWC SWD电平的指令,总约需30个指令周期,在16MHz的系统时钟下,通信速率可达500Kbps.如果一个位输出中有更多的逻辑操作,则通信频率会更低一些.

#define SWD_Delay()	do {\
	NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); \
	NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); \
} while(0);
		//一个时钟周期的模拟
		SWC_CLR();
		SWD_Delay();
		SWD_CLR();
		SWD_Delay();
		SWC_SET();
		SWD_Delay();
通信时序

数据通信由起始位SWD_CLR开始,然后是输出一个字节是由一串8比特数据一个结束位SWD_SET组成,如有多个字节输出,则中间结束位为SWD_CLR,最后的结束位为SWD_SET.每个字节的先输出最低位,再到最高位的顺序输出,即LSB First.

双向引脚SWD输出数据时,在时钟SWC低电平时改变SWD输出数据,写入目标芯片会在SWC的上升沿检测SWD数据.

void SWD_WriteByte(uint8_t start, uint8_t data, uint8_t stop)
{
	volatile uint8_t cnt;
	
	if(start) {
		SWC_CLR();
		SWD_Delay();
		SWD_CLR();
		SWD_Delay();
		SWC_SET();
		SWD_Delay();
	}
	
	// send data
	for(cnt = 0; cnt < 8; cnt++)
	{
		SWC_CLR();
		if(data & 0x1) SWD_SET();
		else SWD_CLR();
		SWD_Delay();
		data >>= 1;
		SWC_SET();
		SWD_Delay();
	}
	
	SWC_CLR();
	if(stop) SWD_SET();
	else SWD_CLR();

	SWD_Delay();
	SWC_SET();
	SWD_Delay();
}

双向引脚SWD输入数据时,在时钟SWC高电平时设为输入并上拉,由写入目标芯片控制SWD,在SWC的下降沿检测SWD数据.

uint8_t SWD_ReadByte(uint8_t start, uint8_t stop)
{
	volatile uint8_t cnt;
	volatile uint8_t bRes = 0;
	
	if(start)
	{
		SWC_CLR();
		SWD_CLR();
		SWD_Delay();
		SWC_SET();
		SWD_Delay();		
	}
	
	SWD_IND();
	//SWD_Delay();
	for(cnt = 0; cnt < 8; cnt++)
	{
		bRes >>= 1;
		SWC_CLR();
		SWD_Delay();
		if(SWDIF_PIN & SWDIF_DAT)
			bRes |= 0x80;

		SWC_SET();
		SWD_Delay();
	}
	
	SWD_OUD();
	
	SWC_CLR();
	if(stop) SWD_SET();
	else SWD_CLR();

	SWD_Delay();
	SWC_SET();
	SWD_Delay();
	
	return bRes;
}

一些操作需要通过一些时序才能完成,故有些需加上SWD_Idle等待.

void SWD_Idle(uint8_t cnt)
{
	volatile uint8_t i;

	SWD_SET();
	
	for(i = 0; i < cnt; i++)
	{
		SWC_CLR();
		SWD_Delay();
		SWC_SET();
		SWD_Delay();
	}
}
Flash写入相关
  • 读取SWD ID
    SWD_ReadSWDID()
  • Unlock()操作,因芯片的保护机制,掉电再上电后,是不能通过SWD接口来读取Flash中的数据的,此Unlock操作相当于对芯片进行全芯片擦除,之后所读数据全部为0xff,并允许写入操作.
uint8_t SWD_UnLock()
{
...
	SWD_UnLock0();
	SWD_EEE_UnlockTiming();
	SWD_UnLock1();
	delayus(100);
	SWD_UnLock0();
	delayus(100);
	SWD_UnLock1();	
...
}
  • 读取Read(),因flash为16位,参数addrword地址,每次读取两个字节,返回一个word数据

uint16_t SWD_EEE_Read(uint16_t addr)
{
	volatile uint8_t hbyte, lbyte;
	
	SWD_EEE_CSEQ(0x00, addr);
	SWD_EEE_CSEQ(0xa0, addr);
	
	SWD_WriteByte(1, 0xaa, 1);
	lbyte = SWD_ReadByte(1, 0);
	hbyte = SWD_ReadByte(0, 1);
	SWD_Idle(10);
	SWD_EEE_CSEQ(0x00, addr);

	return (hbyte << 8) | lbyte;
}
  • 写入write(),因flash为16位,参数addr为word地址,每次写入两个字节,即一个word数据
void SWD_EEE_Write(uint16_t data, uint16_t addr)
{
	volatile uint8_t ib;
	volatile uint8_t timout = 0x1f;
	
	SWD_EEE_CSEQ(0x00, addr);
	SWD_EEE_DSEQ(data);
	SWD_EEE_CSEQ(0x04, addr);
	SWD_EEE_CSEQ(0x84, addr);
	SWD_EEE_CSEQ(0x02, addr);
	
	do {
		delayus(50);
		ib = SWD_EEE_GetBusy();
		--timout;
	} while(ib == 1 && timout > 0);
	
	SWD_EEE_CSEQ(0x00, addr);
}

以上16位地址和数据输入输出均为低字节优先,然后再高字节,即little-endian模式.

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值