Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1301206
  • 博文数量: 548
  • 博客积分: 7597
  • 博客等级: 少将
  • 技术积分: 4224
  • 用 户 组: 普通用户
  • 注册时间: 2010-12-15 13:21
个人简介

嵌入式软件工程师&&太极拳

文章分类

全部博文(548)

文章存档

2014年(10)

2013年(76)

2012年(175)

2011年(287)

分类: LINUX

2013-08-01 15:09:45

 

一天天学用IAR+STM8——PWM

 

开发板上的LED1接在了PD3上,而PD3复用功能是TIM2_CC2,正好可以用来测试PWM功能。当然板上的另外2LED也可以用,LED2对应PD2/TIM3_CC1LED3对应PD0/TIM3_CC2。本例程通过电位器来调整LED1的亮度。

 

i nclude

 

void CLK_init(void)

{

  CLK_CKDIVR = 0x08;         // 16M内部RC2分频后系统时钟为8M

}

 

void GPIO_init(void)

{

  PD_DDR = 0x08;             // 配置PD端口的方向寄存器PD3输出

  PD_CR1 = 0x08;             // 设置PD3为推挽输出

}

 

void TIM2_init(void)

{

  TIM2_CCMR2 = 0x70;         // PWM 模式 2

  TIM2_CCER1 = 0x30;         // CC2配置为输出

  TIM2_ARRH = 0x03;           // 配置PWM分辨率为10位,ARR=0x3FF

  TIM2_ARRL = 0xFF;           // PWM频率=8M/0x03FF=7820Hz

  TIM2_CR1 = 0x01;           // 计数器使能,开始计数

}

 

void ADC_init(void)

{

  ADC_CSR = 0x03;            // ADC输入引脚AIN3

  ADC_CR1 = 0x01;            // ADC开启

}

 

Run(void)

{

  unsigned int value;

  ADC_CSR &= ~0x80;       // 清除EOC转换结束标志

  ADC_CR1 |= 0x01;           // 开始单次转换

  while(!(ADC_CSR&0x80));    // 等待单次转换完成

  value = ((int)ADC_DRH<<2); // 先读高8位,默认设置数据左对齐

  value |= ADC_DRL;                 // 再读低8

  TIM2_CCR2H = (unsigned char)(value>>8);   // 更新CC2比较寄存器

  TIM2_CCR2L = (unsigned char)(value);

}

 

void init_devices(void)

{

  asm("sim");

  CLK_init();

  GPIO_init();

  TIM2_init();

  ADC_init();

  asm("rim");

}

 

void main( void )

{

  init_devices();

 

  while(1)    Run();

}

 


一天天学用IAR+STM8——ADC模数转换器

 

  今天有空来继续写学习笔记。STM8片上集成了10位逐次比较型模数转换器,在开发板上有个电位器接到了AIN3,但没有可以显示数据的LED数码管或LCD液晶显示屏,怎么办呢?通过前面的学习,这个问题不难解决,在这里可以把AD采集数据通过UART发送到电脑上显示。

 

i nclude

 

void CLK_init(void)

{

  CLK_CKDIVR = 0x08;         // 16M内部RC2分频后系统时钟为8M

}

 

void GPIO_init(void)

{

  PD_DDR = 0x08;             // 配置PD端口的方向寄存器PD3输出

  PD_CR1 = 0x08;             // 设置PD3为推挽输出

  PD_CR2 = 0x80;             // 使能PD7外部中断

}

 

void EXTI_init(void)

{

  EXTI_CR1 = 0x80;          // PD口下降沿触发中断

}

 

#pragma vector=0x02

__interrupt void EXTI_PD7_TLI(void)

{

  unsigned int value;

  ADC_CSR &= ~0x80;         // 清除EOC转换结束标志

  ADC_CR1 |= 0x01;           // 开始单次转换

  while(!(ADC_CSR&0x80));    // 等待单次转换完成

  value = ((int)ADC_DRH<<2);  // 先读高8位,默认设置数据左对齐

  value |= ADC_DRL;          // 再读低8

  while(!UART3_SR_TXE);

  UART3_DR = value;         // 通过UART3发送AD采样结果

  while(!UART3_SR_TXE);

  UART3_DR = value>>8;

}

 

void ADC_init(void)

{

  ADC_CSR = 0x03;           // ADC输入引脚AIN3

  ADC_CR1 = 0x01;           // ADC开启

}

 

void UART3_init(void)

{

  UART3_BRR2 = 0x01;

  UART3_BRR1 = 0x34;         // 8M/9600 = 0x341

  UART3_CR2 = 0x0C;          // 允许接收,允许发送

}

 

void init_devices(void)

{

  asm("sim");

  CLK_init();

  GPIO_init();

  EXTI_init();

  ADC_init();

  UART3_init();

  asm("rim");

}

 

void main( void )

