目录

00.00_空工程

说明

实测数据

参考代码

00.00_none.c

/**
 * @brief 空工程
 * @details 
 * 
 */
#include "lks32mc09x_lib.h"
/**
 * @brief 主函数,用于测试HSI时钟输出及GPIO翻转
 */
int main(void)
{
    while (1)
    {
    }
}

使用到的库函数

库函数部分代码




01.01_ADC-软件触发

测试步骤

  1. 初始化ADC0以采样指定的通道(通道9)。
  2. 在主循环中,进行100次ADC采样,并计算采样结果的平均值。
  3. 将平均值转换为实际电压值

实测数据

浮点

右对齐

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

实测数据

TABY
0000
1100
2010
3111

参考代码

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}测试结果
CRC80xBC正确
CRC8_ITU0xE9正确
CRC8_MAXIM0x2A正确
CRC8_ROHC0xFE正确
CRC16_IBM0xBB0E正确
CRC16_MAXIM0x44F1正确
CRC16_USB0x44D5正确
CRC16_MODBUS0xBB2A正确
CRC16_CCITT0xED9B正确
CRC16_CCITT_FALSE0x9304正确
CRC16_X250x22EC正确
CRC16_XMODEM0x8208正确
CRC16_DNP0xE674正确
CRC320x470B99F4正确
CRC32_MPEG_20xE28F4B83正确

参考代码

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进行除法计算

实测数据

实测数据

