OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
楼主: it_do_just

初学者即将毕业实习,希望利用最后两个月的学习顺利找到工作,占个地方做些笔记O(∩_∩)O哈哈~

    [复制链接]

0

主题

27

帖子

0

精华

初级会员

Rank: 2

积分
113
金钱
113
注册时间
2014-4-26
在线时间
22 小时
发表于 2015-11-24 21:32:59 | 显示全部楼层
正点原子逻辑分析仪DL16劲爆上市
回复 支持 反对

使用道具 举报

2

主题

11

帖子

0

精华

新手上路

积分
40
金钱
40
注册时间
2015-9-14
在线时间
0 小时
发表于 2015-11-24 22:11:07 | 显示全部楼层
    Cool !   楼主加油 !
回复 支持 反对

使用道具 举报

558

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
164897
金钱
164897
注册时间
2010-12-1
在线时间
2100 小时
发表于 2015-11-24 22:33:06 | 显示全部楼层
回复【100楼】229382777@qq.com:
---------------------------------
不错.谢谢分享.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

72

主题

2719

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
发表于 2015-11-25 01:09:01 | 显示全部楼层
翻页了,没抢到100楼,加油,200楼的时候试试
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-11-25 08:18:41 | 显示全部楼层
回复【103楼】龙之谷:
---------------------------------
哈哈,谢谢龙哥一直支持
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-11-25 10:47:48 | 显示全部楼层
实战:内部温度传感器

这章有了上面的ADC基础后就比较简单了,开启内部温度传感器使能位,读取内部通道16的ADC值后根据公式进行转换就行了,根据下面两张图就可以解决本章的实验了



我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

27

主题

309

帖子

1

精华

高级会员

Rank: 4

积分
774
金钱
774
注册时间
2012-6-19
在线时间
19 小时
发表于 2015-11-25 13:44:09 | 显示全部楼层
不错,顶楼主!!!!!!!!!
回复 支持 反对

使用道具 举报

4

主题

98

帖子

0

精华

高级会员

Rank: 4

积分
755
金钱
755
注册时间
2015-9-16
在线时间
94 小时
发表于 2015-11-25 14:00:29 | 显示全部楼层
很厉害,点赞。在看到你这么努力加油的同时,我们大家也更加有动力一起学习。
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-11-25 14:05:16 | 显示全部楼层
回复【107楼】SeaOverflow:
---------------------------------
如果我的努力能给你带来动力是我的荣幸,可以一起努力学习,有问题可以回复大家一起讨论,谢谢
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-11-25 21:26:56 | 显示全部楼层

实战:DAC

 

简介:

数字/模拟转换模块(DAC)是12位数字输入,电压输出的数字/模拟转换器。 DAC可以配置为8位或12位模式,也可以与DMA控制器配合使用。 DAC工作在12位模式时,数据可以设置成左对齐或右对齐。 DAC模块有2个输出通道,每个通道都有单独的转换器。在双DAC模式下, 2个通道可以独立地进行转换,也可以同时进行转换并同步地更新2个通道的输出。 DAC可以通过引脚输入参考电压VREF+以获得更精确的转换结果。

 

DAC主要特征
● 2个DAC转换器:每个转换器对应1个输出通道
● 8位或者12位单调输出
● 12位模式下数据左对齐或者右对齐
● 同步更新功能
● 噪声波形生成
● 三角波形生成
● 双DAC通道同时或者分别转换
● 每个通道都有DMA功能
● 外部触发转换
● 输入参考电压VREF+



当 DAC 的参考电压为Vref+的时候(对STM32F103RC来说就是3.3V),DAC的输出
电压是线性的从0~Vref+,12位模式下DAC输出电压与Vref+以及DORx的计算公式如下:
                  DACx 输出电压 = Vref*(DORx/4095)

 

配置步奏:

1)开启 PA 口时钟,设置 PA4 为模拟输入。

      RCC->APB2ENR|=1<<2;    //使能PORTA时钟              

GPIOA->CRL&=0XFFF0FFFF;

      GPIOA->CRL|=0X00000000;//PA4 模拟输入   

 

2)使能 DAC1 时钟。
RCC->APB1ENR|=1<<29;   //使能DAC时钟  

 

3)设置 DAC 的工作模式
DAC->CR|=1<<0;      //使能DAC1

       
DAC->CR|=1<<1;      //DAC1输出缓存不使能 BOFF1=1



DAC->CR|=0<<2;      //不使用触发功能 TEN1=0


DAC->CR|=0<<3;      //DAC TIM6 TRGO,不过要TEN1=1才行



DAC->CR|=0<<6;      //不使用波形发生



DAC->CR|=0<<8;      //屏蔽、幅值设置


DAC->CR|=0<<12;    //DAC1 DMA不使能   
 



DAC->DHR12R1=0;

DAC通道112位右对齐数据保持寄存器(DAC_DHR12R1)


4) 设置 DAC 的输出值。
DAC->DHR12R1=temp;

 

输入阻抗和输出阻抗

http://www.21ic.com/jichuzhishi/analog/questions/2013-04-28/180375.html

 

这章的内容难度不是很大,配置的东西也比较少,自己输出两路DAC,利用两路ADC进行电压值的采集,下面是效果图












我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

558

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
164897
金钱
164897
注册时间
2010-12-1
在线时间
2100 小时
发表于 2015-11-25 22:23:32 | 显示全部楼层
回复【109楼】229382777@qq.com:
---------------------------------
输出2个DAC,很多网友不会弄,诶....
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-11-26 10:16:01 | 显示全部楼层
回复【110楼】正点原子:
---------------------------------
......我就两路单独输出,DAC1和DAC2
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

74

主题

