【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
说完了上位机的开发,接下来就是固件的开发。前面我们说过,目前使用的开发板是极海apm32f103的开发板。它自身包含了iap示例,里面既有bootloader的代码,也有app的代码。所以,我们需要做的就是在app的基础之上,添加自己需要的功能就可以了,bootloader就不用处理了。这也是大部分mcu模块的开发方式。
1、修改中断向量基地址和链接地址
这部分代码如果不修改的话,我们需要每次通过bootloader来加载app的bin文件。这对于开发来说,不是很方便,所以这部分还是建议修改一下,等到最后部署的时候再改回来。其实,修改的地方就两处,
#define FLASH_APP1_ADDR 0x0000
还有一处就是链接的地方,
2、添加新的串口
之前我们使用了debug串口,可以写数据、读数据。实际上开发的时候需要两个串口,最好分开来。一个串口用于debug调试,一个用于和上位机之间的通信。现在是这么安排的,之前的debug串口,也就是pa9、pa10用于串口通信使用。这里重新添加一个usart3,用于调试使用,对应的pin脚是pb10、pb11,这部分可以通过厂家的芯片手册查找到。其中,头文件的定义如下所示,
#ifndef __USART3_H
#define __USART3_H
#include "./SYSTEM/sys/sys.h"
#include "apm32f10x_usart.h"
#include <stdio.h>
#define USART3_TX_GPIO_PORT GPIOB
#define USART3_TX_GPIO_PIN GPIO_PIN_10
#define USART3_TX_GPIO_CLK_ENABLE() do{ RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOB); }while(0)
#define USART3_RX_GPIO_PORT GPIOB
#define USART3_RX_GPIO_PIN GPIO_PIN_11
#define USART3_RX_GPIO_CLK_ENABLE() do{ RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOB); }while(0)
#define USART3_UX USART3
#define USART3_UX_IRQn USART3_IRQn
#define USART3_UX_IRQHandler USART3_IRQHandler
#define USART3_UX_CLK_ENABLE() do{ RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_USART3); }while(0)
void usart3_init(uint32_t baudrate);
int sendChar(int ch);
void sendString(char* str);
void sendNumber(int num);
#endif
具体的c实现代码如下所示,
#include "./SYSTEM/usart3/usart3.h"
#include "apm32f10x_rcm.h"
#include "apm32f10x_gpio.h"
#include "apm32f10x_misc.h"
void usart3_init(uint32_t baudrate)
{
GPIO_Config_T gpio_init_struct = {0};
USART_Config_T usart_init_struct = {0};
USART3_UX_CLK_ENABLE();
USART3_TX_GPIO_CLK_ENABLE();
USART3_RX_GPIO_CLK_ENABLE();
gpio_init_struct.pin = USART3_TX_GPIO_PIN;
gpio_init_struct.speed = GPIO_SPEED_50MHz;
gpio_init_struct.mode = GPIO_MODE_AF_PP;
GPIO_Config(USART3_TX_GPIO_PORT, &gpio_init_struct);
gpio_init_struct.pin = USART3_RX_GPIO_PIN;
gpio_init_struct.speed = GPIO_SPEED_50MHz;
gpio_init_struct.mode = GPIO_MODE_IN_PU;
GPIO_Config(USART3_RX_GPIO_PORT, &gpio_init_struct);
usart_init_struct.baudRate = baudrate;
usart_init_struct.wordLength = USART_WORD_LEN_8B;
usart_init_struct.stopBits = USART_STOP_BIT_1;
usart_init_struct.parity = USART_PARITY_NONE;
usart_init_struct.mode = USART_MODE_TX_RX;
usart_init_struct.hardwareFlow = USART_HARDWARE_FLOW_NONE;
USART_Config(USART3_UX, &usart_init_struct);
USART_Enable(USART3_UX);
}
int sendChar(int ch)
{
while (USART3_UX->STS_B.TXCFLG == 0);
USART3_UX->DATA_B.DATA = (uint16_t)ch;
return ch;
}
void sendString(char* str)
{
char* pStart = str;
while(*pStart)
{
sendChar(*pStart);
pStart++;
}
}
static void _sendNumber(int num)
{
if(num > 9)
{
sendNumber(num / 10);
}
num = num % 10;
switch(num)
{
case 0:
sendChar('0');
break;
case 1:
sendChar('1');
break;
case 2:
sendChar('2');
break;
case 3:
sendChar('3');
break;
case 4:
sendChar('4');
break;
case 5:
sendChar('5');
break;
case 6:
sendChar('6');
break;
case 7:
sendChar('7');
break;
case 8:
sendChar('8');
break;
case 9:
sendChar('9');
break;
default:
break;
}
}
void sendNumber(int num)
{
unsigned int val;
if(num < 0)
{
sendChar('-');
val = -num;
}
else
{
val = num;
}
_sendNumber(val);
}
为了调试的方便,我们还实现了sendChar、sendString、sendNumber三个函数,到时候可以直接利用这几个打印函数调试即可。
3、添加内部flash读写代码
因为涉及到参数的保存,所以势必涉及到内部flash的读写。这部分内容可以直接从对应的示例代码中copy即可,
uint16_t apmflash_read_halfword(uint32_t faddr);
void apmflash_read(uint32_t raddr, uint16_t *pbuf, uint16_t length);
void apmflash_write_nocheck(uint32_t waddr, uint16_t *pbuf, uint16_t length);
void apmflash_write(uint32_t waddr, uint16_t *pbuf, uint32_t length);
void test_write(uint32_t waddr, uint16_t data);
4、添加adc代码
和内部flash读写一样,目前adc也有现成的case可以参考,所以相关代码直接copy即可,
void adc_init(void);
uint16_t adc_get_result(uint8_t ch);
uint16_t adc_get_result_average(uint8_t ch, uint8_t times);
5、确认之前的串口接收协议
除了上面4点之外,还要确认下当前之前debug串口是否包含有接收功能,相关的逻辑是什么。通过仔细阅读代码,正好在中断处理里面发现了相关的逻辑,
void USART_UX_IRQHandler(void)
{
if (USART_ReadIntFlag(USART_UX, USART_INT_RXBNE) == SET)
{
g_rx_buffer[0] = USART_RxData(USART_UX);
if ((g_usart_rx_sta & 0x8000) == 0)
{
if (g_usart_rx_sta & 0x4000)
{
if (g_rx_buffer[0] != 0x0A)
{
g_usart_rx_sta = 0;
}
else
{
g_usart_rx_sta |= 0x8000;
}
}
else
{
if (g_rx_buffer[0] == 0x0D)
{
g_usart_rx_sta |= 0x4000;
}
else
{
g_usart_rx_buf[g_usart_rx_sta & 0x3FFF] = g_rx_buffer[0];
g_usart_rx_sta++;
if (g_usart_rx_sta > (USART_REC_LEN - 1))
{
g_usart_rx_sta = 0;
}
}
}
}
USART_ClearIntFlag(USART_UX, USART_INT_RXBNE);
}
}
6、gpio高低电平设定
这部分之前的demo已经设定好,直接skip即可。
7、总结
等我们想好要实现哪些功能的时候,其实做起来并不麻烦,关键是前面要规划好。还有一点,就是要善于复用厂家给出的demo代码,以及阅读厂家的芯片手册,这些第一手的资料不一定文档上能找到,只能自己通过文档阅读的方法一点、一点去挖掘,总是可以解决的。
最后要提及的就是版本管理,非常推荐大家在本地用git把代码管起来。不管过分相信自己的记忆力,好记忆不如烂笔头。回到代码本身,就是好记忆不如git软件好使。