abDSP_Div(a, b)DSP_Mod(a, b)div errmod err
-2147483648 (0x80000000)-2147483648 (0x80000000)1000
-2147483648 (0x80000000)-100000000 (0xfa0a1f00)21-4748364800
-2147483648 (0x80000000)-32768 (0xffff8000)65536000
-2147483648 (0x80000000)-10000 (0xffffd8f0)214748-364800
-2147483648 (0x80000000)-1 (0xffffffff)21474836470-10
-2147483648 (0x80000000)0 (0x00000000)0-214748364800
-2147483648 (0x80000000)1 (0x00000001)-2147483648000
-2147483648 (0x80000000)10000 (0x00002710)-214748-364800
-2147483648 (0x80000000)32767 (0x00007fff)-65538-200
-2147483648 (0x80000000)100000000 (0x05f5e100)-21-4748364800
-2147483648 (0x80000000)2147483647 (0x7fffffff)-1-100
-100000000 (0xfa0a1f00)-2147483648 (0x80000000)0-10000000000
-100000000 (0xfa0a1f00)-100000000 (0xfa0a1f00)1000
-100000000 (0xfa0a1f00)-32768 (0xffff8000)3051-2483200
-100000000 (0xfa0a1f00)-10000 (0xffffd8f0)10000000
-100000000 (0xfa0a1f00)-1 (0xffffffff)100000000000
-100000000 (0xfa0a1f00)0 (0x00000000)0-10000000000
-100000000 (0xfa0a1f00)1 (0x00000001)-100000000000
-100000000 (0xfa0a1f00)10000 (0x00002710)-10000000
-100000000 (0xfa0a1f00)32767 (0x00007fff)-3051-2788300
-100000000 (0xfa0a1f00)100000000 (0x05f5e100)-1000
-100000000 (0xfa0a1f00)2147483647 (0x7fffffff)0-10000000000
-32768 (0xffff8000)-2147483648 (0x80000000)0-3276800
-32768 (0xffff8000)-100000000 (0xfa0a1f00)0-3276800
-32768 (0xffff8000)-32768 (0xffff8000)1000
-32768 (0xffff8000)-10000 (0xffffd8f0)3-276800
-32768 (0xffff8000)-1 (0xffffffff)32768000
-32768 (0xffff8000)0 (0x00000000)0-3276800
-32768 (0xffff8000)1 (0x00000001)-32768000
-32768 (0xffff8000)10000 (0x00002710)-3-276800
-32768 (0xffff8000)32767 (0x00007fff)-1-100
-32768 (0xffff8000)100000000 (0x05f5e100)0-3276800
-32768 (0xffff8000)2147483647 (0x7fffffff)0-3276800
-10000 (0xffffd8f0)-2147483648 (0x80000000)0-1000000
-10000 (0xffffd8f0)-100000000 (0xfa0a1f00)0-1000000
-10000 (0xffffd8f0)-32768 (0xffff8000)0-1000000
-10000 (0xffffd8f0)-10000 (0xffffd8f0)1000
-10000 (0xffffd8f0)-1 (0xffffffff)10000000
-10000 (0xffffd8f0)0 (0x00000000)0-1000000
-10000 (0xffffd8f0)1 (0x00000001)-10000000
-10000 (0xffffd8f0)10000 (0x00002710)-1000
-10000 (0xffffd8f0)32767 (0x00007fff)0-1000000
-10000 (0xffffd8f0)100000000 (0x05f5e100)0-1000000
-10000 (0xffffd8f0)2147483647 (0x7fffffff)0-1000000
-1 (0xffffffff)-2147483648 (0x80000000)0-100
-1 (0xffffffff)-100000000 (0xfa0a1f00)0-100
-1 (0xffffffff)-32768 (0xffff8000)0-100
-1 (0xffffffff)-10000 (0xffffd8f0)0-100
-1 (0xffffffff)-1 (0xffffffff)1000
-1 (0xffffffff)0 (0x00000000)0-100
-1 (0xffffffff)1 (0x00000001)-1000
-1 (0xffffffff)10000 (0x00002710)0-100
-1 (0xffffffff)32767 (0x00007fff)0-100
-1 (0xffffffff)100000000 (0x05f5e100)0-100
-1 (0xffffffff)2147483647 (0x7fffffff)0-100
0 (0x00000000)-2147483648 (0x80000000)0000
0 (0x00000000)-100000000 (0xfa0a1f00)0000
0 (0x00000000)-32768 (0xffff8000)0000
0 (0x00000000)-10000 (0xffffd8f0)0000
0 (0x00000000)-1 (0xffffffff)0000
0 (0x00000000)0 (0x00000000)0000
0 (0x00000000)1 (0x00000001)0000
0 (0x00000000)10000 (0x00002710)0000
0 (0x00000000)32767 (0x00007fff)0000
0 (0x00000000)100000000 (0x05f5e100)0000
0 (0x00000000)2147483647 (0x7fffffff)0000
1 (0x00000001)-2147483648 (0x80000000)0100
1 (0x00000001)-100000000 (0xfa0a1f00)0100
1 (0x00000001)-32768 (0xffff8000)0100
1 (0x00000001)-10000 (0xffffd8f0)0100
1 (0x00000001)-1 (0xffffffff)-1000
1 (0x00000001)0 (0x00000000)0100
1 (0x00000001)1 (0x00000001)1000
1 (0x00000001)10000 (0x00002710)0100
1 (0x00000001)32767 (0x00007fff)0100
1 (0x00000001)100000000 (0x05f5e100)0100
1 (0x00000001)2147483647 (0x7fffffff)0100
10000 (0x00002710)-2147483648 (0x80000000)01000000
10000 (0x00002710)-100000000 (0xfa0a1f00)01000000
10000 (0x00002710)-32768 (0xffff8000)01000000
10000 (0x00002710)-10000 (0xffffd8f0)-1000
10000 (0x00002710)-1 (0xffffffff)-10000000
10000 (0x00002710)0 (0x00000000)01000000
10000 (0x00002710)1 (0x00000001)10000000
10000 (0x00002710)10000 (0x00002710)1000
10000 (0x00002710)32767 (0x00007fff)01000000
10000 (0x00002710)100000000 (0x05f5e100)01000000
10000 (0x00002710)2147483647 (0x7fffffff)01000000
32767 (0x00007fff)-2147483648 (0x80000000)03276700
32767 (0x00007fff)-100000000 (0xfa0a1f00)03276700
32767 (0x00007fff)-32768 (0xffff8000)03276700
32767 (0x00007fff)-10000 (0xffffd8f0)-3276700
32767 (0x00007fff)-1 (0xffffffff)-32767000
32767 (0x00007fff)0 (0x00000000)03276700
32767 (0x00007fff)1 (0x00000001)32767000
32767 (0x00007fff)10000 (0x00002710)3276700
32767 (0x00007fff)32767 (0x00007fff)1000
32767 (0x00007fff)100000000 (0x05f5e100)03276700
32767 (0x00007fff)2147483647 (0x7fffffff)03276700
100000000 (0x05f5e100)-2147483648 (0x80000000)010000000000
100000000 (0x05f5e100)-100000000 (0xfa0a1f00)-1000
100000000 (0x05f5e100)-32768 (0xffff8000)-30512483200
100000000 (0x05f5e100)-10000 (0xffffd8f0)-10000000
100000000 (0x05f5e100)-1 (0xffffffff)-100000000000
100000000 (0x05f5e100)0 (0x00000000)010000000000
100000000 (0x05f5e100)1 (0x00000001)100000000000
100000000 (0x05f5e100)10000 (0x00002710)10000000
100000000 (0x05f5e100)32767 (0x00007fff)30512788300
100000000 (0x05f5e100)100000000 (0x05f5e100)1000
100000000 (0x05f5e100)2147483647 (0x7fffffff)010000000000
2147483647 (0x7fffffff)-2147483648 (0x80000000)0214748364700
2147483647 (0x7fffffff)-100000000 (0xfa0a1f00)-214748364700
2147483647 (0x7fffffff)-32768 (0xffff8000)-655353276700
2147483647 (0x7fffffff)-10000 (0xffffd8f0)-214748364700
2147483647 (0x7fffffff)-1 (0xffffffff)-2147483647000
2147483647 (0x7fffffff)0 (0x00000000)0214748364700
2147483647 (0x7fffffff)1 (0x00000001)2147483647000
2147483647 (0x7fffffff)10000 (0x00002710)214748364700
2147483647 (0x7fffffff)32767 (0x00007fff)65538100
2147483647 (0x7fffffff)100000000 (0x05f5e100)214748364700
2147483647 (0x7fffffff)2147483647 (0x7fffffff)1000

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进行除法计算