319

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1790
金钱
1790
注册时间
2014-7-1
在线时间
170 小时
发表于 2015-11-26 11:07:35 | 显示全部楼层
佩服毅力,看好你
做一个相信自己的人
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-11-26 11:08:50 | 显示全部楼层
回复【112楼】shenqihao:
---------------------------------
谢谢支持
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

2

主题

20

帖子

0

精华

初级会员

Rank: 2

积分
98
金钱
98
注册时间
2015-4-10
在线时间
12 小时
发表于 2015-11-26 14:21:23 | 显示全部楼层
楼主不错,顶!!!
回复 支持 反对

使用道具 举报

2

主题

48

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
437
金钱
437
注册时间
2013-7-30
在线时间
104 小时
发表于 2015-11-27 17:28:15 | 显示全部楼层
楼主很棒   顶一下  已收藏

 希望继续坚持下去。
不努力,谁也给不了你想要的生活!
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-11-27 18:55:33 | 显示全部楼层

实战:DMA

 

简介:

DMA,全称为:Direct Memory Access,即直接存储器访问。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路,能使CPU的效率大为提高。
STM32 最多有2个DMA控制器(DMA2仅存在大容量产品中), DMA1有7个通道。DMA2有 5个通道。每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁起来协调各个DMA请求的优先权。

 

DMA主要特性
● 12个独立的可配置的通道(请求): DMA1有7个通道, DMA2有5个通道
● 每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过软件来配置。
● 在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推) 。
● 独立数据源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐。
● 支持循环的缓冲器管理
● 每个通道都有3个事件标志(DMA半传输、 DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求。
● 存储器和存储器间的传输
● 外设和存储器、存储器和外设之间的传输
● 闪存、 SRAM、外设的SRAM、 APB1、 APB2和AHB外设均可作为访问的源和目标。
● 可编程的数据传输数目:最大为65535
下面为功能框图:


本次实验用到的ADC1是通道1



自己将原子哥的例程实验后,将DMA和ADC、DAC进行了结合,原子哥的例程是通过DMA读取数据到串口发送到电脑显示,并且通过寄存器的递减来显示传递的进度然后在电脑显示,实现这个功能后,自己又对前面两章学习的内容进行了结合,设置4个ADC通道,2路DAC输出,DMA进行数据传递,并且给LCD增加了新的功能,下面介绍DMA需要配置的几个寄存器


DMA中断状态寄存器(DMA_ISR)

这次自己分别试了下使用中断和不使用中断,都没有问题,下面说下不使用中断的配置

DMA_GetFlagStatus(DMA1_FLAG_TC1)!=RESET //等待通道1传输完成




DMA中断标志清除寄存器(DMA_IFCR)

DMA_ClearFlag(DMA1_FLAG_TC1);//清除通道1传输完成标志


DMA通道x配置寄存器(DMA_CCRx)(x = 17)

DMA_InitStruct.DMA_BufferSize = cndtr;           //设置为4,有4个通道的ADC数据

DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;//外设通过DMA向内存存入数据

DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;  //DMA通道x不设置为内存到内存传输

DMA_InitStruct.DMA_MemoryBaseAddr = cmar;    //DMA内存基地址

DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;    //内存地址寄存器递增

DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;            //循环DMA模式(适合ADC扫描类)

DMA_InitStruct.DMA_PeripheralBaseAddr = cpar;              //DMA外设ADC基地址

DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变

DMA_InitStruct.DMA_Priority=DMA_Priority_High;     //DMA通道优先级高

DMA_Init(DMA_CHx, &DMA_InitStruct);  

DMA_Cmd(DMA1_Channel1,ENABLE);          //使能DMA1通道1传输



DMA通道x外设地址寄存器(DMA_CPARx)(x = 17)

DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外设数据位宽16位(AD转换数据为16位)


DMA通道x存储器地址寄存器(DMA_CMARx)(x = 17)

DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//内存数据位宽16位



设置扫描模式,不用每次都用单次转换和读取一个规则通道的值了,那样的话必须按顺序来读,而用ADC的扫描模式则可以设置ADC的采样顺序了.

ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;//开启连续转换

ADC_InitStruct.ADC_ScanConvMode = ENABLE;//开启扫描模式

ADC_InitStruct.ADC_NbrOfChannel = 4;//顺序进行规则转换的ADC通道的数目

//设置4通道的采样顺序和采样时间(可以更改)

ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_239Cycles5);

ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_239Cycles5);

ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_239Cycles5);

ADC_RegularChannelConfig(ADC1,ADC_Channel_3,4,ADC_SampleTime_239Cycles5);



最后设置一个数组来保存ADC_DR寄存器的4个通道值

ADC_ConvertedValue[4];

MY_DMA_Init(DMA1_Channel1,  (u32)&ADC1->DR,  (u32)ADC_ConvertedValue,  4);//传递进形参地址

 

关于LCD的新功能就是将printf函数重映像到屏幕上,之前试过没有x,y轴定位的方式,但是实际意义不大,这次修改了一下可以支持定位操作了,实现的方法来自于论坛的网友提供,对于原子哥之前要取出整数和小数部分显得比较麻烦,所以用printf%f直接输出就可以了,以后直接可以用屏幕来打印信息了,免得开串口那么麻烦,哈哈,懒人神技!
printf重定向函数:

[mw_shl_code=c,true]//重定义fputc函数 int fputc(int ch, FILE *f) { static u16 x=0, y=0; if(ch == '\n') { x = 0; y += 12; return ch; } if(x > 240-6) { x = 0; y += 12; } if(y > 320-12) { y = 0; LCD_Clear(WHITE); } LCD_Show_Char(x, y, ch, 12); x+=6; return ch; } #include <stdarg.h>//加入头文件[/mw_shl_code] [mw_shl_code=c,true]void LCD_ShowString(u16 x,u16 y,const u8 *p) { u16 x1; x1=x; while(*p!='\0') { if(x>=lcddev.width){x=x1;} if(y>=lcddev.height){y=x=0;LCD_Clear(WHITE);} if(*p=='\n') { y+=16; x=x1; } else { LCD_Show_Char(x,y,*p,16); x+=8; } p++; } } void LCD_printf(u16 x,u16 y,const char *format, ...) { char tmp[25]; __va_list arg; va_start(arg, format); vsprintf(tmp,format,arg); va_end(arg); LCD_ShowString(x,y,(const u8*)tmp); }[/mw_shl_code]


