零、K210概述
1.为什么有这一篇文章?
在入手的K210开发板中,了解相关模块内容,我是先看如何点亮LED,这样基础的操作GPIO的方法就理解了。在看官方给的示例代码时,看到了如下操作:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AgJHfEPJ-1648393343293)(…/pic/image-20220326210947746.png)]
将硬件引脚映射成软件GPIO功能。so what ???
于是,在对FPIOA的内容进行了大致的了解后,输出此篇文章。
2.FPIOA概念
概念摘自 勘智的k210 datasheet
FPIOA为现场可编程IO阵列。此功能允许用户将255个内部功能(GPIO/I2C/UART/SPI等)映射到芯片外围的48个自由IO上:
- 支持IO的可编程功能选择
- 支持IO输出的8种驱动能力选择
- 支持IO的内部上拉电阻选择
- 支持IO的内部下拉电阻选择
- 支持IO输入的内部施密特触发器设置
- 支持IO输出的斜率控制
- 支持内部输入逻辑的电平设置7
本人理解,正常的MCU都是特定的引脚有特定的功能,比如说STM32串口功能都是在固定的引脚上。而这个FPIOA功能则让K210每个引脚的功能由我们自己定义。
3.FPIOA实现
- fpioa_set_function()函数解析
- fpioa_set_function_raw()函数解析
- fpioa_t结构体及相关内容
我们实际LED点亮例程中,进行了GPIO功能配置,代码如下:
void hardware_init(void)
{
fpioa_set_function(PIN_LED_0, FUNC_LED0);
fpioa_set_function(PIN_LED_1, FUNC_LED1);
}
3.1fpioa_set_function()
从上述代码中可以看出,通过调用勘智K210的裸机版SDK中的fpioa_set_function() 函数进行了硬件引脚和软件功能的映射。沿着fpioa_set_function() 的内容对FPIOA的功能实现进行深入分析。fpioa_set_function() 中的逻辑顺序如下:
- 检查引脚编号和功能编号是否超出限制
- 如果指定引脚number要绑定的功能为FUNC_RESV0,则调用
fpioa_set_function_raw(number, FUNC_RESV0) 。 - 如果指定引脚number要绑定的是其他功能,则会遍历所有引脚的功能配置,如果有其他引脚绑定着这个功能,则会将其他引脚的功能设置为FUNC_RESV0。
- 调用
fpioa_set_function_raw(number, function) 配置引脚number为指定功能
int fpioa_set_function(int number, fpioa_function_t function)
{
uint8_t index = 0;
if(number < 0 || number >= FPIOA_NUM_IO || function < 0 || function >= FUNC_MAX)
return -1;
if(function == FUNC_RESV0)
{
fpioa_set_function_raw(number, FUNC_RESV0);
return 0;
}
for(index = 0; index < FPIOA_NUM_IO; index++)
{
if((fpioa->io[index].ch_sel == function) && (index != number))
fpioa_set_function_raw(index, FUNC_RESV0);
}
fpioa_set_function_raw(number, function);
return 0;
}
3.2fpioa_set_function_raw()
从上述fpioa_set_function()定义中可以看出,该函数最终都是调用fpioa_set_function_raw() 来实现的。查看函数定义,可以确认逻辑顺序如下:
- 检测传入参数
- 给fpioa结构体的成员变量io[number]赋值。所赋值来自于function_config中。
int fpioa_set_function_raw(int number, fpioa_function_t function)
{
if(number < 0 || number >= FPIOA_NUM_IO || function < 0 || function >= FUNC_MAX)
return -1;
fpioa->io[number] = (const fpioa_io_config_t){
.ch_sel = function_config[function].ch_sel,
.ds = function_config[function].ds,
.oe_en = function_config[function].oe_en,
.oe_inv = function_config[function].oe_inv,
.do_sel = function_config[function].do_sel,
.do_inv = function_config[function].do_inv,
.pu = function_config[function].pu,
.pd = function_config[function].pd,
.sl = function_config[function].sl,
.ie_en = function_config[function].ie_en,
.ie_inv = function_config[function].ie_inv,
.di_inv = function_config[function].di_inv,
.st = function_config[function].st,
};
return 0;
}
fpioa作为一个结构体变量,赋值如下:
volatile fpioa_t *const fpioa = (volatile fpioa_t *)FPIOA_BASE_ADDR;
fpioa指针变量被const修饰,表明fpioa所存的地址不能修改,而地址指向的值可以改动
3.3fpioa_t结构体及相关内容
typedef struct _fpioa
{
fpioa_io_config_t io[FPIOA_NUM_IO];
fpioa_tie_t tie;
} __attribute__((packed, aligned(4))) fpioa_t;
该结构体由两部分组成:
- 记录每个引脚寄存器配置的io数组,数据类型为
fpioa_io_config_t 结构体 - 记录多路复用器联系的tie结构体,数据类型为
fpio_tie_t 结构体
3.3.1 fpioa_io_config_t结构体
本人理解这个结构体就是记录引脚的寄存器配置的。根据代码注释和结构体的内容。寄存器每个位的功能如下
typedef struct _fpioa_io_config
{
uint32_t ch_sel : 8;
uint32_t ds : 4;
uint32_t oe_en : 1;
uint32_t oe_inv : 1;
uint32_t do_sel : 1;
uint32_t do_inv : 1;
uint32_t pu : 1;
uint32_t pd : 1;
uint32_t resv0 : 1;
uint32_t sl : 1;
uint32_t ie_en : 1;
uint32_t ie_inv : 1;
uint32_t di_inv : 1;
uint32_t st : 1;
uint32_t resv1 : 7;
uint32_t pad_di : 1;
} __attribute__((packed, aligned(4))) fpioa_io_config_t;
- bit[31], PAD_DI: Read current IO’s data input.
- bit[30:24], NA: Reserved bits.
- bit[22], DI_INV: Invert Data input.
- bit[21], IE_INV: Invert the input enable signal
- bit[20], IE_EN: Input enable. It can disable or enable IO input.
- bit[19], SL: Slew rate control enable.
- bit[18], SPU: Strong pull up.
- bit[17], PD: Pull select: 0 for pull down, 1 for pull up.
- bit[16], PU: Pull enable.
- bit[15], DO_INV: Invert the result of data output select (DO_SEL).
- bit[14], DO_SEL: Data output select: 0 for DO, 1 for OE.
- bit[13], OE_INV: Invert the output enable signal.
- bit[12], OE_EN: Output enable.It can disable or enable IO output.
- bit[11:8], DS: Driving selector.
- bit[7:0], CH_SEL: Channel select from 256 input.
3.3.2 fpio_tie_t结构体
此结构体用于配置每个function的使能状态和默认值。
typedef struct _fpioa_tie
{
uint32_t en[FUNC_MAX / 32];
uint32_t val[FUNC_MAX / 32];
} __attribute__((packed, aligned(4))) fpioa_tie_t;
3.3.3 function_config
在fpioa_set_function_raw()函数中给fpioa->io[number]赋值时,是从function_config[]数组中获取的值。查看该数组的内容,大致如下:
static const fpioa_assign_t function_config[FUNC_MAX] =
{
{.ch_sel = FUNC_JTAG_TCLK,
.ds = 0x0,
.oe_en = 0,
.oe_inv = 0,
.do_sel = 0,
.do_inv = 0,
.pu = 0,
.pd = 0,
.resv1 = 0,
.sl = 0,
.ie_en = 1,
.ie_inv = 0,
.di_inv = 0,
.st = 1,
.tie_en = 0,
.tie_val = 0,
.resv0 = 0,
.pad_di = 0},
{.ch_sel = FUNC_JTAG_TDI,
...},
{.ch_sel = FUNC_JTAG_TMS,
...},
...
};
此数组为结构体数组,数组大小与function数量一致,这样在将软件功能映射到硬件引脚上时,就可以直接给对应的引脚io寄存器进行赋值,寄存器的具体配置则来自function_config[]数组中根据function配置好的值。
4.总结
通过FPIOA功能,可以对任意引脚配置不同的功能,相对于我知道的STM32,完全不用再去查原理图看哪个引脚是什么功能,自己想定义成啥就是啥。真是高呀!!!!!
|