实测数据

aDSP_Sqrt(a)err
0 (0x00000000)00
1 (0x00000001)10
10 (0x0000000a)30
100 (0x00000064)100
1000 (0x000003e8)310
10000 (0x00002710)1000
32767 (0x00007fff)1810
65535 (0x0000ffff)2550
65536 (0x00010000)2560
65536 (0x00010000)2560
131072 (0x00020000)3620
262144 (0x00040000)5120
524288 (0x00080000)7240
1048576 (0x00100000)10240
2097152 (0x00200000)14480
4194304 (0x00400000)20480
8388608 (0x00800000)28960
16777216 (0x01000000)40960
33554432 (0x02000000)57920
67108864 (0x04000000)81920
134217728 (0x08000000)115850
268435456 (0x10000000)163840
536870912 (0x20000000)231700
1073741824 (0x40000000)327680
2147483648 (0x80000000)463400
1 (0x00000001)10
3 (0x00000003)10
7 (0x00000007)20
15 (0x0000000f)30
31 (0x0000001f)50
63 (0x0000003f)70
127 (0x0000007f)110
255 (0x000000ff)150
511 (0x000001ff)220
1023 (0x000003ff)310
2047 (0x000007ff)450
4095 (0x00000fff)630
8191 (0x00001fff)900
16383 (0x00003fff)1270
32767 (0x00007fff)1810
65535 (0x0000ffff)2550
8191 (0x00001fff)900
16383 (0x00003fff)1270
32767 (0x00007fff)1810
65535 (0x0000ffff)2550
131071 (0x0001ffff)3620
262143 (0x0003ffff)5110
524287 (0x0007ffff)7240
1048575 (0x000fffff)10230
2097151 (0x001fffff)14480
4194303 (0x003fffff)20470
8388607 (0x007fffff)28960
16777215 (0x00ffffff)40950
33554431 (0x01ffffff)57920
67108863 (0x03ffffff)81910
134217727 (0x07ffffff)115850
268435455 (0x0fffffff)163830
536870911 (0x1fffffff)231700
1073741823 (0x3fffffff)327670
2147483647 (0x7fffffff)463400
4294967295 (0xffffffff)655350

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作为外部中断引脚,上升沿&下降沿均触发中断。 外部输入高低电平(默认低电平),记录中断发生次数。

实测数据

tP0.15Valcount
000
111
202
313
404
515
606
717
808
919
10010
11111
12012
13113
14014
15115
16016
17117
18018
19119

测试结果:通过

参考代码

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作为外部中断引脚,上升沿&下降沿均触发中断。 外部输入高低电平(默认低电平),记录中断发生次数。

实测数据

tP0.15Valcount
000
111
201
312
402
513
603
714
804
915
1005
1116
1206
1317
1407
1518
1608
1719
1809
19110

测试结果:通过

参考代码

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状态。

GPIOAF
P0.5HALL_IN0
P0.6HALL_IN1
P0.7HALL_IN2

实测数据

tHALL_IN0HALL_IN1HALL_IN2hall_valhall_fltval
000000
110011
201022
311033
400144
510155
601166
711177
800000
910011
1001022
1111033
1200144
1310155

参考代码

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-看门狗复位时间

测试步骤

  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的高电平时间即可确认实际看门狗复位时间。

实测数据

看门狗最小复位时间为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