{

  init_devices();

 

// 主循环里没有程序需要执行

  while(1);

}

 

在本例中仅使用了ADC的单次转换模式,每按一次按键进行一次AD转换,并把转换结果通过UART发送,这样可以在电脑上通过超级终端或串口调试软件查看数据。

 

通过这次测试,还发现一个现象,如果把清除EOC转换结束标志放在读取数据之后,那么在下次启动单次转换后,EOC标志位会自动置位,此时必须人为的等待7us后才能读到正确的数据,否则只能读到上一次的转换数据,可能这是STM8ADC与其他MCU不同之处。

 


一天天学用IAR+STM8——EXTI外部中断控制寄存器

 

 这块三合一的开发板上有且只有一个按键,没办法,就拿这唯一的按键来用吧。吸取前面UART3的教训,先看开发板的原理图吧。这个按键被接到了STM8S207SBPD7上,已做了上拉处理。为了简单明了,还是点LED1吧。按一下LED1亮,再按一下LED1灭。好了,写程序吧。

 

i nclude

 

#define LED1_FLASH  PD_ODR_ODR3 =  !PD_ODR_ODR3  // 开发板上的LED1接在PD3

 

void GPIO_init(void)

{

  PD_DDR = 0x08;             // 配置PD端口的方向寄存器PD3输出

  PD_CR1 = 0x08;             // 设置PD3为推挽输出

  PD_CR2 = 0x80;             // 使能PD7外部中断

}

 

void EXTI_init(void)

{

  EXTI_CR1 = 0x80;           // PD口下降沿触发中断

}

 

#pragma vector=0x02      // 这里很关键!看下面说明。

__interrupt void EXTI_PD7_TLI(void)

{

  LED1_FLASH;

}

void init_devices(void)

{

  asm("sim");  // 关全局中断

  GPIO_init();

  EXTI_init();

  asm("rim");  // 开全局中断

}

 

void main( void )

{

  init_devices();

 

// 主循环里没有程序需要执行

  while(1);

}

 

  这里着重要说明的一点是PD7的外部中断程序。看了一下芯片手册,PD口外部中断EXTI3的中断向量号是6,想当然,又是想当然,按IAR的规矩中断向量要加2,就这样写#pragma vector=0x08,结果就是按下按键,程序没响应了,一直在中断里不出来。接下来只能另想办法,仔细翻了资料后发现,PD7PD其他端口不一样,PD7后面拖了个小尾巴TLI,再看手册上的TLI描述,乖乖,TLI拥有芯片最高级别中断,享有独立专用的中断向量号0,这下就好办了,按IAR的规矩,向量号加2,程序改成#pragma vector=0x02,重新来一遍编译、下载、运行,按键终于听话了。


一天天学用IAR+STM8——UART串口发送与中断接收

 STM8UART功能强大,除了常规的串口异步通讯外,还拥有LIN主模式、红外编解码器、智能卡模拟等功能。新手还是从基本的串口通讯入手,那些高级功能等熟练以后再慢慢研究吧。

 

i nclude

 

void CLK_init(void)

{

  CLK_CKDIVR = 0x08;         // 16M内部RC2分频后系统时钟为8M

}

 

void UART3_init(void)

{

  UART3_BRR2 = 0x01;         // 设置波特率9600

  UART3_BRR1 = 0x34;         // 8M/9600 = 0x341

  UART3_CR2 = 0x2C;          // 允许接收中断,允许接收,允许发送

}

 

#pragma vector=UART3_R_RXNE_vector

__interrupt void UART3_RX_RXNE(void)

{

  unsigned char c;

  c = UART3_DR;          // 接收到数据了

  while(!UART3_SR_TXE);

  c++;                              // 把接收到的数据加1

  UART3_DR = c;         // 再发回去,为什么这么做?在电脑上串口调试软件里发1就收到2,发A就收到B,看运行结果比较方便

}

 

void init_devices(void)

{

  asm("sim");  // 关全局中断

  CLK_init();

  UART3_init();  // 开发板上的串口接的是UART3,刚开始想当然的认为UART1,浪费我半个小时

  asm("rim");  // 开全局中断

}

 

void main( void )

{

  init_devices();

 

// 主循环里没有程序需要执行

  while(1);

}

 

IAR提供了UART3_SR_RXNE这样的位变量来判断是否收到数据,应该说很容易使用查询方式接收UART数据,但实际测试中发现,当使用while(!UART3_SR_RXNE);来等待接收数据时,程序并没有在while循环里一直等下去,而是在循环若干次后退出了,但UART并没有收到任何数据。

