- 00.00_空工程
- 01.01_ADC-软件触发
- 01.02_ADC-MCPWM触发ADC采样内部温度传感器
- 02.01_CLU-与门
- 03.01_CMP-基本功能
- 04.01_CRC-所有内置的CRC模型
- 05.01_DAC-1.2V量程(DAC0)
- 05.02_DAC-4.76V量程(DAC0)
- 06.01_DSP-除法
- 06.02_DSP-开方
- 07.01_EEPROM-读写
- 08.01_EXTI-上升沿&下降沿均触发中断
- 08.02_EXTI-上升沿触发
- 09.01_FLASH-MAIN区域读写和擦除
- 09.02_FLASH-NVR区域读写和擦除
- 10.01_GPIO-输出模式
- 10.02_GPIO-输入模式(高阻输入)
- 11.01_HALL-HALL状态值和IO对应关系
- 13.01_I2C-读写EEPROM
- 14.01_IWDG-看门狗复位时间
- 15.01_MCPWM-三路中心对齐PWM
- 15.02_MCPWM-不同的IO状态配置
- 16.01_OPA-输出到IO
- 17.01_QEP-正交编码
- 17.02_QEP-符号加脉冲信号计数
- 17.03_QEP-双向计数模式
- 18.01_SIF-发送数据
- 19.01_SPI-读写SPI_FLASH
- 20.01_TIMER-PWM输出
- 20.02_TIMER-1mS定时器
- 21.01_TMP-ADC采样内部温度传感器数据
- 22.01_UART-发送数据
- 23.01_WAKE-IO唤醒
- 23.02_WAKE-定时唤醒
00.00_空工程
说明
实测数据
参考代码
00.00_none.c
/**
* @brief 空工程
* @details
*
*/
#include "lks32mc09x_lib.h"
/**
* @brief 主函数,用于测试HSI时钟输出及GPIO翻转
*/
int main(void)
{
while (1)
{
}
}
使用到的库函数
库函数部分代码
01.01_ADC-软件触发
测试步骤
- 初始化ADC0以采样指定的通道(通道9)。
- 在主循环中,进行100次ADC采样,并计算采样结果的平均值。
- 将平均值转换为实际电压值
实测数据
浮点
右对齐
val = -0.276070
参考代码
01.01_ADC_DualRangeSample.c
/**
* @brief ADC-软件触发
* @details
* 1. 初始化ADC0以采样指定的通道(通道9)。
* 2. 在主循环中,进行100次ADC采样,并计算采样结果的平均值。
* 3. 将平均值转换为实际电压值
*/
#include "lks32mc09x_adc.h"
#include "lks32mc09x_gpio.h"
#define ADC_SAMPLE_COUNT 1024
volatile float average = 0;
volatile s16 ADCSample[ADC_SAMPLE_COUNT];
volatile int flg;
volatile int adcval;
/**
* @brief 初始化ADC0
*/
void ADC0_Init(void)
{
ADC_InitTypeDef ADC_InitStruct;
// 复位ADC0模块
// Adc_Reset(ADC0);
// 初始化ADC结构体
ADC_StructInit(&ADC_InitStruct);
// 配置ADC参数
ADC_InitStruct.IE = 0; // 禁用中断
ADC_InitStruct.RE = 0; // 禁用DMA请求
ADC_InitStruct.NSMP = 0; // 禁用两段采样
ADC_InitStruct.DATA_ALIGN = 0; // 右对齐
ADC_InitStruct.CSMP = 0; // 禁用连续采样
ADC_InitStruct.TCNT = 0; // 触发一次采样
ADC_InitStruct.TROVS = 0; // 禁用过采样
ADC_InitStruct.OVSR = 0; // 过采样率
ADC_InitStruct.TRIG = ADC_TRIG_SOFT; // 软件触发
ADC_InitStruct.S1 = 1; // 触发1次采样1次
ADC_InitStruct.S2 = 0; // 第二段常规采样次数
ADC_InitStruct.IS1 = 0; // 空闲采样次数
ADC_InitStruct.GAIN = ADC_GAIN_3V6; // 3.6V量程
ADC_InitStruct.LTH = 0; // 禁用模拟看门狗
ADC_InitStruct.HTH = 0xFFFF; // 禁用模拟看门狗
ADC_InitStruct.GEN = 0; // 禁用模拟看门狗
// 初始化ADC0
ADC_Init(ADC0, &ADC_InitStruct);
// 设置ADC0通道9
ADC_SetChn(ADC0, ADC_DAT_0, ADC_CHN_11);
}
/**
* @brief 主函数
*/
int main(void)
{
uint32_t i;
int32_t sum = 0;
GPIO_Config(GPIO0, 0, GPIO_Mode_OUT, GPIO_AF_GPIO);
// 初始化ADC0
ADC0_Init();
flg = 0;
SYS_WR_PROTECT = 0x7a83;
SYS_AFE_REG0 &= ~BIT15;
while (1)
{
if (flg)
{
GPIO_SetBits(GPIO0, GPIO_Pin_0);
// 进行100次采样
for (i = 0; i < ADC_SAMPLE_COUNT; i++)
{
ADC_ClearIRQFlag(ADC0, ADC_IF_SF1);
// 软件触发ADC采样
ADC_SoftTrig(ADC0);
while (ADC_GetIRQFlag(ADC0, ADC_IF_SF1) == 0)
;
// 获取采样结果
ADCSample[i] = ADC_GetConversionValue(ADC0, ADC_DAT_0);
}
// 重置总和,为下一次采样做准备
sum = 0;
// 计算采样结果的总和
for (i = 0; i < ADC_SAMPLE_COUNT; i++)
{
sum += (int32_t)ADCSample[i];
}
adcval = sum / ADC_SAMPLE_COUNT;
// 计算平均值
average = (float)sum * ((3.6f / 32752.0f) / ADC_SAMPLE_COUNT);
GPIO_ResetBits(GPIO0, GPIO_Pin_0);
flg = 0;
}
}
}
使用到的库函数
库函数部分代码
#define ADC0 ((ADC_TypeDef *)ADC0_BASE)
#define ADC0_CFG_DATA_ALIGN_BIT BIT10
#define ADC0_CHN0_PDS0(x) (uint32_t)(((x) & 0xF) << ADC0_CHN0_PDS0_POS)
#define ADC0_CHN0_PDS1(x) (uint32_t)(((x) & 0xF) << ADC0_CHN0_PDS1_POS)
#define ADC0_CHN0_PDS2(x) (uint32_t)(((x) & 0xF) << ADC0_CHN0_PDS2_POS)
#define ADC0_CHN0_PDS3(x) (uint32_t)(((x) & 0xF) << ADC0_CHN0_PDS3_POS)
#define ADC0_CHN1_PDS4(x) (uint32_t)(((x) & 0xF) << ADC0_CHN1_PDS4_POS)
#define ADC0_CHN1_PDS5(x) (uint32_t)(((x) & 0xF) << ADC0_CHN1_PDS5_POS)
#define ADC0_CHN1_PDS6(x) (uint32_t)(((x) & 0xF) << ADC0_CHN1_PDS6_POS)
#define ADC0_CHN1_PDS7(x) (uint32_t)(((x) & 0xF) << ADC0_CHN1_PDS7_POS)
#define ADC0_CHN2_PDS10(x) (uint32_t)(((x) & 0xF) << ADC0_CHN2_PDS10_POS)
#define ADC0_CHN2_PDS11(x) (uint32_t)(((x) & 0xF) << ADC0_CHN2_PDS11_POS)
#define ADC0_CHN2_PDS8(x) (uint32_t)(((x) & 0xF) << ADC0_CHN2_PDS8_POS)
#define ADC0_CHN2_PDS9(x) (uint32_t)(((x) & 0xF) << ADC0_CHN2_PDS9_POS)
#define ADC1 ((ADC_TypeDef *)ADC1_BASE)
#define GPIO_AF_I2C 6
#define NVR_ADDR_ADC_0_AMC0 0x00001424
#define NVR_ADDR_ADC_0_AMC1 0x0000142C
#define NVR_ADDR_ADC_0_DC0 0x00001420
#define NVR_ADDR_ADC_0_DC1 0x00001428
#define NVR_ADDR_ADC_1_AMC0 0x00001434
#define NVR_ADDR_ADC_1_AMC1 0x0000143C
#define NVR_ADDR_ADC_1_DC0 0x00001430
#define NVR_ADDR_ADC_1_DC1 0x00001438
#define REG_READ(reg) (reg)
#define REG_READ_BIT(reg,mask) ((reg) & (mask))
#define REG_READ_BIT_POS_MASK(reg,mask) (((reg) & (mask##_MASK)) >> mask##_POS)
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define REG_WRITE_BIT(reg,mask,val) reg = ((reg) & ~(mask)) | (val)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_ADC0 BIT22
#define SYS_MODULE_ADC1 BIT23
#define SYS_MODULE_GPIO BIT11
/**
* @brief ADC初始化结构体定义
*/
typedef struct ADC_InitTypeDef {
uint16_t IE;
uint16_t RE;
uint16_t NSMP;
uint16_t DATA_ALIGN;
uint16_t CSMP;
uint16_t TCNT;
uint16_t TROVS;
uint16_t OVSR;
uint32_t TRIG;
uint16_t S1;
uint16_t S2;
uint16_t IS1;
uint16_t GAIN;
uint16_t LTH;
uint16_t HTH;
uint16_t GEN;
uint16_t IDLE_PRI;
};
/**
* @brief ADC数据寄存器序号枚举定义
*/
typedef enum ADC_DAT_x {
ADC_DAT_0 = 0,
ADC_DAT_1 = 1,
ADC_DAT_2 = 2,
ADC_DAT_3 = 3,
ADC_DAT_4 = 4,
ADC_DAT_5 = 5,
ADC_DAT_6 = 6,
ADC_DAT_7 = 7,
ADC_DAT_8 = 8,
ADC_DAT_9 = 9,
ADC_DAT_10 = 10,
ADC_DAT_11 = 11,
}} {enum_name};
/**
* @brief ADC采样通道序号枚举定义
*/
typedef enum ADC_CHN_x {
ADC_CHN_0 = 0,
ADC_CHN_1 = 1,
ADC_CHN_2 = 2,
ADC_CHN_3 = 3,
ADC_CHN_4 = 4,
ADC_CHN_5 = 5,
ADC_CHN_6 = 6,
ADC_CHN_7 = 7,
ADC_CHN_8 = 8,
ADC_CHN_9 = 9,
ADC_CHN_10 = 10,
ADC_CHN_11 = 11,
ADC_CHN_12 = 12,
ADC_CHN_13 = 13,
ADC_CHN_14 = 14,
ADC_CHN_15 = 15,
ADC0_CHN_OPA0 = 0,
ADC0_CHN_OPA1 = 1,
ADC0_CHN_OPA2 = 2,
ADC0_CHN_OPA3 = 3,
ADC0_CHN_ADC01_CH4 = 4,
ADC0_CHN_ADC01_CH5 = 5,
ADC0_CHN_ADC01_CH6 = 6,
ADC0_CHN_ADC01_CH7 = 7,
ADC0_CHN_ADC0_CH8 = 8,
ADC0_CHN_REF24 = 9,
ADC0_CHN_ADC01_CH10 = 10,
ADC0_CHN_ADC01_CH11 = 11,
ADC0_CHN_ADC0_CH12 = 12,
ADC0_CHN_ADC0_CH13 = 13,
ADC0_CHN_DAC = 14,
ADC0_CHN_AVSS = 15,
ADC1_CHN_OPA0 = 0,
ADC1_CHN_OPA1 = 1,
ADC1_CHN_OPA2 = 2,
ADC1_CHN_OPA3 = 3,
ADC1_CHN_ADC01_CH4 = 4,
ADC1_CHN_ADC01_CH5 = 5,
ADC1_CHN_ADC01_CH6 = 6,
ADC1_CHN_ADC01_CH7 = 7,
ADC1_CHN_ADC1_CH8 = 8,
ADC1_CHN_ADC1_CH9 = 9,
ADC1_CHN_ADC01_CH10 = 10,
ADC1_CHN_ADC01_CH11 = 11,
ADC1_CHN_ADC1_CH12 = 12,
ADC1_CHN_ADC1_CH13 = 13,
ADC1_CHN_TMP = 14,
ADC1_CHN_AVDD = 15,
}} {enum_name};
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* @brief GPIO模式选择枚举
*/
typedef enum GPIO_Mode_TypeDef {
GPIO_Mode_IN = 0,
GPIO_Mode_OUT = 1,
GPIO_Mode_ANA = 2,
GPIO_Mode_IO = 3,
}} {enum_name};
/**
* @brief GPIO配置函数
* @param GPIOx: GPIO端口
* @param GPIO_PinSource: GPIO引脚
* @param mode: GPIO模式
* @param GPIO_AF_x: GPIO复用功能选择
*/
void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
{
GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = 1 << GPIO_PinSource;
GPIO_InitStructure.GPIO_Mode = mode;
if (GPIO_AF_x == GPIO_AF_I2C)
{
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
}
else
{
__NOP();
}
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**
* @brief 使能ADC模块
* @param ADCx: ADC模块指针,指向ADC0或ADC1
*/
void ADC_Enable(ADC_TypeDef *ADCx)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
// 使能ADC模块时钟
REG_SET(SYS0->AFE_REG7, BIT8 | BIT9);
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 初始化ADC模块
* @param ADCx: ADC模块指针,指向ADC0或ADC1
* @param ADC_InitStruct: 指向ADC初始化结构体的指针
*/
void ADC_Init(ADC_TypeDef *ADCx, ADC_InitTypeDef *ADC_InitStruct)
{
ADC_Enable(ADCx);
SYS_ModuleClockCmd(SYS_MODULE_ADC0, 1);
SYS_ModuleClockCmd(SYS_MODULE_ADC1, 1);
if (ADC_InitStruct->S2 == 0)
{
ADC_InitStruct->NSMP = DISABLE; // 当第二段采样次数为0时,不允许配置第二段使能
}
// 解锁SYS寄存器
REG_WRITE(SYS0->PROTECT, 0x7a83);
// 配置ADC初始化结构体中的各个成员
REG_WRITE(ADCx->IE, ADC_InitStruct->IE | ADC_InitStruct->RE);
// 计算并一次性配置CFG寄存器
REG_WRITE(ADCx->CFG, ((ADC_InitStruct->NSMP << 12) |
(1 << 11) | // 初始化时复位adc状态机
(ADC_InitStruct->DATA_ALIGN << 10) |
(ADC_InitStruct->IDLE_PRI << 9) |
(ADC_InitStruct->CSMP << 8) |
(ADC_InitStruct->TCNT << 4) |
(ADC_InitStruct->TROVS << 3) |
(ADC_InitStruct->OVSR << 0)));
REG_WRITE(ADCx->TRIG, ADC_InitStruct->TRIG);
// 配置CHNT寄存器
REG_WRITE(ADCx->CHNT, (REG_READ(ADCx->CHNT) & ~((0x0F << 0) | (0x0F << 4) | (0x0F << 8))) |
((ADC_InitStruct->S1 << 0) |
(ADC_InitStruct->S2 << 4) |
(ADC_InitStruct->IS1 << 8)));
// 配置GAIN寄存器
REG_WRITE_BIT(ADCx->GAIN, 1 << 0, ADC_InitStruct->GAIN << 0);
REG_WRITE(ADCx->LTH, ADC_InitStruct->LTH);
REG_WRITE(ADCx->HTH, ADC_InitStruct->HTH);
REG_WRITE(ADCx->GEN, ADC_InitStruct->GEN);
if (ADCx == ADC0)
{
REG_WRITE(ADCx->AMC0, TRIM_Read(NVR_ADDR_ADC_0_AMC0));
REG_WRITE(ADCx->DC0, TRIM_Read(NVR_ADDR_ADC_0_DC0));
REG_WRITE(ADCx->AMC1, TRIM_Read(NVR_ADDR_ADC_0_AMC1));
REG_WRITE(ADCx->DC1, TRIM_Read(NVR_ADDR_ADC_0_DC1));
}
else if (ADCx == ADC1)
{
REG_WRITE(ADCx->AMC0, TRIM_Read(NVR_ADDR_ADC_1_AMC0));
REG_WRITE(ADCx->DC0, TRIM_Read(NVR_ADDR_ADC_1_DC0));
REG_WRITE(ADCx->AMC1, TRIM_Read(NVR_ADDR_ADC_1_AMC1));
REG_WRITE(ADCx->DC1, TRIM_Read(NVR_ADDR_ADC_1_DC1));
}
else
{
__NOP();
}
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 复位指定的GPIO引脚位
* @param GPIOx GPIO模块指针
* @param GPIO_Pin 要复位的引脚
*/
void GPIO_ResetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
GPIOx->PDO &= ~GPIO_Pin;
}
/**
* @brief 将ADC初始化结构体置为默认值
* @param ADC_InitStruct: 指向ADC初始化结构体的指针
*/
void ADC_StructInit(ADC_InitTypeDef *ADC_InitStruct)
{
for (int i = 0; i < sizeof(ADC_InitTypeDef) / sizeof(uint16_t); i++)
{
((uint16_t *)ADC_InitStruct)[i] = 0;
}
}
/**
* @brief 执行软件触发ADC采样,采样次数取决于ADC_InitTypeDef里的S1的配置
* @param ADCx: ADC模块指针,指向ADC0或ADC1
*/
void ADC_SoftTrig(ADC_TypeDef *ADCx)
{
// 写入软件触发值
REG_WRITE(ADCx->SWT, 0x5AA5);
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 清除ADC中断标志位
* @param ADCx: ADC模块指针,指向ADC0或ADC1
* @param INT_flag: 中断标志位,可以是ADC_IF_ISF等
*/
void ADC_ClearIRQFlag(ADC_TypeDef *ADCx, uint16_t INT_flag)
{
REG_WRITE(ADCx->IF, INT_flag);
}
/**
* @brief 获取ADC中断标志位状态
* @param ADCx: ADC模块指针,指向ADC0或ADC1
* @param INT_flag: 中断标志位,可以是ADC_IF_ISF等
* @return 中断标志位状态
*/
uint16_t ADC_GetIRQFlag(ADC_TypeDef *ADCx, uint16_t INT_flag)
{
return REG_READ_BIT(ADCx->IF, INT_flag) ? 1 : 0;
}
/**
* @brief 获取ADC通道的转换值
* @param ADCx: ADC模块指针,指向ADC0或ADC1
* @param DATNum: 数据寄存器序号,如ADC_DAT_0
* @return 转换值
*/
s16 ADC_GetConversionValue(ADC_TypeDef *ADCx, ADC_DAT_x DATNum)
{
int32_t adc_dat = ADC_GetConversionValueS32(ADCx, DATNum);
// adc_dat不会小于-32767,这里不做判断
if (adc_dat > 32767)
{
adc_dat = 32767;
}
return adc_dat;
}
/**
* @brief 获取ADC通道的转换值(按int32返回,采样通道4、5、6、7时,返回值大于32767)
* @param ADCx: ADC模块指针,指向ADC0或ADC1
* @param DATNum: 数据寄存器序号,如ADC_DAT_0
* @return 转换值
*/
int32_t ADC_GetConversionValueS32(ADC_TypeDef *ADCx, ADC_DAT_x DATNum)
{
int32_t adc_dat = 0;
uint16_t adc_chn = 0;
switch (DATNum)
{
case ADC_DAT_0:
adc_dat = REG_READ(ADCx->DAT0);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN0, ADC0_CHN0_PDS0);
break;
case ADC_DAT_1:
adc_dat = REG_READ(ADCx->DAT1);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN0, ADC0_CHN0_PDS1);
break;
case ADC_DAT_2:
adc_dat = REG_READ(ADCx->DAT2);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN0, ADC0_CHN0_PDS2);
break;
case ADC_DAT_3:
adc_dat = REG_READ(ADCx->DAT3);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN0, ADC0_CHN0_PDS3);
break;
case ADC_DAT_4:
adc_dat = REG_READ(ADCx->DAT4);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN1, ADC0_CHN1_PDS4);
break;
case ADC_DAT_5:
adc_dat = REG_READ(ADCx->DAT5);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN1, ADC0_CHN1_PDS5);
break;
case ADC_DAT_6:
adc_dat = REG_READ(ADCx->DAT6);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN1, ADC0_CHN1_PDS6);
break;
case ADC_DAT_7:
adc_dat = REG_READ(ADCx->DAT7);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN1, ADC0_CHN1_PDS7);
break;
case ADC_DAT_8:
adc_dat = REG_READ(ADCx->DAT8);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN2, ADC0_CHN2_PDS8);
break;
case ADC_DAT_9:
adc_dat = REG_READ(ADCx->DAT9);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN2, ADC0_CHN2_PDS9);
break;
case ADC_DAT_10:
adc_dat = REG_READ(ADCx->DAT10);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN2, ADC0_CHN2_PDS10);
break;
case ADC_DAT_11:
adc_dat = REG_READ(ADCx->DAT11);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN2, ADC0_CHN2_PDS11);
break;
default:
adc_dat = 0;
break;
}
// 先按照int16处理一下
adc_dat = (int16_t)(adc_dat & 0xffff);
// ADC0/1采样公共通道4/5/6/7时,负端电压为2.4V,实际允许的输入电压范围是
// if ((adc_chn == 4) || (adc_chn == 5) || (adc_chn == 6) || (adc_chn == 7))
// {
// int32_t dat = adc_dat;
// if (REG_READ_BIT(SYS_AFE_REG4, BIT4) == 0)
// {
// if (REG_READ_BIT(ADC0_CFG, ADC0_CFG_DATA_ALIGN_BIT) == 0)
// { // 左对齐
// dat = dat + 0x5550;
// adc_dat = dat;
// }
// else
// { // 右对齐
// dat = dat + 0x555;
// adc_dat = dat;
// }
// }
// }
return adc_dat;
}
/**
* @brief 设置指定的GPIO引脚位
* @param GPIOx GPIO模块指针
* @param GPIO_Pin 要设置的引脚
*/
void GPIO_SetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
GPIOx->PDO |= GPIO_Pin;
}
/**
* @brief 设置第每次的采样通道,例如ADC_SetChn(ADC0,ADC_DAT_0,ADC_CHN_0),把adc第一次采样的通道设置为通道0
* @param ADCx: ADC模块指针,指向ADC0或ADC1
* @param datx: 数据寄存器序号,如ADC_DAT_0
* @param chnx: ADC采样通道序号枚举定义,如ADC_CHN_0
*/
void ADC_SetChn(ADC_TypeDef *ADCx, ADC_DAT_x datx, ADC_CHN_x chnx)
{
uint8_t n = (datx & 0x3) * 4;
switch (datx)
{
case ADC_DAT_0:
REG_WRITE(ADCx->CHN0, (REG_READ(ADCx->CHN0) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_1:
REG_WRITE(ADCx->CHN0, (REG_READ(ADCx->CHN0) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_2:
REG_WRITE(ADCx->CHN0, (REG_READ(ADCx->CHN0) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_3:
REG_WRITE(ADCx->CHN0, (REG_READ(ADCx->CHN0) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_4:
REG_WRITE(ADCx->CHN1, (REG_READ(ADCx->CHN1) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_5:
REG_WRITE(ADCx->CHN1, (REG_READ(ADCx->CHN1) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_6:
REG_WRITE(ADCx->CHN1, (REG_READ(ADCx->CHN1) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_7:
REG_WRITE(ADCx->CHN1, (REG_READ(ADCx->CHN1) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_8:
REG_WRITE(ADCx->CHN2, (REG_READ(ADCx->CHN2) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_9:
REG_WRITE(ADCx->CHN2, (REG_READ(ADCx->CHN2) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_10:
REG_WRITE(ADCx->CHN2, (REG_READ(ADCx->CHN2) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_11:
REG_WRITE(ADCx->CHN2, (REG_READ(ADCx->CHN2) & (~(0xf << n))) | (chnx << n));
break;
}
}
/**
* @brief 配置GPIO引脚复用功能
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource 引脚源
* @param GPIO_AF 复用功能选择
*/
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
uint8_t offset;
uint8_t pins = GPIO_PinSource >> 2;
offset = ((GPIO_PinSource & 0x3) * 4);
switch (pins)
{
case 0:
GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 1:
GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 2:
GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 3:
GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
break;
default:
break;
}
}
01.02_ADC-MCPWM触发ADC采样内部温度传感器
测试步骤
使用MCPWM触发ADC1采样温度传感器并计算温度
实测数据
tmp = 218
参考代码
01.02_ADC_McpwmTrigSampTmp.c
/**
* @brief ADC-MCPWM触发ADC采样内部温度传感器
* @details 使用MCPWM触发ADC1采样温度传感器并计算温度
*/
#include "lks32mc09x_lib.h"
void MCPWM_Config(void);
volatile int32_t tmp = 0;
/**
* @brief 主函数
*/
int main(void)
{
MCPWM_Config();
ADC_InitTypeDef ADC_InitStruct;
// 初始化ADC结构体
ADC_StructInit(&ADC_InitStruct);
// 配置ADC参数
ADC_InitStruct.IE = ADC_IE_SF1; // 禁用中断
ADC_InitStruct.RE = 0; // 禁用DMA请求
ADC_InitStruct.NSMP = 0; // 禁用两段采样
ADC_InitStruct.DATA_ALIGN = 0; // 右对齐
ADC_InitStruct.CSMP = 0; // 禁用连续采样
ADC_InitStruct.TCNT = 0; // 触发一次采样
ADC_InitStruct.TROVS = 0; // 禁用过采样
ADC_InitStruct.OVSR = 0; // 过采样率
ADC_InitStruct.TRIG = ADC_TRIG_MCPWM0_T0; // MCPWM0 T0 事件触发 ADC 常规采样
ADC_InitStruct.S1 = 1; // 第一段常规采样次数
ADC_InitStruct.S2 = 0; // 第二段常规采样次数
ADC_InitStruct.IS1 = 0; // 空闲采样次数
ADC_InitStruct.GAIN = ADC_GAIN_3V6; // 3.6V量程
ADC_InitStruct.LTH = 0; // 禁用模拟看门狗
ADC_InitStruct.HTH = 0xFFFF; // 禁用模拟看门狗
ADC_InitStruct.GEN = 0; // 禁用模拟看门狗
ADC_InitStruct.IDLE_PRI = 0;
ADC_Init(ADC1, &ADC_InitStruct);
ADC_SetChn(ADC1, ADC_DAT_0, ADC1_CHN_TMP);
TMP_Init();
NVIC_EnableIRQ(ADC1_IRQn);
__enable_irq();
while (1)
{
}
}
void ADC1_IRQHandler()
{
ADC_ClearIRQFlag(ADC1, ADC_IF_SF1);
tmp = TMP_GetCurrentTemperature(ADC_GetConversionValue(ADC1, ADC_DAT_0)); // 将ADC采样值转换为温度值并保存到全局变量CurrentTempature中
}
/**
* @brief 配置MCPWM输出三路中心对称的PWM,频率20kHz。
*/
void MCPWM_Config(void)
{
MCPWM_InitTypeDef MCPWM_InitStruct;
// 初始化MCPWM结构体
MCPWM_StructInit(&MCPWM_InitStruct);
// 配置MCPWM基本项
MCPWM_InitStruct.EN = 1; // 使能MCPWM模块
MCPWM_InitStruct.CLK_DIV = MCPWM_CLK_DIV_1; // 时钟1分频
MCPWM_InitStruct.TH = 4000;
MCPWM_InitStruct.TR = MCPWM_TR_T0; // 计数器溢出时刻触发自动更新
// 配置死区时间
MCPWM_InitStruct.DTHP = 0; // 上管死区时间
MCPWM_InitStruct.DTHN = 0; // 下管死区时间
// 配置IO输出
MCPWM_InitStruct.IO0 = MCPWM_IO_HL_PWM; // CH0: 上管高有效,下管低有效,正常输出PWM
MCPWM_InitStruct.IO1 = MCPWM_IO_HL_PWM; // CH1: 上管高有效,下管低有效,正常输出PWM
MCPWM_InitStruct.IO2 = MCPWM_IO_HL_PWM; // CH2: 上管高有效,下管低有效,正常输出PWM
MCPWM_InitStruct.IO3 = MCPWM_IO_DISABLE; // CH3: 不使用
MCPWM_InitStruct.FAIL_IO = MCPWM_FAIL_IO_OFF;
// 配置自动更新使能
MCPWM_InitStruct.AUEN = MCPWM_AUEN_DEFAULT; // 使能TH0自动加载
// 配置中断和DMA(本例程中不使用中断和DMA)
MCPWM_InitStruct.IE = MCPWM_IE_TMR3;
MCPWM_InitStruct.EIE = 0;
MCPWM_InitStruct.RE = 0;
// 初始化MCPWM模块
MCPWM_Init(MCPWM0, &MCPWM_InitStruct);
// 设置输出状态
MCPWM_SetOutputState(MCPWM0, 1);
// 设置PWM输出值,中心对齐模式下占空比50%
MCPWM_SetOutputVal(MCPWM0, 0, -1000, 1000); // CH0: 中心对齐,占空比50%
MCPWM_SetOutputVal(MCPWM0, 1, -1000, 1000); // CH1: 中心对齐,占空比50%
MCPWM_SetOutputVal(MCPWM0, 2, -1000, 1000); // CH2: 中心对齐,占空比50%
// 开始计数
MCPWM_SetTMR(MCPWM0, 0, 0, 1000, 3900);
MCPWM_StartCount(MCPWM0);
MCPWM0_UPDATE = 0xffffffff;
}
使用到的库函数
库函数部分代码
#define ADC0 ((ADC_TypeDef *)ADC0_BASE)
#define ADC0_CFG_DATA_ALIGN_BIT BIT10
#define ADC0_CHN0_PDS0(x) (uint32_t)(((x) & 0xF) << ADC0_CHN0_PDS0_POS)
#define ADC0_CHN0_PDS1(x) (uint32_t)(((x) & 0xF) << ADC0_CHN0_PDS1_POS)
#define ADC0_CHN0_PDS2(x) (uint32_t)(((x) & 0xF) << ADC0_CHN0_PDS2_POS)
#define ADC0_CHN0_PDS3(x) (uint32_t)(((x) & 0xF) << ADC0_CHN0_PDS3_POS)
#define ADC0_CHN1_PDS4(x) (uint32_t)(((x) & 0xF) << ADC0_CHN1_PDS4_POS)
#define ADC0_CHN1_PDS5(x) (uint32_t)(((x) & 0xF) << ADC0_CHN1_PDS5_POS)
#define ADC0_CHN1_PDS6(x) (uint32_t)(((x) & 0xF) << ADC0_CHN1_PDS6_POS)
#define ADC0_CHN1_PDS7(x) (uint32_t)(((x) & 0xF) << ADC0_CHN1_PDS7_POS)
#define ADC0_CHN2_PDS10(x) (uint32_t)(((x) & 0xF) << ADC0_CHN2_PDS10_POS)
#define ADC0_CHN2_PDS11(x) (uint32_t)(((x) & 0xF) << ADC0_CHN2_PDS11_POS)
#define ADC0_CHN2_PDS8(x) (uint32_t)(((x) & 0xF) << ADC0_CHN2_PDS8_POS)
#define ADC0_CHN2_PDS9(x) (uint32_t)(((x) & 0xF) << ADC0_CHN2_PDS9_POS)
#define ADC1 ((ADC_TypeDef *)ADC1_BASE)
#define MCPWM_COUNT_START BIT31
#define MCPWM_IO_NP BIT0
#define MCPWM_IO_PN_SW BIT6
#define MCPWM_IO_PP BIT1
#define NVR_ADDR_ADC_0_AMC0 0x00001424
#define NVR_ADDR_ADC_0_AMC1 0x0000142C
#define NVR_ADDR_ADC_0_DC0 0x00001420
#define NVR_ADDR_ADC_0_DC1 0x00001428
#define NVR_ADDR_ADC_1_AMC0 0x00001434
#define NVR_ADDR_ADC_1_AMC1 0x0000143C
#define NVR_ADDR_ADC_1_DC0 0x00001430
#define NVR_ADDR_ADC_1_DC1 0x00001438
#define NVR_ADDR_TMP_B 0x000014D4
#define NVR_ADDR_TMP_K 0x000014D0
#define REG_READ(reg) (reg)
#define REG_READ_BIT(reg,mask) ((reg) & (mask))
#define REG_READ_BIT_POS_MASK(reg,mask) (((reg) & (mask##_MASK)) >> mask##_POS)
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define REG_WRITE_BIT(reg,mask,val) reg = ((reg) & ~(mask)) | (val)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_ADC0 BIT22
#define SYS_MODULE_ADC1 BIT23
#define SYS_MODULE_MCPWM0 BIT10
/**
* @brief ADC初始化结构体定义
*/
typedef struct ADC_InitTypeDef {
uint16_t IE;
uint16_t RE;
uint16_t NSMP;
uint16_t DATA_ALIGN;
uint16_t CSMP;
uint16_t TCNT;
uint16_t TROVS;
uint16_t OVSR;
uint32_t TRIG;
uint16_t S1;
uint16_t S2;
uint16_t IS1;
uint16_t GAIN;
uint16_t LTH;
uint16_t HTH;
uint16_t GEN;
uint16_t IDLE_PRI;
};
/**
* @brief ADC数据寄存器序号枚举定义
*/
typedef enum ADC_DAT_x {
ADC_DAT_0 = 0,
ADC_DAT_1 = 1,
ADC_DAT_2 = 2,
ADC_DAT_3 = 3,
ADC_DAT_4 = 4,
ADC_DAT_5 = 5,
ADC_DAT_6 = 6,
ADC_DAT_7 = 7,
ADC_DAT_8 = 8,
ADC_DAT_9 = 9,
ADC_DAT_10 = 10,
ADC_DAT_11 = 11,
}} {enum_name};
/**
* @brief ADC采样通道序号枚举定义
*/
typedef enum ADC_CHN_x {
ADC_CHN_0 = 0,
ADC_CHN_1 = 1,
ADC_CHN_2 = 2,
ADC_CHN_3 = 3,
ADC_CHN_4 = 4,
ADC_CHN_5 = 5,
ADC_CHN_6 = 6,
ADC_CHN_7 = 7,
ADC_CHN_8 = 8,
ADC_CHN_9 = 9,
ADC_CHN_10 = 10,
ADC_CHN_11 = 11,
ADC_CHN_12 = 12,
ADC_CHN_13 = 13,
ADC_CHN_14 = 14,
ADC_CHN_15 = 15,
ADC0_CHN_OPA0 = 0,
ADC0_CHN_OPA1 = 1,
ADC0_CHN_OPA2 = 2,
ADC0_CHN_OPA3 = 3,
ADC0_CHN_ADC01_CH4 = 4,
ADC0_CHN_ADC01_CH5 = 5,
ADC0_CHN_ADC01_CH6 = 6,
ADC0_CHN_ADC01_CH7 = 7,
ADC0_CHN_ADC0_CH8 = 8,
ADC0_CHN_REF24 = 9,
ADC0_CHN_ADC01_CH10 = 10,
ADC0_CHN_ADC01_CH11 = 11,
ADC0_CHN_ADC0_CH12 = 12,
ADC0_CHN_ADC0_CH13 = 13,
ADC0_CHN_DAC = 14,
ADC0_CHN_AVSS = 15,
ADC1_CHN_OPA0 = 0,
ADC1_CHN_OPA1 = 1,
ADC1_CHN_OPA2 = 2,
ADC1_CHN_OPA3 = 3,
ADC1_CHN_ADC01_CH4 = 4,
ADC1_CHN_ADC01_CH5 = 5,
ADC1_CHN_ADC01_CH6 = 6,
ADC1_CHN_ADC01_CH7 = 7,
ADC1_CHN_ADC1_CH8 = 8,
ADC1_CHN_ADC1_CH9 = 9,
ADC1_CHN_ADC01_CH10 = 10,
ADC1_CHN_ADC01_CH11 = 11,
ADC1_CHN_ADC1_CH12 = 12,
ADC1_CHN_ADC1_CH13 = 13,
ADC1_CHN_TMP = 14,
ADC1_CHN_AVDD = 15,
}} {enum_name};
typedef struct MCPWM_InitTypeDef {
uint16_t EN;
uint32_t COUNT;
uint16_t CLK_DIV;
uint16_t TH;
uint16_t TR;
int16_t TMR0;
int16_t TMR1;
int16_t TMR2;
int16_t TMR3;
uint16_t DTHP;
uint16_t DTHN;
uint16_t FLT_DIV;
uint16_t IO0;
uint16_t IO1;
uint16_t IO2;
uint16_t IO3;
uint32_t AUEN;
uint32_t IE;
uint32_t EIE;
uint32_t RE;
uint32_t FAIL_IO;
uint32_t FAIL0;
uint32_t FAIL1;
};
/**
* @brief 使能ADC模块
* @param ADCx: ADC模块指针,指向ADC0或ADC1
*/
void ADC_Enable(ADC_TypeDef *ADCx)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
// 使能ADC模块时钟
REG_SET(SYS0->AFE_REG7, BIT8 | BIT9);
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 使能温度传感器
*/
void TMP_Enable(void)
{
SYS_WR_PROTECT = 0x7a83;
SYS_AFE_REG5 |= BIT3;
SYS_WR_PROTECT = 0;
}
/**
* @brief 初始化ADC模块
* @param ADCx: ADC模块指针,指向ADC0或ADC1
* @param ADC_InitStruct: 指向ADC初始化结构体的指针
*/
void ADC_Init(ADC_TypeDef *ADCx, ADC_InitTypeDef *ADC_InitStruct)
{
ADC_Enable(ADCx);
SYS_ModuleClockCmd(SYS_MODULE_ADC0, 1);
SYS_ModuleClockCmd(SYS_MODULE_ADC1, 1);
if (ADC_InitStruct->S2 == 0)
{
ADC_InitStruct->NSMP = DISABLE; // 当第二段采样次数为0时,不允许配置第二段使能
}
// 解锁SYS寄存器
REG_WRITE(SYS0->PROTECT, 0x7a83);
// 配置ADC初始化结构体中的各个成员
REG_WRITE(ADCx->IE, ADC_InitStruct->IE | ADC_InitStruct->RE);
// 计算并一次性配置CFG寄存器
REG_WRITE(ADCx->CFG, ((ADC_InitStruct->NSMP << 12) |
(1 << 11) | // 初始化时复位adc状态机
(ADC_InitStruct->DATA_ALIGN << 10) |
(ADC_InitStruct->IDLE_PRI << 9) |
(ADC_InitStruct->CSMP << 8) |
(ADC_InitStruct->TCNT << 4) |
(ADC_InitStruct->TROVS << 3) |
(ADC_InitStruct->OVSR << 0)));
REG_WRITE(ADCx->TRIG, ADC_InitStruct->TRIG);
// 配置CHNT寄存器
REG_WRITE(ADCx->CHNT, (REG_READ(ADCx->CHNT) & ~((0x0F << 0) | (0x0F << 4) | (0x0F << 8))) |
((ADC_InitStruct->S1 << 0) |
(ADC_InitStruct->S2 << 4) |
(ADC_InitStruct->IS1 << 8)));
// 配置GAIN寄存器
REG_WRITE_BIT(ADCx->GAIN, 1 << 0, ADC_InitStruct->GAIN << 0);
REG_WRITE(ADCx->LTH, ADC_InitStruct->LTH);
REG_WRITE(ADCx->HTH, ADC_InitStruct->HTH);
REG_WRITE(ADCx->GEN, ADC_InitStruct->GEN);
if (ADCx == ADC0)
{
REG_WRITE(ADCx->AMC0, TRIM_Read(NVR_ADDR_ADC_0_AMC0));
REG_WRITE(ADCx->DC0, TRIM_Read(NVR_ADDR_ADC_0_DC0));
REG_WRITE(ADCx->AMC1, TRIM_Read(NVR_ADDR_ADC_0_AMC1));
REG_WRITE(ADCx->DC1, TRIM_Read(NVR_ADDR_ADC_0_DC1));
}
else if (ADCx == ADC1)
{
REG_WRITE(ADCx->AMC0, TRIM_Read(NVR_ADDR_ADC_1_AMC0));
REG_WRITE(ADCx->DC0, TRIM_Read(NVR_ADDR_ADC_1_DC0));
REG_WRITE(ADCx->AMC1, TRIM_Read(NVR_ADDR_ADC_1_AMC1));
REG_WRITE(ADCx->DC1, TRIM_Read(NVR_ADDR_ADC_1_DC1));
}
else
{
__NOP();
}
}
/**
* @brief 初始化MCPWM模块
* @param MCPWMx MCPWM模块指针
* @param MCPWM_InitStruct 指向包含初始化参数的MCPWM_InitTypeDef结构体
*/
void MCPWM_Init(MCPWM_TypeDef *MCPWMx, MCPWM_InitTypeDef *MCPWM_InitStruct)
{
// 复位MCPWM模块
MCPWM_Reset(MCPWMx);
if (MCPWM_InitStruct->EN)
{
// 使能MCPWM模块
MCPWM_Enable(MCPWMx);
// 初始化MCPWM模块
MCPWMx->PRT = 0xdead; // mcpwm模块解锁
if (MCPWM_InitStruct->COUNT & MCPWM_COUNT_START)
{
MCPWMx->TCLK = BIT6 | (MCPWM_InitStruct->CLK_DIV << 12) | BIT2;
}
else
{
if (MCPWM_InitStruct->COUNT != 0)
{
MCPWMx->TCLK = ((MCPWMx->TCLK & (~BIT6)) | BIT8) | (MCPWM_InitStruct->CLK_DIV << 12) | BIT2; // 关闭计数器,并打开外部触发
MCPWMx->EVT0 = MCPWM_InitStruct->COUNT; // 设置外部触发信号
}
else
{
MCPWMx->TCLK = (MCPWM_InitStruct->CLK_DIV << 12) | BIT2;
}
}
{
uint16_t chdef = 0;
chdef = MCPWM_InitStruct->FAIL_IO;
if (MCPWM_InitStruct->IO0 & MCPWM_IO_NP)
{
chdef ^= BIT0;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO0 & MCPWM_IO_PP)
{
chdef ^= BIT1;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO1 & MCPWM_IO_NP)
{
chdef ^= BIT2;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO1 & MCPWM_IO_PP)
{
chdef ^= BIT3;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO2 & MCPWM_IO_NP)
{
chdef ^= BIT4;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO2 & MCPWM_IO_PP)
{
chdef ^= BIT5;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO3 & MCPWM_IO_NP)
{
chdef ^= BIT6;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO3 & MCPWM_IO_PP)
{
chdef ^= BIT7;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO0 & MCPWM_IO_PN_SW)
{
// 交换chdef的bit0和bit1
chdef = (chdef & 0xfffe) | ((chdef & 0x0001) << 1) | ((chdef & 0x0002) >> 1);
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO1 & MCPWM_IO_PN_SW)
{
// 交换chdef的bit2和bit3
chdef = (chdef & 0xfffc) | ((chdef & 0x0004) << 1) | ((chdef & 0x0008) >> 1);
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO2 & MCPWM_IO_PN_SW)
{
// 交换chdef的bit4和bit5
chdef = (chdef & 0xfff0) | ((chdef & 0x0010) << 1) | ((chdef & 0x0020) >> 1);
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO3 & MCPWM_IO_PN_SW)
{
// 交换chdef的bit6和bit7
chdef = (chdef & 0xff00) | ((chdef & 0x0040) << 1) | ((chdef & 0x0080) >> 1);
}
else
{
__NOP();
}
MCPWMx->CH_DEF = chdef;
}
MCPWMx->TH00 = 0;
MCPWMx->TH01 = 0;
MCPWMx->TH10 = 0;
MCPWMx->TH11 = 0;
MCPWMx->TH20 = 0;
MCPWMx->TH21 = 0;
MCPWMx->TH30 = 0;
MCPWMx->TH31 = 0;
MCPWMx->CNT0 = -MCPWM_InitStruct->TH;
MCPWMx->TH0 = MCPWM_InitStruct->TH;
MCPWMx->TMR0 = MCPWM_InitStruct->TMR0;
MCPWMx->TMR1 = MCPWM_InitStruct->TMR1;
MCPWMx->TMR2 = MCPWM_InitStruct->TMR2;
MCPWMx->TMR3 = MCPWM_InitStruct->TMR3;
MCPWMx->DTH00 = MCPWM_InitStruct->DTHP;
MCPWMx->DTH01 = MCPWM_InitStruct->DTHN;
MCPWMx->FLT = MCPWM_InitStruct->FLT_DIV;
MCPWMx->IO01 = MCPWM_InitStruct->IO0 | (MCPWM_InitStruct->IO1 << 8);
MCPWMx->IO23 = MCPWM_InitStruct->IO2 | (MCPWM_InitStruct->IO3 << 8);
MCPWMx->CH_FAIL = MCPWM_InitStruct->FAIL0 | MCPWM_InitStruct->FAIL1;
MCPWMx->AUEN = MCPWM_InitStruct->AUEN;
MCPWMx->IE0 = MCPWM_InitStruct->IE;
MCPWMx->EIE = MCPWM_InitStruct->EIE;
MCPWMx->RE = MCPWM_InitStruct->RE;
MCPWMx->SDCFG = MCPWM_InitStruct->TR;
// 更新所有存在影子寄存器的寄存器
MCPWMx->UPDATE = 0xffffffff;
MCPWMx->IF0 = 0xffff;
MCPWMx->EIF = 0xffff;
MCPWMx->PRT = 0x0000; // mcpwm模块上锁
}
else
{
// 失能MCPWM模块
MCPWM_Disable(MCPWMx);
}
}
/**
* @brief 初始化温度传感器
*/
void TMP_Init(void)
{
tmp_k = TRIM_Read(NVR_ADDR_TMP_K);
tmp_b = TRIM_Read(NVR_ADDR_TMP_B);
TMP_Enable();
}
/**
* @brief 将ADC初始化结构体置为默认值
* @param ADC_InitStruct: 指向ADC初始化结构体的指针
*/
void ADC_StructInit(ADC_InitTypeDef *ADC_InitStruct)
{
for (int i = 0; i < sizeof(ADC_InitTypeDef) / sizeof(uint16_t); i++)
{
((uint16_t *)ADC_InitStruct)[i] = 0;
}
}
/**
* @brief 将MCPWM_InitTypeDef结构体初始化为默认值
* @param MCPWM_InitStruct 指向要初始化的MCPWM_InitTypeDef结构体
*/
void MCPWM_StructInit(MCPWM_InitTypeDef *MCPWM_InitStruct)
{
for (int i = 0; i < sizeof(MCPWM_InitTypeDef); i++)
{
((uint8_t *)MCPWM_InitStruct)[i] = 0;
}
}
/**
* @brief 开始计数
* @param MCPWMx MCPWM模块
* @param count 计数器初始值
*/
void MCPWM_StartCount(MCPWM_TypeDef *MCPWMx)
{
MCPWMx->PRT = 0xdead; // mcpwm模块解锁
// MCPWMx->UPDATE = BIT11;
MCPWMx->TCLK |= BIT6;
MCPWMx->PRT = 0x0000; // mcpwm模块上锁
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 模块使能
* @param MCPWMx MCPWM模块
*/
void MCPWM_Enable(MCPWM_TypeDef *MCPWMx)
{
SYS_ModuleClockCmd(SYS_MODULE_MCPWM0, ENABLE);
MCPWMx->PRT = 0xdead;
MCPWMx->TCLK |= BIT2;
MCPWMx->PRT = 0;
}
/**
* @brief 模块复位
* @param MCPWMx MCPWM模块
*/
void MCPWM_Reset(MCPWM_TypeDef *MCPWMx)
{
SYS_SoftResetModule(SYS_MODULE_MCPWM0);
}
/**
* @brief 模块失能
* @param MCPWMx MCPWM模块
*/
void MCPWM_Disable(MCPWM_TypeDef *MCPWMx)
{
MCPWMx->PRT = 0xdead;
MCPWMx->TCLK &= ~BIT2;
MCPWMx->PRT = 0;
SYS_ModuleClockCmd(SYS_MODULE_MCPWM0, DISABLE);
}
/**
* @brief 清除ADC中断标志位
* @param ADCx: ADC模块指针,指向ADC0或ADC1
* @param INT_flag: 中断标志位,可以是ADC_IF_ISF等
*/
void ADC_ClearIRQFlag(ADC_TypeDef *ADCx, uint16_t INT_flag)
{
REG_WRITE(ADCx->IF, INT_flag);
}
/**
* @brief 获取ADC通道的转换值
* @param ADCx: ADC模块指针,指向ADC0或ADC1
* @param DATNum: 数据寄存器序号,如ADC_DAT_0
* @return 转换值
*/
s16 ADC_GetConversionValue(ADC_TypeDef *ADCx, ADC_DAT_x DATNum)
{
int32_t adc_dat = ADC_GetConversionValueS32(ADCx, DATNum);
// adc_dat不会小于-32767,这里不做判断
if (adc_dat > 32767)
{
adc_dat = 32767;
}
return adc_dat;
}
/**
* @brief 获取ADC通道的转换值(按int32返回,采样通道4、5、6、7时,返回值大于32767)
* @param ADCx: ADC模块指针,指向ADC0或ADC1
* @param DATNum: 数据寄存器序号,如ADC_DAT_0
* @return 转换值
*/
int32_t ADC_GetConversionValueS32(ADC_TypeDef *ADCx, ADC_DAT_x DATNum)
{
int32_t adc_dat = 0;
uint16_t adc_chn = 0;
switch (DATNum)
{
case ADC_DAT_0:
adc_dat = REG_READ(ADCx->DAT0);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN0, ADC0_CHN0_PDS0);
break;
case ADC_DAT_1:
adc_dat = REG_READ(ADCx->DAT1);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN0, ADC0_CHN0_PDS1);
break;
case ADC_DAT_2:
adc_dat = REG_READ(ADCx->DAT2);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN0, ADC0_CHN0_PDS2);
break;
case ADC_DAT_3:
adc_dat = REG_READ(ADCx->DAT3);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN0, ADC0_CHN0_PDS3);
break;
case ADC_DAT_4:
adc_dat = REG_READ(ADCx->DAT4);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN1, ADC0_CHN1_PDS4);
break;
case ADC_DAT_5:
adc_dat = REG_READ(ADCx->DAT5);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN1, ADC0_CHN1_PDS5);
break;
case ADC_DAT_6:
adc_dat = REG_READ(ADCx->DAT6);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN1, ADC0_CHN1_PDS6);
break;
case ADC_DAT_7:
adc_dat = REG_READ(ADCx->DAT7);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN1, ADC0_CHN1_PDS7);
break;
case ADC_DAT_8:
adc_dat = REG_READ(ADCx->DAT8);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN2, ADC0_CHN2_PDS8);
break;
case ADC_DAT_9:
adc_dat = REG_READ(ADCx->DAT9);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN2, ADC0_CHN2_PDS9);
break;
case ADC_DAT_10:
adc_dat = REG_READ(ADCx->DAT10);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN2, ADC0_CHN2_PDS10);
break;
case ADC_DAT_11:
adc_dat = REG_READ(ADCx->DAT11);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN2, ADC0_CHN2_PDS11);
break;
default:
adc_dat = 0;
break;
}
// 先按照int16处理一下
adc_dat = (int16_t)(adc_dat & 0xffff);
// ADC0/1采样公共通道4/5/6/7时,负端电压为2.4V,实际允许的输入电压范围是
// if ((adc_chn == 4) || (adc_chn == 5) || (adc_chn == 6) || (adc_chn == 7))
// {
// int32_t dat = adc_dat;
// if (REG_READ_BIT(SYS_AFE_REG4, BIT4) == 0)
// {
// if (REG_READ_BIT(ADC0_CFG, ADC0_CFG_DATA_ALIGN_BIT) == 0)
// { // 左对齐
// dat = dat + 0x5550;
// adc_dat = dat;
// }
// else
// { // 右对齐
// dat = dat + 0x555;
// adc_dat = dat;
// }
// }
// }
return adc_dat;
}
/**
* @brief 获取当前温度
* @param ADC_value ADC采样值
* @return 当前温度值,单位为摄氏度
*/
s16 TMP_GetCurrentTemperature(s16 ADC_value)
{
s16 t_Temperture;
if (ADC1->CFG & BIT10)
{
t_Temperture = (tmp_b - ((s32)tmp_k * ADC_value) / 1000);
}
else
{
t_Temperture = (tmp_b - ((s32)tmp_k * (ADC_value >> 4)) / 1000);
}
return t_Temperture;
}
/**
* @brief 设置MCPWM触发ADC事件时间
* @param MCPWMx MCPWM模块
* @param tmr0 事件0时间
* @param tmr1 事件1时间
* @param tmr2 事件2时间
* @param tmr3 事件3时间
*/
void MCPWM_SetTMR(MCPWM_TypeDef *MCPWMx, int16_t tmr0, int16_t tmr1, int16_t tmr2, int16_t tmr3)
{
MCPWMx->TMR0 = tmr0;
MCPWMx->TMR1 = tmr1;
MCPWMx->TMR2 = tmr2;
MCPWMx->TMR3 = tmr3;
}
/**
* @brief 设置MCPWM输出值
* @param MCPWMx MCPWM模块
* @param chn 通道号 0-3
* @param p 上管打开时间
* @param n 下管打开时间
*/
void MCPWM_SetOutputVal(MCPWM_TypeDef *MCPWMx, uint8_t chn, int16_t p, int16_t n)
{
switch (chn)
{
case 0:
MCPWMx->TH00 = p;
MCPWMx->TH01 = n;
break;
case 1:
MCPWMx->TH10 = p;
MCPWMx->TH11 = n;
break;
case 2:
MCPWMx->TH20 = p;
MCPWMx->TH21 = n;
break;
case 3:
MCPWMx->TH30 = p;
MCPWMx->TH31 = n;
break;
default:
break;
}
}
/**
* @brief 设置MCPWM输出状态
* @param MCPWMx MCPWM模块
* @param state 输出状态 1打开输出 0关闭输出
*/
void MCPWM_SetOutputState(MCPWM_TypeDef *MCPWMx, uint8_t state)
{
MCPWMx->PRT = 0xdead; // mcpwm模块解锁
if (state)
{
MCPWMx->EIF = MCPWMx->EIF;
MCPWMx->CH_FAIL |= BIT6;
}
else
{
MCPWMx->CH_FAIL &= ~BIT6;
}
MCPWMx->PRT = 0x0000; // mcpwm模块上锁
}
/**
* @brief 设置第每次的采样通道,例如ADC_SetChn(ADC0,ADC_DAT_0,ADC_CHN_0),把adc第一次采样的通道设置为通道0
* @param ADCx: ADC模块指针,指向ADC0或ADC1
* @param datx: 数据寄存器序号,如ADC_DAT_0
* @param chnx: ADC采样通道序号枚举定义,如ADC_CHN_0
*/
void ADC_SetChn(ADC_TypeDef *ADCx, ADC_DAT_x datx, ADC_CHN_x chnx)
{
uint8_t n = (datx & 0x3) * 4;
switch (datx)
{
case ADC_DAT_0:
REG_WRITE(ADCx->CHN0, (REG_READ(ADCx->CHN0) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_1:
REG_WRITE(ADCx->CHN0, (REG_READ(ADCx->CHN0) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_2:
REG_WRITE(ADCx->CHN0, (REG_READ(ADCx->CHN0) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_3:
REG_WRITE(ADCx->CHN0, (REG_READ(ADCx->CHN0) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_4:
REG_WRITE(ADCx->CHN1, (REG_READ(ADCx->CHN1) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_5:
REG_WRITE(ADCx->CHN1, (REG_READ(ADCx->CHN1) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_6:
REG_WRITE(ADCx->CHN1, (REG_READ(ADCx->CHN1) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_7:
REG_WRITE(ADCx->CHN1, (REG_READ(ADCx->CHN1) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_8:
REG_WRITE(ADCx->CHN2, (REG_READ(ADCx->CHN2) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_9:
REG_WRITE(ADCx->CHN2, (REG_READ(ADCx->CHN2) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_10:
REG_WRITE(ADCx->CHN2, (REG_READ(ADCx->CHN2) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_11:
REG_WRITE(ADCx->CHN2, (REG_READ(ADCx->CHN2) & (~(0xf << n))) | (chnx << n));
break;
}
}
/**
* @brief 软复位模块
* @param nModule 模块编号
*/
void SYS_SoftResetModule(uint32_t nModule)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
REG_SET(SYS0->SFT_RST, nModule);
REG_RESET(SYS0->SFT_RST, nModule); // 触发复位后需清除复位位
REG_WRITE(SYS0->PROTECT, 0);
}
02.01_CLU-与门
测试步骤
使用clu实现与门逻辑,使用P0.0(A)P0.6(B)两个IO口,结果输出到P2.6
实测数据
| T | A | B | Y |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 1 | 1 | 0 | 0 |
| 2 | 0 | 1 | 0 |
| 3 | 1 | 1 | 1 |
参考代码
02.01_CLU_init.c
/**
* @brief CLU-与门
* @details
* 使用clu实现与门逻辑,使用P0.0(A)P0.6(B)两个IO口,结果输出到P2.6
*/
#include "lks32mc09x_clu.h"
#include "lks32mc09x_gpio.h"
#include "lks32mc09x_sys.h"
/**
* @brief 主函数,用于测试HSI时钟输出及GPIO翻转
*/
int main(void)
{
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
SYS_ModuleClockCmd(SYS_MODULE_CL0, ENABLE);
CLU_InitTypeDef cluconfig;
CLU_StructInit(&cluconfig);
cluconfig.EN = ENABLE; ///< 模块使能 0关闭模块 1打开模块
cluconfig.FIE = DISABLE; ///< 下降沿中断使能
cluconfig.RIE = DISABLE; ///< 上升沿中断使能
cluconfig.MXA = CLU_MXA_CLU0_P0_0; ///< A输入复用选择 格式 CLU_MXA_CLUa_b a取值范围为[0 1 2 3] 对应不同的CLU b表示选择不同的输入信号
cluconfig.MXB = CLU_MXB_CLU0_P0_6; ///< B输入复用选择 格式 CLU_MXB_CLUa_b a取值范围为[0 1 2 3] 对应不同的CLU b表示选择不同的输入信号
cluconfig.FN = CLU_FN_A_B; ///< 真值表 格式 CLU_FN_x x取值范围为[A NOTA A_NOTB ...]对应为最简与或式下的最小相 例如 y=a+bc+!c 表示为 CLU_FN_A|CLU_FN_B_C|CLU_FN_NOTC
cluconfig.OUTSEL = 1; ///< 输出选择 0: D 触发器输出 1: LUT 输出
cluconfig.OEN = 1; ///< 输出使能 0: 关闭 1: 使能
cluconfig.CLKINV = 0; ///< D 触发器时钟电平 0: 同相 1: 反向
cluconfig.CLKSEL = 0; ///< D 触发器时钟选择 0x0: 进位输入 0x1: MXA 输入 0x2: ALTCLK0 0x3: ALTCLK1
CLU_Init(CLU, CLU_MODEL_CLU0, &cluconfig);
GPIO_Config(GPIO0, GPIO_PinSource_0, GPIO_Mode_IN, GPIO_AF_CL);
GPIO_Config(GPIO0, GPIO_PinSource_6, GPIO_Mode_IN, GPIO_AF_CL);
GPIO_Config(GPIO2, GPIO_PinSource_6, GPIO_Mode_OUT, GPIO_AF_CL);
while (1)
{
}
}
使用到的库函数
库函数部分代码
#define CLU_FN_A 0xf0
#define CLU_FN_B_C 0x88
#define CLU_FN_NOTC 0x55
#define CLU_MODEL_CLU0 BIT0
#define CLU_MODEL_CLU1 BIT1
#define CLU_MODEL_CLU2 BIT2
#define CLU_MODEL_CLU3 BIT3
#define GPIO_AF_I2C 6
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define REG_WRITE_BIT(reg,mask,val) reg = ((reg) & ~(mask)) | (val)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_CL0 BIT21
#define SYS_MODULE_GPIO BIT11
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* @brief GPIO模式选择枚举
*/
typedef enum GPIO_Mode_TypeDef {
GPIO_Mode_IN = 0,
GPIO_Mode_OUT = 1,
GPIO_Mode_ANA = 2,
GPIO_Mode_IO = 3,
}} {enum_name};
typedef struct CLU_InitTypeDef {
uint32_t EN;
uint32_t FIE;
uint32_t RIE;
uint32_t MXA;
uint32_t MXB;
uint32_t FN;
uint32_t OUTSEL;
uint32_t OEN;
uint32_t CLKINV;
uint32_t CLKSEL;
};
/**
* @brief GPIO配置函数
* @param GPIOx: GPIO端口
* @param GPIO_PinSource: GPIO引脚
* @param mode: GPIO模式
* @param GPIO_AF_x: GPIO复用功能选择
*/
void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
{
GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = 1 << GPIO_PinSource;
GPIO_InitStructure.GPIO_Mode = mode;
if (GPIO_AF_x == GPIO_AF_I2C)
{
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
}
else
{
__NOP();
}
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**
* @brief CLU初始化结构体初始化
*/
void CLU_StructInit(CLU_InitTypeDef *CLU_InitStruct)
{
CLU_InitStruct->FIE = 0; ///< 下降沿中断使能
CLU_InitStruct->RIE = 0; ///< 上升沿中断使能
CLU_InitStruct->EN = 0; ///< 模块使能 0关闭模块 1打开模块
CLU_InitStruct->MXA = 0; ///< A输入复用选择 格式 CLU_MXA_CLUa_b a取值范围为[0 1 2 3] 对应不同的CLU b表示选择不同的输入信号
CLU_InitStruct->MXB = 0; ///< B输入复用选择 格式 CLU_MXB_CLUa_b a取值范围为[0 1 2 3] 对应不同的CLU b表示选择不同的输入信号
CLU_InitStruct->FN = 0; ///< 真值表 格式 CLU_FN_x x取值范围为[A NOTA A_NOTB ...]对应为最简与或式下的最小相 例如 y=a+bc+!c 表示为 CLU_FN_A|CLU_FN_B_C|CLU_FN_NOTC
CLU_InitStruct->OUTSEL = 0; ///< 输出选择 0: D 触发器输出 1: LUT 输出
CLU_InitStruct->OEN = 0; ///< 输出使能 0: 关闭 1: 使能
CLU_InitStruct->CLKINV = 0; ///< D 触发器时钟电平 0: 同相 1: 反向
CLU_InitStruct->CLKSEL = 0; ///< D 触发器时钟选择 0x0: 进位输入 0x1: MXA 输入 0x2: ALTCLK0 0x3: ALTCLK1
}
/**
* @brief CLU模块时钟使能
*/
void CLU_Enable(CLU_TypeDef *CLUx)
{
SYS_ModuleClockCmd(SYS_MODULE_CL0, ENABLE);
}
/**
* @brief CLU计算初始化
*/
void CLU_Init(CLU_TypeDef *CLUx, uint8_t CLU_MODEL_CLUx, CLU_InitTypeDef *CLU_InitStruct)
{
CLU_Enable(CLUx);
if ((CLU_MODEL_CLUx & CLU_MODEL_CLU0) != 0)
{
REG_WRITE_BIT(CLUx->EN, BIT0, CLU_InitStruct->EN ? BIT0 : 0);
REG_WRITE_BIT(CLUx->IE, BIT0, CLU_InitStruct->FIE ? BIT0 : 0);
REG_WRITE_BIT(CLUx->IE, BIT1, CLU_InitStruct->RIE ? BIT1 : 0);
REG_WRITE_BIT(CLUx->MX, 0x000f, (CLU_InitStruct->MXA << 4) | (CLU_InitStruct->MXB));
REG_WRITE_BIT(CLUx->FN, 0x000f, CLU_InitStruct->FN);
REG_WRITE_BIT(CLUx->CF, 0x000f, (CLU_InitStruct->OUTSEL << 7) | (CLU_InitStruct->OEN << 6) | (CLU_InitStruct->CLKINV << 2) | (CLU_InitStruct->CLKSEL));
REG_SET(CLUx->CF, BIT3);
}
if ((CLU_MODEL_CLUx & CLU_MODEL_CLU1) != 0)
{
REG_WRITE_BIT(CLUx->EN, BIT1, CLU_InitStruct->EN ? BIT1 : 0);
REG_WRITE_BIT(CLUx->IE, BIT2, CLU_InitStruct->FIE ? BIT2 : 0);
REG_WRITE_BIT(CLUx->IE, BIT3, CLU_InitStruct->RIE ? BIT3 : 0);
REG_WRITE_BIT(CLUx->MX, 0x00f0, (CLU_InitStruct->MXA << 12) | (CLU_InitStruct->MXB << 8));
REG_WRITE_BIT(CLUx->FN, 0x00f0, CLU_InitStruct->FN << 8);
REG_WRITE_BIT(CLUx->CF, 0x00f0, (CLU_InitStruct->OUTSEL << 15) | (CLU_InitStruct->OEN << 14) | (CLU_InitStruct->CLKINV << 10) | (CLU_InitStruct->CLKSEL << 8));
REG_SET(CLUx->CF, BIT11);
}
if ((CLU_MODEL_CLUx & CLU_MODEL_CLU2) != 0)
{
REG_WRITE_BIT(CLUx->EN, BIT2, CLU_InitStruct->EN ? BIT2 : 0);
REG_WRITE_BIT(CLUx->IE, BIT4, CLU_InitStruct->FIE ? BIT4 : 0);
REG_WRITE_BIT(CLUx->IE, BIT5, CLU_InitStruct->RIE ? BIT5 : 0);
REG_WRITE_BIT(CLUx->MX, 0x0f00, (CLU_InitStruct->MXA << 20) | (CLU_InitStruct->MXB << 16));
REG_WRITE_BIT(CLUx->FN, 0x0f00, CLU_InitStruct->FN << 16);
REG_WRITE_BIT(CLUx->CF, 0x0f00, (CLU_InitStruct->OUTSEL << 23) | (CLU_InitStruct->OEN << 22) | (CLU_InitStruct->CLKINV << 18) | (CLU_InitStruct->CLKSEL << 16));
REG_SET(CLUx->CF, BIT19);
}
if ((CLU_MODEL_CLUx & CLU_MODEL_CLU3) != 0)
{
REG_WRITE_BIT(CLUx->EN, BIT3, CLU_InitStruct->EN ? BIT3 : 0);
REG_WRITE_BIT(CLUx->IE, BIT6, CLU_InitStruct->FIE ? BIT6 : 0);
REG_WRITE_BIT(CLUx->IE, BIT7, CLU_InitStruct->RIE ? BIT7 : 0);
REG_WRITE_BIT(CLUx->MX, 0xf000, (CLU_InitStruct->MXA << 28) | (CLU_InitStruct->MXB << 24));
REG_WRITE_BIT(CLUx->FN, 0xf000, CLU_InitStruct->FN << 24);
REG_WRITE_BIT(CLUx->CF, 0xf000, (CLU_InitStruct->OUTSEL << 31) | (CLU_InitStruct->OEN << 30) | (CLU_InitStruct->CLKINV << 26) | (CLU_InitStruct->CLKSEL << 24));
REG_SET(CLUx->CF, BIT27);
}
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 配置GPIO引脚复用功能
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource 引脚源
* @param GPIO_AF 复用功能选择
*/
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
uint8_t offset;
uint8_t pins = GPIO_PinSource >> 2;
offset = ((GPIO_PinSource & 0x3) * 4);
switch (pins)
{
case 0:
GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 1:
GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 2:
GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 3:
GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
break;
default:
break;
}
}
03.01_CMP-基本功能
测试步骤
比较器模拟部分 使用CMP0_IN0 P0.15和CMP0_IP0 P2.9作为输入,P0.14输出比较器结果 其中N端信号固定为1V P端信号从0.9到1.1线性变化 分别记录0.9->1.1V和1.1V->0.9V时,比较器输出变化波形
实测数据
CMP_HYS_20mV
CMP_HYS_0mV
参考代码
03.01_CMP_Compare.c
/**
* @brief CMP-基本功能
* @details
* 比较器模拟部分
* 使用CMP0_IN0 P0.15和CMP0_IP0 P2.9作为输入,P0.14输出比较器结果
* 其中N端信号固定为1V
* P端信号从0.9到1.1线性变化
* 分别记录0.9->1.1V和1.1V->0.9V时,比较器输出变化波形
*/
#include "lks32mc09x_cmp.h"
#include "lks32mc09x_gpio.h"
#include "lks32mc09x_sys.h"
volatile uint32_t sethys = CMP_HYS_20mV;
/**
* @brief 主函数,用于测试HSI时钟输出及GPIO翻转
*/
int main(void)
{
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
CMP_Enable(CMP_CHN_0);
CMP_SetSignal(CMP_CHN_0, CMP_SELN_0_IN_CMP0, CMP_SELP_0_CMP0_IP0); // CMP0_IN0 P0.15 CMP0_IP0 P2.9
GPIO_Config(GPIO0, GPIO_PinSource_14, GPIO_Mode_OUT, GPIO_AF_CMP_OUT);
while (1)
{
CMP_SetHysteresis(sethys);
}
}
使用到的库函数
库函数部分代码
#define CMP_CHN_0 0
#define CMP_CHN_1 1
#define GPIO_AF_I2C 6
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_CMP BIT2
#define SYS_MODULE_GPIO BIT11
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* @brief GPIO模式选择枚举
*/
typedef enum GPIO_Mode_TypeDef {
GPIO_Mode_IN = 0,
GPIO_Mode_OUT = 1,
GPIO_Mode_ANA = 2,
GPIO_Mode_IO = 3,
}} {enum_name};
/**
* @brief GPIO配置函数
* @param GPIOx: GPIO端口
* @param GPIO_PinSource: GPIO引脚
* @param mode: GPIO模式
* @param GPIO_AF_x: GPIO复用功能选择
*/
void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
{
GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = 1 << GPIO_PinSource;
GPIO_InitStructure.GPIO_Mode = mode;
if (GPIO_AF_x == GPIO_AF_I2C)
{
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
}
else
{
__NOP();
}
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**
* @brief 使能比较器
* @param CMP_CHN_x: 比较器通道选择,CMP_CHN_0, CMP_CHN_1
*/
void CMP_Enable(uint8_t CMP_CHN_x)
{
SYS_ModuleClockCmd(SYS_MODULE_CMP, ENABLE);
if (CMP_CHN_x == CMP_CHN_0)
{
SYS0->PROTECT = 0x7a83;
SYS0->AFE_REG5 |= BIT6;
SYS0->PROTECT = 0;
}
else
{
__NOP();
}
if (CMP_CHN_x == CMP_CHN_1)
{
SYS0->PROTECT = 0x7a83;
SYS0->AFE_REG5 |= BIT7;
SYS0->PROTECT = 0;
}
else
{
__NOP();
}
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 设置比较器信号来源
* @param CMP_CHN_x: 比较器通道选择,CMP_CHN_0, CMP_CHN_1
* @param CMP_SELN_x_x: 信号负端选择,注意,由于比较器的各个通道不完全一样,所以这里需要指明使用的比较器
* @param CMP_SELP_x_x: 信号正端选择,注意,由于比较器的各个通道不完全一样,所以这里需要指明使用的比较器
*/
void CMP_SetSignal(uint8_t CMP_CHN_x, uint8_t CMP_SELN_x_x, uint8_t CMP_SELP_x_x)
{
if (CMP_CHN_x == CMP_CHN_0)
{
SYS0->PROTECT = 0x7a83;
SYS0->AFE_REG3 = (SYS0->AFE_REG3 & (~(0x0707))) |
(CMP_SELP_x_x << 8) |
(CMP_SELN_x_x);
SYS0->PROTECT = 0;
}
else
{
__NOP();
}
if (CMP_CHN_x == CMP_CHN_1)
{
SYS0->PROTECT = 0x7a83;
SYS0->AFE_REG3 = (SYS0->AFE_REG3 & (~(0x7070))) |
(CMP_SELP_x_x << 12) |
(CMP_SELN_x_x << 4);
SYS0->PROTECT = 0;
}
else
{
__NOP();
}
}
/**
* @brief 设置比较器回差(两个比较器共用同一个回差)
* @param CMP_HYS_x: 回差选择,CMP_HYS_20mV, CMP_HYS_0mV
*/
void CMP_SetHysteresis(uint8_t CMP_HYS_x)
{
SYS0->PROTECT = 0x7a83;
SYS0->AFE_REG3 = (SYS0->AFE_REG3 & (~(BIT7))) |
(CMP_HYS_x << 7);
SYS0->PROTECT = 0;
}
/**
* @brief 配置GPIO引脚复用功能
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource 引脚源
* @param GPIO_AF 复用功能选择
*/
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
uint8_t offset;
uint8_t pins = GPIO_PinSource >> 2;
offset = ((GPIO_PinSource & 0x3) * 4);
switch (pins)
{
case 0:
GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 1:
GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 2:
GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 3:
GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
break;
default:
break;
}
}
04.01_CRC-所有内置的CRC模型
测试步骤
先后计算01 02 03 04 05不同模型下的结果,然后和已知的CRC结果进行对比。
实测数据
| 参数模型 | 计算结果,数据:{1, 2, 3, 4, 5} | 测试结果 |
|---|---|---|
| CRC8 | 0xBC | 正确 |
| CRC8_ITU | 0xE9 | 正确 |
| CRC8_MAXIM | 0x2A | 正确 |
| CRC8_ROHC | 0xFE | 正确 |
| CRC16_IBM | 0xBB0E | 正确 |
| CRC16_MAXIM | 0x44F1 | 正确 |
| CRC16_USB | 0x44D5 | 正确 |
| CRC16_MODBUS | 0xBB2A | 正确 |
| CRC16_CCITT | 0xED9B | 正确 |
| CRC16_CCITT_FALSE | 0x9304 | 正确 |
| CRC16_X25 | 0x22EC | 正确 |
| CRC16_XMODEM | 0x8208 | 正确 |
| CRC16_DNP | 0xE674 | 正确 |
| CRC32 | 0x470B99F4 | 正确 |
| CRC32_MPEG_2 | 0xE28F4B83 | 正确 |
参考代码
04.01_CRC_Calc.c
/**
* @brief CRC-所有内置的CRC模型
* @details
* 先后计算01 02 03 04 05不同模型下的结果,然后和已知的CRC结果进行对比。
*/
#include "lks32mc09x_lib.h"
u8 crc_data[5] = {1, 2, 3, 4, 5};
volatile uint32_t crc8_answer, crc8_ITU_answer, crc8_ROHC_answer, crc8_MAXIM_answer;
volatile uint32_t crc16_IBM_answer, crc16_MAXIM_answer, crc16_USB_answer, crc16_MODBUS_answer, crc16_CCITT_answer, crc16_CCITT_FALSE_answer, crc16_X25_answer, crc16_XMODEM_answer, crc16_DNP_answer;
volatile uint32_t crc32_answer, crc32_MPEG_2_answer;
volatile uint32_t crc8_errflg = 0;
volatile uint32_t crc8_ITU_errflg = 0;
volatile uint32_t crc8_ROHC_errflg = 0;
volatile uint32_t crc8_MAXIM_errflg = 0;
volatile uint32_t crc16_IBM_errflg = 0;
volatile uint32_t crc16_MAXIM_errflg = 0;
volatile uint32_t crc16_USB_errflg = 0;
volatile uint32_t crc16_MODBUS_errflg = 0;
volatile uint32_t crc16_CCITT_errflg = 0;
volatile uint32_t crc16_CCITT_FALSE_errflg = 0;
volatile uint32_t crc16_X25_errflg = 0;
volatile uint32_t crc16_XMODEM_errflg = 0;
volatile uint32_t crc16_DNP_errflg = 0;
volatile uint32_t crc32_errflg = 0;
volatile uint32_t crc32_MPEG_2_errflg = 0;
int main(void)
{
crc8_answer = CRC_CalcCommon(CRC0, crc_data, sizeof(crc_data), CRC_MODEL_CRC_8); ///< 计算CRC_8
crc8_ITU_answer = CRC_CalcCommon(CRC0, crc_data, sizeof(crc_data), CRC_MODEL_CRC_8_ITU); ///< 计算CRC_8_ITU
crc8_ROHC_answer = CRC_CalcCommon(CRC0, crc_data, sizeof(crc_data), CRC_MODEL_CRC_8_ROHC); ///< 计算CRC_8_ROHC
crc8_MAXIM_answer = CRC_CalcCommon(CRC0, crc_data, sizeof(crc_data), CRC_MODEL_CRC_8_MAXIMx); ///< 计算CRC_8_MAXIMx
crc16_IBM_answer = CRC_CalcCommon(CRC0, crc_data, sizeof(crc_data), CRC_MODEL_CRC_16_IBMx1); ///< 计算CRC_16_IBMx1
crc16_MAXIM_answer = CRC_CalcCommon(CRC0, crc_data, sizeof(crc_data), CRC_MODEL_CRC_16_MAXIM); ///< 计算CRC_16_MAXIM
crc16_USB_answer = CRC_CalcCommon(CRC0, crc_data, sizeof(crc_data), CRC_MODEL_CRC_16_USBx1); ///< 计算CRC_16_USBx1
crc16_MODBUS_answer = CRC_CalcCommon(CRC0, crc_data, sizeof(crc_data), CRC_MODEL_CRC_16_MODBU); ///< 计算CRC_16_MODBU
crc16_CCITT_answer = CRC_CalcCommon(CRC0, crc_data, sizeof(crc_data), CRC_MODEL_CRC_16_CCITT); ///< 计算CRC_16_CCITT
crc16_CCITT_FALSE_answer = CRC_CalcCommon(CRC0, crc_data, sizeof(crc_data), CRC_MODEL_CRC_16_CCITT_FALSE); ///< 计算CRC_16_CCITT_FALSE
crc16_X25_answer = CRC_CalcCommon(CRC0, crc_data, sizeof(crc_data), CRC_MODEL_CRC_16_X25); ///< 计算CRC_16_X25
crc16_XMODEM_answer = CRC_CalcCommon(CRC0, crc_data, sizeof(crc_data), CRC_MODEL_CRC_16_XMODEM); ///< 计算CRC_16_XMODEM
crc16_DNP_answer = CRC_CalcCommon(CRC0, crc_data, sizeof(crc_data), CRC_MODEL_CRC_16_DNP); ///< 计算CRC_16_DNP
crc32_answer = CRC_CalcCommon(CRC0, crc_data, sizeof(crc_data), CRC_MODEL_CRC_32); ///< 计算CRC_32
crc32_MPEG_2_answer = CRC_CalcCommon(CRC0, crc_data, sizeof(crc_data), CRC_MODEL_CRC_32_MPEG_2); ///< 计算CRC_32_MPEG_2
if (crc8_answer != 0xbc)
{
crc8_errflg = 1;
}
if (crc8_ITU_answer != 0xe9)
{
crc8_ITU_errflg = 1;
}
if (crc8_ROHC_answer != 0xfe)
{
crc8_ROHC_errflg = 1;
}
if (crc8_MAXIM_answer != 0x2a)
{
crc8_MAXIM_errflg = 1;
}
if (crc16_IBM_answer != 0xbb0e)
{
crc16_IBM_errflg = 1;
}
if (crc16_MAXIM_answer != 0x44f1)
{
crc16_MAXIM_errflg = 1;
}
if (crc16_USB_answer != 0x44d5)
{
crc16_USB_errflg = 1;
}
if (crc16_MODBUS_answer != 0xbb2a)
{
crc16_MODBUS_errflg = 1;
}
if (crc16_CCITT_answer != 0xed9b)
{
crc16_CCITT_errflg = 1;
}
if (crc16_CCITT_FALSE_answer != 0x9304)
{
crc16_CCITT_FALSE_errflg = 1;
}
if (crc16_X25_answer != 0x22ec)
{
crc16_X25_errflg = 1;
}
if (crc16_XMODEM_answer != 0x8208)
{
crc16_XMODEM_errflg = 1;
}
if (crc16_DNP_answer != 0xe674)
{
crc16_DNP_errflg = 1;
}
if (crc32_answer != 0x470b99f4)
{
crc32_errflg = 1;
}
if (crc32_MPEG_2_answer != 0xe28f4b83)
{
crc32_MPEG_2_errflg = 1;
}
for (;;)
{
}
}
使用到的库函数
库函数部分代码
#define CRC0 ((CRC_TypeDef *)CRC0_BASE)
#define CRC_MODEL_CRC_16_CCITT 8
#define CRC_MODEL_CRC_16_CCITT_FALSE 9
#define CRC_MODEL_CRC_16_DNP 12
#define CRC_MODEL_CRC_16_MAXIM 5
#define CRC_MODEL_CRC_16_MODBU 7
#define CRC_MODEL_CRC_16_X25 10
#define CRC_MODEL_CRC_16_XMODEM 11
#define CRC_MODEL_CRC_32 13
#define CRC_MODEL_CRC_32_MPEG_2 14
#define CRC_MODEL_CRC_8 0
#define CRC_MODEL_CRC_8_ITU 1
#define CRC_MODEL_CRC_8_ROHC 2
#define CRC_WIDTH_16 1
#define CRC_WIDTH_32 0
#define CRC_WIDTH_7 3
#define CRC_WIDTH_8 2
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_CRC0 BIT16
/**
* @brief CRC初始化结构体
*/
typedef struct CRC_InitTypeDef {
uint32_t POLY;
uint32_t INIT;
uint32_t XOROUT;
uint8_t REFIN;
uint8_t REFOUT;
uint8_t WIDTH;
uint8_t null;
};
/**
* @brief CRC计算初始化
*/
void CRC_Init(CRC_TypeDef *CRCx, CRC_InitTypeDef *CRC_InitStruct)
{
if (CRC_InitStruct->WIDTH > 3)
{
switch (CRC_InitStruct->WIDTH)
{
case 7:
CRC_InitStruct->WIDTH = CRC_WIDTH_7;
break;
case 8:
CRC_InitStruct->WIDTH = CRC_WIDTH_8;
break;
case 16:
CRC_InitStruct->WIDTH = CRC_WIDTH_16;
break;
case 32:
CRC_InitStruct->WIDTH = CRC_WIDTH_32;
break;
default:
CRC_InitStruct->WIDTH = CRC_WIDTH_8;
break;
}
}
else
{
__NOP();
}
CRC_Enable(CRCx);
SYS_SoftResetModule(SYS_MODULE_CRC0);
CRC0_CR = (u32)(CRC_InitStruct->REFOUT << 12) | (u32)(CRC_InitStruct->REFIN << 8) | (u32)(CRC_InitStruct->WIDTH << 4);
CRC0_CR |= BIT0; // 复位
CRC0_POL = CRC_InitStruct->POLY;
CRC0_INIT = CRC_InitStruct->INIT;
if (CRC_InitStruct->WIDTH == CRC_WIDTH_16)
{
CRC_XOROUT = CRC_InitStruct->XOROUT << 16;
}
else
{
CRC_XOROUT = CRC_InitStruct->XOROUT;
}
}
/**
* @brief 使用常见模型计算CRC值
* @param CRCx CRC模块指针
* @param data 数据指针
* @param len 数据长度
* @param CRC_MODEL_x CRC模型
* @return 计算得到的CRC值
*/
uint32_t CRC_CalcCommon(CRC_TypeDef *CRCx, uint8_t *data, uint32_t len, uint16_t CRC_MODEL_x)
{
u32 val;
CRC_InitTypeDef CRC_InitStruct;
switch (CRC_MODEL_x)
{
case CRC_MODEL_CRC_8: // 8 07 00 00 false false
CRC_InitStruct.WIDTH = CRC_WIDTH_8; // 宽度
CRC_InitStruct.POLY = 0x07; // 多项式
CRC_InitStruct.INIT = 0x00; // 初始值
CRC_InitStruct.XOROUT = 0x00; // 结果异或值
CRC_InitStruct.REFIN = DISABLE; // 输入数据反转
CRC_InitStruct.REFOUT = DISABLE; // 输出数据反转
CRC_Init(CRCx, &CRC_InitStruct);
break;
case CRC_MODEL_CRC_8_ITU: // 8 07 00 55 false false
CRC_InitStruct.WIDTH = CRC_WIDTH_8; // 宽度
CRC_InitStruct.POLY = 0x07; // 多项式
CRC_InitStruct.INIT = 0x00; // 初始值
CRC_InitStruct.XOROUT = 0x55; // 结果异或值
CRC_InitStruct.REFIN = DISABLE; // 输入数据反转
CRC_InitStruct.REFOUT = DISABLE; // 输出数据反转
CRC_Init(CRCx, &CRC_InitStruct);
break;
case CRC_MODEL_CRC_8_ROHC: // 8 07 FF 00 true true
CRC_InitStruct.WIDTH = CRC_WIDTH_8; // 宽度
CRC_InitStruct.POLY = 0x07; // 多项式
CRC_InitStruct.INIT = 0xff; // 初始值
CRC_InitStruct.XOROUT = 0x00; // 结果异或值
CRC_InitStruct.REFIN = ENABLE; // 输入数据反转
CRC_InitStruct.REFOUT = ENABLE; // 输出数据反转
CRC_Init(CRCx, &CRC_InitStruct);
break;
case CRC_MODEL_CRC_8_MAXIMx: // 8 31 00 00 true true
CRC_InitStruct.WIDTH = CRC_WIDTH_8; // 宽度
CRC_InitStruct.POLY = 0x31; // 多项式
CRC_InitStruct.INIT = 0x00; // 初始值
CRC_InitStruct.XOROUT = 0x00; // 结果异或值
CRC_InitStruct.REFIN = ENABLE; // 输入数据反转
CRC_InitStruct.REFOUT = ENABLE; // 输出数据反转
CRC_Init(CRCx, &CRC_InitStruct);
break;
case CRC_MODEL_CRC_16_IBMx1: // 16 8005 0000 0000 true true
CRC_InitStruct.WIDTH = CRC_WIDTH_16; // 宽度
CRC_InitStruct.POLY = 0x8005; // 多项式
CRC_InitStruct.INIT = 0x0000; // 初始值
CRC_InitStruct.XOROUT = 0x0000; // 结果异或值
CRC_InitStruct.REFIN = ENABLE; // 输入数据反转
CRC_InitStruct.REFOUT = ENABLE; // 输出数据反转
CRC_Init(CRCx, &CRC_InitStruct);
break;
case CRC_MODEL_CRC_16_MAXIM: // 16 8005 0000 FFFF true true
CRC_InitStruct.WIDTH = CRC_WIDTH_16; // 宽度
CRC_InitStruct.POLY = 0x8005; // 多项式
CRC_InitStruct.INIT = 0x0000; // 初始值
CRC_InitStruct.XOROUT = 0xffff; // 结果异或值
CRC_InitStruct.REFIN = ENABLE; // 输入数据反转
CRC_InitStruct.REFOUT = ENABLE; // 输出数据反转
CRC_Init(CRCx, &CRC_InitStruct);
break;
case CRC_MODEL_CRC_16_USBx1: // 16 8005 FFFF FFFF true true
CRC_InitStruct.WIDTH = CRC_WIDTH_16; // 宽度
CRC_InitStruct.POLY = 0x8005; // 多项式
CRC_InitStruct.INIT = 0xffff; // 初始值
CRC_InitStruct.XOROUT = 0xffff; // 结果异或值
CRC_InitStruct.REFIN = ENABLE; // 输入数据反转
CRC_InitStruct.REFOUT = ENABLE; // 输出数据反转
CRC_Init(CRCx, &CRC_InitStruct);
break;
case CRC_MODEL_CRC_16_MODBU: // 16 8005 FFFF 0000 true true
CRC_InitStruct.WIDTH = CRC_WIDTH_16; // 宽度
CRC_InitStruct.POLY = 0x8005; // 多项式
CRC_InitStruct.INIT = 0xffff; // 初始值
CRC_InitStruct.XOROUT = 0x0000; // 结果异或值
CRC_InitStruct.REFIN = ENABLE; // 输入数据反转
CRC_InitStruct.REFOUT = ENABLE; // 输出数据反转
CRC_Init(CRCx, &CRC_InitStruct);
break;
case CRC_MODEL_CRC_16_CCITT: // 16 1021 0000 0000 true true
CRC_InitStruct.WIDTH = CRC_WIDTH_16; // 宽度
CRC_InitStruct.POLY = 0x1021; // 多项式
CRC_InitStruct.INIT = 0x0000; // 初始值
CRC_InitStruct.XOROUT = 0x0000; // 结果异或值
CRC_InitStruct.REFIN = ENABLE; // 输入数据反转
CRC_InitStruct.REFOUT = ENABLE; // 输出数据反转
CRC_Init(CRCx, &CRC_InitStruct);
break;
case CRC_MODEL_CRC_16_CCITT_FALSE: // 16 1021 FFFF 0000 false false
CRC_InitStruct.WIDTH = CRC_WIDTH_16; // 宽度
CRC_InitStruct.POLY = 0x1021; // 多项式
CRC_InitStruct.INIT = 0xffff; // 初始值
CRC_InitStruct.XOROUT = 0x0000; // 结果异或值
CRC_InitStruct.REFIN = DISABLE; // 输入数据反转
CRC_InitStruct.REFOUT = DISABLE; // 输出数据反转
CRC_Init(CRCx, &CRC_InitStruct);
break;
case CRC_MODEL_CRC_16_X25: // 16 1021 FFFF FFFF true true
CRC_InitStruct.WIDTH = CRC_WIDTH_16; // 宽度
CRC_InitStruct.POLY = 0x1021; // 多项式
CRC_InitStruct.INIT = 0xffff; // 初始值
CRC_InitStruct.XOROUT = 0xffff; // 结果异或值
CRC_InitStruct.REFIN = ENABLE; // 输入数据反转
CRC_InitStruct.REFOUT = ENABLE; // 输出数据反转
CRC_Init(CRCx, &CRC_InitStruct);
break;
case CRC_MODEL_CRC_16_XMODEM: // 16 1021 0000 0000 false false
CRC_InitStruct.WIDTH = CRC_WIDTH_16; // 宽度
CRC_InitStruct.POLY = 0x1021; // 多项式
CRC_InitStruct.INIT = 0x0000; // 初始值
CRC_InitStruct.XOROUT = 0x0000; // 结果异或值
CRC_InitStruct.REFIN = DISABLE; // 输入数据反转
CRC_InitStruct.REFOUT = DISABLE; // 输出数据反转
CRC_Init(CRCx, &CRC_InitStruct);
break;
case CRC_MODEL_CRC_16_DNP: // 16 3D65 0000 FFFF true true
CRC_InitStruct.WIDTH = CRC_WIDTH_16; // 宽度
CRC_InitStruct.POLY = 0x3d65; // 多项式
CRC_InitStruct.INIT = 0x0000; // 初始值
CRC_InitStruct.XOROUT = 0xffff; // 结果异或值
CRC_InitStruct.REFIN = ENABLE; // 输入数据反转
CRC_InitStruct.REFOUT = ENABLE; // 输出数据反转
CRC_Init(CRCx, &CRC_InitStruct);
break;
case CRC_MODEL_CRC_32: // 32 04C11DB7 FFFFFFFF FFFFFFFF true true
CRC_InitStruct.WIDTH = CRC_WIDTH_32; // 宽度
CRC_InitStruct.POLY = 0x04C11DB7; // 多项式
CRC_InitStruct.INIT = 0xFFFFFFFF; // 初始值
CRC_InitStruct.XOROUT = 0xFFFFFFFF; // 结果异或值
CRC_InitStruct.REFIN = ENABLE; // 输入数据反转
CRC_InitStruct.REFOUT = ENABLE; // 输出数据反转
CRC_Init(CRCx, &CRC_InitStruct);
break;
case CRC_MODEL_CRC_32_MPEG_2: // 32 04C11DB7 FFFFFFFF 00000000 false false
CRC_InitStruct.WIDTH = CRC_WIDTH_32; // 宽度
CRC_InitStruct.POLY = 0x04C11DB7; // 多项式
CRC_InitStruct.INIT = 0xFFFFFFFF; // 初始值
CRC_InitStruct.XOROUT = 0x00000000; // 结果异或值
CRC_InitStruct.REFIN = DISABLE; // 输入数据反转
CRC_InitStruct.REFOUT = DISABLE; // 输出数据反转
CRC_Init(CRCx, &CRC_InitStruct);
break;
}
val = CRC_Calc(CRCx, data, len);
if (CRC_InitStruct.REFOUT)
{
switch (CRC_InitStruct.WIDTH)
{
case CRC_WIDTH_8:
val >>= 24;
break;
case CRC_WIDTH_16:
val >>= 16;
break;
default:
break;
}
}
else
{
__NOP();
}
return val;
}
/**
* @brief 使能CRC模块
* @param CRCx CRC模块指针
*/
void CRC_Enable(CRC_TypeDef *CRCx)
{
if (CRCx == CRC0)
{
SYS_ModuleClockCmd(SYS_MODULE_CRC0, ENABLE);
}
else
{
__NOP();
}
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 计算CRC值
* @param CRCx CRC模块指针
* @param data 数据指针
* @param len 数据长度
* @return 计算得到的CRC值
*/
uint32_t CRC_Calc(CRC_TypeDef *CRCx, uint8_t *data, uint32_t len)
{
u32 val;
if (len == 0)
{
return 0;
}
else
{
while ((len--) != 0)
{
REG8(&CRCx->DR) = *data;
data++;
}
}
val = (REG32(&CRCx->DR)) ^ CRC_XOROUT;
return val;
}
/**
* @brief 软复位模块
* @param nModule 模块编号
*/
void SYS_SoftResetModule(uint32_t nModule)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
REG_SET(SYS0->SFT_RST, nModule);
REG_RESET(SYS0->SFT_RST, nModule); // 触发复位后需清除复位位
REG_WRITE(SYS0->PROTECT, 0);
}
05.01_DAC-1.2V量程(DAC0)
测试步骤
配置DAC0使用1.2V量程输出电压到P0.0。
实测数据
参考代码
05.01_DAC.c
/**
* @brief DAC-1.2V量程(DAC0)
* @details 配置DAC0使用1.2V量程输出电压到P0.0。
*/
#include "lks32mc09x_gpio.h"
#include "lks32mc09x_dac.h"
volatile uint32_t setdacval = 0;
/**
* @brief 主函数
*/
int main(void)
{
// 配置P0.0为DAC0输出
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_ANA; // 模拟输入模式
GPIO_Init(GPIO0, &GPIO_InitStruct);
// 使能DAC0输出到IO口
DAC_Enable(DAC_CHANNEL_0);
DAC_SetRange(DAC_CHANNEL_0, DAC_RANGE_0_1V2);
DAC_Output(DAC_CHANNEL_0, 1);
while (1)
{
DAC_OutputValue(DAC_CHANNEL_0, setdacval); // 设置DAC0输出电压
}
}
使用到的库函数
库函数部分代码
#define DAC_CHANNEL_0 0
#define DAC_CHANNEL_1 1
#define DAC_RANGE_0_1V2 1
#define DAC_RANGE_1_1V2 1
#define NVR_ADDR_DAC_0_AMC_1_20V 0x00001458
#define NVR_ADDR_DAC_0_AMC_4_8V 0x00001450
#define NVR_ADDR_DAC_0_DC_1_20V 0x0000145C
#define NVR_ADDR_DAC_0_DC_4_8V 0x00001454
#define NVR_ADDR_DAC_1_AMC_1_20V 0x00001468
#define NVR_ADDR_DAC_1_AMC_3_0V 0x00001460
#define NVR_ADDR_DAC_1_DC_1_20V 0x0000146C
#define NVR_ADDR_DAC_1_DC_3_0V 0x00001464
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_GPIO BIT11
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* @brief DAC使能
* @param DAC_Channel DAC通道选择
*/
void DAC_Enable(uint8_t DAC_Channel)
{
DAC_Cmd(DAC_Channel, ENABLE);
}
/**
* @brief DAC输出数值设置(数字量)
* @param DAC_Channel DAC通道选择
* @param DACValue DAC输出数字量
*/
void DAC_OutputValue(uint8_t DAC_Channel, uint32_t DACValue)
{
if (DACValue > 4095)
{
DACValue = 4095;
}
else
{
__NOP();
}
if (DAC_Channel == DAC_CHANNEL_0)
{
SYS0->AFE_DAC0 = DACValue;
}
else if (DAC_Channel == DAC_CHANNEL_1)
{
SYS0->AFE_DAC1 = DACValue >> 4; // DAC1本身是8位,DACValue是12位,这里右移4位,以方便使用
}
else
{
__NOP();
}
}
/**
* @brief DAC输出至IO口使能配置
* @param DAC_Channel DAC通道选择
* @param state 1Dac电压p0.0口 0停止输出到P0.0
*/
void DAC_Output(uint8_t DAC_Channel, uint8_t state)
{
SYS0->PROTECT = 0x7a83;
if (DAC_Channel == DAC_CHANNEL_0)
{
if (state)
{
SYS0->AFE_REG2 |= BIT6;
}
else
{
SYS0->AFE_REG2 &= ~BIT6;
}
}
else if (DAC_Channel == DAC_CHANNEL_1)
{
if (state)
{
SYS0->AFE_REG2 |= BIT7;
}
else
{
SYS0->AFE_REG2 &= ~BIT7;
}
}
else
{
__NOP();
}
SYS0->PROTECT = 0;
}
/**
* @brief DAC通道时钟使能
* @param DAC_Channel DAC通道选择
* @param state 使能状态,1模块使能 0 模块关闭
*/
void DAC_Cmd(uint8_t DAC_Channel, uint8_t state)
{
SYS0->PROTECT = 0x7a83;
if (DAC_Channel == DAC_CHANNEL_0)
{
if (state)
{
SYS0->AFE_REG5 |= BIT1;
}
else
{
SYS0->AFE_REG5 &= ~BIT1;
}
}
else
{
__NOP();
}
if (DAC_Channel == DAC_CHANNEL_1)
{
if (state)
{
SYS0->AFE_REG5 |= BIT2;
}
else
{
SYS0->AFE_REG5 &= ~BIT2;
}
}
else
{
__NOP();
}
SYS0->PROTECT = 0;
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 设置DAC量程
* @param DAC_Channel DAC通道选择
* @param DAC_RANGE_x DAC输出量程
*/
void DAC_SetRange(uint8_t DAC_Channel, uint32_t DAC_RANGE_x)
{
SYS0->PROTECT = 0x7a83;
if (DAC_Channel == DAC_CHANNEL_0)
{
if (DAC_RANGE_x == DAC_RANGE_0_1V2)
{
SYS0->AFE_REG1 |= BIT6;
SYS0->AFE_DAC0_AMC = TRIM_Read(NVR_ADDR_DAC_0_AMC_1_20V);
SYS0->AFE_DAC0_DC = TRIM_Read(NVR_ADDR_DAC_0_DC_1_20V);
}
else
{
SYS0->AFE_REG1 &= ~BIT6;
SYS0->AFE_DAC0_AMC = TRIM_Read(NVR_ADDR_DAC_0_AMC_4_8V);
SYS0->AFE_DAC0_DC = TRIM_Read(NVR_ADDR_DAC_0_DC_4_8V);
}
}
else if (DAC_Channel == DAC_CHANNEL_1)
{
if (DAC_RANGE_x == DAC_RANGE_1_1V2)
{
SYS0->AFE_REG1 |= BIT7;
SYS0->AFE_DAC1_AMC = TRIM_Read(NVR_ADDR_DAC_1_AMC_1_20V);
SYS0->AFE_DAC1_DC = TRIM_Read(NVR_ADDR_DAC_1_DC_1_20V);
}
else
{
SYS0->AFE_REG1 &= ~BIT7;
SYS0->AFE_DAC1_AMC = TRIM_Read(NVR_ADDR_DAC_1_AMC_3_0V);
SYS0->AFE_DAC1_DC = TRIM_Read(NVR_ADDR_DAC_1_DC_3_0V);
}
}
else
{
__NOP();
}
SYS0->PROTECT = 0;
}
05.02_DAC-4.76V量程(DAC0)
测试步骤
配置DAC0使用1.2V量程输出电压到P0.0。
实测数据
参考代码
05.02_DAC.c
/**
* @brief DAC-4.76V量程(DAC0)
* @details 配置DAC0使用1.2V量程输出电压到P0.0。
*/
#include "lks32mc09x_gpio.h"
#include "lks32mc09x_dac.h"
volatile uint32_t setdacval = 0;
/**
* @brief 主函数
*/
int main(void)
{
// 配置P0.0为DAC0输出
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_ANA; // 模拟输入模式
GPIO_Init(GPIO0, &GPIO_InitStruct);
// 使能DAC0输出到IO口
DAC_Enable(DAC_CHANNEL_0);
DAC_SetRange(DAC_CHANNEL_0, DAC_RANGE_0_4V76);
DAC_Output(DAC_CHANNEL_0, 1);
while (1)
{
DAC_OutputValue(DAC_CHANNEL_0, setdacval); // 设置DAC0输出电压
}
}
使用到的库函数
库函数部分代码
#define DAC_CHANNEL_0 0
#define DAC_CHANNEL_1 1
#define DAC_RANGE_0_1V2 1
#define DAC_RANGE_1_1V2 1
#define NVR_ADDR_DAC_0_AMC_1_20V 0x00001458
#define NVR_ADDR_DAC_0_AMC_4_8V 0x00001450
#define NVR_ADDR_DAC_0_DC_1_20V 0x0000145C
#define NVR_ADDR_DAC_0_DC_4_8V 0x00001454
#define NVR_ADDR_DAC_1_AMC_1_20V 0x00001468
#define NVR_ADDR_DAC_1_AMC_3_0V 0x00001460
#define NVR_ADDR_DAC_1_DC_1_20V 0x0000146C
#define NVR_ADDR_DAC_1_DC_3_0V 0x00001464
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_GPIO BIT11
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* @brief DAC使能
* @param DAC_Channel DAC通道选择
*/
void DAC_Enable(uint8_t DAC_Channel)
{
DAC_Cmd(DAC_Channel, ENABLE);
}
/**
* @brief DAC输出数值设置(数字量)
* @param DAC_Channel DAC通道选择
* @param DACValue DAC输出数字量
*/
void DAC_OutputValue(uint8_t DAC_Channel, uint32_t DACValue)
{
if (DACValue > 4095)
{
DACValue = 4095;
}
else
{
__NOP();
}
if (DAC_Channel == DAC_CHANNEL_0)
{
SYS0->AFE_DAC0 = DACValue;
}
else if (DAC_Channel == DAC_CHANNEL_1)
{
SYS0->AFE_DAC1 = DACValue >> 4; // DAC1本身是8位,DACValue是12位,这里右移4位,以方便使用
}
else
{
__NOP();
}
}
/**
* @brief DAC输出至IO口使能配置
* @param DAC_Channel DAC通道选择
* @param state 1Dac电压p0.0口 0停止输出到P0.0
*/
void DAC_Output(uint8_t DAC_Channel, uint8_t state)
{
SYS0->PROTECT = 0x7a83;
if (DAC_Channel == DAC_CHANNEL_0)
{
if (state)
{
SYS0->AFE_REG2 |= BIT6;
}
else
{
SYS0->AFE_REG2 &= ~BIT6;
}
}
else if (DAC_Channel == DAC_CHANNEL_1)
{
if (state)
{
SYS0->AFE_REG2 |= BIT7;
}
else
{
SYS0->AFE_REG2 &= ~BIT7;
}
}
else
{
__NOP();
}
SYS0->PROTECT = 0;
}
/**
* @brief DAC通道时钟使能
* @param DAC_Channel DAC通道选择
* @param state 使能状态,1模块使能 0 模块关闭
*/
void DAC_Cmd(uint8_t DAC_Channel, uint8_t state)
{
SYS0->PROTECT = 0x7a83;
if (DAC_Channel == DAC_CHANNEL_0)
{
if (state)
{
SYS0->AFE_REG5 |= BIT1;
}
else
{
SYS0->AFE_REG5 &= ~BIT1;
}
}
else
{
__NOP();
}
if (DAC_Channel == DAC_CHANNEL_1)
{
if (state)
{
SYS0->AFE_REG5 |= BIT2;
}
else
{
SYS0->AFE_REG5 &= ~BIT2;
}
}
else
{
__NOP();
}
SYS0->PROTECT = 0;
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 设置DAC量程
* @param DAC_Channel DAC通道选择
* @param DAC_RANGE_x DAC输出量程
*/
void DAC_SetRange(uint8_t DAC_Channel, uint32_t DAC_RANGE_x)
{
SYS0->PROTECT = 0x7a83;
if (DAC_Channel == DAC_CHANNEL_0)
{
if (DAC_RANGE_x == DAC_RANGE_0_1V2)
{
SYS0->AFE_REG1 |= BIT6;
SYS0->AFE_DAC0_AMC = TRIM_Read(NVR_ADDR_DAC_0_AMC_1_20V);
SYS0->AFE_DAC0_DC = TRIM_Read(NVR_ADDR_DAC_0_DC_1_20V);
}
else
{
SYS0->AFE_REG1 &= ~BIT6;
SYS0->AFE_DAC0_AMC = TRIM_Read(NVR_ADDR_DAC_0_AMC_4_8V);
SYS0->AFE_DAC0_DC = TRIM_Read(NVR_ADDR_DAC_0_DC_4_8V);
}
}
else if (DAC_Channel == DAC_CHANNEL_1)
{
if (DAC_RANGE_x == DAC_RANGE_1_1V2)
{
SYS0->AFE_REG1 |= BIT7;
SYS0->AFE_DAC1_AMC = TRIM_Read(NVR_ADDR_DAC_1_AMC_1_20V);
SYS0->AFE_DAC1_DC = TRIM_Read(NVR_ADDR_DAC_1_DC_1_20V);
}
else
{
SYS0->AFE_REG1 &= ~BIT7;
SYS0->AFE_DAC1_AMC = TRIM_Read(NVR_ADDR_DAC_1_AMC_3_0V);
SYS0->AFE_DAC1_DC = TRIM_Read(NVR_ADDR_DAC_1_DC_3_0V);
}
}
else
{
__NOP();
}
SYS0->PROTECT = 0;
}
06.01_DSP-除法
测试步骤
使用硬件dsp进行除法计算
实测数据
实测数据
| a | b | DSP_Div(a, b) | DSP_Mod(a, b) | div err | mod err |
|---|---|---|---|---|---|
| -2147483648 (0x80000000) | -2147483648 (0x80000000) | 1 | 0 | 0 | 0 |
| -2147483648 (0x80000000) | -100000000 (0xfa0a1f00) | 21 | -47483648 | 0 | 0 |
| -2147483648 (0x80000000) | -32768 (0xffff8000) | 65536 | 0 | 0 | 0 |
| -2147483648 (0x80000000) | -10000 (0xffffd8f0) | 214748 | -3648 | 0 | 0 |
| -2147483648 (0x80000000) | -1 (0xffffffff) | 2147483647 | 0 | -1 | 0 |
| -2147483648 (0x80000000) | 0 (0x00000000) | 0 | -2147483648 | 0 | 0 |
| -2147483648 (0x80000000) | 1 (0x00000001) | -2147483648 | 0 | 0 | 0 |
| -2147483648 (0x80000000) | 10000 (0x00002710) | -214748 | -3648 | 0 | 0 |
| -2147483648 (0x80000000) | 32767 (0x00007fff) | -65538 | -2 | 0 | 0 |
| -2147483648 (0x80000000) | 100000000 (0x05f5e100) | -21 | -47483648 | 0 | 0 |
| -2147483648 (0x80000000) | 2147483647 (0x7fffffff) | -1 | -1 | 0 | 0 |
| -100000000 (0xfa0a1f00) | -2147483648 (0x80000000) | 0 | -100000000 | 0 | 0 |
| -100000000 (0xfa0a1f00) | -100000000 (0xfa0a1f00) | 1 | 0 | 0 | 0 |
| -100000000 (0xfa0a1f00) | -32768 (0xffff8000) | 3051 | -24832 | 0 | 0 |
| -100000000 (0xfa0a1f00) | -10000 (0xffffd8f0) | 10000 | 0 | 0 | 0 |
| -100000000 (0xfa0a1f00) | -1 (0xffffffff) | 100000000 | 0 | 0 | 0 |
| -100000000 (0xfa0a1f00) | 0 (0x00000000) | 0 | -100000000 | 0 | 0 |
| -100000000 (0xfa0a1f00) | 1 (0x00000001) | -100000000 | 0 | 0 | 0 |
| -100000000 (0xfa0a1f00) | 10000 (0x00002710) | -10000 | 0 | 0 | 0 |
| -100000000 (0xfa0a1f00) | 32767 (0x00007fff) | -3051 | -27883 | 0 | 0 |
| -100000000 (0xfa0a1f00) | 100000000 (0x05f5e100) | -1 | 0 | 0 | 0 |
| -100000000 (0xfa0a1f00) | 2147483647 (0x7fffffff) | 0 | -100000000 | 0 | 0 |
| -32768 (0xffff8000) | -2147483648 (0x80000000) | 0 | -32768 | 0 | 0 |
| -32768 (0xffff8000) | -100000000 (0xfa0a1f00) | 0 | -32768 | 0 | 0 |
| -32768 (0xffff8000) | -32768 (0xffff8000) | 1 | 0 | 0 | 0 |
| -32768 (0xffff8000) | -10000 (0xffffd8f0) | 3 | -2768 | 0 | 0 |
| -32768 (0xffff8000) | -1 (0xffffffff) | 32768 | 0 | 0 | 0 |
| -32768 (0xffff8000) | 0 (0x00000000) | 0 | -32768 | 0 | 0 |
| -32768 (0xffff8000) | 1 (0x00000001) | -32768 | 0 | 0 | 0 |
| -32768 (0xffff8000) | 10000 (0x00002710) | -3 | -2768 | 0 | 0 |
| -32768 (0xffff8000) | 32767 (0x00007fff) | -1 | -1 | 0 | 0 |
| -32768 (0xffff8000) | 100000000 (0x05f5e100) | 0 | -32768 | 0 | 0 |
| -32768 (0xffff8000) | 2147483647 (0x7fffffff) | 0 | -32768 | 0 | 0 |
| -10000 (0xffffd8f0) | -2147483648 (0x80000000) | 0 | -10000 | 0 | 0 |
| -10000 (0xffffd8f0) | -100000000 (0xfa0a1f00) | 0 | -10000 | 0 | 0 |
| -10000 (0xffffd8f0) | -32768 (0xffff8000) | 0 | -10000 | 0 | 0 |
| -10000 (0xffffd8f0) | -10000 (0xffffd8f0) | 1 | 0 | 0 | 0 |
| -10000 (0xffffd8f0) | -1 (0xffffffff) | 10000 | 0 | 0 | 0 |
| -10000 (0xffffd8f0) | 0 (0x00000000) | 0 | -10000 | 0 | 0 |
| -10000 (0xffffd8f0) | 1 (0x00000001) | -10000 | 0 | 0 | 0 |
| -10000 (0xffffd8f0) | 10000 (0x00002710) | -1 | 0 | 0 | 0 |
| -10000 (0xffffd8f0) | 32767 (0x00007fff) | 0 | -10000 | 0 | 0 |
| -10000 (0xffffd8f0) | 100000000 (0x05f5e100) | 0 | -10000 | 0 | 0 |
| -10000 (0xffffd8f0) | 2147483647 (0x7fffffff) | 0 | -10000 | 0 | 0 |
| -1 (0xffffffff) | -2147483648 (0x80000000) | 0 | -1 | 0 | 0 |
| -1 (0xffffffff) | -100000000 (0xfa0a1f00) | 0 | -1 | 0 | 0 |
| -1 (0xffffffff) | -32768 (0xffff8000) | 0 | -1 | 0 | 0 |
| -1 (0xffffffff) | -10000 (0xffffd8f0) | 0 | -1 | 0 | 0 |
| -1 (0xffffffff) | -1 (0xffffffff) | 1 | 0 | 0 | 0 |
| -1 (0xffffffff) | 0 (0x00000000) | 0 | -1 | 0 | 0 |
| -1 (0xffffffff) | 1 (0x00000001) | -1 | 0 | 0 | 0 |
| -1 (0xffffffff) | 10000 (0x00002710) | 0 | -1 | 0 | 0 |
| -1 (0xffffffff) | 32767 (0x00007fff) | 0 | -1 | 0 | 0 |
| -1 (0xffffffff) | 100000000 (0x05f5e100) | 0 | -1 | 0 | 0 |
| -1 (0xffffffff) | 2147483647 (0x7fffffff) | 0 | -1 | 0 | 0 |
| 0 (0x00000000) | -2147483648 (0x80000000) | 0 | 0 | 0 | 0 |
| 0 (0x00000000) | -100000000 (0xfa0a1f00) | 0 | 0 | 0 | 0 |
| 0 (0x00000000) | -32768 (0xffff8000) | 0 | 0 | 0 | 0 |
| 0 (0x00000000) | -10000 (0xffffd8f0) | 0 | 0 | 0 | 0 |
| 0 (0x00000000) | -1 (0xffffffff) | 0 | 0 | 0 | 0 |
| 0 (0x00000000) | 0 (0x00000000) | 0 | 0 | 0 | 0 |
| 0 (0x00000000) | 1 (0x00000001) | 0 | 0 | 0 | 0 |
| 0 (0x00000000) | 10000 (0x00002710) | 0 | 0 | 0 | 0 |
| 0 (0x00000000) | 32767 (0x00007fff) | 0 | 0 | 0 | 0 |
| 0 (0x00000000) | 100000000 (0x05f5e100) | 0 | 0 | 0 | 0 |
| 0 (0x00000000) | 2147483647 (0x7fffffff) | 0 | 0 | 0 | 0 |
| 1 (0x00000001) | -2147483648 (0x80000000) | 0 | 1 | 0 | 0 |
| 1 (0x00000001) | -100000000 (0xfa0a1f00) | 0 | 1 | 0 | 0 |
| 1 (0x00000001) | -32768 (0xffff8000) | 0 | 1 | 0 | 0 |
| 1 (0x00000001) | -10000 (0xffffd8f0) | 0 | 1 | 0 | 0 |
| 1 (0x00000001) | -1 (0xffffffff) | -1 | 0 | 0 | 0 |
| 1 (0x00000001) | 0 (0x00000000) | 0 | 1 | 0 | 0 |
| 1 (0x00000001) | 1 (0x00000001) | 1 | 0 | 0 | 0 |
| 1 (0x00000001) | 10000 (0x00002710) | 0 | 1 | 0 | 0 |
| 1 (0x00000001) | 32767 (0x00007fff) | 0 | 1 | 0 | 0 |
| 1 (0x00000001) | 100000000 (0x05f5e100) | 0 | 1 | 0 | 0 |
| 1 (0x00000001) | 2147483647 (0x7fffffff) | 0 | 1 | 0 | 0 |
| 10000 (0x00002710) | -2147483648 (0x80000000) | 0 | 10000 | 0 | 0 |
| 10000 (0x00002710) | -100000000 (0xfa0a1f00) | 0 | 10000 | 0 | 0 |
| 10000 (0x00002710) | -32768 (0xffff8000) | 0 | 10000 | 0 | 0 |
| 10000 (0x00002710) | -10000 (0xffffd8f0) | -1 | 0 | 0 | 0 |
| 10000 (0x00002710) | -1 (0xffffffff) | -10000 | 0 | 0 | 0 |
| 10000 (0x00002710) | 0 (0x00000000) | 0 | 10000 | 0 | 0 |
| 10000 (0x00002710) | 1 (0x00000001) | 10000 | 0 | 0 | 0 |
| 10000 (0x00002710) | 10000 (0x00002710) | 1 | 0 | 0 | 0 |
| 10000 (0x00002710) | 32767 (0x00007fff) | 0 | 10000 | 0 | 0 |
| 10000 (0x00002710) | 100000000 (0x05f5e100) | 0 | 10000 | 0 | 0 |
| 10000 (0x00002710) | 2147483647 (0x7fffffff) | 0 | 10000 | 0 | 0 |
| 32767 (0x00007fff) | -2147483648 (0x80000000) | 0 | 32767 | 0 | 0 |
| 32767 (0x00007fff) | -100000000 (0xfa0a1f00) | 0 | 32767 | 0 | 0 |
| 32767 (0x00007fff) | -32768 (0xffff8000) | 0 | 32767 | 0 | 0 |
| 32767 (0x00007fff) | -10000 (0xffffd8f0) | -3 | 2767 | 0 | 0 |
| 32767 (0x00007fff) | -1 (0xffffffff) | -32767 | 0 | 0 | 0 |
| 32767 (0x00007fff) | 0 (0x00000000) | 0 | 32767 | 0 | 0 |
| 32767 (0x00007fff) | 1 (0x00000001) | 32767 | 0 | 0 | 0 |
| 32767 (0x00007fff) | 10000 (0x00002710) | 3 | 2767 | 0 | 0 |
| 32767 (0x00007fff) | 32767 (0x00007fff) | 1 | 0 | 0 | 0 |
| 32767 (0x00007fff) | 100000000 (0x05f5e100) | 0 | 32767 | 0 | 0 |
| 32767 (0x00007fff) | 2147483647 (0x7fffffff) | 0 | 32767 | 0 | 0 |
| 100000000 (0x05f5e100) | -2147483648 (0x80000000) | 0 | 100000000 | 0 | 0 |
| 100000000 (0x05f5e100) | -100000000 (0xfa0a1f00) | -1 | 0 | 0 | 0 |
| 100000000 (0x05f5e100) | -32768 (0xffff8000) | -3051 | 24832 | 0 | 0 |
| 100000000 (0x05f5e100) | -10000 (0xffffd8f0) | -10000 | 0 | 0 | 0 |
| 100000000 (0x05f5e100) | -1 (0xffffffff) | -100000000 | 0 | 0 | 0 |
| 100000000 (0x05f5e100) | 0 (0x00000000) | 0 | 100000000 | 0 | 0 |
| 100000000 (0x05f5e100) | 1 (0x00000001) | 100000000 | 0 | 0 | 0 |
| 100000000 (0x05f5e100) | 10000 (0x00002710) | 10000 | 0 | 0 | 0 |
| 100000000 (0x05f5e100) | 32767 (0x00007fff) | 3051 | 27883 | 0 | 0 |
| 100000000 (0x05f5e100) | 100000000 (0x05f5e100) | 1 | 0 | 0 | 0 |
| 100000000 (0x05f5e100) | 2147483647 (0x7fffffff) | 0 | 100000000 | 0 | 0 |
| 2147483647 (0x7fffffff) | -2147483648 (0x80000000) | 0 | 2147483647 | 0 | 0 |
| 2147483647 (0x7fffffff) | -100000000 (0xfa0a1f00) | -21 | 47483647 | 0 | 0 |
| 2147483647 (0x7fffffff) | -32768 (0xffff8000) | -65535 | 32767 | 0 | 0 |
| 2147483647 (0x7fffffff) | -10000 (0xffffd8f0) | -214748 | 3647 | 0 | 0 |
| 2147483647 (0x7fffffff) | -1 (0xffffffff) | -2147483647 | 0 | 0 | 0 |
| 2147483647 (0x7fffffff) | 0 (0x00000000) | 0 | 2147483647 | 0 | 0 |
| 2147483647 (0x7fffffff) | 1 (0x00000001) | 2147483647 | 0 | 0 | 0 |
| 2147483647 (0x7fffffff) | 10000 (0x00002710) | 214748 | 3647 | 0 | 0 |
| 2147483647 (0x7fffffff) | 32767 (0x00007fff) | 65538 | 1 | 0 | 0 |
| 2147483647 (0x7fffffff) | 100000000 (0x05f5e100) | 21 | 47483647 | 0 | 0 |
| 2147483647 (0x7fffffff) | 2147483647 (0x7fffffff) | 1 | 0 | 0 | 0 |
DSP_Div用时统计
time = 131
参考代码
06.01_DSP_Div.c
/**
* @brief DSP-除法
* @details 使用硬件dsp进行除法计算
*/
#include "lks32mc09x_dsp.h"
#include "lks32mc09x_timer.h"
volatile int a = 10000;
volatile int b = 100;
volatile int y;
volatile int mod;
volatile int time;
void Timer0_Init(void);
/**
* @brief 主函数,用于测试HSI时钟输出及GPIO翻转
*/
int main(void)
{
DSP_Init();
Timer0_Init();
while (1)
{
TIMER_StatisticsStart(TIMER0);
y = DSP_Div(a, b);
time = TIMER_StatisticsStop(TIMER0);
mod = DSP_Mod(a, b);
}
}
/**
* @brief 配置定时器0,用来统计除法执行时间
*/
void Timer0_Init(void)
{
TIMER_TimerInitTypeDef TIMER_InitStruct;
TIMER_StructInit(&TIMER_InitStruct);
TIMER_InitStruct.EN = 1;
TIMER_InitStruct.TH = 0xffff;
TIMER_Init(TIMER0, &TIMER_InitStruct);
TIMER_Enable(TIMER0);
}
使用到的库函数
库函数部分代码
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_DSP0 BIT17
#define SYS_MODULE_TIMER0 BIT4
#define SYS_MODULE_TIMER1 BIT5
#define SYS_MODULE_TIMER2 BIT6
#define TIMER0 ((TIMER_TypeDef *)TIMER0_BASE)
#define TIMER1 ((TIMER_TypeDef *)TIMER1_BASE)
#define TIMER2 ((TIMER_TypeDef *)TIMER2_BASE)
/**
* @brief Timer初始化结构体
*/
typedef struct TIMER_TimerInitTypeDef {
uint8_t EN;
uint32_t CAP1_CLR_EN;
uint32_t CAP0_CLR_EN;
uint32_t ONE_TRIG;
uint32_t CENTER;
uint32_t DIR;
uint32_t ClockDiv;
uint32_t ETON;
uint32_t GATE_EN;
uint32_t RL_EN;
uint32_t XCLK_EN;
uint32_t SRC1;
uint32_t CH1Output;
uint32_t CH1_WorkMode;
uint32_t CH1_FE_CAP_EN;
uint32_t CH1_RE_CAP_EN;
uint32_t SRC0;
uint32_t CH0Output;
uint32_t CH0_WorkMode;
uint32_t CH0_FE_CAP_EN;
uint32_t CH0_RE_CAP_EN;
uint32_t TH;
uint32_t CNT;
uint32_t CHN0;
uint32_t CHN1;
uint32_t EVT;
uint32_t FLT;
uint32_t IE;
uint32_t CMP0_TRIGGER_MODE;
uint32_t CMP1_TRIGGER_MODE;
uint32_t SHADOW;
uint32_t CH1_DEFAULT;
uint32_t CH0_DEFAULT;
uint32_t HALT_PRT;
uint32_t FAIL_SEL;
uint32_t FAIL_POL;
uint32_t FAIL_EN;
uint32_t MOE;
};
/**
* @brief Timer初始化
* @param TIMERx Timer实例指针
* @param TIMER_InitStruct Timer初始化结构体指针
*/
void TIMER_Init(TIMER_TypeDef *TIMERx, TIMER_TimerInitTypeDef *TIMER_InitStruct)
{
uint32_t th = TIMER_InitStruct->TH;
TIMER_Enable(TIMERx);
if (TIMERx != TIMER2) // 只有timer2是32位定时器
{
if (th > 0xffff)
{
th = 0xffff;
}
else
{
__NOP();
}
}
else
{
__NOP();
}
// 配置Timer寄存器
TIMERx->CFG2 = (TIMER_InitStruct->EN << 15) |
(TIMER_InitStruct->ONE_TRIG << 9) |
(TIMER_InitStruct->CENTER << 8) |
(TIMER_InitStruct->DIR << 6) |
(TIMER_InitStruct->ClockDiv << 4) |
(TIMER_InitStruct->ETON << 3) |
(TIMER_InitStruct->GATE_EN << 2) |
(TIMER_InitStruct->RL_EN << 1) |
TIMER_InitStruct->XCLK_EN;
TIMERx->CFG0 = (TIMER_InitStruct->CMP0_TRIGGER_MODE << 9) |
(TIMER_InitStruct->CAP0_CLR_EN << 8) |
(TIMER_InitStruct->SRC0 << 4) |
(TIMER_InitStruct->CH0Output << 3) |
(TIMER_InitStruct->CH0_WorkMode << 2) |
(TIMER_InitStruct->CH0_FE_CAP_EN << 1) |
(TIMER_InitStruct->CH0_RE_CAP_EN << 0);
TIMERx->CFG1 = (TIMER_InitStruct->CMP1_TRIGGER_MODE << 9) |
(TIMER_InitStruct->CAP1_CLR_EN << 8) |
(TIMER_InitStruct->SRC1 << 4) |
(TIMER_InitStruct->CH1Output << 3) |
(TIMER_InitStruct->CH1_WorkMode << 2) |
(TIMER_InitStruct->CH1_FE_CAP_EN << 1) |
(TIMER_InitStruct->CH1_RE_CAP_EN << 0);
TIMERx->CHN0 = TIMER_InitStruct->CHN0;
TIMERx->CHN1 = TIMER_InitStruct->CHN1;
TIMERx->TH = th;
TIMERx->CNT = TIMER_InitStruct->CNT;
TIMERx->EVT = TIMER_InitStruct->EVT;
TIMERx->FLT = TIMER_InitStruct->FLT;
TIMERx->IE = TIMER_InitStruct->IE;
TIMERx->IO = (TIMER_InitStruct->HALT_PRT << 7) |
(TIMER_InitStruct->CH0_DEFAULT << 8) |
(TIMER_InitStruct->CH1_DEFAULT << 9) |
(TIMER_InitStruct->MOE << 6) |
(TIMER_InitStruct->FAIL_SEL << 2) |
(TIMER_InitStruct->FAIL_POL << 1) |
(TIMER_InitStruct->FAIL_EN << 0);
// 影子寄存器配置
TIMERx->CFG2 |= (TIMER_InitStruct->SHADOW << 10);
}
/**
* @brief Timer结构体初始化
* @param TIMER_InitStruct Timer初始化结构体指针
*/
void TIMER_StructInit(TIMER_TimerInitTypeDef *TIMER_InitStruct)
{
for (int i = 0; i < sizeof(TIMER_TimerInitTypeDef) / sizeof(uint32_t); i++)
{
((uint32_t *)TIMER_InitStruct)[i] = 0;
}
}
/**
* @brief 使能Timer
* @param TIMERx Timer实例指针
*/
void TIMER_Enable(TIMER_TypeDef *TIMERx)
{
if (TIMERx == TIMER0)
{
SYS_ModuleClockCmd(SYS_MODULE_TIMER0, ENABLE);
}
else if (TIMERx == TIMER1)
{
SYS_ModuleClockCmd(SYS_MODULE_TIMER1, ENABLE);
}
else if (TIMERx == TIMER2)
{
SYS_ModuleClockCmd(SYS_MODULE_TIMER2, ENABLE);
}
else
{
__NOP();
}
}
/**
* @brief 初始化DSP模块
*
* 该函数用于初始化DSP模块,使其处于可用状态。
*/
void DSP_Init(void)
{
SYS_ModuleClockCmd(SYS_MODULE_DSP0, ENABLE);
SYS_SoftResetModule(SYS_MODULE_DSP0);
}
/**
* @brief 执行取模运算
*
* @param a 被除数
* @param b 除数
* @return 余数
*/
s32 DSP_Mod(s32 a, s32 b)
{
DSP0_DID = a;
DSP0_DIS = b;
return DSP0_REM;
}
/**
* @brief 执行整数除法
*
* @param a 被除数
* @param b 除数
* @return 商
*/
s32 DSP_Div(s32 a, s32 b)
{
DSP0_DID = a;
DSP0_DIS = b;
// 执行整数除法,这里直接返回除法结果
return DSP0_QUO;
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 软复位模块
* @param nModule 模块编号
*/
void SYS_SoftResetModule(uint32_t nModule)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
REG_SET(SYS0->SFT_RST, nModule);
REG_RESET(SYS0->SFT_RST, nModule); // 触发复位后需清除复位位
REG_WRITE(SYS0->PROTECT, 0);
}
06.02_DSP-开方
测试步骤
使用硬件dsp进行除法计算
实测数据
| a | DSP_Sqrt(a) | err |
|---|---|---|
| 0 (0x00000000) | 0 | 0 |
| 1 (0x00000001) | 1 | 0 |
| 10 (0x0000000a) | 3 | 0 |
| 100 (0x00000064) | 10 | 0 |
| 1000 (0x000003e8) | 31 | 0 |
| 10000 (0x00002710) | 100 | 0 |
| 32767 (0x00007fff) | 181 | 0 |
| 65535 (0x0000ffff) | 255 | 0 |
| 65536 (0x00010000) | 256 | 0 |
| 65536 (0x00010000) | 256 | 0 |
| 131072 (0x00020000) | 362 | 0 |
| 262144 (0x00040000) | 512 | 0 |
| 524288 (0x00080000) | 724 | 0 |
| 1048576 (0x00100000) | 1024 | 0 |
| 2097152 (0x00200000) | 1448 | 0 |
| 4194304 (0x00400000) | 2048 | 0 |
| 8388608 (0x00800000) | 2896 | 0 |
| 16777216 (0x01000000) | 4096 | 0 |
| 33554432 (0x02000000) | 5792 | 0 |
| 67108864 (0x04000000) | 8192 | 0 |
| 134217728 (0x08000000) | 11585 | 0 |
| 268435456 (0x10000000) | 16384 | 0 |
| 536870912 (0x20000000) | 23170 | 0 |
| 1073741824 (0x40000000) | 32768 | 0 |
| 2147483648 (0x80000000) | 46340 | 0 |
| 1 (0x00000001) | 1 | 0 |
| 3 (0x00000003) | 1 | 0 |
| 7 (0x00000007) | 2 | 0 |
| 15 (0x0000000f) | 3 | 0 |
| 31 (0x0000001f) | 5 | 0 |
| 63 (0x0000003f) | 7 | 0 |
| 127 (0x0000007f) | 11 | 0 |
| 255 (0x000000ff) | 15 | 0 |
| 511 (0x000001ff) | 22 | 0 |
| 1023 (0x000003ff) | 31 | 0 |
| 2047 (0x000007ff) | 45 | 0 |
| 4095 (0x00000fff) | 63 | 0 |
| 8191 (0x00001fff) | 90 | 0 |
| 16383 (0x00003fff) | 127 | 0 |
| 32767 (0x00007fff) | 181 | 0 |
| 65535 (0x0000ffff) | 255 | 0 |
| 8191 (0x00001fff) | 90 | 0 |
| 16383 (0x00003fff) | 127 | 0 |
| 32767 (0x00007fff) | 181 | 0 |
| 65535 (0x0000ffff) | 255 | 0 |
| 131071 (0x0001ffff) | 362 | 0 |
| 262143 (0x0003ffff) | 511 | 0 |
| 524287 (0x0007ffff) | 724 | 0 |
| 1048575 (0x000fffff) | 1023 | 0 |
| 2097151 (0x001fffff) | 1448 | 0 |
| 4194303 (0x003fffff) | 2047 | 0 |
| 8388607 (0x007fffff) | 2896 | 0 |
| 16777215 (0x00ffffff) | 4095 | 0 |
| 33554431 (0x01ffffff) | 5792 | 0 |
| 67108863 (0x03ffffff) | 8191 | 0 |
| 134217727 (0x07ffffff) | 11585 | 0 |
| 268435455 (0x0fffffff) | 16383 | 0 |
| 536870911 (0x1fffffff) | 23170 | 0 |
| 1073741823 (0x3fffffff) | 32767 | 0 |
| 2147483647 (0x7fffffff) | 46340 | 0 |
| 4294967295 (0xffffffff) | 65535 | 0 |
time = 107
参考代码
06.02_DSP_Sqrt.c
/**
* @brief DSP-开方
* @details 使用硬件dsp进行除法计算
*/
#include "lks32mc09x_dsp.h"
#include "lks32mc09x_timer.h"
volatile int a = 10000;
volatile int y;
volatile int time;
void Timer0_Init(void);
/**
* @brief 主函数,用于测试HSI时钟输出及GPIO翻转
*/
int main(void)
{
DSP_Init();
Timer0_Init();
while (1)
{
TIMER_StatisticsStart(TIMER0);
y = DSP_Sqrt(a);
time = TIMER_StatisticsStop(TIMER0);
}
}
/**
* @brief 配置定时器0,用来统计除法执行时间
*/
void Timer0_Init(void)
{
TIMER_TimerInitTypeDef TIMER_InitStruct;
TIMER_StructInit(&TIMER_InitStruct);
TIMER_InitStruct.EN = 1;
TIMER_InitStruct.TH = 0xffff;
TIMER_Init(TIMER0, &TIMER_InitStruct);
TIMER_Enable(TIMER0);
}
#include "math.h"
使用到的库函数
库函数部分代码
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_DSP0 BIT17
#define SYS_MODULE_TIMER0 BIT4
#define SYS_MODULE_TIMER1 BIT5
#define SYS_MODULE_TIMER2 BIT6
#define TIMER0 ((TIMER_TypeDef *)TIMER0_BASE)
#define TIMER1 ((TIMER_TypeDef *)TIMER1_BASE)
#define TIMER2 ((TIMER_TypeDef *)TIMER2_BASE)
/**
* @brief Timer初始化结构体
*/
typedef struct TIMER_TimerInitTypeDef {
uint8_t EN;
uint32_t CAP1_CLR_EN;
uint32_t CAP0_CLR_EN;
uint32_t ONE_TRIG;
uint32_t CENTER;
uint32_t DIR;
uint32_t ClockDiv;
uint32_t ETON;
uint32_t GATE_EN;
uint32_t RL_EN;
uint32_t XCLK_EN;
uint32_t SRC1;
uint32_t CH1Output;
uint32_t CH1_WorkMode;
uint32_t CH1_FE_CAP_EN;
uint32_t CH1_RE_CAP_EN;
uint32_t SRC0;
uint32_t CH0Output;
uint32_t CH0_WorkMode;
uint32_t CH0_FE_CAP_EN;
uint32_t CH0_RE_CAP_EN;
uint32_t TH;
uint32_t CNT;
uint32_t CHN0;
uint32_t CHN1;
uint32_t EVT;
uint32_t FLT;
uint32_t IE;
uint32_t CMP0_TRIGGER_MODE;
uint32_t CMP1_TRIGGER_MODE;
uint32_t SHADOW;
uint32_t CH1_DEFAULT;
uint32_t CH0_DEFAULT;
uint32_t HALT_PRT;
uint32_t FAIL_SEL;
uint32_t FAIL_POL;
uint32_t FAIL_EN;
uint32_t MOE;
};
/**
* @brief Timer初始化
* @param TIMERx Timer实例指针
* @param TIMER_InitStruct Timer初始化结构体指针
*/
void TIMER_Init(TIMER_TypeDef *TIMERx, TIMER_TimerInitTypeDef *TIMER_InitStruct)
{
uint32_t th = TIMER_InitStruct->TH;
TIMER_Enable(TIMERx);
if (TIMERx != TIMER2) // 只有timer2是32位定时器
{
if (th > 0xffff)
{
th = 0xffff;
}
else
{
__NOP();
}
}
else
{
__NOP();
}
// 配置Timer寄存器
TIMERx->CFG2 = (TIMER_InitStruct->EN << 15) |
(TIMER_InitStruct->ONE_TRIG << 9) |
(TIMER_InitStruct->CENTER << 8) |
(TIMER_InitStruct->DIR << 6) |
(TIMER_InitStruct->ClockDiv << 4) |
(TIMER_InitStruct->ETON << 3) |
(TIMER_InitStruct->GATE_EN << 2) |
(TIMER_InitStruct->RL_EN << 1) |
TIMER_InitStruct->XCLK_EN;
TIMERx->CFG0 = (TIMER_InitStruct->CMP0_TRIGGER_MODE << 9) |
(TIMER_InitStruct->CAP0_CLR_EN << 8) |
(TIMER_InitStruct->SRC0 << 4) |
(TIMER_InitStruct->CH0Output << 3) |
(TIMER_InitStruct->CH0_WorkMode << 2) |
(TIMER_InitStruct->CH0_FE_CAP_EN << 1) |
(TIMER_InitStruct->CH0_RE_CAP_EN << 0);
TIMERx->CFG1 = (TIMER_InitStruct->CMP1_TRIGGER_MODE << 9) |
(TIMER_InitStruct->CAP1_CLR_EN << 8) |
(TIMER_InitStruct->SRC1 << 4) |
(TIMER_InitStruct->CH1Output << 3) |
(TIMER_InitStruct->CH1_WorkMode << 2) |
(TIMER_InitStruct->CH1_FE_CAP_EN << 1) |
(TIMER_InitStruct->CH1_RE_CAP_EN << 0);
TIMERx->CHN0 = TIMER_InitStruct->CHN0;
TIMERx->CHN1 = TIMER_InitStruct->CHN1;
TIMERx->TH = th;
TIMERx->CNT = TIMER_InitStruct->CNT;
TIMERx->EVT = TIMER_InitStruct->EVT;
TIMERx->FLT = TIMER_InitStruct->FLT;
TIMERx->IE = TIMER_InitStruct->IE;
TIMERx->IO = (TIMER_InitStruct->HALT_PRT << 7) |
(TIMER_InitStruct->CH0_DEFAULT << 8) |
(TIMER_InitStruct->CH1_DEFAULT << 9) |
(TIMER_InitStruct->MOE << 6) |
(TIMER_InitStruct->FAIL_SEL << 2) |
(TIMER_InitStruct->FAIL_POL << 1) |
(TIMER_InitStruct->FAIL_EN << 0);
// 影子寄存器配置
TIMERx->CFG2 |= (TIMER_InitStruct->SHADOW << 10);
}
/**
* @brief Timer结构体初始化
* @param TIMER_InitStruct Timer初始化结构体指针
*/
void TIMER_StructInit(TIMER_TimerInitTypeDef *TIMER_InitStruct)
{
for (int i = 0; i < sizeof(TIMER_TimerInitTypeDef) / sizeof(uint32_t); i++)
{
((uint32_t *)TIMER_InitStruct)[i] = 0;
}
}
/**
* @brief 使能Timer
* @param TIMERx Timer实例指针
*/
void TIMER_Enable(TIMER_TypeDef *TIMERx)
{
if (TIMERx == TIMER0)
{
SYS_ModuleClockCmd(SYS_MODULE_TIMER0, ENABLE);
}
else if (TIMERx == TIMER1)
{
SYS_ModuleClockCmd(SYS_MODULE_TIMER1, ENABLE);
}
else if (TIMERx == TIMER2)
{
SYS_ModuleClockCmd(SYS_MODULE_TIMER2, ENABLE);
}
else
{
__NOP();
}
}
/**
* @brief 初始化DSP模块
*
* 该函数用于初始化DSP模块,使其处于可用状态。
*/
void DSP_Init(void)
{
SYS_ModuleClockCmd(SYS_MODULE_DSP0, ENABLE);
SYS_SoftResetModule(SYS_MODULE_DSP0);
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 计算平方根
*
* @param val 输入值
* @return 平方根值
*/
uint16_t DSP_Sqrt(uint32_t val)
{
DSP0_RAD = val;
return DSP0_SQRT;
}
/**
* @brief 软复位模块
* @param nModule 模块编号
*/
void SYS_SoftResetModule(uint32_t nModule)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
REG_SET(SYS0->SFT_RST, nModule);
REG_RESET(SYS0->SFT_RST, nModule); // 触发复位后需清除复位位
REG_WRITE(SYS0->PROTECT, 0);
}
07.01_EEPROM-读写
测试步骤
使用芯片内置EEPROM模块对EEPROM进行操作。 写入完成后从 eeprom 里读回来。 判断写入的数据和读回来的是否一致。
实测数据
测试成功
参考代码
07.01_EEPROM_ReadWriteTest.c
/**
* @brief EEPROM-读写
* @details 使用芯片内置EEPROM模块对EEPROM进行操作。
* 写入完成后从 eeprom 里读回来。
* 判断写入的数据和读回来的是否一致。
*/
#include "lks32mc09x_eeprom.h"
#include "lks32mc09x_gpio.h"
void SoftDelaymS(uint32_t n);
volatile uint32_t testflg = 0; // 测试状态,0空闲 1进行中 2写入数据&读回数据一致 3写入数据&读回数据不一致
uint8_t read_data[32];
uint8_t read_data1[32];
uint8_t write_data[32] = {0};
#define EEPROMADDR 0xa0
/**
* @brief 主函数,用于测试HSI时钟输出及GPIO翻转
*/
int main(void)
{
GPIO_Config(GPIO0, 0, GPIO_Mode_OUT, GPIO_AF_GPIO);
GPIO_Config(GPIO2, 1, GPIO_Mode_IO, GPIO_AF_I2C);
GPIO_Config(GPIO2, 2, GPIO_Mode_IO, GPIO_AF_I2C);
EEPROM_InitDefault(); // 初始化时会自动使能
for (;;)
{
if (testflg == 1)
{
for (int len = 1; len <= 32; len++) // 从1个数据到32个数据进行测试,以确保不同数据长度下i2c通信是否正常
{
for (int i = 0; i < len; i++)
{
write_data[i] = i + len; // 确保每次写入的数据都不一样
}
EEPROM_WriteData(EEPROMADDR, 0, write_data, len);
SoftDelaymS(10);
EEPROM_ReadData(EEPROMADDR, 0, read_data, len);
for (int i = 0; i < len; i++)
{
if (read_data[i] != write_data[i])
{
testflg = 3;
}
}
}
if (testflg != 3)
{
testflg = 2; // 测试完成,并且没有出现错误
}
}
}
}
使用到的库函数
库函数部分代码
#define EEPROM_CFG REG32(0x40048000)
#define EEPROM_RDATA REG32(0x40048004)
#define GPIO_AF_I2C 6
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_EEPROM BIT26
#define SYS_MODULE_GPIO BIT11
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* @brief GPIO模式选择枚举
*/
typedef enum GPIO_Mode_TypeDef {
GPIO_Mode_IN = 0,
GPIO_Mode_OUT = 1,
GPIO_Mode_ANA = 2,
GPIO_Mode_IO = 3,
}} {enum_name};
/**
* @brief GPIO配置函数
* @param GPIOx: GPIO端口
* @param GPIO_PinSource: GPIO引脚
* @param mode: GPIO模式
* @param GPIO_AF_x: GPIO复用功能选择
*/
void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
{
GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = 1 << GPIO_PinSource;
GPIO_InitStructure.GPIO_Mode = mode;
if (GPIO_AF_x == GPIO_AF_I2C)
{
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
}
else
{
__NOP();
}
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**
* @brief EEPROM使能
*/
void EEPROM_Enable(void)
{
SYS_ModuleClockCmd(SYS_MODULE_EEPROM, ENABLE);
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 初始化为默认配置,IO不交换,波特率100k,地址数据长度16位,读写时响应中断
*/
void EEPROM_InitDefault(void)
{
EEPROM_Enable();
EEPROM_CFG = BIT16 | BIT9 | (0xa0 << 1) | 0;
}
/**
* @brief 向eeprom写入数据
* @param eeprom_addr eeprom数据地址
* @param data 要写入的数据
*/
void EEPROM_Write(uint16_t eeprom_addr, uint8_t data)
{
if (eeprom_addr > 32767)
{
eeprom_addr = 32767;
}
else
{
__NOP();
}
if (EEPROM_CFG & BIT16)
{
do
{
REG8(eeprom_addr + 0x40040000) = data;
while (EEPROM_CFG & BIT17);
} while (EEPROM_CFG & BIT11); // 收到NACK则自动重发
}
else
{
REG8(eeprom_addr + 0x40040000) = data; // 先触发一次写操作
do
{
REG8(eeprom_addr + 0x40040000) = data; // 这里的写操作时,mcu会暂停运行,等待上一个写操作完成
} while (EEPROM_CFG & BIT11); // 这里判断的是上一次的写操作是否收到nack
}
}
/**
* @brief 向eeprom写入数据(多字节),09上eeprom本身不支持多字节模式,这里用n个单次读写实现,执行时间为len*5mS(典型值)
* @param eepromaddr eeprom模块设备地址,例如0xa0
* @param taddr eeprom数据的起始地址
* @param data 要写入的数据地址
* @param len 要写入的长度
*/
void EEPROM_WriteData(uint8_t eepromaddr, u16 taddr, u8 *data, uint32_t len)
{
uint16_t i;
eepromaddr &= 0xfe;
EEPROM_CFG = (EEPROM_CFG & (~0x1fc)) | (eepromaddr << 1);
for (i = 0; i < len; i++)
{
EEPROM_Write(taddr + i, data[i]);
}
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 读取eeprom内数据
* @param eeprom_addr eeprom数据地址
* @return 数据
*/
uint8_t EEPROM_Read(uint16_t eeprom_addr)
{
uint8_t val;
if (eeprom_addr > 32767)
{
eeprom_addr = 32767;
}
else
{
__NOP();
}
while (EEPROM_CFG & BIT17); // 等待之前的操作结束(如果有的话)
do
{
val = REG8(eeprom_addr | 0x40040000);
while (EEPROM_CFG & BIT17);
} while (EEPROM_CFG & BIT11);
val = EEPROM_RDATA;
return val;
}
/**
* @brief 读取eeprom内数据,09上eeprom本身不支持多字节模式,这里用n个单次读写实现
* @param eepromaddr eeprom模块设备地址,例如0xa0
* @param taddr eeprom数据的起始地址
* @param data 存放数据的地址
* @param len 数据长度
*/
void EEPROM_ReadData(uint8_t eepromaddr, u16 taddr, u8 *data, uint32_t len)
{
uint16_t i;
eepromaddr &= 0xfe;
EEPROM_CFG = (EEPROM_CFG & (~0x1fc)) | (eepromaddr << 1);
for (i = 0; i < len; i++)
{
data[i] = EEPROM_Read(taddr + i);
}
}
/**
* @brief 配置GPIO引脚复用功能
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource 引脚源
* @param GPIO_AF 复用功能选择
*/
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
uint8_t offset;
uint8_t pins = GPIO_PinSource >> 2;
offset = ((GPIO_PinSource & 0x3) * 4);
switch (pins)
{
case 0:
GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 1:
GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 2:
GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 3:
GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
break;
default:
break;
}
}
08.01_EXTI-上升沿&下降沿均触发中断
测试步骤
使用P0.15作为外部中断引脚,上升沿&下降沿均触发中断。 外部输入高低电平(默认低电平),记录中断发生次数。
实测数据
| t | P0.15Val | count |
|---|---|---|
| 0 | 0 | 0 |
| 1 | 1 | 1 |
| 2 | 0 | 2 |
| 3 | 1 | 3 |
| 4 | 0 | 4 |
| 5 | 1 | 5 |
| 6 | 0 | 6 |
| 7 | 1 | 7 |
| 8 | 0 | 8 |
| 9 | 1 | 9 |
| 10 | 0 | 10 |
| 11 | 1 | 11 |
| 12 | 0 | 12 |
| 13 | 1 | 13 |
| 14 | 0 | 14 |
| 15 | 1 | 15 |
| 16 | 0 | 16 |
| 17 | 1 | 17 |
| 18 | 0 | 18 |
| 19 | 1 | 19 |
测试结果:通过
参考代码
08.01_EXTI_Toggle.c
/**
* @brief EXTI-上升沿&下降沿均触发中断
* @details
* 使用P0.15作为外部中断引脚,上升沿&下降沿均触发中断。
* 外部输入高低电平(默认低电平),记录中断发生次数。
*/
#include "lks32mc09x_exti.h"
#include "lks32mc09x_gpio.h"
#include "lks32mc09x_sys.h"
volatile uint32_t count = 0;
// EXTI中断处理函数
void GPIO_IRQHandler(void)
{
// 检查P0.15引脚的中断标志
if (EXTI_GetIRQFlag(EXTI0, EXTI_IF_P0_15))
{
// 清除中断标志
EXTI_ClearIRQFlag(EXTI0, EXTI_IF_P0_15);
count++;
}
}
/**
* @brief 主函数
*/
int main(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_StructInit(&GPIO_InitStruct);
// 使能GPIO0时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 初始化P0.15为输入,并配置为外部中断引脚
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIO0, &GPIO_InitStruct);
// 配置P0.15的上升沿、下降沿均触发外部中断
EXTI_TriggerConfig(EXTI0, EXTI_CR_P0_15, EXTI_Trigger_Edge);
EXTI_EnableIRQFlag(EXTI0, EXTI_IF_P0_15);
// 使能EXTI0中断
NVIC_EnableIRQ(GPIO_IRQn);
__enable_irq();
while (1)
{
}
}
使用到的库函数
库函数部分代码
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_GPIO BIT11
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* EXTI触发信号定义
*/
typedef enum EXTI_Mode_TypeDef {
EXTI_Trigger_None = 0,
EXTI_Trigger_Negedge = 1,
EXTI_Trigger_Posedge = 2,
EXTI_Trigger_Edge = 3,
}} {enum_name};
/**
* @brief 使能外部中断标志
*
* @param EXTIx EXTI模块指针
* @param EXTI_IF_x 外部中断源
*/
void EXTI_EnableIRQFlag(EXTI_TypeDef *EXTIx, uint32_t EXTI_IF_x)
{
exti_ie_flg |= EXTI_IF_x;
if (exti_ie_flg & 0x7f)
{
EXTIx->IE |= BIT0;
}
else
{
EXTIx->IE &= ~BIT0;
}
if (exti_ie_flg & 0x80)
{
EXTIx->IE |= BIT1;
}
else
{
EXTIx->IE &= ~BIT1;
}
if (exti_ie_flg & 0xff00)
{
EXTIx->IE |= BIT2;
}
else
{
EXTIx->IE &= ~BIT2;
}
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 清除GPIO引脚的中断标志
*
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource GPIO引脚源
*/
void EXTI_ClearIRQFlag(EXTI_TypeDef *EXTIx, uint32_t EXTI_IF_x)
{
// 清除对应GPIO引脚的中断标志
EXTIx->IF = EXTI_IF_x;
}
/**
* @brief 获取GPIO引脚的中断标志状态
*
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource GPIO引脚源
* @return 中断标志状态
* @retval 0 无中断
* @retval 1 有中断
*/
uint8_t EXTI_GetIRQFlag(EXTI_TypeDef *EXTIx, uint32_t EXTI_IF_x)
{
// 获取对应GPIO引脚的中断标志状态
return EXTIx->IF & EXTI_IF_x;
}
/**
* @brief 配置GPIO引脚的中断触发模式
*
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource GPIO引脚源
* @param EXTI_Trigger 触发模式
*/
void EXTI_TriggerConfig(EXTI_TypeDef *EXTIx, uint8_t EXTI_CR_x, EXTI_Mode_TypeDef EXTI_Trigger)
{
// 根据GPIO引脚源和触发模式配置EXTI触发寄存器
if (EXTI_CR_x < 8) // cr0
{
EXTIx->CR0 &= ~(0x03 << (EXTI_CR_x * 2));
EXTIx->CR0 |= (EXTI_Trigger << (EXTI_CR_x * 2));
}
else // cr1
{
EXTIx->CR1 &= ~(0x03 << ((EXTI_CR_x - 8) * 2));
EXTIx->CR1 |= (EXTI_Trigger << ((EXTI_CR_x - 8) * 2));
}
}
08.02_EXTI-上升沿触发
测试步骤
使用P0.15作为外部中断引脚,上升沿&下降沿均触发中断。 外部输入高低电平(默认低电平),记录中断发生次数。
实测数据
| t | P0.15Val | count |
|---|---|---|
| 0 | 0 | 0 |
| 1 | 1 | 1 |
| 2 | 0 | 1 |
| 3 | 1 | 2 |
| 4 | 0 | 2 |
| 5 | 1 | 3 |
| 6 | 0 | 3 |
| 7 | 1 | 4 |
| 8 | 0 | 4 |
| 9 | 1 | 5 |
| 10 | 0 | 5 |
| 11 | 1 | 6 |
| 12 | 0 | 6 |
| 13 | 1 | 7 |
| 14 | 0 | 7 |
| 15 | 1 | 8 |
| 16 | 0 | 8 |
| 17 | 1 | 9 |
| 18 | 0 | 9 |
| 19 | 1 | 10 |
测试结果:通过
参考代码
08.02_EXTI_Toggle.c
/**
* @brief EXTI-上升沿触发
* @details
* 使用P0.15作为外部中断引脚,上升沿&下降沿均触发中断。
* 外部输入高低电平(默认低电平),记录中断发生次数。
*/
#include "lks32mc09x_exti.h"
#include "lks32mc09x_gpio.h"
#include "lks32mc09x_sys.h"
volatile uint32_t count = 0;
// EXTI中断处理函数
void GPIO_IRQHandler(void)
{
// 检查P0.15引脚的中断标志
if (EXTI_GetIRQFlag(EXTI0, EXTI_IF_P0_15))
{
// 清除中断标志
EXTI_ClearIRQFlag(EXTI0, EXTI_IF_P0_15);
count++;
}
}
/**
* @brief 主函数
*/
int main(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_StructInit(&GPIO_InitStruct);
// 使能GPIO0时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 初始化P0.15为输入,并配置为外部中断引脚
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIO0, &GPIO_InitStruct);
// 配置P0.15的上升沿、下降沿均触发外部中断
EXTI_TriggerConfig(EXTI0, EXTI_CR_P0_15, EXTI_Trigger_Posedge);
EXTI_EnableIRQFlag(EXTI0, EXTI_IF_P0_15);
// 使能EXTI0中断
NVIC_EnableIRQ(GPIO_IRQn);
__enable_irq();
while (1)
{
// P0.7不停翻转电平,提供中断信号
GPIO_ToggleBits(GPIO0, GPIO_Pin_7);
for (volatile uint32_t i = 0; i < 100; i++)
; // 延时
}
}
使用到的库函数
库函数部分代码
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_GPIO BIT11
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* EXTI触发信号定义
*/
typedef enum EXTI_Mode_TypeDef {
EXTI_Trigger_None = 0,
EXTI_Trigger_Negedge = 1,
EXTI_Trigger_Posedge = 2,
EXTI_Trigger_Edge = 3,
}} {enum_name};
/**
* @brief 使能外部中断标志
*
* @param EXTIx EXTI模块指针
* @param EXTI_IF_x 外部中断源
*/
void EXTI_EnableIRQFlag(EXTI_TypeDef *EXTIx, uint32_t EXTI_IF_x)
{
exti_ie_flg |= EXTI_IF_x;
if (exti_ie_flg & 0x7f)
{
EXTIx->IE |= BIT0;
}
else
{
EXTIx->IE &= ~BIT0;
}
if (exti_ie_flg & 0x80)
{
EXTIx->IE |= BIT1;
}
else
{
EXTIx->IE &= ~BIT1;
}
if (exti_ie_flg & 0xff00)
{
EXTIx->IE |= BIT2;
}
else
{
EXTIx->IE &= ~BIT2;
}
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 清除GPIO引脚的中断标志
*
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource GPIO引脚源
*/
void EXTI_ClearIRQFlag(EXTI_TypeDef *EXTIx, uint32_t EXTI_IF_x)
{
// 清除对应GPIO引脚的中断标志
EXTIx->IF = EXTI_IF_x;
}
/**
* @brief 翻转指定的GPIO引脚位
* @param GPIOx GPIO模块指针
* @param GPIO_Pin 要翻转的引脚
*/
void GPIO_ToggleBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
GPIOx->PDO = GPIOx->PDO ^ GPIO_Pin;
}
/**
* @brief 获取GPIO引脚的中断标志状态
*
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource GPIO引脚源
* @return 中断标志状态
* @retval 0 无中断
* @retval 1 有中断
*/
uint8_t EXTI_GetIRQFlag(EXTI_TypeDef *EXTIx, uint32_t EXTI_IF_x)
{
// 获取对应GPIO引脚的中断标志状态
return EXTIx->IF & EXTI_IF_x;
}
/**
* @brief 配置GPIO引脚的中断触发模式
*
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource GPIO引脚源
* @param EXTI_Trigger 触发模式
*/
void EXTI_TriggerConfig(EXTI_TypeDef *EXTIx, uint8_t EXTI_CR_x, EXTI_Mode_TypeDef EXTI_Trigger)
{
// 根据GPIO引脚源和触发模式配置EXTI触发寄存器
if (EXTI_CR_x < 8) // cr0
{
EXTIx->CR0 &= ~(0x03 << (EXTI_CR_x * 2));
EXTIx->CR0 |= (EXTI_Trigger << (EXTI_CR_x * 2));
}
else // cr1
{
EXTIx->CR1 &= ~(0x03 << ((EXTI_CR_x - 8) * 2));
EXTIx->CR1 |= (EXTI_Trigger << ((EXTI_CR_x - 8) * 2));
}
}
09.01_FLASH-MAIN区域读写和擦除
测试步骤
通过调用FLASH_Write()和FLASH_Erase()函数,实现了对Flash存储器的读写和擦除操作。 向flash存储器写入256字节的数据,然后擦除flash存储器,最后再写入数据。 数据为0~255,然后读取flash存储器,最后比较写入和读取的数据是否一致。
实测数据
芯片上电
REG(0x8000) = ffffffff ffffffff ffffffff ffffffff
写入flash
REG(0x8000) = 03020100 07060504 0b0a0908 0f0e0d0c
擦除flash
REG(0x8000) = ffffffff ffffffff ffffffff ffffffff
写入flash
REG(0x8000) = 03020100 07060504 0b0a0908 0f0e0d0c
擦除flash
REG(0x8000) = ffffffff ffffffff ffffffff ffffffff
参考代码
09.01_FLASH_ReadWrite.c
/**
* @brief FLASH-MAIN区域读写和擦除
* @details
* 通过调用FLASH_Write()和FLASH_Erase()函数,实现了对Flash存储器的读写和擦除操作。
* 向flash存储器写入256字节的数据,然后擦除flash存储器,最后再写入数据。
* 数据为0~255,然后读取flash存储器,最后比较写入和读取的数据是否一致。
*/
#include "lks32mc09x_flash.h"
#include "lks32mc09x_sys.h"
uint32_t address = 0x8000;
uint8_t testdata[256];
uint8_t buffer[256];
volatile uint32_t write_flg = 0;
/**
* @brief 主函数
*/
int main(void)
{
SYS_SetPowerFailureDetection(SYS_PVDSEL_OFF);
for (int i = 0; i < 256; i++)
{
testdata[i] = i;
}
// 主循环,防止程序退出
while (1)
{
switch (write_flg)
{
case 0:
break;
case 1:
write_flg = 0;
FLASH_Write(address, testdata, 256); // 写入flash
break;
case 2:
FLASH_Erase(address); // 擦除flash
write_flg = 0;
break;
case 3:
FLASH_Read(address, buffer, 256); // 读取flash
write_flg = 0;
break;
default:
break;
}
}
}
使用到的库函数
库函数部分代码
#define FLASH FLASH0
#define REG_WRITE(reg,mask) reg = (mask)
#define REG_WRITE_BIT(reg,mask,val) reg = ((reg) & ~(mask)) | (val)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
/**
* @brief 从指定地址读取数据
*
* @param adr 目标地址
* @param buf 数据缓冲区指针
* @param len 数据长度
*/
void FLASH_Read(uint32_t adr, uint8_t *data, uint32_t len)
{
uint32_t i;
for (i = 0; i < len; i++)
{
data[i] = REG8(adr + i);
}
}
/**
* @brief 向指定地址写入数据
* @param adr 目标地址
* @param data 数据缓冲区指针
* @param len 数据长度
* @note 触发掉电检测后,对flash的操作无效,掉电检测的配置参考SYS_SetPowerFailureDetection
*/
void FLASH_Write(uint32_t adr, uint8_t *data, uint32_t len)
{
uint8_t *p = data;
uint32_t flg = __get_PRIMASK();
__disable_irq();
SYS_WR_PROTECT = 0x7a83;
SYS_FLSP = 0x8f35;
FLASH->CFG |= BIT27 | BIT23;
FLASH->ADDR = adr;
len &= ~3; // 低两位值忽略
for (; len > 0; len -= 4)
{
FLASH->WDATA = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
p += 4;
}
SYS_FLSP = 0;
FLASH->CFG &= ~(BIT27 | BIT23);
SYS_WR_PROTECT = 0;
__set_PRIMASK(flg);
}
/**
* @brief 掉电检测配置
* @param SYS_PVDSEL_x 掉电检测配置值
*/
void SYS_SetPowerFailureDetection(uint32_t SYS_PVDSEL_x)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
REG_WRITE_BIT(SYS0->AFE_REG0, (BIT11 | BIT12 | BIT13 | BIT14), SYS_PVDSEL_x);
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 擦除指定扇区
*
* @param adr 目标地址
* @note 触发掉电检测后,对flash的操作无效,掉电检测的配置参考SYS_SetPowerFailureDetection
*/
void FLASH_Erase(uint32_t adr)
{
uint32_t flg = __get_PRIMASK();
__disable_irq();
SYS_WR_PROTECT = 0x7a83;
SYS_FLSE = 0x8fca;
FLASH->CFG |= BIT31;
FLASH->CFG &= ~BIT15;
FLASH->ADDR = adr;
FLASH->ERASE = 0x7654DCBA;
SYS_FLSE = 0;
FLASH->CFG &= ~BIT31;
__set_PRIMASK(flg);
}
09.02_FLASH-NVR区域读写和擦除
测试步骤
通过调用FLASH_WriteNvr()和FLASH_EraseNvr()函数,实现了对Flash存储器的读写和擦除操作。 向flash存储器写入256字节的数据,然后擦除flash存储器,最后再写入数据。 数据为0~255,然后读取flash存储器,最后比较写入和读取的数据是否一致。
实测数据
Start
REG(buffer) = ffffffff
写入flash
REG(buffer) = 03020100
擦除flash
REG(buffer) = ffffffff
写入flash
REG(buffer) = 03020100
擦除flash
REG(buffer) = ffffffff
参考代码
09.02_FLASH_ReadWriteNvr.c
/**
* @brief FLASH-NVR区域读写和擦除
* @details
* 通过调用FLASH_WriteNvr()和FLASH_EraseNvr()函数,实现了对Flash存储器的读写和擦除操作。
* 向flash存储器写入256字节的数据,然后擦除flash存储器,最后再写入数据。
* 数据为0~255,然后读取flash存储器,最后比较写入和读取的数据是否一致。
*/
#include "lks32mc09x_flash.h"
#include "lks32mc09x_sys.h"
uint32_t address = 0;
uint8_t testdata[256];
uint8_t buffer[256];
volatile uint32_t write_flg = 0;
/**
* @brief 主函数
*/
int main(void)
{
SYS_SetPowerFailureDetection(SYS_PVDSEL_OFF);
for (int i = 0; i < 256; i++)
{
testdata[i] = i;
}
FLASH_ReadNvr(address, buffer, 256); // 读取flash
// 主循环,防止程序退出
while (1)
{
switch (write_flg)
{
case 0:
break;
case 1:
write_flg = 0;
FLASH_WriteNvr(address, testdata, 256); // 写入flash
FLASH_ReadNvr(address, buffer, 256); // 读取flash
break;
case 2:
FLASH_EraseNvr(address); // 擦除flash
FLASH_ReadNvr(address, buffer, 256); // 读取flash
write_flg = 0;
break;
default:
write_flg = 0;
break;
}
}
}
使用到的库函数
库函数部分代码
#define FLASH FLASH0
#define REG_WRITE(reg,mask) reg = (mask)
#define REG_WRITE_BIT(reg,mask,val) reg = ((reg) & ~(mask)) | (val)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
/**
* @brief 从指定地址读取数据(NVR区域)
*
* @param adr 目标地址
* @param buf 数据缓冲区指针
* @param len 数据长度
*/
void FLASH_ReadNvr(uint32_t adr, uint8_t *data, uint32_t len)
{
uint32_t i;
uint32_t flg = __get_PRIMASK();
__disable_irq();
FLASH->CFG |= BIT11;
FLASH->ADDR = adr;
for (i = 0; i < len; i += 4)
{
uint32_t val = FLASH_RDATA;
data[i] = val;
data[i + 1] = val >> 8;
data[i + 2] = val >> 16;
data[i + 3] = val >> 24;
}
FLASH->CFG &= ~BIT11;
__set_PRIMASK(flg);
}
/**
* @brief 向指定地址写入数据(NVR区域)
* @param adr 目标地址
* @param data 数据缓冲区指针
* @param len 数据长度
* @note 触发掉电检测后,对flash的操作无效,掉电检测的配置参考SYS_SetPowerFailureDetection
*/
void FLASH_WriteNvr(uint32_t adr, uint8_t *data, uint32_t len)
{
uint8_t *p = data;
uint32_t flg = __get_PRIMASK();
__disable_irq();
SYS_WR_PROTECT = 0x7a83;
SYS_FLSP = 0x8f35;
FLASH->CFG |= BIT27 | BIT23 | BIT11;
FLASH->ADDR = adr;
len &= ~3; // 低两位值忽略
for (; len > 0; len -= 4)
{
FLASH->WDATA = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
p += 4;
}
SYS_FLSP = 0;
FLASH->CFG &= ~(BIT27 | BIT23 | BIT11);
SYS_WR_PROTECT = 0;
__set_PRIMASK(flg);
}
/**
* @brief 掉电检测配置
* @param SYS_PVDSEL_x 掉电检测配置值
*/
void SYS_SetPowerFailureDetection(uint32_t SYS_PVDSEL_x)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
REG_WRITE_BIT(SYS0->AFE_REG0, (BIT11 | BIT12 | BIT13 | BIT14), SYS_PVDSEL_x);
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 擦除指定扇区(NVR区域)
*
* @param adr 目标地址
* @note 触发掉电检测后,对flash的操作无效,掉电检测的配置参考SYS_SetPowerFailureDetection
*/
void FLASH_EraseNvr(uint32_t adr)
{
uint32_t flg = __get_PRIMASK();
__disable_irq();
SYS_WR_PROTECT = 0x7a83;
SYS_FLSE = 0x8fca;
FLASH->CFG |= BIT31 | BIT11;
FLASH->CFG &= ~BIT15;
FLASH->ADDR = adr;
FLASH->ERASE = 0x7654DCBA;
SYS_FLSE = 0;
FLASH->CFG &= ~(BIT31 | BIT11);
__set_PRIMASK(flg);
}
10.01_GPIO-输出模式
测试步骤
初始化P0.0为输出模式,分别输出0和1,读取P0.0上的电压
实测数据
供电3.000V
实测供电电压:2.972
GPIO_WriteBit(GPIO0,GPIO_Pin_0,GPIO_Bit_SET)执行后:P0.0电压为:2.970V
GPIO_WriteBit(GPIO0,GPIO_Pin_0,GPIO_Bit_RESET)执行后:P0.0电压为:0.003V
GPIO_WriteBit(GPIO0,GPIO_Pin_0,GPIO_Bit_SET)执行后:P0.0电压为:2.970V
GPIO_WriteBit(GPIO0,GPIO_Pin_0,GPIO_Bit_RESET)执行后:P0.0电压为:0.003V
供电3.300V
实测供电电压:3.271
GPIO_WriteBit(GPIO0,GPIO_Pin_0,GPIO_Bit_SET)执行后:P0.0电压为:3.269V
GPIO_WriteBit(GPIO0,GPIO_Pin_0,GPIO_Bit_RESET)执行后:P0.0电压为:0.003V
GPIO_WriteBit(GPIO0,GPIO_Pin_0,GPIO_Bit_SET)执行后:P0.0电压为:3.269V
GPIO_WriteBit(GPIO0,GPIO_Pin_0,GPIO_Bit_RESET)执行后:P0.0电压为:0.003V
供电4.000V
实测供电电压:3.969
GPIO_WriteBit(GPIO0,GPIO_Pin_0,GPIO_Bit_SET)执行后:P0.0电压为:3.967V
GPIO_WriteBit(GPIO0,GPIO_Pin_0,GPIO_Bit_RESET)执行后:P0.0电压为:0.003V
GPIO_WriteBit(GPIO0,GPIO_Pin_0,GPIO_Bit_SET)执行后:P0.0电压为:3.967V
GPIO_WriteBit(GPIO0,GPIO_Pin_0,GPIO_Bit_RESET)执行后:P0.0电压为:0.003V
供电4.500V
实测供电电压:4.467
GPIO_WriteBit(GPIO0,GPIO_Pin_0,GPIO_Bit_SET)执行后:P0.0电压为:4.466V
GPIO_WriteBit(GPIO0,GPIO_Pin_0,GPIO_Bit_RESET)执行后:P0.0电压为:0.003V
GPIO_WriteBit(GPIO0,GPIO_Pin_0,GPIO_Bit_SET)执行后:P0.0电压为:4.466V
GPIO_WriteBit(GPIO0,GPIO_Pin_0,GPIO_Bit_RESET)执行后:P0.0电压为:0.003V
供电5.000V
实测供电电压:4.966
GPIO_WriteBit(GPIO0,GPIO_Pin_0,GPIO_Bit_SET)执行后:P0.0电压为:4.964V
GPIO_WriteBit(GPIO0,GPIO_Pin_0,GPIO_Bit_RESET)执行后:P0.0电压为:0.003V
GPIO_WriteBit(GPIO0,GPIO_Pin_0,GPIO_Bit_SET)执行后:P0.0电压为:4.964V
GPIO_WriteBit(GPIO0,GPIO_Pin_0,GPIO_Bit_RESET)执行后:P0.0电压为:0.003V
参考代码
10.01_GPIO.c
/**
* @brief GPIO-输出模式
* @details
* 初始化P0.0为输出模式,分别输出0和1,读取P0.0上的电压
*/
#include "lks32mc09x_gpio.h"
#include "lks32mc09x_sys.h"
volatile int outstate = 0;
/**
* @brief 主函数
*/
int main(void)
{
GPIO_Config(GPIO0, 0, GPIO_Mode_OUT, GPIO_AF_GPIO);
while (1)
{
if (outstate)
{
GPIO_WriteBit(GPIO0, GPIO_Pin_0, GPIO_Bit_SET);
}
else
{
GPIO_WriteBit(GPIO0, GPIO_Pin_0, GPIO_Bit_RESET);
}
}
}
使用到的库函数
库函数部分代码
#define GPIO_AF_I2C 6
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_GPIO BIT11
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* @brief GPIO寄存器写入量枚举
*/
typedef enum GPIO_Bit_Action {
GPIO_Bit_RESET = 0,
GPIO_Bit_SET = 1,
}} {enum_name};
/**
* @brief GPIO模式选择枚举
*/
typedef enum GPIO_Mode_TypeDef {
GPIO_Mode_IN = 0,
GPIO_Mode_OUT = 1,
GPIO_Mode_ANA = 2,
GPIO_Mode_IO = 3,
}} {enum_name};
/**
* @brief GPIO配置函数
* @param GPIOx: GPIO端口
* @param GPIO_PinSource: GPIO引脚
* @param mode: GPIO模式
* @param GPIO_AF_x: GPIO复用功能选择
*/
void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
{
GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = 1 << GPIO_PinSource;
GPIO_InitStructure.GPIO_Mode = mode;
if (GPIO_AF_x == GPIO_AF_I2C)
{
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
}
else
{
__NOP();
}
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 设置或复位指定的GPIO引脚位
* @param GPIOx GPIO模块指针
* @param GPIO_Pin 要操作的引脚
* @param BitVal 要执行的操作:GPIO_Bit_SET 或 GPIO_Bit_RESET
*/
void GPIO_WriteBit(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_Bit_Action BitVal)
{
if (BitVal == GPIO_Bit_SET)
{
GPIOx->PDO |= GPIO_Pin;
}
else
{
GPIOx->PDO &= ~GPIO_Pin;
}
}
/**
* @brief 配置GPIO引脚复用功能
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource 引脚源
* @param GPIO_AF 复用功能选择
*/
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
uint8_t offset;
uint8_t pins = GPIO_PinSource >> 2;
offset = ((GPIO_PinSource & 0x3) * 4);
switch (pins)
{
case 0:
GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 1:
GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 2:
GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 3:
GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
break;
default:
break;
}
}
10.02_GPIO-输入模式(高阻输入)
测试步骤
初始化P0.0为输入模式,给P0.0输入0->vcc的电平,记录单片机读到的电平
实测数据
供电3.000V
实测供电电压:2.972
0->1 阈值电压为1.545
1->0 阈值电压为1.263
回差电压为0.282
供电3.300V
实测供电电压:3.271
0->1 阈值电压为1.690
1->0 阈值电压为1.406
回差电压为0.284
供电4.000V
实测供电电压:3.969
0->1 阈值电压为2.028
1->0 阈值电压为1.736
回差电压为0.292
供电4.500V
实测供电电压:4.467
0->1 阈值电压为2.268
1->0 阈值电压为1.980
回差电压为0.288
供电5.000V
实测供电电压:4.966
0->1 阈值电压为2.515
1->0 阈值电压为2.220
回差电压为0.295
参考代码
10.02_GPIO.c
/**
* @brief GPIO-输入模式(高阻输入)
* @details
* 初始化P0.0为输入模式,给P0.0输入0->vcc的电平,记录单片机读到的电平
*/
#include "lks32mc09x_gpio.h"
#include "lks32mc09x_sys.h"
volatile int val = 0;
/**
* @brief 主函数
*/
int main(void)
{
GPIO_Config(GPIO0, 0, GPIO_Mode_IN, GPIO_AF_GPIO);
while (1)
{
val = GPIO_ReadInputDataBit(GPIO0, GPIO_Pin_0);
}
}
使用到的库函数
库函数部分代码
#define GPIO_AF_I2C 6
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_GPIO BIT11
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* @brief GPIO模式选择枚举
*/
typedef enum GPIO_Mode_TypeDef {
GPIO_Mode_IN = 0,
GPIO_Mode_OUT = 1,
GPIO_Mode_ANA = 2,
GPIO_Mode_IO = 3,
}} {enum_name};
/**
* @brief GPIO配置函数
* @param GPIOx: GPIO端口
* @param GPIO_PinSource: GPIO引脚
* @param mode: GPIO模式
* @param GPIO_AF_x: GPIO复用功能选择
*/
void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
{
GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = 1 << GPIO_PinSource;
GPIO_InitStructure.GPIO_Mode = mode;
if (GPIO_AF_x == GPIO_AF_I2C)
{
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
}
else
{
__NOP();
}
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 读取指定GPIO引脚的输入数据位
* @param GPIOx GPIO模块指针
* @param GPIO_Pin 要读取的引脚
* @return 引脚的输入数据位状态
*/
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
return (GPIOx->PDI & GPIO_Pin) ? 1 : 0;
}
/**
* @brief 配置GPIO引脚复用功能
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource 引脚源
* @param GPIO_AF 复用功能选择
*/
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
uint8_t offset;
uint8_t pins = GPIO_PinSource >> 2;
offset = ((GPIO_PinSource & 0x3) * 4);
switch (pins)
{
case 0:
GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 1:
GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 2:
GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 3:
GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
break;
default:
break;
}
}
11.01_HALL-HALL状态值和IO对应关系
测试步骤
给hall口提供电压,记录输入到IO的电平以及hall状态。
| GPIO | AF |
|---|---|
| P0.5 | HALL_IN0 |
| P0.6 | HALL_IN1 |
| P0.7 | HALL_IN2 |
实测数据
| t | HALL_IN0 | HALL_IN1 | HALL_IN2 | hall_val | hall_fltval |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 |
| 1 | 1 | 0 | 0 | 1 | 1 |
| 2 | 0 | 1 | 0 | 2 | 2 |
| 3 | 1 | 1 | 0 | 3 | 3 |
| 4 | 0 | 0 | 1 | 4 | 4 |
| 5 | 1 | 0 | 1 | 5 | 5 |
| 6 | 0 | 1 | 1 | 6 | 6 |
| 7 | 1 | 1 | 1 | 7 | 7 |
| 8 | 0 | 0 | 0 | 0 | 0 |
| 9 | 1 | 0 | 0 | 1 | 1 |
| 10 | 0 | 1 | 0 | 2 | 2 |
| 11 | 1 | 1 | 0 | 3 | 3 |
| 12 | 0 | 0 | 1 | 4 | 4 |
| 13 | 1 | 0 | 1 | 5 | 5 |
参考代码
11.01_HALL_Count.c
/**
* @brief HALL-HALL状态值和IO对应关系
* @details
* 给hall口提供电压,记录输入到IO的电平以及hall状态。
* |GPIO|AF|
* |-|-|
* |P0.5|HALL_IN0|
* |P0.6|HALL_IN1|
* |P0.7|HALL_IN2|
*
*/
#include "lks32mc09x_gpio.h"
#include "lks32mc09x_hall.h"
volatile uint32_t hall_val; // 当前霍尔值
volatile uint32_t hall_fltval; // 滤波后的霍尔值
/**
* @brief 主函数,用于测试HSI时钟输出及GPIO翻转
*/
int main(void)
{
GPIO_Config(GPIO0, GPIO_PinSource_5, GPIO_Mode_IN, GPIO_AF_HALL); // HALL_IN0
GPIO_Config(GPIO0, GPIO_PinSource_6, GPIO_Mode_IN, GPIO_AF_HALL); // HALL_IN1
GPIO_Config(GPIO0, GPIO_PinSource_7, GPIO_Mode_IN, GPIO_AF_HALL); // HALL_IN2
HALL_InitTypeDef HALL_InitStruct;
HALL_StructInit(&HALL_InitStruct);
HALL_InitStruct.FilterLen = 10; /* Hall信号数字滤波长度 512个时钟周期 */
HALL_InitStruct.ClockDivision = HALL_CLK_DIV1; /* 设置Hall模块时钟分频系数 */
HALL_InitStruct.Filter75_Ena = ENABLE; /* Hall信号滤波方式,7判5模式或者全1有效模式 */
HALL_InitStruct.HALL_Ena = ENABLE; /* 模块使能 */
HALL_InitStruct.Capture_IRQ_Ena = ENABLE; /* 捕捉中断使能 */
HALL_InitStruct.OverFlow_IRQ_Ena = ENABLE; /* 超时中断使能 */
HALL_InitStruct.CountTH = 960000; /* Hall模块计数模值,计数超过模值会产生超时中断 */
HALL_InitStruct.softIE = ENABLE; /* 软件中断失能 */
HALL_Init(&HALL_InitStruct); /* HALL初化 */
HALL_Cmd(ENABLE); /* HALL使能 */
while (1)
{
hall_val = HALL_GetCaptureValue();
hall_fltval = HALL_GetFilterValue();
}
}
使用到的库函数
库函数部分代码
#define GPIO_AF_I2C 6
#define HALL0 ((HALL_TypeDef *)HALL0_BASE)
#define HALL_CAPTURE_EVENT ((uint32_t)0x00010000)
#define HALL_OVERFLOW_EVENT ((uint32_t)0x00020000)
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_GPIO BIT11
#define SYS_MODULE_HALL0 BIT3
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* @brief GPIO模式选择枚举
*/
typedef enum GPIO_Mode_TypeDef {
GPIO_Mode_IN = 0,
GPIO_Mode_OUT = 1,
GPIO_Mode_ANA = 2,
GPIO_Mode_IO = 3,
}} {enum_name};
/**
* @brief HALL初始化结构体句柄,初始化时请定义该句柄,并用其它地址来传参
*/
typedef struct HALL_InitTypeDef {
uint32_t CountTH;
uint16_t FilterLen;
uint8_t ClockDivision;
uint8_t Filter75_Ena;
uint8_t HALL_Ena;
uint8_t Capture_IRQ_Ena;
uint8_t OverFlow_IRQ_Ena;
uint8_t softIE;
};
/**
* @brief GPIO配置函数
* @param GPIOx: GPIO端口
* @param GPIO_PinSource: GPIO引脚
* @param mode: GPIO模式
* @param GPIO_AF_x: GPIO复用功能选择
*/
void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
{
GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = 1 << GPIO_PinSource;
GPIO_InitStructure.GPIO_Mode = mode;
if (GPIO_AF_x == GPIO_AF_I2C)
{
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
}
else
{
__NOP();
}
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**
* @brief 使能或失能HALL模块
* @param state: 功能状态,ENABLE或DISABLE
*/
void HALL_Cmd(uint8_t state)
{
if (state == ENABLE)
{
HALL0->CFG |= (1 << 24); // 使能HALL模块
}
else
{
HALL0->CFG &= ~(1 << 24); // 失能HALL模块
}
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 初始化HALL模块
* @param HALL_InitStruct: 指向HALL_InitTypeDef结构体的指针
*/
void HALL_Init(HALL_InitTypeDef *HALL_InitStruct)
{
if (HALL_InitStruct != 0)
{
// 使能HALL模块时钟
SYS_ModuleClockCmd(SYS_MODULE_HALL0, ENABLE);
// 配置HALL寄存器
HALL0->CFG &= ~(0x00FFFFFF); // 清除相关位
HALL0->CFG |= (HALL_InitStruct->Filter75_Ena << 20) |
(HALL_InitStruct->ClockDivision << 16) |
(HALL_InitStruct->FilterLen & 0xFFFF);
HALL0->TH = HALL_InitStruct->CountTH;
// 配置中断使能
HALL0->CFG &= ~(HALL_CAPTURE_EVENT | HALL_OVERFLOW_EVENT);
HALL0->CFG |= (HALL_InitStruct->Capture_IRQ_Ena << 28) |
(HALL_InitStruct->OverFlow_IRQ_Ena << 29) |
(HALL_InitStruct->softIE << 30);
// 使能HALL模块
HALL0->CFG |= (HALL_InitStruct->HALL_Ena << 24);
}
else
{
__NOP();
}
}
/**
* @brief 初始化HALL结构体
* @param HALL_InitStruct: 指向HALL_InitTypeDef结构体的指针
*/
void HALL_StructInit(HALL_InitTypeDef *HALL_InitStruct)
{
if (HALL_InitStruct != 0)
{
HALL_InitStruct->CountTH = 0;
HALL_InitStruct->FilterLen = 0;
HALL_InitStruct->ClockDivision = 0;
HALL_InitStruct->Filter75_Ena = DISABLE;
HALL_InitStruct->HALL_Ena = DISABLE;
HALL_InitStruct->Capture_IRQ_Ena = DISABLE;
HALL_InitStruct->OverFlow_IRQ_Ena = DISABLE;
HALL_InitStruct->softIE = DISABLE;
}
else
{
__NOP();
}
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 读取未滤波的HALL值
* @return 未滤波的HALL值
*/
uint32_t HALL_GetCaptureValue(void)
{
return (HALL0->INFO >> 8) & 0x07;
}
/**
* @brief 读取滤波后的HALL值
* @return 滤波后的HALL值
*/
uint32_t HALL_GetFilterValue(void)
{
return (HALL0->INFO & 0x07);
}
/**
* @brief 配置GPIO引脚复用功能
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource 引脚源
* @param GPIO_AF 复用功能选择
*/
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
uint8_t offset;
uint8_t pins = GPIO_PinSource >> 2;
offset = ((GPIO_PinSource & 0x3) * 4);
switch (pins)
{
case 0:
GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 1:
GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 2:
GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 3:
GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
break;
default:
break;
}
}
13.01_I2C-读写EEPROM
测试步骤
先后向eeprom里写入两次数据, 写入完成后从 eeprom 里都回来 判断写入的数据和读回来的是否一致
实测数据
测试通过,回读数据一致
参考代码
13.01_I2C_MasterSend.c
/**
* @brief I2C-读写EEPROM
* @details
* 先后向eeprom里写入两次数据,
* 写入完成后从 eeprom 里都回来
* 判断写入的数据和读回来的是否一致
*/
#include "lks32mc09x_gpio.h"
#include "lks32mc09x_i2c.h"
void WriteData(uint8_t eepromaddr, u16 taddr, u8 *data, uint32_t len);
void ReadData(uint8_t eepromaddr, u16 taddr, u8 *data, uint32_t len);
void SoftDelaymS(uint32_t n);
volatile uint32_t testflg = 0; // 测试状态,0空闲 1进行中 2写入数据&读回数据一致 3写入数据&读回数据不一致
volatile uint32_t errline = 0; // 测试状态,0空闲 1进行中 2写入数据&读回数据一致 3写入数据&读回数据不一致
uint8_t read_data[32];
uint8_t read_data1[32];
uint8_t write_data[32] = {0};
#define EEPROMADDR 0xa0
/**
* @brief 主函数,用于测试HSI时钟输出及GPIO翻转
*/
int main(void)
{
GPIO_Config(GPIO0, GPIO_PinSource_3, GPIO_Mode_IO, GPIO_AF_I2C);
GPIO_Config(GPIO0, GPIO_PinSource_4, GPIO_Mode_IO, GPIO_AF_I2C);
{
I2C_InitTypeDef I2C_InitStruct;
I2C_StructInit(&I2C_InitStruct);
I2C_InitStruct.ADRCMP = DISABLE; // I2C 硬件地址比较使能开关,只有在 DMA 模式下开启才有效。
I2C_InitStruct.MST_MODE = ENABLE; // I2C 主模式使能
I2C_InitStruct.SLV_MODE = DISABLE; // I2C 从模式使能
I2C_InitStruct.BaudRate = 10000; // I2C 波特率
I2C_InitStruct.IE = ENABLE; // I2C 中断使能
I2C_InitStruct.TC_IE = ENABLE; // I2C 数据传输完成中断使能
I2C_InitStruct.BUS_ERR_IE = ENABLE; // I2C 总线错误事件中断使能
I2C_InitStruct.STOP_IE = ENABLE; // I2C STOP 事件中断使能
I2C_InitStruct.BURST_NACK = ENABLE; // I2C 传输,NACK 事件中断使能
I2C_InitStruct.BURST_ADDR_CMP = DISABLE; // I2C 传输,硬件地址匹配中断使能
I2C_Init(I2C0, &I2C_InitStruct);
}
NVIC_EnableIRQ(I2C0_IRQn); /* 使能I2C中断*/
NVIC_SetPriority(I2C0_IRQn, 1); /* I2C中断优先级配置*/
__enable_irq(); /* 开启总中断 */
for (;;)
{
if (testflg == 1) // 开始写入数据
{
for (int len = 1; len <= 32; len++) // 数据长度从1到32,以确保不同数据长度下i2c通信都正常
{
for (int i = 0; i < len; i++)
{
write_data[i] = i + len; // 确保每次写入的数据都不一样
}
WriteData(EEPROMADDR, 0, write_data, len);
SoftDelaymS(10);
ReadData(EEPROMADDR, 0, read_data, len);
for (int i = 0; i < len; i++)
{
if (read_data[i] != write_data[i])
{
if (errline == 0)
{
errline = i;
}
testflg = 3;
}
}
}
if (testflg != 3)
{
testflg = 2; // 测试完成,并且没有出现错误
}
}
}
}
void WriteData(uint8_t eepromaddr, u16 taddr, u8 *data, uint32_t len)
{
int i;
static uint8_t txdata[34];
if (len > 32) // 一次最多写32个
{
len = 32;
}
txdata[0] = taddr >> 8;
txdata[1] = taddr;
for (i = 0; i < len; i++)
{
txdata[i + 2] = data[i];
}
for (i = 0; i < 10; i++) // 如果收到nack 则最多尝试发送10次
{
I2C_TableSend(I2C0, eepromaddr, txdata, len + 2);
while (I2C_TableState(I2C0))
; // 等待发送完成
if (I2C_TableAck(I2C0) == 0)
{
return;
}
}
return;
}
void ReadData(uint8_t eepromaddr, u16 taddr, u8 *data, uint32_t len)
{
uint8_t txdata[2];
int i;
uint16_t val;
for (i = 0; i < 10; i++) // 如果收到nack 则最多尝试发送10次
{
txdata[0] = taddr >> 8;
txdata[1] = taddr;
I2C_TableSend(I2C0, eepromaddr, txdata, 2); // 写地址
I2C_TableRead(I2C0, eepromaddr, data, len); // 读数据
while (I2C_TableState(I2C0))
; // 等待发送完成
if (I2C_TableAck(I2C0) == 0)
{
return;
}
}
return;
}
void I2C0_IRQHandler(void)
{
I2C_TableIrq(I2C0);
}
使用到的库函数
库函数部分代码
#define GPIO_AF_I2C 6
#define I2C I2C0
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_CLK_CFG_CLK_DIV_MASK (uint32_t)(0xFF << SYS_CLK_CFG_CLK_DIV_POS)
#define SYS_CLK_CFG_CLK_DIV_POS 0
#define SYS_CLK_CFG_CLK_SEL_MASK (uint32_t)(0x3 << SYS_CLK_CFG_CLK_SEL_POS)
#define SYS_CLK_CFG_CLK_SEL_POS 8
#define SYS_MODULE_GPIO BIT11
#define SYS_MODULE_I2C0 BIT1
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* @brief GPIO模式选择枚举
*/
typedef enum GPIO_Mode_TypeDef {
GPIO_Mode_IN = 0,
GPIO_Mode_OUT = 1,
GPIO_Mode_ANA = 2,
GPIO_Mode_IO = 3,
}} {enum_name};
typedef struct I2C_InitTypeDef {
uint32_t ADRCMP;
uint32_t ADDR;
uint32_t MST_MODE;
uint32_t SLV_MODE;
uint32_t BUSRT_EN;
uint32_t BaudRate;
uint32_t IE;
uint32_t TC_IE;
uint32_t BUS_ERR_IE;
uint32_t STOP_IE;
uint32_t BURST_NACK;
uint32_t BURST_ADDR_CMP;
};
/**
* @brief GPIO配置函数
* @param GPIOx: GPIO端口
* @param GPIO_PinSource: GPIO引脚
* @param mode: GPIO模式
* @param GPIO_AF_x: GPIO复用功能选择
*/
void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
{
GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = 1 << GPIO_PinSource;
GPIO_InitStructure.GPIO_Mode = mode;
if (GPIO_AF_x == GPIO_AF_I2C)
{
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
}
else
{
__NOP();
}
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**
* @brief I2C_SendTable和I2C_ReadTable对应的中断处理函数
*/
void I2C_TableIrq(I2C_TypeDef *I2Cx)
{
if (I2Cx->SCR & BIT1)
{
I2C_TableVariable[0].nackflg = 1;
}
else
{
__NOP();
}
if (I2Cx->SCR & BIT5)
{
I2C_TableVariable[0].state = 0;
I2Cx->SCR = 0;
}
else if (I2C_TableVariable[0].state == 1)
{
if (I2Cx->SCR & BIT3)
{
I2Cx->SCR = 0;
}
else
{
I2Cx->DATA = 0xff;
*I2C_TableVariable[0].rxdata = I2Cx->DATA;
I2C_TableVariable[0].rxdatap--;
if (I2C_TableVariable[0].rxdatap != 0)
{
I2C_TableVariable[0].rxdata++;
I2Cx->SCR = BIT4;
}
else
{
I2Cx->SCR = 0;
}
}
}
else if (I2C_TableVariable[0].state == 2)
{
if (I2C_TableVariable[0].txdatap != 0)
{
I2Cx->DATA = *I2C_TableVariable[0].txdata;
I2C_TableVariable[0].txdata++;
I2C_TableVariable[0].txdatap--;
I2Cx->SCR = BIT2;
}
else
{
I2Cx->SCR = 0;
}
}
else
{
I2Cx->SCR = 0;
}
}
/**
* @brief I2C初始化
* @param I2Cx: I2C外设指针
* @param I2C_InitStruct: I2C初始化结构体指针
*/
void I2C_Init(I2C_TypeDef *I2Cx, I2C_InitTypeDef *I2C_InitStruct)
{
SYS_ModuleClockCmd(SYS_MODULE_I2C0, ENABLE);
SYS_SoftResetModule(SYS_MODULE_I2C0);
I2C_SetBaudRate(I2Cx, I2C_InitStruct->BaudRate);
I2Cx->ADDR = (I2C_InitStruct->ADRCMP << 7);
I2Cx->CFG = (I2C_InitStruct->IE << 7) | (I2C_InitStruct->TC_IE << 6) |
(I2C_InitStruct->BUS_ERR_IE << 5) | (I2C_InitStruct->STOP_IE << 4) |
(I2C_InitStruct->MST_MODE << 1) | (I2C_InitStruct->SLV_MODE);
I2Cx->SCR = 0;
I2Cx->DATA = 0;
I2Cx->MSCR = 0;
I2Cx->BCR = (I2C_InitStruct->BURST_NACK << 7) | (I2C_InitStruct->BURST_ADDR_CMP << 6) |
(I2C_InitStruct->BUSRT_EN << 5);
}
/**
* @brief I2C时钟分频
* @param div 分频系数
*/
void SYS_I2CClkDiv(uint16_t div)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
REG_WRITE(SYS0->CLK_DIV0, div);
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief I2C配置结构体初始化
* @param I2C_InitStruct: I2C初始化结构体指针
*/
void I2C_StructInit(I2C_InitTypeDef *I2C_InitStruct)
{
for (int i = 0; i < sizeof(I2C_InitTypeDef) / sizeof(uint32_t); i++)
{
((uint32_t *)I2C_InitStruct)[i] = 0;
}
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 向从机发送数据 (需要在中断里执行I2C_TableIrq)
* @param I2Cx I2C外设指针
* @param taddr 从机地址
* @param data 数据指针
* @param len 数据长度
* @return 状态,0成功 1失败(i2c总线非空闲,或上一次数据尚未发送完毕)
*/
int I2C_TableSend(I2C_TypeDef *I2Cx, u8 taddr, u8 *data, u8 len)
{
I2C_TableVariable[0].nackflg = 0;
while (I2C_TableVariable[0].state != 0);
I2C->SCR = 0;
I2C_TableVariable[0].txdata = data;
I2C_TableVariable[0].txdatap = len;
I2C_TableVariable[0].state = 2;
I2C->DATA = taddr;
I2C->MSCR = 1; // 触发I2C发送数据
return 0;
}
/**
* @brief 向从机读取数据 (需要在中断里执行I2C_TableIrq)
* @param I2Cx I2C外设指针
* @param taddr 从机地址
* @param data 数据指针
* @param len 数据长度
* @return 状态,0成功 1失败(i2c总线非空闲,或上一次数据尚未发送完毕)
*/
int I2C_TableRead(I2C_TypeDef *I2Cx, u8 taddr, u8 *data, u8 len)
{
I2C_TableVariable[0].nackflg = 0;
while (I2C_TableVariable[0].state != 0);
I2C->SCR = 0;
I2C_TableVariable[0].rxdata = data;
I2C_TableVariable[0].rxdatap = len;
I2C_TableVariable[0].state = 1;
I2C->DATA = taddr | 1;
I2C->MSCR = 1; // 触发I2C发送数据
return 0;
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 获取I2C_Table发送后的ack电平
* @return 收到的ACK电平
*/
int I2C_TableAck(I2C_TypeDef *I2Cx)
{
return I2C_TableVariable[0].nackflg;
}
/**
* @brief 获取I2C_Table发送状态
* @return 状态,0空闲 1正在发送或接收数据
*/
int I2C_TableState(I2C_TypeDef *I2Cx)
{
return I2C_TableVariable[0].state;
}
/**
* @brief 获取当前MCU时钟
* @return 当前MCU时钟频率
*/
uint32_t SYS_ReadMcuClk(void)
{
uint32_t clk = 96000000;
uint32_t clkdiv = (SYS_CLK_CFG & SYS_CLK_CFG_CLK_DIV_MASK) >> SYS_CLK_CFG_CLK_DIV_POS;
uint32_t clksel = (SYS_CLK_CFG & SYS_CLK_CFG_CLK_SEL_MASK) >> SYS_CLK_CFG_CLK_SEL_POS;
switch (clksel) // 0: HRC 1: PLL 2: LRC
{
case 0: // HRC
clk = 8000000;
break;
case 1: // PLL
switch (clkdiv)
{
case 0xff: // 96M
clk = 96000000;
break;
case 0x55: // 48M
clk = 48000000;
break;
case 0x11: // 24M
clk = 24000000;
break;
case 0x01: // 12M
clk = 12000000;
break;
case 0x00: // 12M
clk = 12000000;
break;
default: // 逐位计算
clk = 0;
for (int i = 0; i < 8; i++)
{
if (clkdiv & (1 << i))
{
clk += 12000000;
}
else
{
__NOP();
}
}
break;
}
break;
case 2: // LRC
clk = 32000;
break;
default:
break;
}
return clk;
}
/**
* @brief 设置I2C波特率
* @param BaudRate: 波特率值
*/
void I2C_SetBaudRate(I2C_TypeDef *I2Cx, uint32_t BaudRate)
{
// 根据系统时钟和波特率计算分频值
u32 div;
div = ((u32)SYS_ReadMcuClk() + ((BaudRate * 17) >> 1)) / (BaudRate * 17) - 1;
SYS_I2CClkDiv(div);
}
/**
* @brief 软复位模块
* @param nModule 模块编号
*/
void SYS_SoftResetModule(uint32_t nModule)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
REG_SET(SYS0->SFT_RST, nModule);
REG_RESET(SYS0->SFT_RST, nModule); // 触发复位后需清除复位位
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 配置GPIO引脚复用功能
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource 引脚源
* @param GPIO_AF 复用功能选择
*/
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
uint8_t offset;
uint8_t pins = GPIO_PinSource >> 2;
offset = ((GPIO_PinSource & 0x3) * 4);
switch (pins)
{
case 0:
GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 1:
GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 2:
GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 3:
GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
break;
default:
break;
}
}
14.01_IWDG-看门狗复位时间
测试步骤
- 配置看门狗2S进行复位,P0.6作为指示IO。
- 初始化完成之后0.6输出低电平
- no_feed_dog_flg 等于0时正常喂狗,等于1时将0.6设置为高电平,并停止喂狗。
- feed_dog_time_mS 不等于-1时会更新喂狗时间为feed_dog_time_mS毫秒
- 给P0.6接一个下拉(芯片复位后所有IO都是低电平,看门狗的复位到GPIO初始化需要大约2mS)。
- 读取P0.6的高电平时间即可确认实际看门狗复位时间。
实测数据
看门狗最小复位时间为8mS,最大复位时间为64S。
误差主要由量化误差和LRC时钟本身的误差决定。
参考代码
14.01_IWDG_Test.c
/**
* @brief IWDG-看门狗复位时间
* @details
* 1. 配置看门狗2S进行复位,P0.6作为指示IO。
* 2. 初始化完成之后0.6输出低电平
* 3. no_feed_dog_flg 等于0时正常喂狗,等于1时将0.6设置为高电平,并停止喂狗。
* 4. feed_dog_time_mS 不等于-1时会更新喂狗时间为feed_dog_time_mS毫秒
* 5. 给P0.6接一个下拉(芯片复位后所有IO都是低电平,看门狗的复位到GPIO初始化需要大约2mS)。
* 6. 读取P0.6的高电平时间即可确认实际看门狗复位时间。
*/
#include "lks32mc09x_gpio.h"
#include "lks32mc09x_iwdg.h"
volatile int32_t no_feed_dog_flg = 0;
volatile int32_t feed_dog_time_mS = -1;
/**
* @brief 主函数
*/
int main(void)
{
// IWDG初始化结构体
IWDG_InitTypeDef IWDG_InitStruct;
// 硬件初始化
// 初始化GPIO
GPIO_ResetBits(GPIO0, GPIO_Pin_6);
GPIO_Config(GPIO0, GPIO_PinSource_6, GPIO_Mode_OUT, GPIO_AF_GPIO);
// 初始化IWDG
IWDG_StrutInit(&IWDG_InitStruct);
IWDG_InitStruct.WDG_EN = 1;
IWDG_InitStruct.WTH = IWDG_SECOND2IWDGCNT(2); // 设置看门狗定时唤醒门限值为2秒
IWDG_Init(&IWDG_InitStruct);
IWDG_ENABLE(); // 使能看门狗
while (1)
{
if (feed_dog_time_mS != -1)
{
IWDG_Update(IWDG_SECOND2IWDGCNT((float)feed_dog_time_mS / 1000.0f));
feed_dog_time_mS = -1;
}
if (no_feed_dog_flg == 0)
{
IWDG_Feed();
}
else
{
GPIO_SetBits(GPIO0, GPIO_Pin_6);
}
}
}
使用到的库函数
库函数部分代码
#define GPIO_AF_I2C 6
#define IWDG IWDG0
#define IWDG_SECOND2IWDGCNT(x) IWDG_SECOND2IWDGCNT_SAT((int32_t)(32000 * (float)x + 0x40), 0x100, 0x1fff00)
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_GPIO BIT11
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* @brief GPIO模式选择枚举
*/
typedef enum GPIO_Mode_TypeDef {
GPIO_Mode_IN = 0,
GPIO_Mode_OUT = 1,
GPIO_Mode_ANA = 2,
GPIO_Mode_IO = 3,
}} {enum_name};
/**
* @brief IWDG初始化结构体句柄
*/
typedef struct IWDG_InitTypeDef {
uint32_t DWK_EN;
uint32_t WDG_EN;
uint32_t WTH;
uint32_t RTH;
};
/**
* @brief GPIO配置函数
* @param GPIOx: GPIO端口
* @param GPIO_PinSource: GPIO引脚
* @param mode: GPIO模式
* @param GPIO_AF_x: GPIO复用功能选择
*/
void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
{
GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = 1 << GPIO_PinSource;
GPIO_InitStructure.GPIO_Mode = mode;
if (GPIO_AF_x == GPIO_AF_I2C)
{
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
}
else
{
__NOP();
}
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**
* @brief IWDG初始化
* @param IWDG_InitStruct 指向IWDG初始化结构体的指针
*/
void IWDG_Init(IWDG_InitTypeDef *IWDG_InitStruct)
{
IWDG_InitStruct->RTH = (IWDG_InitStruct->RTH > BIT21) ? BIT21 - 1 : IWDG_InitStruct->RTH;
IWDG_InitStruct->WTH = (IWDG_InitStruct->WTH > BIT21) ? BIT21 - 1 : IWDG_InitStruct->WTH;
IWDG->CFG = (IWDG_InitStruct->DWK_EN << 4) | (IWDG_InitStruct->WDG_EN);
IWDG->PSW = PSW_IWDG_PRE;
IWDG->RTH = IWDG_InitStruct->RTH;
if (IWDG_InitStruct->RTH > IWDG_InitStruct->WTH)
{
IWDG->WTH = IWDG_InitStruct->RTH - IWDG_InitStruct->WTH;
}
else
{
IWDG->WTH = 0x001000;
}
IWDG->PSW = PSW_IWDG_PRE;
IWDG->CLR = PSW_IWDG_CLR;
}
/**
* @brief IWDG配置结构体初始化
* @param IWDG_InitStruct 指向IWDG初始化结构体的指针
*/
void IWDG_StrutInit(IWDG_InitTypeDef *IWDG_InitStruct)
{
IWDG_InitStruct->DWK_EN = DISABLE; // 深度休眠定时唤醒使能
IWDG_InitStruct->WDG_EN = DISABLE; // 独立看门狗使能
IWDG_InitStruct->WTH = IWDG_SECOND2IWDGCNT(1); // 看门狗定时唤醒时间,大于复位时间后无效
IWDG_InitStruct->RTH = IWDG_SECOND2IWDGCNT(2); // 看门狗超时复位时间(21位计数器,但低12恒位0)
// SECOND2IWDGCNT输入参数,秒
}
/**
* @brief 使能看门狗
*/
void IWDG_ENABLE(void)
{
IWDG->CFG = 0x1;
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 喂狗,重置看门狗计数器
*/
void IWDG_Feed(void)
{
IWDG->PSW = PSW_IWDG_PRE;
IWDG->CLR = PSW_IWDG_CLR;
}
/**
* @brief 复位指定的GPIO引脚位
* @param GPIOx GPIO模块指针
* @param GPIO_Pin 要复位的引脚
*/
void GPIO_ResetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
GPIOx->PDO &= ~GPIO_Pin;
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 设置指定的GPIO引脚位
* @param GPIOx GPIO模块指针
* @param GPIO_Pin 要设置的引脚
*/
void GPIO_SetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
GPIOx->PDO |= GPIO_Pin;
}
/**
* @brief 设置超时复位时间
* @param rth 超时复位时间,单位1/64k 秒,建议用 SECOND2IWDGCNT 宏将秒转换
*/
void IWDG_Update(uint32_t rth)
{
IWDG->PSW = PSW_IWDG_PRE;
IWDG->RTH = rth;
IWDG->CLR = PSW_IWDG_CLR;
}
/**
* @brief 配置GPIO引脚复用功能
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource 引脚源
* @param GPIO_AF 复用功能选择
*/
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
uint8_t offset;
uint8_t pins = GPIO_PinSource >> 2;
offset = ((GPIO_PinSource & 0x3) * 4);
switch (pins)
{
case 0:
GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 1:
GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 2:
GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 3:
GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
break;
default:
break;
}
}
15.01_MCPWM-三路中心对齐PWM
测试步骤
- 配置MCPWM模块以输出三路中心对称的PWM信号,频率为20kHz。
- 配置GPIO引脚复用为MCPWM功能。
- 在主循环中,保持MCPWM模块的运行,并可以通过调试接口输出PWM的频率和占空比。
实测数据
| 引脚 | 占空比 | 频率 |
|---|---|---|
| P1.4 | 31.28% | 19.96k |
| P1.5 | 52.15% | 19.96k |
| P1.6 | 31.25% | 19.96k |
| P1.7 | 52.13% | 19.96k |
| P1.8 | 31.25% | 19.97k |
| P1.9 | 52.14% | 19.97k |
参考代码
15.01_MCPWM.c
/**
* @brief MCPWM-三路中心对齐PWM
* @details
* 1. 配置MCPWM模块以输出三路中心对称的PWM信号,频率为20kHz。
* 2. 配置GPIO引脚复用为MCPWM功能。
* 3. 在主循环中,保持MCPWM模块的运行,并可以通过调试接口输出PWM的频率和占空比。
*/
#include "lks32mc09x_gpio.h"
#include "lks32mc09x_mcpwm.h"
/**
* @brief 配置MCPWM输出三路中心对称的PWM,频率20kHz。
*/
void MCPWM_Config(void)
{
MCPWM_InitTypeDef MCPWM_InitStruct;
// 初始化MCPWM结构体
MCPWM_StructInit(&MCPWM_InitStruct);
// 配置MCPWM基本项
MCPWM_InitStruct.EN = 1; // 使能MCPWM模块
MCPWM_InitStruct.CLK_DIV = MCPWM_CLK_DIV_1; // 时钟1分频
MCPWM_InitStruct.TH = 96000000 / (20000 * 2 + 1); // 计数器门限值,对应20kHz
MCPWM_InitStruct.TR = MCPWM_TR_T0; // 计数器溢出时刻触发自动更新
// 配置死区时间
MCPWM_InitStruct.DTHP = 500; // 上管死区时间
MCPWM_InitStruct.DTHN = 500; // 下管死区时间
// 配置IO输出
MCPWM_InitStruct.IO0 = MCPWM_IO_HL_PWM; // CH0: 上管高有效,下管低有效,正常输出PWM
MCPWM_InitStruct.IO1 = MCPWM_IO_HL_PWM; // CH1: 上管高有效,下管低有效,正常输出PWM
MCPWM_InitStruct.IO2 = MCPWM_IO_HL_PWM; // CH2: 上管高有效,下管低有效,正常输出PWM
MCPWM_InitStruct.IO3 = MCPWM_IO_DISABLE; // CH3: 不使用
MCPWM_InitStruct.FAIL_IO = MCPWM_FAIL_IO_OFF;
// 配置自动更新使能
MCPWM_InitStruct.AUEN = MCPWM_AUEN_DEFAULT; // 使能TH0自动加载
// 配置中断和DMA(本例程中不使用中断和DMA)
MCPWM_InitStruct.IE = 0;
MCPWM_InitStruct.EIE = 0;
MCPWM_InitStruct.RE = 0;
// 初始化MCPWM模块
MCPWM_Init(MCPWM0, &MCPWM_InitStruct);
// 设置输出状态
MCPWM_SetOutputState(MCPWM0, 1);
// 设置PWM输出值,中心对齐模式下占空比50%
MCPWM_SetOutputVal(MCPWM0, 0, -1000, 1000); // CH0: 中心对齐,占空比50%
MCPWM_SetOutputVal(MCPWM0, 1, -1000, 1000); // CH1: 中心对齐,占空比50%
MCPWM_SetOutputVal(MCPWM0, 2, -1000, 1000); // CH2: 中心对齐,占空比50%
// 开始计数
MCPWM_StartCount(MCPWM0);
}
/**
* @brief 主函数
*/
int main(void)
{
// 配置GPIO引脚复用为MCPWM功能
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_StructInit(&GPIO_InitStruct);
// 配置P1.4到P1.9为MCPWM功能
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIO1, &GPIO_InitStruct);
// 配置引脚复用功能为MCPWM
GPIO_PinAFConfig(GPIO1, GPIO_PinSource_4, GPIO_AF_MCPWM);
GPIO_PinAFConfig(GPIO1, GPIO_PinSource_5, GPIO_AF_MCPWM);
GPIO_PinAFConfig(GPIO1, GPIO_PinSource_6, GPIO_AF_MCPWM);
GPIO_PinAFConfig(GPIO1, GPIO_PinSource_7, GPIO_AF_MCPWM);
GPIO_PinAFConfig(GPIO1, GPIO_PinSource_8, GPIO_AF_MCPWM);
GPIO_PinAFConfig(GPIO1, GPIO_PinSource_9, GPIO_AF_MCPWM);
// 配置MCPWM输出三路中心对称的PWM,频率20kHz
MCPWM_Config();
// 主循环
while (1)
{
// 主循环中可以添加其他操作
}
}
使用到的库函数
库函数部分代码
#define MCPWM_COUNT_START BIT31
#define MCPWM_IO_NP BIT0
#define MCPWM_IO_PN_SW BIT6
#define MCPWM_IO_PP BIT1
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_GPIO BIT11
#define SYS_MODULE_MCPWM0 BIT10
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
typedef struct MCPWM_InitTypeDef {
uint16_t EN;
uint32_t COUNT;
uint16_t CLK_DIV;
uint16_t TH;
uint16_t TR;
int16_t TMR0;
int16_t TMR1;
int16_t TMR2;
int16_t TMR3;
uint16_t DTHP;
uint16_t DTHN;
uint16_t FLT_DIV;
uint16_t IO0;
uint16_t IO1;
uint16_t IO2;
uint16_t IO3;
uint32_t AUEN;
uint32_t IE;
uint32_t EIE;
uint32_t RE;
uint32_t FAIL_IO;
uint32_t FAIL0;
uint32_t FAIL1;
};
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 初始化MCPWM模块
* @param MCPWMx MCPWM模块指针
* @param MCPWM_InitStruct 指向包含初始化参数的MCPWM_InitTypeDef结构体
*/
void MCPWM_Init(MCPWM_TypeDef *MCPWMx, MCPWM_InitTypeDef *MCPWM_InitStruct)
{
// 复位MCPWM模块
MCPWM_Reset(MCPWMx);
if (MCPWM_InitStruct->EN)
{
// 使能MCPWM模块
MCPWM_Enable(MCPWMx);
// 初始化MCPWM模块
MCPWMx->PRT = 0xdead; // mcpwm模块解锁
if (MCPWM_InitStruct->COUNT & MCPWM_COUNT_START)
{
MCPWMx->TCLK = BIT6 | (MCPWM_InitStruct->CLK_DIV << 12) | BIT2;
}
else
{
if (MCPWM_InitStruct->COUNT != 0)
{
MCPWMx->TCLK = ((MCPWMx->TCLK & (~BIT6)) | BIT8) | (MCPWM_InitStruct->CLK_DIV << 12) | BIT2; // 关闭计数器,并打开外部触发
MCPWMx->EVT0 = MCPWM_InitStruct->COUNT; // 设置外部触发信号
}
else
{
MCPWMx->TCLK = (MCPWM_InitStruct->CLK_DIV << 12) | BIT2;
}
}
{
uint16_t chdef = 0;
chdef = MCPWM_InitStruct->FAIL_IO;
if (MCPWM_InitStruct->IO0 & MCPWM_IO_NP)
{
chdef ^= BIT0;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO0 & MCPWM_IO_PP)
{
chdef ^= BIT1;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO1 & MCPWM_IO_NP)
{
chdef ^= BIT2;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO1 & MCPWM_IO_PP)
{
chdef ^= BIT3;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO2 & MCPWM_IO_NP)
{
chdef ^= BIT4;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO2 & MCPWM_IO_PP)
{
chdef ^= BIT5;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO3 & MCPWM_IO_NP)
{
chdef ^= BIT6;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO3 & MCPWM_IO_PP)
{
chdef ^= BIT7;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO0 & MCPWM_IO_PN_SW)
{
// 交换chdef的bit0和bit1
chdef = (chdef & 0xfffe) | ((chdef & 0x0001) << 1) | ((chdef & 0x0002) >> 1);
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO1 & MCPWM_IO_PN_SW)
{
// 交换chdef的bit2和bit3
chdef = (chdef & 0xfffc) | ((chdef & 0x0004) << 1) | ((chdef & 0x0008) >> 1);
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO2 & MCPWM_IO_PN_SW)
{
// 交换chdef的bit4和bit5
chdef = (chdef & 0xfff0) | ((chdef & 0x0010) << 1) | ((chdef & 0x0020) >> 1);
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO3 & MCPWM_IO_PN_SW)
{
// 交换chdef的bit6和bit7
chdef = (chdef & 0xff00) | ((chdef & 0x0040) << 1) | ((chdef & 0x0080) >> 1);
}
else
{
__NOP();
}
MCPWMx->CH_DEF = chdef;
}
MCPWMx->TH00 = 0;
MCPWMx->TH01 = 0;
MCPWMx->TH10 = 0;
MCPWMx->TH11 = 0;
MCPWMx->TH20 = 0;
MCPWMx->TH21 = 0;
MCPWMx->TH30 = 0;
MCPWMx->TH31 = 0;
MCPWMx->CNT0 = -MCPWM_InitStruct->TH;
MCPWMx->TH0 = MCPWM_InitStruct->TH;
MCPWMx->TMR0 = MCPWM_InitStruct->TMR0;
MCPWMx->TMR1 = MCPWM_InitStruct->TMR1;
MCPWMx->TMR2 = MCPWM_InitStruct->TMR2;
MCPWMx->TMR3 = MCPWM_InitStruct->TMR3;
MCPWMx->DTH00 = MCPWM_InitStruct->DTHP;
MCPWMx->DTH01 = MCPWM_InitStruct->DTHN;
MCPWMx->FLT = MCPWM_InitStruct->FLT_DIV;
MCPWMx->IO01 = MCPWM_InitStruct->IO0 | (MCPWM_InitStruct->IO1 << 8);
MCPWMx->IO23 = MCPWM_InitStruct->IO2 | (MCPWM_InitStruct->IO3 << 8);
MCPWMx->CH_FAIL = MCPWM_InitStruct->FAIL0 | MCPWM_InitStruct->FAIL1;
MCPWMx->AUEN = MCPWM_InitStruct->AUEN;
MCPWMx->IE0 = MCPWM_InitStruct->IE;
MCPWMx->EIE = MCPWM_InitStruct->EIE;
MCPWMx->RE = MCPWM_InitStruct->RE;
MCPWMx->SDCFG = MCPWM_InitStruct->TR;
// 更新所有存在影子寄存器的寄存器
MCPWMx->UPDATE = 0xffffffff;
MCPWMx->IF0 = 0xffff;
MCPWMx->EIF = 0xffff;
MCPWMx->PRT = 0x0000; // mcpwm模块上锁
}
else
{
// 失能MCPWM模块
MCPWM_Disable(MCPWMx);
}
}
/**
* @brief 将MCPWM_InitTypeDef结构体初始化为默认值
* @param MCPWM_InitStruct 指向要初始化的MCPWM_InitTypeDef结构体
*/
void MCPWM_StructInit(MCPWM_InitTypeDef *MCPWM_InitStruct)
{
for (int i = 0; i < sizeof(MCPWM_InitTypeDef); i++)
{
((uint8_t *)MCPWM_InitStruct)[i] = 0;
}
}
/**
* @brief 开始计数
* @param MCPWMx MCPWM模块
* @param count 计数器初始值
*/
void MCPWM_StartCount(MCPWM_TypeDef *MCPWMx)
{
MCPWMx->PRT = 0xdead; // mcpwm模块解锁
// MCPWMx->UPDATE = BIT11;
MCPWMx->TCLK |= BIT6;
MCPWMx->PRT = 0x0000; // mcpwm模块上锁
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 模块使能
* @param MCPWMx MCPWM模块
*/
void MCPWM_Enable(MCPWM_TypeDef *MCPWMx)
{
SYS_ModuleClockCmd(SYS_MODULE_MCPWM0, ENABLE);
MCPWMx->PRT = 0xdead;
MCPWMx->TCLK |= BIT2;
MCPWMx->PRT = 0;
}
/**
* @brief 模块复位
* @param MCPWMx MCPWM模块
*/
void MCPWM_Reset(MCPWM_TypeDef *MCPWMx)
{
SYS_SoftResetModule(SYS_MODULE_MCPWM0);
}
/**
* @brief 模块失能
* @param MCPWMx MCPWM模块
*/
void MCPWM_Disable(MCPWM_TypeDef *MCPWMx)
{
MCPWMx->PRT = 0xdead;
MCPWMx->TCLK &= ~BIT2;
MCPWMx->PRT = 0;
SYS_ModuleClockCmd(SYS_MODULE_MCPWM0, DISABLE);
}
/**
* @brief 设置MCPWM输出值
* @param MCPWMx MCPWM模块
* @param chn 通道号 0-3
* @param p 上管打开时间
* @param n 下管打开时间
*/
void MCPWM_SetOutputVal(MCPWM_TypeDef *MCPWMx, uint8_t chn, int16_t p, int16_t n)
{
switch (chn)
{
case 0:
MCPWMx->TH00 = p;
MCPWMx->TH01 = n;
break;
case 1:
MCPWMx->TH10 = p;
MCPWMx->TH11 = n;
break;
case 2:
MCPWMx->TH20 = p;
MCPWMx->TH21 = n;
break;
case 3:
MCPWMx->TH30 = p;
MCPWMx->TH31 = n;
break;
default:
break;
}
}
/**
* @brief 设置MCPWM输出状态
* @param MCPWMx MCPWM模块
* @param state 输出状态 1打开输出 0关闭输出
*/
void MCPWM_SetOutputState(MCPWM_TypeDef *MCPWMx, uint8_t state)
{
MCPWMx->PRT = 0xdead; // mcpwm模块解锁
if (state)
{
MCPWMx->EIF = MCPWMx->EIF;
MCPWMx->CH_FAIL |= BIT6;
}
else
{
MCPWMx->CH_FAIL &= ~BIT6;
}
MCPWMx->PRT = 0x0000; // mcpwm模块上锁
}
/**
* @brief 软复位模块
* @param nModule 模块编号
*/
void SYS_SoftResetModule(uint32_t nModule)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
REG_SET(SYS0->SFT_RST, nModule);
REG_RESET(SYS0->SFT_RST, nModule); // 触发复位后需清除复位位
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 配置GPIO引脚复用功能
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource 引脚源
* @param GPIO_AF 复用功能选择
*/
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
uint8_t offset;
uint8_t pins = GPIO_PinSource >> 2;
offset = ((GPIO_PinSource & 0x3) * 4);
switch (pins)
{
case 0:
GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 1:
GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 2:
GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 3:
GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
break;
default:
break;
}
}
15.02_MCPWM-不同的IO状态配置
测试步骤
1.配置MCPWM模块以输出三路中心对称的PWM信号,TH设置为2000,实际周期为2*TH+1 => 4000。 2.配置GPIO引脚复用为MCPWM功能。 3.在主循环中,通过MCPWM_SetIOConfig()函数,设置MCPWM输出的IO状态,默认MCPWM_IO_HL_PWM。
实测数据
| 模式 | MCPWM_CH0P占空比 | MCPWM_CH0N占空比 | 说明 |
|---|---|---|---|
| MCPWM_IO_DISABLE | 20.01% | 70.03% | 不使用该通道(默认配置:上管高有效,下管高有效,正常输出PWM) |
| MCPWM_IO_HH_PWM | 20.01% | 70.02% | 上管高有效,下管高有效,正常输出PWM |
| MCPWM_IO_HH_LOW | 0.00% | 100.00% | 上管高有效,下管高有效,下管恒开 此时pwm配置无效,但仍然会正常计数&触发中断 |
| MCPWM_IO_HH_HIGH | 100.00% | 0.00% | 上管高有效,下管高有效,上管恒开 此时pwm配置无效,但仍然会正常计数&触发中断 |
| MCPWM_IO_HH_HPWM | 20.01% | 0.00% | 上管高有效,下管高有效,上管斩波 |
| MCPWM_IO_HH_LPWM | 0.00% | 70.03% | 上管高有效,下管高有效,下管斩波 |
| MCPWM_IO_HH_OFF | 0.00% | 0.00% | 上管高有效,下管高有效,上下管全关,和MOE不同,通过IO可以关闭任意一路pwm |
| MCPWM_IO_HHS_PWM | 70.02% | 20.01% | 上管高有效,下管高有效,通道交换打开,正常输出PWM |
| MCPWM_IO_HHS_LOW | 100.00% | 0.00% | 上管高有效,下管高有效,通道交换打开,下管恒开 此时pwm配置无效,但仍然会正常计数&触发中断 |
| MCPWM_IO_HHS_HIGH | 0.00% | 100.00% | 上管高有效,下管高有效,通道交换打开,上管恒开 此时pwm配置无效,但仍然会正常计数&触发中断 |
| MCPWM_IO_HHS_HPWM | 0.00% | 20.01% | 上管高有效,下管高有效,通道交换打开,上管斩波 |
| MCPWM_IO_HHS_LPWM | 70.02% | 0.00% | 上管高有效,下管高有效,通道交换打开,下管斩波 |
| MCPWM_IO_HHS_OFF | 0.00% | 0.00% | 上管高有效,下管高有效,通道交换打开,上下管全关,和MOE不同,通过IO可以关闭任意一路pwm |
| MCPWM_IO_HL_PWM | 20.02% | 30.06% | 上管高有效,下管低有效,正常输出PWM |
| MCPWM_IO_HL_LOW | 0.00% | 0.00% | 上管高有效,下管低有效,下管恒开 此时pwm配置无效,但仍然会正常计数&触发中断 |
| MCPWM_IO_HL_HIGH | 100.00% | 100.00% | 上管高有效,下管低有效,上管恒开 此时pwm配置无效,但仍然会正常计数&触发中断 |
| MCPWM_IO_HL_HPWM | 20.01% | 100.00% | 上管高有效,下管低有效,上管斩波 |
| MCPWM_IO_HL_LPWM | 0.00% | 30.06% | 上管高有效,下管低有效,下管斩波 |
| MCPWM_IO_HL_OFF | 0.00% | 100.00% | 上管高有效,下管低有效,上下管全关,和MOE不同,通过IO可以关闭任意一路pwm |
| MCPWM_IO_HLS_PWM | 30.06% | 20.01% | 上管高有效,下管低有效,通道交换打开,正常输出PWM |
| MCPWM_IO_HLS_LOW | 0.00% | 0.00% | 上管高有效,下管低有效,通道交换打开,下管恒开 此时pwm配置无效,但仍然会正常计数&触发中断 |
| MCPWM_IO_HLS_HIGH | 100.00% | 100.00% | 上管高有效,下管低有效,通道交换打开,上管恒开 此时pwm配置无效,但仍然会正常计数&触发中断 |
| MCPWM_IO_HLS_HPWM | 100.00% | 20.01% | 上管高有效,下管低有效,通道交换打开,上管斩波 |
| MCPWM_IO_HLS_LPWM | 30.06% | 0.00% | 上管高有效,下管低有效,通道交换打开,下管斩波 |
| MCPWM_IO_HLS_OFF | 100.00% | 0.00% | 上管高有效,下管低有效,通道交换打开,上下管全关,和MOE不同,通过IO可以关闭任意一路pwm |
| MCPWM_IO_LH_PWM | 80.07% | 70.02% | 上管低有效,下管高有效,正常输出PWM |
| MCPWM_IO_LH_LOW | 100.00% | 100.00% | 上管低有效,下管高有效,下管恒开 此时pwm配置无效,但仍然会正常计数&触发中断 |
| MCPWM_IO_LH_HIGH | 0.00% | 0.00% | 上管低有效,下管高有效,上管恒开 此时pwm配置无效,但仍然会正常计数&触发中断 |
| MCPWM_IO_LH_HPWM | 80.07% | 0.00% | 上管低有效,下管高有效,上管斩波 |
| MCPWM_IO_LH_LPWM | 100.00% | 70.03% | 上管低有效,下管高有效,下管斩波 |
| MCPWM_IO_LH_OFF | 100.00% | 0.00% | 上管低有效,下管高有效,上下管全关,和MOE不同,通过IO可以关闭任意一路pwm |
| MCPWM_IO_LHS_PWM | 70.02% | 80.08% | 上管低有效,下管高有效,通道交换打开,正常输出PWM |
| MCPWM_IO_LHS_LOW | 100.00% | 100.00% | 上管低有效,下管高有效,通道交换打开,下管恒开 此时pwm配置无效,但仍然会正常计数&触发中断 |
| MCPWM_IO_LHS_HIGH | 0.00% | 0.00% | 上管低有效,下管高有效,通道交换打开,上管恒开 此时pwm配置无效,但仍然会正常计数&触发中断 |
| MCPWM_IO_LHS_HPWM | 0.00% | 80.07% | 上管低有效,下管高有效,通道交换打开,上管斩波 |
| MCPWM_IO_LHS_LPWM | 70.03% | 100.00% | 上管低有效,下管高有效,通道交换打开,下管斩波 |
| MCPWM_IO_LHS_OFF | 0.00% | 100.00% | 上管低有效,下管高有效,通道交换打开,上下管全关,和MOE不同,通过IO可以关闭任意一路pwm |
| MCPWM_IO_LL_PWM | 80.08% | 30.06% | 上管低有效,下管低有效,正常输出PWM |
| MCPWM_IO_LL_LOW | 100.00% | 0.00% | 上管低有效,下管低有效,下管恒开 此时pwm配置无效,但仍然会正常计数&触发中断 |
| MCPWM_IO_LL_HIGH | 0.00% | 100.00% | 上管低有效,下管低有效,上管恒开 此时pwm配置无效,但仍然会正常计数&触发中断 |
| MCPWM_IO_LL_HPWM | 80.08% | 100.00% | 上管低有效,下管低有效,上管斩波 |
| MCPWM_IO_LL_LPWM | 100.00% | 30.06% | 上管低有效,下管低有效,下管斩波 |
| MCPWM_IO_LL_OFF | 100.00% | 100.00% | 上管低有效,下管低有效,上下管全关,和MOE不同,通过IO可以关闭任意一路pwm |
| MCPWM_IO_LLS_PWM | 30.06% | 80.07% | 上管低有效,下管低有效,通道交换打开,正常输出PWM |
| MCPWM_IO_LLS_LOW | 0.00% | 100.00% | 上管低有效,下管低有效,通道交换打开,下管恒开 此时pwm配置无效,但仍然会正常计数&触发中断 |
| MCPWM_IO_LLS_HIGH | 100.00% | 0.00% | 上管低有效,下管低有效,通道交换打开,上管恒开 此时pwm配置无效,但仍然会正常计数&触发中断 |
| MCPWM_IO_LLS_HPWM | 100.00% | 80.07% | 上管低有效,下管低有效,通道交换打开,上管斩波 |
| MCPWM_IO_LLS_LPWM | 30.06% | 100.00% | 上管低有效,下管低有效,通道交换打开,下管斩波 |
| MCPWM_IO_LLS_OFF | 100.00% | 100.00% | 上管低有效,下管低有效,通道交换打开,上下管全关,和MOE不同,通过IO可以关闭任意一路pwm |
参考代码
15.02_MCPWM_IO.c
/**
* @brief MCPWM-不同的IO状态配置
* @details
* 1.配置MCPWM模块以输出三路中心对称的PWM信号,TH设置为2000,实际周期为2*TH+1 => 4000。
* 2.配置GPIO引脚复用为MCPWM功能。
* 3.在主循环中,通过MCPWM_SetIOConfig()函数,设置MCPWM输出的IO状态,默认MCPWM_IO_HL_PWM。
*/
#include "lks32mc09x_gpio.h"
#include "lks32mc09x_mcpwm.h"
#include "lks32mc09x_user_manual.h"
volatile uint32_t setio = MCPWM_IO_HL_PWM;
/**
* @brief 配置MCPWM输出三路中心对称的PWM,频率20kHz。
*/
void MCPWM_Config(void)
{
MCPWM_InitTypeDef MCPWM_InitStruct;
// 初始化MCPWM结构体
MCPWM_StructInit(&MCPWM_InitStruct);
// 配置MCPWM基本项
MCPWM_InitStruct.EN = 1; // 使能MCPWM模块
MCPWM_InitStruct.CLK_DIV = MCPWM_CLK_DIV_1; // 时钟1分频
MCPWM_InitStruct.TH = 2000; // 计数器门限值
MCPWM_InitStruct.TR = MCPWM_TR_T0; // 计数器溢出时刻触发自动更新
// 配置死区时间
MCPWM_InitStruct.DTHP = 200; // 上管死区时间
MCPWM_InitStruct.DTHN = 200; // 下管死区时间
// 配置IO输出
MCPWM_InitStruct.IO0 = MCPWM_IO_HL_PWM; // CH0: 上管高有效,下管低有效,正常输出PWM
MCPWM_InitStruct.IO1 = MCPWM_IO_HL_PWM; // CH1: 上管高有效,下管低有效,正常输出PWM
MCPWM_InitStruct.IO2 = MCPWM_IO_HL_PWM; // CH2: 上管高有效,下管低有效,正常输出PWM
MCPWM_InitStruct.IO3 = MCPWM_IO_DISABLE; // CH3: 不使用
MCPWM_InitStruct.FAIL_IO = MCPWM_FAIL_IO_OFF;
// 配置自动更新使能
MCPWM_InitStruct.AUEN = MCPWM_AUEN_DEFAULT; // 使能TH0自动加载
// 配置中断和DMA(本例程中不使用中断和DMA)
MCPWM_InitStruct.IE = 0;
MCPWM_InitStruct.EIE = 0;
MCPWM_InitStruct.RE = 0;
// 初始化MCPWM模块
MCPWM_Init(MCPWM0, &MCPWM_InitStruct);
// 设置输出状态
MCPWM_SetOutputState(MCPWM0, 1);
MCPWM_SetOutputVal(MCPWM0, 0, -500, 500); // CH0: 中心对齐,占空比50%
MCPWM_SetOutputVal(MCPWM0, 1, -500, 500); // CH1: 中心对齐,占空比50%
MCPWM_SetOutputVal(MCPWM0, 2, -500, 500); // CH2: 中心对齐,占空比50%
// 开始计数
MCPWM_StartCount(MCPWM0);
}
/**
* @brief 主函数
*/
int main(void)
{
// 配置GPIO引脚复用为MCPWM功能
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_StructInit(&GPIO_InitStruct);
// 配置P1.4到P1.9为MCPWM功能
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIO1, &GPIO_InitStruct);
// 配置引脚复用功能为MCPWM
GPIO_PinAFConfig(GPIO1, GPIO_PinSource_4, GPIO_AF_MCPWM);
GPIO_PinAFConfig(GPIO1, GPIO_PinSource_5, GPIO_AF_MCPWM);
GPIO_PinAFConfig(GPIO1, GPIO_PinSource_6, GPIO_AF_MCPWM);
GPIO_PinAFConfig(GPIO1, GPIO_PinSource_7, GPIO_AF_MCPWM);
GPIO_PinAFConfig(GPIO1, GPIO_PinSource_8, GPIO_AF_MCPWM);
GPIO_PinAFConfig(GPIO1, GPIO_PinSource_9, GPIO_AF_MCPWM);
// 配置MCPWM输出三路中心对称的PWM,频率20kHz
MCPWM_Config();
// 主循环
while (1)
{
MCPWM_SetIOConfig(MCPWM0, setio, setio, setio, MCPWM_IO_DISABLE);
}
}
使用到的库函数
库函数部分代码
#define MCPWM_COUNT_START BIT31
#define MCPWM_IO_NP BIT0
#define MCPWM_IO_PN_SW BIT6
#define MCPWM_IO_PP BIT1
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_GPIO BIT11
#define SYS_MODULE_MCPWM0 BIT10
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
typedef struct MCPWM_InitTypeDef {
uint16_t EN;
uint32_t COUNT;
uint16_t CLK_DIV;
uint16_t TH;
uint16_t TR;
int16_t TMR0;
int16_t TMR1;
int16_t TMR2;
int16_t TMR3;
uint16_t DTHP;
uint16_t DTHN;
uint16_t FLT_DIV;
uint16_t IO0;
uint16_t IO1;
uint16_t IO2;
uint16_t IO3;
uint32_t AUEN;
uint32_t IE;
uint32_t EIE;
uint32_t RE;
uint32_t FAIL_IO;
uint32_t FAIL0;
uint32_t FAIL1;
};
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 初始化MCPWM模块
* @param MCPWMx MCPWM模块指针
* @param MCPWM_InitStruct 指向包含初始化参数的MCPWM_InitTypeDef结构体
*/
void MCPWM_Init(MCPWM_TypeDef *MCPWMx, MCPWM_InitTypeDef *MCPWM_InitStruct)
{
// 复位MCPWM模块
MCPWM_Reset(MCPWMx);
if (MCPWM_InitStruct->EN)
{
// 使能MCPWM模块
MCPWM_Enable(MCPWMx);
// 初始化MCPWM模块
MCPWMx->PRT = 0xdead; // mcpwm模块解锁
if (MCPWM_InitStruct->COUNT & MCPWM_COUNT_START)
{
MCPWMx->TCLK = BIT6 | (MCPWM_InitStruct->CLK_DIV << 12) | BIT2;
}
else
{
if (MCPWM_InitStruct->COUNT != 0)
{
MCPWMx->TCLK = ((MCPWMx->TCLK & (~BIT6)) | BIT8) | (MCPWM_InitStruct->CLK_DIV << 12) | BIT2; // 关闭计数器,并打开外部触发
MCPWMx->EVT0 = MCPWM_InitStruct->COUNT; // 设置外部触发信号
}
else
{
MCPWMx->TCLK = (MCPWM_InitStruct->CLK_DIV << 12) | BIT2;
}
}
{
uint16_t chdef = 0;
chdef = MCPWM_InitStruct->FAIL_IO;
if (MCPWM_InitStruct->IO0 & MCPWM_IO_NP)
{
chdef ^= BIT0;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO0 & MCPWM_IO_PP)
{
chdef ^= BIT1;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO1 & MCPWM_IO_NP)
{
chdef ^= BIT2;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO1 & MCPWM_IO_PP)
{
chdef ^= BIT3;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO2 & MCPWM_IO_NP)
{
chdef ^= BIT4;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO2 & MCPWM_IO_PP)
{
chdef ^= BIT5;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO3 & MCPWM_IO_NP)
{
chdef ^= BIT6;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO3 & MCPWM_IO_PP)
{
chdef ^= BIT7;
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO0 & MCPWM_IO_PN_SW)
{
// 交换chdef的bit0和bit1
chdef = (chdef & 0xfffe) | ((chdef & 0x0001) << 1) | ((chdef & 0x0002) >> 1);
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO1 & MCPWM_IO_PN_SW)
{
// 交换chdef的bit2和bit3
chdef = (chdef & 0xfffc) | ((chdef & 0x0004) << 1) | ((chdef & 0x0008) >> 1);
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO2 & MCPWM_IO_PN_SW)
{
// 交换chdef的bit4和bit5
chdef = (chdef & 0xfff0) | ((chdef & 0x0010) << 1) | ((chdef & 0x0020) >> 1);
}
else
{
__NOP();
}
if (MCPWM_InitStruct->IO3 & MCPWM_IO_PN_SW)
{
// 交换chdef的bit6和bit7
chdef = (chdef & 0xff00) | ((chdef & 0x0040) << 1) | ((chdef & 0x0080) >> 1);
}
else
{
__NOP();
}
MCPWMx->CH_DEF = chdef;
}
MCPWMx->TH00 = 0;
MCPWMx->TH01 = 0;
MCPWMx->TH10 = 0;
MCPWMx->TH11 = 0;
MCPWMx->TH20 = 0;
MCPWMx->TH21 = 0;
MCPWMx->TH30 = 0;
MCPWMx->TH31 = 0;
MCPWMx->CNT0 = -MCPWM_InitStruct->TH;
MCPWMx->TH0 = MCPWM_InitStruct->TH;
MCPWMx->TMR0 = MCPWM_InitStruct->TMR0;
MCPWMx->TMR1 = MCPWM_InitStruct->TMR1;
MCPWMx->TMR2 = MCPWM_InitStruct->TMR2;
MCPWMx->TMR3 = MCPWM_InitStruct->TMR3;
MCPWMx->DTH00 = MCPWM_InitStruct->DTHP;
MCPWMx->DTH01 = MCPWM_InitStruct->DTHN;
MCPWMx->FLT = MCPWM_InitStruct->FLT_DIV;
MCPWMx->IO01 = MCPWM_InitStruct->IO0 | (MCPWM_InitStruct->IO1 << 8);
MCPWMx->IO23 = MCPWM_InitStruct->IO2 | (MCPWM_InitStruct->IO3 << 8);
MCPWMx->CH_FAIL = MCPWM_InitStruct->FAIL0 | MCPWM_InitStruct->FAIL1;
MCPWMx->AUEN = MCPWM_InitStruct->AUEN;
MCPWMx->IE0 = MCPWM_InitStruct->IE;
MCPWMx->EIE = MCPWM_InitStruct->EIE;
MCPWMx->RE = MCPWM_InitStruct->RE;
MCPWMx->SDCFG = MCPWM_InitStruct->TR;
// 更新所有存在影子寄存器的寄存器
MCPWMx->UPDATE = 0xffffffff;
MCPWMx->IF0 = 0xffff;
MCPWMx->EIF = 0xffff;
MCPWMx->PRT = 0x0000; // mcpwm模块上锁
}
else
{
// 失能MCPWM模块
MCPWM_Disable(MCPWMx);
}
}
/**
* @brief 将MCPWM_InitTypeDef结构体初始化为默认值
* @param MCPWM_InitStruct 指向要初始化的MCPWM_InitTypeDef结构体
*/
void MCPWM_StructInit(MCPWM_InitTypeDef *MCPWM_InitStruct)
{
for (int i = 0; i < sizeof(MCPWM_InitTypeDef); i++)
{
((uint8_t *)MCPWM_InitStruct)[i] = 0;
}
}
/**
* @brief 开始计数
* @param MCPWMx MCPWM模块
* @param count 计数器初始值
*/
void MCPWM_StartCount(MCPWM_TypeDef *MCPWMx)
{
MCPWMx->PRT = 0xdead; // mcpwm模块解锁
// MCPWMx->UPDATE = BIT11;
MCPWMx->TCLK |= BIT6;
MCPWMx->PRT = 0x0000; // mcpwm模块上锁
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 模块使能
* @param MCPWMx MCPWM模块
*/
void MCPWM_Enable(MCPWM_TypeDef *MCPWMx)
{
SYS_ModuleClockCmd(SYS_MODULE_MCPWM0, ENABLE);
MCPWMx->PRT = 0xdead;
MCPWMx->TCLK |= BIT2;
MCPWMx->PRT = 0;
}
/**
* @brief 模块复位
* @param MCPWMx MCPWM模块
*/
void MCPWM_Reset(MCPWM_TypeDef *MCPWMx)
{
SYS_SoftResetModule(SYS_MODULE_MCPWM0);
}
/**
* @brief 模块失能
* @param MCPWMx MCPWM模块
*/
void MCPWM_Disable(MCPWM_TypeDef *MCPWMx)
{
MCPWMx->PRT = 0xdead;
MCPWMx->TCLK &= ~BIT2;
MCPWMx->PRT = 0;
SYS_ModuleClockCmd(SYS_MODULE_MCPWM0, DISABLE);
}
/**
* @brief 设置IO配置
* @param MCPWMx MCPWM模块
* @param io0 IO0配置
* @param io1 IO1配置
* @param io2 IO2配置
* @param io3 IO3配置
* @note 由于mcpwm的io0和io1是一个寄存器,io2和io3是一个寄存器,所以需要同时配置
*/
void MCPWM_SetIOConfig(MCPWM_TypeDef *MCPWMx, uint16_t io0, uint16_t io1, uint16_t io2, uint16_t io3)
{
MCPWMx->PRT = 0xdead; // mcpwm模块解锁
MCPWMx->IO01 = io0 | (io1 << 8);
MCPWMx->IO23 = io2 | (io3 << 8);
MCPWMx->PRT = 0x0000; // mcpwm模块上锁
}
/**
* @brief 设置MCPWM输出值
* @param MCPWMx MCPWM模块
* @param chn 通道号 0-3
* @param p 上管打开时间
* @param n 下管打开时间
*/
void MCPWM_SetOutputVal(MCPWM_TypeDef *MCPWMx, uint8_t chn, int16_t p, int16_t n)
{
switch (chn)
{
case 0:
MCPWMx->TH00 = p;
MCPWMx->TH01 = n;
break;
case 1:
MCPWMx->TH10 = p;
MCPWMx->TH11 = n;
break;
case 2:
MCPWMx->TH20 = p;
MCPWMx->TH21 = n;
break;
case 3:
MCPWMx->TH30 = p;
MCPWMx->TH31 = n;
break;
default:
break;
}
}
/**
* @brief 设置MCPWM输出状态
* @param MCPWMx MCPWM模块
* @param state 输出状态 1打开输出 0关闭输出
*/
void MCPWM_SetOutputState(MCPWM_TypeDef *MCPWMx, uint8_t state)
{
MCPWMx->PRT = 0xdead; // mcpwm模块解锁
if (state)
{
MCPWMx->EIF = MCPWMx->EIF;
MCPWMx->CH_FAIL |= BIT6;
}
else
{
MCPWMx->CH_FAIL &= ~BIT6;
}
MCPWMx->PRT = 0x0000; // mcpwm模块上锁
}
/**
* @brief 软复位模块
* @param nModule 模块编号
*/
void SYS_SoftResetModule(uint32_t nModule)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
REG_SET(SYS0->SFT_RST, nModule);
REG_RESET(SYS0->SFT_RST, nModule); // 触发复位后需清除复位位
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 配置GPIO引脚复用功能
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource 引脚源
* @param GPIO_AF 复用功能选择
*/
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
uint8_t offset;
uint8_t pins = GPIO_PinSource >> 2;
offset = ((GPIO_PinSource & 0x3) * 4);
switch (pins)
{
case 0:
GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 1:
GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 2:
GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 3:
GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
break;
default:
break;
}
}
16.01_OPA-输出到IO
测试步骤
使能OPA0,内部增益设置为160k:40k,并输出到P2.7。 测试时输入信号固定N端为1V,P端从0.5-1.5V之间线性变化。
实测数据
输出电压
误差
受到测试环境的性能限制,这里的放大倍数和误差仅供参考
参考代码
16.01_OPA_Config.c
/**
* @brief OPA-输出到IO
* @details 使能OPA0,内部增益设置为160k:40k,并输出到P2.7。
* 测试时输入信号固定N端为1V,P端从0.5-1.5V之间线性变化。
*/
#include "lks32mc09x_opa.h"
volatile uint32_t opa_out = OPA_OUT_CMP_OUT0P;
/**
* @brief 主函数
*/
int main(void)
{
OPA_Enable(OPA_CHANNEL_0);
OPA_Enable(OPA_CHANNEL_1);
OPA_Enable(OPA_CHANNEL_2);
OPA_Enable(OPA_CHANNEL_3);
OPA_SetGain(OPA_CHANNEL_0, OPA_GAIN_4);
OPA_OutCmpSignal(OPA_OUT_CMP_OUT0P, 1);
while (1)
{
OPA_OutCmpSignal(opa_out, 1);
}
}
使用到的库函数
库函数部分代码
#define OPA_CHANNEL_0 0
#define OPA_CHANNEL_1 1
#define OPA_CHANNEL_2 2
#define OPA_CHANNEL_3 3
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
/**
* @brief OPA 使能
* @param OPA_CHANNEL_x OPA通道
*/
void OPA_Enable(uint32_t OPA_CHANNEL_x)
{
SYS0->PROTECT = 0x7a83;
switch (OPA_CHANNEL_x)
{
case OPA_CHANNEL_0:
SYS0->AFE_REG1 |= BIT12;
break;
case OPA_CHANNEL_1:
SYS0->AFE_REG1 |= BIT13;
break;
case OPA_CHANNEL_2:
SYS0->AFE_REG1 |= BIT14;
break;
case OPA_CHANNEL_3:
SYS0->AFE_REG1 |= BIT15;
break;
}
SYS0->PROTECT = 0;
}
/**
* @brief OPA 增益设置
* @param OPA_CHANNEL_x OPA通道
* @param OPA_GAIN_x OPA增益
*/
void OPA_SetGain(uint32_t OPA_CHANNEL_x, uint32_t OPA_GAIN_x)
{
SYS0->PROTECT = 0x7a83;
switch (OPA_CHANNEL_x)
{
case OPA_CHANNEL_0:
SYS0->AFE_REG0 = (SYS0->AFE_REG0 & ~(3)) | OPA_GAIN_x;
break;
case OPA_CHANNEL_1:
SYS0->AFE_REG0 = (SYS0->AFE_REG0 & ~(3 << 2)) | (OPA_GAIN_x << 2);
break;
case OPA_CHANNEL_2:
SYS0->AFE_REG0 = (SYS0->AFE_REG0 & ~(3 << 4)) | (OPA_GAIN_x << 4);
break;
case OPA_CHANNEL_3:
SYS0->AFE_REG0 = (SYS0->AFE_REG0 & ~(3 << 6)) | (OPA_GAIN_x << 6);
break;
}
SYS0->PROTECT = 0;
}
/**
* @brief OPA 输出信号选择(使能输出到GPIO)
* @param OPA_OUT_CMP_x OPA_OUT_CMP信号来源配置
* @param Out2Gpio OPA_OUT_CMP输出到IO使能,P2.7
*/
void OPA_OutCmpSignal(uint32_t OPA_OUT_CMP_x, uint32_t Out2Gpio)
{
SYS0->PROTECT = 0x7a83;
SYS0->AFE_REG2 = (SYS0->AFE_REG2 & ~(0xf)) |
(OPA_OUT_CMP_x) |
(Out2Gpio << 3);
SYS0->PROTECT = 0;
}
17.01_QEP-正交编码
测试步骤
设置P2.11(QEP0_CH0), P2.12(QEP0_CH1)和P0.13(QEP0_Z);
实测数据
T1_T2
qep_cnt = 0
| time | a | b | z | cnt |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 1 | 1 | 0 | 0 | 1 |
| 2 | 1 | 1 | 0 | 2 |
| 3 | 0 | 1 | 0 | 3 |
| 4 | 0 | 0 | 0 | 4 |
| 5 | 1 | 0 | 0 | 5 |
| 6 | 1 | 1 | 0 | 6 |
| 7 | 0 | 1 | 0 | 7 |
| 8 | 0 | 0 | 0 | 8 |
| 9 | 1 | 0 | 0 | 9 |
| 10 | 1 | 1 | 0 | 10 |
| 11 | 0 | 1 | 0 | 11 |
| 12 | 0 | 0 | 0 | 12 |
| 13 | 1 | 0 | 0 | 13 |
| 14 | 1 | 1 | 0 | 14 |
| 15 | 0 | 1 | 0 | 15 |
| 16 | 0 | 1 | 0 | 15 |
| 17 | 1 | 1 | 0 | 14 |
| 18 | 1 | 0 | 0 | 13 |
| 19 | 0 | 0 | 0 | 12 |
| 20 | 0 | 1 | 0 | 11 |
| 21 | 1 | 1 | 0 | 10 |
| 22 | 1 | 0 | 0 | 9 |
| 23 | 0 | 0 | 0 | 8 |
| 24 | 0 | 1 | 0 | 7 |
| 25 | 1 | 1 | 0 | 6 |
| 26 | 1 | 0 | 0 | 5 |
| 27 | 0 | 0 | 0 | 4 |
| 28 | 0 | 1 | 0 | 3 |
| 29 | 1 | 1 | 0 | 2 |
| 30 | 1 | 0 | 0 | 1 |
| 31 | 0 | 0 | 0 | 0 |
参考代码
17.01_QEP_Count.c
/**
* @brief QEP-正交编码
* @details
* 设置P2.11(QEP0_CH0), P2.12(QEP0_CH1)和P0.13(QEP0_Z);
*/
#include "lks32mc09x_gpio.h"
#include "lks32mc09x_qep.h"
volatile uint32_t qep_cnt;
/**
* @brief 主函数
*/
int main(void)
{
// 配置QEP模块
QEP_InitTypeDef QEP_InitStruct;
QEP_StructInit(&QEP_InitStruct); // 初始化QEP结构体为默认值
QEP_InitStruct.Mode = QEP_Mode_T1_T2; ///< 编码器模式选择
QEP_InitStruct.FE_CNT_EN = DISABLE; ///< 下降沿计数使能
QEP_InitStruct.ZC = QEP_ZC_NONE; ///< Z信号配置
QEP_InitStruct.TH = 65535; ///< QEP计数门限寄存器
QEP_InitStruct.ClockDiv = 0; ///< 时钟分频
QEP_InitStruct.Filter = 0; ///< 信号输入滤波
QEP_InitStruct.IRQEna = 0; ///< 中断使能
QEP_Init(QEP0, &QEP_InitStruct); // 初始化QEP0模块
// 配置需要使用的gpio
GPIO_Config(GPIO2, GPIO_PinSource_11, GPIO_Mode_IN, GPIO_AF_QEP0);
GPIO_Config(GPIO2, GPIO_PinSource_12, GPIO_Mode_IN, GPIO_AF_QEP0);
GPIO_Config(GPIO0, GPIO_PinSource_13, GPIO_Mode_IN, GPIO_AF_QEP0);
// 主循环
while (1)
{
qep_cnt = QEP_GetCount(QEP0);
}
}
使用到的库函数
库函数部分代码
#define GPIO_AF_I2C 6
#define QEP0 ((QEP_TypeDef *)QEP0_BASE)
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_GPIO BIT11
#define SYS_MODULE_QEP0 BIT8
#define SYS_MODULE_TIMER0 BIT4
#define SYS_MODULE_TIMER1 BIT5
#define SYS_MODULE_TIMER2 BIT6
#define TIMER0 ((TIMER_TypeDef *)TIMER0_BASE)
#define TIMER1 ((TIMER_TypeDef *)TIMER1_BASE)
#define TIMER2 ((TIMER_TypeDef *)TIMER2_BASE)
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* @brief GPIO模式选择枚举
*/
typedef enum GPIO_Mode_TypeDef {
GPIO_Mode_IN = 0,
GPIO_Mode_OUT = 1,
GPIO_Mode_ANA = 2,
GPIO_Mode_IO = 3,
}} {enum_name};
/**
* @brief QEP初始化结构体定义
*/
typedef struct QEP_InitTypeDef {
uint32_t Mode;
uint32_t FE_CNT_EN;
uint16_t TH;
uint32_t ClockDiv;
uint32_t Filter;
uint32_t IRQEna;
uint32_t ZC;
};
/**
* @brief GPIO配置函数
* @param GPIOx: GPIO端口
* @param GPIO_PinSource: GPIO引脚
* @param mode: GPIO模式
* @param GPIO_AF_x: GPIO复用功能选择
*/
void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
{
GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = 1 << GPIO_PinSource;
GPIO_InitStructure.GPIO_Mode = mode;
if (GPIO_AF_x == GPIO_AF_I2C)
{
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
}
else
{
__NOP();
}
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**
* @brief QEP初始化函数(初始化完成后会自动使能,并使能timer2)
* @param QEPx QEP模块指针
* @param TIM_QEPInitStruct 初始化结构体指针
*/
void QEP_Init(QEP_TypeDef *QEPx, QEP_InitTypeDef *TIM_QEPInitStruct)
{
// 使能QEP模块时钟
if (QEPx == QEP0)
{
SYS_ModuleClockCmd(SYS_MODULE_QEP0, ENABLE);
TIMER_Enable(TIMER2);
}
else
{
__NOP();
}
// QEP配置寄存器
QEPx->CFG = BIT15 |
(TIM_QEPInitStruct->Filter << 16) |
(TIM_QEPInitStruct->FE_CNT_EN << 10) |
(TIM_QEPInitStruct->Mode << 8) |
(TIM_QEPInitStruct->ZC);
// 设置计数门限
QEPx->TH = TIM_QEPInitStruct->TH;
// 设置中断使能
QEPx->IE = TIM_QEPInitStruct->IRQEna;
}
/**
* @brief QEP结构体初始化函数
* @param TIM_QEPInitStruct 初始化结构体指针
*/
void QEP_StructInit(QEP_InitTypeDef *TIM_QEPInitStruct)
{
for (int i = 0; i < sizeof(QEP_InitTypeDef) / sizeof(uint32_t); i++)
{
((uint32_t *)TIM_QEPInitStruct)[i] = 0;
}
}
/**
* @brief 使能Timer
* @param TIMERx Timer实例指针
*/
void TIMER_Enable(TIMER_TypeDef *TIMERx)
{
if (TIMERx == TIMER0)
{
SYS_ModuleClockCmd(SYS_MODULE_TIMER0, ENABLE);
}
else if (TIMERx == TIMER1)
{
SYS_ModuleClockCmd(SYS_MODULE_TIMER1, ENABLE);
}
else if (TIMERx == TIMER2)
{
SYS_ModuleClockCmd(SYS_MODULE_TIMER2, ENABLE);
}
else
{
__NOP();
}
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 获取QEP计数器当前值
* @param QEPx QEP模块指针
* @return 计数器当前值
*/
uint16_t QEP_GetCount(QEP_TypeDef *QEPx)
{
return QEPx->CNT;
}
/**
* @brief 配置GPIO引脚复用功能
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource 引脚源
* @param GPIO_AF 复用功能选择
*/
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
uint8_t offset;
uint8_t pins = GPIO_PinSource >> 2;
offset = ((GPIO_PinSource & 0x3) * 4);
switch (pins)
{
case 0:
GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 1:
GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 2:
GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 3:
GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
break;
default:
break;
}
}
17.02_QEP-符号加脉冲信号计数
测试步骤
设置P2.11(QEP0_CH0), P2.12(QEP0_CH1)和P0.13(QEP0_Z);
实测数据
CCWSIGN_UpDown
qep_cnt = 0
| time | a | b | z | cnt |
|---|---|---|---|---|
| 0 | 0 | 1 | 0 | 0 |
| 1 | 1 | 1 | 0 | 1 |
| 2 | 0 | 1 | 0 | 2 |
| 3 | 1 | 1 | 0 | 3 |
| 4 | 0 | 1 | 0 | 4 |
| 5 | 1 | 1 | 0 | 5 |
| 6 | 0 | 1 | 0 | 6 |
| 7 | 1 | 1 | 0 | 7 |
| 8 | 0 | 1 | 0 | 8 |
| 9 | 1 | 1 | 0 | 9 |
| 10 | 0 | 1 | 0 | 10 |
| 11 | 1 | 1 | 0 | 11 |
| 12 | 0 | 1 | 0 | 12 |
| 13 | 1 | 1 | 0 | 13 |
| 14 | 0 | 1 | 0 | 14 |
| 15 | 1 | 1 | 0 | 15 |
| 16 | 1 | 0 | 0 | 15 |
| 17 | 0 | 0 | 0 | 14 |
| 18 | 1 | 0 | 0 | 13 |
| 19 | 0 | 0 | 0 | 12 |
| 20 | 1 | 0 | 0 | 11 |
| 21 | 0 | 0 | 0 | 10 |
| 22 | 1 | 0 | 0 | 9 |
| 23 | 0 | 0 | 0 | 8 |
| 24 | 1 | 0 | 0 | 7 |
| 25 | 0 | 0 | 0 | 6 |
| 26 | 1 | 0 | 0 | 5 |
| 27 | 0 | 0 | 0 | 4 |
| 28 | 1 | 0 | 0 | 3 |
| 29 | 0 | 0 | 0 | 2 |
| 30 | 1 | 0 | 0 | 1 |
| 31 | 0 | 0 | 0 | 0 |
参考代码
17.02_QEP_Count.c
/**
* @brief QEP-符号加脉冲信号计数
* @details
* 设置P2.11(QEP0_CH0), P2.12(QEP0_CH1)和P0.13(QEP0_Z);
*/
#include "lks32mc09x_gpio.h"
#include "lks32mc09x_qep.h"
volatile uint32_t qep_cnt;
/**
* @brief 主函数
*/
int main(void)
{
// 配置QEP模块
QEP_InitTypeDef QEP_InitStruct;
QEP_StructInit(&QEP_InitStruct); // 初始化QEP结构体为默认值
QEP_InitStruct.Mode = QEP_Mode_CCWSIGN_UpDown; ///< 编码器模式选择
QEP_InitStruct.FE_CNT_EN = ENABLE; ///< 下降沿计数使能
QEP_InitStruct.ZC = QEP_ZC_NONE; ///< 上升沿清零
QEP_InitStruct.TH = 65535; ///< QEP计数门限寄存器
QEP_InitStruct.ClockDiv = 0; ///< 时钟分频
QEP_InitStruct.Filter = 0; ///< 信号输入滤波
QEP_InitStruct.IRQEna = 0; ///< 中断使能
QEP_Init(QEP0, &QEP_InitStruct); // 初始化QEP0模块
// 配置需要使用的gpio
GPIO_Config(GPIO2, GPIO_PinSource_11, GPIO_Mode_IN, GPIO_AF_QEP0);
GPIO_Config(GPIO2, GPIO_PinSource_12, GPIO_Mode_IN, GPIO_AF_QEP0);
GPIO_Config(GPIO0, GPIO_PinSource_13, GPIO_Mode_IN, GPIO_AF_QEP0);
// 主循环
while (1)
{
qep_cnt = QEP_GetCount(QEP0);
}
}
使用到的库函数
库函数部分代码
#define GPIO_AF_I2C 6
#define QEP0 ((QEP_TypeDef *)QEP0_BASE)
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_GPIO BIT11
#define SYS_MODULE_QEP0 BIT8
#define SYS_MODULE_TIMER0 BIT4
#define SYS_MODULE_TIMER1 BIT5
#define SYS_MODULE_TIMER2 BIT6
#define TIMER0 ((TIMER_TypeDef *)TIMER0_BASE)
#define TIMER1 ((TIMER_TypeDef *)TIMER1_BASE)
#define TIMER2 ((TIMER_TypeDef *)TIMER2_BASE)
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* @brief GPIO模式选择枚举
*/
typedef enum GPIO_Mode_TypeDef {
GPIO_Mode_IN = 0,
GPIO_Mode_OUT = 1,
GPIO_Mode_ANA = 2,
GPIO_Mode_IO = 3,
}} {enum_name};
/**
* @brief QEP初始化结构体定义
*/
typedef struct QEP_InitTypeDef {
uint32_t Mode;
uint32_t FE_CNT_EN;
uint16_t TH;
uint32_t ClockDiv;
uint32_t Filter;
uint32_t IRQEna;
uint32_t ZC;
};
/**
* @brief GPIO配置函数
* @param GPIOx: GPIO端口
* @param GPIO_PinSource: GPIO引脚
* @param mode: GPIO模式
* @param GPIO_AF_x: GPIO复用功能选择
*/
void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
{
GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = 1 << GPIO_PinSource;
GPIO_InitStructure.GPIO_Mode = mode;
if (GPIO_AF_x == GPIO_AF_I2C)
{
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
}
else
{
__NOP();
}
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**
* @brief QEP初始化函数(初始化完成后会自动使能,并使能timer2)
* @param QEPx QEP模块指针
* @param TIM_QEPInitStruct 初始化结构体指针
*/
void QEP_Init(QEP_TypeDef *QEPx, QEP_InitTypeDef *TIM_QEPInitStruct)
{
// 使能QEP模块时钟
if (QEPx == QEP0)
{
SYS_ModuleClockCmd(SYS_MODULE_QEP0, ENABLE);
TIMER_Enable(TIMER2);
}
else
{
__NOP();
}
// QEP配置寄存器
QEPx->CFG = BIT15 |
(TIM_QEPInitStruct->Filter << 16) |
(TIM_QEPInitStruct->FE_CNT_EN << 10) |
(TIM_QEPInitStruct->Mode << 8) |
(TIM_QEPInitStruct->ZC);
// 设置计数门限
QEPx->TH = TIM_QEPInitStruct->TH;
// 设置中断使能
QEPx->IE = TIM_QEPInitStruct->IRQEna;
}
/**
* @brief QEP结构体初始化函数
* @param TIM_QEPInitStruct 初始化结构体指针
*/
void QEP_StructInit(QEP_InitTypeDef *TIM_QEPInitStruct)
{
for (int i = 0; i < sizeof(QEP_InitTypeDef) / sizeof(uint32_t); i++)
{
((uint32_t *)TIM_QEPInitStruct)[i] = 0;
}
}
/**
* @brief 使能Timer
* @param TIMERx Timer实例指针
*/
void TIMER_Enable(TIMER_TypeDef *TIMERx)
{
if (TIMERx == TIMER0)
{
SYS_ModuleClockCmd(SYS_MODULE_TIMER0, ENABLE);
}
else if (TIMERx == TIMER1)
{
SYS_ModuleClockCmd(SYS_MODULE_TIMER1, ENABLE);
}
else if (TIMERx == TIMER2)
{
SYS_ModuleClockCmd(SYS_MODULE_TIMER2, ENABLE);
}
else
{
__NOP();
}
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 获取QEP计数器当前值
* @param QEPx QEP模块指针
* @return 计数器当前值
*/
uint16_t QEP_GetCount(QEP_TypeDef *QEPx)
{
return QEPx->CNT;
}
/**
* @brief 配置GPIO引脚复用功能
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource 引脚源
* @param GPIO_AF 复用功能选择
*/
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
uint8_t offset;
uint8_t pins = GPIO_PinSource >> 2;
offset = ((GPIO_PinSource & 0x3) * 4);
switch (pins)
{
case 0:
GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 1:
GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 2:
GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 3:
GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
break;
default:
break;
}
}
17.03_QEP-双向计数模式
测试步骤
设置P2.11(QEP0_CH0), P2.12(QEP0_CH1)和P0.13(QEP0_Z)
实测数据
CCWCW_UpDown
qep_cnt = 0
| time | a | b | z | cnt |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 1 | 1 | 0 | 0 | 1 |
| 2 | 0 | 0 | 0 | 2 |
| 3 | 1 | 0 | 0 | 3 |
| 4 | 0 | 0 | 0 | 4 |
| 5 | 1 | 0 | 0 | 5 |
| 6 | 0 | 0 | 0 | 6 |
| 7 | 1 | 0 | 0 | 7 |
| 8 | 0 | 0 | 0 | 8 |
| 9 | 1 | 0 | 0 | 9 |
| 10 | 0 | 0 | 0 | 10 |
| 11 | 1 | 0 | 0 | 11 |
| 12 | 0 | 0 | 0 | 12 |
| 13 | 1 | 0 | 0 | 13 |
| 14 | 0 | 0 | 0 | 14 |
| 15 | 1 | 0 | 0 | 15 |
| 16 | 0 | 1 | 0 | 15 |
| 17 | 0 | 0 | 0 | 14 |
| 18 | 0 | 1 | 0 | 13 |
| 19 | 0 | 0 | 0 | 12 |
| 20 | 0 | 1 | 0 | 11 |
| 21 | 0 | 0 | 0 | 10 |
| 22 | 0 | 1 | 0 | 9 |
| 23 | 0 | 0 | 0 | 8 |
| 24 | 0 | 1 | 0 | 7 |
| 25 | 0 | 0 | 0 | 6 |
| 26 | 0 | 1 | 0 | 5 |
| 27 | 0 | 0 | 0 | 4 |
| 28 | 0 | 1 | 0 | 3 |
| 29 | 0 | 0 | 0 | 2 |
| 30 | 0 | 1 | 0 | 1 |
| 31 | 0 | 0 | 0 | 0 |
参考代码
17.03_QEP_Count.c
/**
* @brief QEP-双向计数模式
* @details
* 设置P2.11(QEP0_CH0), P2.12(QEP0_CH1)和P0.13(QEP0_Z)
*/
#include "lks32mc09x_gpio.h"
#include "lks32mc09x_qep.h"
volatile uint32_t qep_cnt;
/**
* @brief 主函数
*/
int main(void)
{
// 配置QEP模块
QEP_InitTypeDef QEP_InitStruct;
QEP_StructInit(&QEP_InitStruct); // 初始化QEP结构体为默认值
QEP_InitStruct.Mode = QEP_Mode_CCWCW_UpDown; ///< 编码器模式选择
QEP_InitStruct.FE_CNT_EN = ENABLE; ///< 下降沿计数使能
QEP_InitStruct.ZC = QEP_ZC_NONE; ///< 上升沿清零
QEP_InitStruct.TH = 65535; ///< QEP计数门限寄存器
QEP_InitStruct.ClockDiv = 0; ///< 时钟分频
QEP_InitStruct.Filter = 0; ///< 信号输入滤波
QEP_InitStruct.IRQEna = 0; ///< 中断使能
QEP_Init(QEP0, &QEP_InitStruct); // 初始化QEP0模块
// 配置需要使用的gpio
GPIO_Config(GPIO2, GPIO_PinSource_11, GPIO_Mode_IN, GPIO_AF_QEP0);
GPIO_Config(GPIO2, GPIO_PinSource_12, GPIO_Mode_IN, GPIO_AF_QEP0);
GPIO_Config(GPIO0, GPIO_PinSource_13, GPIO_Mode_IN, GPIO_AF_QEP0);
// 主循环
while (1)
{
qep_cnt = QEP_GetCount(QEP0);
}
}
使用到的库函数
库函数部分代码
#define GPIO_AF_I2C 6
#define QEP0 ((QEP_TypeDef *)QEP0_BASE)
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_GPIO BIT11
#define SYS_MODULE_QEP0 BIT8
#define SYS_MODULE_TIMER0 BIT4
#define SYS_MODULE_TIMER1 BIT5
#define SYS_MODULE_TIMER2 BIT6
#define TIMER0 ((TIMER_TypeDef *)TIMER0_BASE)
#define TIMER1 ((TIMER_TypeDef *)TIMER1_BASE)
#define TIMER2 ((TIMER_TypeDef *)TIMER2_BASE)
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* @brief GPIO模式选择枚举
*/
typedef enum GPIO_Mode_TypeDef {
GPIO_Mode_IN = 0,
GPIO_Mode_OUT = 1,
GPIO_Mode_ANA = 2,
GPIO_Mode_IO = 3,
}} {enum_name};
/**
* @brief QEP初始化结构体定义
*/
typedef struct QEP_InitTypeDef {
uint32_t Mode;
uint32_t FE_CNT_EN;
uint16_t TH;
uint32_t ClockDiv;
uint32_t Filter;
uint32_t IRQEna;
uint32_t ZC;
};
/**
* @brief GPIO配置函数
* @param GPIOx: GPIO端口
* @param GPIO_PinSource: GPIO引脚
* @param mode: GPIO模式
* @param GPIO_AF_x: GPIO复用功能选择
*/
void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
{
GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = 1 << GPIO_PinSource;
GPIO_InitStructure.GPIO_Mode = mode;
if (GPIO_AF_x == GPIO_AF_I2C)
{
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
}
else
{
__NOP();
}
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**
* @brief QEP初始化函数(初始化完成后会自动使能,并使能timer2)
* @param QEPx QEP模块指针
* @param TIM_QEPInitStruct 初始化结构体指针
*/
void QEP_Init(QEP_TypeDef *QEPx, QEP_InitTypeDef *TIM_QEPInitStruct)
{
// 使能QEP模块时钟
if (QEPx == QEP0)
{
SYS_ModuleClockCmd(SYS_MODULE_QEP0, ENABLE);
TIMER_Enable(TIMER2);
}
else
{
__NOP();
}
// QEP配置寄存器
QEPx->CFG = BIT15 |
(TIM_QEPInitStruct->Filter << 16) |
(TIM_QEPInitStruct->FE_CNT_EN << 10) |
(TIM_QEPInitStruct->Mode << 8) |
(TIM_QEPInitStruct->ZC);
// 设置计数门限
QEPx->TH = TIM_QEPInitStruct->TH;
// 设置中断使能
QEPx->IE = TIM_QEPInitStruct->IRQEna;
}
/**
* @brief QEP结构体初始化函数
* @param TIM_QEPInitStruct 初始化结构体指针
*/
void QEP_StructInit(QEP_InitTypeDef *TIM_QEPInitStruct)
{
for (int i = 0; i < sizeof(QEP_InitTypeDef) / sizeof(uint32_t); i++)
{
((uint32_t *)TIM_QEPInitStruct)[i] = 0;
}
}
/**
* @brief 使能Timer
* @param TIMERx Timer实例指针
*/
void TIMER_Enable(TIMER_TypeDef *TIMERx)
{
if (TIMERx == TIMER0)
{
SYS_ModuleClockCmd(SYS_MODULE_TIMER0, ENABLE);
}
else if (TIMERx == TIMER1)
{
SYS_ModuleClockCmd(SYS_MODULE_TIMER1, ENABLE);
}
else if (TIMERx == TIMER2)
{
SYS_ModuleClockCmd(SYS_MODULE_TIMER2, ENABLE);
}
else
{
__NOP();
}
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 获取QEP计数器当前值
* @param QEPx QEP模块指针
* @return 计数器当前值
*/
uint16_t QEP_GetCount(QEP_TypeDef *QEPx)
{
return QEPx->CNT;
}
/**
* @brief 配置GPIO引脚复用功能
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource 引脚源
* @param GPIO_AF 复用功能选择
*/
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
uint8_t offset;
uint8_t pins = GPIO_PinSource >> 2;
offset = ((GPIO_PinSource & 0x3) * 4);
switch (pins)
{
case 0:
GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 1:
GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 2:
GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 3:
GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
break;
default:
break;
}
}
18.01_SIF-发送数据
测试步骤
在P2.6上发送0x0xf0,并记录实际波形
实测数据
参考代码
18.01_SIF_SendData.c
/**
* @brief SIF-发送数据
* @details
* 在P2.6上发送0x0xf0,并记录实际波形
*/
#include "lks32mc09x_lib.h"
void SoftDelaymS(uint32_t n);
volatile int baud = 115200; // 设定波特率
volatile int refbaud = 115200; // 设定波特率
volatile int sendflg = 0; // 设定波特率
int main(void)
{
__disable_irq();
SIF_InitTypeDef SIF_InitStruct;
SIF_StructInit(&SIF_InitStruct);
SIF_InitStruct.SIF_DONE = DISABLE; // 传输完成后默认低电平
SIF_InitStruct.SIF_SYNC = DISABLE; // 同步信号默认低电平
SIF_InitStruct.SIF_SYNC_PULSE = DISABLE; // 同步信号有电平反转
SIF_InitStruct.SIF_DONE_VLD = DISABLE; // 无结束信号
SIF_InitStruct.SIF_SYNC_VLD = DISABLE; // 有同步信号
SIF_InitStruct.SIF_RATIO = DISABLE; // 数据占空比 2:1
SIF_InitStruct.SIF_MSB = ENABLE; // 数据传送顺序 高字节在前
SIF_InitStruct.SIF_EN = ENABLE; // SIF模块使能
SIF_InitStruct.SIF_TOSC = 2; // 时基设置 47 * 333 = 15.67US
SIF_InitStruct.SIF_TSTH1 = 2; // 同步时长 1000 * SIF_TOSC * 32
SIF_InitStruct.SIF_TDTH1 = 2; // 结束信号时长 1MS
SIF_InitStruct.SIF_IRQ_IF = ENABLE; // 有SIF中断标志位
SIF_InitStruct.SIF_DMA_EN = DISABLE; // DMA传输使能
SIF_InitStruct.SIF_IRQ_EN = DISABLE; // SIF中断关闭
SIF_Init(SIF0, &SIF_InitStruct);
GPIO_Config(GPIO2, GPIO_PinSource_6, GPIO_Mode_OUT, GPIO_AF_SIF);
for (;;)
{
if (sendflg)
{
SIF0->IRQ = BIT4;
SIF_Senddata(SIF0, 0xf0);
while ((SIF0->IRQ & BIT4) == 0)
;
sendflg = 0;
}
}
}
使用到的库函数
库函数部分代码
#define GPIO_AF_I2C 6
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_GPIO BIT11
#define SYS_MODULE_SIF0 BIT20
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* @brief GPIO模式选择枚举
*/
typedef enum GPIO_Mode_TypeDef {
GPIO_Mode_IN = 0,
GPIO_Mode_OUT = 1,
GPIO_Mode_ANA = 2,
GPIO_Mode_IO = 3,
}} {enum_name};
/**
* @brief SIF初始化结构体定义
*/
typedef struct SIF_InitTypeDef {
uint8_t SIF_DONE;
uint8_t SIF_SYNC;
uint8_t SIF_SYNC_PULSE;
uint8_t SIF_DONE_VLD;
uint8_t SIF_SYNC_VLD;
uint8_t SIF_RATIO;
uint8_t SIF_MSB;
uint8_t SIF_EN;
uint16_t SIF_TOSC;
uint16_t SIF_TSTH1;
uint8_t SIF_TDTH1;
uint8_t SIF_IRQ_IF;
uint8_t SIF_DMA_EN;
uint8_t SIF_IRQ_EN;
};
/**
* @brief GPIO配置函数
* @param GPIOx: GPIO端口
* @param GPIO_PinSource: GPIO引脚
* @param mode: GPIO模式
* @param GPIO_AF_x: GPIO复用功能选择
*/
void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
{
GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = 1 << GPIO_PinSource;
GPIO_InitStructure.GPIO_Mode = mode;
if (GPIO_AF_x == GPIO_AF_I2C)
{
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
}
else
{
__NOP();
}
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 初始化SIF模块
* @param SIFx 指向SIF_TypeDef结构体的指针,表示具体的SIF实例
* @param SIFInitStruct 指向SIF_InitTypeDef结构体的指针,包含初始化参数
*/
void SIF_Init(SIF_TypeDef *SIFx, SIF_InitTypeDef *SIFInitStruct)
{
// 使能SIF模块时钟
SYS_ModuleClockCmd(SYS_MODULE_SIF0, ENABLE);
// 配置SIF寄存器
SIFx->CFG = (SIFInitStruct->SIF_DONE << 7) |
(SIFInitStruct->SIF_SYNC << 6) |
(SIFInitStruct->SIF_SYNC_PULSE << 5) |
(SIFInitStruct->SIF_DONE_VLD << 4) |
(SIFInitStruct->SIF_SYNC_VLD << 3) |
(SIFInitStruct->SIF_RATIO << 2) |
(SIFInitStruct->SIF_MSB << 1) |
SIFInitStruct->SIF_EN;
SIFx->TOSC = SIFInitStruct->SIF_TOSC;
SIFx->TSTH1 = SIFInitStruct->SIF_TSTH1;
SIFx->TDTH1 = SIFInitStruct->SIF_TDTH1;
SIFx->IRQ = (SIFInitStruct->SIF_IRQ_IF << 4) |
(SIFInitStruct->SIF_DMA_EN << 1) |
SIFInitStruct->SIF_IRQ_EN;
}
/**
* @brief 初始化SIF结构体
* @param SIFInitStruct 指向SIF_InitTypeDef结构体的指针
*/
void SIF_StructInit(SIF_InitTypeDef *SIFInitStruct)
{
for (int i = 0; i < sizeof(SIF_InitTypeDef); i++)
{
((uint8_t *)SIFInitStruct)[i] = 0;
}
}
/**
* @brief 发送数据
* @param Data 要发送的数据
*/
void SIF_Senddata(SIF_TypeDef *SIFx, uint8_t Data)
{
SIFx->WDATA = Data;
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 配置GPIO引脚复用功能
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource 引脚源
* @param GPIO_AF 复用功能选择
*/
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
uint8_t offset;
uint8_t pins = GPIO_PinSource >> 2;
offset = ((GPIO_PinSource & 0x3) * 4);
switch (pins)
{
case 0:
GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 1:
GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 2:
GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 3:
GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
break;
default:
break;
}
}
19.01_SPI-读写SPI_FLASH
测试步骤
初始化SPI,并和SPI_FLASH通信。
实测数据
ID = ef4018
testflg = 2
测试通过
参考代码
19.01_SPI_SendData.c
/**
* @brief SPI-读写SPI_FLASH
* @details
* 初始化SPI,并和SPI_FLASH通信。
*/
#include "lks32mc09x_lib.h"
uint8_t write_data[256] = {0};
uint8_t read_data[256] = {0};
volatile uint32_t testflg = 0; // 测试状态,0空闲 1进行中 2写入数据&读回数据一致 3写入数据&读回数据不一致
volatile uint32_t id;
void SoftDelaymS(uint32_t n);
uint32_t SPI_FLASH_ReadID(void);
void SPI_WaitForWriteEnd(void);
void SPI_FLASH_WriteEnable(void);
void SPI_FLASH_SectorErase(u32 SectorAddr);
void SPI_FLASH_BlockErase64k(u32 SectorAddr);
void SPI_Write_Data(uint32_t addr, uint8_t *writeBuff, uint32_t numByteToWrite);
void SPI_Read_Data(uint32_t addr, uint8_t *readBuff, uint32_t numByteToRead);
/**
* @brief 主函数,用于测试HSI时钟输出及GPIO翻转
*/
int main(void)
{
{
SPI_InitTypeDef SPI_InitStruct;
SPI_StructInit(&SPI_InitStruct); // SPI结构体初始化
SPI_InitStruct.Duplex = SPI_Full; // 双工模式设置
SPI_InitStruct.Mode = SPI_Master; // master主机模式
SPI_InitStruct.EN = ENABLE; // 使能SPI模块
SPI_InitStruct.TRANS_MODE = SPI_DMA_DISABLE; // 选择SPI搬移方式,由MCU搬运数据到SPI
SPI_InitStruct.Trig = 0; // 内部自动触发传输
SPI_InitStruct.ENDIAN = SPI_FIRSTSEND_MSB; // 该字节先发送 LSB
SPI_InitStruct.CPHA = 0; // 第一个沿为发送数据时刻
SPI_InitStruct.CPOL = 0; // CLK默认高电平
SPI_InitStruct.BaudRate = 100; // SPI 传输速度 = 系统时钟 / (2*(BAUD + 1))
SPI_InitStruct.ByteLength = 8; // 发送8个字节
SPI_InitStruct.IRQEna = DISABLE; // 关闭SPI各中断
SPI_Init(SPI0, &SPI_InitStruct); // SPI初始化程序
}
GPIO_Config(GPIO1, GPIO_PinSource_1, GPIO_Mode_OUT, GPIO_AF_GPIO); // CS
GPIO_Config(GPIO0, GPIO_PinSource_14, GPIO_Mode_OUT, GPIO_AF_SPI); // CLK
GPIO_Config(GPIO1, GPIO_PinSource_0, GPIO_Mode_IN, GPIO_AF_SPI); // DI
GPIO_Config(GPIO0, GPIO_PinSource_15, GPIO_Mode_OUT, GPIO_AF_SPI); // DO
GPIO_SetBits(GPIO1, GPIO_Pin_1);
for (int i = 0; i < 256; i++)
{
write_data[i] = i; // 确保每次写入的数据都不一样
}
SPI0->IE = BIT2;
while (1)
{
if (testflg == 1)
{
id = SPI_FLASH_ReadID();
id = SPI_FLASH_ReadID();
SPI_FLASH_BlockErase64k(0);
for (int len = 1; len < 256; len++) // 从1个数据到32个数据进行测试,以确保不同数据长度下i2c通信是否正常
{
for (int i = 0; i < len; i++)
{
write_data[i] = i + len; // 确保每次写入的数据都不一样
}
SPI_Write_Data(len << 8, write_data, len);
SPI_Read_Data(len << 8, read_data, len);
for (int i = 0; i < len; i++)
{
if (read_data[i] != write_data[i])
{
testflg = 3;
}
}
}
if (testflg != 3)
{
testflg = 2; // 测试完成,并且没有出现错误
}
}
}
}
uint8_t SPI_FLASH_Send_Byte(uint8_t val)
{
SPI0->IE = BIT2;
SPI0->TX_DATA = val;
while ((SPI0->IE & BIT2) == 0)
;
val = SPI0->RX_DATA;
return val;
}
/**
* @brief 读取设备ID
* @return 设备ID
*/
uint32_t SPI_FLASH_ReadID(void)
{
uint32_t temp = 0, temp0 = 0, temp1 = 0, temp2 = 0;
GPIO_ResetBits(GPIO1, GPIO_Pin_1); // 低电平片选有效,SPI通讯开始
SPI_FLASH_Send_Byte(0x9f); // 发送读取ID指令
temp0 = SPI_FLASH_Send_Byte(0x00); // 读取制造商ID(M7-M0)
temp1 = SPI_FLASH_Send_Byte(0x00); // 读取芯片ID(ID15~ID8)
temp2 = SPI_FLASH_Send_Byte(0x00); // 读取芯片ID(ID7~ID0)
GPIO_SetBits(GPIO1, GPIO_Pin_1); // 停止SPI通讯
temp = (temp0 << 16) | (temp1 << 8) | temp2; // 组合数据
return temp;
}
/**
* @brief 等待flash内部时序操作完成
*/
void SPI_WaitForWriteEnd(void)
{
uint8_t status_reg = 0;
GPIO_ResetBits(GPIO1, GPIO_Pin_1);
SPI_FLASH_Send_Byte(0x05); // 发送“Read Status Register”指令
do
{
SoftDelaymS(1);
status_reg = SPI_FLASH_Send_Byte(0x00);
} while ((status_reg & 0x01) == 1);
GPIO_SetBits(GPIO1, GPIO_Pin_1);
}
/**
* @brief 写使能
*/
void SPI_FLASH_WriteEnable(void)
{
GPIO_ResetBits(GPIO1, GPIO_Pin_1);
SPI_FLASH_Send_Byte(0x06); // 发送“Write Enable”指令,06h
GPIO_SetBits(GPIO1, GPIO_Pin_1);
}
/**
* @brief 块擦除(64k)
* @param SectorAddr 擦除地址
*/
void SPI_FLASH_BlockErase64k(u32 SectorAddr)
{
SPI_WaitForWriteEnd();
SPI_FLASH_WriteEnable();
GPIO_ResetBits(GPIO1, GPIO_Pin_1);
SPI_FLASH_Send_Byte(0xd8);
SPI_FLASH_Send_Byte((SectorAddr & 0xFF0000) >> 16); // 高位擦除地址A23~A16
SPI_FLASH_Send_Byte((SectorAddr & 0xFF00) >> 8); // 中位擦除地址A15~A8
SPI_FLASH_Send_Byte(SectorAddr & 0xFF); // 低位擦除地址A7~A0
GPIO_SetBits(GPIO1, GPIO_Pin_1);
SPI_WaitForWriteEnd();
}
/**
* @brief 扇区擦除
* @param SectorAddr 擦除地址
*/
void SPI_FLASH_SectorErase(u32 SectorAddr)
{
SPI_FLASH_WriteEnable();
SPI_WaitForWriteEnd();
GPIO_ResetBits(GPIO1, GPIO_Pin_1);
SPI_FLASH_Send_Byte(0x20);
SPI_FLASH_Send_Byte((SectorAddr & 0xFF0000) >> 16); // 高位擦除地址A23~A16
SPI_FLASH_Send_Byte((SectorAddr & 0xFF00) >> 8); // 中位擦除地址A15~A8
SPI_FLASH_Send_Byte(SectorAddr & 0xFF); // 低位擦除地址A7~A0
GPIO_SetBits(GPIO1, GPIO_Pin_1);
SPI_WaitForWriteEnd();
}
/**
* @brief 数据写入
* @param addr 写入地址
* @param writeBuff 包含数据的指针
* @param numByteToWrite 写入字节数
*/
void SPI_Write_Data(uint32_t addr, uint8_t *writeBuff, uint32_t numByteToWrite)
{
SPI_FLASH_WriteEnable();
GPIO_ResetBits(GPIO1, GPIO_Pin_1);
SPI_FLASH_Send_Byte(0x02); // 发送“Page Program”指令,02h
SPI_FLASH_Send_Byte((addr >> 16) & 0xff);
SPI_FLASH_Send_Byte((addr >> 8) & 0xff);
SPI_FLASH_Send_Byte(addr & 0xff);
while (numByteToWrite--)
{
SPI_FLASH_Send_Byte(*writeBuff);
writeBuff++;
}
GPIO_SetBits(GPIO1, GPIO_Pin_1);
SPI_WaitForWriteEnd();
}
/**
* @brief 读取flash数据
* @param addr 读取地址
* @param readBuff 存放读出的数据的指针
* @param numByteToRead 读出的字节数
*/
void SPI_Read_Data(uint32_t addr, uint8_t *readBuff, uint32_t numByteToRead)
{
GPIO_ResetBits(GPIO1, GPIO_Pin_1);
SPI_FLASH_Send_Byte(0x03);
SPI_FLASH_Send_Byte((addr >> 16) & 0xff);
SPI_FLASH_Send_Byte((addr >> 8) & 0xff);
SPI_FLASH_Send_Byte(addr & 0xff);
while (numByteToRead--)
{
*readBuff = SPI_FLASH_Send_Byte(0x00);
readBuff++;
}
GPIO_SetBits(GPIO1, GPIO_Pin_1);
}
使用到的库函数
库函数部分代码
#define GPIO_AF_I2C 6
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_GPIO BIT11
#define SYS_MODULE_SPI0 BIT0
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* @brief GPIO模式选择枚举
*/
typedef enum GPIO_Mode_TypeDef {
GPIO_Mode_IN = 0,
GPIO_Mode_OUT = 1,
GPIO_Mode_ANA = 2,
GPIO_Mode_IO = 3,
}} {enum_name};
/**
* @brief SPI模块初始化结构体
*/
typedef struct SPI_InitTypeDef {
SPI_Duplex Duplex;
uint8_t CS;
SPI_Mode Mode;
uint8_t CPHA;
uint8_t CPOL;
uint8_t ENDIAN;
uint8_t EN;
uint8_t IRQEna;
uint8_t Trig;
uint8_t TRANS_MODE;
uint16_t BaudRate;
uint8_t ByteLength;
};
/**
* @brief GPIO配置函数
* @param GPIOx: GPIO端口
* @param GPIO_PinSource: GPIO引脚
* @param mode: GPIO模式
* @param GPIO_AF_x: GPIO复用功能选择
*/
void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
{
GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = 1 << GPIO_PinSource;
GPIO_InitStructure.GPIO_Mode = mode;
if (GPIO_AF_x == GPIO_AF_I2C)
{
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
}
else
{
__NOP();
}
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**
* @brief SPI初始化结构体默认配置函数
* @param SPI_InitStruct 指向SPI初始化结构体的指针
*/
void SPI_StructInit(SPI_InitTypeDef *SPI_InitStruct)
{
for (int i = 0; i < sizeof(SPI_InitTypeDef); i++)
{
((uint8_t *)SPI_InitStruct)[i] = 0;
}
}
/**
* @brief SPI模块初始化函数
* @param SPIx SPI模块指针
* @param SPI_InitStruct 指向SPI初始化结构体的指针
*/
void SPI_Init(SPI_TypeDef *SPIx, SPI_InitTypeDef *SPI_InitStruct)
{
// 使能SPI模块时钟
SYS_ModuleClockCmd(SYS_MODULE_SPI0, ENABLE);
// 配置SPI控制寄存器
SPIx->CFG = (SPI_InitStruct->Duplex << 6) |
(SPI_InitStruct->CS << 5) |
(SPI_InitStruct->Mode << 4) |
(SPI_InitStruct->CPHA << 3) |
(SPI_InitStruct->CPOL << 2) |
(SPI_InitStruct->ENDIAN << 1) |
SPI_InitStruct->EN;
// 配置SPI中断使能寄存器
SPIx->IE = (SPI_InitStruct->IRQEna << 4) |
(SPI_InitStruct->Trig << 3);
// 配置SPI波特率
SPIx->DIV = (SPI_InitStruct->TRANS_MODE << 15) |
(SPI_InitStruct->BaudRate);
// 配置SPI数据长度
SPIx->SIZE = SPI_InitStruct->ByteLength;
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 复位指定的GPIO引脚位
* @param GPIOx GPIO模块指针
* @param GPIO_Pin 要复位的引脚
*/
void GPIO_ResetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
GPIOx->PDO &= ~GPIO_Pin;
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 设置指定的GPIO引脚位
* @param GPIOx GPIO模块指针
* @param GPIO_Pin 要设置的引脚
*/
void GPIO_SetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
GPIOx->PDO |= GPIO_Pin;
}
/**
* @brief 配置GPIO引脚复用功能
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource 引脚源
* @param GPIO_AF 复用功能选择
*/
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
uint8_t offset;
uint8_t pins = GPIO_PinSource >> 2;
offset = ((GPIO_PinSource & 0x3) * 4);
switch (pins)
{
case 0:
GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 1:
GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 2:
GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 3:
GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
break;
default:
break;
}
}
20.01_TIMER-PWM输出
测试步骤
- 使用P0.15并复用为timer0
- 初始化timer0 频率10k 输出占空比50%
- 注意:必须配置输出使能
实测数据
实测频率为9.981361khz
实测输出占空比为50.014572%
参考代码
20.01_TIMER.c
/**
* @brief TIMER-PWM输出
* @details
* 1. 使用P0.15并复用为timer0
* 2. 初始化timer0 频率10k 输出占空比50%
* 3. 注意:必须配置输出使能
*/
#include "lks32mc09x_gpio.h"
#include "lks32mc09x_sys.h"
#include "lks32mc09x_timer.h"
void Timer0_IRQHandler(void);
int main(void)
{
GPIO_Config(GPIO0, GPIO_PinSource_15, GPIO_Mode_OUT, GPIO_AF_TIMER0);
// 初始化TIMER0
TIMER_TimerInitTypeDef TIMER_InitStruct;
TIMER_StructInit(&TIMER_InitStruct);
// 配置TIMER0为比较模式,输出PWM
TIMER_InitStruct.EN = ENABLE; ///< 定时器使能,ENABLE使能,DISABLE失能,影响timer的cnt是否计数
TIMER_InitStruct.CAP1_CLR_EN = 0; ///< 当发生CAP1捕获事件时,清零 Timer 计数器,高有效
TIMER_InitStruct.CAP0_CLR_EN = 0; ///< 当发生CAP0捕获事件时,清零 Timer 计数器,高有效
TIMER_InitStruct.ONE_TRIG = 0; ///< 单次发送模式,此位需要在Timer比较模式下使用,且对应定时器使能需设置为0
TIMER_InitStruct.CENTER = 0; ///< 中心计数模式使能,0:Timer向上从0计数至TH,然后回0,或Timer向下从TH计数至0,然后回到TH,1:Timer向上从0计数至TH,然后向下计数至0
TIMER_InitStruct.DIR = 0; ///< 0:0->TH 递增计数,1:TH->递减计数
TIMER_InitStruct.ClockDiv = 0; ///< Timer计数器频率配置,0:1分频,1:2分频,2:4分频,3:8分频,4:16分频,5:32分频,6:64分频,7:128分频
TIMER_InitStruct.ETON = 0; ///< Timer计数器计数外部启动使能,0: 自动运行,1:外部事件触发计数
TIMER_InitStruct.GATE_EN = 0; ///< Timer暂停使能,0:不暂停 1:当外部信号为低时,Timer暂停计数,外部信号根据TIMER1_EVT.EVT_SRC 进行选择
TIMER_InitStruct.RL_EN = 0; ///< Timer重装使能,0:禁用外部事件重装,1:使能外部事件重装
TIMER_InitStruct.XCLK_EN = 0; ///< Timer时钟源,0: 芯片内部时钟,1:外部时钟
TIMER_InitStruct.SRC1 = 0; ///< Timer 捕获模式通道1信号来源,0:Timer通道0输入信号,1:Timer通道1输入信号,2:CLU0输出信号,3:CLU1输出信号,4:CLU2输出信号,5:CLU3输出信号,6:比较器0输出信号,7:比较器1输出信号,8:比较器2输出信号,9:Timer通道0和1的异或信号
TIMER_InitStruct.CH1Output = 0; ///< Timer 通道1在比较模式下的输出极性控制,当计数器 CNT使用到的库函数
库函数部分代码
#define GPIO_AF_I2C 6
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_CLK_CFG_CLK_DIV_MASK (uint32_t)(0xFF << SYS_CLK_CFG_CLK_DIV_POS)
#define SYS_CLK_CFG_CLK_DIV_POS 0
#define SYS_CLK_CFG_CLK_SEL_MASK (uint32_t)(0x3 << SYS_CLK_CFG_CLK_SEL_POS)
#define SYS_CLK_CFG_CLK_SEL_POS 8
#define SYS_MODULE_GPIO BIT11
#define SYS_MODULE_TIMER0 BIT4
#define SYS_MODULE_TIMER1 BIT5
#define SYS_MODULE_TIMER2 BIT6
#define TIMER0 ((TIMER_TypeDef *)TIMER0_BASE)
#define TIMER1 ((TIMER_TypeDef *)TIMER1_BASE)
#define TIMER2 ((TIMER_TypeDef *)TIMER2_BASE)
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* @brief GPIO模式选择枚举
*/
typedef enum GPIO_Mode_TypeDef {
GPIO_Mode_IN = 0,
GPIO_Mode_OUT = 1,
GPIO_Mode_ANA = 2,
GPIO_Mode_IO = 3,
}} {enum_name};
/**
* @brief Timer初始化结构体
*/
typedef struct TIMER_TimerInitTypeDef {
uint8_t EN;
uint32_t CAP1_CLR_EN;
uint32_t CAP0_CLR_EN;
uint32_t ONE_TRIG;
uint32_t CENTER;
uint32_t DIR;
uint32_t ClockDiv;
uint32_t ETON;
uint32_t GATE_EN;
uint32_t RL_EN;
uint32_t XCLK_EN;
uint32_t SRC1;
uint32_t CH1Output;
uint32_t CH1_WorkMode;
uint32_t CH1_FE_CAP_EN;
uint32_t CH1_RE_CAP_EN;
uint32_t SRC0;
uint32_t CH0Output;
uint32_t CH0_WorkMode;
uint32_t CH0_FE_CAP_EN;
uint32_t CH0_RE_CAP_EN;
uint32_t TH;
uint32_t CNT;
uint32_t CHN0;
uint32_t CHN1;
uint32_t EVT;
uint32_t FLT;
uint32_t IE;
uint32_t CMP0_TRIGGER_MODE;
uint32_t CMP1_TRIGGER_MODE;
uint32_t SHADOW;
uint32_t CH1_DEFAULT;
uint32_t CH0_DEFAULT;
uint32_t HALT_PRT;
uint32_t FAIL_SEL;
uint32_t FAIL_POL;
uint32_t FAIL_EN;
uint32_t MOE;
};
/**
* @brief GPIO配置函数
* @param GPIOx: GPIO端口
* @param GPIO_PinSource: GPIO引脚
* @param mode: GPIO模式
* @param GPIO_AF_x: GPIO复用功能选择
*/
void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
{
GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = 1 << GPIO_PinSource;
GPIO_InitStructure.GPIO_Mode = mode;
if (GPIO_AF_x == GPIO_AF_I2C)
{
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
}
else
{
__NOP();
}
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**
* @brief Timer初始化
* @param TIMERx Timer实例指针
* @param TIMER_InitStruct Timer初始化结构体指针
*/
void TIMER_Init(TIMER_TypeDef *TIMERx, TIMER_TimerInitTypeDef *TIMER_InitStruct)
{
uint32_t th = TIMER_InitStruct->TH;
TIMER_Enable(TIMERx);
if (TIMERx != TIMER2) // 只有timer2是32位定时器
{
if (th > 0xffff)
{
th = 0xffff;
}
else
{
__NOP();
}
}
else
{
__NOP();
}
// 配置Timer寄存器
TIMERx->CFG2 = (TIMER_InitStruct->EN << 15) |
(TIMER_InitStruct->ONE_TRIG << 9) |
(TIMER_InitStruct->CENTER << 8) |
(TIMER_InitStruct->DIR << 6) |
(TIMER_InitStruct->ClockDiv << 4) |
(TIMER_InitStruct->ETON << 3) |
(TIMER_InitStruct->GATE_EN << 2) |
(TIMER_InitStruct->RL_EN << 1) |
TIMER_InitStruct->XCLK_EN;
TIMERx->CFG0 = (TIMER_InitStruct->CMP0_TRIGGER_MODE << 9) |
(TIMER_InitStruct->CAP0_CLR_EN << 8) |
(TIMER_InitStruct->SRC0 << 4) |
(TIMER_InitStruct->CH0Output << 3) |
(TIMER_InitStruct->CH0_WorkMode << 2) |
(TIMER_InitStruct->CH0_FE_CAP_EN << 1) |
(TIMER_InitStruct->CH0_RE_CAP_EN << 0);
TIMERx->CFG1 = (TIMER_InitStruct->CMP1_TRIGGER_MODE << 9) |
(TIMER_InitStruct->CAP1_CLR_EN << 8) |
(TIMER_InitStruct->SRC1 << 4) |
(TIMER_InitStruct->CH1Output << 3) |
(TIMER_InitStruct->CH1_WorkMode << 2) |
(TIMER_InitStruct->CH1_FE_CAP_EN << 1) |
(TIMER_InitStruct->CH1_RE_CAP_EN << 0);
TIMERx->CHN0 = TIMER_InitStruct->CHN0;
TIMERx->CHN1 = TIMER_InitStruct->CHN1;
TIMERx->TH = th;
TIMERx->CNT = TIMER_InitStruct->CNT;
TIMERx->EVT = TIMER_InitStruct->EVT;
TIMERx->FLT = TIMER_InitStruct->FLT;
TIMERx->IE = TIMER_InitStruct->IE;
TIMERx->IO = (TIMER_InitStruct->HALT_PRT << 7) |
(TIMER_InitStruct->CH0_DEFAULT << 8) |
(TIMER_InitStruct->CH1_DEFAULT << 9) |
(TIMER_InitStruct->MOE << 6) |
(TIMER_InitStruct->FAIL_SEL << 2) |
(TIMER_InitStruct->FAIL_POL << 1) |
(TIMER_InitStruct->FAIL_EN << 0);
// 影子寄存器配置
TIMERx->CFG2 |= (TIMER_InitStruct->SHADOW << 10);
}
/**
* @brief Timer结构体初始化
* @param TIMER_InitStruct Timer初始化结构体指针
*/
void TIMER_StructInit(TIMER_TimerInitTypeDef *TIMER_InitStruct)
{
for (int i = 0; i < sizeof(TIMER_TimerInitTypeDef) / sizeof(uint32_t); i++)
{
((uint32_t *)TIMER_InitStruct)[i] = 0;
}
}
/**
* @brief 使能Timer
* @param TIMERx Timer实例指针
*/
void TIMER_Enable(TIMER_TypeDef *TIMERx)
{
if (TIMERx == TIMER0)
{
SYS_ModuleClockCmd(SYS_MODULE_TIMER0, ENABLE);
}
else if (TIMERx == TIMER1)
{
SYS_ModuleClockCmd(SYS_MODULE_TIMER1, ENABLE);
}
else if (TIMERx == TIMER2)
{
SYS_ModuleClockCmd(SYS_MODULE_TIMER2, ENABLE);
}
else
{
__NOP();
}
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 获取当前MCU时钟
* @return 当前MCU时钟频率
*/
uint32_t SYS_ReadMcuClk(void)
{
uint32_t clk = 96000000;
uint32_t clkdiv = (SYS_CLK_CFG & SYS_CLK_CFG_CLK_DIV_MASK) >> SYS_CLK_CFG_CLK_DIV_POS;
uint32_t clksel = (SYS_CLK_CFG & SYS_CLK_CFG_CLK_SEL_MASK) >> SYS_CLK_CFG_CLK_SEL_POS;
switch (clksel) // 0: HRC 1: PLL 2: LRC
{
case 0: // HRC
clk = 8000000;
break;
case 1: // PLL
switch (clkdiv)
{
case 0xff: // 96M
clk = 96000000;
break;
case 0x55: // 48M
clk = 48000000;
break;
case 0x11: // 24M
clk = 24000000;
break;
case 0x01: // 12M
clk = 12000000;
break;
case 0x00: // 12M
clk = 12000000;
break;
default: // 逐位计算
clk = 0;
for (int i = 0; i < 8; i++)
{
if (clkdiv & (1 << i))
{
clk += 12000000;
}
else
{
__NOP();
}
}
break;
}
break;
case 2: // LRC
clk = 32000;
break;
default:
break;
}
return clk;
}
/**
* @brief 配置GPIO引脚复用功能
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource 引脚源
* @param GPIO_AF 复用功能选择
*/
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
uint8_t offset;
uint8_t pins = GPIO_PinSource >> 2;
offset = ((GPIO_PinSource & 0x3) * 4);
switch (pins)
{
case 0:
GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 1:
GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 2:
GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 3:
GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
break;
default:
break;
}
}
20.02_TIMER-1mS定时器
测试步骤
- 初始化timer0,设置timer时钟为2分频(主频96M,分频后48M),周期为1ms
- 在timer中断里翻转P0.0
- 不配置timer分频的情况下,最大计数时间为0.682mS,大于该时间后需要设置时钟分频
实测数据
实测中断间隔时间为1.001958mS
参考代码
20.02_TIMER.c
/**
* @brief TIMER-1mS定时器
* @details
* 1. 初始化timer0,设置timer时钟为2分频(主频96M,分频后48M),周期为1ms
* 2. 在timer中断里翻转P0.0
* 3. 不配置timer分频的情况下,最大计数时间为0.682mS,大于该时间后需要设置时钟分频
*/
#include "lks32mc09x_gpio.h"
#include "lks32mc09x_sys.h"
#include "lks32mc09x_timer.h"
#include "lks32mc09x_user_manual.h"
void Timer0_IRQHandler(void);
int main(void)
{
GPIO_Config(GPIO0, GPIO_PinSource_0, GPIO_Mode_OUT, GPIO_AF_GPIO);
// 初始化TIMER0
TIMER_TimerInitTypeDef TIMER_InitStruct;
TIMER_StructInit(&TIMER_InitStruct);
// 配置TIMER0为比较模式,输出PWM
TIMER_InitStruct.EN = ENABLE; ///< 定时器使能,ENABLE使能,DISABLE失能,影响timer的cnt是否计数
TIMER_InitStruct.CAP1_CLR_EN = 0; ///< 当发生CAP1捕获事件时,清零 Timer 计数器,高有效
TIMER_InitStruct.CAP0_CLR_EN = 0; ///< 当发生CAP0捕获事件时,清零 Timer 计数器,高有效
TIMER_InitStruct.ONE_TRIG = 0; ///< 单次发送模式,此位需要在Timer比较模式下使用,且对应定时器使能需设置为0
TIMER_InitStruct.CENTER = 0; ///< 中心计数模式使能,0:Timer向上从0计数至TH,然后回0,或Timer向下从TH计数至0,然后回到TH,1:Timer向上从0计数至TH,然后向下计数至0
TIMER_InitStruct.DIR = 0; ///< 0:0->TH 递增计数,1:TH->递减计数
TIMER_InitStruct.ClockDiv = 1; ///< Timer计数器频率配置,0:1分频,1:2分频,2:4分频,3:8分频,4:16分频,5:32分频,6:64分频,7:128分频
TIMER_InitStruct.ETON = 0; ///< Timer计数器计数外部启动使能,0: 自动运行,1:外部事件触发计数
TIMER_InitStruct.GATE_EN = 0; ///< Timer暂停使能,0:不暂停 1:当外部信号为低时,Timer暂停计数,外部信号根据TIMER1_EVT.EVT_SRC 进行选择
TIMER_InitStruct.RL_EN = 0; ///< Timer重装使能,0:禁用外部事件重装,1:使能外部事件重装
TIMER_InitStruct.XCLK_EN = 0; ///< Timer时钟源,0: 芯片内部时钟,1:外部时钟
TIMER_InitStruct.SRC1 = 0; ///< Timer 捕获模式通道1信号来源,0:Timer通道0输入信号,1:Timer通道1输入信号,2:CLU0输出信号,3:CLU1输出信号,4:CLU2输出信号,5:CLU3输出信号,6:比较器0输出信号,7:比较器1输出信号,8:比较器2输出信号,9:Timer通道0和1的异或信号
TIMER_InitStruct.CH1Output = 0; ///< Timer 通道1在比较模式下的输出极性控制,当计数器 CNT使用到的库函数
库函数部分代码
#define GPIO_AF_I2C 6
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_CLK_CFG_CLK_DIV_MASK (uint32_t)(0xFF << SYS_CLK_CFG_CLK_DIV_POS)
#define SYS_CLK_CFG_CLK_DIV_POS 0
#define SYS_CLK_CFG_CLK_SEL_MASK (uint32_t)(0x3 << SYS_CLK_CFG_CLK_SEL_POS)
#define SYS_CLK_CFG_CLK_SEL_POS 8
#define SYS_MODULE_GPIO BIT11
#define SYS_MODULE_TIMER0 BIT4
#define SYS_MODULE_TIMER1 BIT5
#define SYS_MODULE_TIMER2 BIT6
#define TIMER0 ((TIMER_TypeDef *)TIMER0_BASE)
#define TIMER1 ((TIMER_TypeDef *)TIMER1_BASE)
#define TIMER2 ((TIMER_TypeDef *)TIMER2_BASE)
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* @brief GPIO模式选择枚举
*/
typedef enum GPIO_Mode_TypeDef {
GPIO_Mode_IN = 0,
GPIO_Mode_OUT = 1,
GPIO_Mode_ANA = 2,
GPIO_Mode_IO = 3,
}} {enum_name};
/**
* @brief Timer初始化结构体
*/
typedef struct TIMER_TimerInitTypeDef {
uint8_t EN;
uint32_t CAP1_CLR_EN;
uint32_t CAP0_CLR_EN;
uint32_t ONE_TRIG;
uint32_t CENTER;
uint32_t DIR;
uint32_t ClockDiv;
uint32_t ETON;
uint32_t GATE_EN;
uint32_t RL_EN;
uint32_t XCLK_EN;
uint32_t SRC1;
uint32_t CH1Output;
uint32_t CH1_WorkMode;
uint32_t CH1_FE_CAP_EN;
uint32_t CH1_RE_CAP_EN;
uint32_t SRC0;
uint32_t CH0Output;
uint32_t CH0_WorkMode;
uint32_t CH0_FE_CAP_EN;
uint32_t CH0_RE_CAP_EN;
uint32_t TH;
uint32_t CNT;
uint32_t CHN0;
uint32_t CHN1;
uint32_t EVT;
uint32_t FLT;
uint32_t IE;
uint32_t CMP0_TRIGGER_MODE;
uint32_t CMP1_TRIGGER_MODE;
uint32_t SHADOW;
uint32_t CH1_DEFAULT;
uint32_t CH0_DEFAULT;
uint32_t HALT_PRT;
uint32_t FAIL_SEL;
uint32_t FAIL_POL;
uint32_t FAIL_EN;
uint32_t MOE;
};
/**
* @brief GPIO配置函数
* @param GPIOx: GPIO端口
* @param GPIO_PinSource: GPIO引脚
* @param mode: GPIO模式
* @param GPIO_AF_x: GPIO复用功能选择
*/
void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
{
GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = 1 << GPIO_PinSource;
GPIO_InitStructure.GPIO_Mode = mode;
if (GPIO_AF_x == GPIO_AF_I2C)
{
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
}
else
{
__NOP();
}
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**
* @brief Timer初始化
* @param TIMERx Timer实例指针
* @param TIMER_InitStruct Timer初始化结构体指针
*/
void TIMER_Init(TIMER_TypeDef *TIMERx, TIMER_TimerInitTypeDef *TIMER_InitStruct)
{
uint32_t th = TIMER_InitStruct->TH;
TIMER_Enable(TIMERx);
if (TIMERx != TIMER2) // 只有timer2是32位定时器
{
if (th > 0xffff)
{
th = 0xffff;
}
else
{
__NOP();
}
}
else
{
__NOP();
}
// 配置Timer寄存器
TIMERx->CFG2 = (TIMER_InitStruct->EN << 15) |
(TIMER_InitStruct->ONE_TRIG << 9) |
(TIMER_InitStruct->CENTER << 8) |
(TIMER_InitStruct->DIR << 6) |
(TIMER_InitStruct->ClockDiv << 4) |
(TIMER_InitStruct->ETON << 3) |
(TIMER_InitStruct->GATE_EN << 2) |
(TIMER_InitStruct->RL_EN << 1) |
TIMER_InitStruct->XCLK_EN;
TIMERx->CFG0 = (TIMER_InitStruct->CMP0_TRIGGER_MODE << 9) |
(TIMER_InitStruct->CAP0_CLR_EN << 8) |
(TIMER_InitStruct->SRC0 << 4) |
(TIMER_InitStruct->CH0Output << 3) |
(TIMER_InitStruct->CH0_WorkMode << 2) |
(TIMER_InitStruct->CH0_FE_CAP_EN << 1) |
(TIMER_InitStruct->CH0_RE_CAP_EN << 0);
TIMERx->CFG1 = (TIMER_InitStruct->CMP1_TRIGGER_MODE << 9) |
(TIMER_InitStruct->CAP1_CLR_EN << 8) |
(TIMER_InitStruct->SRC1 << 4) |
(TIMER_InitStruct->CH1Output << 3) |
(TIMER_InitStruct->CH1_WorkMode << 2) |
(TIMER_InitStruct->CH1_FE_CAP_EN << 1) |
(TIMER_InitStruct->CH1_RE_CAP_EN << 0);
TIMERx->CHN0 = TIMER_InitStruct->CHN0;
TIMERx->CHN1 = TIMER_InitStruct->CHN1;
TIMERx->TH = th;
TIMERx->CNT = TIMER_InitStruct->CNT;
TIMERx->EVT = TIMER_InitStruct->EVT;
TIMERx->FLT = TIMER_InitStruct->FLT;
TIMERx->IE = TIMER_InitStruct->IE;
TIMERx->IO = (TIMER_InitStruct->HALT_PRT << 7) |
(TIMER_InitStruct->CH0_DEFAULT << 8) |
(TIMER_InitStruct->CH1_DEFAULT << 9) |
(TIMER_InitStruct->MOE << 6) |
(TIMER_InitStruct->FAIL_SEL << 2) |
(TIMER_InitStruct->FAIL_POL << 1) |
(TIMER_InitStruct->FAIL_EN << 0);
// 影子寄存器配置
TIMERx->CFG2 |= (TIMER_InitStruct->SHADOW << 10);
}
/**
* @brief Timer结构体初始化
* @param TIMER_InitStruct Timer初始化结构体指针
*/
void TIMER_StructInit(TIMER_TimerInitTypeDef *TIMER_InitStruct)
{
for (int i = 0; i < sizeof(TIMER_TimerInitTypeDef) / sizeof(uint32_t); i++)
{
((uint32_t *)TIMER_InitStruct)[i] = 0;
}
}
/**
* @brief 使能Timer
* @param TIMERx Timer实例指针
*/
void TIMER_Enable(TIMER_TypeDef *TIMERx)
{
if (TIMERx == TIMER0)
{
SYS_ModuleClockCmd(SYS_MODULE_TIMER0, ENABLE);
}
else if (TIMERx == TIMER1)
{
SYS_ModuleClockCmd(SYS_MODULE_TIMER1, ENABLE);
}
else if (TIMERx == TIMER2)
{
SYS_ModuleClockCmd(SYS_MODULE_TIMER2, ENABLE);
}
else
{
__NOP();
}
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 清除Timer中断标志
* @param TIMERx Timer实例指针
* @param tempFlag 要清除的中断标志
*/
void TIMER_ClearIRQFlag(TIMER_TypeDef *TIMERx, uint32_t tempFlag)
{
TIMERx->IF = tempFlag;
}
/**
* @brief 翻转指定的GPIO引脚位
* @param GPIOx GPIO模块指针
* @param GPIO_Pin 要翻转的引脚
*/
void GPIO_ToggleBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
GPIOx->PDO = GPIOx->PDO ^ GPIO_Pin;
}
/**
* @brief 获取当前MCU时钟
* @return 当前MCU时钟频率
*/
uint32_t SYS_ReadMcuClk(void)
{
uint32_t clk = 96000000;
uint32_t clkdiv = (SYS_CLK_CFG & SYS_CLK_CFG_CLK_DIV_MASK) >> SYS_CLK_CFG_CLK_DIV_POS;
uint32_t clksel = (SYS_CLK_CFG & SYS_CLK_CFG_CLK_SEL_MASK) >> SYS_CLK_CFG_CLK_SEL_POS;
switch (clksel) // 0: HRC 1: PLL 2: LRC
{
case 0: // HRC
clk = 8000000;
break;
case 1: // PLL
switch (clkdiv)
{
case 0xff: // 96M
clk = 96000000;
break;
case 0x55: // 48M
clk = 48000000;
break;
case 0x11: // 24M
clk = 24000000;
break;
case 0x01: // 12M
clk = 12000000;
break;
case 0x00: // 12M
clk = 12000000;
break;
default: // 逐位计算
clk = 0;
for (int i = 0; i < 8; i++)
{
if (clkdiv & (1 << i))
{
clk += 12000000;
}
else
{
__NOP();
}
}
break;
}
break;
case 2: // LRC
clk = 32000;
break;
default:
break;
}
return clk;
}
/**
* @brief 配置GPIO引脚复用功能
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource 引脚源
* @param GPIO_AF 复用功能选择
*/
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
uint8_t offset;
uint8_t pins = GPIO_PinSource >> 2;
offset = ((GPIO_PinSource & 0x3) * 4);
switch (pins)
{
case 0:
GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 1:
GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 2:
GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 3:
GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
break;
default:
break;
}
}
21.01_TMP-ADC采样内部温度传感器数据
测试步骤
使用软件触发ADC1采样温度传感器并计算温度
实测数据
tmp = 218
参考代码
21.01_TMP_TempSenseADC.c
/**
* @brief TMP-ADC采样内部温度传感器数据
* @details 使用软件触发ADC1采样温度传感器并计算温度
*/
#include "lks32mc09x_lib.h"
volatile int32_t tmp = 0;
/**
* @brief 主函数
*/
int main(void)
{
ADC_InitTypeDef ADC_InitStruct;
// 初始化ADC结构体
ADC_StructInit(&ADC_InitStruct);
// 配置ADC参数
ADC_InitStruct.IE = ADC_IE_SF1; // 禁用中断
ADC_InitStruct.RE = 0; // 禁用DMA请求
ADC_InitStruct.NSMP = 0; // 禁用两段采样
ADC_InitStruct.DATA_ALIGN = 0; // 右对齐
ADC_InitStruct.CSMP = 0; // 禁用连续采样
ADC_InitStruct.TCNT = 0; // 触发一次采样
ADC_InitStruct.TROVS = 0; // 禁用过采样
ADC_InitStruct.OVSR = 0; // 过采样率
ADC_InitStruct.TRIG = 0; // MCPWM0 T0 事件触发 ADC 常规采样
ADC_InitStruct.S1 = 1; // 第一段常规采样次数
ADC_InitStruct.S2 = 0; // 第二段常规采样次数
ADC_InitStruct.IS1 = 0; // 空闲采样次数
ADC_InitStruct.GAIN = ADC_GAIN_3V6; // 3.6V量程
ADC_InitStruct.LTH = 0; // 禁用模拟看门狗
ADC_InitStruct.HTH = 0xFFFF; // 禁用模拟看门狗
ADC_InitStruct.GEN = 0; // 禁用模拟看门狗
ADC_InitStruct.IDLE_PRI = 0;
ADC_Init(ADC1, &ADC_InitStruct);
ADC_SetChn(ADC1, ADC_DAT_0, ADC1_CHN_TMP);
TMP_Init();
while (1)
{
ADC_ClearIRQFlag(ADC1, ADC_IF_SF1);
ADC_SoftTrig(ADC1);
while (ADC_GetIRQFlag(ADC1, ADC_IF_SF1) == 0)
;
tmp = TMP_GetCurrentTemperature(ADC_GetConversionValue(ADC1, ADC_DAT_0)); // 将ADC采样值转换为温度值并保存到全局变量CurrentTempature中
}
}
使用到的库函数
库函数部分代码
#define ADC0 ((ADC_TypeDef *)ADC0_BASE)
#define ADC0_CFG_DATA_ALIGN_BIT BIT10
#define ADC0_CHN0_PDS0(x) (uint32_t)(((x) & 0xF) << ADC0_CHN0_PDS0_POS)
#define ADC0_CHN0_PDS1(x) (uint32_t)(((x) & 0xF) << ADC0_CHN0_PDS1_POS)
#define ADC0_CHN0_PDS2(x) (uint32_t)(((x) & 0xF) << ADC0_CHN0_PDS2_POS)
#define ADC0_CHN0_PDS3(x) (uint32_t)(((x) & 0xF) << ADC0_CHN0_PDS3_POS)
#define ADC0_CHN1_PDS4(x) (uint32_t)(((x) & 0xF) << ADC0_CHN1_PDS4_POS)
#define ADC0_CHN1_PDS5(x) (uint32_t)(((x) & 0xF) << ADC0_CHN1_PDS5_POS)
#define ADC0_CHN1_PDS6(x) (uint32_t)(((x) & 0xF) << ADC0_CHN1_PDS6_POS)
#define ADC0_CHN1_PDS7(x) (uint32_t)(((x) & 0xF) << ADC0_CHN1_PDS7_POS)
#define ADC0_CHN2_PDS10(x) (uint32_t)(((x) & 0xF) << ADC0_CHN2_PDS10_POS)
#define ADC0_CHN2_PDS11(x) (uint32_t)(((x) & 0xF) << ADC0_CHN2_PDS11_POS)
#define ADC0_CHN2_PDS8(x) (uint32_t)(((x) & 0xF) << ADC0_CHN2_PDS8_POS)
#define ADC0_CHN2_PDS9(x) (uint32_t)(((x) & 0xF) << ADC0_CHN2_PDS9_POS)
#define ADC1 ((ADC_TypeDef *)ADC1_BASE)
#define NVR_ADDR_ADC_0_AMC0 0x00001424
#define NVR_ADDR_ADC_0_AMC1 0x0000142C
#define NVR_ADDR_ADC_0_DC0 0x00001420
#define NVR_ADDR_ADC_0_DC1 0x00001428
#define NVR_ADDR_ADC_1_AMC0 0x00001434
#define NVR_ADDR_ADC_1_AMC1 0x0000143C
#define NVR_ADDR_ADC_1_DC0 0x00001430
#define NVR_ADDR_ADC_1_DC1 0x00001438
#define NVR_ADDR_TMP_B 0x000014D4
#define NVR_ADDR_TMP_K 0x000014D0
#define REG_READ(reg) (reg)
#define REG_READ_BIT(reg,mask) ((reg) & (mask))
#define REG_READ_BIT_POS_MASK(reg,mask) (((reg) & (mask##_MASK)) >> mask##_POS)
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define REG_WRITE_BIT(reg,mask,val) reg = ((reg) & ~(mask)) | (val)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_ADC0 BIT22
#define SYS_MODULE_ADC1 BIT23
/**
* @brief ADC初始化结构体定义
*/
typedef struct ADC_InitTypeDef {
uint16_t IE;
uint16_t RE;
uint16_t NSMP;
uint16_t DATA_ALIGN;
uint16_t CSMP;
uint16_t TCNT;
uint16_t TROVS;
uint16_t OVSR;
uint32_t TRIG;
uint16_t S1;
uint16_t S2;
uint16_t IS1;
uint16_t GAIN;
uint16_t LTH;
uint16_t HTH;
uint16_t GEN;
uint16_t IDLE_PRI;
};
/**
* @brief ADC数据寄存器序号枚举定义
*/
typedef enum ADC_DAT_x {
ADC_DAT_0 = 0,
ADC_DAT_1 = 1,
ADC_DAT_2 = 2,
ADC_DAT_3 = 3,
ADC_DAT_4 = 4,
ADC_DAT_5 = 5,
ADC_DAT_6 = 6,
ADC_DAT_7 = 7,
ADC_DAT_8 = 8,
ADC_DAT_9 = 9,
ADC_DAT_10 = 10,
ADC_DAT_11 = 11,
}} {enum_name};
/**
* @brief ADC采样通道序号枚举定义
*/
typedef enum ADC_CHN_x {
ADC_CHN_0 = 0,
ADC_CHN_1 = 1,
ADC_CHN_2 = 2,
ADC_CHN_3 = 3,
ADC_CHN_4 = 4,
ADC_CHN_5 = 5,
ADC_CHN_6 = 6,
ADC_CHN_7 = 7,
ADC_CHN_8 = 8,
ADC_CHN_9 = 9,
ADC_CHN_10 = 10,
ADC_CHN_11 = 11,
ADC_CHN_12 = 12,
ADC_CHN_13 = 13,
ADC_CHN_14 = 14,
ADC_CHN_15 = 15,
ADC0_CHN_OPA0 = 0,
ADC0_CHN_OPA1 = 1,
ADC0_CHN_OPA2 = 2,
ADC0_CHN_OPA3 = 3,
ADC0_CHN_ADC01_CH4 = 4,
ADC0_CHN_ADC01_CH5 = 5,
ADC0_CHN_ADC01_CH6 = 6,
ADC0_CHN_ADC01_CH7 = 7,
ADC0_CHN_ADC0_CH8 = 8,
ADC0_CHN_REF24 = 9,
ADC0_CHN_ADC01_CH10 = 10,
ADC0_CHN_ADC01_CH11 = 11,
ADC0_CHN_ADC0_CH12 = 12,
ADC0_CHN_ADC0_CH13 = 13,
ADC0_CHN_DAC = 14,
ADC0_CHN_AVSS = 15,
ADC1_CHN_OPA0 = 0,
ADC1_CHN_OPA1 = 1,
ADC1_CHN_OPA2 = 2,
ADC1_CHN_OPA3 = 3,
ADC1_CHN_ADC01_CH4 = 4,
ADC1_CHN_ADC01_CH5 = 5,
ADC1_CHN_ADC01_CH6 = 6,
ADC1_CHN_ADC01_CH7 = 7,
ADC1_CHN_ADC1_CH8 = 8,
ADC1_CHN_ADC1_CH9 = 9,
ADC1_CHN_ADC01_CH10 = 10,
ADC1_CHN_ADC01_CH11 = 11,
ADC1_CHN_ADC1_CH12 = 12,
ADC1_CHN_ADC1_CH13 = 13,
ADC1_CHN_TMP = 14,
ADC1_CHN_AVDD = 15,
}} {enum_name};
/**
* @brief 使能ADC模块
* @param ADCx: ADC模块指针,指向ADC0或ADC1
*/
void ADC_Enable(ADC_TypeDef *ADCx)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
// 使能ADC模块时钟
REG_SET(SYS0->AFE_REG7, BIT8 | BIT9);
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 使能温度传感器
*/
void TMP_Enable(void)
{
SYS_WR_PROTECT = 0x7a83;
SYS_AFE_REG5 |= BIT3;
SYS_WR_PROTECT = 0;
}
/**
* @brief 初始化ADC模块
* @param ADCx: ADC模块指针,指向ADC0或ADC1
* @param ADC_InitStruct: 指向ADC初始化结构体的指针
*/
void ADC_Init(ADC_TypeDef *ADCx, ADC_InitTypeDef *ADC_InitStruct)
{
ADC_Enable(ADCx);
SYS_ModuleClockCmd(SYS_MODULE_ADC0, 1);
SYS_ModuleClockCmd(SYS_MODULE_ADC1, 1);
if (ADC_InitStruct->S2 == 0)
{
ADC_InitStruct->NSMP = DISABLE; // 当第二段采样次数为0时,不允许配置第二段使能
}
// 解锁SYS寄存器
REG_WRITE(SYS0->PROTECT, 0x7a83);
// 配置ADC初始化结构体中的各个成员
REG_WRITE(ADCx->IE, ADC_InitStruct->IE | ADC_InitStruct->RE);
// 计算并一次性配置CFG寄存器
REG_WRITE(ADCx->CFG, ((ADC_InitStruct->NSMP << 12) |
(1 << 11) | // 初始化时复位adc状态机
(ADC_InitStruct->DATA_ALIGN << 10) |
(ADC_InitStruct->IDLE_PRI << 9) |
(ADC_InitStruct->CSMP << 8) |
(ADC_InitStruct->TCNT << 4) |
(ADC_InitStruct->TROVS << 3) |
(ADC_InitStruct->OVSR << 0)));
REG_WRITE(ADCx->TRIG, ADC_InitStruct->TRIG);
// 配置CHNT寄存器
REG_WRITE(ADCx->CHNT, (REG_READ(ADCx->CHNT) & ~((0x0F << 0) | (0x0F << 4) | (0x0F << 8))) |
((ADC_InitStruct->S1 << 0) |
(ADC_InitStruct->S2 << 4) |
(ADC_InitStruct->IS1 << 8)));
// 配置GAIN寄存器
REG_WRITE_BIT(ADCx->GAIN, 1 << 0, ADC_InitStruct->GAIN << 0);
REG_WRITE(ADCx->LTH, ADC_InitStruct->LTH);
REG_WRITE(ADCx->HTH, ADC_InitStruct->HTH);
REG_WRITE(ADCx->GEN, ADC_InitStruct->GEN);
if (ADCx == ADC0)
{
REG_WRITE(ADCx->AMC0, TRIM_Read(NVR_ADDR_ADC_0_AMC0));
REG_WRITE(ADCx->DC0, TRIM_Read(NVR_ADDR_ADC_0_DC0));
REG_WRITE(ADCx->AMC1, TRIM_Read(NVR_ADDR_ADC_0_AMC1));
REG_WRITE(ADCx->DC1, TRIM_Read(NVR_ADDR_ADC_0_DC1));
}
else if (ADCx == ADC1)
{
REG_WRITE(ADCx->AMC0, TRIM_Read(NVR_ADDR_ADC_1_AMC0));
REG_WRITE(ADCx->DC0, TRIM_Read(NVR_ADDR_ADC_1_DC0));
REG_WRITE(ADCx->AMC1, TRIM_Read(NVR_ADDR_ADC_1_AMC1));
REG_WRITE(ADCx->DC1, TRIM_Read(NVR_ADDR_ADC_1_DC1));
}
else
{
__NOP();
}
}
/**
* @brief 初始化温度传感器
*/
void TMP_Init(void)
{
tmp_k = TRIM_Read(NVR_ADDR_TMP_K);
tmp_b = TRIM_Read(NVR_ADDR_TMP_B);
TMP_Enable();
}
/**
* @brief 将ADC初始化结构体置为默认值
* @param ADC_InitStruct: 指向ADC初始化结构体的指针
*/
void ADC_StructInit(ADC_InitTypeDef *ADC_InitStruct)
{
for (int i = 0; i < sizeof(ADC_InitTypeDef) / sizeof(uint16_t); i++)
{
((uint16_t *)ADC_InitStruct)[i] = 0;
}
}
/**
* @brief 执行软件触发ADC采样,采样次数取决于ADC_InitTypeDef里的S1的配置
* @param ADCx: ADC模块指针,指向ADC0或ADC1
*/
void ADC_SoftTrig(ADC_TypeDef *ADCx)
{
// 写入软件触发值
REG_WRITE(ADCx->SWT, 0x5AA5);
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 清除ADC中断标志位
* @param ADCx: ADC模块指针,指向ADC0或ADC1
* @param INT_flag: 中断标志位,可以是ADC_IF_ISF等
*/
void ADC_ClearIRQFlag(ADC_TypeDef *ADCx, uint16_t INT_flag)
{
REG_WRITE(ADCx->IF, INT_flag);
}
/**
* @brief 获取ADC中断标志位状态
* @param ADCx: ADC模块指针,指向ADC0或ADC1
* @param INT_flag: 中断标志位,可以是ADC_IF_ISF等
* @return 中断标志位状态
*/
uint16_t ADC_GetIRQFlag(ADC_TypeDef *ADCx, uint16_t INT_flag)
{
return REG_READ_BIT(ADCx->IF, INT_flag) ? 1 : 0;
}
/**
* @brief 获取ADC通道的转换值
* @param ADCx: ADC模块指针,指向ADC0或ADC1
* @param DATNum: 数据寄存器序号,如ADC_DAT_0
* @return 转换值
*/
s16 ADC_GetConversionValue(ADC_TypeDef *ADCx, ADC_DAT_x DATNum)
{
int32_t adc_dat = ADC_GetConversionValueS32(ADCx, DATNum);
// adc_dat不会小于-32767,这里不做判断
if (adc_dat > 32767)
{
adc_dat = 32767;
}
return adc_dat;
}
/**
* @brief 获取ADC通道的转换值(按int32返回,采样通道4、5、6、7时,返回值大于32767)
* @param ADCx: ADC模块指针,指向ADC0或ADC1
* @param DATNum: 数据寄存器序号,如ADC_DAT_0
* @return 转换值
*/
int32_t ADC_GetConversionValueS32(ADC_TypeDef *ADCx, ADC_DAT_x DATNum)
{
int32_t adc_dat = 0;
uint16_t adc_chn = 0;
switch (DATNum)
{
case ADC_DAT_0:
adc_dat = REG_READ(ADCx->DAT0);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN0, ADC0_CHN0_PDS0);
break;
case ADC_DAT_1:
adc_dat = REG_READ(ADCx->DAT1);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN0, ADC0_CHN0_PDS1);
break;
case ADC_DAT_2:
adc_dat = REG_READ(ADCx->DAT2);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN0, ADC0_CHN0_PDS2);
break;
case ADC_DAT_3:
adc_dat = REG_READ(ADCx->DAT3);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN0, ADC0_CHN0_PDS3);
break;
case ADC_DAT_4:
adc_dat = REG_READ(ADCx->DAT4);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN1, ADC0_CHN1_PDS4);
break;
case ADC_DAT_5:
adc_dat = REG_READ(ADCx->DAT5);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN1, ADC0_CHN1_PDS5);
break;
case ADC_DAT_6:
adc_dat = REG_READ(ADCx->DAT6);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN1, ADC0_CHN1_PDS6);
break;
case ADC_DAT_7:
adc_dat = REG_READ(ADCx->DAT7);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN1, ADC0_CHN1_PDS7);
break;
case ADC_DAT_8:
adc_dat = REG_READ(ADCx->DAT8);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN2, ADC0_CHN2_PDS8);
break;
case ADC_DAT_9:
adc_dat = REG_READ(ADCx->DAT9);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN2, ADC0_CHN2_PDS9);
break;
case ADC_DAT_10:
adc_dat = REG_READ(ADCx->DAT10);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN2, ADC0_CHN2_PDS10);
break;
case ADC_DAT_11:
adc_dat = REG_READ(ADCx->DAT11);
adc_chn = REG_READ_BIT_POS_MASK(ADCx->CHN2, ADC0_CHN2_PDS11);
break;
default:
adc_dat = 0;
break;
}
// 先按照int16处理一下
adc_dat = (int16_t)(adc_dat & 0xffff);
// ADC0/1采样公共通道4/5/6/7时,负端电压为2.4V,实际允许的输入电压范围是
// if ((adc_chn == 4) || (adc_chn == 5) || (adc_chn == 6) || (adc_chn == 7))
// {
// int32_t dat = adc_dat;
// if (REG_READ_BIT(SYS_AFE_REG4, BIT4) == 0)
// {
// if (REG_READ_BIT(ADC0_CFG, ADC0_CFG_DATA_ALIGN_BIT) == 0)
// { // 左对齐
// dat = dat + 0x5550;
// adc_dat = dat;
// }
// else
// { // 右对齐
// dat = dat + 0x555;
// adc_dat = dat;
// }
// }
// }
return adc_dat;
}
/**
* @brief 获取当前温度
* @param ADC_value ADC采样值
* @return 当前温度值,单位为摄氏度
*/
s16 TMP_GetCurrentTemperature(s16 ADC_value)
{
s16 t_Temperture;
if (ADC1->CFG & BIT10)
{
t_Temperture = (tmp_b - ((s32)tmp_k * ADC_value) / 1000);
}
else
{
t_Temperture = (tmp_b - ((s32)tmp_k * (ADC_value >> 4)) / 1000);
}
return t_Temperture;
}
/**
* @brief 设置第每次的采样通道,例如ADC_SetChn(ADC0,ADC_DAT_0,ADC_CHN_0),把adc第一次采样的通道设置为通道0
* @param ADCx: ADC模块指针,指向ADC0或ADC1
* @param datx: 数据寄存器序号,如ADC_DAT_0
* @param chnx: ADC采样通道序号枚举定义,如ADC_CHN_0
*/
void ADC_SetChn(ADC_TypeDef *ADCx, ADC_DAT_x datx, ADC_CHN_x chnx)
{
uint8_t n = (datx & 0x3) * 4;
switch (datx)
{
case ADC_DAT_0:
REG_WRITE(ADCx->CHN0, (REG_READ(ADCx->CHN0) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_1:
REG_WRITE(ADCx->CHN0, (REG_READ(ADCx->CHN0) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_2:
REG_WRITE(ADCx->CHN0, (REG_READ(ADCx->CHN0) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_3:
REG_WRITE(ADCx->CHN0, (REG_READ(ADCx->CHN0) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_4:
REG_WRITE(ADCx->CHN1, (REG_READ(ADCx->CHN1) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_5:
REG_WRITE(ADCx->CHN1, (REG_READ(ADCx->CHN1) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_6:
REG_WRITE(ADCx->CHN1, (REG_READ(ADCx->CHN1) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_7:
REG_WRITE(ADCx->CHN1, (REG_READ(ADCx->CHN1) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_8:
REG_WRITE(ADCx->CHN2, (REG_READ(ADCx->CHN2) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_9:
REG_WRITE(ADCx->CHN2, (REG_READ(ADCx->CHN2) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_10:
REG_WRITE(ADCx->CHN2, (REG_READ(ADCx->CHN2) & (~(0xf << n))) | (chnx << n));
break;
case ADC_DAT_11:
REG_WRITE(ADCx->CHN2, (REG_READ(ADCx->CHN2) & (~(0xf << n))) | (chnx << n));
break;
}
}
22.01_UART-发送数据
测试步骤
- 初始化串口波特兰 115200
- 串口数据长度8位 停止位1位 MSL 关闭校验
- 串口发送数据0x50
实测数据
参考代码
22.01_UART_Send.c
/**
* @brief UART-发送数据
* @details
* 1. 初始化串口波特兰 115200
* 2. 串口数据长度8位 停止位1位 MSL 关闭校验
* 3. 串口发送数据0x50
*/
#include "lks32mc09x_lib.h"
volatile int baud = 115200; // 设定波特率
volatile int refbaud = 115200; // 实际波特率
volatile int sendflg = 0; // 设定波特率
volatile int data = 0xa0; // 要发送的数据
int main(void)
{
__disable_irq();
SYS_WR_PROTECT = 0x7a83;
SYS_CLK_CFG = 0x1ff;
SYS_WR_PROTECT = 0;
UART_InitTypeDef UART_InitType;
UART_StructInit(&UART_InitType);
UART_InitType.BaudRate = baud; ///< 波特率
UART_InitType.WordLength = UART_WORDLENGTH_8b; ///< 数据长度
UART_InitType.StopBits = UART_STOPBITS_1b; ///< 停止位长度
UART_InitType.FirstSend = UART_FIRSTSEND_MSB; ///< 发送数据大小端设置
UART_InitType.ParityMode = UART_Parity_NO; ///< 奇偶校验
UART_InitType.MultiDropEna = 0; ///< 使能Multi-drop, 0:Disable 1:Enable
UART_InitType.Bit9Value = 0; ///< Multi-drop Master模式时, 第9个数据位值
UART_InitType.DUPLEX = 0; ///< 双工选择, 0:全双工 1:半双工
UART_InitType.Match485Addr = 0; ///< 用作485通信时的匹配地址
UART_InitType.IRQEna = 0; ///< 中断使能寄存器
UART_InitType.DMARE = 0; ///< DMA 请求使能
UART_InitType.RXD_INV = 0; ///< 接收电平取反
UART_InitType.TXD_INV = 0; ///< 发送电平取反
UART_Init(UART0, &UART_InitType);
GPIO_Config(GPIO0, GPIO_PinSource_0, GPIO_Mode_OUT, GPIO_AF_UART);
for (;;)
{
refbaud = UART_SetBaud(UART0, baud);
if (sendflg)
{
UART_SendData(UART0, data);
sendflg = 0;
}
}
}
使用到的库函数
库函数部分代码
#define GPIO_AF_I2C 6
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS SYS0
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_CLK_CFG_CLK_DIV_MASK (uint32_t)(0xFF << SYS_CLK_CFG_CLK_DIV_POS)
#define SYS_CLK_CFG_CLK_DIV_POS 0
#define SYS_CLK_CFG_CLK_SEL_MASK (uint32_t)(0x3 << SYS_CLK_CFG_CLK_SEL_POS)
#define SYS_CLK_CFG_CLK_SEL_POS 8
#define SYS_MODULE_GPIO BIT11
#define SYS_MODULE_UART0 BIT12
#define SYS_MODULE_UART1 BIT13
#define SYS_MODULE_UART2 BIT14
#define UART0 ((UART_TypeDef *)UART0_BASE)
#define UART1 ((UART_TypeDef *)UART1_BASE)
#define UART2 ((UART_TypeDef *)UART2_BASE)
#define UART_FIRSTSEND_LSB 0
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* @brief GPIO模式选择枚举
*/
typedef enum GPIO_Mode_TypeDef {
GPIO_Mode_IN = 0,
GPIO_Mode_OUT = 1,
GPIO_Mode_ANA = 2,
GPIO_Mode_IO = 3,
}} {enum_name};
/// UART 初始化结构体定义
typedef struct UART_InitTypeDef {
uint32_t BaudRate;
uint8_t WordLength;
uint8_t StopBits;
uint8_t FirstSend;
UART_ParityMode ParityMode;
uint8_t MultiDropEna;
uint8_t Bit9Value;
uint8_t DUPLEX;
uint16_t Match485Addr;
uint16_t IRQEna;
uint8_t DMARE;
uint8_t RXD_INV;
uint8_t TXD_INV;
uint8_t ABD_EN;
uint8_t LBDL;
uint8_t LIN_EN;
uint8_t CK_EN;
};
/**
* @brief GPIO配置函数
* @param GPIOx: GPIO端口
* @param GPIO_PinSource: GPIO引脚
* @param mode: GPIO模式
* @param GPIO_AF_x: GPIO复用功能选择
*/
void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
{
GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = 1 << GPIO_PinSource;
GPIO_InitStructure.GPIO_Mode = mode;
if (GPIO_AF_x == GPIO_AF_I2C)
{
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
}
else
{
__NOP();
}
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 初始化UART模块
* @param UARTx UART模块指针
* @param UART_InitStruct 初始化结构体指针
*/
void UART_Init(UART_TypeDef *UARTx, UART_InitTypeDef *UART_InitStruct)
{
// 使能UART模块时钟
if (UARTx == UART0)
{
SYS_ModuleClockCmd(SYS_MODULE_UART0, ENABLE);
}
else if (UARTx == UART1)
{
SYS_ModuleClockCmd(SYS_MODULE_UART1, ENABLE);
}
else if (UARTx == UART2)
{
SYS_ModuleClockCmd(SYS_MODULE_UART2, ENABLE);
}
UART_SetBaud(UARTx, UART_InitStruct->BaudRate);
// 配置UART控制寄存器
UARTx->CTRL = (UART_InitStruct->DUPLEX << 7) |
(UART_InitStruct->LIN_EN << 6) |
(UART_InitStruct->MultiDropEna << 5) |
(UART_InitStruct->CK_EN << 4) |
(UART_InitStruct->ParityMode << 3) |
(UART_InitStruct->FirstSend << 2) |
(UART_InitStruct->StopBits << 1) |
(UART_InitStruct->WordLength);
// 配置中断使能寄存器
UARTx->IE = UART_InitStruct->IRQEna;
// 配置DMA请求使能寄存器
UARTx->RE = UART_InitStruct->DMARE;
// 配置IO极性
UARTx->IOC =
(UART_InitStruct->LBDL << 6) | ///< LIN同步段检测长度 0:10bits 1:11bits
(UART_InitStruct->ABD_EN << 4) | ///< LIN波特率自适应使能
(UART_InitStruct->TXD_INV << 1) |
(UART_InitStruct->RXD_INV) |
0;
UARTx->IF = 0x3ff;
}
/**
* @brief 初始化UART结构体为默认值
* @param UART_InitStruct 初始化结构体指针
*/
void UART_StructInit(UART_InitTypeDef *UART_InitStruct)
{
for (int i = 0; i < sizeof(UART_InitTypeDef); i++)
{
((uint8_t *)UART_InitStruct)[i] = 0;
}
// 默认配置
UART_InitStruct->WordLength = UART_WORDLENGTH_8b;
UART_InitStruct->StopBits = UART_STOPBITS_1b;
UART_InitStruct->FirstSend = UART_FIRSTSEND_LSB;
UART_InitStruct->ParityMode = UART_Parity_NO;
UART_InitStruct->MultiDropEna = DISABLE;
UART_InitStruct->DUPLEX = 0; // 全双工
}
/**
* @brief 发送数据
* @param UARTx UART模块指针
* @param n 要发送的数据
*/
void UART_SendData(UART_TypeDef *UARTx, uint32_t n)
{
UARTx->BUFF = n & 0xFF;
while ((UARTx->STT & BIT0) == 0)
{
// 等待发送完成
}
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 获取当前MCU时钟
* @return 当前MCU时钟频率
*/
uint32_t SYS_ReadMcuClk(void)
{
uint32_t clk = 96000000;
uint32_t clkdiv = (SYS_CLK_CFG & SYS_CLK_CFG_CLK_DIV_MASK) >> SYS_CLK_CFG_CLK_DIV_POS;
uint32_t clksel = (SYS_CLK_CFG & SYS_CLK_CFG_CLK_SEL_MASK) >> SYS_CLK_CFG_CLK_SEL_POS;
switch (clksel) // 0: HRC 1: PLL 2: LRC
{
case 0: // HRC
clk = 8000000;
break;
case 1: // PLL
switch (clkdiv)
{
case 0xff: // 96M
clk = 96000000;
break;
case 0x55: // 48M
clk = 48000000;
break;
case 0x11: // 24M
clk = 24000000;
break;
case 0x01: // 12M
clk = 12000000;
break;
case 0x00: // 12M
clk = 12000000;
break;
default: // 逐位计算
clk = 0;
for (int i = 0; i < 8; i++)
{
if (clkdiv & (1 << i))
{
clk += 12000000;
}
else
{
__NOP();
}
}
break;
}
break;
case 2: // LRC
clk = 32000;
break;
default:
break;
}
return clk;
}
/**
* @brief 设置波特率
* @param UARTx UART模块
* @param Baud 波特率范围
* @return 实际波特率(不考虑时钟误差)
*/
uint32_t UART_SetBaud(UART_TypeDef *UARTx, uint32_t Baud)
{
uint32_t uartclk = SYS_ReadMcuClk() / (SYS->CLK_DIV2 + 1); // 串口的时钟频率
uint32_t div = uartclk / Baud - 1;
if (div > 0xffff)
{
div = 0xffff;
}
else
{
__NOP();
}
UARTx->DIVL = div & 0xFF;
UARTx->DIVH = (div & 0xFF00) >> 8;
Baud = uartclk / (div + 1);
return Baud;
}
/**
* @brief 配置GPIO引脚复用功能
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource 引脚源
* @param GPIO_AF 复用功能选择
*/
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
uint8_t offset;
uint8_t pins = GPIO_PinSource >> 2;
offset = ((GPIO_PinSource & 0x3) * 4);
switch (pins)
{
case 0:
GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 1:
GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 2:
GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 3:
GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
break;
default:
break;
}
}
23.01_WAKE-IO唤醒
测试步骤
P2.6设置为高电平之后进入休眠状态,P0.0输入高电平之后芯片唤醒。 注意:使用深度休眠时,不能使用调试读写变量(例如debug或lksscope),会导致芯片无法正常唤醒。
实测数据
开机
芯片电流 : 13.895975mA
触发芯片进入休眠状态
芯片电流 : 1.681591mA
使用IO唤醒芯片
芯片电流 : 1.681912mA
参考代码
23.01_WAKE_SleepGpioWake.c
/**
* @brief WAKE-IO唤醒
* @details
* P2.6设置为高电平之后进入休眠状态,P0.0输入高电平之后芯片唤醒。
* 注意:使用深度休眠时,不能使用调试读写变量(例如debug或lksscope),会导致芯片无法正常唤醒。
*/
#include "lks32mc09x.h"
#include "lks32mc09x_adc.h"
#include "lks32mc09x_cmp.h"
#include "lks32mc09x_dac.h"
#include "lks32mc09x_gpio.h"
#include "lks32mc09x_lib.h"
#include "lks32mc09x_opa.h"
#include "lks32mc09x_reg.h"
#include "lks32mc09x_sys.h"
#include "lks32mc09x_tmp.h"
#include "lks32mc09x_wake.h"
volatile int ena = 0; // 1 进入休眠
/**
* @brief 主函数,用于测试HSI时钟输出及GPIO翻转
*/
int main(void)
{
// 打开所有模块,模拟实际应用场景
ADC_Enable(ADC0);
DAC_Enable(DAC_CHANNEL_0);
DAC_Enable(DAC_CHANNEL_1);
OPA_Enable(OPA_CHANNEL_0);
OPA_Enable(OPA_CHANNEL_1);
OPA_Enable(OPA_CHANNEL_2);
OPA_Enable(OPA_CHANNEL_3);
CMP_Enable(CMP_CHN_0);
CMP_Enable(CMP_CHN_1);
TMP_Enable();
GPIO_Enable();
GPIO_Config(GPIO0, GPIO_PinSource_0, GPIO_Mode_IN, GPIO_AF_GPIO);
GPIO_Config(GPIO2, GPIO_PinSource_6, GPIO_Mode_IN, GPIO_AF_GPIO);
while (1)
{
if (ena == 0)
{
if (GPIO_ReadInputDataBit(GPIO2, GPIO_Pin_6))
{
ena = 1;
WAKE_SetWakeIO(WAKE_IO_P0_0, WAKE_TRIG_HIGH, 0, 1); // 设置为P0.0高电平触发芯片唤醒,不使用gpio滤波
WAKE_FallSleep(); // 进入休眠状态
}
}
}
}
void WAKE_IRQHandler()
{
}
使用到的库函数
库函数部分代码
#define AON0 ((AON_TypeDef *)AON_BASE)
#define CMP_CHN_0 0
#define CMP_CHN_1 1
#define DAC_CHANNEL_0 0
#define DAC_CHANNEL_1 1
#define GPIO_AF_I2C 6
#define OPA_CHANNEL_0 0
#define OPA_CHANNEL_1 1
#define OPA_CHANNEL_2 2
#define OPA_CHANNEL_3 3
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_CMP BIT2
#define SYS_MODULE_GPIO BIT11
#define WAKE_FLT_EN 0x02
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* @brief GPIO模式选择枚举
*/
typedef enum GPIO_Mode_TypeDef {
GPIO_Mode_IN = 0,
GPIO_Mode_OUT = 1,
GPIO_Mode_ANA = 2,
GPIO_Mode_IO = 3,
}} {enum_name};
/**
* @brief 低功耗IO唤醒电平枚举成员
*/
typedef enum WAKE_TRIG_x {
WAKE_TRIG_LOW = 0,
WAKE_TRIG_HIGH = 1,
}} {enum_name};
/**
* @brief GPIO配置函数
* @param GPIOx: GPIO端口
* @param GPIO_PinSource: GPIO引脚
* @param mode: GPIO模式
* @param GPIO_AF_x: GPIO复用功能选择
*/
void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
{
GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = 1 << GPIO_PinSource;
GPIO_InitStructure.GPIO_Mode = mode;
if (GPIO_AF_x == GPIO_AF_I2C)
{
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
}
else
{
__NOP();
}
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**
* @brief DAC使能
* @param DAC_Channel DAC通道选择
*/
void DAC_Enable(uint8_t DAC_Channel)
{
DAC_Cmd(DAC_Channel, ENABLE);
}
/**
* @brief DAC通道时钟使能
* @param DAC_Channel DAC通道选择
* @param state 使能状态,1模块使能 0 模块关闭
*/
void DAC_Cmd(uint8_t DAC_Channel, uint8_t state)
{
SYS0->PROTECT = 0x7a83;
if (DAC_Channel == DAC_CHANNEL_0)
{
if (state)
{
SYS0->AFE_REG5 |= BIT1;
}
else
{
SYS0->AFE_REG5 &= ~BIT1;
}
}
else
{
__NOP();
}
if (DAC_Channel == DAC_CHANNEL_1)
{
if (state)
{
SYS0->AFE_REG5 |= BIT2;
}
else
{
SYS0->AFE_REG5 &= ~BIT2;
}
}
else
{
__NOP();
}
SYS0->PROTECT = 0;
}
/**
* @brief OPA 使能
* @param OPA_CHANNEL_x OPA通道
*/
void OPA_Enable(uint32_t OPA_CHANNEL_x)
{
SYS0->PROTECT = 0x7a83;
switch (OPA_CHANNEL_x)
{
case OPA_CHANNEL_0:
SYS0->AFE_REG1 |= BIT12;
break;
case OPA_CHANNEL_1:
SYS0->AFE_REG1 |= BIT13;
break;
case OPA_CHANNEL_2:
SYS0->AFE_REG1 |= BIT14;
break;
case OPA_CHANNEL_3:
SYS0->AFE_REG1 |= BIT15;
break;
}
SYS0->PROTECT = 0;
}
/**
* @brief gpio使能
*/
void GPIO_Enable(void)
{
SYS_ModuleClockCmd(SYS_MODULE_GPIO, 1);
}
/**
* @brief 使能ADC模块
* @param ADCx: ADC模块指针,指向ADC0或ADC1
*/
void ADC_Enable(ADC_TypeDef *ADCx)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
// 使能ADC模块时钟
REG_SET(SYS0->AFE_REG7, BIT8 | BIT9);
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 使能比较器
* @param CMP_CHN_x: 比较器通道选择,CMP_CHN_0, CMP_CHN_1
*/
void CMP_Enable(uint8_t CMP_CHN_x)
{
SYS_ModuleClockCmd(SYS_MODULE_CMP, ENABLE);
if (CMP_CHN_x == CMP_CHN_0)
{
SYS0->PROTECT = 0x7a83;
SYS0->AFE_REG5 |= BIT6;
SYS0->PROTECT = 0;
}
else
{
__NOP();
}
if (CMP_CHN_x == CMP_CHN_1)
{
SYS0->PROTECT = 0x7a83;
SYS0->AFE_REG5 |= BIT7;
SYS0->PROTECT = 0;
}
else
{
__NOP();
}
}
/**
* @brief 使能温度传感器
*/
void TMP_Enable(void)
{
SYS_WR_PROTECT = 0x7a83;
SYS_AFE_REG5 |= BIT3;
SYS_WR_PROTECT = 0;
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 设置唤醒IO
* @param wakeIO 唤醒IO选择,必须打开对应的gpio输入使能(GPIO设置为输入模式),可以复用成任何模式
* @param trigLevel 触发电平
* @param flt 滤波使能
* @param ena 使能标志
*/
void WAKE_SetWakeIO(uint32_t WAKE_IO_x, WAKE_TRIG_x trigLevel, uint32_t flt, uint8_t ena)
{
if (ena)
{
AON_IO_WAKE_EN |= WAKE_IO_x;
// 使能IO唤醒
AON0->IO_WAKE_EN |= WAKE_IO_x;
// 设置触发电平
if (trigLevel == WAKE_TRIG_HIGH)
{
AON0->IO_WAKE_POL |= WAKE_IO_x;
}
else
{
AON0->IO_WAKE_POL &= ~WAKE_IO_x;
}
// 设置滤波使能
if (flt == WAKE_FLT_EN)
{
AON0->PWR_CFG |= BIT1;
}
else
{
AON0->PWR_CFG &= ~BIT1;
}
}
else
{
// 禁用IO唤醒
AON0->IO_WAKE_EN &= ~WAKE_IO_x;
}
}
/**
* @brief 读取指定GPIO引脚的输入数据位
* @param GPIOx GPIO模块指针
* @param GPIO_Pin 要读取的引脚
* @return 引脚的输入数据位状态
*/
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
return (GPIOx->PDI & GPIO_Pin) ? 1 : 0;
}
/**
* @brief 进入低功耗模式,必须定义WAKE_IRQHandler函数,WAKE_IRQHandler内可以不做任何操作
*/
void WAKE_FallSleep(void)
{
int irqstate;
uint16_t sys_reg_0;
uint16_t sys_reg_1;
uint16_t sys_reg_2;
uint16_t sys_reg_3;
uint16_t sys_reg_4;
uint16_t sys_reg_5;
uint16_t sys_reg_7;
uint32_t sys_clk_fen;
uint32_t sys_clk_cfg;
uint32_t nvic_state; // 各个使能的状态
irqstate = __get_PRIMASK(); // 保存全局中断状态
__disable_irq(); // 关闭全局中断
SYS_WR_PROTECT = 0x7a83; /*开启寄存器写使能*/
sys_reg_0 = SYS_AFE_REG0;
sys_reg_1 = SYS_AFE_REG1;
sys_reg_2 = SYS_AFE_REG2;
sys_reg_3 = SYS_AFE_REG3;
sys_reg_4 = SYS_AFE_REG4;
sys_reg_5 = SYS_AFE_REG5;
sys_reg_7 = SYS_AFE_REG7;
sys_clk_fen = SYS_CLK_FEN;
sys_clk_cfg = SYS_CLK_CFG;
SYS_AFE_REG0 = 0;
SYS_AFE_REG1 = BIT11 | BIT10; // 关闭BGP和RCH
SYS_AFE_REG2 = 0;
SYS_AFE_REG3 = 0;
SYS_AFE_REG4 = 0;
SYS_AFE_REG5 = (sys_reg_5 & (BIT8 | BIT9)); // 保持复位电压阈值不变
SYS_AFE_REG7 = 0;
SYS_CLK_FEN = 0;
SYS_CLK_CFG = 0x0;
{
int cnt;
for (cnt = 0; cnt < 4000; cnt++)
{
__NOP();
}
}
SYS_WR_PROTECT = 0x0;
nvic_state = NVIC->ICER[0];
NVIC->ICER[0] = 0xffffffff; // 关闭所有中断
NVIC_EnableIRQ(WAKE_IRQn); /* 使能WAKE中断 */
NVIC_SetPriority(WAKE_IRQn, 0); /* 配置WAKE中断优先级 */
IWDG_PSW = 0xA6B4;
IWDG_CLR = 0x798D;
SCB->SCR |= (1UL << 2);
__enable_irq();
__WFI(); // 使用WFI指令进入休眠模式
// 休眠后程序会停在这里,唤醒后会执行WAKE_IRQHandler中断,随后从这里继续执行
__disable_irq();
SYS_WR_PROTECT = 0x7a83; /*开启寄存器写使能*/
SYS_AFE_REG0 = sys_reg_0;
SYS_AFE_REG1 = sys_reg_1;
SYS_AFE_REG2 = sys_reg_2;
SYS_AFE_REG3 = sys_reg_3;
SYS_AFE_REG4 = sys_reg_4;
SYS_AFE_REG5 = sys_reg_5;
SYS_AFE_REG7 = sys_reg_7;
SYS_CLK_FEN = sys_clk_fen;
{
int cnt;
for (cnt = 0; cnt < 4000; cnt++)
{
__NOP();
}
}
SYS_CLK_CFG = sys_clk_cfg;
{
int cnt;
for (cnt = 0; cnt < 4000; cnt++)
{
__NOP();
}
}
SYS_WR_PROTECT = 0x0;
__set_PRIMASK(irqstate); // 还原中断状态
NVIC->ISER[0] = nvic_state;
}
/**
* @brief 配置GPIO引脚复用功能
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource 引脚源
* @param GPIO_AF 复用功能选择
*/
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
uint8_t offset;
uint8_t pins = GPIO_PinSource >> 2;
offset = ((GPIO_PinSource & 0x3) * 4);
switch (pins)
{
case 0:
GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 1:
GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 2:
GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 3:
GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
break;
default:
break;
}
}
23.02_WAKE-定时唤醒
测试步骤
设置芯片为定时唤醒,唤醒时间为1S。 注意:使用深度休眠时,不能使用调试读写变量(例如debug或lksscope),会导致芯片无法正常唤醒。
实测数据
触发休眠后开始记录电流波形。
休眠前电流为14.412mA。
芯片电流 : 14.436193mA
参考代码
23.02_WAKE_SleepIwdgWake.c
/**
* @brief WAKE-定时唤醒
* @details
* 设置芯片为定时唤醒,唤醒时间为1S。
* 注意:使用深度休眠时,不能使用调试读写变量(例如debug或lksscope),会导致芯片无法正常唤醒。
*/
#include "lks32mc09x.h"
#include "lks32mc09x_adc.h"
#include "lks32mc09x_cmp.h"
#include "lks32mc09x_dac.h"
#include "lks32mc09x_gpio.h"
#include "lks32mc09x_lib.h"
#include "lks32mc09x_opa.h"
#include "lks32mc09x_reg.h"
#include "lks32mc09x_sys.h"
#include "lks32mc09x_tmp.h"
#include "lks32mc09x_wake.h"
/**
* @brief 主函数,用于测试HSI时钟输出及GPIO翻转
*/
int main(void)
{
// 打开所有模块,模拟实际应用场景
ADC_Enable(ADC0);
DAC_Enable(DAC_CHANNEL_0);
DAC_Enable(DAC_CHANNEL_1);
OPA_Enable(OPA_CHANNEL_0);
OPA_Enable(OPA_CHANNEL_1);
OPA_Enable(OPA_CHANNEL_2);
OPA_Enable(OPA_CHANNEL_3);
CMP_Enable(CMP_CHN_0);
CMP_Enable(CMP_CHN_1);
TMP_Enable();
GPIO_Enable();
GPIO_Config(GPIO0, GPIO_PinSource_0, GPIO_Mode_IN, GPIO_AF_GPIO);
GPIO_Config(GPIO2, GPIO_PinSource_6, GPIO_Mode_IN, GPIO_AF_GPIO);
while (1)
{
if (GPIO_ReadInputDataBit(GPIO2, GPIO_Pin_6))
{
WAKE_SetWakeInterval(IWDG_SECOND2IWDGCNT(2.0), IWDG_SECOND2IWDGCNT(1), 1); // 设置为休眠1秒后唤醒,如果没有唤醒,则2S后复位(复位功能需要先打开看门狗)
WAKE_FallSleep(); // 进入休眠状态
}
}
}
void WAKE_IRQHandler()
{
}
使用到的库函数
库函数部分代码
#define CMP_CHN_0 0
#define CMP_CHN_1 1
#define DAC_CHANNEL_0 0
#define DAC_CHANNEL_1 1
#define GPIO_AF_I2C 6
#define IWDG0 ((IWDG_TypeDef *)IWDG_BASE)
#define OPA_CHANNEL_0 0
#define OPA_CHANNEL_1 1
#define OPA_CHANNEL_2 2
#define OPA_CHANNEL_3 3
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_CMP BIT2
#define SYS_MODULE_GPIO BIT11
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* @brief GPIO模式选择枚举
*/
typedef enum GPIO_Mode_TypeDef {
GPIO_Mode_IN = 0,
GPIO_Mode_OUT = 1,
GPIO_Mode_ANA = 2,
GPIO_Mode_IO = 3,
}} {enum_name};
/**
* @brief GPIO配置函数
* @param GPIOx: GPIO端口
* @param GPIO_PinSource: GPIO引脚
* @param mode: GPIO模式
* @param GPIO_AF_x: GPIO复用功能选择
*/
void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
{
GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = 1 << GPIO_PinSource;
GPIO_InitStructure.GPIO_Mode = mode;
if (GPIO_AF_x == GPIO_AF_I2C)
{
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
}
else
{
__NOP();
}
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**
* @brief DAC使能
* @param DAC_Channel DAC通道选择
*/
void DAC_Enable(uint8_t DAC_Channel)
{
DAC_Cmd(DAC_Channel, ENABLE);
}
/**
* @brief DAC通道时钟使能
* @param DAC_Channel DAC通道选择
* @param state 使能状态,1模块使能 0 模块关闭
*/
void DAC_Cmd(uint8_t DAC_Channel, uint8_t state)
{
SYS0->PROTECT = 0x7a83;
if (DAC_Channel == DAC_CHANNEL_0)
{
if (state)
{
SYS0->AFE_REG5 |= BIT1;
}
else
{
SYS0->AFE_REG5 &= ~BIT1;
}
}
else
{
__NOP();
}
if (DAC_Channel == DAC_CHANNEL_1)
{
if (state)
{
SYS0->AFE_REG5 |= BIT2;
}
else
{
SYS0->AFE_REG5 &= ~BIT2;
}
}
else
{
__NOP();
}
SYS0->PROTECT = 0;
}
/**
* @brief OPA 使能
* @param OPA_CHANNEL_x OPA通道
*/
void OPA_Enable(uint32_t OPA_CHANNEL_x)
{
SYS0->PROTECT = 0x7a83;
switch (OPA_CHANNEL_x)
{
case OPA_CHANNEL_0:
SYS0->AFE_REG1 |= BIT12;
break;
case OPA_CHANNEL_1:
SYS0->AFE_REG1 |= BIT13;
break;
case OPA_CHANNEL_2:
SYS0->AFE_REG1 |= BIT14;
break;
case OPA_CHANNEL_3:
SYS0->AFE_REG1 |= BIT15;
break;
}
SYS0->PROTECT = 0;
}
/**
* @brief gpio使能
*/
void GPIO_Enable(void)
{
SYS_ModuleClockCmd(SYS_MODULE_GPIO, 1);
}
/**
* @brief 使能ADC模块
* @param ADCx: ADC模块指针,指向ADC0或ADC1
*/
void ADC_Enable(ADC_TypeDef *ADCx)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
// 使能ADC模块时钟
REG_SET(SYS0->AFE_REG7, BIT8 | BIT9);
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 使能比较器
* @param CMP_CHN_x: 比较器通道选择,CMP_CHN_0, CMP_CHN_1
*/
void CMP_Enable(uint8_t CMP_CHN_x)
{
SYS_ModuleClockCmd(SYS_MODULE_CMP, ENABLE);
if (CMP_CHN_x == CMP_CHN_0)
{
SYS0->PROTECT = 0x7a83;
SYS0->AFE_REG5 |= BIT6;
SYS0->PROTECT = 0;
}
else
{
__NOP();
}
if (CMP_CHN_x == CMP_CHN_1)
{
SYS0->PROTECT = 0x7a83;
SYS0->AFE_REG5 |= BIT7;
SYS0->PROTECT = 0;
}
else
{
__NOP();
}
}
/**
* @brief 使能温度传感器
*/
void TMP_Enable(void)
{
SYS_WR_PROTECT = 0x7a83;
SYS_AFE_REG5 |= BIT3;
SYS_WR_PROTECT = 0;
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 设置唤醒间隔时间
* @param rth 唤醒复位阈值,由于计数器计数阈值是从复位阈值开始计数的,所有唤醒阈值必须大于复位阈值
* @param wth 唤醒阈值
* @param ena 使能标志
*/
void WAKE_SetWakeInterval(uint32_t rth, uint32_t wth, uint8_t ena)
{
if (ena)
{
int32_t val;
IWDG0->CFG |= BIT4;
IWDG0->PSW = PSW_IWDG_PRE;
IWDG0->RTH = rth; // 设置复位阈值
val = rth - wth;
if (val < 0x100)
{
val = 0x100;
}
else
{
__NOP();
}
IWDG0->WTH = val; // 设置唤醒阈值
}
else
{
IWDG0->CFG &= ~BIT4;
}
}
/**
* @brief 读取指定GPIO引脚的输入数据位
* @param GPIOx GPIO模块指针
* @param GPIO_Pin 要读取的引脚
* @return 引脚的输入数据位状态
*/
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
return (GPIOx->PDI & GPIO_Pin) ? 1 : 0;
}
/**
* @brief 进入低功耗模式,必须定义WAKE_IRQHandler函数,WAKE_IRQHandler内可以不做任何操作
*/
void WAKE_FallSleep(void)
{
int irqstate;
uint16_t sys_reg_0;
uint16_t sys_reg_1;
uint16_t sys_reg_2;
uint16_t sys_reg_3;
uint16_t sys_reg_4;
uint16_t sys_reg_5;
uint16_t sys_reg_7;
uint32_t sys_clk_fen;
uint32_t sys_clk_cfg;
uint32_t nvic_state; // 各个使能的状态
irqstate = __get_PRIMASK(); // 保存全局中断状态
__disable_irq(); // 关闭全局中断
SYS_WR_PROTECT = 0x7a83; /*开启寄存器写使能*/
sys_reg_0 = SYS_AFE_REG0;
sys_reg_1 = SYS_AFE_REG1;
sys_reg_2 = SYS_AFE_REG2;
sys_reg_3 = SYS_AFE_REG3;
sys_reg_4 = SYS_AFE_REG4;
sys_reg_5 = SYS_AFE_REG5;
sys_reg_7 = SYS_AFE_REG7;
sys_clk_fen = SYS_CLK_FEN;
sys_clk_cfg = SYS_CLK_CFG;
SYS_AFE_REG0 = 0;
SYS_AFE_REG1 = BIT11 | BIT10; // 关闭BGP和RCH
SYS_AFE_REG2 = 0;
SYS_AFE_REG3 = 0;
SYS_AFE_REG4 = 0;
SYS_AFE_REG5 = (sys_reg_5 & (BIT8 | BIT9)); // 保持复位电压阈值不变
SYS_AFE_REG7 = 0;
SYS_CLK_FEN = 0;
SYS_CLK_CFG = 0x0;
{
int cnt;
for (cnt = 0; cnt < 4000; cnt++)
{
__NOP();
}
}
SYS_WR_PROTECT = 0x0;
nvic_state = NVIC->ICER[0];
NVIC->ICER[0] = 0xffffffff; // 关闭所有中断
NVIC_EnableIRQ(WAKE_IRQn); /* 使能WAKE中断 */
NVIC_SetPriority(WAKE_IRQn, 0); /* 配置WAKE中断优先级 */
IWDG_PSW = 0xA6B4;
IWDG_CLR = 0x798D;
SCB->SCR |= (1UL << 2);
__enable_irq();
__WFI(); // 使用WFI指令进入休眠模式
// 休眠后程序会停在这里,唤醒后会执行WAKE_IRQHandler中断,随后从这里继续执行
__disable_irq();
SYS_WR_PROTECT = 0x7a83; /*开启寄存器写使能*/
SYS_AFE_REG0 = sys_reg_0;
SYS_AFE_REG1 = sys_reg_1;
SYS_AFE_REG2 = sys_reg_2;
SYS_AFE_REG3 = sys_reg_3;
SYS_AFE_REG4 = sys_reg_4;
SYS_AFE_REG5 = sys_reg_5;
SYS_AFE_REG7 = sys_reg_7;
SYS_CLK_FEN = sys_clk_fen;
{
int cnt;
for (cnt = 0; cnt < 4000; cnt++)
{
__NOP();
}
}
SYS_CLK_CFG = sys_clk_cfg;
{
int cnt;
for (cnt = 0; cnt < 4000; cnt++)
{
__NOP();
}
}
SYS_WR_PROTECT = 0x0;
__set_PRIMASK(irqstate); // 还原中断状态
NVIC->ISER[0] = nvic_state;
}
/**
* @brief 配置GPIO引脚复用功能
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource 引脚源
* @param GPIO_AF 复用功能选择
*/
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
uint8_t offset;
uint8_t pins = GPIO_PinSource >> 2;
offset = ((GPIO_PinSource & 0x3) * 4);
switch (pins)
{
case 0:
GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 1:
GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 2:
GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 3:
GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
break;
default:
break;
}
}