测试步骤

  1. 配置MCPWM模块以输出三路中心对称的PWM信号,频率为20kHz。
  2. 配置GPIO引脚复用为MCPWM功能。
  3. 在主循环中,保持MCPWM模块的运行,并可以通过调试接口输出PWM的频率和占空比。

实测数据

引脚占空比频率
P1.431.28%19.96k
P1.552.15%19.96k
P1.631.25%19.96k
P1.752.13%19.96k
P1.831.25%19.97k
P1.952.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_DISABLE20.01%70.03%不使用该通道(默认配置:上管高有效,下管高有效,正常输出PWM)
MCPWM_IO_HH_PWM20.01%70.02%上管高有效,下管高有效,正常输出PWM
MCPWM_IO_HH_LOW0.00%100.00%上管高有效,下管高有效,下管恒开 此时pwm配置无效,但仍然会正常计数&触发中断
MCPWM_IO_HH_HIGH100.00%0.00%上管高有效,下管高有效,上管恒开 此时pwm配置无效,但仍然会正常计数&触发中断
MCPWM_IO_HH_HPWM20.01%0.00%上管高有效,下管高有效,上管斩波
MCPWM_IO_HH_LPWM0.00%70.03%上管高有效,下管高有效,下管斩波
MCPWM_IO_HH_OFF0.00%0.00%上管高有效,下管高有效,上下管全关,和MOE不同,通过IO可以关闭任意一路pwm
MCPWM_IO_HHS_PWM70.02%20.01%上管高有效,下管高有效,通道交换打开,正常输出PWM
MCPWM_IO_HHS_LOW100.00%0.00%上管高有效,下管高有效,通道交换打开,下管恒开 此时pwm配置无效,但仍然会正常计数&触发中断
MCPWM_IO_HHS_HIGH0.00%100.00%上管高有效,下管高有效,通道交换打开,上管恒开 此时pwm配置无效,但仍然会正常计数&触发中断
MCPWM_IO_HHS_HPWM0.00%20.01%上管高有效,下管高有效,通道交换打开,上管斩波
MCPWM_IO_HHS_LPWM70.02%0.00%上管高有效,下管高有效,通道交换打开,下管斩波
MCPWM_IO_HHS_OFF0.00%0.00%上管高有效,下管高有效,通道交换打开,上下管全关,和MOE不同,通过IO可以关闭任意一路pwm
MCPWM_IO_HL_PWM20.02%30.06%上管高有效,下管低有效,正常输出PWM
MCPWM_IO_HL_LOW0.00%0.00%上管高有效,下管低有效,下管恒开 此时pwm配置无效,但仍然会正常计数&触发中断
MCPWM_IO_HL_HIGH100.00%100.00%上管高有效,下管低有效,上管恒开 此时pwm配置无效,但仍然会正常计数&触发中断
MCPWM_IO_HL_HPWM20.01%100.00%上管高有效,下管低有效,上管斩波
MCPWM_IO_HL_LPWM0.00%30.06%上管高有效,下管低有效,下管斩波
MCPWM_IO_HL_OFF0.00%100.00%上管高有效,下管低有效,上下管全关,和MOE不同,通过IO可以关闭任意一路pwm
MCPWM_IO_HLS_PWM30.06%20.01%上管高有效,下管低有效,通道交换打开,正常输出PWM
MCPWM_IO_HLS_LOW0.00%0.00%上管高有效,下管低有效,通道交换打开,下管恒开 此时pwm配置无效,但仍然会正常计数&触发中断
MCPWM_IO_HLS_HIGH100.00%100.00%上管高有效,下管低有效,通道交换打开,上管恒开 此时pwm配置无效,但仍然会正常计数&触发中断
MCPWM_IO_HLS_HPWM100.00%20.01%上管高有效,下管低有效,通道交换打开,上管斩波
MCPWM_IO_HLS_LPWM30.06%0.00%上管高有效,下管低有效,通道交换打开,下管斩波
MCPWM_IO_HLS_OFF100.00%0.00%上管高有效,下管低有效,通道交换打开,上下管全关,和MOE不同,通过IO可以关闭任意一路pwm
MCPWM_IO_LH_PWM80.07%70.02%上管低有效,下管高有效,正常输出PWM
MCPWM_IO_LH_LOW100.00%100.00%上管低有效,下管高有效,下管恒开 此时pwm配置无效,但仍然会正常计数&触发中断
MCPWM_IO_LH_HIGH0.00%0.00%上管低有效,下管高有效,上管恒开 此时pwm配置无效,但仍然会正常计数&触发中断
MCPWM_IO_LH_HPWM80.07%0.00%上管低有效,下管高有效,上管斩波
MCPWM_IO_LH_LPWM100.00%70.03%上管低有效,下管高有效,下管斩波
MCPWM_IO_LH_OFF100.00%0.00%上管低有效,下管高有效,上下管全关,和MOE不同,通过IO可以关闭任意一路pwm
MCPWM_IO_LHS_PWM70.02%80.08%上管低有效,下管高有效,通道交换打开,正常输出PWM
MCPWM_IO_LHS_LOW100.00%100.00%上管低有效,下管高有效,通道交换打开,下管恒开 此时pwm配置无效,但仍然会正常计数&触发中断
MCPWM_IO_LHS_HIGH0.00%0.00%上管低有效,下管高有效,通道交换打开,上管恒开 此时pwm配置无效,但仍然会正常计数&触发中断
MCPWM_IO_LHS_HPWM0.00%80.07%上管低有效,下管高有效,通道交换打开,上管斩波
MCPWM_IO_LHS_LPWM70.03%100.00%上管低有效,下管高有效,通道交换打开,下管斩波
MCPWM_IO_LHS_OFF0.00%100.00%上管低有效,下管高有效,通道交换打开,上下管全关,和MOE不同,通过IO可以关闭任意一路pwm
MCPWM_IO_LL_PWM80.08%30.06%上管低有效,下管低有效,正常输出PWM
MCPWM_IO_LL_LOW100.00%0.00%上管低有效,下管低有效,下管恒开 此时pwm配置无效,但仍然会正常计数&触发中断
MCPWM_IO_LL_HIGH0.00%100.00%上管低有效,下管低有效,上管恒开 此时pwm配置无效,但仍然会正常计数&触发中断
MCPWM_IO_LL_HPWM80.08%100.00%上管低有效,下管低有效,上管斩波
MCPWM_IO_LL_LPWM100.00%30.06%上管低有效,下管低有效,下管斩波
MCPWM_IO_LL_OFF100.00%100.00%上管低有效,下管低有效,上下管全关,和MOE不同,通过IO可以关闭任意一路pwm
MCPWM_IO_LLS_PWM30.06%80.07%上管低有效,下管低有效,通道交换打开,正常输出PWM
MCPWM_IO_LLS_LOW0.00%100.00%上管低有效,下管低有效,通道交换打开,下管恒开 此时pwm配置无效,但仍然会正常计数&触发中断
MCPWM_IO_LLS_HIGH100.00%0.00%上管低有效,下管低有效,通道交换打开,上管恒开 此时pwm配置无效,但仍然会正常计数&触发中断
MCPWM_IO_LLS_HPWM100.00%80.07%上管低有效,下管低有效,通道交换打开,上管斩波
MCPWM_IO_LLS_LPWM30.06%100.00%上管低有效,下管低有效,通道交换打开,下管斩波
MCPWM_IO_LLS_OFF100.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

