#include <stdio.h> #include <stm32f10x.h>
#define RS_0 (GPIOA->BRR = GPIO_BRR_BR0) #define RS_1 (GPIOA->BSRR = GPIO_BSRR_BS0) #define RW_0 (GPIOA->BRR = GPIO_BRR_BR1) #define RW_1 (GPIOA->BSRR = GPIO_BSRR_BS1) #define E_0 (GPIOA->BRR = GPIO_BRR_BR2) #define E_1 (GPIOA->BSRR = GPIO_BSRR_BS2)
#define FILE_USART (FILE *)10
struct fifo { uint8_t buf[127]; uint8_t front; uint8_t rear; };
uint32_t num = 0; uint8_t seg8[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90}; struct fifo usartbuf;
/* // 正常的液晶延時函數 void delay(void) { uint8_t i; for (i = 0; i < 10; i++); } */
// 故意減慢液晶接收字符的速度 void delay(void) { uint32_t i; for (i = 0; i < 100000; i++); }
void delay2(void) { uint32_t i; for (i = 0; i < 3000000; i++); }
void fifo_init(struct fifo *f) { f->front = f->rear = 0; }
uint8_t fifo_full(struct fifo *f) { return f->front == (f->rear + 1) % sizeof(f->buf); }
uint8_t fifo_empty(struct fifo *f) { return f->front == f->rear; }
uint8_t fifo_in(struct fifo *f, uint8_t data) { if (fifo_full(f)) return 0; f->buf[f->rear] = data; f->rear = (f->rear + 1) % sizeof(f->buf); return 1; }
uint8_t fifo_out(struct fifo *f, uint8_t *data) { if (fifo_empty(f)) return 0; *data = f->buf[f->front]; f->front = (f->front + 1) % sizeof(f->buf); return 1; }
void LCD1602_BusyWait(void) { RS_0; RW_1; E_1; GPIOC->CRL = 0x44444444; // 讀端口 while (GPIOC->IDR & GPIO_IDR_IDR7); GPIOC->CRL = 0x33333333; E_0; }
void LCD1602_WriteCmd(uint8_t cmd) { LCD1602_BusyWait(); RS_0; RW_0; __disable_irq(); GPIOC->ODR = (GPIOC->ODR & 0xff00) | cmd; __enable_irq(); E_1; delay(); E_0; }
void LCD1602_WriteData(uint8_t data) { LCD1602_BusyWait(); RS_1; RW_0; __disable_irq(); GPIOC->ODR = (GPIOC->ODR & 0xff00) | data; __enable_irq(); E_1; delay(); E_0; }
void LCD1602_Init(void) { LCD1602_WriteCmd(0x38); LCD1602_WriteCmd(0x01); LCD1602_WriteCmd(0x0c); // 設置背光電壓 DAC->CR = DAC_CR_EN1; DAC->DHR8R1 = 140; }
int fputc(int ch, FILE *fp) { if (fp == &__stderr || fp == FILE_USART) { USART1->DR = ch; while ((USART1->SR & USART_SR_TXE) == 0); } else if (fp == &__stdout) { if (ch == '\n') LCD1602_WriteCmd(0x01); // 遇到\n就清屏 else LCD1602_WriteData(ch); } return ch; }
uint8_t PCF8591_ADC(void) { uint8_t temp; I2C1->CR1 |= I2C_CR1_START; // 開始信號 while ((I2C1->SR1 & I2C_SR1_SB) == 0); I2C1->DR = 0x90; // 地址碼(寫) while ((I2C1->SR1 & I2C_SR1_ADDR) == 0); // PB6~7必須接外部上拉電阻, 否則程序會卡死在這裏 temp = I2C1->SR2; // 讀SR2, 清ADDR I2C1->DR = 0x04; // 四路單輸入, 通道0, 自動增益 while ((I2C1->SR1 & I2C_SR1_TXE) == 0); I2C1->CR1 |= I2C_CR1_START; // 重新發送開始信號 while ((I2C1->SR1 & I2C_SR1_SB) == 0); I2C1->DR = 0x91; // 地址碼(讀) while ((I2C1->SR1 & I2C_SR1_ADDR) == 0); temp = I2C1->SR2; // 讀SR2, 清ADDR while ((I2C1->SR1 & I2C_SR1_RXNE) == 0); temp = I2C1->DR; I2C1->CR1 |= I2C_CR1_STOP; // 停止信號 while (I2C1->CR1 & I2C_CR1_STOP); return temp; }
int main(void) { uint8_t value; uint8_t pos = 0; uint8_t display_finish = 1; RCC->APB1ENR = RCC_APB1ENR_DACEN | RCC_APB1ENR_I2C1EN | RCC_APB1ENR_TIM2EN; RCC->APB2ENR = RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_USART1EN; GPIOA->CRL = 0x00000333; // 1602液晶控制端 GPIOA->CRH = 0x000004b0; // USART端口 GPIOB->CRL = 0xff000000; // I2C總線: PB6~7設為復用開漏50MHz輸出 GPIOB->CRH = 0x33333333; // 數碼管段選 GPIOC->CRL = 0x33333333; // 1602液晶數據端 GPIOC->ODR = 0x3000; // 熄滅數碼管 GPIOC->CRH = 0x00333333; // 數碼管位選 // 數碼管掃描中斷配置 TIM2->ARR = 29; TIM2->PSC = 7199; TIM2->EGR = TIM_EGR_UG; // 立即觸發TIM2中斷, 點亮數碼管 TIM2->DIER = TIM_DIER_UIE; NVIC_EnableIRQ(TIM2_IRQn); // 該函數位於core_cm3.h, 是MDK自帶的函數, 不是庫函數 TIM2->CR1 |= TIM_CR1_CEN; fifo_init(&usartbuf); // MAX232的電源必須接3.3V, 否則PC端會收到很多奇怪的數據, 例如0xfb, 0xff, 0xbf等 USART1->BRR = 0x271; USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE; NVIC_SetPriority(USART1_IRQn, 1); // 數碼管中斷的優先級為0, 因此可以搶佔串口接收中斷, 防止數碼管閃爍 NVIC_EnableIRQ(USART1_IRQn); I2C1->CR2 = 36; // FREQ=100100(36MHz) I2C1->CCR = 90; // CCR=FREQ/2f, f=200kHz I2C1->CR1 = I2C_CR1_PE; // 啟動總線 LCD1602_Init(); perror("Hello World!"); while (1) { while (fifo_out(&usartbuf, &value)) { display_finish = 0; if (value == '\n') pos = 0; putchar(value); pos++; if (pos == 16) LCD1602_WriteCmd(0xc0); else if (pos == 32) { pos = 0; LCD1602_WriteCmd(0x80); } } if (display_finish == 0) { // 液晶字符顯示完畢時, 向串口發送AD轉換值 value = PCF8591_ADC(); fprintf(FILE_USART, "ADC Value:%d\r\nusartbuf.front=%d, usartbuf.rear=%d\r\n", value, usartbuf.front, usartbuf.rear); // 必須用\r\n才能正確換行 display_finish = 1; } } }
// 數碼管動態掃描(高優先級中斷) void TIM2_IRQHandler(void) { static uint8_t pos = 5; static uint32_t numbuf; if (TIM2->SR & TIM_SR_UIF) // 定時器2溢出 { TIM2->SR &= ~TIM_SR_UIF; if (pos == 5) numbuf = num; else numbuf /= 10; GPIOC->ODR = (GPIOC->ODR & 0xf0ff) | 0x3000; // 關閉位選 GPIOB->ODR = (GPIOB->ODR & 0xff) | (seg8[numbuf % 10] << 8); // 設置顯示字符 if (pos <= 3) GPIOC->ODR |= 1 << (pos + 8); // 位選直連I/O端 else GPIOC->ODR &= ~(1 << (pos + 8)); // 三極管驅動的位選端 if (pos == 0) pos = 5; else pos--; } }
// 串口接收中斷(較低優先級中斷) void USART1_IRQHandler(void) { uint8_t data; if (USART1->SR & USART_SR_RXNE) { data = USART1->DR; num = (num + data) % 1000000; fifo_in(&usartbuf, data); } }
|