经测试,只有使用while(!(UART3_SR&0x20);才能正确判断是否接收到数据。

这个问题的具体原因要查看IAR编译后的汇编代码来分析。

while(!(UART3_SR & 0x20);

??ReceivedChar_0:

LD A, #0x20

AND A, L:0x5240

TNZ A

JREQ L:??ReceivedChar_0

 

while(!UART3_SR_RXNE);

??ReceivedChar_0:

LD A, L:0x5240

CALL L:?srl8_a_a_6 -->这个是做什么?

AND A, #0x1

TNZ A

JREQ L:??ReceivedChar_0

 

CALL L:?srl8_a_a_6究竟做了什么?lst文件中没找到srl8_a_a_6,动用仿真器跟踪吧!

:?srl8_a_a_6

SRL A

SRL A

SWAP A

AND A, #?b15

RET

对比一下汇编代码,区别还是很大的。从代码分析结果来看,还是前一种方式更直观、简洁。


一天天学用IAR+STM8——TIM1定时溢出中断

 

推荐STM8TIM116位高级控制定时器,作为新手不要急着玩高级功能,先从简单的定时溢出中断开始。那么这个简单的目标就定为LED1500ms,灭500ms,循环往复,如此而已。

i nclude

 

#define LED1_FLASH  PD_ODR_ODR3 =  !PD_ODR_ODR3  // 开发板上的LED1接在PD3

 

void CLK_init(void)

{

  CLK_CKDIVR = 0x08;         // 16M内部RC2分频后系统时钟为8M

}

 

void GPIO_init(void)

{

  PD_DDR = 0x08;  // 配置PD端口的方向寄存器PD3输出

  PD_CR1 = 0x08;  // 设置PD3为推挽输出

}

 

void TIM1_init(void)

{

  TIM1_PSCRH = 0x1F;  // 8M系统时钟经预分频f=fck/(PSCR+1)

  TIM1_PSCRL = 0x3F;  // PSCR=0x1F3Ff=8M/(0x1F3F+1)=1000Hz,每个计数周期1ms

  TIM1_ARRH = 0x01;  // 自动重载寄存器ARR=0x01F4=500

  TIM1_ARRL = 0xF4;  // 每记数500次产生一次中断,即500ms

  TIM1_IER = 0x01;    // 允许更新中断

  TIM1_CR1 = 0x01;  // 计数器使能,开始计数

}

 

#pragma vector=TIM1_OVR_UIF_vector

__interrupt void TIM1_OVR_UIF(void)

{

  LED1_FLASH;

  TIM1_SR1 = 0;  // 清除更新中断标记,这步不能漏掉,否则会连续进入中断程序

}

 

void init_devices(void)

{

  asm("sim");  // 关全局中断

  CLK_init();

  GPIO_init();

  TIM1_init();

  asm("rim");  // 开全局中断

}

 

void main( void )

{

  init_devices();

 

// 主循环里没有程序需要执行

  while(1);

}

 

好了,同样编译、下载、运行,看结果吧。

 


一天天学用IAR+STM8——系统时钟

 STM8上电运行时默认使用内部16MRC振荡器经8分频后的2M时钟频率作为系统时钟。程序开始运行后可以通过设置相关寄存器来修改主时钟源,可以选择外部晶振作为主时钟源和CPU时钟分频。那么这里就选择比较简单的操作,修改内部RC时钟预分频器获得8M系统时钟。

 

增加内部RC时钟预分频后的代码如下:

 

i nclude

 

#define LED1_FLASH  PD_ODR_ODR3 =  !PD_ODR_ODR3  // 开发板上的LED1接在PD3

 

void delay(unsigned int count)

{

  while(count--);

}

 

void CLK_init(void)

{

  CLK_CKDIVR = 0x08;         // 16M内部RC2分频后系统时钟为8M

}

 

void GPIO_init(void)

{

  PD_DDR = 0x08;  // 配置PD端口的方向寄存器PD3输出

  PD_CR1 = 0x08;  // 设置PD3为推挽输出

}

 

void init_devices(void)

{

  CLK_init();

  GPIO_init();

}

 

void main( void )

{

  init_devices();

  while(1)

  {

    delay(50000);

    LED1_FLASH;

  }

}

编译后运行一下看看,LED1是不是闪得更快了

 

一天天学用IAR+STM8——GPIO

 第二天,从最基本的IO操作开始学习。在STMIO绝大多数是GPIO

 

刚开始学习,测试程序不要搞复杂,越简单越不容易出错。下面是代码,没有使用STM8官方固件库。

 

// GPIO测试

 

i nclude

 

#define LED1_FLASH  PD_ODR_ODR3 =  !PD_ODR_ODR3  // 开发板上的LED1接在PD3

 

void delay(unsigned int count)

{

  while(count--);

}

 

void GPIO_init(void)

{

  PD_DDR = 0x08;  // 配置PD端口的方向寄存器PD3输出

  PD_CR1 = 0x08;  // 设置PD3为推挽输出

}

 

void init_devices(void)

{

  GPIO_init();

}

 

void main( void )

{

  init_devices();

  while(1)

  {

    delay(50000);

    LED1_FLASH;

  }

}

 

 

 

阅读(3474) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~