今天同事让我帮忙打印DSP芯片的配置信息,该信息在内存里,所以要打印内存某个指定地址的内容。怎么实现?第一次做这么底层的事情,总结下经验。
刚好前两个星期把驱动看完了,知道实现思路如下:
1.因为用的是CortexM3系统,没有移植文件系统,所以,不能打印成文件,只能用串口输出。
2.串口输出用什么函数?printf就可以了,系统一般也提供了DEBUG宏,该宏只是封装了printf。
3.printf怎么实现的?要有串口驱动uart.c.他是库函数,好像需要先在串口驱动中实现putc函数,不过我没发现uart驱动有这个函数,以后有机会再研究。网络搜到解释:
printf需要调用 _putc函数,你在自己的代码里,重定向 _putc就行。在_putc里面向串口输出字符。具体的实现方法在对应的编译器里面有
我转了3篇文章,研究这个问题,估计在Keil或者其它开发工具里,要配置串口和GNU的接口,让你写好的UART的read, write函数关联到printf。这个过程也可能是自动的,只不过是我不知道去哪里找这个设置。
http://blog.csdn.net/xzongyuan/article/details/28626163
http://blog.csdn.net/xzongyuan/article/details/28625457
http://blog.csdn.net/xzongyuan/article/details/28632495 (STM32 keil printf的使用)
下面是最新的研究成果,在我的系统里,看到debug.c定义了fputc,应该是利用这个函数实现UART和fprintf的重定向的。
http://blog.csdn.net/xzongyuan/article/details/28634221
函数如:
void fputc_hook(char ch)
{
if (DebugType == 0)
{
UARTWriteByte(ch, 1000);
}
else
{
VirtualUartWrite(ch);
}
}
int fputc(int ch, FILE *f)
{
uint8 dgbBuffer[DEBUG_TIME_LEN];
uint32 tmpcnt, i;
if (ch == '\n')
{
tmpcnt = SysTickCounter;
for (i = 0; i < DEBUG_TIME_LEN; i++)
{
dgbBuffer[i] = tmpcnt % 10;
tmpcnt = tmpcnt / 10;
}
fputc_hook('\r');
fputc_hook('\n');
fputc_hook('[');
for (i = 0; i < DEBUG_TIME_LEN; i++)
{
fputc_hook(dgbBuffer[DEBUG_TIME_LEN - 1 -i]+0x30);
if (DEBUG_TIME_LEN - 1 -i == 2)
{
fputc_hook('.');
}
}
fputc_hook(']');
return OK;
}
fputc_hook(ch);
return OK;
}
uart.c的相关代码如下,其它函数如debug.c会调用UARTWriteByte和其他的基础函数(用来实现fputc,建立printf和uart的关联):
int32 UARTReadByte(uint8 *pdata, uint32 uartTimeOut)
{
while ((UartReg->UART_USR & UART_RECEIVE_FIFO_NOT_EMPTY) != UART_RECEIVE_FIFO_NOT_EMPTY)
{
if (uartTimeOut == 0)
{
return (-1);
}
uartTimeOut--;
}
*pdata = (uint8 )UartReg->UART_RBR;
return (0);
}
#endif
void BT_UARTSend(char * data, int len)
{
// memcpy(UartContext.TxBuffer, data, len);
// while(UartContext.isInTx);
UartContext.isInTx = 1;
UartContext.TxBufferP = data;
UartContext.TxBufferSize = len;
UartContext.TxOutIndex = 0;
UARTSetIntEnabled(UART_IE_TX);
//UartContext.TxOutIndex++;
//UartReg->UART_THR = UartContext.TxBuffer[0];
//UartReg->UART_IIR = UartReg->UART_IIR | UART_IF_THR_EMPTY;
}
4.打印数据的驱动已经有了,我们不用管,只需要像普通的C语言一样写printf就行了。但是怎么获取DSP芯片的数据呢?
5.一般厂家都会提供DSP芯片的驱动,所以我只需要调用这个DSP芯片的API
uint8 Codec_DSPReadMem(uint16 wAdd, uint8 *pbData, uint16 wWordSize)
这个函数,虽然有很多判断语句,但是核心的就是读取I2C数据,因为Soc和DSP之间,一般都是通过I2C协议通信。所以,我找到了核心函数如下:
if (OK != I2CReadData(pbData))
判断读取数据是否成功,把数据读到pbData指针。
这个函数时怎么定义的呢?参考下面两句
#define I2CReadData(Data) (((pI2CReadData)(Addr_I2CReadData))(Data))
typedef int32 (*pI2CReadData)(UINT8 *Data);
这样看,它是一个宏,定义了一个指针函数,通常,我的理解是这个指针函数会在某个地方被赋值,给他一个实际的函数值(这样,同样的对象,可以调用不同的函数)。但这个地方不是这样用的,他声明一个函数指针,是因为它的作用是把一个void类型的指针转化为函数指针类型,上面的Addr_I2CReadData其实就是一个宏,如下代码,定义了一个指针地址(通常,我们需要把一些常用的模块的函数,指定在一个指定的内存区域,压缩空间,避免系统随机申请函数地址,导致内存碎片太多):
#define Addr_I2CReadData (0x00004248)
那这个函数怎么定义呢?如下,把宏看做是一个函数名。里面的内容都是驱动的知识,控制I2C的控制寄存器,这部分内容不难,但是要弄懂不同控制器的datasheet对应的“时序”:
_ATTR_DRIVERLIB_CODE_ //这个是一个属性标识,见解释
int32 I2CReadData(UINT8 *Data)
{
int intstatus;
int timeout; // 超时退出
timeout = 200000; // timeout要足够长,以适应个别I2C应答较慢的情况
I2cReg->I2C_LCMR |= I2C_LCMR_RESUME;
// waiting ACK
do
{
intstatus = I2cReg->I2C_ISR;
// Clear INT_MACK status
if ((intstatus & I2C_INT_AL) != 0)
{
// Clear INT_AL status
I2cReg->I2C_ISR &= ~(I2C_INT_AL);
// stop
I2CStop();
return ERROR;
}
Delay10cyc(1);
timeout--;
}
while (((intstatus & I2C_INT_MACKP) == 0) && (timeout > 0));
*Data = (UINT8)(I2cReg->I2C_MRXR);
// Clear INT_MACKP status
I2cReg->I2C_ISR &= ~(I2C_INT_MACKP);
return OK;
}
#endif
代码中的属性标识是一个宏
#define _ATTR_DRIVERLIB_CODE_ __attribute__((section("DriverLib")))
这是ARM中的scatter功能,通过自定义的scatter文档,可指定某个驱动模块在内存某个地址0x0000aaaa内运行。这个知识要查看ARM官网的scatter文件。
I2C时序图:
6.通过上面的API,我获得了一个指针地址pbData,该地址存放着从I2C中读到的数据。我只需要把这个数据用printf打印出来就可以了。代码如下,要注意打印的是十六进制,且有的数据时8位,有的16位,要小心,不然会打错。
下面的代码是打印0x4000(DSP_MEM_ADD_CRAMTOP)到0x43FF的内容。因为I2C缓存有限,一次读取太多数据会死机,所以,我每次读0x020个字节,循环读取并打印。
BOOLEAN AudioPause(void)
{
uint8 _DSPResult = -1 ;
uint8 _Data[0x010] = {0} ;
uint8 *_DataPointer = _Data;
uint8 i = 0;
uint8 j = 0;
uint16 wordSize = (uint16)0x10;
uint16 repeat = (uint16)0x20;
uint16 _startAddr = DSP_MEM_ADD_CRAMTOP;
if (AUDIO_STATE_PLAY == AudioPlayState)
{
<span style="white-space:pre"> </span>.......
<span style="white-space:pre"> </span>//add by norton , just for dump the data of yda174
<span style="white-space:pre"> </span>printf("\n*********data of yda174************\n");
<span style="white-space:pre"> </span>while(i<repeat)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>_DSPResult = Codec_DSPReadMem(_startAddr+i*byteSize,_DataPointer+i*byteSize,wordSize);
<span style="white-space:pre"> </span>// printf("result code is: %d\n",_DSPResult);
<span style="white-space:pre"> </span>printf("[%X-%X]:",_startAddr+i*byteSize,_startAddr+(i+1)*byteSize);
<span style="white-space:pre"> </span>for(j=0;j<32;j++){
<span style="white-space:pre"> </span>printf("%02X ",*(_DataPointer+i*byteSize+j));
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>printf("\n");
<span style="white-space:pre"> </span> i++ ;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>i = 0;
<span style="white-space:pre"> </span>printf("\n*********data of yda174************\n"); .........
最后输出结果