DAC输出:

Dac1_Set_Vol(1500);  //1.5V

Dac2_Set_Vol(2100);  //2.1V        

ADC获取:

adcx = ADC_ConvertedValue;

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

LCD_printf(96, 40+i*16, "%1.3fV", temp);

算出值后直接定位打印出来就好了,非常方便,下面是例程的效果图,4路ADC+2路DAC+DMA的效果(本章代码在附件中,有需要的可以参考下)

ADC+DAC+DMA.zip

5.52 MB, 下载次数: 2530

我的博客:http://blog.csdn.net/itdo_just
回复 支持 1 反对 0

使用道具 举报

16

主题

173

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2441
金钱
2441
注册时间
2014-11-5
在线时间
348 小时
发表于 2015-11-27 19:13:41 | 显示全部楼层
楼主好强大,值得学习
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-11-27 19:58:08 | 显示全部楼层
回复【115楼】konghuiju:
---------------------------------
回复【117楼】hello_galaxy:
---------------------------------
谢谢支持!!
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

2

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
62
金钱
62
注册时间
2015-11-26
在线时间
2 小时
发表于 2015-11-29 10:43:19 | 显示全部楼层
研究生入学两个月老师让做一个小项目,所以看这些教程都很粗略,直接学LWIP,发现基础很薄弱,前面的学习就是一带而过,无奈时间又催的很紧,敬佩楼主这样的精神,向你学习!
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-11-29 11:26:32 | 显示全部楼层
回复【119楼】六子93:
---------------------------------
真羡慕有个好学位呀,学习环境比我好多了,层主教程过的快也能理解,毕竟要学的比较多
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-11-29 13:39:20 | 显示全部楼层

实战:IIC

 

