本文共 8073 字,大约阅读时间需要 26 分钟。
龙芯1c库是把龙芯1c的常用外设的常用功能封装为一个库,类似于STM32库。Git地址:https://gitee.com/caogos/OpenLoongsonLib1c
引脚复用不是一个单独的功能,但在很多地方都需要用到。我想还是有必要单独讨论一下的,所以就有了此文。
提供两个接口,分别是
/* * 把指定pin设置为指定用途(普通gpio,非gpio) * @gpio gpio引脚编号 * @purpose 用途 */void pin_set_purpose(unsigned int gpio, pin_purpose_t purpose);/* * 设置指定pin为第n复用 * @gpio gpio编号 * @remap 第n复用 */void pin_set_remap(unsigned int gpio, pin_remap_t remap);再来看看第二个参数分别是啥
// 引脚用途typedef enum{ PIN_PURPOSE_GPIO = 0, // 引脚用作普通gpio PIN_PURPOSE_OTHER, // 引脚用作其它功能(非gpio)}pin_purpose_t;// 引脚复用typedef enum{ PIN_REMAP_FIRST = 0, // 第一复用 PIN_REMAP_SECOND, // 第二复用 PIN_REMAP_THIRD, // 第三复用 PIN_REMAP_FOURTH, // 第四复用 PIN_REMAP_FIFTH, // 第五复用}pin_remap_t;这里讨论的是引脚复用,为何会涉及gpio呢,请看下图
这是《龙芯1c300处理器用户手册》v1.4中的截图,其中有提到GPIO复用,我想这里的GPIO复用应该就是指引脚用作GPIO,其优先级仅高于默认功能。
再来看看《龙芯1c300处理器数据手册》v1.3中关于引脚复用的表格,如下按道理上讲,引脚复用的接口应该有引脚编号,并且应该以引脚编号作为索引,可前面提到的接口怎么没有引脚编号,而是以gpio编号作为索引呢?
龙芯1c处理器上有些引脚是固定的,只能用作某个固定功能,不能用作其它功能,否则cpu不能正常工作,比如晶振,内存占用的引脚,flash占用的引脚等。一般可以复用的引脚也可以作为普通GPIO使用,如果这个理由还不够充分,那么再给你个理由上图中的寄存器是用于配置引脚复用功能的,虽然手册中说的是PAD[31:0],但经过实际测试,应该是GPIO[31:0],也就是说寄存器CBUS_FIRSTx,CBUS_SECONDx,CBUS_THIRDx,CBUS_FOURTHx和CBUS_FIFTHx是以GPIOx为索引来设置复用的,而不是PADx。这也就是前面接口中为啥第一个参数是gpio,以gpio作为索引来设计复用的原因。
比如将某个引脚复用为pwm,代码如下
// pwm引脚定义#define LS1C_PWM0_GPIO06 (6) // gpio06用作pwm0#define LS1C_PWM0_GPIO04 (4) // gpio04复用为pwm0#define LS1C_PWM1_GPIO92 (92) // gpio92用作pwm1#define LS1C_PWM1_GPIO05 (5) // gpio05复用为pwm1#define LS1C_PWM2_GPIO52 (52) // gpio52复用为pwm2#define LS1C_PWM2_GPIO46 (46) // gpio46复用为pwm2#define LS1C_PWM3_GPIO47 (47) // gpio47复用为pwm3#define LS1C_PWM3_GPIO53 (53) // gpio53复用为pwm3 // 配置相应引脚用作pwm,而非gpio pin_set_purpose(gpio, PIN_PURPOSE_OTHER); // 复用 switch (gpio) { // 不需要复用 case LS1C_PWM0_GPIO06: case LS1C_PWM1_GPIO92: break; case LS1C_PWM0_GPIO04: // gpio04的第三复用 pin_set_remap(LS1C_PWM0_GPIO04, PIN_REMAP_THIRD); break; case LS1C_PWM1_GPIO05: // gpio05的第三复用 pin_set_remap(LS1C_PWM1_GPIO05, PIN_REMAP_THIRD); break; case LS1C_PWM2_GPIO52: // gpio52的第四复用 pin_set_remap(LS1C_PWM2_GPIO52, PIN_REMAP_FOURTH); break; case LS1C_PWM2_GPIO46: // gpio46的第四复用 pin_set_remap(LS1C_PWM2_GPIO46, PIN_REMAP_FOURTH); break; case LS1C_PWM3_GPIO47: // gpio47的第四复用 pin_set_remap(LS1C_PWM3_GPIO47, PIN_REMAP_FOURTH); break; case LS1C_PWM3_GPIO53: // gpio53的第四复用 pin_set_remap(LS1C_PWM3_GPIO53, PIN_REMAP_FOURTH); break; default: break; }
来看看《龙芯1c处理器用户手册》中的描述
寄存器CBUS_FIRST0为GPIO[31:0]第一复用控制寄存器
寄存器CBUS_SECOND0为GPIO[31:0]的第二复用控制寄存器寄存器CBUS_THIRD0为GPIO[31:0]的第三复用控制寄存器寄存器CBUS_FOURTH0为GPIO[31:0]的第四复用控制寄存器寄存器CBUS_FIFTH0为GPIO[31:0]的第五复用控制寄存器依次类推GPIO[63:32]的复用控制寄存器组为CBUS_xxx1,GPIO[95:64]的复用控制寄存器组为CBUS_xxx2GPIO[127:96]的复用控制寄存器组委CBUS_xxx3这里就不一一粘贴了,更详细的请查看《龙芯1c300处理器数据手册》
// 引脚功能(普通gpio,pwm,复用等)相关接口#ifndef __OPENLOONGSON_PIN_H#define __OPENLOONGSON_PIN_H// 引脚用途typedef enum{ PIN_PURPOSE_GPIO = 0, // 引脚用作普通gpio PIN_PURPOSE_OTHER, // 引脚用作其它功能(非gpio)}pin_purpose_t;// 引脚复用typedef enum{ PIN_REMAP_FIRST = 0, // 第一复用 PIN_REMAP_SECOND, // 第二复用 PIN_REMAP_THIRD, // 第三复用 PIN_REMAP_FOURTH, // 第四复用 PIN_REMAP_FIFTH, // 第五复用}pin_remap_t;/* * 把指定pin设置为指定用途(普通gpio,非gpio) * @gpio gpio引脚编号 * @purpose 用途 */void pin_set_purpose(unsigned int gpio, pin_purpose_t purpose);/* * 设置指定pin为第n复用 * @gpio gpio编号 * @remap 第n复用 */void pin_set_remap(unsigned int gpio, pin_remap_t remap);#endif
// 引脚功能(普通gpio,pwm,复用等)相关接口#include "public.h"#include "ls1c_regs.h"#include "gpio.h"#include "pin.h"/* * 把指定pin设置为指定用途(普通gpio,非gpio) * @gpio gpio引脚编号 * @purpose 用途 */void pin_set_purpose(unsigned int gpio, pin_purpose_t purpose){ volatile unsigned int *gpio_cfgx; // GPIO_CFGx寄存器 unsigned int pin = GPIO_GET_PIN(gpio); gpio_cfgx = gpio_get_cfg_reg(gpio); if (PIN_PURPOSE_GPIO == purpose) // 引脚用作普通gpio { reg_set_one_bit(gpio_cfgx, pin); } else // 引脚用作其它功能(非gpio) { reg_clr_one_bit(gpio_cfgx, pin); } return ;}/* * 设置指定pin为第n复用 * @gpio gpio编号 * @remap 第n复用 */void pin_set_remap(unsigned int gpio, pin_remap_t remap){ volatile unsigned int *reg = NULL; // 复用寄存器 unsigned int port = GPIO_GET_PORT(gpio); unsigned int pin = GPIO_GET_PIN(gpio); switch (port) { case 0: switch (remap) { case PIN_REMAP_FIRST: reg = (volatile unsigned int *)LS1C_CBUS_FIRST0; break; case PIN_REMAP_SECOND: reg = (volatile unsigned int *)LS1C_CBUS_SECOND0; break; case PIN_REMAP_THIRD: reg = (volatile unsigned int *)LS1C_CBUS_THIRD0; break; case PIN_REMAP_FOURTH: reg = (volatile unsigned int *)LS1C_CBUS_FOURTH0; break; case PIN_REMAP_FIFTH: reg = (volatile unsigned int *)LS1C_CBUS_FIFTH0; break; } break; case 1: switch (remap) { case PIN_REMAP_FIRST: reg = (volatile unsigned int *)LS1C_CBUS_FIRST1; break; case PIN_REMAP_SECOND: reg = (volatile unsigned int *)LS1C_CBUS_SECOND1; break; case PIN_REMAP_THIRD: reg = (volatile unsigned int *)LS1C_CBUS_THIRD1; break; case PIN_REMAP_FOURTH: reg = (volatile unsigned int *)LS1C_CBUS_FOURTH1; break; case PIN_REMAP_FIFTH: reg = (volatile unsigned int *)LS1C_CBUS_FIFTH1; break; } break; case 2: switch (remap) { case PIN_REMAP_FIRST: reg = (volatile unsigned int *)LS1C_CBUS_FIRST2; break; case PIN_REMAP_SECOND: reg = (volatile unsigned int *)LS1C_CBUS_SECOND2; break; case PIN_REMAP_THIRD: reg = (volatile unsigned int *)LS1C_CBUS_THIRD2; break; case PIN_REMAP_FOURTH: reg = (volatile unsigned int *)LS1C_CBUS_FOURTH2; break; case PIN_REMAP_FIFTH: reg = (volatile unsigned int *)LS1C_CBUS_FIFTH2; break; } break; case 3: switch (remap) { case PIN_REMAP_FIRST: reg = (volatile unsigned int *)LS1C_CBUS_FIRST3; break; case PIN_REMAP_SECOND: reg = (volatile unsigned int *)LS1C_CBUS_SECOND3; break; case PIN_REMAP_THIRD: reg = (volatile unsigned int *)LS1C_CBUS_THIRD3; break; case PIN_REMAP_FOURTH: reg = (volatile unsigned int *)LS1C_CBUS_FOURTH3; break; case PIN_REMAP_FIFTH: reg = (volatile unsigned int *)LS1C_CBUS_FIFTH3; break; } break; default: return ; } // 置1 reg_set_one_bit(reg, pin); return ;}