华大MCU的应用中的问题记录

摘要:
因为货期和价格的问题,我所在公司的一些方案也开始转向使用国产单片机,综合来看,华大MCU是一个比较好的选择。刚开始应用这个MCU的时候我遇到了不少问题,现在记录下来,以防自己忘记了。但是华大的库函数提供的延时函数并不准确,延时1ms实际上延时了1.4ms左右。

这两年芯片的价格越炒越贵,特别是像STM32,TI,NXP等等知名品牌更是水涨船高,甚至有时候你即使你愿意花大价钱去购买,也不一定能买得到,所以很多公司纷纷转向了国产芯片。国产芯片其实也不太好买得到,价钱也不便宜,而且可供参考的资料也是寥寥无几,有时候你遇到了问题想上网找资料都很难找到可以参考的,这也是国产芯片的一个弱势吧。

废话不多说了,接下来谈谈华大单片机的一些应用吧。因为货期和价格的问题,我所在公司的一些方案也开始转向使用国产单片机,综合来看,华大MCU是一个比较好的选择。刚开始应用这个MCU的时候我遇到了不少问题,现在记录下来,以防自己忘记了。

1、Timer0

华大MCU有提供库函数,可以在KEIL或者IAR软件上进行编程,大部分常用的外设都可以找到,像串口,定时器,I2C,SPI等等都可以找到demo code,可以直接上华大的官网查找。但是华大的库函数提供的延时函数华大MCU的应用中的问题记录第1张并不准确,延时1ms实际上延时了1.4ms左右。延时1us实际延时了2.4us。如下图:

华大MCU的应用中的问题记录第2张华大MCU的应用中的问题记录第3张

这个时间差距是有点离谱,连FAE也坦言一般情况下他们都不会用到这两个延时,因为这两个延时好像是利用内部RC振荡器分频得到的,所以并不准确,且容易受到温度的影响。

所以我动手写了一个利用Timer0的精准延时,开始的设想是不利用中断,因为利用中断有些浪费资源了,但是实验效果并不是特别好,延时不准确,所以我还是开启了中断,只是在没有使用延时的时候,可以把时间设置得长一些,这样可以避免频繁进入中断。假设要写一个精准的微妙级别的延时函数,思路是这样的,把Timer0的时钟源选择为PCLK1,然后通过分频把频率降到1MHz,这样的话就可以CNT每隔1us就会增加一次,然后设置基准值寄存器(TMR0_CMPA<B>R
)的值,当CNT增加到基准值寄存器的数值时将会产生一个中断。比如基准值寄存器设置为10,那就是10us会产生一个中断,进入中断后对某一个我们自定义的标志位进行置位,同时延时函数中等待这个标志位置位,等不到就死等,这样就可以形成一个精准延时。具体代码如下:

/*定时器时钟初始化 */
void TimerInit(void)
{  
   stc_tim0_base_init_t stcTimerCfg;
    stc_irq_regi_conf_t stcIrqRegiConf;
    stc_port_init_t stcPortInit;

    stc_clk_freq_t stcClkTmp;
    uint32_t u32tmp;

    MEM_ZERO_STRUCT(stcTimerCfg);
    MEM_ZERO_STRUCT(stcIrqRegiConf);
    MEM_ZERO_STRUCT(stcPortInit);


    /*Get pclk1 */CLK_GetClockFreq(&stcClkTmp);
    u32Pclk1 =stcClkTmp.pclk1Freq;

    /*Enable XTAL32 */CLK_Xtal32Cmd(Enable);

    /*Timer0 peripheral enable */ENABLE_TMR0();

    /*config register for channel B */stcTimerCfg.Tim0_CounterMode =Tim0_Sync;
    stcTimerCfg.Tim0_SyncClockSource =Tim0_Pclk1;
    stcTimerCfg.Tim0_ClockDivision =Tim0_ClkDiv2;
    stcTimerCfg.Tim0_CmpValue = (uint16_t)((u32Pclk1/1000000/2ul)*1000 - 1ul);
    TIMER0_BaseInit(TMR_UNIT,Tim0_ChannelB,&stcTimerCfg);

    /*Enable channel B interrupt */TIMER0_IntCmd(TMR_UNIT,Tim0_ChannelB,Enable);
    /*Register TMR_INI_GCMB Int to Vect.No.002 */stcIrqRegiConf.enIRQn =Int002_IRQn;
    /*Select I2C Error or Event interrupt function */stcIrqRegiConf.enIntSrc =TMR_INI_GCMB;
    /*Callback function */stcIrqRegiConf.pfnCallback = &Timer0B_CallBack;
    /*Registration IRQ */enIrqRegistration(&stcIrqRegiConf);
    /*Clear Pending */NVIC_ClearPendingIRQ(stcIrqRegiConf.enIRQn);
    /*Set priority */NVIC_SetPriority(stcIrqRegiConf.enIRQn, DDL_IRQ_PRIORITY_15);
    /*Enable NVIC */NVIC_EnableIRQ(stcIrqRegiConf.enIRQn);

    /*start timer0*/TIMER0_Cmd(TMR_UNIT,Tim0_ChannelB,Enable);

}
//中断回调函数
void Timer0B_CallBack(void)
{
    /*同步计数方式中断,该方式定时更加准确*/TimerFlag = 1;
}
//延时函数
voidTIM0_CHB_Delay_us(uint16_t us)
{
        TMR_UNIT->CMPBR = (u32Pclk1/1000000/2ul)*us-1ul;
        TMR_UNIT->CNTBR = 0;
        TimerFlag = 0;
        while(!TimerFlag);    
        //延时时间到了,重新修改基准值寄存器的值,使其不频繁进入中断,不过不设置也是可以的
        TMR_UNIT->CMPBR = (u32Pclk1/1000000/2ul)*1000-1ul;
        TMR_UNIT->CNTBR = 0;
}                

