极海APM32电机驱动板记录(二)

文章目录

  • 1、解除写保护
  • 2、极海驱动板资源概述
  • 3、新建工程
  • 4、点灯
  • 5、嘀嗒定时器
  • 6、中断
  • 7、串口打印
  • 8、adc读取
  • 9、i2c尝试
  • 10、定时器测试
  • 11、电机驱动pwm测试

上一篇文章算是简单了解了一下极海的板子开发环境吧,结果前几天板子来了,然后发现一个大bug,板子的芯片居然写保护了,不能刷flash,这里记录一下排查的过程
在这里插入图片描述
烧录是这个样子
在这里插入图片描述

1、解除写保护

先说一下解决方案吧,目前来看解决方案就是,安装一个jlink,之后打卡这个
在这里插入图片描述
这里因为我们是APM32F0,这里对应的选一下F0就行,也就是2
在这里插入图片描述
之后打印这个就是OK
在这里插入图片描述
下面的一个步骤至关重要,就是不要断电后再用stlink连接,这时候就可以刷固件了
在这里插入图片描述

2、极海驱动板资源概述

下面看一下板子的资源,首先是电源电路,稳压出来了12v,5v和3.3v
在这里插入图片描述
最小系统这里没什么特别的,就是晶振加复位电路
在这里插入图片描述
之后是一些常见的外设,这里关注一下USB引出了一路串口,为串口2,然后是两个按键,一个led
在这里插入图片描述
下面是foc的检测电路,电流和霍尔的
在这里插入图片描述
foc的驱动芯片用的是FD6288
在这里插入图片描述
三相驱动的mos
在这里插入图片描述
电流检测电路
在这里插入图片描述

3、新建工程

这个参考这个老哥的吧,或者参考我之前的那篇eide的文章,也是可以的,这里因为apm32使用eide不能下代码,后面我就还是用keil了
添加链接描述

我是比较喜欢这种方式的
在这里插入图片描述
添加到keil里面这样
在这里插入图片描述
bsp里面是自己实现的一些驱动,app里面写一些功能性的东西,最后编译一下(这里直接把demo的程序复制过来就行)
在这里插入图片描述
编译通过的话就说明工程没什么问题了

4、点灯

这里我们参考这个样例
在这里插入图片描述
我觉得官方的这个样例写的还不错,他所有的外设是统一管理的,用这两个文件来描述外设资源
在这里插入图片描述
然后相关的资源都是用的宏来表示,这个和之前看过的野火的stm32教程很像
在这里插入图片描述
值得好评的是这里还用了枚举的方式来表示具体的外设资源,值得好评
在这里插入图片描述
但是他这个demo的板子的资源和我们的还是不太一样,因此还是不能直接用的,需要一个适合我们自己的,因此这里我结合他的代码风格写了一个适合我们驱动板的,这里直接贴代码

/*!
 * @file        main.h
 *
 * @brief       Header for main.c module
 *
 * @version     V1.0.0
 *
 * @date        2022-09-30
 *
 * @attention
 *
 *  Copyright (C) 2022-2023 Geehy Semiconductor
 *
 *  You may not use this file except in compliance with the
 *  GEEHY COPYRIGHT NOTICE (GEEHY SOFTWARE PACKAGE LICENSE).
 *
 *  The program is only for reference, which is distributed in the hope
 *  that it will be useful and instructional for customers to develop
 *  their software. Unless required by applicable law or agreed to in
 *  writing, the program is distributed on an "AS IS" BASIS, WITHOUT
 *  ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the GEEHY SOFTWARE PACKAGE LICENSE for the governing permissions
 *  and limitations under the License.
 */

/* Define to prevent recursive inclusion */
#ifndef __MAIN_H
#define __MAIN_H

#ifdef __cplusplus
extern "C" {
#endif

/* Includes */
#include "apm32f035.h"

#include "apm32f035_gpio.h"
#include "apm32f035_usart.h"
#include "apm32f035_rcm.h"
#include "apm32f035_misc.h"
#include "apm32f035_eint.h"
#include "apm32f035_syscfg.h"
#include "apm32f035_adc.h"

#define DEBUG_USART            USART2


#define LEDn                   1
#define LED1_PIN               GPIO_PIN_15
#define LED1_GPIO_PORT         GPIOC
#define LED1_GPIO_CLK          RCM_AHB_PERIPH_GPIOC

#define BUTTONn                2

#define KEY1_BUTTON_PIN        GPIO_PIN_13
#define KEY1_BUTTON_GPIO_PORT  GPIOC
#define KEY1_BUTTON_GPIO_CLK   RCM_AHB_PERIPH_GPIOC
#define KEY1_BUTTON_EXTI_LINE  EINT_LINE13

#define KEY2_BUTTON_PIN        GPIO_PIN_14
#define KEY2_BUTTON_GPIO_PORT  GPIOC
#define KEY2_BUTTON_GPIO_CLK   RCM_AHB_PERIPH_GPIOC
#define KEY2_BUTTON_EXTI_LINE  EINT_LINE14

#define COMn                   2

#define MINI_COM1              USART1
#define MINI_COM1_CLK          RCM_APB2_PERIPH_USART1

#define MINI_COM1_TX_PIN       GPIO_PIN_9
#define MINI_COM1_TX_GPIO_PORT GPIOA
#define MINI_COM1_TX_GPIO_CLK  RCM_AHB_PERIPH_GPIOA
#define MINI_COM1_TX_SOURCE    GPIO_PIN_SOURCE_9
#define MINI_COM1_TX_AF        GPIO_AF_PIN1

#define MINI_COM1_RX_PIN       GPIO_PIN_10
#define MINI_COM1_RX_GPIO_PORT GPIOA
#define MINI_COM1_RX_GPIO_CLK  RCM_AHB_PERIPH_GPIOA
#define MINI_COM1_RX_SOURCE    GPIO_PIN_SOURCE_10
#define MINI_COM1_RX_AF        GPIO_AF_PIN1

#define MINI_COM1_IRQn         USART1_IRQn

#define MINI_COM2              USART2
#define MINI_COM2_CLK          RCM_APB1_PERIPH_USART2

#define MINI_COM2_TX_PIN       GPIO_PIN_11
#define MINI_COM2_TX_GPIO_PORT GPIOB
#define MINI_COM2_TX_GPIO_CLK  RCM_AHB_PERIPH_GPIOB
#define MINI_COM2_TX_SOURCE    GPIO_PIN_SOURCE_11
#define MINI_COM2_TX_AF        GPIO_AF_PIN4

#define MINI_COM2_RX_PIN       GPIO_PIN_12
#define MINI_COM2_RX_GPIO_PORT GPIOB
#define MINI_COM2_RX_GPIO_CLK  RCM_AHB_PERIPH_GPIOB
#define MINI_COM2_RX_SOURCE    GPIO_PIN_SOURCE_12
#define MINI_COM2_RX_AF        GPIO_AF_PIN4

#define MINI_COM2_IRQn         USART2_IRQn

typedef enum {
    LED1 = 0
} LED_T;

typedef enum {
    BUTTON_KEY1 = 0,
    BUTTON_KEY2 = 1
} BUTTON_T;

typedef enum {
    BUTTON_MODE_GPIO = 0,
    BUTTON_MODE_EINT = 1
} BUTTONMODE_T;

typedef enum {
    COM1 = 0,
    COM2 = 1
} COM_T;

#ifdef __cplusplus
}
#endif

#endif

新建了一个main.h的文件,结合当前这个驱动板的外设,设计如下
在这里插入图片描述
下面开始驱动led,这里关于gpio的驱动我都新建一个文件来管理
在这里插入图片描述
前面定义了那些宏,在这里就要用上了,这样就完成了一次封装,很不错
在这里插入图片描述
下面是驱动的代码,这里直接贴出来

void GPIO_Init(void)
{
    GPIO_Config_T gpioConfig;
    RCM_EnableAHBPeriphClock(LED1_GPIO_CLK);
    /* LED1 GPIO configuration */
    gpioConfig.pin     = LED1_PIN;
    gpioConfig.mode    = GPIO_MODE_OUT;
    gpioConfig.outtype = GPIO_OUT_TYPE_PP;
    gpioConfig.speed   = GPIO_SPEED_50MHz;
    gpioConfig.pupd    = GPIO_PUPD_NO;
    GPIO_Config(LED1_GPIO_PORT, &gpioConfig);
}

void APM_MINI_LEDOn(LED_T Led)
{
    GPIO_PORT[Led]->BR = GPIO_PIN[Led];
}

void APM_MINI_LEDOff(LED_T Led)
{
    GPIO_PORT[Led]->BSC = GPIO_PIN[Led];
}

void APM_MINI_LEDToggle(LED_T Led)
{
    GPIO_PORT[Led]->ODATA ^= GPIO_PIN[Led];
}

在这里插入图片描述

5、嘀嗒定时器

这里写法和stm32的那一套基本一致,直接贴,为什么这么写可以参考正点原子的教程

#include "systick.h"

uint32_t cntUs = 0;
uint32_t cntMs = 0;

void APM_MINI_DelayInit(void)
{
    SysTick_ConfigCLKSource(SysTick_CLKSource_HCLK_Div8);

    cntUs = SystemCoreClock / 8000000;
    cntMs = (uint16_t)cntUs * 1000;
}

void APM_MINI_DelayMs(__IO uint32_t nms)
{
    uint32_t temp;

    SysTick->LOAD = (uint32_t)nms * cntMs;
    SysTick->VAL  = 0x00;
    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
    do {
        temp = SysTick->CTRL;
    } while ((temp & 0x01) && !(temp & (1 << 16)));
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
    SysTick->VAL = 0x00;
}

void APM_MINI_DelayUs(__IO uint32_t nus)
{
    uint32_t temp;

    SysTick->LOAD = (uint32_t)nus * cntUs;
    SysTick->VAL  = 0x00;
    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
    do {
        temp = SysTick->CTRL;
    } while ((temp & 0x01) && !(temp & (1 << 16)));
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
    SysTick->VAL = 0x00;
}

我用逻辑分析仪抓了一下数据,还是比较准的
在这里插入图片描述
效果如下:
请添加图片描述

6、中断

下面看一下gpio的中断部分,首先还是gpio的配置,这里还是普通配置,配置为输入部分,如果要配置中断,可以看到下面加了个中断模式的配置,就需要配置对应的中断。
在这里插入图片描述
然后,毫不意外会有一个中断的文件,在这里加入我们的中断服务程序
在这里插入图片描述
这个服务函数可以查到是这个
在这里插入图片描述
要实现中断服务函数的判断按键状态,也就是下面这个函数
在这里插入图片描述
在中断服务程序中判断中断线的状态,之后清除中断标志
在这里插入图片描述
效果如下:
请添加图片描述

7、串口打印

串口也算是基本外设了,这里串口的功能配置如下
在这里插入图片描述
直接贴代码