timeabzcnt
00000
11001
21102
30103
40004
51005
61106
70107
80008
91009
1011010
1101011
1200012
1310013
1411014
1501015
1601015
1711014
1810013
1900012
2001011
2111010
221009
230008
240107
251106
261005
270004
280103
291102
301001
310000

参考代码

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

timeabzcnt
00100
11101
20102
31103
40104
51105
60106
71107
80108
91109
1001010
1111011
1201012
1311013
1401014
1511015
1610015
1700014
1810013
1900012
2010011
2100010
221009
230008
241007
250006
261005
270004
281003
290002
301001
310000

参考代码

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

timeabzcnt
00000
11001
20002
31003
40004
51005
60006
71007
80008
91009
1000010
1110011
1200012
1310013
1400014
1510015
1601015
1700014
1801013
1900012
2001011
2100010
220109
230008
240107
250006
260105
270004
280103
290002
300101
310000

参考代码

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输出

测试步骤

  1. 使用P0.15并复用为timer0
  2. 初始化timer0 频率10k 输出占空比50%
  3. 注意:必须配置输出使能

实测数据

实测频率为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定时器

测试步骤

  1. 初始化timer0,设置timer时钟为2分频(主频96M,分频后48M),周期为1ms
  2. 在timer中断里翻转P0.0
  3. 不配置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-发送数据

测试步骤

  1. 初始化串口波特兰 115200
  2. 串口数据长度8位 停止位1位 MSL 关闭校验
  3. 串口发送数据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;
    }
}