Após escrever "Zigbee Journey (9)" em 3 de março, o autor originalmente planejava começar imediatamente a escrever um pequeno experimento sobre "sistema de monitoramento de temperatura" como um resumo de uma série de pontos de conhecimento dispersos anteriores. No entanto, também percebi que, embora cada um dos pequenos experimentos anteriores tenha sido descrito em detalhes, a natureza normativa e estrutural de seu código pode ser considerada insuportável. Como é um resumo, devemos avançar com base na base original, em vez de juntar mecanicamente os pequenos pontos de conhecimento anteriores. Por isso, coloquei meu plano original em pausa, tirei um tempo para aprender as técnicas gerais de desenvolvimento embarcado e escrevi dois ensaiosEspecificação de programação C51 embutida" e "Hierarquia da estrutura de código de projeto embarcada》。 Este registro não é apenas um resumo da primeira viagem do Zigbee, mas também incorpora a experiência de aprendizado do autor nos últimos dias, esperando ser útil para iniciantes em Zigbee.
O texto completo é organizado de acordo com o processo básico de desenvolvimento de software: análise de requisitos, design de esboços, design detalhado, implementação de codificação e testes.
1. Análise da demandaApós discussão entre o "cliente" e o "desenvolvedor", foi determinada a seguinte descrição da função do sistema:
… A temperatura ambiente atual é coletada por nós baseados em CC2430, e seus valores de temperatura podem ser monitorados por um PC
… O próprio nó CC2430 deve ter certo grau de estabilidade e pode retornar automaticamente ao estado normal
… O intervalo de amostragem e o gerenciamento de energia do nó podem ser controlados por um PC
2. Projeto de esboçoDe acordo com a análise de requisitos acima, podemos dividir o sistema em dois módulos:Nó CC2430ePC。
[CC2430 Node]
… Parâmetros externos podem ser coletados regularmente e enviados ao PC
… Reinício automático quando a máquina é desligada
… Comandos do PC podem ser recebidos e processados de acordo: alterar o intervalo de amostra/gerenciamento de energia
[PC]
… A máquina C recebe e exibe dados através da ferramenta de porta serial
… Instruções podem ser enviadas ao microcontrolador através da ferramenta de porta serial para controlar sua velocidade de amostragem e gerenciamento de energia
3. Projeto detalhado(1) Estrutura do código
A estrutura de código desse sistema já foi descrita no ensaio "Hierarquia da estrutura de código de projeto embarcada, e a cópia é a seguinte:
(1) Camada de abstração de hardware
[ioCC2430.h] (Sistema Incluído):Todos os vetores SFR e de interrupção do CC2430 são definidos
[hal.h] Inclui definições comuns de tipo, macros de atribuição comuns e configuração comum dos recursos do CC2430 no chip (E/S, comunicação serial, ADC, temporizador, gerenciamento de energia, etc.)
(2) Camada funcional de módulo
[module.h] define recursos no chip (temporizadores, E/S), módulos de expansão fora do chip (LEDs) e declarações de funções relacionadas
[module.cImplemente a inicialização de cada módulo (LED).
(3) Camada de aplicação
[main.cConsulte hal.h, ioCC2430.h e module.h para atender a requisitos específicos de aplicação, como aquisição de temperatura, intercomunicação com PC e desligamento e reset
(2) Métodos de implementação de cada módulo
De acordo com os módulos divididos segundo o projeto de esboço, o sistema intrínseco pode ser dividido em dois módulos principais:Nó CC2430ePC。
Como existem ferramentas de comunicação por porta serial no PC, suas funções podem atender aos requisitos, então não precisamos fazer essa parte do PC, nem analisá-la. Vamos falar sobre a seção CC2430 abaixo
O método de implementação de cada subfunção do ponto:
… Use a interrupção de contagem de overflow do temporizador para acionar a amostragem cronometrada
… O modo UART0 com porta serial transmite dados de temperatura para um PC
… O circuito watchdog embutido do CC2430 é usado para realizar a função de reset automático do sistema
… A porta serial é usada para receber interrupções para capturar e responder a comandos de controle do PC
1) Se recebido@O caractere é o comando de controle de intervalo de amostragem, seguido por um número indicando o intervalo de amostragem: 0-0,5s, 1-1s, 2-2s
如:@0,表示每隔0.5秒采样一次。
2) Se recebido$ O caractere é o comando de controle de suspensão, seguido por um número indicando o modo de energia
Por exemplo: $3, o que significa colocar o sistema no modo de energia 3.
(3) Fluxograma de programas
- Fluxograma do programa mestre
- Diagrama de Fluxo do Programa de Interrupção de Transbordamento do Timer 1
- Fluxograma do procedimento de recepção de interrupção por porta serial
4. Implementação de codificação(1) Camada de abstração de hardware
A camada de abstração de hardware inclui ioCC2430.h e hal.h. Como o sistema anterior vem com ele, ele não será listado.
A seguir está uma lista de todo o conteúdo do hal.h (como este arquivo é muito longo e parece inconveniente, vou mostrá-lo em módulos):
- cabeça
- Portas de E/S
- Interrompido
- Porta serial
- Gerenciamento de energia e clock
- Temporizador
- Cão de guarda
- ADC
[url=]
[/url]
/***********************************************************
*Nome do arquivo: hal.h
*Autor: hustlzp
*Data: 8 de março de 2011
*Edição: 1.1
*Descrição da função: Camada de abstração de hardware
*Recordes modificados:
***********************************************************/
#ifndef HAL_H
#defineHAL_H
#include
/***********************************************************
Definições comuns de tipos
***********************************************************/
typedef sem sinalChar BYTE;
typedef sem sinalint PALAVRA;
typedef sem sinalLongas DWORD;
/***********************************************************
Definições macro comumente usadas
***********************************************************/
//8 lugares acima
#defineHIGH_BYTE(a) ((BYTE) (((WORD)(a)) >> 8))
//8 lugares abaixo
#defineLOW_BYTE(a) ((BYTE) ((PALAVRA)(a)))
//Designação
#defineSET_WORD(regH,regL,palavra)
fazer{
(regH)=HIGH_BYTE(palavra);
(regL)=LOW_BYTE(palavra);
}enquanto(0)
[url=] [/url]
[url=] [/url]
/***********************************************************
Portas de E/S
***********************************************************/
/*Configure a direção da porta de E/S
-----------------------------------------*/
#defineIO_DIR_PORT_PIN(porta, pin, dir)
fazer{
se(dir: == IO_OUT)
P##port##DIR |= (0x01<<(pin));
senão
P##port##DIR &= ~(0x01<<(pin));
}enquanto(0)
//O valor do dir do parâmetro é:
#defineIO_IN 0
#defineIO_OUT 1
/*Configure o modo de entrada da porta de E/S
-----------------------------------------*/
#defineIO_IMODE_PORT_PIN(porta, pino, imode)
fazer{
se(imode == IO_IMODE_TRI)
P##port##INP |= (0x01<<(pin));
senão
P##port##INP &= ~(0x01<<(pin));
}enquanto (0)
#define IO_PUD_PORT(bombordo, pud)
do {
if (pud == IO_PULLDOWN)
P2INP |= (0x01 << (porta+5));
senão
P2INP &= ~(0x01 << (porta+5));
} enquanto (0)
O valor do parâmetro PUD é:
#define IO_PULLUP 0 // Puxe para cima
#define IO_PULLDOWN 1 // Puxar para baixo
/*配置I/O口的功能
-----------------------------------------*/
#define IO_FUNC_PORT_PIN(porta, pino, func)
do {
if((port == 2) & & (pin == 3)){
se (func) {
P2SEL |= 0x02;
} else {
P2SEL &= ~0x02;
}
}
else if((port == 2) & & (pin == 4)){
se (func) {
P2SEL |= 0x04;
} else {
P2SEL &= ~0x04;
}
}
else{
se (func) {
P##port##SEL |= (0x01<<(pin));
} else {
P##port##SEL &= ~(0x01<<(pin));
}
}
} enquanto (0)
O valor do parâmetro func é:
#define IO_FUNC_GIO 0 // E/S Geral
#define IO_FUNC_PERIPH 1 // E/S periférica
Configure a localização da E/S periférica
#define IO_PER_LOC_TIMER1_AT_PORT0_PIN234() do { PERCFG = (PERCFG&~0x40)|0x00; } enquanto (0)
#define IO_PER_LOC_TIMER1_AT_PORT1_PIN012() do { PERCFG = (PERCFG&~0x40)|0x40; } enquanto (0)
#define IO_PER_LOC_TIMER3_AT_PORT1_PIN34() do { PERCFG = (PERCFG&~0x20)|0x00; } enquanto (0)
#define IO_PER_LOC_TIMER3_AT_PORT1_PIN67() do { PERCFG = (PERCFG&~0x20)|0x20; } enquanto (0)
#define IO_PER_LOC_TIMER4_AT_PORT1_PIN01() do { PERCFG = (PERCFG&~0x10)|0x00; } enquanto (0)
#define IO_PER_LOC_TIMER4_AT_PORT2_PIN03() do { PERCFG = (PERCFG&~0x10)|0x10; } enquanto (0)
#define IO_PER_LOC_SPI1_AT_PORT0_PIN2345() do { PERCFG = (PERCFG&~0x08)|0x00; } enquanto (0)
#define IO_PER_LOC_SPI1_AT_PORT1_PIN4567() do { PERCFG = (PERCFG&~0x08)|0x08; } enquanto (0)
#define IO_PER_LOC_SPI0_AT_PORT0_PIN2345() do { PERCFG = (PERCFG&~0x04)|0x00; } enquanto (0)
#define IO_PER_LOC_SPI0_AT_PORT1_PIN2345() do { PERCFG = (PERCFG&~0x04)|0x04; } enquanto (0)
#define IO_PER_LOC_UART1_AT_PORT0_PIN2345() do { PERCFG = (PERCFG&~0x02)|0x00; } enquanto (0)
#define IO_PER_LOC_UART1_AT_PORT1_PIN4567() do { PERCFG = (PERCFG&~0x02)|0x02; } enquanto (0)
#define IO_PER_LOC_UART0_AT_PORT0_PIN2345() do { PERCFG = (PERCFG&~0x01)|0x00; } enquanto (0)
#define IO_PER_LOC_UART0_AT_PORT1_PIN2345() do { PERCFG = (PERCFG&~0x01)|0x01; } enquanto (0)
//O valor do parâmetro imode é:
#defineIO_IMODE_PUD 0 //Puxar para cima/puxar para baixo
#defineIO_IMODE_TRI 1 //Três estados[url=] [/url]
[url=] [/url]
/***********************************************************
Interrompido
***********************************************************/
//Para interrupções liga/desliga
#defineINT_ON 1
#defineINT_OFF 0
//Usado para colocar/limpar flags de interrupção
#defineINT_SET 1
#defineINT_CLR 0
//Configurações globais de interrupção
#defineINT_GLOBAL_ENABLE(on) EA=(!! ligado)
//Defina a quebra
#defineINUM_RFERR 0
#defineINUM_ADC 1
#defineINUM_URX0 2
#defineINUM_URX1 3
#defineINUM_ENC 4
#defineINUM_ST 5
#defineINUM_P2INT 6
#define7 INUM_UTX0
#define8 INUM_DMA
#defineINUM_T1 9
#defineINUM_T2 10
#define11 INUM_T3
#define12 INUM_T4
#define13 INUM_P0INT
#define14 INUM_UTX1
#define15 INUM_P1INT
#define16 INUM_RF
#define17 INUM_WDT
/*Interrupções permitidas
-----------------------------------------*/
#defineINT_ENABLE(inum, on)
fazer{
se (inum==INUM_RFERR) { RFERRIE = ligado; }
senão se(inum==INUM_ADC) { ADCIE = on; }
senão se(inum==INUM_URX0) { URX0IE = ligado; }
senão se(inum==INUM_URX1) { URX1IE = ligado; }
senão se(inum==INUM_ENC) { ENCIE = on; }
senão se(inum==INUM_ST) { STIE = ligado; }
senão se(inum==INUM_P2INT) { (ligado)? (IEN2 |=0x02) : (IEN2 &= ~0x02); }
senão se(inum==INUM_UTX0) { (ligado)? (IEN2 |=0x04) : (IEN2 &= ~0x04); }
senão se(inum==INUM_DMA) { DMAIE = ligado; }
senão se(inum==INUM_T1) { T1IE = on; }
senão se(inum==INUM_T2) { T2IE = on; }
senão se(inum==INUM_T3) { T3IE = on; }
senão se(inum==INUM_T4) { T4IE = on; }
senão se(inum==INUM_P0INT) { P0IE = on; }
senão se(inum==INUM_UTX1) { (ligado)? (IEN2 |=0x08) : (IEN2 &= ~0x08); }
senão se(inum==INUM_P1INT) { (ligado)? (IEN2 |=0x10) : (IEN2 &= ~0x10); }
senão se(inum==INUM_RF) { (ligado)? (IEN2 |=0x01) : (IEN2 &= ~0x01); }
senão se(inum==INUM_WDT) { (ligado)? (IEN2 |=0x20) : (IEN2 &= ~0x20); }
}enquanto (0)
/*Defina a prioridade de interrupção
-----------------------------------------*/
#defineINT_PRIORITY(grupo, pri)
fazer{
se(pri ==0) { IP0 &= ~group; IP1 &= ~group; }
se(pri ==1) { IP0 |= grupo; IP1 &= ~group; }
se(pri ==2) { IP0 &= ~group; IP1 |= grupo; }
se(pri ==3) { IP0 |= grupo; IP1 |= grupo; }
}enquanto (0)
//O valor do parâmetro pri é: 0/1/2/3 (prioridade máxima)
//O valor do grupo de parâmetros é:
#defineRFERR_RF_DMA 0x01//IP0 do Grupo
#defineADC_P2INT_T1 0x02//Grupo IP1
#defineURX0_UTX0_T2 0x04//Grupo IP2
#defineURX1_UTX1_T3 0x08//Grupo IP3
#defineENC_P1INT_T4 0x10//Grupo IP4
#defineST_WDT_P0INT 0x20//Grupo IP5
/*Receba a flag de interrupção
-----------------------------------------*/
#defineINT_GETFLAG(inum) (
(inum==INUM_RFERR) ? RFERRIF:
(inum==INUM_ADC) ? ADCIF:
(inum==INUM_URX0) ? URX0IF :
(inum==INUM_URX1) ? URX1IF:
(inum==INUM_ENC) ? ENCIF_0 :
(inum==INUM_ST) ? STIF:
(inum==INUM_P2INT) ? P2FI:
(inum==INUM_UTX0) ? UTX0IF :
(inum==INUM_DMA) ? DMAIF :
(inum==INUM_T1) ? T1FI:
(inum==INUM_T2) ? T2FI:
(inum==INUM_T3) ? T3FI:
(inum==INUM_T4) ? T4FI:
(inum==INUM_P0INT) ? P0IF :
(inum==INUM_UTX1) ? UTX1IF :
(inum==INUM_P1INT) ? P1FI:
(inum==INUM_RF) ? S1CON &= ~0x03 :
(inum==INUM_WDT) ? WDTIF:
0
)
/*Defina a flag de interrupção
-----------------------------------------*/
#defineINT_SETFLAG(inum, f)
fazer{
se (inum==INUM_RFERR) { RFERRIF= f; }
senão se(inum==INUM_ADC) { ADCIF = f; }
senão se(inum==INUM_URX0) { URX0IF = f; }
senão se(inum==INUM_URX1) { URX1IF = f; }
senão se(inum==INUM_ENC) { ENCIF_1 = ENCIF_0 = f; }
senão se(inum==INUM_ST) { STIF = f; }
senão se(inum==INUM_P2INT) { P2IF = f; }
senão se(inum==INUM_UTX0) { UTX0IF= f; }
senão se(inum==INUM_DMA) { DMAIF = f; }
senão se(inum==INUM_T1) { T1IF = f; }
senão se(inum==INUM_T2) { T2IF = f; }
senão se(inum==INUM_T3) { T3IF = f; }
senão se(inum==INUM_T4) { T4IF = f; }
senão se(inum==INUM_P0INT) { P0IF = f; }
senão se(inum==INUM_UTX1) { UTX1IF= f; }
senão se(inum==INUM_P1INT) { P1IF = f; }
senão se(inum==INUM_RF) { (f) ? (S1CON |=0x03) : (S1CON &= ~0x03); }
senão se(inum==INUM_WDT) { WDTIF = f; }
}enquanto (0)
[url=] [/url]
[url=] [/url]
/***********************************************************
Porta serial
***********************************************************/
//O valor da BAUD_E corresponde a diferentes taxas de bauds
#defineBAUD_E(baud, clkDivPow) (
(baud==2400) ? 6 +clkDivPow :
(baud==4800) ? 7 +clkDivPow :
(baud==9600) ? 8 +clkDivPow :
(baud==14400) ? 8 +clkDivPow :
(baud==19200) ? 9 +clkDivPow :
(baud==28800) ? 9 +clkDivPow :
(baud==38400) ? 10+clkDivPow :
(baud==57600) ? 10+clkDivPow :
(baud==76800) ? 11+clkDivPow :
(baud==115200) ? 11+clkDivPow :
(baud==153600) ? 12+clkDivPow :
(baud==230400) ? 12+clkDivPow :
(baud==307200) ? 13+clkDivPow :
0 )
//O valor da BAUD_M corresponde a diferentes taxas de baud
#defineBAUD_M(baud) (
(baud==2400) ? 59 :
(baud==4800) ? 59 :
(baud==9600) ? 59 :
(baud==14400) ? 216 :
(baud==19200) ? 59 :
(baud==28800) ? 216 :
(baud==38400) ? 59 :
(baud==57600) ? 216 :
(baud==76800) ? 59 :
(baud==115200) ? 216 :
(baud==153600) ? 59 :
(baud==230400) ? 216 :
(baud==307200) ? 59 :
0)
/*Configuração da porta serial no modo UART
-----------------------------------------*/
#defineUART_SETUP(uart, receiveEnable, baudRate, options)
fazer{
se((uart) ==0){
se(PERCFG &0x01){
P1SEL |=0x30;
}senão{
P0SEL |=0x0C;
}
}
senão{
se(PERCFG &0x02){
P1SEL |=0xC0;
}senão{
P0SEL |=0x30;
}
}
U##uart##GCR = BAUD_E((baudRate),CLKSPD);
U##uart##BAUD = BAUD_M(baudRate);
U##uart##CSR |=0x80;
U##uart##CSR |= receiveEnable;
U##uart##UCR |= ((opções) |0x80);
}enquanto(0)
//O valor do parâmetro receiveEnable:
#defineUART_RECEIVE_ENABLE 0x40 //Recebendo permissão
#defineUART_RECEIVE_DISABLE 0x00
//O valor das opções de parâmetro:
#defineFLOW_CONTROL_ENABLE 0x40 //Controle de fluxo
#defineFLOW_CONTROL_DISABLE 0x00
#defineEVEN_PARITY 0x20 //Verificação ocasional
#defineODD_PARITY 0x00 //Verificação estranha
#defineNINE_BIT_TRANSFER 0x10 //Transferência de 9 bytes
#defineEIGHT_BIT_TRANSFER 0x00 //Transferência de 8 bytes
#definePARITY_ENABLE 0x08 //Habilitação da verificação de paridade
#definePARITY_DISABLE 0x00
#defineTWO_STOP_BITS 0x04 //Posição de parada de 2 posições
#defineONE_STOP_BITS 0x00 //1 posição de parada
#defineHIGH_STOP 0x02 //O nível de parada é alto
#defineLOW_STOP 0x00 //A posição de parada é baixa
#defineHIGH_START 0x01 //O nível inicial do bit é alto
#defineLOW_START 0x00 //O nível inicial do bit é baixo
//A porta serial envia caracteres
#defineUART_SEND(uart,data)
fazer{
enquanto(U##uart##CSR &0x01);
U##uart##DBUF = data;
}enquanto (0)
#defineUART0_SEND(dados) UART_SEND(0,dados)
#defineUART1_SEND(dados) UART_SEND(1,dados)
//A porta serial recebe caracteres
#defineUART_RECEIVE(uart,data)
fazer{
enquanto(! (U##uart##CSR&0x04));
data=U##uart##DBUF;
}enquanto(0)
#defineUART0_RECEIVE(dados) UART_RECEIVE(0,dados)
#defineUART1_RECEIVE(dados) UART_RECEIVE(1,dados)
[url=] [/url]
[url=] [/url]
/***********************************************************
Gerenciamento de energia e clock
***********************************************************/
//Faça o crossover do relógio
#defineCLKSPD (CLKCON & 0x07)
//Defina o modo de energia
#defineSET_POWER_MODE(modo)
fazer{
se(modo ==0) { DURMA &= ~0x03; }
senão se(modo ==3) { SLEEP |=0x03; }
senão{ SONO &= ~0x03; SLEEP |= modo; }
PCON |=0x01;
asm("NÃO");
}enquanto (0)
//O modo de parâmetros é definido para os seguintes valores:
#definePOWER_MODE_0 0x00
#definePOWER_MODE_1 0x01
#definePOWER_MODE_2 0x02
#definePOWER_MODE_3 0x03
//Usado para detectar a estabilidade de osciladores RC de alta frequência
#defineHIGH_FREQUENCY_RC_OSC_STABLE (SONO & 0x20)
//Usado para detectar a condição estável do oscilador de cristal
#defineXOSC_STABLE (SONO & 0x40)
//Obtenha o valor da frequência de tick do temporizador
#defineTICKSPD ((CLKCON & 0x38) >> 3)
//Defina a frequência do clock mestre
#defineSET_MAIN_CLOCK_SOURCE(fonte)
fazer{
se(fonte) {
CLKCON |=0x40;
enquanto(! HIGH_FREQUENCY_RC_OSC_STABLE);
se(TICKSPD ==0){
CLKCON |=0x08;
}
SONO |=0x04;
}
senão{
SONO &= ~0x04;
enquanto(! XOSC_STABLE);
asm("NÃO");
CLKCON &= ~0x47;
SONO |=0x04;
}
}enquanto (0)
//O valor da fonte do parâmetro é:
#defineCRISTAL 0x00 //Oscilador de cristal
#defineRC 0x01 //Oscilador RC
[url=] [/url]
[url=] [/url]
/***********************************************************
Temporizador 1
***********************************************************/
//O temporizador 1 permite que o overflow de contagem interrompa
#defineTIMER1_ENABLE_OVERFLOW_INT(Val)
(TIMIF = (val) ? TIMIF |0x40: TIMIF & ~0x40)
//Defina a flag de interrupção de overflow para o temporizador 1
#defineTIMER1_OVERFLOW_INT_SETFLAG(f) (T1CTL= ((T1CTL & (~0x10)) | f))
//O Temporizador 1 começa
#defineTIMER1_RUN(valor) (T1CTL = (valor) ? T1CTL|0x02 : T1CTL&~0x03)
//Ajuste a divisão do relógio para o cronômetro
#defineSET_TIMER_TICK(valor) do{ CLKCON = ((CLKCON & (~0x38)) | valor); } while(0)
//O valor do valor é:
#defineTIMER1_TICK_32M 0x00 //32MHz
#defineTIMER1_TICK_16M 0x08 //16MHz, o valor padrão para o reset do sistema
#defineTIMER1_TICK_8M 0x10 //8MHz
#defineTIMER1_TICK_4M 0x18 //4MHz
#defineTIMER1_TICK_2M 0x20 //2MHz
#defineTIMER1_TICK_1M 0x28 //1MHz
#defineTIMER1_TICK_500k 0x30 //500kHz
#defineTIMER1_TICK_250k 0x38 //250kHz
//Defina o crossover TICK para o temporizador 1
#defineSET_TIMER1_TICKDIV(valor)
fazer{
T1CTL &= ~0x0c;
T1CTL |= valor;
}enquanto (0)
//onde o valor é:
#defineTIMER1_TICKDIV_1 0x00 //1 Divisão
#defineTIMER1_TICKDIV_8 0x04 //Frequência de 8 vias
#defineTIMER1_TICKDIV_32 0x08
#defineTIMER1_TICKDIV_128 0x0c
//Defina o período de overflow do temporizador
#defineSET_TIMER1_PERIOD(valor)
fazer{
T1CC0H = HIGH_BYTE(valor);
T1CC0L = LOW_BYTE(valor);
}enquanto (0)
//Defina o modo de operação do Temporizador 1
#defineSET_TIMER1_MODE(modo)
fazer{
T1CTL = ((T1CTL & (~0x03)) | modo);
}enquanto (0)
//O valor do modo é:
#defineTIMER1_MODE_STOP 0x00
#defineTIMER1_MODE_FREE 0x01
#defineTIMER1_MODE_MODULE 0x02
#defineTIMER1_MODE_UPDOWN 0x03
[url=] [/url]
[url=] [/url]
/***********************************************************
Cão de guarda
***********************************************************/
//Defina o período de overflow para o temporizador watchdog
#defineWDT_SET_TIMEOUT_PERIOD(tempo)
fazer{ WDCTL &= ~0x03; WDCTL |= tempo técnico; }enquanto (0)
//O valor do timeout do parâmetro é:
#defineSEC_1 0x00 //após 1 segundo
#defineM_SEC_250 0x01 //após 250 ms
#defineM_SEC_15 0x02 //após 15 ms
#defineM_SEC_2 0x03 //após 2 ms
//Procedimentos de alimentação para cães
#defineWDT_RESET() do {
WDCTL = (WDCTL & ~0xF0) |0xA0;
WDCTL = (WDCTL & ~0xF0) |0x50;
} enquanto (0)
//Iniciar/parar o temporizador watchdog
#defineWDT_ENABLE() WDCTL |= 0x08
#defineWDT_DISABLE() WDCTL &= ~0x08
[url=] [/url]
[url=] [/url]
/***********************************************************
ADC
***********************************************************/
//Configure um único ADC
#defineADC_SINGLE_CONVERSION(cenários)
fazer{ ADCCON3 = configurações; }enquanto(0)
//A configuração de parâmetros é composta pelas seguintes combinações:
//Tensão de referência
#defineADC_REF_1_25_V 0x00 //Tensão de referência interna de 1,25V
#defineADC_REF_P0_7 0x40 //Tensão de referência externa no pino AIN7
#defineADC_REF_AVDD 0x80 //AVDD_SOC Pins
#defineADC_REF_P0_6_P0_7 0xC0 //AIN6-AIN7 Tensão de referência externa para entradas diferenciais
//Taxa de amostragem
#defineADC_8_BIT 0x00 //8º lugar
#defineADC_10_BIT 0x10 //10º lugar
#defineADC_12_BIT 0x20 //12º lugar
#defineADC_14_BIT 0x30 //14º lugar
//Entra no canal
#defineADC_AIN0 0x00 //P0_0
#defineADC_AIN1 0x01 //P0_1
#defineADC_AIN2 0x02 //P0_2
#defineADC_AIN3 0x03 //P0_3
#defineADC_AIN4 0x04 //P0_4
#defineADC_AIN5 0x05 //P0_5
#defineADC_AIN6 0x06 //P0_6
#defineADC_AIN7 0x07 //P0_7
#defineADC_GND 0x0C //Terra
#defineADC_TEMP_SENS 0x0E //Sensor de temperatura integrado no chip
#defineADC_VDD_3 0x0F //VDD/3
Conversão para ADC concluída
#define ADC_SAMPLE_READY() (ADCCON1 & 0x80)
#endif
//启动ADC转化
#define ADC_START()
do { ADCCON1 |= 0x40; } enquanto (0)//Selecione o modo de gatilho do ADC como manual (ou seja, ADC_SAMPLE_READY)
#defineADC_STOP()
fazer{ ADCCON1 |=0x30; }enquanto (0)[url=] [/url]
(2) Camada funcional de módulo
[url=] [/url]
/***********************************************************
*Nome do arquivo: module.h
*Autor: hustlzp
*Data: 6 de março de 2011
*Versão: 1.0
*Descrição da função: Arquivo de cabeçalho da camada funcional do módulo
*Lista de funções: void led_init()
void timer1_init()
vazio uart0_init(vazio);
void Uart0SendString(carcaça não assinada *s);
flutuar adc_start(vazio)
void get_temperature(caráter não assinado *saída, temperatura flutuante);
vazio watchdog_init(vazio);
*Recordes modificados:
***********************************************************/
#ifndef MODULE_H
#defineMODULE_H
#include"hal.h"
/***********************************************************
LED
***********************************************************/
//Defina os pinos do LED
#defineliderou1 P1_0
#defineLed2 P1_1
#defineLed3 P1_2
#defineliderou4 P1_3
//Luz LED e desligado
#defineLED_OFF 1
#defineLED_ON 0
//Inicialização de LED
vazioled_init(vazio);
/***********************************************************
timer1
***********************************************************/
//Usado para definir o valor do período de transbordamento para o temporizador
#defineTIMER1_OVF_2SEC 0xF424 //2s
#defineTIMER1_OVF_1SEC 0x7A12 //1s
#defineTIMER1_OVF_dot5SEC 0x3D09 //0,5s
//O temporizador 1 inicializa
vazio timer1_init(vazio);
/***********************************************************
UART0
***********************************************************/
//Inicialização do UART0
vazio uart0_init(vazio);
//Cadeia de transmissão por porta serial
vazio Uart0SendString(sem sinal)Char*s);
/***********************************************************
ADC-14
***********************************************************/
//Usado para converter os dados obtidos pelo ADC para temperatura Celsius
#defineADC_TO_CELSIUS(temperatura) (temperatura * 0,06229 - 311,43)
//Iniciar conversão para ADC
Flutuadoradc_start(vazio);
//Conversão
vazio get_temperature(não assinadoChar*saída,Flutuadortemp);
/***********************************************************
WatchDog
***********************************************************/
//Inicialização do cão de guarda
vazio watchdog_init(vazio);
#endif
[url=] [/url]
[url=] [/url]
/***********************************************************
*Nome do arquivo: module.c
*Autor: hustlzp
*Data: 2011/3/11
*Versão: 1.0
*Descrição da função: Arquivo fonte da camada funcional do módulo
*Lista de funções: (omitida)
*Recordes modificados:
***********************************************************/
#include"module.h"
/***********************************************************
*Nome da função: led_init
*Função funcional: inicialização do LED
*Parâmetros de entrada: Nenhum
*Parâmetros de exportação: Nenhum
***********************************************************/
vazioled_init(vazio)
{
//Configure P1.0, P1.1, P1.2 e P1.3 como portas gerais de E/S
IO_FUNC_PORT_PIN(1, 0, IO_FUNC_GIO);
IO_FUNC_PORT_PIN(1, 1, IO_FUNC_GIO);
IO_FUNC_PORT_PIN(1, 2, IO_FUNC_GIO);
IO_FUNC_PORT_PIN(1, 3, IO_FUNC_GIO);
//Configure P1.0, P1.1, P1.2 e P1.3 como saídas
IO_DIR_PORT_PIN(1, 0, IO_OUT);
IO_DIR_PORT_PIN(1, 1, IO_OUT);
IO_DIR_PORT_PIN(1, 2, IO_OUT);
IO_DIR_PORT_PIN(1, 3, IO_OUT);
liderou1 = LED_ON;
liderou2 = LED_OFF;
liderado3 = LED_OFF;
liderado4 = LED_OFF;
}
/***********************************************************
*Nome da função: timer1_init
* Função função: inicialização do Timer 1
*Parâmetros de entrada: Nenhum
*Parâmetros de exportação: Nenhum
***********************************************************/
vaziotimer1_init(vazio)
{
INT_GLOBAL_ENABLE(INT_ON); //Abra a interrupção global
INT_ENABLE(INUM_T1, INT_ON); //Abertura de interrupção T1
TIMER1_ENABLE_OVERFLOW_INT(INT_ON); //Interrupção de overflow de contagem aberta T1
SET_TIMER_TICK(TIMER1_TICK_4M); //Ajuste o temporizador TICK para 4MHz
SET_TIMER1_PERIOD(TIMER1_OVF_2SEC); //Defina o período de contagem para T1 para 2s
SET_TIMER1_TICKDIV(TIMER1_TICKDIV_128); //Ajuste o crossover do clock para T1 para 128
SET_TIMER1_MODE(TIMER1_MODE_MODULE); //Defina o modo de execução do T1 para módulo
}
/***********************************************************
*Nome da função: uart0_init
*Função função: Inicialização da porta serial UART0
*Parâmetros de entrada: Nenhum
*Parâmetros de exportação: Nenhum
***********************************************************/
vaziouart0_init(vazio)
{
//Selecione o local do UART
IO_PER_LOC_UART0_AT_PORT0_PIN2345();
//Configurar UART: Recebimento permitido, 115200bps, bit de parada de um bit, sem paridade
UART_SETUP(0, UART_RECEIVE_ENABLE,115200, ONE_STOP_BITS | PARITY_DISABLE);
//Abra a interrupção total
INT_GLOBAL_ENABLE(INT_ON);
//Abra a porta serial 0 para receber interrupções
INT_ENABLE(INUM_URX0, INT_ON);
}
/***********************************************************
*Nome da função: Uart0SendString
* Função função: inicialização do Timer 1
*Parâmetro de entrada: carvão não assinado *s
A corda que você quer enviar
*Parâmetros de exportação: Nenhum
***********************************************************/
vazioUart0SendString(sem sinal)Char*s)
{
enquanto(*s !=0)
UART0_SEND(*s++);
}
/***********************************************************
*Nome da função: adc_start
*Função funcional: Iniciar conversão de ADC
*Parâmetros de entrada: Nenhum
*Parâmetro de exportação: float
O valor de temperatura no tablet
***********************************************************/
Flutuadoradc_start(vazio)
{
sem assinaturaintTemporário;
//A tensão de referência é 1,25V, a precisão da amostragem é de 14 bits, e o alvo de conversão é o sensor de temperatura integrado ao chip
ADC_SINGLE_CONVERSION(ADC_REF_1_25_V | ADC_14_BIT | ADC_TEMP_SENS);
ADC_STOP(); //Defina o método de gatilho para conversão de ADC para manual
ADC_START(); //Iniciar conversão para ADC
enquanto(! ADC_SAMPLE_READY()); //Espere a conversão terminar
temp = ADCL >>2; //Salvar os resultados da conversão na temperatura
temp |= (((sem sinalint) ADCH) <<6);
retornoADC_TO_CELSIUS(temporário); //Retorna o valor real da temperatura após a conversão
}
/***********************************************************
*Nome da função: get_temperature
*Função função: Processar o valor de temperatura e armazená-lo no array de caracteres para saída serial
*Parâmetro de entrada: unsigned char *saída
Usado para armazenar o valor de temperatura convertido
Temperatura do flutuador
Valor da temperatura Celsius
*Parâmetros de exportação: Nenhum
***********************************************************/
vazioget_temperature(não assinadoChar*saída,Flutuadortemporário)
{
saída[0] = (sem assinaturaChar)(temporária) /10 + 48; //Dez vagas
saída[1] = (sem assinaturaChar(temp) %10 + 48; //dígitos simples
saída[2] ='.'; //Ponto decimal
saída[3] = (sem assinaturaChar)(temp*10) %10 + 48; //Décima
saída[4] = (sem assinaturaChar)(temp*100) %10 + 48; //Percentil
saída[5] =''; //Endifadores de cordas
}
/***********************************************************
*Nome da função: watchdog_init
*Função função: Inicialização do cão de guarda
*Parâmetros de entrada: Nenhum
*Parâmetros de exportação: Nenhum
***********************************************************/
vaziowatchdog_init(vazio)
{
WDT_SET_TIMEOUT_PERIOD(SEC_1); //Defina o tempo de timeout para 1s
WDT_ENABLE(); //Comece o cão de guarda
}
[url=] [/url]
(3) Camada de aplicação
[url=] [/url]
/*******************************************************************
Nome do arquivo: main.c
Autor: hustlzp
Data: 2011/3/11
Versão: 1.0
Descrição da função: Arquivo mestre do programa
Lista de Funções: (omitida)
Histórico de Modificações:
*******************************************************************/
#include
/********************************************************************
Procedimentos de serviço de interrupção
********************************************************************/
/* 定时器1溢出中断子程序
-------------------------------------------------------*/
#pragma vetor=T1_VECTOR
__interrupt vazio T1_ISR(vazio)
{
EA=0; O portão é interrompido
liderou2 = LED_ON;
get_temperature(saída,adc_start()); Converta o valor da temperatura em um array de caracteres a serem produzidos
Uart0SendString (saída); Valor da temperatura de saída
Uart0SendString("°C");
led2
/* 串口接收中断子程序
-------------------------------------------------------*/
#pragma vetor=URX0_VECTOR
__interrupt vazio RE_ISR(vazio)
{
EA=0;
liderado3 = LED_ON;
recebe = U0DBUF;
if(type==1) // type=1, o que indica que o caractere recebido é usado para definir o período de overflow do temporizador
{
tipo=0;
Troca (recebe)
{
caso '0': // O período de transbordamento do temporizador é de 0,5 s
{
SET_TIMER1_PERIOD(TIMER1_OVF_dot5SEC);
intervalo;
}
caso '1': // O período de transbordamento do temporizador é 1s
{
SET_TIMER1_PERIOD(TIMER1_OVF_1SEC);
intervalo;
}
caso '2': // O período de transbordamento do temporizador é de 2s
{
SET_TIMER1_PERIOD(TIMER1_OVF_2SEC);
intervalo;
}
}
}
else if(type==2) // type=2, indicando que os caracteres recebidos são usados para controle de sono
{
tipo=0;
liderado1 = LED_OFF;
liderou2 = LED_OFF;
liderado3 = LED_OFF;
Troca (recebe)
{
case '1': // Entrar no modo de energia PM1
{
SET_POWER_MODE(1);
intervalo;
}
case '2': // Entrar no modo de energia PM2
{
SET_POWER_MODE(2);
intervalo;
}
caso '3': //Entrar no modo de energia PM3
{
SET_POWER_MODE(3);
intervalo;
}
}
}
else if(type==0) // type=0, o que indica que o caractere recebido é o tipo de comando control: @ ou $
{
se(receber=='@')
{
tipo=1; '@' é recebido para indicar que o próximo caractere é usado para definir o período de overflow
}
se(receber=='$')
{
tipo=2; '$' é recebido, indicando que o próximo caractere é usado para controle de sono do sistema
}
}
liderado3 = LED_OFF;
EA=1;
}
=LED_OFF;
TIMER1_OVERFLOW_INT_SETFLAG(INT_CLR); Limpe o sinal de interrupção
EA=1; Interrupção aberta
}
/* 主函数
-------------------------------------------------------*/
void main(void)
{
SET_MAIN_CLOCK_SOURCE(CRYSTAL); Ajuste o clock do sistema para oscilador de cristal de 32MHz
led_init(); Inicialização de LED
uart0_init(); Inicialização da porta serial UART0
timer1_init(); O temporizador 1 inicializa
watchdog_init(); Inicialização do cão de guarda
while(1)
{
WDT_RESET(); Alimente o cachorro o tempo todo
}
}/********************************************************************
Programa principal
********************************************************************/
/* 全局变量
-------------------------------------------------------*/
saída do carvão não assinado[6]={0}; Os dados de temperatura são armazenados para facilitar a saída serial
O personagem não assinado recebe; Armazene os caracteres recebidos
tipo de personagem sem sinal=0; A flag de tipo do caractere recebido é definida como 0/1/2"module.h"[url=] [/url]
5. TestesOh~ O código finalmente foi colado, é realmente exaustivo, vamos testar esse pequeno sistema:
(1) Amostragem cronometrada
Abra a porta serial, inicie a depuração do IAR e veja que o LED1 está ligado, e o valor de temperatura na ferramenta da porta serial é constantemente gerado, e o intervalo de amostragem é determinado como 2s:
(2) Controle de intervalos de amostragem
Digite "@1" na ferramenta de porta serial, depois teste o intervalo de amostragem, e veja que ele se tornou 1s; Digite "@0" e o intervalo de amostragem mudou para 0,5s.
(3) Controle do sono
Digite "$1" na ferramenta de porta serial e veja que todos os LEDs estão desligados e a amostragem de temperatura parou:
Após os testes, o sistema funciona normalmente e de forma estável, e basicamente atende aos requisitos.
Alunos que precisam de código-fonteClique aqui para baixar
6. ConclusãoEste artigo toma um experimento um pouco abrangente como exemplo para mostrar como integrar recursos CC2430 no chip para escrever um sistema pequeno relativamente padronizado. Em alguns dias, vou dedicar um tempo para escrever um manual do usuário simples para o hal.h para que eu e todos possamos usar facilmente o CC2430.
Em seguida, vou terminar minha pesquisa sobre os recursos do CC2430 no chip e me dedicar a aprender a pilha de protocolos TI Z-Stack~
A escrita de posts no blog desta série chegou ao fim por enquanto, mas a jornada de Zigbee continuará. A paisagem à frente é desconhecida, mas acredito que o autor superará obstáculos com todos e experimentará altos e baixos, e haverá ganhos.
Fique ligado: posts no blog "Junte-se ao TI Z-Stack"!