#include "uart.h"
#include "stdio.h"

USART_T *COM_USART[COMn]                        = {MINI_COM1, MINI_COM2};
GPIO_T *COM_TX_PORT[COMn]                       = {MINI_COM1_TX_GPIO_PORT, MINI_COM2_TX_GPIO_PORT};
GPIO_T *COM_RX_PORT[COMn]                       = {MINI_COM1_RX_GPIO_PORT, MINI_COM2_RX_GPIO_PORT};
const uint32_t COM_USART_CLK[COMn]              = {MINI_COM1_CLK, MINI_COM2_CLK};
const RCM_AHB_PERIPH_T COM_TX_PORT_CLK[COMn]    = {MINI_COM1_TX_GPIO_CLK, MINI_COM2_TX_GPIO_CLK};
const RCM_AHB_PERIPH_T COM_RX_PORT_CLK[COMn]    = {MINI_COM1_RX_GPIO_CLK, MINI_COM2_RX_GPIO_CLK};
const GPIO_PIN_T COM_TX_PIN[COMn]               = {MINI_COM1_TX_PIN, MINI_COM2_TX_PIN};
const GPIO_PIN_T COM_RX_PIN[COMn]               = {MINI_COM1_RX_PIN, MINI_COM2_RX_PIN};
const GPIO_PIN_SOURCE_T COM_TX_PIN_SOURCE[COMn] = {MINI_COM1_TX_SOURCE, MINI_COM2_TX_SOURCE};
const GPIO_PIN_SOURCE_T COM_RX_PIN_SOURCE[COMn] = {MINI_COM1_RX_SOURCE, MINI_COM2_RX_SOURCE};
const GPIO_AF_T COM_TX_AF[COMn]                 = {MINI_COM1_TX_AF, MINI_COM2_TX_AF};
const GPIO_AF_T COM_RX_AF[COMn]                 = {MINI_COM1_RX_AF, MINI_COM2_RX_AF};


void APM_MINI_COMInit(COM_T COM)
{
    GPIO_Config_T gpioConfig;
    USART_Config_T usartConfigStruct;
    /* Enable GPIO clock */
    RCM_EnableAHBPeriphClock(COM_TX_PORT_CLK[COM] | COM_RX_PORT_CLK[COM]);

    /* Enable COM1 or COM2 clock */
    if (COM == COM1) {
        RCM_EnableAPB2PeriphClock(COM_USART_CLK[COM]);
    } else {
        RCM_EnableAPB1PeriphClock(COM_USART_CLK[COM]);
    }

    /* Connect PXx to USARTx_Tx */
    GPIO_ConfigPinAF(COM_TX_PORT[COM], COM_TX_PIN_SOURCE[COM], COM_TX_AF[COM]);

    /* Connect PXx to USARTx_Rx */
    GPIO_ConfigPinAF(COM_RX_PORT[COM], COM_RX_PIN_SOURCE[COM], COM_RX_AF[COM]);

    /* Configure USART Tx as alternate function push-pull */
    gpioConfig.mode    = GPIO_MODE_AF;
    gpioConfig.pin     = COM_TX_PIN[COM];
    gpioConfig.speed   = GPIO_SPEED_50MHz;
    gpioConfig.outtype = GPIO_OUT_TYPE_PP;
    gpioConfig.pupd    = GPIO_PUPD_PU;
    GPIO_Config(COM_TX_PORT[COM], &gpioConfig);

    /* Configure USART Rx as input floating */
    gpioConfig.pin = COM_RX_PIN[COM];
    GPIO_Config(COM_RX_PORT[COM], &gpioConfig);

    /* MINI_USARTs configured as follow:
    - BaudRate = 115200 baud
    - Word Length = 8 Bits
    - One Stop Bit
    - No parity
    - Hardware flow control disabled (RTS and CTS signals)
    - Receive and transmit enabled
    */

    usartConfigStruct.baudRate         = 115200;
    usartConfigStruct.mode             = USART_MODE_TX_RX;
    usartConfigStruct.hardwareFlowCtrl = USART_FLOW_CTRL_NONE;
    usartConfigStruct.parity           = USART_PARITY_NONE;
    usartConfigStruct.stopBits         = USART_STOP_BIT_1;
    usartConfigStruct.wordLength       = USART_WORD_LEN_8B;
    USART_Config(COM_USART[COM], &usartConfigStruct);

    /* Enable USART_Interrupt_RXBNEIE */
    USART_EnableInterrupt(COM_USART[COM], USART_INT_RXBNEIE);

    if (COM_USART[COM] == USART1) {
        NVIC_EnableIRQRequest(USART1_IRQn, 2);
    } else if (COM_USART[COM] == USART2) {
        NVIC_EnableIRQRequest(USART2_IRQn, 3);
    }

    /* Enable USART */
    USART_Enable(COM_USART[COM]);
}

#if defined(__CC_ARM) || defined(__ICCARM__) || (defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050))

int fputc(int ch, FILE *f)
{
    /* send a byte of data to the serial port */
    USART_TxData(DEBUG_USART, (uint8_t)ch);

    /* wait for the data to be send  */
    while (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET)
        ;

    return (ch);
}

#elif defined(__GNUC__)
int __io_putchar(int ch)
{
    /* send a byte of data to the serial port */
    USART_TxData(DEBUG_USART, ch);

    /* wait for the data to be send  */
    while (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET)
        ;

    return ch;
}
int _write(int file, char *ptr, int len)
{
    int i;
    for (i = 0; i < len; i++) {
        __io_putchar(*ptr++);
    }

    return len;
}

#else
#warning Not supported compiler type
#endif

测试串口输出
在这里插入图片描述

8、adc读取

这里adc用这个进行测试,原理图如下,可以看到时PA7引脚,然后连了这两个一个旋转编码器
在这里插入图片描述
这里直接贴上adc的配置程序

void ADC_Init(void)
{
    GPIO_Config_T gpioConfig;
    ADC_Config_T  adcConfig;

    /* RCM Enable*/
    RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOA);
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC);
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SYSCFG);
    /* GPIO Configuration */
    gpioConfig.pin = GPIO_PIN_7;
    gpioConfig.mode = GPIO_MODE_AN;
    gpioConfig.pupd = GPIO_PUPD_PU;
    GPIO_Config(GPIOA, &gpioConfig);
    /* ADC Configuration */
    ADC_Reset();
    ADC_ConfigStructInit(&adcConfig);
    /* Set resolution*/
    adcConfig.resolution    = ADC_RESOLUTION_12B;
    /* Set dataAlign*/
    adcConfig.dataAlign     = ADC_DATA_ALIGN_RIGHT;
    /* Set scanDir*/
    adcConfig.scanDir       = ADC_SCAN_DIR_UPWARD;
    /* Set convMode continous*/
    adcConfig.convMode      = ADC_CONVERSION_CONTINUOUS;
    /* Set extTrigConv*/
    adcConfig.extTrigConv1  = ADC_EXT_TRIG_CONV_TRG0;
    /* Set TrigEdge*/
    adcConfig.extTrigEdge1  = ADC_EXT_TRIG_EDGE_NONE;

    ADC_Config(&adcConfig);
    ADC_ConfigChannel(ADC_CHANNEL_7, ADC_SAMPLE_TIME_239_5);
    /* Enable Interrupt*/
    ADC_EnableInterrupt(ADC_INT_CS);

    NVIC_EnableIRQRequest(ADC_COMP_IRQn, 2);

    /* Calibration*/
    ADC_ReadCalibrationFactor();
    /* Enable ADC*/
    ADC_Enable();
}

上面设置了中断读取,因此我们还需要写一个中断服务程序

void ADC_Isr(void)
{
    uint32_t adcData = 0;
    uint32_t voltage = 0;

    if (ADC_ReadIntFlag(ADC_INT_FLAG_CS) == SET)
    {
        ADC_ClearIntFlag(ADC_INT_FLAG_CS);
        /* Read ADC Conversion value */
        adcData = ADC_ReadConversionValue();
        /*  voltage(mV) =  adcData * (3300mV / 4095) */
        voltage = (adcData * 3300) / 4095;
        /* output to serial port */
        printf("voltage : %d mV\r\n", voltage);

        APM_MINI_LEDToggle(LED1);
        APM_MINI_DelayMs(1000);
    }
}

运行结果如下:
在这里插入图片描述

9、i2c尝试

这里简单做个oled的实验,没有单片机引脚引出了,因此这里用这个串口的来试试
在这里插入图片描述
这里直接贴代码

#include "oled_soft.h"
#include "Font.h"
#include "systick.h"

/*
SDA=PD2;		//master out slave in
SCL=PC12;		//总线时钟
*/
#define OLED_IIC_SDA_APB_GPIO 		RCM_AHB_PERIPH_GPIOB
#define OLED_IIC_SDA_Pin			GPIO_PIN_11
#define OLED_IIC_SDA_GPIO			GPIOB

#define OLED_IIC_SCL_APB_GPIO 		RCM_AHB_PERIPH_GPIOB
#define OLED_IIC_SCL_Pin			GPIO_PIN_12
#define OLED_IIC_SCL_GPIO			GPIOB

#define OLED_IIC_SDA_HIGH() 		GPIO_SetBit(OLED_IIC_SDA_GPIO,OLED_IIC_SDA_Pin)		//SDA置位
#define OLED_IIC_SDA_LOW() 			GPIO_ClearBit(OLED_IIC_SDA_GPIO,OLED_IIC_SDA_Pin)	//SDA清0

#define OLED_IIC_SCL_HIGH() 		GPIO_SetBit(OLED_IIC_SCL_GPIO,OLED_IIC_SCL_Pin)		//SCL置位
#define OLED_IIC_SCL_LOW() 			GPIO_ClearBit(OLED_IIC_SCL_GPIO,OLED_IIC_SCL_Pin)	//SCL清0

void OLED_IIC_SCL(unsigned char sta)
{
    if (sta) {
        OLED_IIC_SCL_HIGH();
    } else {
        OLED_IIC_SCL_LOW();
    }

    //	APM_MINI_DelayMs(1);//按照需要添加延时
}
void OLED_IIC_SDA(unsigned char sta)
{
    if (sta) {
        OLED_IIC_SDA_HIGH();
    } else {
        OLED_IIC_SDA_LOW();
    }

    //	APM_MINI_DelayMs(1);//按照需要添加延时
}

