视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
ARM_STM32F103_使用心得
2025-09-23 22:17:09 责编:小OO
文档
IO端口输入输出模式设置:    1

Delay延时函数:    2

IO端口使用总结:    2

IO口时钟配置:    2

初始化IO口参数:    2

注意:时钟使能之后操作IO口才有效!    2

IO端口输出高低电平函数:    2

IO的输入和输出宏定义方式:    3

读取某个IO的电平函数:    3

IO口方向切换成双向    3

IO 口外部中断的一般步骤:    3

内部ADC使用总结:    4

LCDTFT函数使用大全    5

TFTLCD使用注意点:    5

IO端口宏定义和使用方法:    6

Keil使用心得:    6

ucGUI移植    6

DDS AD9850测试程序:    6

ADC 使用小结:    7

ADC测试程序:    9

DAC—tlv5638测试程序    9

红外测试程序:    9

DMA使用心得:    9

通用定时器使用:    9

BUG发现:    10

编程总结:    10

时钟总结:    10

汉字显示(外部SD卡字库):    11

字符、汉字显示(内部FLASH)    12

图片显示:    16

触摸屏:    17

引脚连接:    18

IO端口输入输出模式设置:

GPIO_Mode_AIN = 0x0,                //模拟输入,常用于ADC 

      GPIO_Mode_IN_FLOATING = 0x04,      //浮空输入 

      GPIO_Mode_IPD = 0x28,               //下拉输入 ,常用于输入模式

     GPIO_Mode_IPU = 0x48,               //上拉输入 ,常用于输入模式

      GPIO_Mode_Out_OD = 0x14,          //开漏输出 

      GPIO_Mode_Out_PP = 0x10,          //通用推挽输出 ,常用于输出模式

      GPIO_Mode_AF_OD = 0x1C,          //复用开漏输出 

      GPIO_Mode_AF_PP = 0x18              //复用推挽

Delay延时函数:

delay_ms(u16 nms);

delay_us(u32 nus);

IO端口使用总结:

1) 使能IO 口时钟。调用函数为RCC_APB2PeriphClockCmd()。 

2) 初始化IO 参数。调用函数GPIO_Init(); 

3) 操作IO。

IO口时钟配置:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);

初始化IO口参数:

注意:时钟使能之后操作IO口才有效!

    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_13|GPIO_Pin_15;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;            //上拉输入

     GPIO_Init(GPIOA, &GPIO_InitStructure);

GPIO_InitTypeDef GPIO_InitStructure;

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;                     //LED0-->PA.8端口配置

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;         //推挽输出

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOA, &GPIO_InitStructure);

技巧:如果为同一端口的不同引脚,可以使用或运算,如GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_13|GPIO_Pin_15;

IO端口输出高低电平函数:

GPIO_SetBits(GPIOA,GPIO_Pin_8|GPIO_Pin_9);                         //PA.8 输出高

GPIO_ResetBits(GPIOA,GPIO_Pin_8);

GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);//可以输出1,也可以输出0

GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);//整体输出一个值

IO的输入和输出宏定义方式:

#define DATAOUT(x) GPIOB->ODR=x; //数据输出

#define DATAIN GPIOB->IDR; //数据输入

#define    DATAOUT(DataValue) {GPIO_Write(GPIOB,(GPIO_ReadOutputData(GPIOB)&0xff00)|(DataValue&0x00FF));}  //PB0~7,作为数据线

读取某个IO的电平函数:

(一)读出一个IO口电平

GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)

#define KEY0 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_13)  //PA13

#define KEY1 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_15)    //PA15 

#define KEY2 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)

(二)读出某个IO口的全部电平

GPIO_ReadInputData (GPIOC)

IO口方向切换成双向

IIC里面的一个实例

#define SDA_IN() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=8<<12;}    //PC12

#define SDA_OUT() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=3<<12;}

IO 口外部中断的一般步骤: 

1)初始化IO 口为输入。 

2)开启IO 口复用时钟,设置IO 口与中断线的映射关系。 

3)初始化线上中断,设置触发条件等。 

4)配置中断分组(NVIC),并使能中断。 

5)编写中断服务函数。

例程:

    开启IO 口复用时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);

GPIOA.13 中断线以及中断初始化配置

      GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource13);

EXTI_InitTypeDef     EXTI_InitStructure;

      EXTI_InitStructure.EXTI_Line=EXTI_Line13;

      EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;    

      EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;

//[ 此外还可以为(EXTI_Trigger_Rising,EXTI_Trigger_Rising_Falling) ]

      EXTI_InitStructure.EXTI_LineCmd = ENABLE;

      EXTI_Init(&EXTI_InitStructure);    

NVIC_InitTypeDef     NVIC_InitStructure;

    NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //使能按键所在的外部中断通道

//[ 此外还可以为NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn ];            

      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2, 

      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;        //子优先级1

      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            //使能外部中断通道

      NVIC_Init(&NVIC_InitStructure);

中断函数的编写:(蓝色字体为格式)

void EXTI0_IRQHandler(void)

{

    delay_ms(10);    //消抖

      if(EXTI_GetITStatus(EXTI_Line0) != RESET)      //检查指定的EXTI0线路触发请求发生与否

    {      

        LED0=!LED0;

        LED1=!LED1;    

    }

    EXTI_ClearITPendingBit(EXTI_Line0);  //清除EXTI0线路挂起位

}

void EXTI15_10_IRQHandler(void)