但是不知道什么原因,1us的时候并不准确,1us延时的时候实际测得是2.6us,但是1us以上就很精确了。

华大MCU的应用中的问题记录第4张华大MCU的应用中的问题记录第5张华大MCU的应用中的问题记录第6张

2、GPIO

华大MCU的GPIO有一些默认是具备特殊功能的,PA13PA14PA15PB3PB4 端口复位后初始状态为 JTAG/SWD 功能有效,我的板子刚好LED灯是接在PA15上的,所以当我的板子在上电的时候,就看到这个LED亮着,但又不是完全亮着,用万用表量了电平是1.7V左右,无论我怎么操作这个引脚,这个引脚的电平就是不为所动。后面才知道原因是因为它默认就是特殊功能,如果要正常操作这个引脚,必须修改它的功能,步骤就是:

①需要先解锁,才能对寄存器进行修改;

②因为要把这个引脚的默认状态TDI修改到GPO,所以需要先使这个TDI的功能无效,具体是修改特殊控制寄存器(PSRCR)b3的值,从1改为0;

③功能选择寄存器(PFSRxy,x=A,B,...,H,y=1,2,...,15)的FSEL[5:0]设为b000000,表示选择为Func0

④上锁

华大MCU的应用中的问题记录第7张华大MCU的应用中的问题记录第8张

具体代码:

stc_port_init_t stcPortInit;
MEM_ZERO_STRUCT(stcPortInit);
    
stcPortInit.enPinMode = Pin_Mode_Out;
stcPortInit.enExInt = Enable;
stcPortInit.enPullUp = Disable;

PORT_Unlock();
M4_PORT->PSPCR = 0x17;
M4_PORT->PFSRA15 &= ~(0x3f);
PORT_Lock();

//#define LED1_PORT     (PortA)
//#define LED1_PIN     (Pin15)
PORT_Init(LED1_PORT, LED1_PIN, &stcPortInit);

3、UART

在这次项目中,UART我是直接从官方例程中移植到我的项目中,但是发现并没有数据传送出来,或者隔了很久才接收到板子上发出的一些错误的数据。所以我用KEIL仿真模式进行调试,发现程序死在了BEAB BKPT 0xAB处,上网查找了资料,具体的原因我还是不太清楚,大概就是我使用了printf()函数,使用了半主机模式,就会出现这种情况,解决的办法就是使用微库,也就是MiclroLIB,即勾选上USEMiclroLIB,重新编译即可。如图:

华大MCU的应用中的问题记录第9张

这个我在使用STM32单片机的时候没有遇到过,好像只要重定向了都可以。

4、SMBus