void OLED12864_IoInit(void)//IO初始化
{
    GPIO_Config_T GPIO_InitStructure;						//定义GPIO结构体
    RCM_EnableAPB2PeriphClock(OLED_IIC_SDA_APB_GPIO); 		//开启GPIO模块的APB时钟
    RCM_EnableAPB2PeriphClock(OLED_IIC_SCL_APB_GPIO); 		//开启GPIO模块的APB时钟
    GPIO_InitStructure.pin = OLED_IIC_SDA_Pin;				//配置端口
    GPIO_InitStructure.mode = GPIO_MODE_OUT;				//配置端口为开漏输出模式
    GPIO_InitStructure.outtype = GPIO_OUT_TYPE_OD;
    GPIO_InitStructure.speed = GPIO_SPEED_50MHz;				//配置端口速度
    
    GPIO_Config(OLED_IIC_SDA_GPIO, &GPIO_InitStructure);	//对配置初始化
    GPIO_InitStructure.pin = OLED_IIC_SCL_Pin;				//配置端口
    GPIO_InitStructure.mode = GPIO_MODE_OUT;				//配置端口为开漏输出模式
    GPIO_InitStructure.outtype = GPIO_OUT_TYPE_OD;
    GPIO_InitStructure.speed = GPIO_SPEED_50MHz;				//配置端口速度
    GPIO_Config(OLED_IIC_SCL_GPIO, &GPIO_InitStructure);	//对配置初始化
}
unsigned char OLED_IIC_SDA_Read(void)	//读取SDA
{
    return GPIO_ReadInputBit(OLED_IIC_SDA_GPIO, OLED_IIC_SDA_Pin);
}
void OLED_IIC_Start(void)//IIC start
{
    OLED_IIC_SCL(1);			//SCL 1 释放SCL
    OLED_IIC_SDA(1);			//SDA 1
    OLED_IIC_SDA(0);			//SDA 0
    OLED_IIC_SCL(0);
}
void OLED_IIC_Stop(void)//IIC stop
{
    OLED_IIC_SCL(1);			//SCL 1
    OLED_IIC_SDA(0);			//SDA 0
    OLED_IIC_SDA(1);			//SDA 1
}
void OLED_IIC_SendByte(unsigned char dat)
{
    unsigned char i, temp, Even;
    temp = dat;

    for (i = 0; i < 8; i++) {
        Even = temp & 0x80;
        temp = temp << 1;

        if (Even == 0x80) { //先写入高位
            OLED_IIC_SDA(1);
        } else {
            OLED_IIC_SDA(0);
        }

        OLED_IIC_SCL(1);
        OLED_IIC_SCL(0);
    }
}
void OLED_IIC_SendACK(unsigned char dat)//主机产生应答
{
    OLED_IIC_SDA(dat);
    OLED_IIC_SCL(1);
    OLED_IIC_SCL(0);
}
unsigned char OLED_IIC_ReceByte(void)
{
    unsigned char i, temp = 0;
    OLED_IIC_SDA(1);//释放SDA 有SCL接管

    for (i = 0; i < 8; i++) {
        OLED_IIC_SCL(1);

        if (OLED_IIC_SDA_Read()) {
            temp = temp | 0x01;
        }

        temp = temp << 1;
        OLED_IIC_SCL(0);
    }

    return temp;
}
unsigned char OLED_IIC_ReceACK(void)//丛机产生应答 由主机读取
{
    unsigned char AckBit = 0x00;
    OLED_IIC_SDA(1);//释放SDA
    OLED_IIC_SCL(1);
    AckBit = OLED_IIC_SDA_Read();
    OLED_IIC_SCL(0);
    return AckBit;
}
void OLED12864_SendCom(unsigned char cmd)
{
    OLED_IIC_Start();
    OLED_IIC_SendByte(0x78);	//器件地址0x78 读写位0 写入
    OLED_IIC_ReceACK();
    OLED_IIC_SendByte(0x00);	//命令关键字
    OLED_IIC_ReceACK();
    OLED_IIC_SendByte(cmd);		//写入的命令
    OLED_IIC_ReceACK();
    OLED_IIC_Stop();
}
void OLED12864_SendData(unsigned char dat)
{
    OLED_IIC_Start();
    OLED_IIC_SendByte(0x78);	//器件地址0x78 读写位0 写入
    OLED_IIC_ReceACK();
    OLED_IIC_SendByte(0x40);	//数据寄存器地址
    OLED_IIC_ReceACK();
    OLED_IIC_SendByte(dat);		//写入的数据
    OLED_IIC_ReceACK();
    OLED_IIC_Stop();
}
unsigned char OLED12864_ReadData(unsigned char Add)
{
    unsigned char temp = 0;
    OLED_IIC_Start();
    OLED_IIC_SendByte(0x78);	//器件地址0x78 读写位0 写入
    OLED_IIC_ReceACK();
    OLED_IIC_SendByte(Add);		//数据寄存器地址
    OLED_IIC_ReceACK();
    OLED_IIC_Start();
    OLED_IIC_SendByte(0x78 | 0x01);	//器件地址0x78 读写位1 读出
    OLED_IIC_ReceACK();
    temp = OLED_IIC_ReceByte();
    OLED_IIC_SendACK(1);
    OLED_IIC_Stop();
    return temp;
}

void  OLED12864_Init(void)
{
    OLED12864_IoInit();			//IO闁告帗绻傞~鎰板礌閿燂拷
    APM_MINI_DelayMs(20);
    OLED12864_SendCom(0xAE);//命令AE:关闭OLED面板 AF:ON
    OLED12864_SendCom(0x02);//---set low column address
    OLED12864_SendCom(0x10);//---set high column address
    OLED12864_SendCom(0x40);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
    OLED12864_SendCom(0x81);//--set contrast control register
    OLED12864_SendCom(0xCF); // Set SEG Output Current Brightness
    OLED12864_SendCom(0xA1);//0xa0左右反置 0xa1正常
    OLED12864_SendCom(0xC8);//0xc0上下反置 0xc8正常
    OLED12864_SendCom(0xA6);//--set normal display
    OLED12864_SendCom(0xA8);//--set multiplex ratio(1 to 64)
    OLED12864_SendCom(0x3f);//--1/64 duty
    OLED12864_SendCom(0x81);//对比度设置
    OLED12864_SendCom(0xFF);//1~255;默认0X7F (亮度设置,越大越亮)
    OLED12864_SendCom(0xD3);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
    OLED12864_SendCom(0x00);//-not offset
    OLED12864_SendCom(0xd5);//--set display clock divide ratio/oscillator frequency
    OLED12864_SendCom(0x80);//--set divide ratio, Set Clock as 100 Frames/Sec
    OLED12864_SendCom(0xD9);//--set pre-charge period
    OLED12864_SendCom(0xF1);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
    OLED12864_SendCom(0xDA);//--set com pins hardware configuration
    OLED12864_SendCom(0x12);
    OLED12864_SendCom(0xDB);//--set vcomh
    OLED12864_SendCom(0x40);//Set VCOM Deselect Level
    OLED12864_SendCom(0x20);//-Set Page Addressing Mode (0x00/0x01/0x02)
    OLED12864_SendCom(0x02);//
    OLED12864_SendCom(0x8D);//--set Charge Pump enable/disable
    OLED12864_SendCom(0x14);//--set(0x10) disable
    OLED12864_SendCom(0xA4);// Disable Entire Display On (0xa4/0xa5)
    OLED12864_SendCom(0xA6);// Disable Inverse Display On (0xa6/a7)
    OLED12864_SendCom(0xAF);//--turn on oled panel
    OLED12864_Clean();		//
}

Point Buff;
Point Start;

unsigned char DisBuff[8][128] = {0}; //用于显示的缓存
/*********将光标指向坐标(x,y)*********************
** 功能:  将光标指向坐标(x,y)
** 参数:  X:y:坐标 x 0-127 ,y 0-7
** 返回值:无
******************************************************/
void  Location(unsigned char x, unsigned char y)
{
    OLED12864_SendCom(0xb0 + y); //设置页
    OLED12864_SendCom(0x10 + ((x >> 4) & 0x0f)); //设置列高4位
    OLED12864_SendCom(x & 0x0f); //设置列低4位
}
/*********OLED对比度调节************************************
** 功能:OLED对比度调节
** 参数:  vlcd:微调对比度数值 Vlcd:0x00~0xff 中间值:0x7f
** 返回值:无
******************************************************/
void  OLED12864_TurnBias(unsigned char Vlcd)
{
    OLED12864_SendCom(0x81); 	//微调对比度命令
    OLED12864_SendCom(Vlcd); 	//微调对比度的值,要与命令0x81一起使用 可设置范围 0x00~0xff
}
/*********设置缓冲区指针坐标*********************
** 功能:  设置缓冲区指针坐标
** 参数:  x:0~127 y:0~63
** 返回值:无
******************************************************/
void  OLED12864_Location(unsigned char x, unsigned char y)
{
    if (y >= LCD_Y) {
        y = 0;    //纠正错误纵坐标输入
    }

    if (x >= LCD_X) {
        x = 0;    //纠正错误横坐标输入
    }

    Buff.y = y;			//设置纵坐标
    Buff.x = x;			//设置横坐标
}
/*********将整个缓冲区全部写入液晶*********************
** 功能:  将整个缓冲区全部写入液晶 即显示整个DisBuff的内容 调试用
** 参数:  无
** 返回值:无
******************************************************/
void  RefreshAll(void)
{
    unsigned char i, j;
    Location(0, 0);

    for (i = 0; i < LCD_COLUMN; i++) {
        for (j = 0; j < LCD_X; j++) {
            Location(j, i);
            OLED12864_SendData(DisBuff[i][j]);
        }
    }
}
/*********将指定缓冲区指定区域写入*********************
** 功能:  将整个缓冲区指定区域写入
** 参数:  x:0-127 y:0-63  Width:0-127:长度 Height:0-63:高度
** 返回值:无
******************************************************/
void  Refresh(unsigned char x, unsigned char y, unsigned char Width, unsigned char Height)
{
    unsigned char WidthI, HeightI;
    unsigned char StartRow, StartColumn, EndRow, EndColumn;
    StartRow = y / 8;											//刷新起始行
    StartColumn = x;											//刷新起始列
    EndRow = (y + Height - 1) / 8;									//刷新终止行
    EndColumn = x + Width - 1;

    //刷新终止列
    for (HeightI = StartRow; HeightI <= EndRow; HeightI++) {		//刷新第height_i行	//刷新指定行
        Location(StartColumn, HeightI);						//刷新该行的width列		//从行首开始刷新

        for (WidthI = StartColumn; WidthI <= EndColumn; WidthI++) {	//刷新每行的指定列
            OLED12864_SendData(DisBuff[HeightI][WidthI]);
        }
    }
}
/*********清除缓冲区1字节数据*********************
** 功能:  清除缓冲区1字节数据,x,y为该字节最低位起始坐标
** 参数:  x:0-127 y:0-63
** 返回值:无
比如 ClearPointofBuf(15,25); 即清除当前点所在字节的坐标点 即清除点:
(15,25)(15,26)(15,27)(15,28)(15,29)(15,30)(15,31)(15,32)
******************************************************/
void  ClearPointofBuf(unsigned char x, unsigned char y)
{
    unsigned char Column, ColumnBit;
    OLED12864_Location(x, y);
    Column = Buff.y / 8;				//要清除的缓冲区的字节行坐标
    ColumnBit = Buff.y % 8;				//坐标在该字节8bit的bit位置
    DisBuff[Column][Buff.x] &= ~(0x01 << (ColumnBit));	//清除指向的缓冲区字节
    //if(ColumnBit)
    //DisBuff[Column+1][Buff.x]&=~(0xff>>(8-ColumnBit));//字节跨两行就清除下一行
}
/*********写一个字节数据到缓冲区*********************
** 功能:  写一个字节数据到缓冲区,写入字节后x会自加
** 参数:  byte:要写入的字节
** 返回值:无
******************************************************/
void  WriteBytetoBuf(unsigned char byte)
{
    unsigned char Column, ColumnBit;
    Column = Buff.y / 8;							//要写的缓冲区的字节行坐标
    ColumnBit = Buff.y % 8;							//坐标在该字节8bit的bit位置
    DisBuff[Column][Buff.x] |= byte << (ColumnBit);	//将1字节数据写入DisBuff指向的缓冲区字节

    if (ColumnBit) {
        DisBuff[Column + 1][Buff.x] |= byte >> (8 - ColumnBit);    //字节跨两行就写下一行
    }

    if (Buff.x >= LCD_X) {						//到达行尾缓冲区指针自动跳转到下一行行首
        Buff.x = 0;
        Buff.y += 8;

        if (Buff.y >= LCD_Y) {			  		//到达页面末尾,缓冲区指针跳到页面开头
            Buff.y = 0;
        }
    } else {
        Buff.x++;    //缓冲区指针指向下一列
    }
}