{

      delay_ms(10);    //消抖             

    if(EXTI_GetITStatus(EXTI_Line13) != RESET){        }

    else if (EXTI_GetITStatus(EXTI_Line15) != RESET) {            }

     EXTI_ClearITPendingBit(EXTI_Line13);  //清除EXTI13线路挂起位

     EXTI_ClearITPendingBit(EXTI_Line15);  //清除EXTI15线路挂起位

}

内部ADC使用总结:

1)STM32F103系列最少都拥有2个ADC,我们选择的STM32F103RBT6也包含有2个ADC。

2)STM32的ADC最大的转换速率为1Mhz,也就是转换时间为1us(在ADCCLK=14M,采样周期为1.5个ADC时钟下得到),不要让ADC的时钟超过14M,否则将导致结果准确度下降。

3)STM32将ADC的转换分为2个通道组:规则通道组和注入通道组。规则通道相当于你运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换,  在注入通道被转换完成之后,规则通道才得以继续转换。

4)STM32ADC的规则通道组最多包含16个转换,而注入通道组最多包含4个通道。

5)STM32的ADC在单次转换模式下,只执行一次转换,该模式可以通过ADC_CR2寄存器的ADON位(只适用于规则通道)启动,也可以通过外部触发启动(适用于规则通道和注入通道),这是CONT位为0。

6)寄存器ADC_CR2操作模式:ADCON位用于开关AD转换器。而CONT位用于设置是否进行连续转换,我们使用单次转换,所以CONT位必须为0。CAL和RSTCAL用

7)于AD校准。ALIGN用于设置数据对齐,我们使用右对齐,该位设置为0。

8)中文参考手册中有详细的描述

9)关于通道:每个通用定时器都有四个通道,这四个通道都可以配置成分别不同的模式。

LCDTFT函数使用大全

注意:画笔颜色,背景颜色的定义,在使用前要设置POINT_COLOR

POINT_COLOR

BACK_COLOR 

void LCD_Init(void);

void LCD_DisplayOn(void);

void LCD_DisplayOff(void);

void LCD_Clear(u16 Color);     //清屏

void LCD_SetCursor(u16 Xpos, u16 Ypos);

void LCD_Scan_Dir(u8 dir);

void LCD_DrawPoint(u16 x,u16 y);//画点

u16  LCD_ReadPoint(u16 x,u16 y); //读点

void Draw_Circle(u16 x0,u16 y0,u8 r);

void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2);

void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2);           

void LCD_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 color);

void LCD_ShowChar(u16 x,u16 y,u8 chararater,u8 size,u8 mode);//显示一个字符,size=12或16

void LCD_ShowNum(u16 x,u16 y,u32 num,u8 len,u8 size);  //显示长度为len数字

void LCD_Show2Num(u16 x,u16 y,u16 num,u8 len,u8 size,u8 mode);//显示2个数字

void LCD_ShowString(u16 x,u16 y,const u8 *p,u8 mode)         //显示一个字符串,16字体

image_display(u16 x,u16 y,u8 * imgx);//显示图片

void Test_Show_CH_Font16(u16 x,u16 y,u8 index,u16 color);//显示汉字16*16

void Test_Show_CH_Font24(u16 x,u16 y,u8 index,u16 color); //显示汉字24*24

TFTLCD使用注意点:

在切换方向的时候,一定不能偷懒;

void LCD_DATA_IN_DIR(void)    //数据输入方向

{

    GPIO_InitTypeDef GPIO_InitStructure; 

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All    ;                 

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;         

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    //这句千万不能注释掉

    GPIO_Init(GPIOB, &GPIO_InitStructure);    

}

void LCD_DATA_OUT_DIR(void)     //数据输出方向

{

    GPIO_InitTypeDef GPIO_InitStructure; 

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All    ;                     

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;         

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    //这句千万不能注释掉

    GPIO_Init(GPIOB, &GPIO_InitStructure);

}

IO端口宏定义和使用方法:

#define AD9850_CONTROL_PORT  GPIOA

#define AD9850_FQUD   GPIO_Pin_2

#define AD9850_WCLK   GPIO_Pin_3

#define AD9850_RST    GPIO_Pin_4

置1和置0

#define AD9850_WCLK_SET GPIO_WriteBit(AD9850_CONTROL_PORT,AD9850_WCLK,Bit_SET)

#define AD9850_WCLK_CLR GPIO_WriteBit(AD9850_CONTROL_PORT,AD9850_WCLK,Bit_RESET)

#define AD9850_DATA_Write_1 GPIO_WriteBit(AD9850_CONTROL_PORT,AD9850_DATA,Bit_SET)

#define AD9850_DATA_Write_0 GPIO_WriteBit(AD9850_CONTROL_PORT,AD9850_DATA,Bit_RESET)

GPIO_InitStructure.GPIO_Pin = AD9850_WCLK | AD9850_FQUD | AD9850_RST | AD9850_DATA ;

GPIO_Init(AD9850_CONTROL_PORT ,&GPIO_InitStructure) ;

方法2:

#define LCD_RD_LOW       GPIOC->BRR|=GPIO_Pin_6;

#define LCD_RD_HIGH       GPIOC->BSRR|=GPIO_Pin_6;

Keil使用心得:

和的区别:

只要不改变头文件,我们一般可以使用F7进行编译,即,这样话的时间比较短;

而是整个重新编译,花的时间比较长;

ucGUI移植

LCD_CONTROLLER             (-1)   /*  lcd 控制器的具体型号,-1 表示是自己定义的型号*/

ucGUI是可以不带操作系统的;

DDS AD9850测试程序:

    Init_AD9850();

    AD9850_Reset_Sreial();

    AD9850_Write_Serial(0x00,2000) ;

    delay_ms(1000);