SMBus是一种类似I2C的协议,大多数情况下工程师都会选择用模拟SMBus来进行通讯,当然也可以用硬件SMBus。在上一版项目中我也是用了模拟SMBus来实现通讯的,经过验证并没有问题,这一次我在移植过来的时候发现通讯不上了,用示波器和逻辑分析仪看过,还是不知道问题出在哪里(逻辑分析仪用得不熟练)。我用示波器看过官方的延时函数的精度,发现差距比较大,所以怀疑是延时函数的问题,导致时序出错,所以我才研究了用定时器0(Timer0)做精准延时的函数(上面有讲述),但是发现实际上还是没有作用。后面我去慢慢一条一条地比较代码,终于发现一点蛛丝马迹,原来我在采集电池信息的时候,没有参考上一版的程序,当时觉得写得比较乱,所以在网上找了一点资料参考,逻辑还是一样的,只是在某些地方延时不一样,网上的资料延时比较短,当我完完全全复制我前一版代码的时候,发现问题解决了!我勒个天,我调了好几天没调出来的问题居然是不够自信,没有参考自己的劳动成果造成的!

接下来,我把SMBus的代码贴出来,除了自己以后可以参考,也希望可以帮到有需要的人,这个代码是MCU作为主机通过SMBus跟电池通讯(电池的电源控制芯片是BQ4050,默认从机地址是0x16):

#include "SMBus.h"
#include "timer.h"
/********************************   SMBus 1   
*#define SMBus1_SCL_PORT		(PortB)
#define SMBus1_SCL_PIN			(Pin06)

#define SMBus1_SDA_PORT		(PortB)
#define SMBus1_SDA_PIN			(Pin07)

#define SMBus1_SCL_H				PORT_SetBits(SMBus1_SCL_PORT,SMBus1_SCL_PIN)

							
#define SMBus1_SCL_L				PORT_ResetBits(SMBus1_SCL_PORT,SMBus1_SCL_PIN)


#define SMBus1_SDA_H				PORT_SetBits(SMBus1_SDA_PORT,SMBus1_SDA_PIN)

#define SMBus1_SDA_L				PORT_ResetBits(SMBus1_SDA_PORT,SMBus1_SDA_PIN)


#define SMBus1_READ_SDA		PORT_GetBit(SMBus1_SDA_PORT,SMBus1_SDA_PIN)
*
*
********************************/
/*********************************
*函数名称:void SMBus1_SDA_OUT(void)
*函数功能:SDA线的引脚配置为输出
*函数形参:无
*函数返回值:无
*********************************/
void SMBus1_SDA_OUT(void)
{
		stc_port_init_t stcPortInit;
	  MEM_ZERO_STRUCT(stcPortInit);
    
    stcPortInit.enPinMode = Pin_Mode_Out;
    stcPortInit.enExInt = Enable;
    stcPortInit.enPullUp = Enable;
		stcPortInit.enPinDrv = Pin_Drv_H;
		stcPortInit.enPinOType = Pin_OType_Cmos;

		PORT_Init(SMBus1_SDA_PORT, SMBus1_SDA_PIN, &stcPortInit);
}
/*********************************
*函数名称:void SMBus1_SDA_IN(void)
*函数功能:SDA线的引脚配置为输入
*函数形参:无
*函数返回值:无
*********************************/
void SMBus1_SDA_IN(void)
{
		stc_port_init_t stcPortInit;
	  MEM_ZERO_STRUCT(stcPortInit);
    
    stcPortInit.enPinMode = Pin_Mode_In;
    stcPortInit.enExInt = Enable;
    stcPortInit.enPullUp = Enable;
	
		PORT_Init(SMBus1_SDA_PORT, SMBus1_SDA_PIN, &stcPortInit);
}

/*********************************
*函数名称:void SMBus1_Init(void)
*函数功能:SDA和SCL初始化
*函数形参:无
*函数返回值:无
*备注:都配置为上拉推挽输出(不上拉,开漏好像也没影响)  
*********************************/
void SMBus1_Init(void)
{
		stc_port_init_t stcPortInit;
	  MEM_ZERO_STRUCT(stcPortInit);
    
    stcPortInit.enPinMode = Pin_Mode_Out;
    stcPortInit.enExInt = Enable;
    stcPortInit.enPullUp = Enable;
		stcPortInit.enPinDrv = Pin_Drv_H;
		stcPortInit.enPinOType = Pin_OType_Cmos;
		
		PORT_Init(SMBus1_SCL_PORT, SMBus1_SCL_PIN, &stcPortInit);
		PORT_Init(SMBus1_SDA_PORT, SMBus1_SDA_PIN, &stcPortInit);
	
		SMBus1_SCL_H; 
		SMBus1_SDA_H;
	
}


/*********************************
*函数名称:void SMBus1_Start(void)
*函数功能:SMBus开始通讯
*函数形参:无
*函数返回值:无  
*********************************/
void SMBus1_Start(void)
{
	SMBus1_SDA_OUT(); //sda线输出
	SMBus1_SCL_L;
	Ddl_Delay1us(2);
	SMBus1_SDA_H;
	Ddl_Delay1us(1);
	SMBus1_SCL_H;
	Ddl_Delay1us(9);
	SMBus1_SDA_L;
	Ddl_Delay1us(9);
	SMBus1_SCL_L;//钳住I2C总线,准备发送或接收数据
}