void  WriteRevBytetoBuf(unsigned char byte)
{
    unsigned char Column, ColumnBit, temp;
    Column = Buff.y / 8;							//要写的缓冲区的字节行坐标
    ColumnBit = Buff.y % 8;
    temp = byte << (ColumnBit);
    temp = ~temp;
    temp = temp >> (ColumnBit);							//坐标在该字节8bit的bit位置
    temp = temp << (ColumnBit);
    DisBuff[Column][Buff.x] |= temp;	             //将1字节数据写入DisBuff指向的缓冲区字节

    if (ColumnBit) {
        temp = byte >> (8 - ColumnBit);
        temp = ~temp;
        temp = temp << (8 - ColumnBit);
        temp = temp >> (8 - ColumnBit);
        DisBuff[Column + 1][Buff.x] |= temp;
    }//字节跨两行就写下一行

    if (Buff.x >= LCD_X) {						//到达行尾缓冲区指针自动跳转到下一行行首
        Buff.x = 0;
        Buff.y += 8;

        if (Buff.y >= LCD_Y) {			  		//到达页面末尾,缓冲区指针跳到页面开头
            Buff.y = 0;
        }
    } else {
        Buff.x++;    //缓冲区指针指向下一列
    }
}
/*********清空整个缓冲区*********************
** 功能:  清空整个缓冲区
** 参数:  无
** 返回值:无
******************************************************/
void  ClearAllBuf(void)
{
    unsigned char i, j;

    for (j = 0; j < LCD_COLUMN; j++) {
        for (i = 0; i < LCD_ROW; i++) {
            DisBuff[j][i] = 0x00;    //写0到缓冲区以清除该字节数据
        }
    }
}
/*********清空整个缓冲区并将缓冲区中数据全部写入*********************
** 功能:  清空整个缓冲区并送入数据
** 参数:  无
** 返回值:无
******************************************************/
void  OLED12864_Clean(void)
{
    ClearAllBuf();							//清除缓冲区
    RefreshAll();							//刷新 送入数据
}
/*********清空缓冲区指定区域*********************
** 功能:  清空缓冲区指定区域
** 参数:  x:0-127 y:0-63  Width:0-127 Height:0-63
** 返回值:无
******************************************************/
void  ClearBuf(unsigned char x, unsigned char y, unsigned char Width, unsigned char Height)
{
    unsigned char i, j;
    Height = y + Height;								//获得刷新的终止纵坐标
    Width = Width + x;									//获得刷新的终止横坐标

    for (j = y; j < Height; j++) {
        for (i = x; i < Width; i++) {
            ClearPointofBuf(i, j);    //写0到缓冲区以清除该点
        }
    }
}
/*********清空指定区域*********************
** 功能:  清空指定区域
** 参数:  x:0-127 y:0-63  Width:0-127 Height:0-63
** 返回值:无
******************************************************/
void  ClearArea(unsigned char x, unsigned char y, unsigned char Width, unsigned char Height)
{
    ClearBuf(x, y, Width, Height);
    Refresh(x, y, Width, Height);			//刷新 指定区域
}

高度为8的字符显示函数//
/*********写一个(6*8)字符到缓冲区(不需要坐标,紧跟上个字符)*********************
** 功能:  写一个(6*8)字符到缓冲区
** 参数:  value:字符
** 返回值:无
******************************************************/
void  WriteChartoBuf(unsigned char value)
{
    unsigned char i;

    for (i = 0; i < 6; i++) {						//连续写六个字节
        WriteBytetoBuf(Font6x8[value - 0x20][i]);
    }
}
/*********写一个反白(6*8)*********************
** 功能:  写一个反白(6*8)字符到缓冲区
** 参数:  value:字符
** 返回值:无
******************************************************/
void  WriteRevChartoBuf(unsigned char value)
{
    unsigned char i;

    for (i = 0; i < 6; i++) {						//连续写六个字节
        WriteBytetoBuf(~Font6x8[value - 0x20][i]);
    }
}
/*********通过缓冲区写一个ASCII字符(6*8)并刷新(不需要坐标,紧跟上个字符)*********************
** 功能:  通过缓冲区写一个ASCII字符(6*8)并刷新
** 参数:  value:字符
** 返回值:无
******************************************************/
void  OLED12864_DisChar(unsigned char x, unsigned char y, unsigned char value)
{
    OLED12864_Location(x, y);
    Start.x = Buff.x;								//获得开始横坐标
    Start.y = Buff.y;								//获得开始纵坐标
    WriteChartoBuf(value);
    Refresh(Start.x, Start.y, 6, 8);			//刷新 指定区域
}
/*********刷新显示一个独立的字符*********************
** 功能:  刷新显示一个独立的字符
** 参数:  value:字符
** 返回值:无
******************************************************/
void  OLED12864_DisOneChar(unsigned char x, unsigned char y, bit Rev, unsigned char value)
{
    OLED12864_Location(x, y);
    Start.x = Buff.x;								//获得开始横坐标
    Start.y = Buff.y;								//获得开始纵坐标
    ClearBuf(Start.x, Start.y, 6, 8);
    OLED12864_Location(x, y);

    if (Rev) {
        WriteRevChartoBuf(value);
    } else {
        WriteChartoBuf(value);
    }

    Refresh(Start.x, Start.y, 6, 8);			  //刷新 指定区域
}



/*********写一个字符串(高度8)到缓冲区(不需要坐标,紧跟上个字符)*********************
** 功能:  写一个字符串(高度8)到缓冲区
** 参数:  *str:字符串	注意:字符串长度不能超过22
** 返回值:字符串长度
******************************************************/
unsigned char  WriteStrtoBuf(char *str)
{
    unsigned char str_len = 0;

    while (*str != '\0') {
        WriteChartoBuf(*str++);
        str_len++;
    }

    return str_len;										//返回字符串长度
}
/*********写一个反白字符串(高度8)到缓冲区(不需要坐标,紧跟上个字符)*********************
** 功能:  写一个反白字符串(高度8)到缓冲区
** 参数:  *str:字符串
** 返回值:字符串长度
******************************************************/
unsigned char  WriteRevStrtoBuf(char *str)
{
    unsigned char str_len = 0;

    while (*str != '\0') {
        WriteRevChartoBuf(*str++);
        str_len++;
    }

    return str_len;										//返回字符串长度
}