while(1) 

    {         

    AD9850_Write_Serial(0x00,freq) ;

    delay_ms(1000);

    freq  += 2000;

if(freq >= 20000000)

    freq = 2000;

    }    

ADC 使用小结:

在内部ADC中,单次转换和连续转换的区别:如果用TIME去定时的时候,就要选择单次转换,用定时器去启动转换;如果使用连续转换的话,那么,它是不听TIME定时的,会不听的转换。程序是:ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;

采样频率不能太高,2K采样100HZ 方波,频谱图:

三角波的频谱:

正弦波的频谱:

ADC测试程序:

连接方法:与PA0相连

    u16 adcx;

    float temp;    

Adc_Init();

    while(1)

    {

        adcx= Get_Adc_Average(ADC_Channel_0,10);

        LCD_ShowNum(156,130,adcx,4,16);//显示ADC的值

        temp=(float)adcx*(3.3/4096);

        adcx=temp;

        LCD_ShowNum(156,150,adcx,1,16);//显示电压值

        temp-=adcx;

        temp*=1000;

        LCD_ShowNum(172,150,temp,3,16);

        LED0=!LED0;

        delay_ms(250);

    }

DAC—tlv5638测试程序

Init_TLV5638();        //初始化DA TLV5638   

SetDAC_B(PASTSP,IN_2048,0,i); //改变I的值就可以了,比如 i= 0.02

红外测试程序:

Remote_Init();        //初始化红外接收

if(Remote_Rdy)

    {Key_value = Get_Remote_Value();    }

DMA使用心得:

使用的例子是官方的例程;

调试了半天,原来是没有打开DMA时钟;

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

通用定时器使用:

1)定时器调试了半天,终于知道了了,是由于时钟错了,通用定时器的时钟是:RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);而不是: RCC_APB2PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);一字之差,害的我调试了好久,由此,请看时钟总结;

2)定时器分频器:f CK_PSC/(PSC[15:0]+1);TIM_TimeBaseStructure.TIM_Period = 71;   则是时钟频率72M / (71+1) = 1M;

BUG发现:

1)在使用定时器触发AD,利用DMA传输的时候,TIM2的CC2事件不能使用,这件事情让我调试了好几天,最终无果最后使用TIM1触发解决问题;

编程总结:

一定要注意数的使用范围;

例子:如果N为256时

    U8 i;