/*********************************
*函数名称:void SMBus1_Stop(void)
*函数功能:SMBus停止通讯
*函数形参:无
*函数返回值:无  
*********************************/
void SMBus1_Stop(void)
{
	SMBus1_SDA_OUT(); //sda线输出
	SMBus1_SCL_L;
	Ddl_Delay1us(1);
	SMBus1_SDA_L; //STOP:when CLK is high DATA change form low to high
	Ddl_Delay1us(9);
	SMBus1_SCL_H;
	Ddl_Delay1us(9);
	SMBus1_SDA_H;//发送I2C总线结束信号
	Ddl_Delay1us(9);
}


/***********************************************
*函数名称:uint8_t SMBus1_Wait_Ack(void)
*函数功能:SMBus等待应答
*函数形参:无
*函数返回值:uint8_t类型,返回1表示超时,返回0表示接收到应答
************************************************/
uint8_t SMBus1_Wait_Ack(void)
{
	uint16_t uErrTime=0;

	SMBus1_SDA_IN(); //SDA设置为输入
	SMBus1_SDA_H;
	Ddl_Delay1us(9);
	SMBus1_SCL_H;
	Ddl_Delay1us(9);
	while(SMBus1_READ_SDA) 
	{
	uErrTime++;
	if(uErrTime > 250)
	{
	SMBus1_Stop();
	return 1;
	}
	//hrt_delay_us(1);
	}
	SMBus1_SCL_L; //时钟输出0

	return 0;
}

/***********************************************
*函数名称:void SMBus1_Ack(void)
*函数功能:SMBus产生应答信号
*函数形参:无
*函数返回值:无
************************************************/
void SMBus1_Ack(void)
{
	SMBus1_SCL_L;
	SMBus1_SDA_OUT();
	SMBus1_SDA_L;
	Ddl_Delay1us(9);
	SMBus1_SCL_H;
	Ddl_Delay1us(9);
	SMBus1_SCL_L;
}

/***********************************************
*函数名称:void SMBus1_Ack(void)
*函数功能:SMBus产生非应答信号
*函数形参:无
*函数返回值:无
************************************************/
void SMBus1_NAck(void)
{
	SMBus1_SCL_L;
	SMBus1_SDA_OUT();
	SMBus1_SDA_H;
	Ddl_Delay1us(9);
	SMBus1_SCL_H;
	Ddl_Delay1us(9);
	SMBus1_SCL_L;
}

/***********************************************
*函数名称:void SMBus1_Send_Byte(void)
*函数功能:SMBus发送一个字节的数据
*函数形参:无
*函数返回值:无
************************************************/
void SMBus1_Send_Byte(uint8_t txd)
{
	uint8_t t=0;

	SMBus1_SDA_OUT();
	SMBus1_SCL_L;//拉低时钟开始数据传输
	for(t=0;t<8;t++)
	{
		if((txd&0x80)>>7)
		{
			SMBus1_SDA_H;
		}
		else
		{
			SMBus1_SDA_L;
		}
		txd <<= 1;
		Ddl_Delay1us(8);
		SMBus1_SCL_H;
		Ddl_Delay1us(8);
		SMBus1_SCL_L;
		Ddl_Delay1us(8);
	}
}

/***********************************************
*函数名称:uint8_t SMBus1_Read_Byte(void)
*函数功能:SMBus接收一个字节的数据
*函数形参:无
*函数返回值:返回这个数据
************************************************/
uint8_t SMBus1_Read_Byte(void)
{
	uint8_t i;
	uint8_t recv=0;

	SMBus1_SDA_IN(); //SDA设置为输入
	for(i=0; i<8; i++)
	{
		SMBus1_SCL_L;
		Ddl_Delay1us(12);
		SMBus1_SCL_H;
		recv <<= 1;
		if(SMBus1_READ_SDA)
		{
			recv++;
		}
		Ddl_Delay1us(9);
	}

	return recv;
}

通讯的基础函数在网上都可以找得到,接下来是跟BQ4050的通讯部分,获取电池信息:

/************************************************************
*函数名称:int16_t Get_Battery1_Info(uint8_t slaveAddr, uint8_t Comcode)
*函数功能:获取电池信息
*函数形参:slaveAddr,从机地址,Comcode,命令
*函数返回值:将数据返回出来,可能是电压,电流,RSOC,RMC,温度等,具体跟Comcode相关
*************************************************************/
int16_t Get_Battery1_Info(uint8_t slaveAddr, uint8_t Comcode)
{  
	int16_t Value;
	 uint8_t data[2] = {0};

	SMBus1_Start();
	SMBus1_Send_Byte(slaveAddr);//发送地址
	if(SMBus1_Wait_Ack() == 1)
	{
		batterry_info.LostContact[0] = 1;
//		printf("SlaveAddr wait ack fail!
");
		return -1;
	}
	SMBus1_Send_Byte(Comcode);
	Ddl_Delay1us(90);            //需要注意的是,这个地方的延时特别长
	if(SMBus1_Wait_Ack() == 1)
	{
		batterry_info.LostContact[0] = 1;
//		printf("Comcode wait ack fail!
");
		return -1;
	}

	SMBus1_Start();
	SMBus1_Send_Byte(slaveAddr|0x01);//发送地址
	if(SMBus1_Wait_Ack() == 1)
	{
		batterry_info.LostContact[0] = 1;
//		printf("slaveAddr+1 wait ack fail!
");
		return -1;
	}
	Ddl_Delay1us(50);         //需要注意的是,这个地方的延时特别长
	

		data[0] = SMBus1_Read_Byte(); 
		SMBus1_Ack();
		Ddl_Delay1us(125);       //需要注意的是,这个地方的延时特别长
		data[1] = SMBus1_Read_Byte();
		SMBus1_NAck();

	Ddl_Delay1us(58);            
	SMBus1_Stop();
	Value = (data[0] |(data[1]<<8));
	
	batterry_info.LostContact[0] = 0;
	Ddl_Delay1us(100);

	return Value;
}

免责声明:文章转载自《华大MCU的应用中的问题记录》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇深入浅出-多租户微信小程序---》分包加载下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

如何获取STM32 MCU的唯一ID

  前段时间由于应用需要对产品授权进行限制,所以研究了一下有关STM32 MCU的唯一ID的资料,并最终利用它实现了我们的目标。 1、基本描述   在STM32的全系列MCU中均有一个96位的唯一设备标识符。在ST的相关资料中,对其功能的描述有3各方面: 用作序列号(例如 USB 字符串序列号或其它终端应用程序) 在对内部 Flash 进行编程前将唯一...

【电路】DC风扇控制

一、选择风扇类型:Qualtek FAD1-06020CSHW11 12V/0.16A 只有VCC和GND两根线外接,内部采用AH276霍尔磁性传感器。(DIODES or Anachip) 实测数值: 12V-85mA  11V-78mA   10V-72mA  9V-66mA  8V-59mA 7V-53mA  6V-47mA  5V-42mA  4...

MCU与FPGA通信

1.MCU启动FPGA相应功能模块 通过译码器选择相应的功能模块,调用实现功能。 2.MCU与FPGA串口通信 SPI协议简单、可靠、易实现,速度快,推荐使用SPI。SPI为四线机制,包含MOSI、MISO、SCK、SSEL。 (SPI协议:http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_B...

痞子衡嵌入式:盘点国内RISC-V内核MCU厂商(2020年发布产品)

大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是国内RISC-V内核MCU厂商(2020)。 虽然RISC-V风潮已经吹了好几年,但2019年才是其真正进入主流市场的元年,最近国内大量芯片公司崛起,其中有很多公司想在RISC-V新赛道有一番作为,毕竟ARM内核早已是红海,而RISC-V尚处于蓝海。今天痞子衡就为大家盘点一下发布过RISC-...

新能源汽车三大核心技术

            在新能源汽车的整个平台架构中,VCU (Vehicle Control Unit整车控制器)、MCU (Moter Control Unit 电机控制器)和 BMS (BATTERY MANAGEMENT SYSTEM 电池管理系统)是最重要的核心技术,对整车的动力性、经济性、可靠性和安全性等有着重要影响。 目录: VCU MCU...

痞子衡嵌入式:恩智浦i.MX RT1xxx系列MCU特性那些事(4)- RT105x选型

  大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是恩智浦i.MX RT1xxx系列MCU的RT105x选型。   大家都知道i.MX RT105x是i.MX RT系列第一款产品,在提这款产品特性的时候,我们往往说的是i.MXRT1052DVL6B的特性,这也是RT105x系列主推的一款核心芯片,目前一些第三方硬件公司(比如ZLG、野火、...