一、STM32单片机外接16Mhz晶振时,需要更改单片机头文件里关于时钟的宏定义以及 system_stm32f0xx.c中的倍频数,因为默认是8Mhz。如会导致串口打印乱码等。
16M * 3 = 48M
二、
问题:焊接下图这种封装的芯片时,UFQFPN32封装,底部为GND时,如果与焊盘虚焊,那么可能导致能读到芯片但是下载不了程序。
三、
问题:
烧录程序后就不能再次烧录程序了,且读不到芯片。
解决:可能是上一次烧录的程序导致下载引脚被禁用,此时将BOOT0引脚拉高就可读取到芯片,下载一个没有禁用引脚的程序即可。也可使用Jlink软件进行Flash的整块擦除。
四、
问题:串口发送连续数据包不完整。当上位机下发给单片机查询数据包时,单片机应该进行数据的回复,但实际发现只回复了包内的两个数据回来,因为回来的数据包是连续发送的。单步调试的时候回来的数据包完整,全速运行时回来的数据包只有两个数据。
解决:在每发一个字节的数据后加上延时,即可解决。
五、移植同系列其他芯片工程时,需要更改头文件的宏定义。如使用STM32F051的工程模板改为STM32F031,那么需要更改stm32f0xx.h中的宏定义:
六、STM32串口发送连续的数据出去的时候,使用while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);这条语句将会更好,这里面的USART_FLAG_TC标志位是发送完成标志位。
七、晶振知识与嘀嗒定时器:1M的晶振就1s震动1000000次,那么振动1次就是0.000001s,也就是1us。就SYStick定时器为例,当其时钟为系统时钟(48M)的8分频后得到的时钟为6M,那么该时钟1s震动6000000次,1us震动6次;同理,定时器的分频系数也一样。
八、LWIP连接过程中更改IP地址
1、关闭端口、强制删除TCP_server主动断开时的Time_Wait、释放pcb控制块的内存和对应的数组
2、初始化IP,网关,子网掩码,禁用网卡,重设IP,网关,子网掩码,启用网卡。
3、创建服务器即可或直接可以ping通
九、STM32程序运行一段时间后程序卡在“0x0800XXXX BEAB BKPT 0xAB”处,可能是使用了printf函数导致的,解决方法如下:
#include "stdio.h"
//----------------------------//
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
/* Whatever you require here. If the only file you are using is */
/* standard output using printf() for debugging, no file handling */
/* is required. */
};
FILE __stdout;
void _sys_exit(int x)
{
x = x;
}
//重定向printf函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);
USART1->DR = (u8) ch;
return ch;
}
//---------------------------------//
十、STM32程序使用内部参考电压作为基准,其他通道采集电压,开启ADC采集完成后关闭ADC,导致功耗上升30ua左右,解决方法:
1、关闭ADC电压调节器
2、关闭ADC参考电压通道
3、关闭ADC时钟
其他通道电压计算公式:
Vchx = VrefInt * (ADchx/ADrefInt);
Vchx : 通道x的电压
VrefInt :STM32内部参考电压,通过数据手册查看约为1.2v左右
ADchx:通道x实际采集到的电压
ADrefInt:内部通道采集到的电压
代码如下:
// PB1----ADC_IN8
void ADInit(void)
{
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
/* (1) Select HSI16 by writing 00 in CKMODE (reset value) */
/* (2) Select the auto off mode */
/* (3) Select CHSEL17 for VRefInt */
/* (4) Select a sampling mode of 111 i.e. 239.5 ADC clk to be greater than 17.1us */
/* (5) Wake-up the VREFINT (only for VLCD, Temp sensor and VRefInt) */
ADC1->CFGR2 &= ~ADC_CFGR2_CKMODE; /* (1) */
ADC1->CFGR1 |= ADC_CFGR1_AUTOFF; /* (2) */
ADC1->CHSELR = ADC_CHSELR_CHSEL17;//ADC_CHSELR_CHSEL3; /* (3)
ADC1->SMPR |= ADC_SMPR_SMP_0 | ADC_SMPR_SMP_1 | ADC_SMPR_SMP_2; /* (4) */
ADC->CCR |= ADC_CCR_VREFEN; /* (5) */
/* (1) Ensure that ADEN = 0 */
/* (2) Clear ADEN */
/* (3) Set ADCAL=1 */
/* (4) Wait until EOCAL=1 */
/* (5) Clear EOCAL */
if ((ADC1->CR & ADC_CR_ADEN) != 0) /* (1) */
{
ADC1->CR &= (uint32_t)(~ADC_CR_ADEN); /* (2) */
}
ADC1->CR |= ADC_CR_ADCAL; /* (3) */
while ((ADC1->ISR & ADC_ISR_EOCAL) == 0) /* (4) */
{
/* For robust implementation, add here time-out management */
}
ADC1->ISR |= ADC_ISR_EOCAL; /* (5) */
/* (1) Enable the ADC */
/* (2) Wait until ADC ready if AUTOFF is not set */
ADC1->CR |= ADC_CR_ADEN; /* (1) */
if ((ADC1->CFGR1 & ADC_CFGR1_AUTOFF) == 0)
{
while ((ADC1->ISR & ADC_ISR_ADRDY) == 0) /* (2) */
{
/* For robust implementation, add here time-out management */
}
}
}
// 4.
u16 GetBatADCValue(void)
{
u16 dat1,dat;
float vol;
BatteryOn;
ADInit();
ADC1->CR |= ADC_CR_ADSTART;
while ((ADC1->ISR & ADC_ISR_EOC) == 0); /* wait end of conversion */
dat1 = ADC1->DR;
ADC1->CHSELR = ADC_CHSELR_CHSEL9;
ADC1->CR |= ADC_CR_ADSTART;
while ((ADC1->ISR & ADC_ISR_EOC) == 0); /* wait end of conversion */
dat = ADC1->DR;
BatteryOff;
DisableADC();
vol = 1.2 * ((float)dat/(float)dat1) * 2 + 0.08;
Card.Bat = ((u8)(vol*10)) & 0x3F;
RangeData.Bat = Card.Bat;
if(vol < 3.4)
Card.Flag |= 0x04;
else
Card.Flag &= ~0x04;
return dat;
}
十一、STM32G030F6 SPI1移植问题
u8 SendByteLora(u8 value)
{
while(LL_SPI_IsActiveFlag_TXE(SPI1) == 0);
LL_SPI_TransmitData8(SPI1,value);
while(LL_SPI_IsActiveFlag_RXNE(SPI1) == 0);
return LL_SPI_ReceiveData8(SPI1);
// while((SPI1->SR & (1<<1)) == 0);
// SPI1->DR = value;
// while((SPI1->SR & (1<<0)) == 0);
// return SPI1->DR;
}
注释的部分是L051可用的程序,移植到G030就用不了,后使用CUBEMX生成的LL库就可以用,有空查下原因