for(i=0;i    {

        printf("%d\\n",Adc_value[i]);

        delay_ms(40);

    }

这个就不能正常输出了!

    u16 i;

for(i=0;i    {

        printf("%d\\n",Adc_value[i]);

        delay_ms(40);

    }

这个可以正常输出!

时钟总结:

使用的使能函数是:RCC_APB2PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

#define RCC_APB2Periph_AFIO              ((uint32_t)0x00000001)

#define RCC_APB2Periph_GPIOA             ((uint32_t)0x00000004)

#define RCC_APB2Periph_GPIOB             ((uint32_t)0x00000008)

#define RCC_APB2Periph_GPIOC             ((uint32_t)0x00000010)

#define RCC_APB2Periph_GPIOD             ((uint32_t)0x00000020)

#define RCC_APB2Periph_GPIOE             ((uint32_t)0x00000040)

#define RCC_APB2Periph_GPIOF             ((uint32_t)0x00000080)

#define RCC_APB2Periph_GPIOG             ((uint32_t)0x00000100)

#define RCC_APB2Periph_ADC1              ((uint32_t)0x00000200)

#define RCC_APB2Periph_ADC2              ((uint32_t)0x00000400)

#define RCC_APB2Periph_TIM1              ((uint32_t)0x00000800)

#define RCC_APB2Periph_SPI1              ((uint32_t)0x00001000)

#define RCC_APB2Periph_TIM8              ((uint32_t)0x00002000)

#define RCC_APB2Periph_USART1            ((uint32_t)0x00004000)

#define RCC_APB2Periph_ADC3              ((uint32_t)0x00008000)

#define RCC_APB2Periph_TIM15             ((uint32_t)0x00010000)

#define RCC_APB2Periph_TIM16             ((uint32_t)0x00020000)

#define RCC_APB2Periph_TIM17             ((uint32_t)0x00040000)

#define RCC_APB2Periph_TIM9              ((uint32_t)0x00080000)

#define RCC_APB2Periph_TIM10             ((uint32_t)0x00100000)

#define RCC_APB2Periph_TIM11             ((uint32_t)0x00200000)

使用的使能函数是:RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

#define RCC_APB1Periph_TIM2              ((uint32_t)0x00000001)

#define RCC_APB1Periph_TIM3              ((uint32_t)0x00000002)

#define RCC_APB1Periph_TIM4              ((uint32_t)0x00000004)

#define RCC_APB1Periph_TIM5              ((uint32_t)0x00000008)

#define RCC_APB1Periph_TIM6              ((uint32_t)0x00000010)

#define RCC_APB1Periph_TIM7              ((uint32_t)0x00000020)

#define RCC_APB1Periph_TIM12             ((uint32_t)0x00000040)

#define RCC_APB1Periph_TIM13             ((uint32_t)0x00000080)

#define RCC_APB1Periph_TIM14             ((uint32_t)0x00000100)

#define RCC_APB1Periph_WWDG              ((uint32_t)0x00000800)

#define RCC_APB1Periph_SPI2              ((uint32_t)0x00004000)

#define RCC_APB1Periph_SPI3              ((uint32_t)0x00008000)

#define RCC_APB1Periph_USART2            ((uint32_t)0x00020000)

#define RCC_APB1Periph_USART3            ((uint32_t)0x00040000)

#define RCC_APB1Periph_UART4             ((uint32_t)0x00080000)

#define RCC_APB1Periph_UART5             ((uint32_t)0x00100000)

#define RCC_APB1Periph_I2C1              ((uint32_t)0x00200000)

#define RCC_APB1Periph_I2C2              ((uint32_t)0x00400000)

#define RCC_APB1Periph_USB               ((uint32_t)0x00800000)

#define RCC_APB1Periph_CAN1              ((uint32_t)0x02000000)

#define RCC_APB1Periph_CAN2              ((uint32_t)0x04000000)

#define RCC_APB1Periph_BKP               ((uint32_t)0x08000000)

#define RCC_APB1Periph_PWR               ((uint32_t)0x10000000)

#define RCC_APB1Periph_DAC               ((uint32_t)0x20000000)

#define RCC_APB1Periph_CEC               ((uint32_t)0x40000000)

汉字显示(外部SD卡字库):

初始化函数:

    SPI_Flash_Init();    //SPI FLASH初始化

    f_mount(0, &fs); //初始化必须mount

    while(font_init())

    { 

         while(update_font())//从SD卡更新

        {           }

    }

    Show_Str(60,50,"Mini STM32开发板",16,0);

详见:FATFS浅谈.pdf

字符、汉字显示(内部FLASH)

汉字的显示,研究下面的两个例子,可以惊奇的发现,是多么惊人的相似。我在后面有注释,看看就可以懂了。

void Test_Show_CH_Font16(u16 x,u16 y,u8 index,u16 color)

{                   

    u8 temp,t,t1;

    u16 y0=y;                   

for(t=0;t<32;t++)//每个16*16的汉字点阵 有32个字节 计算方法为:16*16/8=32

    {   

     if(t<16)temp=tfont16[index*2][t]; //前16个字节

        else temp=tfont16[index*2+1][t-16];    //后16个字节    ,可以看到在tfont里面每个字占用两行                       

for(t1=0;t1<8;t1++)

        {

            if(temp&0x80)LCD_Draw_Point(x,y,color);//画实心点

            else LCD_Draw_Point(x,y,BACK_COLOR);   //画空白点(使用背景色)

         temp<<=1;

            y++;

            if((y-y0)==16)

            {

                y=y0;

                x++;

                break;

            }

        }       

    }          

}

const u8 tfont16[][16]=

{

{0x02,0x00,0x42,0x02,0x42,0x04,0x42,0x08,0x42,0x30,0x7F,0xC0,0x42,0x00,0x42,0x00},

{0x42,0x00,0x42,0x00,0x7F,0xFE,0x42,0x00,0x42,0x00,0x42,0x00,0x42,0x00,0x00,0x00},/*"开",0*/

看到了,占据了两行。

//在指定位置 显示1个24*24的汉字

//(x,y):汉字显示的位置

//index:tfont数组里面的第几个汉字

//color:这个汉字的颜色

void Test_Show_CH_Font24(u16 x,u16 y,u8 index,u16 color)

{                   

    u8 temp,t,t1;

    u16 y0=y;                   

for(t=0;t<72;t++)//每个24*24的汉字点阵 有72个字节    计算方法为:24*24/8= 72

    {   

     if(t<24)temp=tfont24[index*3][t]; //前24个字节    可以看到在tfont里面每个字占用三行行 

     else if(t<48)temp=tfont24[index*3+1][t-24]; //中24个字节                              

        else temp=tfont24[index*3+2][t-48];         //后24个字节

for(t1=0;t1<8;t1++)

        {

            if(temp&0x80)LCD_Draw_Point(x,y,color);//画实心点

            else LCD_Draw_Point(x,y,BACK_COLOR);   //画空白点(使用背景色)

         temp<<=1;

            y++;

            if((y-y0)==24)

            {

                y=y0;

                x++;

                break;

            }

        }       

    }          

}

const u8 tfont24[][24]=

{   

{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x08,0x08,0x02,0x08,0x08,0x04,0x08,0x08,0x08,0x08,0x08,0x10,0x08,0x08,0xE0},

{0x0F,0xFF,0x80,0x08,0x08,0x00,0x08,0x08,0x00,0x08,0x08,0x00,0x08,0x08,0x00,0x08,0x08,0x00,0x08,0x08,0x00,0x0F,0xFF,0xFE},

{0x0F,0xFF,0xFE,0x08,0x08,0x00,0x08,0x08,0x00,0x10,0x08,0x00,0x10,0x10,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"开",0*/

可以看出占据了三行

字符显示    

if(!mode) //非叠加方式

    {

     for(pos=0;pos        {

            if(size==12)temp=asc2_1206[num][pos];//调用1206字体

            else temp=asc2_1608[num][pos];         //调用1608字体

         for(t=0;t            {                 

                if(temp&0x01)POINT_COLOR=colortemp;

                else POINT_COLOR=BACK_COLOR;

                LCD_DrawPoint(x,y);    

             temp>>=1;

                x++;

            }

            x=x0;

            y++;

        }    

const unsigned char asc2_1206[95][12]={

{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/

看到了,只占据了一行,当然,这个可以做成字库。

const unsigned char asc2_1206[95][12]={

{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/

{0x00,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x00,0x04,0x00,0x00},/*"!",1*/

{0x00,0x14,0x0A,0x0A,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*""",2*/

{0x00,0x00,0x14,0x14,0x3F,0x14,0x0A,0x3F,0x0A,0x0A,0x00,0x00},/*"#",3*/

{0x00,0x04,0x1E,0x15,0x05,0x06,0x0C,0x14,0x15,0x0F,0x04,0x00},/*"$",4*/

{0x00,0x00,0x12,0x15,0x0D,0x0A,0x14,0x2C,0x2A,0x12,0x00,0x00},/*"%",5*/

{0x00,0x00,0x04,0x0A,0x0A,0x1E,0x15,0x15,0x09,0x36,0x00,0x00},/*"&",6*/

{0x00,0x02,0x02,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"'",7*/

{0x00,0x20,0x10,0x08,0x08,0x08,0x08,0x08,0x08,0x10,0x20,0x00},/*"(",8*/

{0x00,0x02,0x04,0x08,0x08,0x08,0x08,0x08,0x08,0x04,0x02,0x00},/*")",9*/

{0x00,0x00,0x00,0x04,0x15,0x0E,0x0E,0x15,0x04,0x00,0x00,0x00},/*"*",10*/

{0x00,0x00,0x04,0x04,0x04,0x1F,0x04,0x04,0x04,0x00,0x00,0x00},/*"+",11*/

{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x02,0x01},/*

{0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00},/*"-",13*/

{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00},/*".",14*/

{0x00,0x10,0x08,0x08,0x08,0x04,0x04,0x02,0x02,0x02,0x01,0x00},/*"/",15*/

{0x00,0x00,0x0E,0x11,0x11,0x11,0x11,0x11,0x11,0x0E,0x00,0x00},/*"0",16*/

{0x00,0x00,0x04,0x06,0x04,0x04,0x04,0x04,0x04,0x0E,0x00,0x00},/*"1",17*/

{0x00,0x00,0x0E,0x11,0x11,0x08,0x04,0x02,0x01,0x1F,0x00,0x00},/*"2",18*/

{0x00,0x00,0x0E,0x11,0x10,0x0C,0x10,0x10,0x11,0x0E,0x00,0x00},/*"3",19*/

{0x00,0x00,0x08,0x0C,0x0A,0x0A,0x09,0x1E,0x08,0x18,0x00,0x00},/*"4",20*/

{0x00,0x00,0x1F,0x01,0x01,0x0F,0x10,0x10,0x11,0x0E,0x00,0x00},/*"5",21*/

{0x00,0x00,0x0E,0x09,0x01,0x0F,0x11,0x11,0x11,0x0E,0x00,0x00},/*"6",22*/

{0x00,0x00,0x1F,0x09,0x08,0x04,0x04,0x04,0x04,0x04,0x00,0x00},/*"7",23*/

{0x00,0x00,0x0E,0x11,0x11,0x0E,0x11,0x11,0x11,0x0E,0x00,0x00},/*"8",24*/

{0x00,0x00,0x0E,0x11,0x11,0x11,0x1E,0x10,0x12,0x0E,0x00,0x00},/*"9",25*/

{0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x04,0x00,0x00},/*":",26*/

{0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x04,0x04,0x00},/*";",27*/

{0x00,0x20,0x10,0x08,0x04,0x02,0x04,0x08,0x10,0x20,0x00,0x00},/*"<",28*/

{0x00,0x00,0x00,0x00,0x1F,0x00,0x00,0x1F,0x00,0x00,0x00,0x00},/*"=",29*/

{0x00,0x02,0x04,0x08,0x10,0x20,0x10,0x08,0x04,0x02,0x00,0x00},/*">",30*/

{0x00,0x00,0x0E,0x11,0x11,0x08,0x04,0x04,0x00,0x04,0x00,0x00},/*"?",31*/

{0x00,0x00,0x0E,0x11,0x19,0x15,0x15,0x1D,0x01,0x1E,0x00,0x00},/*"@",32*/

{0x00,0x00,0x04,0x04,0x0C,0x0A,0x0A,0x1E,0x12,0x33,0x00,0x00},/*"A",33*/

{0x00,0x00,0x0F,0x12,0x12,0x0E,0x12,0x12,0x12,0x0F,0x00,0x00},/*"B",34*/

{0x00,0x00,0x1E,0x11,0x01,0x01,0x01,0x01,0x11,0x0E,0x00,0x00},/*"C",35*/

{0x00,0x00,0x0F,0x12,0x12,0x12,0x12,0x12,0x12,0x0F,0x00,0x00},/*"D",36*/

{0x00,0x00,0x1F,0x12,0x0A,0x0E,0x0A,0x02,0x12,0x1F,0x00,0x00},/*"E",37*/

{0x00,0x00,0x1F,0x12,0x0A,0x0E,0x0A,0x02,0x02,0x07,0x00,0x00},/*"F",38*/

{0x00,0x00,0x1C,0x12,0x01,0x01,0x39,0x11,0x12,0x0C,0x00,0x00},/*"G",39*/

{0x00,0x00,0x33,0x12,0x12,0x1E,0x12,0x12,0x12,0x33,0x00,0x00},/*"H",40*/

{0x00,0x00,0x1F,0x04,0x04,0x04,0x04,0x04,0x04,0x1F,0x00,0x00},/*"I",41*/

{0x00,0x00,0x3E,0x08,0x08,0x08,0x08,0x08,0x08,0x09,0x07,0x00},/*"J",42*/

{0x00,0x00,0x37,0x12,0x0A,0x06,0x0A,0x0A,0x12,0x37,0x00,0x00},/*"K",43*/

{0x00,0x00,0x07,0x02,0x02,0x02,0x02,0x02,0x22,0x3F,0x00,0x00},/*"L",44*/

{0x00,0x00,0x1B,0x1B,0x1B,0x1B,0x15,0x15,0x15,0x15,0x00,0x00},/*"M",45*/

{0x00,0x00,0x3B,0x12,0x16,0x16,0x1A,0x1A,0x12,0x17,0x00,0x00},/*"N",46*/

{0x00,0x00,0x0E,0x11,0x11,0x11,0x11,0x11,0x11,0x0E,0x00,0x00},/*"O",47*/

{0x00,0x00,0x0F,0x12,0x12,0x0E,0x02,0x02,0x02,0x07,0x00,0x00},/*"P",48*/

{0x00,0x00,0x0E,0x11,0x11,0x11,0x11,0x17,0x19,0x0E,0x18,0x00},/*"Q",49*/

{0x00,0x00,0x0F,0x12,0x12,0x0E,0x0A,0x12,0x12,0x37,0x00,0x00},/*"R",50*/

{0x00,0x00,0x1E,0x11,0x01,0x06,0x08,0x10,0x11,0x0F,0x00,0x00},/*"S",51*/

{0x00,0x00,0x1F,0x15,0x04,0x04,0x04,0x04,0x04,0x0E,0x00,0x00},/*"T",52*/

{0x00,0x00,0x33,0x12,0x12,0x12,0x12,0x12,0x12,0x0C,0x00,0x00},/*"U",53*/

{0x00,0x00,0x33,0x12,0x12,0x0A,0x0A,0x0C,0x04,0x04,0x00,0x00},/*"V",54*/

{0x00,0x00,0x15,0x15,0x15,0x0E,0x0A,0x0A,0x0A,0x0A,0x00,0x00},/*"W",55*/

…….

综合实验中的大字符显示实验:

void LCD_ShowBigChar(u8 x,u16 y,u8 num)

{

    u8 n,t;

    u8 temp;

    u8 t1,deadline;

    u16 colortemp=POINT_COLOR; 

    u8 x0=x;     

     if(num==':')t1=150;

    else if(num=='.')t1=165;

    else if(num=='C')t1=180;

    else t1=15*num;

    deadline=t1+15;

  for(;t1    {     

     for(n=0;n<16;n++)

        {

            temp=BIG_ASCII[t1][n];

         for(t=0;t<8;t++)

            {

                if(temp&0x80)POINT_COLOR=colortemp;

                else POINT_COLOR=BACK_COLOR;

                LCD_DrawPoint(x,y);       

             temp<<=1;

                x++;

                if(((n%4)==3)&&t==5)

                {

                    x=x0;

                    y++;  

                    break;

                }

            }

        }

    }       

    POINT_COLOR=colortemp;                          

}   

图片显示:

#include "string.h"

const char filedir[]="0:/PICTURE";

专门调用显示图片的函数:

void viewPictures(const char *fileDir){

    DIR dir;

    BOOL result;

    FRESULT res;

    FILINFO fileInfo;

    char *filename;

    char  temp[256];

#if _USE_LFN                //定义了可以使用长文件名

    static char lfn[_MAX_LFN + 1];

    fileInfo.lfname = lfn;

    fileInfo.lfsize = sizeof(lfn);

#endif

    delay_ms(300);

    LCD_Clear(BRRED);

     while(1)

    {

         res= f_opendir(&dir,  fileDir);

       if(res==FR_OK)

       {      

         for(;;)

         {

            res =f_readdir(&dir,&fileInfo);

            if(res!=0||fileInfo.fname[0]==0)break;

            if(fileInfo.fname[0]=='.')continue  ;

#if _USE_LFN

            filename = *fileInfo.lfname ? fileInfo.lfname : fileInfo.fname;

#else

            filename = fileInfo.fname;

#endif

            if ( !(fileInfo.fattrib & AM_DIR) ) 

            {

                strcpy(temp,fileDir);

               strcat(temp,"/");

               LCD_Clear(WHITE);//清屏,加载下一幅图片的时候,一定清屏

                printf("%s\\n",filename)     ;

            //可以显示需要的图片了

                {

                   result=AI_LoadPicFile(((u8*)strcat(temp,filename)),30,20,240,320);//显示图片的函数

               }

               if(result==FALSE) continue;

               POINT_COLOR=RED;

               Show_Str(0,0,(u8 *)filename,16,1);//显示图片名字

               delay_ms(1000); 

            }

          } 

       }     

     }

  }

触摸屏:

Touch_Init();    

if(Pen_Point.Key_Sta==Key_Down)//触摸屏被按下

        {

            Pen_Int_Set(0);//关闭中断

            do

            {

                Convert_Pos();

                Pen_Point.Key_Sta=Key_Up;

             if(Pen_Point.X0>216&&Pen_Point.Y0<16)Load_Drow_Dialog();//清除

                else 

                {

                    Draw_Big_Point(Pen_Point.X0,Pen_Point.Y0);//画图        

                 GPIOC->ODR|=1<<1; //PC1 上拉       

                }

            }while(PEN==0);//如果PEN一直有效,则一直执行

            Pen_Int_Set(1);//开启中断

        }else delay_ms(10);

坐标变换

void Plot_ADC_Data(u16 *p)                 

{

    u16 i;

    LCD_Fill(0,319-RATE,N,319,WHITE);

for(i=0;i    {

        LCD_DrawLine(i, 319-p[i], (i+1), 319-p[i+1]);        

    }    

}

坐标扩大一倍后:

void Plot_ADC_Data(u16 *p)                 

{

    u16 i;

    LCD_Fill(0,319-RATE,N,319,WHITE);

for(i=0;i    {

        LCD_DrawLine(2*i, 319-p[i], 2*(i+1), 319-p[i+1]);        

    }    

}

拓展:扩大m倍

void Plot_ADC_Data(u16 *p,u8 m)                 //这里引入m开控制波形显示

{

    u16 i;

    LCD_Fill(0,319-RATE,N,319,WHITE);

for(i=0;i    {

        LCD_DrawLine(m*i, 319-p[i], m*(i+1), 319-p[i+1]);        

    }    

}

引脚连接:

LED端口连接:

LED0 ——PORTA.8

LED1 ——PORTD.2

按键端口连接:

KEY0——GPIOA.13

KEY1——GPIOA.15

WK_UP——GPIOA.0

 

键盘接口:

PA0-PA7

PWM输出:

PA8

TFT连接端口

数据口 – PB

LCD_CS——PC9

LCD_RS——PC8

LCD_RW——PC7

LCD_RD——PC6

LCD_BL——PC10

DAC_TLV5638

DIN    PC1

SCLK PC4

CS    PC5

红外遥控:

PA.1

DDS 

/*AD9850使用到的引脚为:

GPIOA

#define AD9850_RST    GPIO_Pin_11

AD9850_DATA            GPIO_Pin_12 

AD9850_FQUD              GPIO_Pin_13

#define AD9850_WCLK   GPIO_Pin_14

*/

今晚听原子说起函数指针作为结构体成员的用法,很久没去复习过c语言,回来后特地找了个测试程序,发现这种用法有点像java里面的函数调用,思想值得借鉴。

把一些实体定义为结构体变量,而实体的固有属性便是基本数据类型定义的成员变量,而函数指针类型的成员变量可以抽象为你能对这个实体发出的请求,

例如一个遥控器实体,它具备的固有属性为品牌,按键数等等,而你可以发出的请求为按按键发送指令。

#include "stdio.h"   

struct DEMO   //遥控器实体

{   

    char x;   // 按键

    int (*func)(int x,int y); //函数指针    //按按键

};   

  

int add2(int x,int y)   //定义遥控器发送指令

{   

    return x+y;   

}   

  

int dec2(int x,int y)   //定义遥控器发送指令

{   

    return x-y;   

}   

  

struct DEMO demo[2] =    

{   

    {1,add2},       {2,dec2}   

};   

int main(void)

{   

    printf("func%d=%d\\n",demo[0].x,demo[0].func(4,3));   

    printf("func%d=%d\\n",demo[1].x,demo[1].func(4,3));   

    return 0;   

}  

说明:demo[0],demo[1]可以看做两种不同功能的遥控器实体,

demo[0].x为获取遥控器的固有属性,可以是遥控器的品牌

demo[0].func(4,3)可以抽象为同时按下4,3按键后遥控器的反应;

此源码没有运行过!

--

我发一个运行过的源码。

在.h文件中,有如下代码:

//TVM控制状态

typedef struct _m_TVM_CTRL

{

 u32(*get_coin_totval)(void); //得到此次投入硬币总金额

 u32(*get_coin_totcnt)(void); //得到此次投入硬币总数目

 void(*clr_coin)(void);   //清空金额和数目,每次新投币开始执行

 u32(*get_coin_totrval)(void);//得到此次找零的总金额

 u32(*get_coin_totrcnt)(void);//得到此次找零的硬币总数目

 void(*clr_rcoin)(void);   //清空金额和数目,每次新找零开始执行

 void(*clr_hpx)(void);      //清空某个hp中的所有硬币

 void(*thp_add_money)(void);  //将钱数汇总到循环找零hp中,当chg_mode为1的时候,可以执行该函数

  u8  cur_cmd;           //当前指令 0X00,无有效指令;0X32,开始收钱;0X33,硬币回收;0X34,找零; 

  u16 cmd_prm;         //指令参数

 u8  cmd_sta;                 //指令执行状态

 m_HP_MESSAGE hp_msg[4];   //每个hopper的状态信息

}m_TVM_CTRL; 

extern m_TVM_CTRL     TVM_CTRL;    //控制器

在.c文件中,有如下代码:

//TVM主控制器      

m_TVM_CTRL     TVM_CTRL=    //控制器

{

 get_coin_totval,

 get_coin_totcnt,

 clr_coin,

 get_coin_totrval,

 get_coin_totrcnt,

 clr_rcoin,

 clr_hpx,

 thp_add_money,

  0,

 0,

 0,

 0,   

};

get_coin_totva, clr_coinl等都是在.c文件中存在的函数的函数名。

这样,就完成了TVM_CTRL这个结构体的函数指针初始化,

接下来就可以方便应用了。

比如我要清空TVM的金额,我只需要写:TVM_CTRL.clr_coin();即可。

又比如,我要清空某个HOPPER,只需要写:TVM_CTRL.clr_hpx();

这样可以及其方便的管理某些设备的各个状态。

你可以把任何一个外设都定义为一个结构体,然后在这个里面包含了该外设的所有执行函数,及状态变量。从而非常方便的控制该外设。

更好的是,一旦有任何新增功能,只需要在原来的结构体里面塞函数或者变量就可以了。及其方便维护。

这个对typedef的讲解很详细!

不管实在C还是C++代码中,typedef这个词都不少见,当然出现频率较高的还是在C代码中。typedef与#define有些相似,但更多的是不同,特别是在一些复杂的用法上,就完全不同了,看了网上一些C/C++的学习者的博客,其中有一篇关于typedef的总结还是很不错,由于总结的很好,我就不加修改的引用过来了,以下是引用的内容(红色部分是我自己写的内容)。用途一:

定义一种类型的别名,而不只是简单的宏替换。可以用作同时声明指针型的多个对象。比如:

char* pa, pb; // 这多数不符合我们的意图,它只声明了一个指向字符变量的指针,

// 和一个字符变量;

以下则可行:

typedef char* PCHAR;

PCHAR pa, pb;  

这种用法很有用,特别是char* pa, pb的定义,初学者往往认为是定义了两个字符型指针,其实不是,而用typedef char* PCHAR就不会出现这样的问题,减少了错误的发生。

用途二:

用在旧的C代码中,帮助struct。以前的代码中,声明struct新对象时,必须要带上struct,即形式为: struct 结构名对象名,如:

struct tagPOINT1

{

    int x; 

    int y; 

};

struct tagPOINT1 p1;

而在C++中,则可以直接写:结构名对象名,即:tagPOINT1 p1;

typedef struct tagPOINT

{

    int x;

    int y;

}POINT;

POINT p1; // 这样就比原来的方式少写了一个struct,比较省事,尤其在大量使用的时

候,或许,在C++中,typedef的这种用途二不是很大,但是理解了它,对掌握以前的旧代

码还是有帮助的,毕竟我们在项目中有可能会遇到较早些年代遗留下来的代码。

用途三:

用typedef来定义与平台无关的类型。

比如定义一个叫 REAL 的浮点类型,在目标平台一上,让它表示最高精度的类型为:

typedef long double REAL;

在不支持 long double 的平台二上,改为:

typedef double REAL;

在连 double 都不支持的平台三上,改为:

typedef float REAL;

也就是说,当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。

标准库就广泛使用了这个技巧,比如size_t。另外,因为typedef是定义了一种类型的新别名,不是简单的字符串替换,所以它比宏来得稳健。

    这个优点在我们写代码的过程中可以减少不少代码量哦!

用途四: 

为复杂的声明定义一个新的简单的别名。方法是:在原来的声明里逐步用别名替换一部

分复杂声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化

版。举例:  

原声明:void (*b[10]) (void (*)());

变量名为b,先替换右边部分括号里的,pFunParam为别名一:

typedef void (*pFunParam)();

再替换左边的变量b,pFunx为别名二:

typedef void (*pFunx)(pFunParam);

原声明的最简化版:

pFunx b[10];

原声明:doube(*)() (*e)[9];

变量名为e,先替换左边部分,pFuny为别名一:

typedef double(*pFuny)();

再替换右边的变量e,pFunParamy为别名二

typedef pFuny (*pFunParamy)[9];

原声明的最简化版:

pFunParamy e;

理解复杂声明可用的“右左法则”:从变量名看起,先往右,再往左,碰到一个圆括号

就调转阅读的方向;括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直

到整个声明分析完。举例:

int (*func)(int *p);

首先找到变量名func,外面有一对圆括号,而且左边是一个*号,这说明func是一个指针

;然后跳出这个圆括号,先看右边,又遇到圆括号,这说明(*func)是一个函数,所以

func是一个指向这类函数的指针,即函数指针,这类函数具有int*类型的形参,返回值

类型是int。

int (*func[5])(int *);

func右边是一个[]运算符,说明func是具有5个元素的数组;func的左边有一个*,说明

func的元素是指针(注意这里的*不是修饰func,而是修饰func[5]的,原因是[]运算符

优先级比*高,func先跟[]结合)。跳出这个括号,看右边,又遇到圆括号,说明func数

组的元素是函数类型的指针,它指向的函数具有int*类型的形参,返回值类型为int。

这种用法是比较复杂的,出现的频率也不少,往往在看到这样的用法却不能理解,相信以上的解释能有所帮助。

*****以上为参考部分,以下为本人领悟部分*****

使用示例:

1.比较一:

#include

using namespace std;

typedef int (*A) (char, char);

int ss(char a, char b)

{

cout<<"功能1"<cout<cout<    return 0;

}

int bb(char a, char b)

{

cout<<"功能2"<cout<cout<    return 0;

}

void main()

{

    A a;

    a = ss;

    a('a','b');

    a = bb;

    a('a', 'b');

}

2.比较二:

typedef int (A) (char, char);

void main()

{

    A *a;

    a = ss;

    a('a','b');

    a = bb;

    a('a','b');

}

两个程序的结果都一样:

功能1

a

b

功能2

b

a

*****以下是参考部分*****

参考自:http://blog.hc360.com/portal/personShowArticle.do?articleId=57527

typedef 与 #define的区别:

案例一:

通常讲,typedef要比#define要好,特别是在有指针的场合。请看例子:

typedef char *pStr1;

#define pStr2 char *;

pStr1 s1, s2;

pStr2 s3, s4;

在上述的变量定义中,s1、s2、s3都被定义为char *,而s4则定义成了char,不是我们

所预期的指针变量,根本原因就在于#define只是简单的字符串替换而typedef则是为一

个类型起新名字。

案例二:

下面的代码中编译器会报一个错误,你知道是哪个语句错了吗?

typedef char * pStr;

char string[4] = "abc";

const char *p1 = string;

const pStr p2 = string;

p1++;

p2++;

  是p2++出错了。这个问题再一次提醒我们:typedef和#define不同,它不是简单的

文本替换。上述代码中const pStr p2并不等于const char * p2。const pStr p2和

const long x本质上没有区别,都是对变量进行只读,只不过此处变量p2的数据类

型是我们自己定义的而不是系统固有类型而已。因此,const pStr p2的含义是:限定数

据类型为char *的变量p2为只读,因此p2++错误。虽然作者在这里已经解释得很清楚了,可我在这个地方仍然还是糊涂的,真的希望哪位高手能帮忙指点一下,特别是这一句“只不过此处变量p2的数据类型是我们自己定义的而不是系统固有类型而已”,难道自己定义的类型前面用const修饰后,就不能执行更改运算,而系统定义的类型却可以?下载本文

显示全文
专题