基础外设
中断
SCB->VTOR = FLASH_BASE | 0x10000; 重定向中断向量表
__set_FAULTMASK(0); 打开关闭全局中断
唯一ID
static uint32_t idAddr[]={
0x1FFFF7AC,/*STM32F0唯一ID起始地址*/
0x1FFFF7E8,/*STM32F1唯一ID起始地址*/
0x1FFF7A10, /*STM32F2唯一ID起始地址*/
0x1FFFF7AC,/*STM32F3唯一ID起始地址*/
0x1FFF7A10, /*STM32F4唯一ID起始地址*/
0x1FF0F420,/*STM32F7唯一ID起始地址*/
0x1FF80050, /*STM32L0唯一ID起始地址*/
0x1FF80050, /*STM32L1唯一ID起始地址*/
0x1FFF7590, /*STM32L4唯一ID起始地址*/
0x1FF0F420
}; /*STM32H7唯一ID起始地址*/
Stm32CubeMx 和 HAL库
HAL库是上层封装库, 封装完善,但是性能稍微弱些,个人认为现在硬件资源比较多如果可以有稳定的开发环境和代码可以牺牲一些性能
LL库直接操作底层性能比较好,但是要对底层比较了解要做一些额外工作
CMSIS就是cortex M核心,包含系统时钟和启动代码,中断屏蔽等一些函数,基本不用我们操作这些东西
BSP就是对应每个开发板的板子驱动程序,如果你的硬件电路是参考开发板可以直接使用驱动程序
TCP,USB,FAT, RTOS,Graphic图形,这些是组件的方式提供,官方还能下载一些其他的组件例如:蓝牙,LORAWan
HAL使用基础
HAL库封装采用了回调的方式,官方封装上层,自己实现底层
例如:HAL_UART_Init 会回调 HAL_UART_MspInit 我们要实现这个函数具体引脚和中断等的配置,但是STM32cubemx帮我们配置好了,挺开心
每个外设数据传输方式都分为:阻塞, 中断,DMA传输,具体使用在每个头文件有介绍,固件库包目录有历程
输出传输完成和完成一半的时候会回调一些函数,例如:TxHalfCpltCallback(),用户要实现这个函数就可以在里面做一些任务,
回调函数原理,在每个函数头部用_weak声明,如果定义了新的函数编译器会链接新的函数不会发出警告
回调也可以用注册的方式,例如:HAL_UART_UnRegisterCallback()
Stm32CubeMx
看门狗配置
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
{
if(wwdg_tick++ < 1000) {
HAL_WWDG_Refresh(hwwdg);
}
}
ETH配置 LAN8720A
ADC配置
//ADC初始化
HAL_ADC_Start_DMA(&hadc3, (uint32_t*)&ADC_Value, 800); //开启DMA
RTC低功耗
static void SystemPower_Config(void)
{
/* Enable Power Control clock */
__HAL_RCC_PWR_CLK_ENABLE();
/* Enable Ultra low power mode */
HAL_PWREx_EnableUltraLowPower();
/* Enable the fast wake up from Ultra low power mode */
HAL_PWREx_EnableFastWakeUp();
}
main中添加如下
----------------------------------------------------------------
SystemPower_Config();
/* Check and handle if the system was resumed from StandBy mode */
if(__HAL_PWR_GET_FLAG(PWR_FLAG_SB) != RESET)
{
/* Clear Standby flag */
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);
printf("OK");
}
/* Insert 5 seconds delay */
HAL_Delay(5000);
/* Disable all used wakeup sources*/
HAL_RTCEx_DeactivateWakeUpTimer(&hrtc);
/* Clear all related wakeup flags */
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
//HAL_PWR_EnableFlashPowerDown();
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 0xFFFF * 20, RTC_WAKEUPCLOCK_RTCCLK_DIV16);
/* Enter the Standby mode */
HAL_PWR_EnterSTANDBYMode();
DAC配置
HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, usRegHoldingBuf[index++] / 100.0 * 3.3 / 20 * (4095 / 3.3));
关闭DAC缓冲可以改变为线性输出
TIM配置PWM
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1 );
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2 );
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_3 );
TIM
I2C
HAL_I2C_Mem_Read(&hi2c1, 0xA1, i, I2C_MEMADD_SIZE_16BIT, point + i, 1, 1000);
HAL_I2C_Mem_Write(&hi2c1, 0xA0, i, I2C_MEMADD_SIZE_16BIT, point + i, 1, 1000);
SPI
Lwip
#define LWIP_DHCP 1 启用自动分配ip 注意(MAC地址要符合标准不然获取不到ip)
#define LWIP_DNS 启用DNS功能
LWIP_NETIF_LINK_CALLBACK 打开 网口热插拔回调处理
LWIP_DHCP_CHECK_LINK_UP 自动监测自动监测网络状态开启和关闭重新开启dhcp
在 MX_LWIP_Init()函数中修改
取消自动生成:dhcp_start(&gnetif);
添加
if (netif_is_link_up(&gnetif)) {
dhcp_start(&gnetif);
}
添加 监测
void ethernetif_notify_conn_changed(struct netif *netif)
{
if(netif_is_link_up(netif)) {
netif_set_up(netif);
if(SysConfigure.NetworkWay == 1) { //动态ip
struct dhcp *dhcp = netif_dhcp_data(netif);
if(dhcp == NULL) {
dhcp_start(netif);
}
}
} else {
netif_set_down(netif);
}
}
DNS获取
err_t err = dns_gethostbyname("mqtt.huikezk.com", &dnsip, dns_found, NULL);
if(err == ERR_OK) {
printf("DNS Found IP: %s\n", ip4addr_ntoa(&dnsip));
}
static void dns_found(const char *name, const ip_addr_t *ipaddr, void *callback_arg)
{
if (ipaddr != NULL) {
printf("DNS Found IP: %s\n", ip4addr_ntoa(ipaddr));
}
}
状态回调打开自动生成监测函数:
在main主循环或者线程循环中 一直调用这个函数监测网络状态
void ethernetif_set_link(struct netif *netif)
{
uint32_t regvalue = 0;
/* Ethernet Link every 200ms */
if (HAL_GetTick() - EthernetLinkTimer >= 200)
{
EthernetLinkTimer = HAL_GetTick();
/* Read PHY_BSR*/
HAL_ETH_ReadPHYRegister(&heth, PHY_BSR, ®value);
regvalue &= PHY_LINKED_STATUS;
/* Check whether the netif link down and the PHY link is up */
if(!netif_is_link_up(netif) && (regvalue))
{
/* network cable is connected */
netif_set_link_up(netif);
}
else if(netif_is_link_up(netif) && (!regvalue))
{
/* network cable is disconnected */
netif_set_link_down(netif);
}
}
}
UDP
struct sockaddr_in my_addr; //服务器网络地址结构体
struct sockaddr_in remote_addr; //客户端网络地址结构体
int server_sockfd;//服务器端套接字
memset((void *)&my_addr, 0, sizeof(my_addr)); //数据初始化--清零
memset((void *)&remote_addr, 0, sizeof(remote_addr));
my_addr.sin_family = AF_INET; //设置为IP通信
my_addr.sin_addr.s_addr = INADDR_ANY;//服务器IP地址--允许连接到所有本地地址上
my_addr.sin_port = htons(502); //服务器端口号
if((server_sockfd = socket(PF_INET,SOCK_STREAM,0)) < 0) { }
/*将套接字绑定到服务器的网络地址上*/
if (bind(server_sockfd,(struct sockaddr *)&my_addr, sizeof(struct sockaddr)) < 0) { }
struct timeval timeout={0, 10};
//即timeout={4,0};或者timeout.tv_sec=4; timeout.tv_usec=0;
//设置接收超时
setsockopt(server_sockfd, SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,sizeof(struct timeval));
/* Infinite loop */
for(;;)
{
socklen_t len = 0;
int reciverLen = 0;
if((reciverLen = recvfrom(server_sockfd, udpBuf, BUFSIZ, 0, (struct sockaddr *)&remote_addr, &len)) > 0) {
printf("%.*s", reciverLen, udpBuf);
}
osDelay(1);
static struct timeval timeout={0, 1000 * 1}; //1s
//ÉèÖýÓÊÕ³¬Ê±
if(setsockopt(server_sockfd, SOL_SOCKET, SO_RCVTIMEO,(char*)&timeout,sizeof(struct timeval)) < 0) {
Soft_Reset();
}
if(setsockopt(server_sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {//ÉèÖÃÌ×½Ó×Ö·¢Ë͹㲥
Soft_Reset();
}
TCP keepalive
1.设置 LWIP_KEEPALIVE true
if(setsockopt(server_sockfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, sizeof(keepalive)) < 0)
/*将套接字绑定到服务器的网络地址上*/
if (bind(server_sockfd,(struct sockaddr *)&my_addr, sizeof(struct sockaddr)) < 0) { }
/*监听连接请求--监听队列长度为1*/
if(listen(server_sockfd, 1) < 0) { }
u32_t sin_size = sizeof(struct sockaddr_in);
/* Infinite loop */
for(;;)
{
/*等待客户端连接请求到达*/
if((client_sockfd = accept(server_sockfd,(struct sockaddr *)&remote_addr, &sin_size)) < 0) {
printf("accept faild");
continue;
}
setsockopt(client_sockfd, IPPROTO_TCP, TCP_KEEPIDLE, (void*)&keepidle , sizeof(keepidle));
setsockopt(client_sockfd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&keepinterval , sizeof(keepinterval));
setsockopt(client_sockfd, IPPROTO_TCP, TCP_KEEPCNT, (void *)&keepcount , sizeof(keepcount));
setsockopt(client_sockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&flag , sizeof(flag)); //没缓冲直接发送
printf("accept client %s:%d/n", inet_ntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port));
/*接收客户端的数据并将其发送给客户端--recv返回接收到的字节数,send返回发送的字节数*/
while((len = recv(client_sockfd, buf, BUFSIZ, 0)) > 0)
{
buf[len]='\0';
printf("%s/n",buf);
if(send(client_sockfd,buf,len,0) < 0) {
break;
}
}
close(client_sockfd);
printf("sock disconnect\n");
}
LWIP listen(server_sockfd, 1) 监听个数
TCP_LISTEN_BACKLG 设置为 true
Lwip连接个数被限制
发现MEMP_NUM_NETCONN这个值太小了 导致后面的连接分配不到内存 将其改大些就可以了
LwIP BUG之TCP连接丢失
LwIP所有版本包括最新的2.0版本具有以下缺陷,当用户使用raw编程并在err或poll回调函数中操作了内核全局tcp_active_pcbs链表(最典型的,比如进行了重连操作),将有可能导致链表异常,严重情况下,链表中的很多tcp_pcb会丢失,从而导致部分连接没有任何反应,出现假死的现象。
网络质量不好或者其他原因具体原因未知,因为同样的程序我就一个项目发现了问题
解决方法
跟踪发现是这个结构体用完了没有释放导致,添加定时监测,如果不能再申请到说明用完了重启系统
//检测内存
struct tcp_seg *seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG);
if(seg == NULL)
Soft_Reset();
memp_free(MEMP_TCP_SEG, seg);
stm32 lua
Lua的官网是:www.lua.org
移植
加入�lua源代码到MDK,把lua.c 和luac.c删除。
l 改动堆栈大小:堆最小为5.5kb,栈最小是1.5kb。lua的空间分配在堆空间。16kb的内存建议分配是堆11Kb,栈4Kb,留1Kb给全局变量。
在startup_stm32f10x_md.s文件里
; stack size 0x1 == 4Kb
Stack_Size EQU 0x01
; heap size 0x2c00 == 11Kb
Heap_Size EQU 0x02C00
1) 把 lua.c 和 luac.c 删除,这两个是一个Lua Shell,和平台相关,单片机中一般没用。
2) 对内存敏感的项目可以替换掉 lauxlib.c 文件里 l_alloc 函数调用的 free 和 realloc 函数。
3) loslib.c 和系统相关,单片机中最多跑RTOS,所以这个文件可以删除。
4) liolib.c 中使用了标准文件操作fopen、fclose、fread、fwrite等函数。虽然有些单片机支持这些函数,但还是要自己重定向这些函数,而且在单片机中一般也不用文件来操作IO,所以这个文件可以删除。
5) 如果删除了 loslib.c 和 liolib.c,那么在 linit.c 中要把 loadlibs 数组中相关的元素注释掉。这样调用 luaL_openlibs 时就不会加载这两个库了。
6) 在luaconfig.h 中有 luai_writestring 和 luai_writeline 两个宏,这两个宏关系到了 lua 中 print 的输出,需要针对平台重定向,一般定向到调试串口就行。还有个 luai_writestringerrir 宏,定义了 lua 如何报错,可以定义到调试串口,也可以定义到log文件,或者两者兼有。
7)LUA_32BITS使能 int 和 float使用32否则使用64位
8)添加time()函数
完成以上6步,lua就移植完成了。