本帖最后由 Harvard 于 2020-1-3 14:04 编辑
从官方的AN2017文档中,我们了解到官方提供的两种形式的底层库: ALD和MD
当然也包括 了两者的混合形式: MD_ALD_MIX
ES32 SDK底层驱动包括MD、ALD、BSP、CMSIS驱动,底层驱动从不同的角度给出微控制器操作的接口,用户可以自由选择相应的库,以满足开发的需求。
MD(Micro Driver:微驱动)驱动接口主要在头文件中定义,使用内联的方式直接对寄存器做操作,MD库编译出的程序往往执行效率高、微控制器处理速度快,同时容易控制最终编译出的程序空间。MD驱动可裁剪,用户可以通过MD驱动配置文件来选择需要使用哪些模块的驱动。
使用MD驱动操作外设可以分为以下几步:
1 调用MD驱动初始化函数,做好MD驱动心跳时钟移植;
2 使用MD驱动配置文件,选择需要用到的外设模块;
3 调用MD驱动相关外设初始化接口,进行外设初始化操作;
4 调用MD驱动外设功能性接口,对外设进行操作。
ALD(Abstraction Layer Driver:抽象层驱动)驱动屏蔽硬件底层操作,用户无需关心相关外设的硬件原理,更不用自己代码实现外设的工作过程,只需做好相关外设的使用准备即可调用外设功能接口来对外设进行操作。ALD驱动接口可以分为三方面,初始化接口、中断处理接口、功能接口。初始化接口用来初始化相关外设,在外设使用之前调用;中断处理函数需在外设使用前在相关中断服务函数中被调用,中断处理函数主要用来处理微控制器相关中断,在ALD库中给出了每个外设以及内核的中断处理函数,用户只需要进行调用即可;功能接口即每个外设核心功能的接口,比如串行、并行外设总线发送数据。功能接口分为阻塞以及非阻塞类型,在调用非阻塞接口时,操作完成的回调函数由应用程序给出,需要作为参数传递到ALD模块驱动中(当然回调函数可以为NULL),当操作完成时回调函数会被调用。
通过上面的描述, 我们能够理解到es32库的魅力,非常的灵活. 用户可以根据自己的需要采用合适的调用方式.也可以混合. 让我们想起来stm32的标准外设库和CubeMx库,
即MD相当于标准外设库, 当然MD应该比标准外设库更加的高效,我理解应该是介于直接寄存器操作和标准外设库的一种中间状态, 友商中,新唐单片机也是这样一种形式, 简洁高效,一旦适应,用起来非常的得心应手,正好,我也是从新唐转用咱们东软, 相信,用起来会很方便.
ALD库,应该是结构化操作更好.调用方式更加优秀. 也希望以后官方可以出品一个可以和Cubemx类似的配置工具,通过图形化来剪裁和配置ALD驱动.降低入门的难度.
下面,我们从官方库提供的具体历程,来对比两者的一些差别;
在 ES32_SDK_V1.03\Projects\ES32F065x\Examples_MD\UART\send_recv\MDK-ARM
和
ES32_SDK_V1.03\Projects\ES32F065x\Examples_ALD\UART\01_send_recv\MDK-ARM
两个文件夹下, 用KEIL分别打开这两个工程,
对比两者的主程序:
1 MD
md_uart_init_t init;
uint8_t tx_buf[32];
uint8_t rx_buf[32];
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] Initializate pin
* @retval None
*/
void uart_pin_init(void)
{
md_gpio_init_t x;
/* Initialize tx pin */
x.mode = GPIO_MODE_OUTPUT;
x.odos = GPIO_PUSH_PULL;
x.pupd = GPIO_PUSH_UP;
x.odrv = GPIO_OUT_DRIVE_NORMAL;
x.flt = GPIO_FILTER_DISABLE;
x.type = GPIO_TYPE_TTL;
x.func = GPIO_FUNC_3;
md_gpio_init(UART1_TX_PORT, UART1_TX_PIN, &x);
/* Initialize rx pin */
x.mode = GPIO_MODE_INPUT;
x.odos = GPIO_PUSH_PULL;
x.pupd = GPIO_PUSH_UP;
x.odrv = GPIO_OUT_DRIVE_NORMAL;
x.flt = GPIO_FILTER_DISABLE;
x.type = GPIO_TYPE_TTL;
x.func = GPIO_FUNC_3;
md_gpio_init(UART1_RX_PORT, UART1_RX_PIN, &x);
return;
}
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] Test main function
* @retval Status.
*/
int main()
{
/* Initialize 1ms tick */
md_init_1ms_tick();
/* clear md_uart_init_t structure */
memset(&init, 0x0, sizeof(md_uart_init_t));
/* Initialize pin */
uart_pin_init();
/* Initialize tx_buf */
memset(tx_buf, 0x55, 32);
/* Initialize UART */
init.baud = 115200;
init.word_length = MD_UART_WORD_LENGTH_8B;
init.stop_bits = MD_UART_STOP_BITS_1;
init.parity = MD_UART_PARITY_NONE;
init.fctl = MD_UART_FLOW_CTL_DISABLE;
md_uart_init(UART1, &init);
/* Send a message */
md_uart_send_data8(UART1, tx_buf[0]);
/* Receive a message */
rx_buf[0] = (uint8_t)md_uart_recv_data8(UART1);
while (1) {
md_delay_1ms(1000);
}
}
2 ALD
uart_handle_t h_uart;
uint8_t tx_buf[32];
uint8_t rx_buf[32];
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] Initializate pin of uart module.
* @retval None
*/
void uart_pin_init(void)
{
gpio_init_t x;
/* Initialize tx pin */
x.mode = GPIO_MODE_OUTPUT;
x.odos = GPIO_PUSH_PULL;
x.pupd = GPIO_PUSH_UP;
x.odrv = GPIO_OUT_DRIVE_NORMAL;
x.flt = GPIO_FILTER_DISABLE;
x.type = GPIO_TYPE_TTL;
x.func = GPIO_FUNC_3;
ald_gpio_init(UART1_TX_PORT, UART1_TX_PIN, &x);
/* Initialize rx pin */
x.mode = GPIO_MODE_INPUT;
x.odos = GPIO_PUSH_PULL;
x.pupd = GPIO_PUSH_UP;
x.odrv = GPIO_OUT_DRIVE_NORMAL;
x.flt = GPIO_FILTER_DISABLE;
x.type = GPIO_TYPE_TTL;
x.func = GPIO_FUNC_3;
ald_gpio_init(UART1_RX_PORT, UART1_RX_PIN, &x);
return;
}
/**
* @brief Send message complete.
* @param arg: Pointer to uart_handle_t structure.
* @retval None.
*/
void uart_send_complete(uart_handle_t *arg)
{
return;
}
/**
* @brief Receive a message complete.
* @param arg: Pointer to uart_handle_t structure.
* @retval None.
*/
void uart_recv_complete(uart_handle_t *arg)
{
return;
}
/**
* @brief Occurs error.
* @param arg: Pointer to uart_handle_t structure.
* @retval None.
*/
void uart_error(uart_handle_t *arg)
{
return;
}
/**
* @brief Test main function
* @retval Status.
*/
int main()
{
/* Initialize ALD */
ald_cmu_init();
/* Configure system clock */
ald_cmu_auto_calib_clock(CMU_AUTO_CALIB_INPUT_LOSE, CMU_AUTO_CALIB_OUTPUT_24M);
ald_cmu_pll1_config(CMU_PLL1_INPUT_HRC_6, CMU_PLL1_OUTPUT_48M);
ald_cmu_clock_config(CMU_CLOCK_PLL1, 48000000);
/* clear uart_handle_t structure */
memset(&h_uart, 0x0, sizeof(h_uart));
/* Initialize pin */
uart_pin_init();
/* Initialize tx_buf */
memset(tx_buf, 0x55, 32);
/* Initialize uart */
h_uart.perh = UART1;
h_uart.init.baud = 115200;
h_uart.init.word_length = UART_WORD_LENGTH_8B;
h_uart.init.stop_bits = UART_STOP_BITS_1;
h_uart.init.parity = UART_PARITY_NONE;
h_uart.init.mode = UART_MODE_UART;
h_uart.init.fctl = UART_HW_FLOW_CTL_DISABLE;
h_uart.tx_cplt_cbk = uart_send_complete;
h_uart.rx_cplt_cbk = uart_recv_complete;
h_uart.error_cbk = uart_error;
ald_uart_init(&h_uart);
/* Send a message */
ald_uart_send(&h_uart, tx_buf, 32, 1000);
/* Receive a message */
ald_uart_recv(&h_uart, rx_buf, 8, 10000);
while (1) {
ald_delay_ms(1000);
}
}
对比两个程序,在代码的功能执行顺序上,几乎是完全一致的, 不同的是调用的一些官方底层库驱动的名称不一样;比如初始化tx引脚:
md_gpio_init_t x; /* 先定义一个gpio初始化的结构体 */
x.mode = GPIO_MODE_OUTPUT;
x.odos = GPIO_PUSH_PULL;
x.pupd = GPIO_PUSH_UP;
x.odrv = GPIO_OUT_DRIVE_NORMAL;
x.flt = GPIO_FILTER_DISABLE;
x.type = GPIO_TYPE_TTL;
x.func = GPIO_FUNC_3;
md_gpio_init(UART1_TX_PORT, UART1_TX_PIN, &x);
=======================================================
gpio_init_t x; /* 定义一个GPIO初始化的结构体 */
x.mode = GPIO_MODE_OUTPUT;
x.odos = GPIO_PUSH_PULL;
x.pupd = GPIO_PUSH_UP;
x.odrv = GPIO_OUT_DRIVE_NORMAL;
x.flt = GPIO_FILTER_DISABLE;
x.type = GPIO_TYPE_TTL;
x.func = GPIO_FUNC_3;
ald_gpio_init(UART1_TX_PORT, UART1_TX_PIN, &x);
同样的初始化函数, 不同就在于调用的gpio_init初始化函数,就在于最后一行,两个功能一样的库函数,表面他们一个是md库, 一个是ald库,
我们可以通过goto definition
和
分别跳转到相应的定义,也会发现两者的内部细节惊人一致. 这两个函数分别位于md_gpio.c和ald_gpio.c中. 只不过是后者多了一些断言;
相比友商, 可以看出es32提供的库,md和ald的一致性非常好.有些时候.如果要做移植,可能就是简单的把库函数的前缀修改下,就可以达到.
再对比两者的发送函数:
MD的操作显得比较简单直接.
md_uart_send_data8(UART1, tx_buf[0]);
查看其定义.发现就是一个直接寄存器操作.
而ALD的这个同样功能的操作.则考虑的比较全面;更加实用.
ald_uart_send(&h_uart, tx_buf, 32, 1000);
ald_status_t ald_uart_send(uart_handle_t *hperh, uint8_t *buf, uint16_t size, uint32_t timeout)
{
assert_param(IS_UART_ALL(hperh->perh));
if ((hperh->state != UART_STATE_READY) && (hperh->state != UART_STATE_BUSY_RX))
return BUSY;
if ((buf == NULL) || (size == 0))
return ERROR;
__LOCK(hperh);
hperh->err_code = UART_ERROR_NONE;
SET_BIT(hperh->state, UART_STATE_TX_MASK);
hperh->tx_size = size;
hperh->tx_count = size;
while (hperh->tx_count-- > 0)
{
if (uart_wait_flag(hperh, UART_STATUS_TBEM, SET, timeout) != OK)
{
__UNLOCK(hperh);
hperh->state = UART_STATE_READY;
return TIMEOUT;
}
WRITE_REG(hperh->perh->TBR, (*buf++ & 0xFF));
}
if (uart_wait_flag(hperh, UART_STATUS_TEM, SET, timeout) != OK)
{
__UNLOCK(hperh);
hperh->state = UART_STATE_READY;
return TIMEOUT;
}
CLEAR_BIT(hperh->state, UART_STATE_TX_MASK);
__UNLOCK(hperh);
return OK;
}
可以看出.这个发送函数功能更大强大.可以满足各种个性化需求了.
综上, md适合入门 适合功能简单的一些应用实现, 把更多的主动**给工程师,
而ALD显然是功能更加丰富,让工程师有更多的精力仅需要关注自己的应用就可以.底层的操作支持力度
很大,功能很丰富;
总的来说, 东软载波的库还是比较有自己鲜明特点的.
|