中国互联网协会副会长名单百度seo规则最新
今天同事让我帮忙打印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 ACKdo{intstatus = I2cReg->I2C_ISR;// Clear INT_MACK statusif ((intstatus & I2C_INT_AL) != 0){// Clear INT_AL statusI2cReg->I2C_ISR &= ~(I2C_INT_AL);// stopI2CStop();return ERROR;}Delay10cyc(1);timeout--;}while (((intstatus & I2C_INT_MACKP) == 0) && (timeout > 0));*Data = (UINT8)(I2cReg->I2C_MRXR);// Clear INT_MACKP statusI2cReg->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"); .........
最后输出结果