/*********写一个字符串(高度8)到缓冲区并刷新(不需要坐标,紧跟上个字符)*********************
** 功能:  写一个字符串(高度8)到缓冲区并刷新
			注意刷新范围为字符串所在的行 字符串显示不超过一行 对于128*64 最多不超过21个字符
** 参数:  	x:0-127 横坐标
			y:0-63 纵坐标
			Rev:0:正常显示 1:反白显示
			*str:字符串
** 返回值:字符串长度
******************************************************/
unsigned char  OLED12864_DisStr(unsigned char x, unsigned char y, bit Rev, char *str)
{
    unsigned char Width, Height, str_len;
    OLED12864_Location(x, y);
    Start.x = Buff.x;								//获得开始横坐标
    Start.y = Buff.y;								//获得开始纵坐标

    if (Rev) {
        str_len = WriteRevStrtoBuf(str);
    } else {
        str_len = WriteStrtoBuf(str);
    }

    Height = 8;									//获得刷新高度
    Width = str_len * 6;;		                 	//获得字符串起始纵坐标
    Refresh(Start.x, Start.y, Width, Height);			//刷新显示区域
    return str_len;								//返回字符串长度
}
/*********显示反白字符串*********************
** 功能:  显示反白字符串
			注意刷新范围为字符串所在的行 字符串显示不超过一行
** 参数:  *str:字符串
** 返回值:字符串长度
******************************************************/
unsigned char  OLED12864_DisRevStr(char *str)
{
    unsigned char Width, Height, str_len;
    Start.x = Buff.x;								//获得开始横坐标
    Start.y = Buff.y;								//获得开始纵坐标
    str_len = WriteRevStrtoBuf(str);
    Height = 8;									//获得刷新高度
    Width = str_len * 6;;		                 	//获得字符串起始纵坐标
    Refresh(Start.x, Start.y, Width, Height);			//刷新显示区域
    return str_len;								//返回字符串长度
}
/*-------将一个数字转化为字符串并6*8显示------------------------------------------------
** 功能:将一个数字转化为字符串并显示
		 如果要改变显示格式 就要改变sprintf函数的命令格式符
		 如果要改变显示长度  就要改变str[]数组的长度
** 参数: temp:要显示的变量

** 返回值:无
-----------------------------------------------------------------------*/
void  OLED12864_Format1(unsigned char x, unsigned char y, bit Rev, unsigned int temp)
{
    char str[5];
    unsigned char L;
    //sprintf(str,"%d",temp);  //产生“123″
    L = sprintf(str, "%04d", temp); //产生“0123″
    ClearBuf(x, y, L * 6, 8);
    OLED12864_DisStr(x, y, Rev, str);
}
void  OLED12864_Format2(unsigned char x, unsigned char y, bit Rev, unsigned int temp)
{
    char str[5];
    unsigned char L;
    L = sprintf(str, "%02x", temp); //产生“01″
    ClearBuf(x, y, L * 6, 8);
    OLED12864_DisStr(x, y, Rev, str);
}
void  OLED12864_Format3(unsigned char x, unsigned char y, bit Rev, unsigned int temp)
{
    char str[4];
    unsigned char L;
    //sprintf(str,"%d",temp);  //产生“123″
    L = sprintf(str, "%02d", temp); //产生“001″
    ClearBuf(x, y, L * 6, 8);
    OLED12864_DisStr(x, y, Rev, str);
}
void OLED12864_Format4(unsigned char x, unsigned char y, bit Rev, unsigned int temp)
{
    char str[10];
    unsigned char L;
    //sprintf(str,"%d",temp);  //产生“123″
    L = sprintf(str, "%04dmA", temp); //产生“1.22A″
    ClearBuf(x, y, L * 6, 8);
    OLED12864_DisStr(x, y, Rev, str);
}
void OLED12864_Format5(unsigned char x, unsigned char y, bit Rev, float temp)
{
    char str[10];
    unsigned char L;
    //sprintf(str,"%d",temp);  //产生“123″
    L = sprintf(str, "%4.2fV", temp); //产生“1.22V″
    ClearBuf(x, y, L * 6, 8);
    OLED12864_DisStr(x, y, Rev, str);
}
void  OLED12864_Format6(unsigned char x, unsigned char y, bit Rev, unsigned int temp)
{
    char str[5];
    unsigned char L;
    //sprintf(str,"%d",temp);  //产生“123″
    L = sprintf(str, "%03d", temp); //产生“0123″
    ClearBuf(x, y, L * 6, 8);
    OLED12864_DisStr(x, y, Rev, str);
}
高度为12的字符显示函数///
/*-------写一个ASCII字符(8*12)到缓冲区------------------------------------------------
** 功能:写一个ASCII字符(8*12)到缓冲区
** 参数: value:要显示的数字ASCII
** 返回值:无
-----------------------------------------------------------------------*/
void  WriteBigChar(unsigned char value)
{
    unsigned char i;

    for (i = 0; i < 8; i++) { //写上面一行
        WriteBytetoBuf(Font8x12[value - 0x20][i]);
    }

    Buff.x = Buff.x - 8; //写下面一行
    Buff.y = Buff.y + 8;

    for (i = 8; i < 16; i++) {
        WriteBytetoBuf(Font8x12[value - 0x20][i]);
    }
}
/*-------写一个反白ASCII字符(8*12)到缓冲区------------------------------------------------
** 功能:写一个反白ASCII字符(8*12)到缓冲区
** 参数: value:要显示的数字ASCII
** 返回值:无
-----------------------------------------------------------------------*/
void  WriteBigRevChar(unsigned char value)
{
    unsigned char i;

    for (i = 0; i < 8; i++) { //写上面一行
        WriteBytetoBuf(~Font8x12[value - 0x20][i]);
    }

    Buff.x = Buff.x - 8; //写下面一行
    Buff.y = Buff.y + 8;

    for (i = 8; i < 16; i++) {
        WriteBytetoBuf((~(Font8x12[value - 0x20][i] << 4)) >> 4);    //只反白低4位
    }
}

/*********显示一个8*12字符*********************
** 功能:  刷新显示一个8*12字符串
** 参数:  value:字符ASCIi Rev:是否反白
** 返回值:无
******************************************************/
void  OLED12864_DisBigChar(unsigned char value, bit Rev)
{
    Start.x = Buff.x;							//获得开始横坐标
    Start.y = Buff.y;							//获得开始纵坐标

    if (Rev) {
        WriteBigRevChar(value);
    } else {
        WriteBigChar(value);
    }

    Refresh(Start.x, Start.y, 8, 12);			//刷新 指定区域
}

/*********写一个8*12字符串到缓冲区*********************
** 功能:  写一个8*12字符串到缓冲区	,每行最多显示128/8个字符
** 参数:  *str:字符串	Rev:1:反白 0:不反白
** 返回值:字符串长度
******************************************************/
unsigned char  WriteBigStr(char *str, bit Rev)
{
    unsigned char str_len = 0;

    while (*str != '\0') {
        if (Rev) {
            WriteBigRevChar(*str++);
        } else {
            WriteBigChar(*str++);
        }

        Buff.y = Buff.y - 8;
        str_len++;
    }

    return str_len;										//返回字符串长度
}
void  Write6BigStr(char *str, bit Rev)
{
    unsigned char i = 0;

    for (i = 0; i < 6; i++) {
        if (Rev) {
            WriteBigRevChar(*str++);
        } else {
            WriteBigChar(*str++);
        }

        Buff.y = Buff.y - 8;
    }
}										//返回字符串长度

/*********刷新显示一个独立8*12的字符*********************
** 功能:  刷新显示一个独立的字符
** 参数:  value:字符
** 返回值:无
******************************************************/
void  OLED12864_DisOneBigChar(unsigned char x, unsigned char y, bit Rev, unsigned char value)
{
    OLED12864_Location(x, y);
    Start.x = Buff.x;								//获得开始横坐标
    Start.y = Buff.y;								//获得开始纵坐标
    ClearBuf(Start.x, Start.y, 8, 12);
    OLED12864_Location(x, y);

    if (Rev) {
        WriteBigRevChar(value);
    } else {
        WriteBigChar(value);
    }

    Refresh(Start.x, Start.y, 8, 12);			  //刷新 指定区域
}
/*********写一个字符串(高度12)到缓冲区并刷新(不需要坐标,紧跟上个字符)*********************
** 功能:  写一个字符串(高度12)到缓冲区并刷新
			注意刷新范围为字符串所在的行 字符串显示不超过一行 对于128*64 最多不超过21个字符
** 参数:  	x:0-127 横坐标
			y:0-63 纵坐标
			Rev:0:正常显示 1:反白显示
			*str:字符串
** 返回值:字符串长度
******************************************************/
unsigned char  OLED12864_DisBigStr(unsigned char x, unsigned char y, bit Rev, char *str)
{
    unsigned char Width, Height, str_len;
    OLED12864_Location(x, y);
    Start.x = Buff.x;								//获得开始横坐标
    Start.y = Buff.y;

    if (Rev) {									//获得开始纵坐标
        str_len = WriteBigStr(str, 1);
    } else {
        str_len = WriteBigStr(str, 0);
    }

    Height = 12;									//获得刷新高度
    Width = str_len * 8;;		                 	//获得字符串起始纵坐标
    Refresh(Start.x, Start.y, Width, Height);			//刷新显示区域
    return str_len;								//返回字符串长度
}

void  OLED12864_DisBigStr1(unsigned char x, unsigned char y, bit Rev, char *str)
{
    unsigned char Width, Height;
    OLED12864_Location(x, y);
    Start.x = Buff.x;									//获得开始横坐标
    Start.y = Buff.y;

    if (Rev) {										//获得开始纵坐标
        Write6BigStr(str, 1);
    } else {
        Write6BigStr(str, 0);
    }

    Height = 12;										//获得刷新高度
    Width = 6 * 8;;		                 				//获得字符串起始纵坐标
    Refresh(Start.x, Start.y, Width, Height);			//刷新显示区域								//返回字符串长度
}
/*-------将一个变量转化为字符串并8*12显示------------------------------------------------
** 功能:将一个数字转化为字符串并显示
		 如果要改变显示格式 就要改变sprintf函数的命令格式符
		 如果要改变显示长度  就要改变str[]数组的长度
** 参数: temp:要显示的变量

** 返回值:无
-----------------------------------------------------------------------*/
void  OLED12864_BigFormat1(unsigned char x, unsigned char y, bit Rev, unsigned int temp)
{
    char str[5];
    unsigned char L;
    //L=sprintf(str,"%d",temp);  //产生“1″
    L = sprintf(str, "%04d", temp); //产生“0123″
    ClearBuf(x, y, L * 8, 12);
    OLED12864_DisBigStr(x, y, Rev, str);
}
void  OLED12864_BigFormat2(unsigned char x, unsigned char y, bit Rev, unsigned int temp)
{
    char str[5];
    unsigned char L;
    //L=sprintf(str,"%d",temp);  //产生“1″
    L = sprintf(str, "%02d", temp); //产生“01″
    ClearBuf(x, y, L * 8, 12);
    OLED12864_DisBigStr(x, y, Rev, str);
}
void  OLED12864_BigFormat3(unsigned char x, unsigned char y, bit Rev, float temp)
{
    char str[7];
    unsigned char L;
    //L=sprintf(str,"%d",temp);  //产生“1″
    L = sprintf(str, "%04.1f", temp); //产生“01.00v″
    ClearBuf(x, y, L * 8, 12);
    OLED12864_DisBigStr(x, y, Rev, str);
}
void  OLED12864_BigFormat4(unsigned char x, unsigned char y, bit Rev, float temp)
{
    char str[10];
    unsigned char L;
    //L=sprintf(str,"%d",temp);  //产生“1″
    L = sprintf(str, "%4.2fV", temp); //产生“01.1V″
    ClearBuf(x, y, L * 8, 12);
    OLED12864_DisBigStr(x, y, Rev, str);
}
void  OLED12864_BigFormat5(unsigned char x, unsigned char y, bit Rev, unsigned int temp)
{
    char str[10];
    unsigned char L;
    //L=sprintf(str,"%d",temp);  //产生“1″
    L = sprintf(str, "%3d%", temp); //产生“100%″
    ClearBuf(x, y, L * 8, 12);
    OLED12864_DisBigStr(x, y, Rev, str);
}
/*********写一个12X12汉字到缓冲区*********************
** 功能: 写一个12X12汉字符到缓冲区
** 参数:  x:0-127 y:0-63 Hanzi[]:存放汉字数组 单行24个数值  Rev:反白 1:反白 0:正常显示
** 返回值:无
******************************************************/
void  WriteBigCh(unsigned char x, unsigned char y, const unsigned char Hanzi[], bit Rev)
{
    unsigned char i;
    OLED12864_Location(x, y); //写上面一行

    for (i = 0; i < 12; i ++) {
        if (Rev) {
            WriteBytetoBuf(~Hanzi[i]);
        } else {
            WriteBytetoBuf(Hanzi[i]);
        }
    }

    OLED12864_Location(x, y + 8); //写下面一行

    for (i = 12; i < 24; i++) {
        if (Rev) {
            WriteBytetoBuf((~(Hanzi[i] << 4)) >> 4);
        } else {
            WriteBytetoBuf(Hanzi[i]);
        }
    }
}
/*********显示一个12X12汉字*********************
** 功能: 显示一个12X12汉字
** 参数:  x:0-83 y:0-48 Hanzi[]:存放汉字数组 Rev:反白
** 返回值:无
******************************************************/
void  OLED12864_DisBigCh(unsigned char x, unsigned char y, const unsigned char Hanzi[], bit Rev)
{
    WriteBigCh(x, y, Hanzi, Rev);
    Refresh(x, y, 12, 12);
}