IIC(Inter-Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。它是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,原子哥手册上写的高速是400K应该是手误,IIC 数据传输速率有标准模式(100 kbps)、快速模式(400 kbps)和高速模式(3.4 Mbps)

 

因为所有的IIC器件都支持低速,但却未必支持另外两种速度,手册上查到AT24C02是可以支持400KHZ的,也就是说实际程序产生的时序必须小于等于 400k的时序参数,也就是要求SCL的高低电平持续时间都不短于1.25us(一个周期1/400K=2.5us),下图也是显示兼容400KHZ,所以原子哥代码有些地方使用4us延时有些地方使用2us延时应该是出于对数据稳定接收的考虑,所以代码的延时是完全没有问题的。如下图



在硬件上,IIC总线是由时钟总线SCL和数据总线SDA两条线构成,连接到总线上的所有器件的SCL都连到一起,所有SDA都连到一起。IIC总线是开漏引脚并联的结构,因此外部要添加上拉电阻。对于开漏电路外部加上拉电阻,就组成了线“与”的关系。总线上线“与”的关系就是说,所有接入的器件保持高电平,这条线才是高电平,而任何一个器件输出一个低电平,那这条线就会保持低电平,因此可以做到任何一个器件都可以拉低电平,也就是任何一个器件都可以作为主机,如下图的两个上拉电阻

EEPROM

在实际的应用中,保存在单片RAM中的数据,掉电后就丢失了,保存在单片机的FLASH 中的数据,又不能随意改变,也就是不能用它来记录变化的数值。但是在某些场合,又确实需要记录下某些数据,而它们还时常需要改变或更新,掉电之后数据还不能丢失,比如家用电表度数,电视机里边的频道记忆,一般都是使用 EEPROM 来保存数据,特点就是掉电后不丢失。板子上使用的这个器件是 24C02,是一个容量大小是2Kbits,也就是256个字节的EEPROM。一般情况下,EEPROM拥有30万到100万次的寿命,也就是它可以反复写入30-100万次,而读取次数是无限的。

 

下面是IIC的总线时序图



程序方面只要根据这个时序来进行编写就好了,感觉原子哥那样移位有点麻烦,下面更改了下原子哥的代码,感觉这样写会更好,哈哈….
[mw_shl_code=c,true]void IIC_Send_Byte(u8 txd) { u8 t;     SDA_OUT(); IIC_SCL=0; //拉低时钟开始数据传输     for(t=0x80; t!=0; t>>=1) //8次循环,每次右移一位 { IIC_SDA = (txd & t)?1:0; //三目运算符做判断 delay_us(2); IIC_SCL=1; delay_us(2); IIC_SCL=0; delay_us(2); } } [/mw_shl_code]

下面是自己对原子哥读24c02芯片代码的解析
[mw_shl_code=c,true]//在AT24CXX指定地址读出一个数据 //ReadAddr:开始读数的地址 //返回值 :读到的数据 u8 AT24CXX_ReadOneByte(u16 ReadAddr) //类型为u16原子哥是考虑通用性,因为容量大于2048地址字节就按16位操作,如下面的第一张图片所示 {     u8 temp=0; IIC_Start(); //IIC通信的起始信号(Start)后,首先要发送一个从机的地址,这个地址一共有 7位,紧跟着的第 8 位是数据方向位(R/W)     if(EE_TYPE>AT24C16) //判断容量     {     IIC_Send_Byte(0XA0); //发送写命令,A0由下面第二幅图得出 IIC_Wait_Ack(); IIC_Send_Byte(ReadAddr>>8);//发送高地址,容量大于2048字节按16位操作     }else IIC_Send_Byte(0XA0+((ReadAddr/256)<<1)); //发送器件地址0XA0,写数据,8位操作最后一位是RW位,不存在高地址     IIC_Wait_Ack(); // RW=0表示接下来要发送数据(写) IIC_Send_Byte(ReadAddr%256); //发送低地址     IIC_Wait_Ack();     IIC_Start(); //发送完读取的地址后进入接收模式     IIC_Send_Byte(0XA1); //进入接收模式, RW=1表示接下来是请求数据(读)     IIC_Wait_Ack(); temp=IIC_Read_Byte(0); IIC_Stop(); //产生一个停止条件     return temp; } [/mw_shl_code]




接下来讲讲自己修改原子哥的代码实现页写的功能,论坛上也找了下,自己没有找到有页写的代码,就自己写了一个,其实页写对于效率来说确实高了不少,读取 EEPROM 的时候很简单,EEPROM根据所送的时序,直接就把数据送出来了,但是写EEPROM却没有这么简单了。给EEPROM发送数据后,先保存在了 EEPROM的缓存, EEPROM 必须要把缓存中的数据搬移到“非易失”的区域,才能达到掉电不丢失的效果。而往非易失区域写需要一定的时间,每种器件不完全一样, ATMEL 公司的 24C02 的这个写入时间最高不超过 5ms。在往非易失区域写的过程, EEPROM 是不会再响应访问的,不仅接收不到数据,我们即使用 IIC 标准的寻址模式去寻址,EEPROM都不会应答,就如同这个总线上没有这个器件一样。数据写入非易失区域完毕后, EEPROM 再次恢复正常,可以正常读写了。 自己从英文手册上找到这句,如下图


在向 EEPROM 连续写入多个字节的数据时,如果每写一个字节都要等待几 ms 的话,整体上的写入效率就太低了。原子哥的代码利用了每写一次利用了10个ms的延时


如果每次写都等10个ms的话感觉会大大降低效率,因此 EEPROM 的厂商就想了一个办法,把 EEPROM 分页管理。24C01、 24C02 这两个型号是 8 个字节一个页,而 24C04、 24C08、 24C16 是 16 个字节一页。英文手册上也有提到这一点,如下图



开发板上用的型号是 24C02,一共是 256 个字节, 8 个字节一页,那么就一共有 32 页。如果在同一个页内连续写入几个字节后,最后再发送停止位的时序并进行一段延时。EEPROM 检测到这个停止位后,就会一次性把这一页的数据写到非易失区域,就不需要像单个字节那样写一个字节检测一次了,并且页写入的时间也不会超过 5ms。如果写入的数据跨页了,那么写完了一页之后,只要发送一个停止位,然后等待并且检测 EEPROM 的空闲,一直等到把上一页数据完全写到非易失区域后,再进行下一页的写入,这样就可以在很大程度上提高数据的写入效率。其实原子哥延时这10个ms也是可以避免的,一次写周期结束后再去写的时候判断是否能读到器件返回的ACK也是可以的,10ms感觉定死了,自己这个地方也没有改动,需要的话改一下就好了,下面手册上的说明也证实了这样写的优势,如下图



最后附上自己修改原子哥后的页写代码,其实就是加了加了换页的判断然后一次写入8字节,自己没有逻辑分析仪和示波器所以看不到这两段代码的时间差距,但是网上好像有个测试过,确实会快那么几个ms

[mw_shl_code=c,true]//在AT24CXX里面的指定地址开始写入长度为len的数据 //buf :数据数组首地址 //WriteAddr :开始写入的地址 //Len :要写入数据字节数 void AT24CXX_PageWrite(unsigned char *buf, unsigned int WriteAddr, unsigned char len) { while (len > 0) { IIC_Start(); if(EE_TYPE>AT24C16) { IIC_Send_Byte(0XA0); //发送写命令 IIC_Wait_Ack(); IIC_Send_Byte(WriteAddr>>8);//发送高地址 }else IIC_Send_Byte(0XA0+((WriteAddr/256)<<1)); //发送器件地址0XA0,写数据 IIC_Wait_Ack(); IIC_Send_Byte(WriteAddr%256); //发送低地址 IIC_Wait_Ack(); while(len>0) { IIC_Send_Byte(*buf++); //发送字节 IIC_Wait_Ack(); len--; WriteAddr++; if((WriteAddr&0x07)==0) //是否超过一页字节,一页8字节 { break; } } IIC_Stop(); //产生一个停止条件 delay_ms(10); //这段延时可以自己修改成判断ACK也行,自己懒就不想改了 } } [/mw_shl_code]


自己实现的效果哥原子哥的例程差不多就不上效果图,最后想说一句……..“有道翻译真累”,附上论坛大神"济世良驹"对IIC时序的详解,“前人栽树后人乘凉”非常感谢他做出这么好的PDF文档,还有个PPT的介绍也不错附在下面了

如何对AT24C02编写驱动程序——IIC总线协议.ppt

368 KB, 下载次数: 664

IIC总结.pdf

327.29 KB, 下载次数: 672

我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

2

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
62
金钱
62
注册时间
2015-11-26
在线时间
2 小时
发表于 2015-11-29 13:46:56 | 显示全部楼层
回复【120楼】229382777@qq.com:
---------------------------------
看了很长时间你的笔记 受教了!
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-11-29 13:49:42 | 显示全部楼层
回复【122楼】六子93:
---------------------------------
共勉,希望能一起进步
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

72

主题

2719

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
发表于 2015-11-29 17:37:19 | 显示全部楼层
回复【116楼】229382777@qq.com:
---------------------------------
看来用的是库函数了

很好,稳扎稳打,学得很扎实
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2719

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
发表于 2015-11-29 18:01:23 | 显示全部楼层
回复【121楼】229382777@qq.com:
---------------------------------
很好

原子哥用的发送字节函数也是大家普遍使用的,简单明了

楼主用到了右移和三目运算符进行替换,于我个人认为,初学者禁用,C语言一般的少用,同时不得不说方法也很巧妙,体现出了扎实的C语言功底
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-11-29 18:05:05 | 显示全部楼层
回复【125楼】龙之谷:
---------------------------------
基本上我库函数和寄存器都看,有时候更新用库函数有时候用寄存器,但是都会去学,三目运算符其实也考虑了下用不用,其实用if判断也不错,循环移位是我感觉比较好用的
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

72

主题

2719

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
发表于 2015-11-29 18:09:43 | 显示全部楼层
回复【126楼】229382777@qq.com:
---------------------------------
我说的“初学者禁用,C语言一般的少用”是说给看你帖子的初学者的,你现在水平是看心情随便用
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-11-29 18:17:26 | 显示全部楼层
回复【127楼】龙之谷:
---------------------------------
还是龙哥考虑的周到。。。多谢你那么认真看我的贴
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手入门

积分
27
金钱
27
注册时间
2015-9-4
在线时间
1 小时
发表于 2015-11-29 20:57:06 | 显示全部楼层
真心很不错,期待楼主更新
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-11-30 09:30:23 | 显示全部楼层
回复【129楼】wangjin:
---------------------------------
谢谢支持。。。
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

7

主题

63

帖子

0

精华

初级会员

Rank: 2

积分
142
金钱
142
注册时间
2013-1-23
在线时间
4 小时
发表于 2015-11-30 09:43:04 | 显示全部楼层
佩服楼主的毅力,很多事情都是贵在坚持!而且楼主能够主动深挖一些看似很普通的没什么要解释的东西(比如LED发光原理,按键原理等这些),这对以后的学习会有很大帮助,
顶!
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-11-30 11:12:22 | 显示全部楼层
回复【131楼】浮生长恨:
---------------------------------
谢谢,共勉!
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-12-1 20:39:12 | 显示全部楼层

实战:SPI

 

简介:

SPI 是英语 Serial Peripheral interface 的缩写,顾名思义就是串行外围设备接口。是 Motorola首先在其 MC68HCXX 系列处理器上定义的。 SPI 接口主要应用在 EEPROM, FLASH,实时时钟, AD 转换器,还有数字信号处理器和数字信号解码器之间。 SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为 PCB 的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议, STM32 也有 SPI 接口。


SPI 主要特点有
可以同时发出和接收串行数据; 可以当作主机或从机工作; 提供频率可编程时钟; 发送结束中断标志; 写冲突保护; 总线竞争保护等。

 

本章的内容自己遇到很多卡住的地方,所以耽误的时间也比较多,首先是读写函数,还有就是看英文手册遇到些不懂的地方,但最后还是都通过查找资料解决了,下面先把这章要配置的寄存器做下笔记

 

设置寄存器

SPI1->CR1|=0<<10;//全双工模式 

SPI1->CR1|=1<<9; //软件nss管理

SPI1->CR1|=1<<8; //NSS高电平

SPI1->CR1|=1<<2; //SPI主机(主设备)

SPI1->CR1|=0<<11;//8bit数据格式       

SPI1->CR1|=1<<1; //空闲模式下SCK1 CPOL=1

SPI1->CR1|=1<<0; //数据采样从第二个时间边沿开始,CPHA=1 

SPI1->CR1|=7<<3; //Fsck=Fcpu/256

SPI1->CR1|=0<<7; //MSBfirst  

SPI1->CR1|=1<<6; //SPI设备使能


设置库函数

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工

SPI_InitStructure.SPI_Mode = SPI_Mode_Master;           //设置SPI工作模式:设置为主SPI

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;       //设置SPI的数据大小:SPI发送接收8位帧结构

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;             //选择了串行时钟的稳态:时钟悬空高

SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;          //数据捕获于第二个时钟沿

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;       //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;            //定义波特率预分频的值:波特率预分频值为256

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;           //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始

SPI_InitStructure.SPI_CRCPolynomial = 7;                      //CRC值计算的多项式

SPI_Init(SPI1, &SPI_InitStructure);            //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器


对应手册寄存器

SPI控制寄存器 1(SPI_CR1)




SPI
状态寄存器(SPI_SR)



SPI
状态寄存器(SPI_SR)



硬件部分:

HOLD为高时可以多设备共享一个SPI信号,WP(写保护)为低电平有效,因为本章需要往里面写数据所以接高电平,如下图解释








这是原子哥代码设置CPOL=1;CPHA=1;的时序图



本章最主要的函数,读写函数,一开始没在意为什么读写会放在一起,后面论坛查了后才发现有人注意到这一点,并且说明了原因,坛友给的解释是“做主机时会控制通信的时钟,从机是不能产生时钟的。如果从机要发送数据,那可以在主机发送数据 的时钟上发送数据。”下面是原子哥读写程序的代码
[mw_shl_code=c,true]//SPIx 读写一个字节 //TxData:要写入的字节 //返回值:读取到的字节 u8 SPI1_ReadWriteByte(u8 TxData) { u8 retry=0; while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位 { retry++; if(retry>200)return 0; } SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个数据 retry=0; while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)//检查指定的SPI标志位设置与否:接受缓存非空标志位 { retry++; if(retry>200)return 0; } return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据 } [/mw_shl_code]


W25Q64 将 8M 的容量分为 128 个块( Block),每个块大小为 64K 字节,每个块又分为 16个扇区( Sector),每个扇区 4K 个字节。 W25Q64 的最少擦除单位为一个扇区,也就是每次必须擦除 4K 个字节。这样我们需要给 W25Q64 开辟一个至少 4K 的缓存区,这样对 SRAM 要求比较高,要求芯片必须有 4K 以上 SRAM 才能很好的操作。

 

原子哥代码的写入是按页来写的,一页256个字节,并且最大只能连续写256个字节,从下图可以看出



原子哥代码里面最主要的函数是“SPI_Flash_Write”,这个函数的逻辑写的非常好,是自己该学习的地方,函数首先是计算出地址所在的扇区,一个扇区4096个字节,然后是计算在扇区中的偏移,最后计算扇区剩余空间的大小以方便后面字节写入到下一扇区空间。循环里面先读出整个扇区的内容进行校验是否有不等于0xff的,如果有进行整个扇区的擦除,没有的话直接写入扇区剩余空间,剩余空间不够写的话进行扇区地址的递增并把偏移地址设为0,如此循环直到把数据全部送完。看到原子哥擦除扇区的代码有等待busy的,查了下手册,只要是画红线部分的都需要等待,所以原子哥代码的页写、扇区擦除都有等待busy的,但是看到“while ((SPI_Flash_ReadSR()&0x01)==0x01); ”等待空闲里面的0x01时没有特别说明,如第一幅图 有说到在S0位也就是最低位,加上读取状态寄存器在时序上是先读高位的,所以得出以上那句判断。





之后自己测试了下NSS硬件模式和软件模式,将“SPI1->CR1|=1<<9; //软件nss管理”这句去掉,设置为硬件模式,代码可以正常运行,但是这种模式下1个SPI只能接1个从机,后面论坛查找了下,找到原子哥有帮网友回答的一个关于软件NSS和硬件的解释,如下:

1,硬件NSS,是指SPI自动控制SPI的片选信号,发送数据的时候,输出低电平,不发送的时候,是高电平,这个模式一般不用.因为这种方式只能1个SPI接1个从机,很是蛋疼. 

2,软件模式就是完全软件控制SPI片选,就是一个普通IO控制,你要SPI通信之前,必须先用软件的方式,控制SPI从机的片选为低电平,然后在发送数据.发完后,拉高. 一般用这个模式,因为可以一个SPI控制N多个从机

 
这里还是存在点疑问,“发送数据的时候,输出低电平,不发送的时候,是高电平”这个是一定的吗?我找不到手册中有解释的地方,另外自己如果只单纯改为硬件模式其他代码原封不动,也就是去掉“SPI1->CR1|=1<<9; //软件nss管理”这句程序照常运行,但是如果我将“GPIOA->ODR|=0X7<<2;    //PA2.3.4 输出高”这句的”0x7”改为”0x0”后就一直显示芯片检测失败,照理说由硬件控制的话这里设置NSS引脚输出0应该不影响吧?麻烦原子哥这里可以解释一下

 

下图是有关NSS两种模式的



最后说下两个概念

SPI从模式:SCK引脚用于接收从主设备来的串行时钟

SPI主模式:SCK脚产生串行时钟


我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-12-1 21:10:03 | 显示全部楼层
能麻烦“原子哥”看下这个地方吗?也就是上面红色字体,直接提出来了, 如下

自己测试了下NSS硬件模式和软件模式,将“SPI1->CR1|=1<<9; //软件nss管理”这句去掉,设置为硬件模式,代码可以正常运行,但是这种模式下1个SPI只能接1个从机,后面论坛查找了下,找到原子哥有帮网友回答的一个关于软件NSS和硬件的解释,如下:

1,硬件NSS,是指SPI自动控制SPI的片选信号,发送数据的时候,输出低电平,不发送的时候,是高电平,这个模式一般不用.因为这种方式只能1个SPI接1个从机,很是蛋疼. 

2,软件模式就是完全软件控制SPI片选,就是一个普通IO控制,你要SPI通信之前,必须先用软件的方式,控制SPI从机的片选为低电平,然后在发送数据.发完后,拉高. 一般用这个模式,因为可以一个SPI控制N多个从机

 
这里还是存在点疑问,“发送数据的时候,输出低电平,不发送的时候,是高电平”这个是一定的吗?我找不到手册中有解释的地方,另外自己如果只单纯改为硬件模式其他代码原封不动,也就是去掉“SPI1->CR1|=1<<9; //软件nss管理”这句程序照常运行,但是如果我将“GPIOA->ODR|=0X7<<2;    //PA2.3.4 输出高”这句的”0x7”改为”0x0”后就一直显示芯片检测失败,照理说由硬件控制的话这里设置NSS引脚输出0应该不影响吧?麻烦原子哥这里可以解释一下吗?谢谢
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

72

主题

2719

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
发表于 2015-12-2 01:09:49 | 显示全部楼层
回复【133楼】229382777@qq.com:
---------------------------------
很好,资料查阅很到位

NSS不熟悉,引脚低电平是选中元件信号,既然选择硬件NSS就不要输出低电平干扰硬件了
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-12-2 07:53:59 | 显示全部楼层
回复【135楼】龙之谷:
---------------------------------
恩,主要想验证下,NSS硬件模式确实用的不多
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手入门

积分
41
金钱
41
注册时间
2015-10-26
在线时间
5 小时
发表于 2015-12-2 22:27:24 | 显示全部楼层
强强强,看了楼主的学习过程,实在佩服
回复 支持 反对

使用道具 举报

18

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
152
金钱
152
注册时间
2015-4-11
在线时间
0 小时
发表于 2015-12-3 00:32:25 | 显示全部楼层
回复【85楼】229382777@qq.com:
---------------------------------
怎么显示彩色艺术字体呢?
while{努力};
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-12-3 07:52:17 | 显示全部楼层
回复【138楼】未来的大神:
---------------------------------
每显示一点的时候换一种颜色,在显示字符函数里面加个颜色的数组,每进一次字符函数加1,不就是彩色了吗....116楼的代码是带彩色和艺术字的,你可以看一下
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

1

主题

9

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
241
金钱
241
注册时间
2015-5-11
在线时间
51 小时
发表于 2015-12-3 11:06:16 | 显示全部楼层
如此精彩,怎能不顶。 加油
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-12-3 11:09:25 | 显示全部楼层
回复【140楼】行星电子:
---------------------------------
谢谢支持!
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
21
金钱
21
注册时间
2015-12-3
在线时间
0 小时
发表于 2015-12-3 12:44:07 | 显示全部楼层
顶一下。
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-12-3 18:45:48 | 显示全部楼层

实战:触摸屏

 

简介:

电阻触摸屏的主要部分是一块与显示器表面非常配合的电阻薄膜屏,这是一种多层的复合薄膜,它以一层玻璃或硬塑料平板作为基层,表面涂有一层透明氧化金属(透明的导电电阻)导电层,上面再盖有一层外表面硬化处理、光滑防擦的塑料层、它的内表面也涂有一层涂层、在他们之间有许多细小的(小于 1/1000 英寸)的透明隔离点把两层导电层隔开绝缘。 当手指触摸屏幕时,两层导电层在触摸点位置就有了接触,电阻发生变化,在 X 和 Y 两个方向上产生信号,然后送触摸屏控制器。控制器侦测到这一接触并计算出( X, Y)的位置,再根据获得的位置模拟鼠标的方式运作。这就是电阻技术触摸屏的最基本的原理。



XPT2046
XPT2046 是一款4导线制触摸屏控制器,内含 12 位分辨率 125KHz 转换速率逐步逼近型 A/D 转换器。XPT2046 支持从1.5V到5.25V的低电压I/O接口。XPT2046能通过执行两次A/D转换查出被按的屏幕位置,除此之外,还可以测量加在触摸屏上的压力。内部自带 2.5V 参考电压可以作为辅助输入、温度测量和电池监测模式之用,电池监测的电压范围可以从0V到6V。 XPT2046 片内集成有一个温度传感器。 在 2.7V 的典型工作状态下,关闭参考电压,功耗可小于 0.75mW。XPT2046 采用微小的封装形式:TSSOP-16,QFN-16(0.75mm 厚度)和 VFBGA-48。工作温度范围为-40℃~+85℃。下图为原理




这章的例程代码量有点大,需要仔细查对手册才能知道触摸屏的操作是如何实现的,其中最重要的部分应该就是电阻屏的校准程序了,下面是原子哥教程中解释的,为什么需要校准?如下



上图中很清楚的解释了为什么需要校准,一开始自己也不是很懂上面的公式,后面看了代码后慢慢才明白,先从XPT2046读出4个坐标位置,这4个坐标是物理坐标,不是实际的坐标,需要转换为屏幕尺寸的对应坐标,在原子哥代码里面计算比例因子是这样的,用屏幕实际宽度去除以从屏幕上定好的两个坐标之差,这两个定好的坐标分别放在屏幕的两个个角落,读取出它们的物理值后,进行除法运算便可以知道它们的比例因子,可能这个专业名词也不太好理解,其实就是实际坐标值的点对应物理坐标上的点数,原子哥的代码是这样的 
tp_dev.xfac=(float)(lcddev.width-40)/(pos_temp[1][0]-pos_temp[0][0]);//得到xfac,x轴方向的比例因子"  这里还有个宽度减去40,其实一开始看了半天不知道这个40是干什么的,后面想了一下其实校准点是画在坐标为(20,20) TP_Drow_Touch_Point(20,20,RED);” ,减去两个点的距离后刚好是40,接着代码中还有一个公式是
tp_dev.xoff=(lcddev.width-tp_dev.xfac*(pos_temp[1][0]+pos_temp[0][0]))/2;//得到xoff ,x轴的方向偏移量” 这个是计算偏移量的,这句理解起来也挺困难,其实下载例程后就明白了,自己设4个校准点,只要符合fac的要求就行了,如果自己设的不是全屏而是一个小矩形的范围,可以发现在校准的小矩形范围内画图的话会画到外面部分,这也就是偏移产生的,再仔细看看结构体的定义,这两个 short  xoff;  short  yoff; 定义的都是short“无符号型”,因为有可能产生负数,当你在小矩形范围内画图的时候如果xoff是负数会减去xoff,所以画到了外面的地方。因此实际的LCD坐标xy可以通过乘以比例因子加上偏移量得出。理解完这里似乎整个校准代码其他地方也不难理解了。        

 

本章用的是模拟的SPI时序写的代码,下面是代码和解析
[mw_shl_code=c,true]//SPI写数据 //向触摸屏IC写入1byte数据 //num:要写入的数据 void TP_Write_Byte(u8 num) { u8 count=0; for(count=0;count<8;count++) { if(num&0x80)TDIN=1; else TDIN=0; num<<=1; TCLK=0; TCLK=1; //上升沿有效 } } //SPI读数据 //从触摸屏IC读取adc值 //CMD:指令 //返回值:读到的数据 u16 TP_Read_AD(u8 CMD) { u8 count=0; u16 Num=0; TCLK=0; //先拉低时钟 TDIN=0; //拉低数据线 TCS=0; //选中触摸屏IC TP_Write_Byte(CMD); //发送命令字 delay_us(6); //ADS7846的转换时间最长为6us TCLK=0; delay_us(1); TCLK=1; //给1个时钟,清除BUSY TCLK=0; for(count=0;count<16;count++)//读出16位数据,只有高12位有效 { Num<<=1; TCLK=0; //下降沿有效 TCLK=1; if(DOUT)Num++; } Num>>=4; //只有高12位有效. TCS=1; //释放片选 return(Num); } [/mw_shl_code]
上面两个函数一个是SPI写函数和XPT2046读取模数转换值函数,这两个函数可以通过时序图推出来,如下图,首先上面TP_Write_Byte函数有句高电平有效,可以看出DCLK在上升沿的时候DIN引脚的数据是稳定的没有变化,而在DCLK变下降沿的时候数据是变化的,故可推出上升沿写数据有效,再看下后面TP_Read_AD函数,那个延时6us实在有点不能理解,查了下XPT2046的转换速率是125Khz,那转换一次至少需要8us,但是手册又有说转换时间为12个时钟周期,具体我不知道一个时钟周期为多少,后面有一个延时1us,根据手册可以得知最大为200个ns,所以这里延时1us完全没有问题,后面由于这个ADC是12位精度的,读到的首12位为AD值,所以读出16位后取出高12位返回就行了。




原子哥代码里面还有两个出现频率很高的指令“u8  CMD_RDX=0XD0;  u8  CMD_RDY=0X90;”这两个指令手册中有提到,分别是获取X,Y坐标值的,如下图





最后看下硬件部分的连接


这里比较显眼的就是T_PEN接了个上拉电阻,至于为什么接上拉电阻是因为一旦有触摸按下会产生笔中断,会拉低T_PEN引脚,下图可以看出这个引脚是也是低电平才有效



总结一下,这章的代码量比较大,而且感觉代码部分对于初学者来说还是比较难啃的,如果仔细对着资料和手册来学习的话还是能看懂的,但是要写出这样的驱动代码还是需要时间的积累。
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-12-3 18:57:02 | 显示全部楼层
后面会过掉几个硬件外设的部分,因为自己没有那些外设,但是学到后面会考虑买这些外设来学习一下,到时候再来学这几章
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

72

主题

2719

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
发表于 2015-12-4 15:20:49 | 显示全部楼层
回复【143楼】229382777@qq.com:
---------------------------------
不错,第一张图片挂了.....
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-12-4 15:42:17 | 显示全部楼层
回复【145楼】龙之谷:
---------------------------------
晕,估计网址复制过来的图片不行,我自己可以看到,现在又截图了一遍,应该没问题了
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

15

主题

787

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3222
金钱
3222
注册时间
2015-7-26
在线时间
811 小时
 楼主| 发表于 2015-12-4 18:24:37 | 显示全部楼层
实战:FLASH模拟EEPROM










需要使用的寄存器:

键寄存器FPEC(FLASH_KEYR)
选择字节键寄存器(FLASH_OPTKEYR)
闪存控制寄存器(FLASH_CR)
闪存状态寄存器(FLASH_SR)
闪存地址寄存器(FLASH_AR)
选择字节寄存器(FLASH_OBR)
写保护寄存器(FLASH_WRPR)

 

主要代码:

[mw_shl_code=c,true]u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];//最多是2K字节 void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) { u32 secpos; //扇区地址 u16 secoff; //扇区内偏移地址(16位字计算) u16 secremain; //扇区内剩余地址(16位字计算) u16 i; u32 offaddr; //去掉0X08000000后的地址 if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址 STMFLASH_Unlock(); //解锁 offaddr=WriteAddr-STM32_FLASH_BASE; //实际偏移地址. secpos=offaddr/STM_SECTOR_SIZE; //扇区地址 0~127 for STM32F103RBT6 secoff=(offaddr%STM_SECTOR_SIZE)/2; //在扇区内的偏移(2个字节为基本单位.) secremain=STM_SECTOR_SIZE/2-secoff; //扇区剩余空间大小 if(NumToWrite<=secremain)secremain=NumToWrite;//不大于该扇区范围 while(1) { STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容 for(i=0;i<secremain;i++)//校验数据 { if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除 } if(i<secremain)//需要擦除 { STMFLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除这个扇区 for(i=0;i<secremain;i++)//复制 { STMFLASH_BUF[i+secoff]=pBuffer; } STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区 }else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间. if(NumToWrite==secremain)break;//写入结束了 else//写入未结束 { secpos++; //扇区地址增1 secoff=0; //偏移位置为0 pBuffer+=secremain; //指针偏移 WriteAddr+=secremain*2; //写地址偏移(16位数据地址,需要*2) NumToWrite-=secremain; //字节(16位)数递减 if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完 else secremain=NumToWrite;//下一个扇区可以写完了 } }; STMFLASH_Lock();//上锁 } [/mw_shl_code]

本章的内容方面比较少,主要放一些资料方便自己以后查看,其中整段代码都以写FLASH为中心,这段代码和之前的SPI读写W25Q64很相似,不过之前的W25Q64是外置FLASH,容量为8MB,而内置的只有256KB,属于大容量。还有句很关键的代码 #define FLASH_SAVE_ADDR  0X08020000      //设置FLASH 保存地址(必须为偶数,且其值要大于本代码所占用FLASH的大小+0X08000000) 这句直接将写入的地址限在了131KB外的地址,这里的设置只需要比代码所占的容量小就行了,大了会覆盖用户代码导致程序错误。本章库函数确实比寄存器操作轻松很多,期间看到篇原子哥写的关于堆栈的酷帖,地址在下面

http://www.openedv.com/posts/list/24152.htm
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

14

主题

1593

帖子

0

精华

资深版主

Rank: 8Rank: 8

积分
2615
金钱
2615
注册时间
2014-7-17
在线时间
348 小时
发表于 2015-12-4 21:36:37 | 显示全部楼层
cool....
回复 支持 反对

使用道具 举报

2

主题

3

帖子

0

精华

新手入门

积分
31
金钱
31
注册时间
2015-7-11
在线时间
0 小时
发表于 2015-12-4 21:41:54 | 显示全部楼层
子从排版、内容质量都很棒,坚持下去必有很大提升~~~~~ 

楼主作息很规律啊,我都计划好几个月作息依然艰难于执行,向你学习
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-6-10 20:38

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表