/*********显示一个12X12汉字字符串*********************
** 功能: 显示一个12X12汉字字符串
** 参数:  x:0-127 y:0-63 Hanzi[][]:存放汉字数组 num:1-10 汉字个数 Rev:反白
** 返回值:无
******************************************************/
void  OLED12864_DisBigChs(unsigned char x, unsigned char y, bit Rev, unsigned char num, const unsigned char Hanzi[][24])
{
    unsigned char i;
    OLED12864_Location(x, y); //写上面一行

    for (i = 0; i < num; i++) {
        WriteBigCh(x + i * 12, y, Hanzi[i], Rev);
    }

    Refresh(x, y, 12 * num, 12);
}
/高度为16的字符显示函数
/*-------写一个ASCII字符(8*16)到缓冲区------------------------------------------------
** 功能:写一个ASCII字符(8*16)到缓冲区
** 参数: temp:要显示的数字ASCII
** 返回值:无
-----------------------------------------------------------------------*/
void  WriteLargeChar(unsigned char value)
{
    unsigned char i;

    for (i = 0; i < 8; i++) { //写上面一行
        WriteBytetoBuf(Font8x16[value - 0x20][i]);
    }

    Buff.x = Buff.x - 8; //写下面一行
    Buff.y = Buff.y + 8;

    for (i = 8; i < 16; i++) {
        WriteBytetoBuf(Font8x16[value - 0x20][i]);
    }
}
/*-------写一个反白ASCII字符(8*16)到缓冲区------------------------------------------------
** 功能:写一个反白ASCII字符(8*16)到缓冲区
** 参数: temp:要显示的数字ASCII
** 返回值:无
-----------------------------------------------------------------------*/
void  WriteLargeRevChar(unsigned char value)
{
    unsigned char i;

    for (i = 0; i < 8; i++) { //写上面一行
        WriteBytetoBuf(~Font8x16[value - 0x20][i]);
    }

    Buff.x = Buff.x - 8; //写下面一行
    Buff.y = Buff.y + 8;

    for (i = 8; i < 16; i++) {
        WriteBytetoBuf(~Font8x16[value - 0x20][i]);
    }
}

/*********显示一个8*16字符串*********************
** 功能:  刷新显示一个8*16字符串
** 参数:  value:字符ASCIi Rev:是否反白
** 返回值:无
******************************************************/
void  OLED12864_DisLargeChar(unsigned char value, bit Rev)
{
    Start.x = Buff.x;							//获得开始横坐标
    Start.y = Buff.y;							//获得开始纵坐标

    if (Rev) {
        WriteLargeRevChar(value);
    } else {
        WriteLargeChar(value);
    }

    Refresh(Start.x, Start.y, 8, 16);			//刷新 指定区域
}
/*********写一个8X16字符串到缓冲区*********************
** 功能:  写一个8X16字符串到缓冲区	,每行最多显示8个字符
** 参数:  *str:字符串	Rev:反白 0:不反白
** 返回值:字符串长度
******************************************************/
unsigned char  WriteLargeStr(char *str, bit Rev)
{
    unsigned char str_len = 0;

    while (*str != '\0') {
        if (Rev) {
            WriteLargeRevChar(*str++);
        } else {
            WriteLargeChar(*str++);
        }

        Buff.y = Buff.y - 8;
        str_len++;
    }

    return str_len;										//返回字符串长度
}

/*********显示一个8X16字符串*********************
** 功能:  显示一个8X16字符串
** 参数:  *str:字符串	Rev:反白 0:不反白
** 返回值:字符串长度
******************************************************/
unsigned char  OLED12864_DisLargeStr(unsigned char x, unsigned char y, bit Rev, char *str)
{
    unsigned char Width, Height, str_len;
    OLED12864_Location(x, y);
    Start.x = Buff.x;								//获得开始横坐标
    Start.y = Buff.y;

    if (Rev) {							//获得开始纵坐标
        str_len = WriteLargeStr(str, 1);
    } else {
        str_len = WriteLargeStr(str, 0);
    }

    Height = 16;									//获得刷新高度
    Width = str_len * 8;;		                 	//获得字符串起始纵坐标
    Refresh(Start.x, Start.y, Width, Height);			//刷新显示区域
    return str_len;								//返回字符串长度
}
/*-------将一个变量转化为字符串并8*16显示------------------------------------------------
** 功能:将一个数字转化为字符串并显示
		 如果要改变显示格式 就要改变sprintf函数的命令格式符
		 如果要改变显示长度  就要改变str[]数组的长度
** 参数: temp:要显示的变量

** 返回值:无
-----------------------------------------------------------------------*/
void  OLED12864_LargeFormat1(unsigned char x, unsigned char y, bit Rev, unsigned int temp)
{
    char str[5];
    unsigned char L;
    //L=sprintf(str,"%d",temp);  //产生“123″
    L = sprintf(str, "%04d", temp); //产生“01″
    ClearBuf(x, y, L * 8, 16);
    OLED12864_DisLargeStr(x, y, Rev, str);
}
void  OLED12864_LargeFormat2(unsigned char x, unsigned char y, bit Rev, float temp)
{
    char str[10];
    unsigned char L;
    //L=sprintf(str,"%d",temp);  //产生“123″
    L = sprintf(str, "%05.2fV", temp); //产生“01″
    ClearBuf(x, y, L * 8, 16);
    OLED12864_DisLargeStr(x, y, Rev, str);
}


/*********写一个16X16汉字到缓冲区*********************
** 功能: 写一个16X16汉字符到缓冲区
** 参数:  x:0-127 y:0-63 Hanzi[]:存放汉字数组 Rev:反白
** 返回值:无
******************************************************/
void  WriteLargeCh(unsigned char x, unsigned char y, const unsigned char Hanzi[], bit Rev)
{
    unsigned char i;
    OLED12864_Location(x, y); //写上面一行

    for (i = 0; i < 16; i ++) {
        if (Rev) {
            WriteBytetoBuf(~Hanzi[i]);
        } else {
            WriteBytetoBuf(Hanzi[i]);
        }
    }

    OLED12864_Location(x, y + 8); //写下面一行

    for (i = 16; i < 32; i++) {
        if (Rev) {
            WriteBytetoBuf(~Hanzi[i]);
        } else {
            WriteBytetoBuf(Hanzi[i]);
        }
    }
}
/*********显示一个16X16汉字*********************
** 功能: 显示一个16X16汉字
** 参数:  x:0-127 y:0-63 Hanzi[]:存放汉字数组 Rev:反白
** 返回值:无
******************************************************/
void  OLED12864_DisLargeCh(unsigned char x, unsigned char y, const unsigned char Hanzi[], bit Rev)
{
    WriteLargeCh(x, y, Hanzi, Rev);
    Refresh(x, y, 16, 16);
}

/*********显示一个16X16汉字字符串*********************
** 功能: 显示一个16X16汉字字符串
** 参数:  x:0-127 y:0-63 Hanzi[][]:存放汉字数组 num:1-6 汉字个数 Rev:反白
** 返回值:无
******************************************************/
void  OLED12864_DisLargeChStr(unsigned char x, unsigned char y, bit Rev, unsigned char num,
                              const unsigned char Hanzi[][32])
{
    unsigned char i;
    OLED12864_Location(x, y); //写上面一行

    for (i = 0; i < num; i++) {
        WriteLargeCh(x + i * 16, y, Hanzi[i], Rev);
    }

    Refresh(x, y, 16 * num, 16);
}
///以下为画点,线,折线,矩形,圆,bmp图片函数
/*********获得一个有符号字符型数的绝对值*********************
** 功能:  获得一个有符号字符型数的绝对值
** 参数:  无
** 返回值:绝对值
******************************************************/
unsigned char  CharAbs(char a)
{
    if ((unsigned char)a >= 128) {
        return 256 - a;
    } else {
        return a;
    }
}
/*********交换两个无符号字符型变量位置*********************
** 功能:  交换两个无符号字符型变量位置
** 参数:  *a *b
** 返回值:绝对值
******************************************************/
void  Swap(unsigned char *a, unsigned char *b)
{
    unsigned char temp;
    temp = *a;
    *a = *b;
    *b = temp;
}
/*********画一个点到缓冲区*********************
** 功能: 画一个点到缓冲区
** 参数:  x:0-127 y:0-63
** 返回值:无
******************************************************/
void  WritePoint(unsigned char x, unsigned char y)
{
    unsigned char row, line;
    row = y / 8; //获得y方向起始坐标
    line = y % 8;
    DisBuff[row][x] |= 0x01 << (line);
}
/*********显示一个点*********************
** 功能: 显示一个点
** 参数:  x:0-127 y:0-63
** 返回值:无
******************************************************/
void  DisPoint(unsigned char x, unsigned char y)
{
    WritePoint(x, y);
    Refresh(x, y, 1, 8);
}
/*********获得最小值*********************
** 功能: 获得最小值
** 参数:  a b
** 返回值:a,b中的最小值
******************************************************/
unsigned char  Min(unsigned char a, unsigned char b)
{
    return a <= b ? a : b;
}
/*********获得划线单位增加的大小*********************
** 功能: 获得划线单位增加的大小
** 参数:  x1 x2
** 返回值:
******************************************************/
short  GetUnitAdd(unsigned char x1, unsigned char x2)
{
    if (x1 == x2) {
        return 0;
    } else if (x2 > x1) {
        return 1;
    } else {
        return (-1);
    }
}
/*********填充缓冲区指定区域*********************
** 功能:  清空缓冲区指定区域
** 参数:  x:0-127 y:0-63  Width:0-127 Height:0-63
** 返回值:无
******************************************************/
static void  FillBuf(unsigned char x, unsigned char y, unsigned char Width, unsigned char Height)
{
    unsigned char i, j;
    Height = y + Height;								//获得刷新的终止纵坐标
    Width = Width + x;									//获得刷新的终止横坐标

    for (j = y; j < Height; j++) {
        for (i = x; i < Width; i++) {
            WritePoint(i, j);    //写1到缓冲区以清除该点
        }
    }
}
/*********画一条线到缓冲区*********************
** 功能: 画一条线到缓冲区
** 参数:  x1:0-127 起始点横坐标
		   y1:0-63 起始点纵坐标
		   x2:0-127 起始点横坐标
		   y2:0-63 起始点纵坐标
** 返回值:无
******************************************************/
void  WriteLine(unsigned char x1, unsigned char y1, unsigned char x2, unsigned char y2)
{
    unsigned char delt_x, delt_y, flag, i;
    short add_x, add_y, error;
    delt_x = CharAbs(x2 - x1);		//计算Δx 取绝对值
    delt_y = CharAbs(y2 - y1);		//计算Δy

    if (delt_y > delt_x) {	//如果斜率大于1,置位flag,交换delt_x和delt_y
        flag = 1;
        Swap(&delt_x, &delt_y);
    } else {
        flag = 0;
    }

    add_x = GetUnitAdd(x1, x2);	//x轴单位增量(-1,0或1)
    add_y = GetUnitAdd(y1, y2);	//y轴单位增量(-1,0或1)
    error = delt_y << 1 -
            delt_x;		//算法改进后由error=delt_y/delt_x-0.5;变为error=2*delt_x*(delt_y/delt_x-0.5)=2*delt_y-delt_x;

    for (i = 0; i <= delt_x; i++) {	//横向扫描
        WritePoint(x1, y1);		//画点

        if (error >= 0) {		//进行error>0时的增量计算
            if (flag) {		//斜率大于1的话,x轴进行增量计算
                x1 += add_x;
            } else {
                y1 += add_y;    //斜率小于1的话,y轴进行增量计算
            }

            error -= (delt_x << 1);
        }

        if (flag) {			//进行基本增良扑憧,即每次循环都会进行的增量计算//斜率大于1的话,y轴进行增量计算
            y1 += add_y;
        } else {				//斜率小于1的话,x轴进行增量计算
            x1 += add_x;
        }

        error += (delt_y << 1);
    }
}
/*********显示一条线*********************
** 功能: 显示一条线
** 参数:  x1:0-127 起始点横坐标
		   y1:0-63 起始点纵坐标
		   x2:0-127 起始点横坐标
		   y2:0-63 起始点纵坐标
** 返回值:无
******************************************************/
void  OLED12864_DisLine(unsigned char x1, unsigned char y1, unsigned char x2, unsigned char y2)
{
    unsigned char x, y, width, height;
    width = CharAbs(x2 - x1) + 1;		//获得刷新宽度
    height = CharAbs(y2 - y1) + 1;	//获得刷新高度
    x = Min(x1, x2);				//获得刷新起始横坐标
    y = Min(y1, y2);				//获得刷新起始纵坐标
    WriteLine(x1, y1, x2, y2);
    Refresh(x, y, width, height);
}
/*********显示折线*********************
** 功能: 显示折线
** 参数:  *p:节点结构体数组
		  例如:PolyLine
		  code Point PolyLine[3]=
			{
				{0,0},
				{20,30},
				{40,30},
			};
		   num:节点个数
** 返回值:无
******************************************************/
void  OLED12864_DisPolyline(const Point *p, unsigned char num)
{
    unsigned char i;

    for (i = 0; i < num; i++) {
        WriteLine(p[i].x, p[i].y, p[i + 1].x, p[i + 1].y);
    }

    RefreshAll();//全部刷新显示
}



/*********画矩形*********************
** 功能: 画矩形
** 参数:  x1:0-127 矩形左上角横坐标
		   y1:0-63 矩形左上角纵坐标
		   width:0-127 矩形宽度
		   height:0-63 矩形高度
** 返回值:无
******************************************************/
void  OLED12864_DisSqu(unsigned char x, unsigned char y, unsigned char width, unsigned char height, bit Rev)
{
    if (x > 127) {
        x = 127;
    }

    if (y > 63) {
        y = 63;
    }

    if (Rev) {
        FillBuf(x, y, width, height);    //范围内的空间全部显示
    }

    WriteLine(x, y, x + width, y);
    WriteLine(x + width, y, x + width, y + height);
    WriteLine(x, y, x, y + height);
    WriteLine(x, y + height, x + width, y + height);
    Refresh(x, y, width + 1, height + 1);
}

/*********画圆*********************
** 功能: 画圆
** 参数:  center_x:0-127 圆心横坐标
		   center_y:0-64 圆心左上角纵坐标
		   r:0-32 半径
** 返回值:无
******************************************************/
void  OLED12864_DisCircle(unsigned char center_x, unsigned char center_y, unsigned char r)
{
    unsigned char x = 0, y = r;
    short d = 5 - (r << 2);		//判别式初值4*(1.25-r)

    while (x <= y) {
        //将圆分成对称8份分别画
        WritePoint(center_x + x, center_y + y);
        WritePoint(center_x + x, center_y - y);
        WritePoint(center_x - x, center_y + y);
        WritePoint(center_x - x, center_y - y);
        WritePoint(center_x + y, center_y + x);
        WritePoint(center_x + y, center_y - x);
        WritePoint(center_x - y, center_y + x);
        WritePoint(center_x - y, center_y - x);
        x++;

        if (d < 0) {	//如果d<0判别式d=d+4*(2*x+3)
            d += (x << 3) + 12;
        } else {		//如果d>=0判别式d=d+4*(2*(x-y)+5)
            y--;
            d += (x << 3) - (y << 3) + 20;
        }
    }

    Refresh(center_x - r, center_y - r, (r << 1) + 1, (r << 1) + 1);
}

/*********画Bmp图像*********************
** 功能: 画Bmp图像
** 参数:  x:0-127 图像左上角横坐标
		   y:0-63 图像左上角纵坐标
		   width:0-128 图像宽度
		   height:0-64 图像高度
		   Rev:   是否反白 0:正常 1:反白
		   bitmap[]:存放图像的数组
** 返回值:无
******************************************************/
/*void OLED12864_DisBmp(unsigned char x,unsigned char y,unsigned char width,unsigned char height,bit Rev,char bitmap[])
{
	unsigned int p=0;
	unsigned char HeightMax;
	unsigned char i,j;
	HeightMax=(height-1)/8+1;
	for(j=0;j<HeightMax;j++)//
	{
		OLED12864_Location(x,y+j*8);
		for(i=0;i<width;i++)//
		{
			if(Rev)
			WriteRevBytetoBuf(bitmap[p++]);
			else
			WriteBytetoBuf(bitmap[p++]);
		}
	}
	Refresh(x,y,width,height);
}*/
void  OLED12864_DisBmp(unsigned char x, unsigned char y, unsigned char width, unsigned char height, bit Rev,
                       const unsigned char bitmap[])
{
    unsigned int p = 0;
    unsigned char HeightMax;
    unsigned char i, j, combit, temp = 0xff;
    HeightMax = (height - 1) / 8 + 1;
    combit = height % 8; //有效数据位
    temp <<= combit;

    if (Rev) {
        if (combit) {
            for (j = 0; j < HeightMax - 1; j++) { //
                OLED12864_Location(x, y + j * 8);

                for (i = 0; i < width; i++) { //
                    WriteRevBytetoBuf(bitmap[p++]);
                }
            }

            OLED12864_Location(x, y + (HeightMax - 1) * 8);

            for (i = 0; i < width; i++) { //
                WriteRevBytetoBuf(bitmap[p++] | temp);
            }
        } else {							 //8的整数倍行
            for (j = 0; j < HeightMax; j++) { //
                OLED12864_Location(x, y + j * 8);

                for (i = 0; i < width; i++) { //
                    WriteRevBytetoBuf(bitmap[p++]);
                }
            }
        }
    } else {
        for (j = 0; j < HeightMax; j++) { //
            OLED12864_Location(x, y + j * 8);

            for (i = 0; i < width; i++) { //
                WriteBytetoBuf(bitmap[p++]);
            }
        }
    }

    Refresh(x, y, width, height);
}

/*********画幂函数图像*********************
** 功能: 画B幂函数图像
** 参数:  x:0-127 图像左上角横坐标
		   y:0-63 图像左上角纵坐标
		   width:0-128 图像宽度
		   height:0-64 图像高度
		   MAxX:x方向的最大值 y方向最大值=x
		   Rev:   正向负向显示 0:负向 1:正向
		   index:幂指数
** 返回值:无
******************************************************/
void  OLED12864_DisPower(unsigned char x, unsigned char y, unsigned char width, unsigned char height, int MaxX, bit Rev,
                         float index)
{
    unsigned char i;
    unsigned int DeltaX, DeltaY, LocationY;
    DeltaX = MaxX / width;
    DeltaY = MaxX / height;

    if (Rev) {
        for (i = 0; i < width; i++) {
            LocationY = pow(((float)i * DeltaX / 512), index) * 512; //获取Y方向的坐标
            LocationY = LocationY / DeltaY;

            if (LocationY > height) {
                LocationY = height;
            }

            WritePoint(x + i, y - LocationY);
        }

        Refresh(x, y - height, width, height);
    } else {
        for (i = 0; i < width; i++) {
            LocationY = pow(((float)i * DeltaX / 512), index) * 512; //获取Y方向的坐标
            LocationY = LocationY / DeltaY;

            if (LocationY > height) {
                LocationY = height;
            }

            WritePoint(x - i, y + LocationY);
        }

        Refresh(x - width, y, width, height);
    }
}
/*********画油门函数图像*********************
** 功能: 画B幂函数图像
** 参数:  x:0-127 图像左上角横坐标
		   y:0-63 图像左上角纵坐标
		   width:0-128 图像宽度
		   height:0-64 图像高度
		   MAxX:x方向的最大值 y方向最大值=x
		   Rev: 正向负向显示 0:负向 1:正向
		   index:幂指数
		   kndex:比例指数
** 返回值:无
******************************************************/
void OLED12864_DisThro(unsigned char x, unsigned char y, unsigned char width, unsigned char height, int MaxX,
                       float index, float kndex)
{
    unsigned char i;
    unsigned int DeltaX, DeltaY, LocationY;
    DeltaX = MaxX / width;
    DeltaY = MaxX / height;

    for (i = 0; i < width; i++) {
        LocationY = 1024 * kndex * pow(((float)i * DeltaX / 1024), index) + (1 - kndex) * 512;
        LocationY = LocationY / DeltaY;

        if (LocationY > height) {
            LocationY = height;
        }

        WritePoint(x + i, y - LocationY);
    }

    Refresh(x, y - height, width, height);
}

效果如下所示

10、定时器测试

通过查看数据手册,可以看到这个mcu的定时器如下所示
在这里插入图片描述
现在先来看一下基本定时器的测试,基本定时器也就是6和7,只能做一些基本的定时,这个可以用来一些系统的时钟源,下面直接贴配置的代码

void TMR6_Config(unsigned int TimerLen)
{
	TMR_TimeBase_T TMR_BaseConfigStruct;
    RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR6);			//开启定时器时钟

//    TMR_BaseConfigStruct.clockDivision = TMR_CLOCK_DIV_1;
    TMR_BaseConfigStruct.counterMode = TMR_COUNTER_MODE_UP;		//向上计数模式
    TMR_BaseConfigStruct.clockDivision = 48-1;				    //48 计数频率为48M/48=1M 1s/1M=1us
//    TMR_BaseConfigStruct.period = 50000;						//定时器重装载数值
//    TMR_BaseConfigStruct.repetitionCounter = 0;
    TMR_ConfigTimeBase(TMR6, &TMR_BaseConfigStruct);			//定时器初始化

    TMR6->AUTORLD=TimerLen;
    TMR_EnableInterrupt(TMR6, TMR_INT_UPDATE);					//开启中断
    TMR_Enable(TMR6);											//使能中断
    NVIC_EnableIRQRequest(TMR6_IRQn, 3);
}

上面代码开了一个基本定时器的更新中断,使用这个中断可以

在这里插入图片描述
就可以周期性的做一些事情了

11、电机驱动pwm测试

pwm是电机驱动里面很常见的东西了,直接贴代码吧,和stm32的比较像

void APM_MINI_TMR1_PWMOutPut_Init(void)
{
    TMR_TimeBase_T  timeBaseConfig;
    TMR_OCConfig_T  occonfig;
    GPIO_Config_T   gpioconfig;

    /* Enable Clock*/
    RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOA);
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SYSCFG);
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_TMR1);

    /* Connect TMR1 to CH1 */
    GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_8, GPIO_AF_PIN2);
    gpioconfig.mode  = GPIO_MODE_AF;
    gpioconfig.outtype = GPIO_OUT_TYPE_PP;
    gpioconfig.pin   = GPIO_PIN_8;
    gpioconfig.pupd  = GPIO_PUPD_NO;
    gpioconfig.speed = GPIO_SPEED_50MHz;
    GPIO_Config(GPIOA, &gpioconfig);

    /* Set clockDivision = 1 */
    timeBaseConfig.clockDivision = TMR_CKD_DIV1;
    /* Up-counter */
    timeBaseConfig.counterMode = TMR_COUNTER_MODE_UP;
    /* Set divider = 47 .So TMR1 clock freq ~= 48/(47+1) = 1MHZ */
    timeBaseConfig.div = 47 ;
    /* Set counter = 1000 */
    timeBaseConfig.period = 1000;
    /* Repetition counter = 0x0 */
    timeBaseConfig.repetitionCounter = 0;
    TMR_ConfigTimeBase(TMR1, &timeBaseConfig);

    /* PWM1 mode */
    occonfig.OC_Mode = TMR_OC_MODE_PWM1;
    /* Idle State is reset */
    occonfig.OC_Idlestate = TMR_OCIDLESTATE_RESET;
    /* NIdle State is reset */
    occonfig.OC_NIdlestate = TMR_OCNIDLESTATE_RESET;
    /* Enable CH1N ouput */
    occonfig.OC_OutputNState = TMR_OUTPUT_NSTATE_DISABLE;
    /* Enable CH1 ouput */
    occonfig.OC_OutputState = TMR_OUTPUT_STATE_ENABLE;
    /* CH1  polarity is high */
    occonfig.OC_Polarity = TMR_OC_POLARITY_HIGH;
    /* CH1N polarity is high */
    occonfig.OC_NPolarity = TMR_OC_NPOLARITY_HIGH;
    /* Set compare value */
    occonfig.Pulse = 500;
    TMR_OC1Config(TMR1, &occonfig);

    /* Enable PWM output */
    TMR_EnablePWMOutputs(TMR1);
    /* Enable TMR1  */
    TMR_Enable(TMR1);
}

之后我们用这个函数来驱动,其实就是直接设置这个比较值
在这里插入图片描述
可以用逻辑分析仪来抓一下数据
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/538227.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

力扣题目 19:删除链表的倒数第N个节点 【python】

&#x1f464;作者介绍&#xff1a;10年大厂数据\经营分析经验&#xff0c;现任大厂数据部门负责人。 会一些的技术&#xff1a;数据分析、算法、SQL、大数据相关、python 欢迎加入社区&#xff1a; 码上找工作http://t.csdnimg.cn/Q59WX 作者专栏每日更新&#xff1a; LeetCod…

Qt-绘制多边形、椭圆、多条直线

1、说明 所有的绘图操作是在绘图事件中进行。mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACEclass MainWindow : public QMainWindow {Q_OBJECTpublic:MainWi…

C++ 类和对象(一)

目录 0.前言 1.面向过程&面向对象 1.1面向过程编程&#xff08;PP&#xff09; 1.2面向对象编程&#xff08;OOP&#xff09; 1.3从C到C 2.类的引入 2.1C语言中的结构体 2.2C中类的引入 2.3结构体与类的区别 2.4为什么引入类 3.类的定义 3.1声明与定义不分离 …

【Java探索之旅】从输入输出到猜数字游戏

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; Java编程秘籍 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一、输入输出1.1 输出到控制台1.2 从键盘输入 二、猜数字游戏2.1 所需知识&#xff1a…

【动态规划】【01背包】Leetcode 1049. 最后一块石头的重量 II

【动态规划】【01背包】Leetcode 1049. 最后一块石头的重量 II 解法 ---------------&#x1f388;&#x1f388;题目链接&#x1f388;&#x1f388;------------------- 解法 &#x1f612;: 我的代码实现> 动规五部曲 ✒️确定dp数组以及下标的含义 dp[j]表示容量为…

Learn SRP 01

学习链接&#xff1a;Custom Render Pipeline (catlikecoding.com) 使用Unity版本&#xff1a;Unity 2022.3.5f1 1.A new Render Pipeline 1.1Project Setup 创建一个默认的3D项目&#xff0c;项目打开后可以到默认的包管理器删掉所有不需要的包&#xff0c;我们只使用Unit…

陆面、生态、水文模拟与多源遥感数据同化

原文链接&#xff1a;陆面、生态、水文模拟与多源遥感数据同化https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247601198&idx6&sn51b9b26b75c9df1f11dcb9a187878261&chksmfa820dc9cdf584df9ac3b997c767d63fef263d79d30238a6523db94f68aec621e1f91df85f6…

算法——字符串

T04BF &#x1f44b;热门专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 小比特 大梦想 此篇文章与大家分享字符串相关算法 如果有不足的或者错误的请您指出! 目录 1.最长公共前缀1.1解析1.2题解 2.最长回文子串2.1解析2.2题解 3.二级制求和3.1解析3.2题解 4.字符串相乘4.1解析4.2…

【环境变量】常见的环境变量 | 相关指令 | 环境变量系统程序的结合理解 | 环境变量表 | 本地变量环境变量 | 外部命令内建命令

目录 常见的环境变量 HOME PWD SHELL HISTSIZE 环境变量相关的指令 echo&env export unset 本地变量 环境变量整体理解 程序现象_代码查看环境变量 ​整体理解 环境变量表 环境变量表的传递 环境变量表的查看 内建命令 少说废话&#x1f197; 每个用…

大型网站系统架构演化

大型网站质量属性优先级&#xff1a;高性能 高可用 可维护 应变 安全 一、单体架构 应用程序&#xff0c;数据库&#xff0c;文件等所有资源都在一台服务器上。 二、垂直架构 应用和数据分离&#xff0c;使用三台服务器&#xff1a;应用服务器、文件服务器、数据服务器 应用服…

JavaEE 初阶篇-深入了解 CAS 机制与12种锁的特征(如乐观锁和悲观锁、轻量级锁与重量级锁、自旋锁与挂起等待锁、可重入锁与不可重入锁等等)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 乐观锁与悲观锁概述 1.1 悲观锁&#xff08;Pessimistic Locking&#xff09; 1.2 乐观锁&#xff08;Optimistic Locking&#xff09; 1.3 区别与适用场景 2.0 轻…

我企业的业务需要制作企业网站吗?11个支持的理由以及5个反对的理由!

如果你的企业经营得还不错&#xff0c;你可能会找出很多理由&#xff0c;说明为什么一个高效的网站对你来说并不那么重要。确实&#xff0c;你明白企业需要在互联网上有一定的存在感&#xff0c;但你可能并不认为一个高效的网站会对你的特定业务产生太大的影响——尤其是当你已…

实战纪实 | 编辑器漏洞之Ueditor-任意文件上传漏洞 (老洞新谈)

UEditor 任意文件上传漏洞 前言 前段时间在做某政府单位的项目的时候发现存在该漏洞&#xff0c;虽然是一个老洞&#xff0c;但这也是容易被忽视&#xff0c;且能快速拿到shell的漏洞&#xff0c;在利用方式上有一些不一样的心得&#xff0c;希望能帮助到一些还不太了解的小伙…

PCIe总线-存储器域和PCIe总线域访问流程(二)

1.概述 PCIe总线的最大特点是像CPU访问DDR一样&#xff0c;可以直接使用地址访问PCIe设备&#xff08;桥&#xff09;&#xff0c;但不同的是DDR和CPU同属于存储器域&#xff0c;而CPU和PCIe设备属于两个不同的域&#xff0c;PCIe设备&#xff08;桥&#xff09;的地址空间属于…

[RK3399 Linux] 使用busybox 1.36.1制作rootfs

一、 编译、安装、配置 busybox 1.1 下载源码 根文件系统是根据busybox来制作的。 下载地址:https://busybox.net/downloads/。 这里就以1.36.1版本为例进行编译安装介绍: 注意:编译linux内核与文件系统中的所有程序要使用相同的交叉编译器。 下载完成后解压: mkdir …

03 SQL基础 -- 查询与运算符

一、SELECT 语句基础 1.1 从表中选取数据 SELECT 语句 从表中选取数据时需要使用SELECT语句,也就是只从表中选出(SELECT)必要数据的意思。通过SELECT语句查询并选取出必要数据的过程称为匹配查询或查询(query) 基本SELECT语句包含了SELECT和FROM两个子句(clause)。示…

NAT实验

要求&#xff1a; 1、AR2为ISP路由器&#xff0c;其上只能配置IP地址&#xff0c;不得再进行其他的任何配置 2、PC1-PC2可以ping通客户平板和DNS服务器&#xff1b; 3、客户端可以通过域名访问http1&#xff0c;通过地址访问http2 4、R1为边界路由器&#xff0c;其上只有一…

计算机视觉工程师

为进一步贯彻落实中共中央印发《关于深化人才发展体制机制改革的意见》和国务院印发《关于“十四五”数字经济发展规划》等有关工作的部署要求&#xff0c;深入实施人才强国战略和创新驱动发展战略&#xff0c;加强全国数字化人才队伍建设&#xff0c;持续推进人工智能从业人员…

【深度学习】深度学习md笔记总结第4篇:TensorFlow介绍,学习目标【附代码文档】

深度学习笔记完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;深度学习课程&#xff0c;深度学习介绍要求,目标,学习目标,1.1.1 区别,学习目标,学习目标。TensorFlow介绍&#xff0c;2.4 张量学习目标,2.4.1 张量(Tensor),2.4.2 创建张量的指令,2.4.3 张量…

C语言世界上最详细自定义类型:联合和枚举

前言&#xff1a; hello! 大家好&#xff0c;我是小陈&#xff0c;今天给大家带来一篇联合和枚举的博客&#xff01;&#xff01;&#xff01; 1.联合体类型的声明 像结构体⼀样&#xff0c;联合体也是由⼀个或者多个成员构成&#xff0c;这些成员可以不同的类型。 但是编译…